design: per-driver context-management spec + role-aware three-tier (proactive/immediate/peripheral) context probe #373

Closed
opened 2026-06-26 07:21:17 +00:00 by coilysiren · 5 comments
Owner

Motivation

Low-capability drivers (the qwen trivial tier, any small local model, a degraded
remote) fail not only when context is missing but when it is too much.
Three tiers of context load a driver, and each drowns a weak model differently:

  • proactively presented - the eager bytes in the prompt at session start
    (composed AGENTS/CLAUDE doctrine, every mounted skill's frontmatter, the
    statusline, the issue-corpus index, the front-load seed block).
  • immediately available - the working-dir clone under /workspace/<name>,
    one tool call away. A 3k-file repo is a real grep/attention surface even though
    it is not in the prompt.
  • peripherally available - the /substrate/<name> reference repos (8 today)
    the agent can grep without leaving its box. Cheap to provide, not free to a
    small model that now has a much larger haystack to pick wrongly from.

The thesis: a low-capability warded driver is fundamentally incapable of its
task if any tier is too heavy, so each tier must be curated per driver. We want a
programmatic, agent-runnable probe that spins (or composes) a warded role and
reports all three tiers with numbers, plus a per-driver context-management
spec
that declares and enforces the budget. This issue is design-first:
propose the probe architecture and the spec schema, do not lock a schema yet.

What already exists (do not rebuild)

  • check_context_budget (agentic-os, ward context-budget,
    agentic_os/pre_commit/check_context_budget.py, docs/context-budget.md).
    Measures the proactive axis only - composed doc + skill frontmatter + an
    mcp server-count note - per harness (claude/codex/opencode), static,
    host-side, chars/4 proxy, with --check for CI and per-harness budgets.
  • agent-adapters.yaml per-agent contextLevel: 2|1|0 (full/scoped/minimal),
    the WARD_CONTEXT_LEVEL ladder the entrypoint composes against
    (docs/agent-adapter-manifest.md). This is the only per-driver context
    knob today: one integer, not a spec.
  • substrate manifest (preclone-repos.txt, 8 repos) - the peripheral tier.
  • agentic-os#269 - the open consult seed: "investigate how much surrounding
    context commands like goose ask use." This issue is the per-role, three-tier
    generalization of that seed.

The gap

  1. Per-harness, not per-role. check_context_budget measures claude vs codex
    vs opencode. It does not model the warded roles (engineer / advisor /
    director / architect), which differ in WARD_CONTEXT_LEVEL composition and
    in live container-only additions (the container doctrine overlay, the architect
    read-only section, the issue-corpus index ward#367, the statusline ward#294,
    the front-load seed block). The user explicitly wants engineer / consultant
    (advisor) / director measured as they actually land in the container.
  2. Proactive axis only. Nothing measures the immediately-available
    (working-dir clone: file count, bytes, token-proxy) or peripherally-available
    (substrate: repo count, total grep surface) tiers, which the thesis says matter
    most for weak drivers.
  3. contextLevel is one int, not a spec. There is no per-driver declaration
    of a budget across the three tiers, and no enforcement.

Proposed shape (design these, do not lock yet)

  • Role-aware three-tier probe. For a given role+driver, compose (or spin) the
    container and report: proactive (extend/reuse check_context_budget's doc+skill
    accounting plus the container-only additions), immediately-available (walk
    /workspace/<name>: tracked-file count, bytes, token-proxy), peripherally-
    available (walk /substrate/<name>: per-repo and total). Programmatic,
    --check-able, agent-runnable. Spin-real-container vs static-composition is an
    open design fork - state the trade and pick.
  • Per-driver context-management spec. Propose extending agent-adapters.yaml
    from a single contextLevel int to a per-tier budget block (proactive /
    immediate / peripheral), so each driver declares its ceiling and the probe's
    --check enforces it. The first schema instance sets the pattern everything
    copies - propose it, surface it for Kai to shape, do not autonomously commit a
    locked schema.
  • Authoring-vs-rollout split (per aos doctrine): the measurement is
    authored in agentic-os (extend check_context_budget with the working-dir +
    substrate tiers, measurement-only, reversible). The role-spin probe + the
    spec schema
    live in ward, which owns roles, containers, substrate, and
    WARD_CONTEXT_LEVEL. Cross-file an agentic-os companion for the measurement leg.

Acceptance (design-first)

  • A design proposal (comment or docs/<feature>.md) covering: the probe
    architecture (real-spin vs compose), the per-tier accounting for each role, the
    proposed per-driver spec schema, and the agentic-os/ward split.
  • The proposal names the open forks for Kai (spec schema shape, real-spin vs
    static, where --check enforcement lives) rather than silently resolving them.
  • No locked schema committed to agent-adapters.yaml until Kai blesses it.

Non-goals

  • A real qwen tokenizer (the chars/4 proxy is fine for a zero-sum comparison).
  • Runtime/accumulated context (this measures init load, not sweep blow-out).

Filed from a read-only architect session (design walk on per-driver context
curation). Generalizes agentic-os#269; builds on docs/context-budget.md.

