forked from bchanot/claude
docs(plan): model routing wave 2 — pure-execution + reflection-split skills (doc/status/hotfix/commit-change/release-candidate)
This commit is contained in:
@@ -1058,3 +1058,305 @@ git commit -m "chore(memory): BDR-066 model routing + journal + TODO follow-ups"
|
|||||||
|
|
||||||
Run: `make test`
|
Run: `make test`
|
||||||
Expected: green, exit 0. Then report the full commit list (`git log --oneline develop..HEAD`) for the human merge gate. Do NOT run `gitflow finish`.
|
Expected: green, exit 0. Then report the full commit list (`git log --oneline develop..HEAD`) for the human merge gate. Do NOT run `gitflow finish`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# WAVE 2 — pure-execution + reflection-split skills (user directive 2026-07-15)
|
||||||
|
|
||||||
|
The wave-1 exclusion list left `hotfix, commit-change, doc, status, release-candidate`
|
||||||
|
inheriting the session model — i.e. running EXECUTION on the big model, the
|
||||||
|
waste the split exists to kill. User verdicts (2026-07-15):
|
||||||
|
- **doc / status** = pure non-interactive execution → convert inline-load to a
|
||||||
|
dispatched subagent so its pin takes effect. doc-syncer stays sonnet;
|
||||||
|
status-reporter stays **haiku** (right tier for a read-only collector; the
|
||||||
|
win is getting it off the big model, not the tier).
|
||||||
|
- **hotfix** = reflection (locate root cause + propose fix) + execution → split
|
||||||
|
like /feat: reflection inline (+ MODEL GATE), execution dispatched to the
|
||||||
|
sonnet hotfixer executor. hotfix JOINS the gated group (12→13).
|
||||||
|
- **commit-change** = grouping (judgment) + committing (execution), interactive
|
||||||
|
→ dispatch EVERYTHING (grouping included) to a sonnet commit-changer, relocate
|
||||||
|
the two approval gates to the dispatcher (propose→confirm→execute, seo-applier
|
||||||
|
shape). No MODEL GATE (no inline reflection — grouping runs on sonnet).
|
||||||
|
- **release-candidate** = create a sonnet `release-executor` agent for the
|
||||||
|
mechanical spans (version.txt, CHANGELOG rewrite, gitflow start/finish, tag),
|
||||||
|
relocate the two human gates (when-to-release, push) to the dispatcher. No
|
||||||
|
MODEL GATE (user's explicit choice — force dispatch, not gate).
|
||||||
|
|
||||||
|
Post-wave-2 gate exclusion list = `commit-change, doc, status, release-candidate`
|
||||||
|
(hotfix removed — now wired).
|
||||||
|
|
||||||
|
## Global Constraints (wave 2)
|
||||||
|
|
||||||
|
Same as wave 1: branch `feature/model-routing`, no merge, no attribution
|
||||||
|
trailers, `make test` green per commit, shellcheck clean, config-protection
|
||||||
|
sentinel before each `lib/tests/*` write, YAML `safe_load`-parseable frontmatter.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 11: doc + status → dispatched execution
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `skills/doc/SKILL.md` (add `Agent` to allowed-tools; body → dispatch)
|
||||||
|
- Modify: `skills/status/SKILL.md` (add `Agent` to allowed-tools; body → dispatch)
|
||||||
|
|
||||||
|
**Interfaces:** doc-syncer.md is already `model: sonnet`; status-reporter.md is
|
||||||
|
already `model: haiku` — no agent edits. Only the skills change from inline-load
|
||||||
|
to `Agent(subagent_type=…)` so the pins take effect.
|
||||||
|
|
||||||
|
- [ ] **Step 1: doc → dispatch.** In `skills/doc/SKILL.md`, add ` - Agent` to the
|
||||||
|
`allowed-tools` list, and replace the body block
|
||||||
|
```
|
||||||
|
Load and follow strictly:
|
||||||
|
- $HOME/.claude/agents/doc-syncer.md
|
||||||
|
|
||||||
|
Execute the DOC SYNCER on this project.
|
||||||
|
|
||||||
|
Context from the user (if any):
|
||||||
|
$ARGUMENTS
|
||||||
|
```
|
||||||
|
with:
|
||||||
|
```
|
||||||
|
Dispatch the doc-syncer as a subagent so its `model: sonnet` pin takes
|
||||||
|
effect (doc-sync = execution, not the session's big model):
|
||||||
|
|
||||||
|
Agent(subagent_type="doc-syncer")
|
||||||
|
prompt: "Audit + sync public docs for this project. Context from the user:
|
||||||
|
$ARGUMENTS. Report PATCHED_FILES and a summary — do NOT commit."
|
||||||
|
|
||||||
|
Then commit the patched docs from THIS loop per `$HOME/.claude/lib/doc-commit.md`
|
||||||
|
(surgical: only doc-syncer's PATCHED_FILES, never `.claude/`/`CLAUDE.md`,
|
||||||
|
no-op if nothing patched).
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: status → dispatch.** In `skills/status/SKILL.md`, add `Agent` to
|
||||||
|
`allowed-tools` (`Read, Bash, Glob, Grep, Agent`), and replace the body
|
||||||
|
`Load and follow strictly:\n- $HOME/.claude/agents/status-reporter.md\n\nProduce the full PROJECT STATUS report for the current working directory.`
|
||||||
|
with a dispatch:
|
||||||
|
```
|
||||||
|
Dispatch the status-reporter as a subagent so its `model: haiku` pin takes
|
||||||
|
effect (read-only collection = cheapest tier, off the big session model):
|
||||||
|
|
||||||
|
Agent(subagent_type="status-reporter")
|
||||||
|
prompt: "Produce the full PROJECT STATUS report for the current working
|
||||||
|
directory. $ARGUMENTS"
|
||||||
|
```
|
||||||
|
Keep the existing "Fallback when agent file missing" section intact (it still
|
||||||
|
applies — if the dispatch target is unreachable, emit the missing-agent line
|
||||||
|
and STOP).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify + commit.** `grep -c 'subagent_type="doc-syncer"' skills/doc/SKILL.md`
|
||||||
|
→ 1; `grep -c 'subagent_type="status-reporter"' skills/status/SKILL.md` → 1.
|
||||||
|
`make test` green.
|
||||||
|
```bash
|
||||||
|
git add skills/doc/SKILL.md skills/status/SKILL.md
|
||||||
|
git commit -m "feat(model-routing): doc/status dispatch their agent (sonnet/haiku pins take effect)"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 12: hotfix — reflection inline + dispatched sonnet executor (/feat pattern)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify (rewrite): `skills/hotfix/SKILL.md` — becomes the reflection orchestrator
|
||||||
|
- Modify (rewrite): `agents/hotfixer.md` — becomes pure executor
|
||||||
|
- Modify: `lib/tests/loops-light.test.sh` — repoint hotfix structure locks (guarded)
|
||||||
|
|
||||||
|
**Pattern:** mirror the shipped `/feat` split (skills/feat/SKILL.md + agents/feater.md).
|
||||||
|
|
||||||
|
- [ ] **Step 1: Rewrite `skills/hotfix/SKILL.md` as the orchestrator.** Keep `Agent`
|
||||||
|
in allowed-tools. Structure:
|
||||||
|
- `# /hotfix — quick-fix orchestrator (reflection inline, execution dispatched)`
|
||||||
|
- `MODEL GATE (blocking): run $HOME/.claude/lib/model-gate.md BEFORE any step. small → STOP.`
|
||||||
|
(hotfix now has a reflection phase → it joins the gated group.)
|
||||||
|
- STEP 1 LOCATE (reflection, inline): find the bug from the description, read
|
||||||
|
the file(s), CONFIRM the root cause is obvious/superficial, escalate to
|
||||||
|
`/bugfix` if deeper. Optional blockers-only memory glance (as today).
|
||||||
|
- STEP 1.5 DESIGN GATE (`lib/design-gate.md`, as today).
|
||||||
|
- STEP 1.7 CONTRACT (silent autofill, `lib/contract-interview.md`, zero
|
||||||
|
questions, as today).
|
||||||
|
- STEP 2 PRE-FLIGHT (inline): gitflow aiguillage (type `hotfix`); snapshot
|
||||||
|
`git rev-parse HEAD` (the revert SHA) + dirty-tree check (as today's STEP 2
|
||||||
|
pre-flight).
|
||||||
|
- STEP 3 DISPATCH EXECUTOR: `Agent(subagent_type="hotfixer")` with the
|
||||||
|
contract path, the located file(s), the proposed minimal fix, and the branch.
|
||||||
|
Parse a `HOTFIX-EXEC REPORT` with `STATUS : DONE | BLOCKED`.
|
||||||
|
- STEP 4 VERIFY + SECURE + COMMIT (main loop, LRN-083): on executor DONE, the
|
||||||
|
smoke result is in its report; then the security gate — dispatch a FRESH
|
||||||
|
security-auditor (`MODE: gate`, SCOPE = diff vs the pre-flight SHA). **hotfix
|
||||||
|
keeps revert-not-loop**: smoke FAIL or security BLOCK → `git restore .` to the
|
||||||
|
pre-flight SHA + STOP + "escalate to /bugfix" (verbatim from today's STEP 3).
|
||||||
|
No verifier at hotfix weight. Commit only after smoke + security pass.
|
||||||
|
- STEP 5 DOC SYNC + STEP 6 CAPITALIZE: identical to today's STEP 4/5 (doc-sync
|
||||||
|
auto-mode + `doc-commit.md`; lightweight capitalize + always-on journal +
|
||||||
|
`capitalize-commit.md`).
|
||||||
|
- RULES: max 2 files; execution never stays inline, reflection never leaves it
|
||||||
|
(BDR-066); executor dispatched fresh; revert-not-loop preserved.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Rewrite `agents/hotfixer.md` as the executor.** Frontmatter:
|
||||||
|
`tools: Read, Edit, Write, Bash, Grep, Glob` (DROP `Agent` — no nested dispatch;
|
||||||
|
security moved to the orchestrator), `model: sonnet`. Body: receive
|
||||||
|
CONTRACT + located file(s) + proposed fix + BRANCH (verify with
|
||||||
|
`git branch --show-current`, never switch). Apply the minimal edit (no
|
||||||
|
refactoring), run the stack smoke/test cascade (keep today's detection cascade),
|
||||||
|
report. FORBIDDEN: git commit, branch ops, security dispatch, user questions,
|
||||||
|
attribution trailers. End with:
|
||||||
|
```
|
||||||
|
HOTFIX-EXEC REPORT
|
||||||
|
STATUS : DONE | BLOCKED
|
||||||
|
FILE(S) : <changed files>
|
||||||
|
FIX : <one-line description>
|
||||||
|
SMOKE : <test/build result, verbatim line>
|
||||||
|
NOTES : <BLOCKED: the blocker; DONE: none>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Repoint `lib/tests/loops-light.test.sh` hotfix locks** (guarded —
|
||||||
|
sentinel first). The hotfix block currently checks `agents/hotfixer.md` for
|
||||||
|
orchestration clauses now moved to the skill. Introduce `HSKL="$REPO/skills/hotfix/SKILL.md"`
|
||||||
|
and repoint: contract/silent → HSKL `STEP 1.7 — CONTRACT`; security gate +
|
||||||
|
`failure REVERTS, never loops` + `No verifier is dispatched at hotfix weight`
|
||||||
|
→ HSKL; `hotfix skill has Agent` (HSK ` - Agent`) unchanged. The
|
||||||
|
`hotfix has Agent tool` lock on hotfixer INVERTS (hotfixer no longer has Agent)
|
||||||
|
→ change to assert hotfixer LACKS Agent and carries `model: sonnet` + the
|
||||||
|
`HOTFIX-EXEC REPORT` grammar. Add a `hotfix dispatches hotfixer` lock
|
||||||
|
(`subagent_type="hotfixer"` in HSKL). Keep include/feat/bugfix blocks untouched.
|
||||||
|
Add a negative-match helper `tn()` (mirror `tf`, invert the grep) if asserting
|
||||||
|
Agent-absence.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Wire hotfix into the census + gate lists.** In
|
||||||
|
`lib/tests/model-routing.test.sh` (guarded — sentinel first): MOVE `hotfix`
|
||||||
|
from the excluded loop to the wired loop (`has "skills/hotfix/SKILL.md" 'lib/model-gate.md'`).
|
||||||
|
Update the expected count in the run message accordingly.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Verify + commit.** `bash lib/tests/loops-light.test.sh` green;
|
||||||
|
`bash lib/tests/model-routing.test.sh` green; YAML check both rewritten files;
|
||||||
|
`make test` green.
|
||||||
|
```bash
|
||||||
|
git add skills/hotfix/SKILL.md agents/hotfixer.md lib/tests/loops-light.test.sh lib/tests/model-routing.test.sh
|
||||||
|
git commit -m "feat(model-routing): /hotfix split — reflection inline + gate, hotfixer = sonnet executor"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 13: commit-change — dispatch grouping+commit to sonnet, relocate approval gates
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify (rewrite): `skills/commit-change/SKILL.md` — dispatcher owns the two gates
|
||||||
|
- Modify (rewrite): `agents/commit-changer.md` — sonnet, propose/execute phases, no AskUserQuestion
|
||||||
|
|
||||||
|
**Pattern:** seo-applier shape (subagent proposes → dispatcher confirms → subagent executes).
|
||||||
|
|
||||||
|
- [ ] **Step 1: Rewrite `agents/commit-changer.md`.** Frontmatter:
|
||||||
|
`tools: Bash, Read, Grep, Glob` (DROP `AskUserQuestion` — gates move to the
|
||||||
|
dispatcher), `model: sonnet`. Body: two modes driven by the dispatch prompt.
|
||||||
|
- `MODE: propose` → Phase 0 (gitflow aiguillage, type chore — bash), Phase 1
|
||||||
|
(gather), Phase 2 (reconstruct steps), Phase 2.5 → EMIT the `COMMIT PLAN`
|
||||||
|
block + any edge-case flags (sensitive files, staged-only, conflicts) +
|
||||||
|
Phase-4 capitalize candidates, then STOP with
|
||||||
|
`READY TO APPLY — awaiting dispatcher confirmation`. Writes NOTHING.
|
||||||
|
- `MODE: apply` → receive the APPROVED plan (steps + messages) + approved
|
||||||
|
capitalize entries; execute Phase 3 (stage-per-step + commit) and write the
|
||||||
|
approved memory via `capitalize-commit.md`; report the commit hashes.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Rewrite `skills/commit-change/SKILL.md` as dispatcher.** Keep
|
||||||
|
`Agent` + `AskUserQuestion` in allowed-tools. Flow: pre-flight (detached HEAD /
|
||||||
|
conflicts / identity — STOP as today) → `Agent(subagent_type="commit-changer")`
|
||||||
|
with `MODE: propose` → show the returned COMMIT PLAN, `AskUserQuestion`
|
||||||
|
(all / numbers / edit / skip) → show capitalize candidates, `AskUserQuestion`
|
||||||
|
(all / IDs / skip) → `Agent(subagent_type="commit-changer")` with `MODE: apply`
|
||||||
|
+ the approved plan + approved entries → report hashes. NO MODEL GATE (grouping
|
||||||
|
runs on the sonnet subagent — no inline reflection to protect).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify + commit.** `grep -c 'subagent_type="commit-changer"' skills/commit-change/SKILL.md`
|
||||||
|
≥ 1; `grep -c 'model: sonnet' agents/commit-changer.md` → 1;
|
||||||
|
`grep -c 'AskUserQuestion' agents/commit-changer.md` → 0; YAML check; `make test` green.
|
||||||
|
```bash
|
||||||
|
git add skills/commit-change/SKILL.md agents/commit-changer.md
|
||||||
|
git commit -m "feat(model-routing): /commit-change dispatch to sonnet commit-changer, gates relocated to dispatcher"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 14: release-candidate — sonnet release-executor, human gates relocated
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `agents/release-executor.md` — sonnet, mechanical release spans
|
||||||
|
- Modify (rewrite): `skills/release-candidate/SKILL.md` — dispatcher owns the two human gates
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create `agents/release-executor.md`.** Frontmatter:
|
||||||
|
`tools: Read, Edit, Write, Bash, Grep, Glob`, `model: sonnet`. Two-span
|
||||||
|
executor driven by the dispatch prompt (a human gate sits BETWEEN the spans, so
|
||||||
|
it cannot be one dispatch):
|
||||||
|
- `SPAN: prep <X.Y.Z>` → `gitflow start release <X.Y.Z>`, set `version.txt`,
|
||||||
|
rewrite CHANGELOG (`## [Unreleased]` → `## [<X.Y.Z>] — <date>`, re-open empty
|
||||||
|
Unreleased; a MAJOR must spell out breaking), run the test suite (RC gate —
|
||||||
|
never release red), commit the prep on the release branch. Report the branch
|
||||||
|
+ test result. No merge, no tag, no push.
|
||||||
|
- `SPAN: finish <X.Y.Z>` → `gitflow finish` (fan-out), `git tag -a v<X.Y.Z> main
|
||||||
|
-m "release <X.Y.Z>"` AFTER finish. Report. NEVER push (dispatcher's gate).
|
||||||
|
- FORBIDDEN: deciding the version number (dispatcher/user owns it), the
|
||||||
|
when-to-release decision, `git push`, attribution trailers.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Rewrite `skills/release-candidate/SKILL.md` as dispatcher.** Add
|
||||||
|
`allowed-tools: Read, Write, Edit, Bash, Grep, Glob, Agent, AskUserQuestion` to
|
||||||
|
the frontmatter (it currently has none). Keep all the Overview/Versioning/Common-
|
||||||
|
mistakes doctrine. Flow: preconditions (clean tree, identity, develop ahead of
|
||||||
|
main) → the version-number decision stays HERE (judgment: derives from change
|
||||||
|
nature; decide before running) → `Agent(subagent_type="release-executor")`
|
||||||
|
`SPAN: prep <X.Y.Z>` → **HUMAN GATE — when to release** (`AskUserQuestion`,
|
||||||
|
explicit go, never on "tests pass") → `Agent(subagent_type="release-executor")`
|
||||||
|
`SPAN: finish <X.Y.Z>` → **push GATE (ASK)** (`AskUserQuestion`; on go only,
|
||||||
|
LRN-069): `git push origin main develop && git push origin v<X.Y.Z>` from THIS
|
||||||
|
loop. No MODEL GATE.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify + commit.** `RC_WORK=$(mktemp -d) RC_TAG=1 bash lib/tests/run-release-candidate.sh`
|
||||||
|
→ 5/5 (the release mechanics test is unchanged — the lib still fans out + the
|
||||||
|
dispatcher still tags); `grep -c 'subagent_type="release-executor"' skills/release-candidate/SKILL.md`
|
||||||
|
≥ 1; YAML check the new agent; `make test` green.
|
||||||
|
```bash
|
||||||
|
git add agents/release-executor.md skills/release-candidate/SKILL.md
|
||||||
|
git commit -m "feat(model-routing): /release-candidate dispatches sonnet release-executor, human gates in dispatcher"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 15: census + docs + memory for wave 2
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `lib/tests/model-routing.test.sh` (guarded) — wave-2 assertions
|
||||||
|
- Modify: `README.md`, `CHANGELOG.md`, `.claude/memory/decisions.md`, `.claude/tasks/TODO.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Extend the census** (`lib/tests/model-routing.test.sh`, guarded —
|
||||||
|
sentinel first). The excluded loop drops `hotfix` (moved to wired by Task 12 Step 4)
|
||||||
|
and now reads `for s in commit-change doc status release-candidate`. Add
|
||||||
|
execution-dispatch asserts: `has skills/doc/SKILL.md 'subagent_type="doc-syncer"'`;
|
||||||
|
`has skills/status/SKILL.md 'subagent_type="status-reporter"'`;
|
||||||
|
`has skills/commit-change/SKILL.md 'subagent_type="commit-changer"'`;
|
||||||
|
`has skills/release-candidate/SKILL.md 'subagent_type="release-executor"'`;
|
||||||
|
pins `has agents/commit-changer.md 'model: sonnet'`,
|
||||||
|
`has agents/release-executor.md 'model: sonnet'`; and executor-shape
|
||||||
|
`lacks agents/commit-changer.md 'AskUserQuestion'`. Update the printed expected
|
||||||
|
count. Flip-test one new assertion (LRN-096).
|
||||||
|
|
||||||
|
- [ ] **Step 2: README + CHANGELOG.** Update the BDR-066 agent-model table:
|
||||||
|
hotfixer stays sonnet (now an effective executor), commit-changer → sonnet,
|
||||||
|
release-executor (new) → sonnet, status-reporter → haiku (now effective via
|
||||||
|
dispatch). Move doc/status/commit-change/release-candidate out of the "inherit"
|
||||||
|
row into a new "execution — dispatched" line. CHANGELOG Unreleased: add the
|
||||||
|
wave-2 bullets (doc/status/hotfix/commit-change/release-candidate routing).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Capitalize.** Append to the BDR-066 entry a `**Wave 2**` bullet:
|
||||||
|
doc/status dispatched (sonnet/haiku pins effective); hotfix split like /feat
|
||||||
|
(joins gated group); commit-change dispatched sonnet with relocated gates;
|
||||||
|
release-candidate sonnet release-executor with relocated human gates; exclusion
|
||||||
|
list now commit-change/doc/status/release-candidate. Journal line +
|
||||||
|
TODO tick under the 2026-07-15 section.
|
||||||
|
```bash
|
||||||
|
git add lib/tests/model-routing.test.sh README.md CHANGELOG.md .claude/memory/decisions.md .claude/memory/journal.md .claude/tasks/TODO.md
|
||||||
|
git commit -m "chore(model-routing): wave-2 census + docs + BDR-066 update"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Final wave-2 review** — dispatch a whole-branch reviewer (opus) over
|
||||||
|
the wave-2 range; confirm both consumers of verify-secure-loop still coherent,
|
||||||
|
hotfix revert-not-loop preserved, no execution left on the big model in the
|
||||||
|
five converted skills. Report the full `git log --oneline develop..HEAD`. Do NOT
|
||||||
|
merge.
|
||||||
|
|||||||
Reference in New Issue
Block a user