diff --git a/.claude/memory/decisions.md b/.claude/memory/decisions.md index 02928f0..cb36a38 100644 --- a/.claude/memory/decisions.md +++ b/.claude/memory/decisions.md @@ -1058,3 +1058,6 @@ Supersedes the "Excluded: hotfix" clause of [[BDR-075]]. hotfix now wired (STEP ### BDR-076 — Dispatched judgment agents pinned OPUS; session model = orchestration + inline reflection ONLY [accepted] (2026-07-19) Reverses the BDR-066 rejected alternative "opus pins on audit agents (session-independent)". Context changed: session default now Fable (Mythos tier, /model 2026-07-19) — inherit meant every dispatched audit/challenge burned Fable quota, exactly the waste BDR-066 killed for executors. New rule: Fable does ONLY main-loop orchestration + reflection (brainstorm, plan, contract, synthesis, gates); EVERY dispatched subagent pinned. Pinned `model: opus` (big tier, session-independent; NEVER sonnet — silent audit downgrade, the thing old §F5 guarded): analyzer, plan-challenger, seo-analyzer, geo-analyzer, validator-analyzer + onboard's 6 general-purpose audit dispatches (`model="opus"`) + tour Phase B. NOT pinned (justified deviation from approved "7 agents"): interviewer + client-handover-writer — inline-load only, never dispatched → frontmatter pin inert + misleading (BDR-066 wave-4 precedent: its inert opus pin was dropped); they ARE the main loop = Fable per the rule. Explore built-in stays inherit (wave-3 decision conserved: no owned prompt, search feeds inline reflection). Local session pin `opus-4-8[1m]` dropped from `.claude/settings.local.json` (gitignored) — Fable default from settings.json now applies in this repo too. model-gate.md unchanged (still guards inline reflection, Fable-or-Opus = big). Census: model-routing.test.sh §3 flip + §11 (61 pass), loops-light 35 pass, full `make test` green. User directives via gate: "Opus partout" + "Supprimer le pin". Branch feature/opus-pin-audit-agents, unmerged. + +### BDR-077 — Model-tiering v2: 4-tier explicit routing, mode-based splits, no-inherit dispatches [accepted] (2026-07-19) +Supersedes BDR-076 scope + amends BDR-066. Doctrine: session model (Fable) = main-loop reflection/orchestration/planning/logic ONLY; main-loop retention criteria = interactive | conversation-context access | orchestration decision | dispatch overhead > step cost. NOTHING dispatched inherits: typed agents = frontmatter pin, built-ins = `model=` at every call site (`fable` for skill-runner reflection children, else complexity tier). Spike+smoke proven: `model:"fable"` resolves claude-fable-5 (enum-validated, loud fail, no silent fallback); call-site override BEATS a typed pin (sonnet-pinned verifier ran haiku). Fail-safe pin rule: mixed-mode agents keep the HIGH tier as pin, overrides go DOWN — forgotten override over-tiers (cost), never downgrades judgment. Mode-based splits (commit-changer precedent generalized; file splits rejected): doc-syncer audit(opus)/patch(sonnet) — ALSO fixed a latent defect: /doc dispatched an agent whose STEP 8 interactive gate could never fire; gates hoisted to a DISPATCHER PROTOCOL section; handover-doc-writer synthesize(opus)/render(sonnet) via run-scoped `.audit/handover-draft-.md` + DRAFT COMPLETE sentinel; seo/geo collect(sonnet)/judge(OPUS PIN)/template(sonnet) via `.audit/*-signals-.md` + COLLECTION COMPLETE + fail-closed judge + dispatcher ERROR contract (mute/ERROR judge NEVER carried into templating; retry once, escalate). File split only for a genuinely new role: plugin-probe (sonnet, facts-only) + plugin-advisor repinned opus reasoner (fail-closed on missing PROBE REPORT) + lib/plugin-gate.md (checkpoint + apply gate, doc-commit ×N include pattern). Inline→dispatch conversions: scaffolder, onboarder, doc-commit steps ×5 flows — their sonnet pins were INERT since creation, now live; CHANGE SUMMARY crosses the doc dispatch into doc-commit (LRN-126 wire). Tier moves: validator-analyzer opus→sonnet (deterministic runner); commit-changer propose=opus/apply=pin; ship-feature/init-project code-review dispatches = opus explicit (WAS an inherit leak); client-handover-writer's 7 skill-runners = model:"fable". Every wave shipped an IN-WAVE planted-input smoke as its merge gate — all PASSED disk-verified. Census §12-18 (125 pass; one vacuous line-wrapped lock self-caught = LRN-093 live). 6 waves, branches feature/model-tiering-w1..w6, merged on user standing signal. Plan: challenged 3 blind lenses + 1 confirmation (1 BLOCKER closed by spike, 8 MAJORs + 8 MINORs closed by named changes, 0 deferred). Refs: `.claude/tasks/plans/2026-07-19-model-tiering-v2-{analysis,plan}.md`. diff --git a/.claude/memory/journal.md b/.claude/memory/journal.md index f0f66ac..1cb9ec8 100644 --- a/.claude/memory/journal.md +++ b/.claude/memory/journal.md @@ -412,3 +412,4 @@ rules: ## 2026-07-19 - BDR-076: dispatched judgment agents pinned opus (analyzer, plan-challenger, seo/geo/validator-analyzer + 6 onboard general-purpose dispatches); Fable now = inline orchestration/reflection only. interviewer + client-handover-writer left unpinned (inline-load, pin inert). Local opus-4-8 session pin dropped from settings.local.json. Census §11 added (61 pass), loops-light 35, make test green. feature/opus-pin-audit-agents, UNMERGED. +- BDR-077 model-tiering v2 SHIPPED: 6 waves (W0 baseline merge → W1 no-inherit+fable skill-runners → W2 plugin split + doc two-mode + inert-pin conversions → W3 tier moves → W4 handover two-mode → W5 seo/geo 3-mode pipelines → W6 doctrine sweep). Plan challenged 4 passes (1 BLOCKER closed by fable spike). Per-wave planted-input smokes disk-verified. Census 125/0, make test green throughout. [[BDR-077]] [[LRN-137]]. diff --git a/.claude/memory/learnings.md b/.claude/memory/learnings.md index c1a6dd1..61472ca 100644 --- a/.claude/memory/learnings.md +++ b/.claude/memory/learnings.md @@ -1342,3 +1342,10 @@ rules: ### LRN-136 — config-protection live state follows checked-out branch's symlinked settings.json (2026-07-17) ~/.claude/settings.json is a SYMLINK to the repo settings.json; Claude Code hot-reloads settings on change → the config-protection PreToolUse hook's active/inactive state tracks the CURRENT branch's settings.json. On feature/drop-config-protection (hook deregistered) a protected edit passed silently, sentinel unconsumed; after gitflow-switch to a branch off develop (hook still registered) the SAME class of edit was blocked. Apply: a change that removes a settings-registered hook is live only on that branch until merged; use the one-shot sentinel for protected edits on any branch that still registers it. ([[BDR-074]] context.) + +## LRN-137 — mode-based re-tiering beats file splits for mixed-tier agents +- **pattern**: three planned agent splits (doc-syncer, handover-doc-writer, seo/geo analyzers) shipped as MODES + per-dispatch `model=` instead of new files; only plugin-probe justified a real new file (genuinely new role, no shared body). +- **why**: a file split severs implicit data paths (LRN-126), relocates body-text test locks (seo-data fetch-wiring), breaks name/dispatch-string census locks, duplicates templates. A mode split keeps ALL locks and text in place; the dispatcher's gate sits BETWEEN mode dispatches; call-site `model=` precedence over the frontmatter pin is spike-proven (sonnet-pinned verifier ran haiku on override). +- **fail-safe pin rule**: keep the HIGHEST tier as the frontmatter pin and override DOWN at call sites — a forgotten override then over-tiers (costs money) instead of silently downgrading judgment (costs correctness). +- **future application**: before splitting any agent across model tiers, try MODE + `model=` first; create a new agent file only for a genuinely new role. Run-scoped `.audit/-` files + completeness sentinel + fail-closed consumer for any cross-dispatch artifact. +- **cousin**: [[LRN-125]] [[LRN-126]] [[BDR-077]]. diff --git a/.claude/tasks/plans/2026-07-19-model-tiering-v2-plan.md b/.claude/tasks/plans/2026-07-19-model-tiering-v2-plan.md index 1bc8e39..5980800 100644 --- a/.claude/tasks/plans/2026-07-19-model-tiering-v2-plan.md +++ b/.claude/tasks/plans/2026-07-19-model-tiering-v2-plan.md @@ -265,6 +265,26 @@ W6 DOCTRINE + CLOSE-OUT: model-gate.md rewrite (protects main loop; tier - simplicity: CONCERNS(1) — 3-way seo/geo YAGNI → 2-way worker/judge (fixed S3/S4); twin-templater share (dissolved by 2-way; divergence stated); plugin gate ×4 copies → lib/plugin-gate.md include (fixed S1). +## EXECUTION NOTES (2026-07-19 — as-built deviations, all justified in-commit) + +- S2/S3/S4/S5 shipped MODE-BASED (one agent, modes + call-site `model=`) + instead of file splits — the challenge's own commit-changer precedent + generalized; locks and body text stayed in place (LRN-137). plugin S1 + kept the `plugin-advisor` NAME for the reasoner (repinned opus) — only + plugin-probe is a new file. +- seo/geo keep the OPUS pin (not sonnet+judge-override): fail-safe + direction — a forgotten override over-tiers, never downgrades. /harden + narrow-scope + /onboard report-only keep legacy no-MODE single-shot on + that pin. +- W0's seo-geo-integrity arbitrage was phantom (branch already merged) — + TODO.md corrected instead. +- Per-wave smokes ran in-wave as merge gates (confirmation-pass fix) — + all PASSED, disk-verified. Registry note: a NEW subagent_type registers + at next session start; typed resolution re-checked post-restart before + the W2 merge. + +## CHALLENGE LOG (final) + - Confirmation pass (fresh robustness challenger on v2): CONCERNS(2) — v1 fixes HOLD (doc-commit CHANGE SUMMARY, plugin-probe sonnet, rollback order, fable spike, RUNID); 2 new MAJORs + 1 MINOR opened by the revisions, all diff --git a/agents/client-handover-writer.md b/agents/client-handover-writer.md index 6d0697a..6a71e36 100644 --- a/agents/client-handover-writer.md +++ b/agents/client-handover-writer.md @@ -1,6 +1,6 @@ --- name: client-handover-writer -description: Final ship-and-handover orchestrator — called by /client-handover. Runs the audit/fix/gate pipeline (SEO+GEO+HARDEN to ≥17/20, live VALIDATE) inline on the big session model, then delegates the non-technical client deliverable (Markdown + branded HTML + PDF) to the sonnet-pinned handover-doc-writer. +description: Final ship-and-handover orchestrator — called by /client-handover. Runs the audit/fix/gate pipeline (SEO+GEO+HARDEN to ≥17/20, live VALIDATE) inline on the big session model with fable-pinned skill-runner children, then delegates the client deliverable to the two-mode handover-doc-writer (synthesize opus / render sonnet — BDR-077). tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUserQuestion, Agent --- @@ -1130,3 +1130,7 @@ Parse the returned `HANDOVER-DOC REPORT`: - `STATUS: BLOCKED` → surface the report verbatim (including which PACKAGE field the doc-writer flagged) and stop — do not retry or patch the PACKAGE silently. + +In BOTH branches, then clean the transient draft: +`rm -f ".audit/handover-draft-${RUNID}.md"` (run-scoped, gitignored — +cleanup keeps `.audit/` from accumulating stranded drafts). diff --git a/agents/doc-syncer.md b/agents/doc-syncer.md index 7fb0bd5..7411114 100644 --- a/agents/doc-syncer.md +++ b/agents/doc-syncer.md @@ -380,9 +380,10 @@ Omit any section whose delegated target does not exist and is not being proposed this run (e.g. drop "Deploy" entirely when `DEPLOY_COMPLEXITY` is `NONE`/`TRIVIAL`; drop "Configuration" when there is no config schema). -Tag as **AUTO** — create on first audit. Surface the rendered README in -the validation gate before writing so the user can `edit` if needed, but -do NOT skip creation; "skip" is not an offered option on README bootstrap. +Tag as **AUTO** — create on first audit. The rendered README is a DRAFT +inside the audit report (`[CREATE-AUTO]` in the PATCH PLAN); the +DISPATCHER's gate surfaces it so the user can `edit`, but do NOT skip +creation; "skip" is not an offered option on README bootstrap. ### STEP 6 — DEPLOY.md GATE @@ -669,9 +670,10 @@ Last updated: () CHANGELOG entries always HUMAN. DEPLOY.md creation always HUMAN. CLEAN removals always HUMAN. -**README.md creation is AUTO** — always render and write, never gate on -user input. The validation gate (STEP 8) still surfaces the rendered -file so the user can edit before write, but "skip" is not an option for +**README.md creation is AUTO** — always render (audit mode: as a draft +in the report) and write (patch mode), never gate on user input. The +DISPATCHER's validation gate still surfaces the rendered draft so the +user can edit before the patch dispatch, but "skip" is not an option for README bootstrap; it is mandatory. If no drift in any doc and no missing required doc (and, in CLEAN MODE, diff --git a/agents/plugin-advisor.md b/agents/plugin-advisor.md index 106c45c..7d9141e 100644 --- a/agents/plugin-advisor.md +++ b/agents/plugin-advisor.md @@ -46,8 +46,8 @@ Detect signals from REQUEST + the PROBE REPORT fields: | `skill-creation` | "create a skill", "new skill", "custom skill", `/plugin-dev:create-plugin` in description | | `embedded` | "firmware", "bare-metal", "microcontroller", "STM32", "ESP32", "RTOS", "driver", "kernel", "bootloader" in description; **or** `platformio.ini` present; **or** linker script (`*.ld`, `*.lds`) present; **or** `Makefile` + `src/*.c` + no `package.json`/`Cargo.toml`/`go.mod`/`setup.py`/`pyproject.toml` (C project without standard ecosystems). Note: `.c` files with a Rust/Node/Go manifest = FFI binding, NOT embedded. | | `simple` | single file, hotfix, quick script, no frontend, no deploy | -| `anim-lib-eligible` | output of `detect_anim_eligibility` starts with `eligible|` (React/Vue/Svelte stack) | -| `anim-lib-installed` | `is_anim_lib_installed` returns 0 (any of motion / motion-v / framer-motion / gsap / lottie-react / react-spring / popmotion / auto-animate present) | +| `anim-lib-eligible` | PROBE REPORT `ANIM` field: `eligibility=eligible|…` (React/Vue/Svelte stack) | +| `anim-lib-installed` | PROBE REPORT `ANIM` field: `installed=` (any of motion / motion-v / framer-motion / gsap / lottie-react / react-spring / popmotion / auto-animate) | --- diff --git a/lib/tests/model-routing.test.sh b/lib/tests/model-routing.test.sh index 7e56af8..ae5342b 100755 --- a/lib/tests/model-routing.test.sh +++ b/lib/tests/model-routing.test.sh @@ -114,6 +114,7 @@ has "skills/doc/SKILL.md" 'MODE: patch' lacks "agents/scaffolder.md" 'INLINE-LOAD' for s in bugfix hotfix feat ship-feature init-project; do has "skills/$s/SKILL.md" 'MODE: audit' + has "skills/$s/SKILL.md" 'doc-syncer", model="opus"' # shellcheck disable=SC2016 # literal $HOME wanted: matching the exact inline-load string lacks "skills/$s/SKILL.md" 'Load `$HOME/.claude/agents/doc-syncer.md`' done @@ -160,6 +161,9 @@ has "skills/seo/SKILL.md" 'never re-derive a score' has "skills/seo/SKILL.md" 'DISPATCHER ERROR CONTRACT' has "skills/geo/SKILL.md" 'MODE: collect' has "skills/geo/SKILL.md" 'geo-analyzer", model="sonnet"' +has "skills/geo/SKILL.md" 'ERROR CONTRACT' +has "skills/geo/SKILL.md" 'never re-derive a score' +has "agents/handover-doc-writer.md" 'SYNTH REPORT' printf 'model-routing census: %d pass, %d fail\n' "$pass" "$fail" [ "$fail" -eq 0 ] diff --git a/skills/client-handover/SKILL.md b/skills/client-handover/SKILL.md index 2fb9a7a..dad6e8b 100644 --- a/skills/client-handover/SKILL.md +++ b/skills/client-handover/SKILL.md @@ -29,9 +29,11 @@ Load and follow strictly: - $HOME/.claude/agents/client-handover-writer.md Execute the CLIENT HANDOVER WRITER agent on this project. It runs the -audit/fix/gate pipeline INLINE on the big session model (gated above), then -delegates the client deliverable (Markdown + branded HTML + PDF) to the -sonnet-pinned `handover-doc-writer` subagent (BDR-066). +audit/fix/gate pipeline INLINE on the big session model (gated above), its +skill-runner children dispatched `model: "fable"`, then delegates the +client deliverable to the two-mode `handover-doc-writer` subagent — +synthesize on opus, render (Markdown + branded HTML + PDF) on the sonnet +pin (BDR-077). The agent runs a **ship-and-handover pipeline** with explicit gates: diff --git a/skills/commit-change/SKILL.md b/skills/commit-change/SKILL.md index 0e86daa..d47c53f 100644 --- a/skills/commit-change/SKILL.md +++ b/skills/commit-change/SKILL.md @@ -82,8 +82,9 @@ AskUserQuestion: uncommitted for a later run. - `edit ` → re-dispatch `commit-changer` with `MODE: propose` and the user's correction for step N folded into the prompt, so all grouping / - message judgment stays on the sonnet subagent (never redrawn inline on - the session model); show the redrawn plan and re-ask. + message judgment stays on the dispatched propose mode (`model="opus"`, + BDR-077 — never redrawn inline on the session model); show the redrawn + plan and re-ask. - `skip` → exit cleanly, no commits created, no `MODE: apply` dispatch. Note: if the propose run created a `chore/*` branch (gitflow aiguillage off a protected base), that branch stays checked out with the work diff --git a/skills/init-project/SKILL.md b/skills/init-project/SKILL.md index 9567fb8..e4832e2 100644 --- a/skills/init-project/SKILL.md +++ b/skills/init-project/SKILL.md @@ -104,12 +104,14 @@ Creates: CLAUDE.md, `.claude/settings.json`, `.claudeignore`, `.gitignore`, `.en Verify: `git init` + build passes. ## STEP 5b — CREATE README -Dispatch the doc pipeline (BDR-077): `Agent(subagent_type="doc-syncer", -model="opus")` — `MODE: audit`, `auto-mode scope: full project`. README.md -missing → the report carries the rendered README draft as `[CREATE-AUTO]`; -re-dispatch `Agent(subagent_type="doc-syncer")` (sonnet pin) with -`MODE: patch` + that plan to write it. No stop (README bootstrap is -unconditional). +Dispatch the doc pipeline (BDR-077): +`Agent(subagent_type="doc-syncer", model="opus")` +— `MODE: audit` (FULL-AUDIT path, NOT `auto-mode scope:` — +auto-mode gates a missing README as SIGNIFICANT; the full audit's STEP 5 +renders it `[CREATE-AUTO]`, unconditional). README.md missing → the +report carries the rendered README draft as `[CREATE-AUTO]`; re-dispatch +`Agent(subagent_type="doc-syncer")` (sonnet pin) with `MODE: patch` + +that plan to write it. No stop (README bootstrap is unconditional). ## STEP 5c — CTX7 PRE-FETCH (if fast-libs detected) If `fast-libs` signal was detected in STEP 0 (Next.js, React 18+, Prisma, Supabase, Drizzle, etc.): @@ -298,8 +300,9 @@ does NOT commit them, and `gitflow finish` integrates only COMMITTED history — so a patch left uncommitted never reaches the merge/PR. Same PR-stranding class as the STEP 10b capitalize fix (BDR-034). -Dispatch the doc pipeline (BDR-077): `Agent(subagent_type="doc-syncer", -model="opus")` — `MODE: audit` + `auto-mode scope: `; NONE → done; `[MINOR]` plan → `MODE: patch` re-dispatch (sonnet pin, no gate; SHAPE ESCALATION comes back gated); SIGNIFICANT → gate here, then `MODE: patch` with the approved subset.