## Motivation Low-capability drivers (the qwen trivial tier, any small local model, a degraded remote) fail not only when context is **missing** but when it is **too much**. Three tiers of context load a driver, and each drowns a weak model differently: - **proactively presented** - the eager bytes in the prompt at session start (composed AGENTS/CLAUDE doctrine, every mounted skill's frontmatter, the statusline, the issue-corpus index, the front-load seed block). - **immediately available** - the working-dir clone under `/workspace/<name>`, one tool call away. A 3k-file repo is a real grep/attention surface even though it is not in the prompt. - **peripherally available** - the `/substrate/<name>` reference repos (8 today) the agent can grep without leaving its box. Cheap to provide, not free to a small model that now has a much larger haystack to pick wrongly from. The thesis: a low-capability warded driver is **fundamentally incapable** of its task if any tier is too heavy, so each tier must be curated per driver. We want a **programmatic, agent-runnable probe** that spins (or composes) a warded role and reports all three tiers with numbers, plus a **per-driver context-management spec** that declares and enforces the budget. This issue is **design-first**: propose the probe architecture and the spec schema, do not lock a schema yet. ## What already exists (do not rebuild) - **`check_context_budget`** (agentic-os, `ward context-budget`, `agentic_os/pre_commit/check_context_budget.py`, `docs/context-budget.md`). Measures the **proactive** axis only - composed doc + skill frontmatter + an mcp server-count note - **per harness** (claude/codex/opencode), static, host-side, chars/4 proxy, with `--check` for CI and per-harness budgets. - **`agent-adapters.yaml`** per-agent `contextLevel: 2|1|0` (full/scoped/minimal), the `WARD_CONTEXT_LEVEL` ladder the entrypoint composes against (`docs/agent-adapter-manifest.md`). This is the **only** per-driver context knob today: one integer, not a spec. - **substrate manifest** (`preclone-repos.txt`, 8 repos) - the peripheral tier. - **agentic-os#269** - the open consult seed: "investigate how much surrounding context commands like goose ask use." This issue is the per-role, three-tier generalization of that seed. ## The gap 1. **Per-harness, not per-role.** `check_context_budget` measures claude vs codex vs opencode. It does **not** model the warded **roles** (engineer / advisor / director / architect), which differ in `WARD_CONTEXT_LEVEL` composition **and** in live container-only additions (the container doctrine overlay, the architect read-only section, the issue-corpus index ward#367, the statusline ward#294, the front-load seed block). The user explicitly wants engineer / consultant (advisor) / director measured **as they actually land in the container**. 2. **Proactive axis only.** Nothing measures the **immediately-available** (working-dir clone: file count, bytes, token-proxy) or **peripherally-available** (substrate: repo count, total grep surface) tiers, which the thesis says matter most for weak drivers. 3. **`contextLevel` is one int, not a spec.** There is no per-driver declaration of a budget across the three tiers, and no enforcement. ## Proposed shape (design these, do not lock yet) - **Role-aware three-tier probe.** For a given role+driver, compose (or spin) the container and report: proactive (extend/reuse `check_context_budget`'s doc+skill accounting plus the container-only additions), immediately-available (walk `/workspace/<name>`: tracked-file count, bytes, token-proxy), peripherally- available (walk `/substrate/<name>`: per-repo and total). Programmatic, `--check`-able, agent-runnable. Spin-real-container vs static-composition is an open design fork - state the trade and pick. - **Per-driver context-management spec.** Propose extending `agent-adapters.yaml` from a single `contextLevel` int to a per-tier budget block (proactive / immediate / peripheral), so each driver declares its ceiling and the probe's `--check` enforces it. **The first schema instance sets the pattern everything copies - propose it, surface it for Kai to shape, do not autonomously commit a locked schema.** - **Authoring-vs-rollout split** (per aos doctrine): the **measurement** is authored in agentic-os (extend `check_context_budget` with the working-dir + substrate tiers, measurement-only, reversible). The **role-spin probe + the spec schema** live in ward, which owns roles, containers, substrate, and `WARD_CONTEXT_LEVEL`. Cross-file an agentic-os companion for the measurement leg. ## Acceptance (design-first) - A design proposal (comment or `docs/<feature>.md`) covering: the probe architecture (real-spin vs compose), the per-tier accounting for each role, the proposed per-driver spec schema, and the agentic-os/ward split. - The proposal names the open forks for Kai (spec schema shape, real-spin vs static, where `--check` enforcement lives) rather than silently resolving them. - No locked schema committed to `agent-adapters.yaml` until Kai blesses it. ## Non-goals - A real qwen tokenizer (the chars/4 proxy is fine for a zero-sum comparison). - Runtime/accumulated context (this measures **init** load, not sweep blow-out). Filed from a read-only architect session (design walk on per-driver context curation). Generalizes agentic-os#269; builds on `docs/context-budget.md`.
Author
Owner

🔎 ward agent advisor

ward agent advisor --driver claude ran a one-shot standard research pass on this question:

Produce the design proposal this issue asks for: the three-tier (proactive/immediate/peripheral) role-aware context probe architecture; the proposed per-driver context-management spec schema as an extension of agent-adapters.yaml's contextLevel; the real-spin-container vs static-compose trade; and the agentic-os (measurement extends check_context_budget) vs ward (role-spin probe + spec schema) authoring split. Name the open forks for Kai and do NOT commit a locked schema - post the proposal to the issue thread as a comment.


