feat(workflows): tier the standing reading for schema-bound stages

The anonymous script stages received the full standing-reading
instruction (project CLAUDE.md + git log -10 --format=full) although
a tree-footprint or text-extraction stage consumes none of it and a
verify stage needs only the build/test commands — re-executed on
every dispatch including tight repair rounds, at ~40k+ real tokens
per freshly-primed context.

Two slim variants in both workflow scripts: STANDING_FACTS
(verify-type stages — mini-verify, standard end-verify,
compiler-driven build/suite verify: project-facts build/test commands
only) and STANDING_NONE (tree/extraction stages — preflight,
plan-index, plan-extract[-all], snapshot: nothing beyond their own
instructions). Judgment roles (implementer, spec/quality reviewers,
tester/e2e, compiler-driven edit stage) keep the full read.
conventions.md § Standing reading and agent-template.md § Standing
reading list carry the tiering sentence; the precedent is the
narrowed per-role reading the named agents already practise
(tester -3, plan-recon/grounding-check -5, fieldtester -8,
docwriter --oneline).

closes #31
This commit is contained in:
2026-07-17 15:39:06 +02:00
parent 44f45d16a9
commit fe39658d67
4 changed files with 54 additions and 8 deletions
+6
View File
@@ -201,6 +201,12 @@ the per-role standing reading the project lists in its CLAUDE.md project
facts. The agent's body says, prosaically: "Read everything in the
standing reading list before doing anything else."
The always list binds every *role* (agent file). The anonymous
schema-bound stages inside `implement/workflows/*.js` are tiered
below it (issue #31): verify-type stages read only the project-facts
build/test commands, tree/extraction stages read nothing — see
`conventions.md` § Standing reading.
When the project's CLAUDE.md project facts name a glossary path, that
glossary is implicitly part of the always list, so every role reads the
project glossary without a per-role entry.
+13
View File
@@ -122,6 +122,19 @@ project facts (`standing reading`). If the project declares a
glossary, that file is implicitly standing reading for every role
too.
**Schema-bound workflow stages are tiered down** (issue #31). The
anonymous script stages in `implement/workflows/*.js` have no agent
file and author no code, and a freshly-primed context costs ~40k+ real
tokens regardless of task size — so they do not take the full read:
verify-type stages (mini-verify, the standard end-verify, the
compiler-driven build/suite verify) read only the project-facts
build/test commands; tree/extraction stages (preflight, plan-index,
plan-extract[-all], snapshot) read nothing beyond their own
instructions. This extends the narrowed per-role standing reading the
named agents already practise (`tester` reads `git log -3`,
`plan-recon`/`grounding-check` `-5`, `fieldtester` `-8`, `docwriter`
`--oneline`); judgment roles keep the full convention.
## Git discipline
- **Only the orchestrator commits.** No skill agent runs `git commit`.
+10 -1
View File
@@ -113,6 +113,15 @@ const STANDING =
'(its "## Skills plugin: project facts" name the build and test commands) and ' +
'`git log -10 --format=full`. Resolve the build/test commands from those facts.'
// Standing-reading tiering (issue #31): the verify stage is a schema-bound
// check that only needs the build/test commands — no git log, no full
// CLAUDE.md read. The edit stage keeps the full standing read (it authors
// code and must match project idiom).
const STANDING_FACTS =
'Project facts first: read ONLY the "## Skills plugin: project facts" section of the project\'s CLAUDE.md for ' +
'the build and test commands — no other standing reading (no git log). This is a schema-bound check stage; ' +
'do not assume a toolchain.'
// Working-directory anchor (issue #25): same defect class as implement-loop —
// in a worktree session a dispatched agent's cwd can resolve to the primary
// checkout and verify the wrong (clean) tree. Carrier-supplied only here (two
@@ -225,7 +234,7 @@ if (!edit.applied_changes) {
// ---- Phase 2 — verify (the independently-invokable phase) ---------------
phase('Verify')
const verify = await agent(
`${STANDING}${ANCHOR}\n\nVerify the edit is behaviour-preserving WITHOUT changing any code or test. Run the project build ` +
`${STANDING_FACTS}${ANCHOR}\n\nVerify the edit is behaviour-preserving WITHOUT changing any code or test. Run the project build ` +
'and the full test suite. Report whether the build is clean and whether the suite is green AND UNCHANGED — i.e. ' +
'every test passes and NO test was added, removed, or weakened versus the pre-edit baseline (compare `git diff ' +
'HEAD` for test-file changes). Also report working_tree_dirty — whether `git status --porcelain` / `git diff HEAD` ' +
+25 -7
View File
@@ -199,6 +199,24 @@ const STANDING =
'design ledger) and `git log -10 --format=full`. Resolve the build/test commands ' +
'from those facts — do not assume a toolchain.'
// Standing-reading tiering (issue #31): the full standing read primes the
// judgment roles (implementer, reviewers, tester), but a schema-bound check
// stage consumes none of it — at ~40k+ real tokens per freshly-primed context
// the blanket read was pure priming tax on the highest-volume dispatches.
// Two slim variants, extending the narrowed per-role standing reading the
// named agents already practise (conventions.md § Standing reading):
// STANDING_FACTS — verify-type stages that run the suite: only the
// project-facts build/test commands, no git log.
// STANDING_NONE — tree/extraction stages (preflight, plan-index,
// plan-extract[-all], snapshot): nothing beyond their own instructions.
const STANDING_FACTS =
'Project facts first: read ONLY the "## Skills plugin: project facts" section of the project\'s CLAUDE.md for ' +
'the build and test commands — no other standing reading (no git log). This is a schema-bound check stage; ' +
'do not assume a toolchain.'
const STANDING_NONE =
'Schema-bound mechanical stage: NO standing reading (no CLAUDE.md, no git log) — do exactly what is instructed ' +
'below, nothing else.'
// Working-directory anchor (issue #25): in worktree sessions a dispatched
// agent's cwd can resolve to the PRIMARY checkout instead of the worktree the
// loop operates on — observed as a per-dispatch race producing false
@@ -516,7 +534,7 @@ let startSha = null
if (mode === 'mini') {
phase('Preflight')
const pre = await agent(
`${STANDING}${ANCHOR}\n\nYou are the clean-tree preflight for an implement iteration. ` +
`${STANDING_NONE}${ANCHOR}\n\nYou are the clean-tree preflight for an implement iteration. ` +
'Run `git status --porcelain`, `git rev-parse HEAD`, `git rev-parse --abbrev-ref HEAD`, and ' +
'`git rev-parse --show-toplevel`. ' +
'Report whether the tree is clean (no porcelain output), the HEAD sha, the branch, the repo_root, and any ' +
@@ -584,7 +602,7 @@ if (mode === 'mini') {
// narrower than the closed across-tasks vector and rests on the planner's
// bite-sized-task invariant; it is the one truncation slice left open here.
const index = await agent(
`${STANDING}${ANCHOR}\n\nFIRST the folded clean-tree preflight: run \`git status --porcelain\`, \`git rev-parse HEAD\`, ` +
`${STANDING_NONE}${ANCHOR}\n\nFIRST the folded clean-tree preflight: run \`git status --porcelain\`, \`git rev-parse HEAD\`, ` +
'`git rev-parse --abbrev-ref HEAD`, and `git rev-parse --show-toplevel`; report `clean` (no porcelain output), ' +
'`head_sha`, `branch`, `repo_root`, and any ' +
'`dirty_paths`. The HEAD sha is an informational anchor only — never a reset target. If the tree is DIRTY, ' +
@@ -707,7 +725,7 @@ if (mode === 'mini') {
let extractedTasks = null
if (expectedIds.length > 1 && totalApprox !== null && totalApprox <= EXTRACT_ALL_BYTE_CEILING) {
const all = await agent(
`${STANDING}${ANCHOR}\n\nRead the plan at ${plan_path}. Extract EXACTLY these tasks (1-based ids): ${expectedIds.join(', ')}` +
`${STANDING_NONE}${ANCHOR}\n\nRead the plan at ${plan_path}. Extract EXACTLY these tasks (1-based ids): ${expectedIds.join(', ')}` +
'EACH as its VERBATIM block: the full task text as written, including any code blocks. Do not summarise, ' +
'truncate, reorder, or merge tasks into each other. Return exactly those tasks, nothing else. Do not edit anything.',
{ model: 'sonnet', effort: 'medium', label: 'plan-extract-all', phase: 'Per-task loop', schema: TASK_TEXTS_SCHEMA },
@@ -725,7 +743,7 @@ if (mode === 'mini') {
extractedTasks = await parallel(
expectedIds.map((id) => () =>
agent(
`${STANDING}${ANCHOR}\n\nRead the plan at ${plan_path}. Extract EXACTLY task ${id} (1-based) as its VERBATIM block — ` +
`${STANDING_NONE}${ANCHOR}\n\nRead the plan at ${plan_path}. Extract EXACTLY task ${id} (1-based) as its VERBATIM block — ` +
'the full task text as written, including any code blocks. Do not summarise, truncate, reorder, or merge in ' +
'any other task. Return that one task only. Do not edit anything.',
{ model: 'sonnet', effort: 'medium', label: `plan-extract:${id}`, phase: 'Per-task loop', schema: TASK_TEXT_SCHEMA },
@@ -992,7 +1010,7 @@ for (const task of tasks) {
results.push(r)
if (tasks.length > 1) {
const snap = await agent(
`${STANDING}${ANCHOR}\n\nRecord a working-tree snapshot boundary WITHOUT changing anything. Run exactly these two ` +
`${STANDING_NONE}${ANCHOR}\n\nRecord a working-tree snapshot boundary WITHOUT changing anything. Run exactly these two ` +
'commands and report their output:\n' +
`1. \`git stash create "iter ${iter_id} post-task ${task.id}"\` — prints a sha (a dangling commit; it ` +
'touches neither HEAD, nor the index, nor the working tree). Report it as snapshot_sha; empty string if ' +
@@ -1077,7 +1095,7 @@ let treeState = null
if (mode === 'mini' && outcome === 'DONE') {
phase('Verify')
miniVerify = await agent(
`${STANDING}${ANCHOR}\n\nVerify a bug/feature fix WITHOUT changing any code or test. The fix was meant to drive this ` +
`${STANDING_FACTS}${ANCHOR}\n\nVerify a bug/feature fix WITHOUT changing any code or test. The fix was meant to drive this ` +
`RED test to GREEN: ${red_test_path}\n\nRun that test by name and confirm it now PASSES (it was RED before the ` +
'fix). Then run the full suite and confirm it is green (no regression). Also report working_tree_dirty — ' +
'whether `git status --porcelain` (NOT `git diff HEAD`, which misses new untracked files) shows any change at ' +
@@ -1105,7 +1123,7 @@ if (mode === 'mini' && outcome === 'DONE') {
} else if (mode === 'standard' && outcome === 'DONE') {
phase('Verify')
treeState = await agent(
`${STANDING}${ANCHOR}\n\nEnd-verify an implement iteration WITHOUT changing any code or test. Two independent checks:\n` +
`${STANDING_FACTS}${ANCHOR}\n\nEnd-verify an implement iteration WITHOUT changing any code or test. Two independent checks:\n` +
'1. Run `git status --porcelain` and report files_touched = its line count (every changed, added, or ' +
'untracked path). Use `git status --porcelain`, NOT `git diff`, so brand-new untracked files are counted.\n' +
'2. Run the FULL project suite (the test command from the CLAUDE.md project facts) and report suite_green — ' +