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
+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 — ' +