Now I have enough grounding. The key structural fact I confirmed: contextLevel is keyed per-driver (claude=2, codex=1, qwen=0, goose=2), while the actually-composed context is per-role (the entrypoint prepends AGENTS.container.md, conditionally appends doctrine by level, and injects the read-only block for architect). That driver-vs-role asymmetry is the crux the schema has to resolve. Writing the proposal.

Design: role-aware three-tier context probe + per-driver context-management spec

This is a design walk, not an implementation. It proposes the probe architecture, a candidate spec schema, the real-spin vs static-compose trade, and the agentic-os/ward authoring split. It deliberately leaves the schema unlocked and surfaces the open forks at the end for Kai to shape.

The crux nobody has named yet: driver-keyed knob, role-keyed load

The single most important finding from the current wiring:

  • contextLevel is keyed per-driver. In agent-adapters.yaml it is one int per harness (claude=2, codex=1, qwen=0, goose=2). It is the driver's declared appetite.
  • The composed context is keyed per-role. entrypoint.sh:compose_context always prepends AGENTS.container.md, then appends host doctrine gated on the level, then injects the read-only architect block when WARD_READONLY=1. So the same driver carries materially different proactive bytes in engineer vs architect, before you even count the container-only additions (issue-corpus index ward#367, statusline ward#294, front-load seed).

So the load the thesis cares about is a (role x driver) cell, but the only ceiling we can declare today is a driver column. Any spec has to decide how a per-driver ceiling is checked against a per-role load. That decision is the schema, and it is fork #1 below. Everything else is plumbing.

The three tiers, as they actually land in the container

  • proactive - the composed ~/.claude/CLAUDE.md (for goose, the mirrored .goosehints) plus every mounted skill's frontmatter plus the statusline plus the issue-corpus index plus the front-load seed. This is the only tier that is literally bytes-in-the-prompt, so it is the only one where a token-proxy is a true "loaded" number. check_context_budget already measures the doc+skill+mcp slice of this per harness, statically, host-side. The gap is the container-only additions (the AGENTS.container.md overlay, the read-only block, ward#367, ward#294, the seed) which only exist once the container composes.
  • immediate - the /workspace/<target> clone plus any --repo-granted working copies. Not in the prompt, one tool call away. The honest unit here is haystack size: tracked-file count and total bytes, with a token-proxy as a secondary number. A 3k-file repo is a real attention surface for a weak driver even at zero prompt cost.
  • peripheral - the /substrate/<name> references. The manifest is 8 repos today, minus the target when the target is also on the manifest (the entrypoint skips re-warming it). Same unit as immediate (repo count, total grep surface), one notch cheaper to provide and one notch easier for a small model to pick wrongly from.

A clean way to think about the units: proactive is measured in loaded tokens (hard, prompt-resident), immediate and peripheral are measured in reachable surface (file count + bytes, token-proxy advisory). Conflating them into one "token" number is a trap, because 600k tokens of greppable substrate is not remotely the same load as 6k tokens of prompt. The spec should keep the units distinct.

Probe architecture

A single agent-runnable entrypoint, proposed surface ward context-probe --role <r> --driver <d> [--check], emitting a structured report per tier:

role=architect driver=qwen
  proactive    tokens≈ 7,420   (doctrine 5,1k + skills 1,2k + readonly 0,9k + statusline 0,2k)
  immediate    files=312  bytes=1,8M  tokens≈ 460k   (/workspace/ward)
  peripheral   repos=7    bytes=22M   tokens≈ 5,4M   (/substrate, target excluded)

The report is the deliverable; --check compares each tier against the spec's ceiling for that driver and exits non-zero on breach. The accounting primitives (count tracked files, sum bytes, chars/4 token-proxy, parse skill frontmatter) are the same ones check_context_budget already uses for the proactive doc - the probe reuses them and points them at three roots instead of one.

The fork that gates everything else: real-spin vs static-compose

  • Static-compose - replicate compose_context's logic host-side and walk a clone, the way check_context_budget already models the proactive doc without a container. Fast, no docker, CI/pre-commit friendly, deterministic. Cost: it must re-implement the bash composition and every container-only addition, so it drifts the moment entrypoint.sh or ward#367/ward#294 changes, and it cannot truthfully measure immediate/peripheral without actually cloning the workspace and warming substrate (at which point it is not really "static" anymore).
  • Real-spin - ward context-probe boots the actual container through the real entrypoint with WARD_CONTEXT_LEVEL and WARD_READONLY set for the role, then a probe step measures the real ~/.claude/CLAUDE.md, the real /workspace, the real /substrate. Truthful by construction - it measures the artifact, not a model of it, so it captures every container-only addition for free and never drifts. Cost: needs the docker socket, the gitcache/network for substrate warm, is slower, and is non-deterministic across runs as substrate repos grow.

Recommendation: hybrid, with real-spin as the source of truth. ward already owns container spin, so the real-spin probe is cheap for ward to build and is the only thing that measures immediate/peripheral honestly. Keep the existing fast static path in check_context_budget for the proactive tier only, as the pre-commit-speed gate that catches doctrine bloat on every push. Run the full three-tier real-spin probe on a docker-capable CI lane or cron, not in pre-commit. Stating the trade plainly: two measurement paths means the static proactive number can disagree with the real one (it will under-count by exactly the container-only additions), so the static path must be documented as a lower-bound smoke check, not the authority. Whether Kai accepts two paths or forces one is fork #2.

Candidate spec schema (NOT for commit - shape this, Kai)

Extend each agent-adapters.yaml entry with an optional context block. Keep contextLevel exactly as-is so nothing breaks and the composition ladder is untouched - the new block is a ceiling the probe enforces, not a new composition input.

  - name: qwen
    binary: opencode
    contextLevel: 0
    context:
      budget:
        proactive:  { tokens: 8000 }                 # hard: prompt-resident
        immediate:  { files: 500,  tokens: 500000 }  # reachable surface
        peripheral: { repos: 3,    tokens: 800000 }  # reachable surface

Design intent baked into this shape, each a thing to accept or reject:

  • One ceiling per driver, checked against every role. The driver is the capability constraint, so the budget lives on the driver row. The probe evaluates each (role, driver) cell against this single column and flags any role whose composed load exceeds it. This keeps the schema flat and matches where contextLevel already lives. The alternative - a full (role x driver) matrix - is more precise but multiplies the surface 4x and is the heavier first-pattern to copy.
  • Distinct units per tier. proactive in tokens (real, loaded), immediate/peripheral in files and/or token-proxy (reachable). Mixing them into one number is the trap above.
  • Optional block. A driver with no context: block is unbudgeted (today's behavior), so adoption is incremental and claude/goose can stay unbudgeted while qwen gets a tight one first.

I am deliberately not committing this. The issue is right that the first schema instance sets the pattern everything copies, and the per-driver-vs-matrix choice (fork #1) changes the shape fundamentally.

Authoring-vs-rollout split (per aos doctrine)

  • agentic-os owns the measurement primitives. Extend check_context_budget / agentic_os/pre_commit/check_context_budget.py so the tier-accounting functions (tracked-file count, byte sum, token-proxy, frontmatter parse) are reusable against an arbitrary path, not just the composed doc. This is measurement-only, reversible, host-side, and stays in the catalog where validators live. It does not learn about ward's roles or containers. A companion issue cross-files this leg into agentic-os.
  • ward owns the orchestration and the spec. ward knows the roles, owns compose_context, the /workspace clone, the substrate warm, and WARD_CONTEXT_LEVEL. So the role-spin probe (ward context-probe), the schema in agent-adapters.yaml, and the --check enforcement live here. ward calls the aos primitives and points them at the three real roots.

This is the same split the manifest comment already describes: aos publishes the validator, ward embeds and orchestrates. The probe is "how to measure a tier" (aos) wired to "what the tiers are and what budget to enforce" (ward).

One scope flag that falls out of the peripheral tier: the thesis says each tier must be curated per driver, but /substrate is one shared preclone-repos.txt for all drivers today. A peripheral budget number only measures and warns. Actually curating peripheral per-driver means a driver-aware substrate manifest (a qwen run warms 3 repos, a claude run warms 8), which is a bigger change than this issue's measurement scope and should be its own ticket. This design covers measure-and-enforce-a-ceiling, not driver-filtered substrate seeding.

Open forks for Kai (resolve before any commit)

  1. Spec shape. Per-driver single ceiling checked across all roles (proposed, flat) vs full (role x driver) matrix vs per-driver with per-role overrides. And the unit for immediate/peripheral: file-count, token-proxy, or grep-surface bytes - and are those tiers hard --check failures or warn-only while only proactive blocks.
  2. Probe authority. Hybrid with real-spin authoritative + static proactive smoke check (recommended) vs forcing a single path. If hybrid, do we accept that the static and real proactive numbers legitimately differ by the container-only additions.
  3. Where --check gates. Fast static proactive in pre-commit, full three-tier real-spin in a docker-capable CI/cron lane, or both. And the sharper question: does a budget breach only fail CI, or does the entrypoint refuse to run a driver in a role whose composed load exceeds that driver's ceiling at runtime.
  4. Relationship to contextLevel. Does the budget block coexist with contextLevel (level composes, budget enforces - proposed) or eventually subsume it (derive the int from the budget).
  5. Peripheral curation scope. Is per-driver substrate curation (a driver-filtered preclone-repos.txt) in scope, or does this issue stop at measure-and-budget and curation gets its own ticket (recommended).

Acceptance check against the issue

This proposal gives the probe architecture (real-spin authoritative, static proactive smoke check), the per-tier accounting for each role as it actually lands in the container, a candidate per-driver schema presented unlocked, and the aos/ward split with a companion cross-file named. The forks above are surfaced for Kai rather than silently resolved, and no schema is committed to agent-adapters.yaml.


Researched and posted automatically by ward agent advisor --driver claude (ward#179). This is one-shot research, not a carried change - verify before acting on it.

— Claude (she/her), via ward agent

### 🔎 ward agent advisor `ward agent advisor --driver claude` ran a one-shot **standard** research pass on this question: > Produce the design proposal this issue asks for: the three-tier (proactive/immediate/peripheral) role-aware context probe architecture; the proposed per-driver context-management spec schema as an extension of agent-adapters.yaml's contextLevel; the real-spin-container vs static-compose trade; and the agentic-os (measurement extends check_context_budget) vs ward (role-spin probe + spec schema) authoring split. Name the open forks for Kai and do NOT commit a locked schema - post the proposal to the issue thread as a comment. --- Now I have enough grounding. The key structural fact I confirmed: `contextLevel` is keyed **per-driver** (claude=2, codex=1, qwen=0, goose=2), while the actually-composed context is **per-role** (the entrypoint prepends `AGENTS.container.md`, conditionally appends doctrine by level, and injects the read-only block for architect). That driver-vs-role asymmetry is the crux the schema has to resolve. Writing the proposal. # Design: role-aware three-tier context probe + per-driver context-management spec This is a design walk, not an implementation. It proposes the probe architecture, a candidate spec schema, the real-spin vs static-compose trade, and the agentic-os/ward authoring split. It deliberately leaves the schema unlocked and surfaces the open forks at the end for Kai to shape. ## The crux nobody has named yet: driver-keyed knob, role-keyed load The single most important finding from the current wiring: - **`contextLevel` is keyed per-driver.** In `agent-adapters.yaml` it is one int per harness (claude=2, codex=1, qwen=0, goose=2). It is the driver's declared appetite. - **The composed context is keyed per-role.** `entrypoint.sh:compose_context` always prepends `AGENTS.container.md`, then appends host doctrine gated on the level, then injects the read-only architect block when `WARD_READONLY=1`. So the **same driver** carries materially different proactive bytes in `engineer` vs `architect`, before you even count the container-only additions (issue-corpus index ward#367, statusline ward#294, front-load seed). So the load the thesis cares about is a **(role x driver) cell**, but the only ceiling we can declare today is a **driver column**. Any spec has to decide how a per-driver ceiling is checked against a per-role load. That decision is the schema, and it is fork #1 below. Everything else is plumbing. ## The three tiers, as they actually land in the container * **proactive** - the composed `~/.claude/CLAUDE.md` (for goose, the mirrored `.goosehints`) plus every mounted skill's frontmatter plus the statusline plus the issue-corpus index plus the front-load seed. This is the only tier that is literally bytes-in-the-prompt, so it is the only one where a token-proxy is a true "loaded" number. `check_context_budget` already measures the doc+skill+mcp slice of this **per harness**, statically, host-side. The gap is the **container-only additions** (the `AGENTS.container.md` overlay, the read-only block, ward#367, ward#294, the seed) which only exist once the container composes. * **immediate** - the `/workspace/<target>` clone plus any `--repo`-granted working copies. Not in the prompt, one tool call away. The honest unit here is **haystack size**: tracked-file count and total bytes, with a token-proxy as a secondary number. A 3k-file repo is a real attention surface for a weak driver even at zero prompt cost. * **peripheral** - the `/substrate/<name>` references. The manifest is 8 repos today, minus the target when the target is also on the manifest (the entrypoint skips re-warming it). Same unit as immediate (repo count, total grep surface), one notch cheaper to provide and one notch easier for a small model to pick wrongly from. A clean way to think about the units: proactive is measured in **loaded tokens** (hard, prompt-resident), immediate and peripheral are measured in **reachable surface** (file count + bytes, token-proxy advisory). Conflating them into one "token" number is a trap, because 600k tokens of greppable substrate is not remotely the same load as 6k tokens of prompt. The spec should keep the units distinct. ## Probe architecture A single agent-runnable entrypoint, proposed surface `ward context-probe --role <r> --driver <d> [--check]`, emitting a structured report per tier: ``` role=architect driver=qwen proactive tokens≈ 7,420 (doctrine 5,1k + skills 1,2k + readonly 0,9k + statusline 0,2k) immediate files=312 bytes=1,8M tokens≈ 460k (/workspace/ward) peripheral repos=7 bytes=22M tokens≈ 5,4M (/substrate, target excluded) ``` The report is the deliverable; `--check` compares each tier against the spec's ceiling for that driver and exits non-zero on breach. The accounting primitives (count tracked files, sum bytes, chars/4 token-proxy, parse skill frontmatter) are the **same** ones `check_context_budget` already uses for the proactive doc - the probe reuses them and points them at three roots instead of one. ## The fork that gates everything else: real-spin vs static-compose * **Static-compose** - replicate `compose_context`'s logic host-side and walk a clone, the way `check_context_budget` already models the proactive doc without a container. Fast, no docker, CI/pre-commit friendly, deterministic. Cost: it must **re-implement** the bash composition and every container-only addition, so it drifts the moment `entrypoint.sh` or ward#367/ward#294 changes, and it cannot truthfully measure immediate/peripheral without actually cloning the workspace and warming substrate (at which point it is not really "static" anymore). * **Real-spin** - `ward context-probe` boots the actual container through the real entrypoint with `WARD_CONTEXT_LEVEL` and `WARD_READONLY` set for the role, then a probe step measures the real `~/.claude/CLAUDE.md`, the real `/workspace`, the real `/substrate`. Truthful by construction - it measures the artifact, not a model of it, so it captures every container-only addition for free and never drifts. Cost: needs the docker socket, the gitcache/network for substrate warm, is slower, and is non-deterministic across runs as substrate repos grow. **Recommendation: hybrid, with real-spin as the source of truth.** ward already owns container spin, so the real-spin probe is cheap for ward to build and is the only thing that measures immediate/peripheral honestly. Keep the existing fast static path in `check_context_budget` for the **proactive tier only**, as the pre-commit-speed gate that catches doctrine bloat on every push. Run the full three-tier real-spin probe on a docker-capable CI lane or cron, not in pre-commit. Stating the trade plainly: two measurement paths means the static proactive number can disagree with the real one (it will under-count by exactly the container-only additions), so the static path must be documented as a **lower-bound smoke check**, not the authority. Whether Kai accepts two paths or forces one is fork #2. ## Candidate spec schema (NOT for commit - shape this, Kai) Extend each `agent-adapters.yaml` entry with an optional `context` block. Keep `contextLevel` exactly as-is so nothing breaks and the composition ladder is untouched - the new block is a **ceiling the probe enforces**, not a new composition input. ```yaml - name: qwen binary: opencode contextLevel: 0 context: budget: proactive: { tokens: 8000 } # hard: prompt-resident immediate: { files: 500, tokens: 500000 } # reachable surface peripheral: { repos: 3, tokens: 800000 } # reachable surface ``` Design intent baked into this shape, each a thing to accept or reject: * **One ceiling per driver, checked against every role.** The driver is the capability constraint, so the budget lives on the driver row. The probe evaluates each `(role, driver)` cell against this single column and flags any role whose composed load exceeds it. This keeps the schema flat and matches where `contextLevel` already lives. The alternative - a full `(role x driver)` matrix - is more precise but multiplies the surface 4x and is the heavier first-pattern to copy. * **Distinct units per tier.** `proactive` in tokens (real, loaded), `immediate`/`peripheral` in `files` and/or token-proxy (reachable). Mixing them into one number is the trap above. * **Optional block.** A driver with no `context:` block is unbudgeted (today's behavior), so adoption is incremental and claude/goose can stay unbudgeted while qwen gets a tight one first. I am deliberately **not** committing this. The issue is right that the first schema instance sets the pattern everything copies, and the per-driver-vs-matrix choice (fork #1) changes the shape fundamentally. ## Authoring-vs-rollout split (per aos doctrine) * **agentic-os owns the measurement primitives.** Extend `check_context_budget` / `agentic_os/pre_commit/check_context_budget.py` so the tier-accounting functions (tracked-file count, byte sum, token-proxy, frontmatter parse) are reusable against an arbitrary path, not just the composed doc. This is measurement-only, reversible, host-side, and stays in the catalog where validators live. It does **not** learn about ward's roles or containers. A companion issue cross-files this leg into agentic-os. * **ward owns the orchestration and the spec.** ward knows the roles, owns `compose_context`, the `/workspace` clone, the substrate warm, and `WARD_CONTEXT_LEVEL`. So the role-spin probe (`ward context-probe`), the schema in `agent-adapters.yaml`, and the `--check` enforcement live here. ward calls the aos primitives and points them at the three real roots. This is the same split the manifest comment already describes: aos publishes the validator, ward embeds and orchestrates. The probe is "how to measure a tier" (aos) wired to "what the tiers are and what budget to enforce" (ward). One scope flag that falls out of the peripheral tier: the thesis says each tier must be **curated** per driver, but `/substrate` is one shared `preclone-repos.txt` for all drivers today. A peripheral *budget number* only measures and warns. Actually *curating* peripheral per-driver means a **driver-aware substrate manifest** (a qwen run warms 3 repos, a claude run warms 8), which is a bigger change than this issue's measurement scope and should be its own ticket. This design covers measure-and-enforce-a-ceiling, not driver-filtered substrate seeding. ## Open forks for Kai (resolve before any commit) 1. **Spec shape.** Per-driver single ceiling checked across all roles (proposed, flat) vs full `(role x driver)` matrix vs per-driver with per-role overrides. And the unit for immediate/peripheral: file-count, token-proxy, or grep-surface bytes - and are those tiers hard `--check` failures or warn-only while only proactive blocks. 2. **Probe authority.** Hybrid with real-spin authoritative + static proactive smoke check (recommended) vs forcing a single path. If hybrid, do we accept that the static and real proactive numbers legitimately differ by the container-only additions. 3. **Where `--check` gates.** Fast static proactive in pre-commit, full three-tier real-spin in a docker-capable CI/cron lane, or both. And the sharper question: does a budget breach only fail CI, or does the **entrypoint refuse** to run a driver in a role whose composed load exceeds that driver's ceiling at runtime. 4. **Relationship to `contextLevel`.** Does the budget block coexist with `contextLevel` (level composes, budget enforces - proposed) or eventually subsume it (derive the int from the budget). 5. **Peripheral curation scope.** Is per-driver substrate *curation* (a driver-filtered `preclone-repos.txt`) in scope, or does this issue stop at measure-and-budget and curation gets its own ticket (recommended). ## Acceptance check against the issue This proposal gives the probe architecture (real-spin authoritative, static proactive smoke check), the per-tier accounting for each role as it actually lands in the container, a candidate per-driver schema presented unlocked, and the aos/ward split with a companion cross-file named. The forks above are surfaced for Kai rather than silently resolved, and no schema is committed to `agent-adapters.yaml`. --- Researched and posted automatically by `ward agent advisor --driver claude` (ward#179). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Member

Canonical for #372 (merged). Flipped headless: agent drafts the per-driver context-management spec + three-tier probe design as a doc PR; Kai reviews there. Recorded by Claude Code (Fable) during the 2026-07-01 ward launch triage session with Kai.

Canonical for #372 (merged). Flipped headless: agent drafts the per-driver context-management spec + three-tier probe design as a doc PR; Kai reviews there. Recorded by Claude Code (Fable) during the 2026-07-01 ward launch triage session with Kai.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-373 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-03T06:24:44Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-373` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-03T06:24:44Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

Design proposal: role-aware three-tier context probe + per-driver spec

Landed the design as docs/context-probe.md (kept under the repo's 80-line/4000-char doc cap). This comment carries the fuller accounting the doc points back to. No schema is committed - the fleet KDL is untouched; the block below is proposed for you to shape.

The three tiers, precisely

  • Proactive (eager prompt bytes) - composed by compose_context in entrypoint.sh: AGENTS.container.md always, then per WARD_CONTEXT_LEVEL (L2 = target CLAUDE.md+AGENTS.md, L1 = AGENTS.md, L0 = nothing), then role overlays (read-only block, front-load seed, statusline, issue-corpus index), plus mounted-skill frontmatter. Measure: chars/4, reusing check_context_budget's doc+skill accounting and adding the container-only overlays that never exist in the host-side load point.
  • Immediate (working clone) - /workspace/<name>: git ls-files walk -> tracked-file count, bytes, token proxy. Tracked-only so build/vendor trees do not inflate it.
  • Peripheral (substrate) - /substrate/<name> from preclone-repos.txt (8 today): same walk per-repo + a total.

Per-role accounting

Tiers 2/3 are role-invariant (same clone, same substrate). Roles diverge in the proactive tier only:

  • engineer - full autonomy doctrine + the keyword-matched front-load seed block + statusline + corpus index. Heaviest: it carries the seed.
  • advisor - issue + a lighter seed, no read-only overlay.
  • director - the read-only surface overlay replaces the autonomy framing with the capture-and-dispatch block, plus statusline. Distinct shape, measured as it lands.

The driver axis (claude L2 / codex L1 / opencode L0) then scales the doctrine portion.

Probe architecture (recommendation, one of the open forks)

Static-compose by default + --spin verifier. The proactive tier is deterministic from inputs ward owns (embedded doctrine, the level ladder, the overlay strings via readOnlyContextBlock / agentSeedPrompt), so a host-side probe reproduces it with no Docker. Tiers 2/3 are a git ls-files walk. --spin boots one real container per (role, driver) cell and diffs measured vs. predicted, catching composition drift. Reuse ward's own compose functions so drift is a test failure, not a silent wrong number.

Proposed per-driver spec (NOT committed)

The issue names agent-adapters.yaml, but that mirror was retired in ward#419 - the live source is ward-kdl.fleet.kdl (dialect 2). Candidate additive block:

agent opencode {
    context-level 0                          // kept: the compose ladder reads it
    context-budget {
        proactive  tokens=5000
        immediate  files=1500 tokens=400000
        peripheral repos=3 tokens=600000     // can also gate the warmed subset
    }
}

Absent block = unbudgeted (backward-compatible).

Authoring split + companion

  • Measurement leg -> agentic-os, cross-filed as agentic-os#319 (extend check_context_budget with the working-dir + substrate walkers, measurement-only).
  • Role-spin probe + spec schema -> ward (owns roles/containers/substrate/WARD_CONTEXT_LEVEL).

Open forks for you (not silently resolved)

  1. Spec schema shape - units per tier (tokens only, or +file-count), whether peripheral gates the manifest, per-driver vs per-(driver, role). First instance sets the pattern.
  2. Real-spin vs static - recommendation is static + --spin.
  3. Where --check lives - ward verb, aos hook, or split (aos cannot reach up into ward's role model).
  4. Token meaning for tiers 2/3 - a 3k-file repo is millions of chars; file-count + bytes may signal more honestly than a token proxy implying full ingest.
## Design proposal: role-aware three-tier context probe + per-driver spec Landed the design as [`docs/context-probe.md`](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/src/branch/main/docs/context-probe.md) (kept under the repo's 80-line/4000-char doc cap). This comment carries the fuller accounting the doc points back to. **No schema is committed** - the fleet KDL is untouched; the block below is proposed for you to shape. ### The three tiers, precisely - **Proactive (eager prompt bytes)** - composed by `compose_context` in `entrypoint.sh`: `AGENTS.container.md` always, then per `WARD_CONTEXT_LEVEL` (L2 = target `CLAUDE.md`+`AGENTS.md`, L1 = `AGENTS.md`, L0 = nothing), then role overlays (read-only block, front-load seed, statusline, issue-corpus index), plus mounted-skill frontmatter. Measure: chars/4, reusing `check_context_budget`'s doc+skill accounting and **adding** the container-only overlays that never exist in the host-side load point. - **Immediate (working clone)** - `/workspace/<name>`: `git ls-files` walk -> tracked-file count, bytes, token proxy. Tracked-only so build/vendor trees do not inflate it. - **Peripheral (substrate)** - `/substrate/<name>` from `preclone-repos.txt` (8 today): same walk per-repo + a total. ### Per-role accounting Tiers 2/3 are role-invariant (same clone, same substrate). Roles diverge in the **proactive** tier only: - **engineer** - full autonomy doctrine + the keyword-matched front-load seed block + statusline + corpus index. Heaviest: it carries the seed. - **advisor** - issue + a lighter seed, no read-only overlay. - **director** - the read-only surface overlay **replaces** the autonomy framing with the capture-and-dispatch block, plus statusline. Distinct shape, measured as it lands. The driver axis (claude L2 / codex L1 / opencode L0) then scales the doctrine portion. ### Probe architecture (recommendation, one of the open forks) **Static-compose by default + `--spin` verifier.** The proactive tier is deterministic from inputs ward owns (embedded doctrine, the level ladder, the overlay strings via `readOnlyContextBlock` / `agentSeedPrompt`), so a host-side probe reproduces it with no Docker. Tiers 2/3 are a `git ls-files` walk. `--spin` boots one real container per (role, driver) cell and diffs measured vs. predicted, catching composition drift. Reuse ward's own compose functions so drift is a test failure, not a silent wrong number. ### Proposed per-driver spec (NOT committed) The issue names `agent-adapters.yaml`, but that mirror was retired in ward#419 - the live source is `ward-kdl.fleet.kdl` (dialect 2). Candidate additive block: ```kdl agent opencode { context-level 0 // kept: the compose ladder reads it context-budget { proactive tokens=5000 immediate files=1500 tokens=400000 peripheral repos=3 tokens=600000 // can also gate the warmed subset } } ``` Absent block = unbudgeted (backward-compatible). ### Authoring split + companion - **Measurement leg -> agentic-os**, cross-filed as **agentic-os#319** (extend `check_context_budget` with the working-dir + substrate walkers, measurement-only). - **Role-spin probe + spec schema -> ward** (owns roles/containers/substrate/`WARD_CONTEXT_LEVEL`). ### Open forks for you (not silently resolved) 1. **Spec schema shape** - units per tier (tokens only, or +file-count), whether `peripheral` gates the manifest, per-driver vs per-(driver, role). First instance sets the pattern. 2. **Real-spin vs static** - recommendation is static + `--spin`. 3. **Where `--check` lives** - ward verb, aos hook, or split (aos cannot reach up into ward's role model). 4. **Token meaning for tiers 2/3** - a 3k-file repo is millions of chars; file-count + bytes may signal more honestly than a token proxy implying full ingest.
Author
Owner

WARD-OUTCOME: done - landed docs/context-probe.md (three-tier role-aware probe + proposed per-driver context-budget block, schema unlocked) and cross-filed the measurement leg as agentic-os#319.

Design-first felt like the right call here - the real work was reading how context actually composes (the compose_context ladder, the read-only overlay, the front-load seed) so the tier accounting matched what lands in a container rather than what the docs claim. Two things fought back. First, the issue names agent-adapters.yaml as the spec home, but that mirror was retired in ward#419 - the live source is the fleet KDL, so I proposed the block there and flagged the staleness. Second, and more annoyingly, the repo-wide code-comments hook was already red on main from the recent Windows-guard commits, which blocked my commit without --no-verify. I trimmed those blocks - then discovered mid-rebase that #168 had independently trimmed the exact same lines, so I dropped my redundant commit and kept just the doc.

Confidence is high on the design being a faithful, useful map and on the tier boundaries. I deliberately did not lock the schema - the KDL block, real-spin-vs-static, and where --check enforcement lives are all named as forks for you rather than resolved. The one rough edge worth a look: the 4000-char doc cap forced the durable doc to be a summary with the full accounting in the issue thread, which is a slightly awkward split if the thread ages out of attention. If the design survives your read, the natural follow-ups are the two implementation issues (the aos walker in #319, the ward probe verb) once you settle the forks.

WARD-OUTCOME: done - landed docs/context-probe.md (three-tier role-aware probe + proposed per-driver context-budget block, schema unlocked) and cross-filed the measurement leg as agentic-os#319. Design-first felt like the right call here - the real work was reading how context actually composes (the `compose_context` ladder, the read-only overlay, the front-load seed) so the tier accounting matched what lands in a container rather than what the docs claim. Two things fought back. First, the issue names `agent-adapters.yaml` as the spec home, but that mirror was retired in ward#419 - the live source is the fleet KDL, so I proposed the block there and flagged the staleness. Second, and more annoyingly, the repo-wide code-comments hook was already red on main from the recent Windows-guard commits, which blocked my commit without `--no-verify`. I trimmed those blocks - then discovered mid-rebase that #168 had independently trimmed the exact same lines, so I dropped my redundant commit and kept just the doc. Confidence is high on the design being a faithful, useful map and on the tier boundaries. I deliberately did not lock the schema - the KDL block, real-spin-vs-static, and where `--check` enforcement lives are all named as forks for you rather than resolved. The one rough edge worth a look: the 4000-char doc cap forced the durable doc to be a summary with the full accounting in the issue thread, which is a slightly awkward split if the thread ages out of attention. If the design survives your read, the natural follow-ups are the two implementation issues (the aos walker in #319, the ward probe verb) once you settle the forks.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#373
No description provided.