Skip to content

Commit 5077919

Browse files
chore: reduce agent context overhead (#3270)
Refs #3269. Co-authored-by: savasp-agent[bot] <275188714+savasp-agent[bot]@users.noreply.github.com>
1 parent 2212817 commit 5077919

9 files changed

Lines changed: 131 additions & 91 deletions

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
2-
globs: "src/Cvoya.Spring.Dapr/Data/Migrations/**"
2+
paths:
3+
- "src/Cvoya.Spring.Dapr/Data/Migrations/**"
34
---
45

56
Shared rule text lives in [`docs/agent-rules/database-migrations.md`](../../docs/agent-rules/database-migrations.md).

.claude/rules/documentation.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
---
2-
globs: "docs/**,**/AGENTS.md,**/README.md,CONVENTIONS.md"
2+
paths:
3+
- "docs/**"
4+
- "**/AGENTS.md"
5+
- "**/README.md"
6+
- "CONVENTIONS.md"
37
---
48

59
Shared rule text lives in [`docs/agent-rules/documentation.md`](../../docs/agent-rules/documentation.md).

.claude/rules/generated-files.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
2-
globs: "**/src/lib/api/schema.d.ts"
2+
paths:
3+
- "**/src/lib/api/schema.d.ts"
34
---
45

56
Shared rule text lives in [`docs/agent-rules/generated-files.md`](../../docs/agent-rules/generated-files.md).

.claudeignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Build artifacts and caches
2+
node_modules/
3+
**/bin/
4+
**/obj/
5+
artifacts/
6+
TestResults/
7+
__pycache__/
8+
9+
# Agent-managed worktrees live under ~/dev/worktrees, never in the repository.
10+
.claude/worktrees/
11+
.codex/worktrees/
12+
.worktrees/
13+
14+
# Secrets and local state
15+
eng/config/spring.env
16+
.env
17+
.env.*

.github/workflows/ci.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ jobs:
4545
runtime_catalog: ${{ steps.filter.outputs.runtime_catalog }}
4646
sample: ${{ steps.filter.outputs.sample }}
4747
platform_dockerfile: ${{ steps.filter.outputs.platform_dockerfile }}
48+
agent_context: ${{ steps.filter.outputs.agent_context }}
4849
steps:
4950
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
5051
with:
@@ -186,6 +187,28 @@ jobs:
186187
# input (web filter, gated on jointly below).
187188
- 'eng/build/Dockerfile'
188189
- 'eng/ci/enforce-npm.cjs'
190+
agent_context:
191+
- 'AGENTS.md'
192+
- 'CLAUDE.md'
193+
- '.claude/rules/**'
194+
- '.claudeignore'
195+
- 'eng/agent-hooks/context-budget.test.sh'
196+
- 'eng/agent-hooks/hooks.test.sh'
197+
198+
agent-context:
199+
name: Agent context contract
200+
runs-on: ubuntu-latest
201+
needs: changes
202+
if: needs.changes.outputs.agent_context == 'true' || needs.changes.outputs.ci_workflow == 'true'
203+
timeout-minutes: 2
204+
205+
steps:
206+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
207+
with:
208+
persist-credentials: false
209+
210+
- name: Check instruction budgets and lazy rules
211+
run: bash eng/agent-hooks/context-budget.test.sh
189212

190213
workflow-yaml:
191214
name: Workflow YAML syntax

AGENTS.md

Lines changed: 37 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,57 @@
1-
# Spring Voyage — Project Rules
1+
# Spring Voyage — project instructions
22

3-
A source-available collaboration platform for teams of AI agents and the humans they work with. General-purpose, domain-agnostic. Built on .NET 10 and Dapr. Namespace: `Cvoya.Spring.*`.
3+
Spring Voyage is a source-available, domain-agnostic collaboration platform for teams of AI agents and humans. It uses .NET 10 and Dapr under the `Cvoya.Spring.*` namespace. Read [CONVENTIONS.md](CONVENTIONS.md) before changing code.
44

5-
## Coding conventions
5+
The as-built architecture is under [docs/architecture/](docs/architecture/README.md), with accepted decisions under [docs/decisions/](docs/decisions/README.md). Read the relevant pages before working in an area.
66

7-
[`CONVENTIONS.md`](CONVENTIONS.md) is mandatory. Read it before writing code.
7+
## Platform invariants
88

9-
## Architecture
9+
- Agents are Dapr virtual actors with durable per-thread mailboxes.
10+
- A unit is a composite agent; unit routing is runtime behavior, not platform configuration. See [units vs agents](docs/concepts/units-vs-agents.md).
11+
- Humans are addressable thread participants, not agents; see [humans](docs/concepts/humans.md).
12+
- Connectors bridge external systems to units but are not routable subjects.
13+
- Messages are typed, one-way communications on durable threads.
14+
- The platform coordinates external agent runtimes in ephemeral or persistent containers; it does not host its own tool loop.
15+
- Prompt assembly has platform, unit, thread, and agent layers.
1016

11-
Architecture lives under [`docs/architecture/`](docs/architecture/README.md); decision records under [`docs/decisions/`](docs/decisions/README.md). Read the relevant doc before working on an issue.
17+
## Build and verification
1218

13-
Key concepts:
19+
Use the repository `build`, `test`, `lint`, and `format` skills. Bare `dotnet test SpringVoyage.slnx` can exit successfully without running tests, so never use it as evidence.
1420

15-
- **Agents** — Dapr virtual actors with a per-thread mailbox.
16-
- **Units** — composite agents that have children. A unit **is** an agent — see [`docs/concepts/units-vs-agents.md`](docs/concepts/units-vs-agents.md) for the quick reference on what's shared vs different; most features apply to both subjects identically. How a unit routes work across its members is runtime behaviour, not platform configuration ([ADR-0053](docs/decisions/0053-units-are-agents-and-one-way-delivery.md)).
17-
- **Humans** — addressable thread participants (not agents). A human implements only `IMessageReceiver` and can be a member of a unit with permission grants. See [`docs/concepts/humans.md`](docs/concepts/humans.md). Portal surfaces for humans are scoped to v0.2.
18-
- **Connectors** — non-routable bridges between external systems and units.
19-
- **Messages** — typed, one-way communications between addressable entities, exchanged on durable threads.
20-
- **Execution** — the platform coordinates external agent runtimes (Claude Code, Codex, Gemini, …) running in containers; it does not host its own tool-use loop. Agents run in one of two hosting modes — _ephemeral_ (a container per turn) or _persistent_ (a long-lived container).
21-
- **Prompt assembly** — four-layer composition: platform, unit context, thread context, agent instructions.
21+
Install `eng/install-hooks.sh` once per clone. The pre-push hook runs the change-scoped static gate in `eng/ci/ci-local.sh`; hosted CI runs the full build, lint, and test gates. During iteration use focused checks, but API/contract changes still require the solution-wide test gate because integration failures often surface first. A subagent's unqualified “tests pass” means the complete repository test gate passed.
2222

23-
## Build, test, lint
23+
Generated-file and migration rules live in [generated files](docs/agent-rules/generated-files.md) and [database migrations](docs/agent-rules/database-migrations.md). Claude loads their matching shims lazily from `.claude/rules/`.
2424

25-
Use the `build`, `test`, and `lint` commands/skills. Each points at the canonical CI invocation.
25+
## Source-available extension boundary
2626

27-
Pitfall: bare `dotnet test SpringVoyage.slnx` exits 0 without running tests. Always go through the `test` command/skill.
27+
The public repository is the core platform. A private host extends it through dependency injection with tenancy, identity, billing, and premium behavior. Preserve that one-way boundary:
2828

29-
Shared generated-file and migration guardrails live in [`docs/agent-rules/generated-files.md`](docs/agent-rules/generated-files.md) and [`docs/agent-rules/database-migrations.md`](docs/agent-rules/database-migrations.md). Claude auto-loads matching `.claude/rules/` shims; Codex reads the shared docs through these project instructions.
29+
- Resolve tenancy through `ITenantContext.CurrentTenantId`; never hardcode `"default"`. Fresh OSS rows use `OssTenantIds.Default`.
30+
- Tenant-owned persisted entities implement `ITenantScopedEntity`.
31+
- Services use DI, not static state or ad-hoc singletons. Register replaceable defaults with `TryAdd*`.
32+
- Extension contracts are public, interface-first, and composable; do not seal intended extension points.
33+
- `Cvoya.Spring.Core` remains dependency-free.
34+
- Do not reference private-repository issues, branches, or PRs.
35+
- New features must remain replaceable or decoratable by a downstream host without forks or patches.
3036

31-
### Pre-push checks
37+
Agent runtimes implement `IAgentRuntime`; connectors implement `IConnectorType`. Each ships in its own `Cvoya.Spring.AgentRuntimes.<Name>` or `Cvoya.Spring.Connector.<Name>` project and registers through one DI extension.
3238

33-
Install the git hook once per clone with `eng/install-hooks.sh`; its pre-push gate runs the fast static checks (`eng/ci/ci-local.sh` — build, format, lint, typecheck for the areas you touched) automatically before every push. CI runs the full `build`, `lint`, and `test` gates on the PR and again in the merge queue, reusing one .NET restore/build for formatting and tests. The resulting `main` push does not repeat ordinary CI, so a regression is caught before merge without a third identical run.
39+
Operational mutations for runtime, connector, credential, tenant-seed, and skill-bundle administration are CLI-only. The portal may expose read-only visibility. User-facing features still follow the UI/CLI parity rule in [CONVENTIONS.md](CONVENTIONS.md).
3440

35-
Two things the fast hook does **not** run, so keep them in mind: `test` is solution-wide for a reason — integration tests (`tests/integration/Cvoya.Spring.Integration.Tests`) routinely break first when API contracts change, and a scoped `dotnet test path/to/single.csproj` is no substitute. A dispatched sub-agent's claim of "tests pass" means the full solution-wide `test` gate (e.g. `eng/ci/ci-local.sh --full`), not a project-scoped subset.
41+
## Documentation
3642

37-
## Documentation updates
43+
Architecture pages are living as-built truth. A design-affecting change updates the relevant page and diagrams in the same PR. Feature changes update the relevant guide; new concepts get a `docs/concepts/` entry; decisions get an ADR. Web changes keep `src/Cvoya.Spring.Web/DESIGN.md` synchronized. The concise cross-agent checklist is [documentation](docs/agent-rules/documentation.md).
3844

39-
[`docs/architecture/`](docs/architecture/README.md) is a **living description of the system** — keep it current the same way ADRs are authored. Any design-affecting change updates the relevant `docs/architecture/` page **and its diagrams** in the same PR; a behavioural or architectural change that leaves those docs stale is not complete. The architecture docs are the single source of truth — these instruction files, and the `docs/concepts/` and `docs/guide/` docs, point at them rather than restating them.
45+
Release plans live under `docs/plan/<release>/README.md`; use the active release plan as the plan of record.
4046

41-
When shipping a feature, also update the relevant guide doc, and add a `docs/concepts/` entry for any new concept. The "why" behind a design decision belongs in an ADR under [`docs/decisions/`](docs/decisions/README.md).
47+
## Workflow
4248

43-
For changes under `src/Cvoya.Spring.Web/`, keep `src/Cvoya.Spring.Web/DESIGN.md` in sync — it is the portal's visual contract.
49+
- Work only in a dedicated task worktree under `~/dev/worktrees/spring-voyage/<task>`, based on current `origin/main`; never edit the main checkout.
50+
- One PR owns one coherent outcome. Combine issues only when instructed or when they are inseparable within one owned surface and verification boundary.
51+
- Rebase on current `origin/main` before pushing and before merging.
52+
- Append to shared registries such as `StateKeys`, DI registrations, and enums; avoid reorder churn.
53+
- File and natively wire genuinely separate follow-ups before the PR lands.
4454

45-
The documentation quick reference lives in [`docs/agent-rules/documentation.md`](docs/agent-rules/documentation.md) and applies to both Claude and Codex.
55+
All GitHub writes for this repository, including commits, use `gh-app`. PRs are review-ready by default, use squash merge, and repeat the closing keyword for every completed issue.
4656

47-
## Source-available platform and extensibility
48-
49-
This repository is the **public, source-available core** of Spring Voyage. A private repository extends it via git submodule and dependency injection — adding multi-tenancy, OAuth/SSO, billing, and premium features.
50-
51-
- **Don't bypass `ITenantContext`.** Resolve the current tenant through `ITenantContext.CurrentTenantId` (a `Guid`). Never hardcode the literal string `"default"` and never assume only one tenant exists. The OSS deployment's fresh-install rows are owned by `Cvoya.Spring.Core.Tenancy.OssTenantIds.Default` (a deterministic v5 UUID; see [`docs/architecture/data-and-identity.md`](docs/architecture/data-and-identity.md)) — reference the constant from code, never the literal string. New persisted entities that should be tenant-scoped must implement `ITenantScopedEntity` so the cloud host can enforce isolation through its scoped overrides.
52-
- **Don't make services static or use singletons outside DI.** Everything must go through the container so the private repo can control lifetime and scoping.
53-
- **Don't create internal types that the private repo would need to access.** If a type is part of the extension contract, make it `public`. Use `internal` only for true implementation details.
54-
- **Don't reference private repo issues, PRs, or branches.** It is fine to acknowledge that a Spring Voyage hosted service exists, but do not link to or create dependencies on `cvoya-com/spring` issues or PRs from this repo. The dependency direction is one-way: the private repo may reference this repo's work, not the reverse.
55-
- **Every design decision must account for extensibility.** The private repo extends, overrides, or composes OSS behaviour cleanly — without forking, patching, or working around limitations. Treat this repo as a framework that consumers use.
56-
57-
### Extension model
58-
59-
- **Tenant-aware overrides.** The OSS core models tenancy through `ITenantContext` and ships a single-tenant default. The cloud host swaps in a scoped implementation. OSS code must not assume a single tenant or hardcode the default tenant id.
60-
- **DI overrides.** The cloud host replaces OSS service registrations with tenant-aware implementations using `TryAdd*`-friendly registration on the OSS side.
61-
- **Plugin contracts.** Implement `IAgentRuntime` (LLM backend + execution tool + credential schema + model catalogue) or `IConnectorType` (external-system binding) and register via DI; the host picks new implementations up without core changes.
62-
- **Cloud API host.** Layers middleware (auth, tenant context) on top of the OSS API host.
63-
64-
### Design principles for extensibility
65-
66-
1. **Interface-first.** Define interfaces in `Cvoya.Spring.Core`, implement in `Cvoya.Spring.Dapr`. Alternative implementations slot in via DI.
67-
2. **`TryAdd*` for DI registrations.** Downstream consumers register their own implementations before calling `AddCvoyaSpring*()`, and OSS registrations don't overwrite them.
68-
3. **Don't seal extensible types.** Services, handlers, strategies, middleware are not `sealed` unless leaf-only.
69-
4. **Composition over inheritance.** Inject collaborators; extend by wrapping or decorating.
70-
5. **No hardcoded single-tenant assumptions.** Use injected services for anything the cloud might scope per tenant.
71-
6. **Virtual hooks on base classes.** Make template methods on `*Base` classes `virtual`.
72-
7. **`Cvoya.Spring.Core` stays dependency-free.** Domain abstractions only — zero NuGet packages.
73-
8. **Extension-point checklist** for new features:
74-
- Can the cloud swap this implementation via DI? → interface.
75-
- Can it extend behaviour? → decorator/wrapper or virtual methods.
76-
- Does it assume a single deployment context? → parameterise via DI.
77-
78-
### Plugins (agent runtimes and connectors)
79-
80-
Agent runtimes and connectors are first-class plugins. Each ships as its own `Cvoya.Spring.AgentRuntimes.<Name>` or `Cvoya.Spring.Connector.<Name>` project, references only what its contract demands, and registers via a single `AddCvoyaSpring<Kind><Name>()` DI extension. Host-side code references the abstraction only — the registry, install surface, and bootstrap pick up new plugins automatically. Per-project READMEs document each runtime/connector's contract; see also `CONVENTIONS.md` § "Agent Runtimes and Connectors Are Plugins".
81-
82-
## Operator surfaces — relaxation of UI/CLI parity
83-
84-
Operational surfaces (agent-runtime config, connector config, credential health, tenant seeds, skill-bundle bindings) are **CLI-only by design**. The portal MAY expose **read-only** views for visibility, but every mutation goes through the `spring` CLI.
85-
86-
User-facing features remain strictly parity-bound — see [`CONVENTIONS.md`](CONVENTIONS.md) § "UI / CLI Feature Parity".
87-
88-
## Agents, Sub-agents, concurrent agents
89-
90-
Multiple coding agents work on this codebase simultaneously.
91-
92-
Rules:
93-
94-
- Every PR must be developed in a dedicated worktree — create one before starting any code work. Never work directly in the main checkout. Other agent processes may be active concurrently and so making changes to the main worktree might result into conflicts or agents tripping on each other's work.
95-
- Small, focused PRs — one issue per PR unless instructed to combine issues into one.
96-
- Rebase onto `main` before merging.
97-
- When adding to shared files (`StateKeys`, DI registrations, enums) — append to the end.
98-
- File follow-up issues before the PR lands and reference concrete numbers in the PR body. Prose-only "we'll file it later" routinely drops follow-ups on the floor.
99-
100-
## Repository Configuration
101-
102-
- `.claude/settings.local.json` is gitignored and is the correct place for user-specific tooling (MCP servers, design tools, personal preferences). Do not add user-specific tool configuration to committed repo files (`settings.json`, agent definitions, or CLAUDE.md). Repo-level config should reflect project requirements shared by all contributors, not individual workflow preferences.
57+
User-specific tools and MCP servers belong in `.claude/settings.local.json` or user configuration, not committed project settings, agent definitions, or this file.

CLAUDE.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
# Spring Voyage — Claude Code Configuration
1+
# Claude Code
22

3-
Read [`AGENTS.md`](AGENTS.md) and [`CONVENTIONS.md`](CONVENTIONS.md). Both are mandatory.
4-
5-
## Plans
6-
7-
Release plans live under [`docs/plan/`](docs/plan/), one subdirectory per release. Each subdirectory contains a `README.md` with the plan-of-record for that release. When working on a task, find the current release directory and use its `README.md` as the authoritative plan.
3+
@AGENTS.md
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
# Keep always-loaded agent instructions within the repository context budget.
3+
set -uo pipefail
4+
5+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
6+
fail=0
7+
8+
check_size() {
9+
local rel="$1" limit="$2" bytes
10+
if [ ! -f "$ROOT/$rel" ]; then
11+
printf 'FAIL: missing %s\n' "$rel" >&2
12+
fail=1
13+
return
14+
fi
15+
bytes="$(wc -c <"$ROOT/$rel" | tr -d '[:space:]')"
16+
if [ "$bytes" -gt "$limit" ]; then
17+
printf 'FAIL: %s is %s bytes; context budget is %s bytes\n' "$rel" "$bytes" "$limit" >&2
18+
fail=1
19+
else
20+
printf 'PASS: %s context budget (%s/%s bytes)\n' "$rel" "$bytes" "$limit"
21+
fi
22+
}
23+
24+
check_size AGENTS.md 8192
25+
check_size CLAUDE.md 512
26+
27+
if ! grep -Fxq '@AGENTS.md' "$ROOT/CLAUDE.md"; then
28+
printf 'FAIL: CLAUDE.md must import the canonical AGENTS.md with @AGENTS.md\n' >&2
29+
fail=1
30+
fi
31+
32+
legacy=""
33+
if [ -d "$ROOT/.claude/rules" ]; then
34+
legacy="$(grep -R -n --include='*.md' '^globs:' "$ROOT/.claude/rules" 2>/dev/null || true)"
35+
fi
36+
if [ -n "$legacy" ]; then
37+
printf 'FAIL: Claude rules must use lazy paths: frontmatter, not legacy globs:\n%s\n' "$legacy" >&2
38+
fail=1
39+
fi
40+
41+
[ "$fail" -eq 0 ]

eng/agent-hooks/hooks.test.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
set -u
55
cd "$(dirname "${BASH_SOURCE[0]}")"
66

7+
bash context-budget.test.sh || exit 1
8+
79
if ! command -v jq >/dev/null 2>&1; then
810
printf 'hooks.test.sh SKIPPED: jq is not installed, so the guards fail open here.\n'
911
exit 0

0 commit comments

Comments
 (0)