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:
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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` ' +
|
||||
|
||||
@@ -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 — ' +
|
||||
|
||||
Reference in New Issue
Block a user