Merge feature/model-tiering-w2-conversions into develop

This commit is contained in:
Bastien Chanot
2026-07-19 22:42:39 +02:00
15 changed files with 449 additions and 222 deletions
+80 -53
View File
@@ -1,6 +1,6 @@
---
name: doc-syncer
description: Detect stale PUBLIC documentation by cross-referencing git history against the doc layout (README, CHANGELOG, docs/**…) — dispatched by /doc and orchestrators. Convention-aware (Diátaxis, Keep a Changelog); never touches .claude/. Audit, report, patch.
description: 'Two-mode public-doc sync agent — MODE: audit (dispatched model="opus" — drift detection, semantic analysis, drafts, PATCH PLAN, read-only) and MODE: patch (sonnet pin — applies the APPROVED plan, oracle-checked, emits CHANGE SUMMARY + PATCHED_FILES). The validation gate lives in the DISPATCHER (BDR-077). Convention-aware (Diátaxis, Keep a Changelog); never touches .claude/.'
tools: Read, Write, Edit, Bash, Grep, Glob
model: sonnet
---
@@ -54,18 +54,25 @@ audit, report, and patch.
---
## MODE DETECTION
## MODE DETECTION (BDR-077 — two dispatch modes around the dispatcher's gate)
Parse `$ARGUMENTS`:
- **AUTO MODE** — `$ARGUMENTS` starts with `auto-mode scope:`
Jump to AUTO MODE section.
- **FULL AUDIT** — anything else (empty, file list, description).
Run the full audit workflow.
- **CLEAN MODE** — set when `$ARGUMENTS` contains the token `clean`.
Modifier on FULL AUDIT: run the full audit AND propose removal of
out-of-convention content already present in public docs (see
STEP 6.5). Not a separate flow.
- **`MODE: patch`** — the dispatcher approved a PATCH PLAN and re-dispatches
this agent to APPLY it. Jump to MODE: PATCH section. Runs on the sonnet
frontmatter pin.
- **`MODE: audit`** (or no explicit MODE — audit is the default) — analysis
half, dispatched with `model: "opus"` (judgment tier; the call-site
override takes precedence over the sonnet pin). **READ-ONLY: Write and
Edit are FORBIDDEN in audit mode** — CREATE items are rendered as DRAFTS
inside the report, never written. Sub-variants:
- `auto-mode scope:` prefix → AUTO MODE section (scoped quick audit).
- `clean` token → CLEAN modifier on the full audit (STEP 6.5).
- anything else → FULL AUDIT workflow.
- **The validation gate is NOT yours.** A dispatched agent cannot ask the
user. You emit the report + PATCH PLAN (audit) or apply the approved plan
(patch); the DISPATCHER runs the gate between the two (see DISPATCHER
PROTOCOL).
---
@@ -670,11 +677,27 @@ README bootstrap; it is mandatory.
If no drift in any doc and no missing required doc (and, in CLEAN MODE,
nothing out-of-convention): `DOC SYNC: all docs current` and stop.
### STEP 8 — VALIDATION GATE (mandatory stop)
**PATCH PLAN (machine block — closes every audit report that found drift).**
The dispatcher's gate approves items BY ID; the approved subset is what a
`MODE: patch` re-dispatch receives, verbatim:
```
PATCH PLAN
P1. [AUTO] <file> — <section> — <exact change, diffable>
P2. [HUMAN] <file> — <section> — <exact change> — reason: <…>
C1. [CREATE-AUTO] README.md — write the rendered draft above
C2. [CREATE-HUMAN] DEPLOY.md — write the rendered draft above
R1. [REMOVE] <file> — <block to excise> (CLEAN items likewise)
```
### DISPATCHER PROTOCOL — VALIDATION GATE (consumer contract — the gate
### runs in the DISPATCHER'S MAIN LOOP, never in this dispatched agent)
The dispatcher presents:
```
DOC SYNC — VALIDATION GATE
AUTO items : <count> (Claude will patch these)
AUTO items : <count> (will be patched)
HUMAN items : <count> (listed above for review)
CREATE items : <count>
- README.md (AUTO — will be written; `edit` to refine the rendered draft)
@@ -694,22 +717,40 @@ README.md CREATE is unconditional: the only valid responses are `yes`
write). Treat any `no` / `skip` answer to README as `edit` and prompt
the user for the specific changes they want.
Wait for explicit approval. Do not proceed without it.
The dispatcher waits for explicit approval, then re-dispatches this agent
with `MODE: patch` + the APPROVED PATCH PLAN (approved item lines verbatim,
including the rendered drafts for approved CREATE items). Nothing is
applied without that round-trip.
### STEP 9 — PATCH
## MODE: PATCH
Apply only approved items. **Never write under `.claude/` or to
`CLAUDE.md`** — they are not targets under any circumstance.
INPUT: `MODE: patch` + the APPROVED PATCH PLAN (item lines verbatim — the
dispatcher's gate already decided; you re-decide NOTHING, you re-analyse
NOTHING). Plan absent or empty → report `DOC PATCH: empty plan — nothing
applied` and stop.
Apply only the listed items. **Never write under `.claude/` or to
`CLAUDE.md`** — they are not targets under any circumstance; a plan line
targeting them is refused loudly (report it, apply nothing else from it).
- Surgical Edit for AUTO items. Preserve structure and tone.
- Write for approved CREATE items (README, DEPLOY). Use real project
data only — no `<TODO>` placeholders, no fabricated feature
descriptions.
- Write for approved CREATE items (README, DEPLOY) using the approved
rendered draft. Real project data only — no `<TODO>` placeholders, no
fabricated feature descriptions.
- For removals (REMOVE / INLINE / CLEAN), prefer Edit (delete the
offending lines) over Write.
- Re-read each modified file post-edit to verify no broken markdown,
no orphaned references.
- **Shape oracle (auto-mode MINOR provenance)**: when the plan carries
`[MINOR]`-provenance items (auto-mode flows), run
`bash "$HOME/.claude/lib/doc-shape.sh" check <every patched path>` (all
paths, ONE call) AFTER patching. exit 0 → keep. exit 1 (or 2/3 —
broken check never passes) → the oracle OVERRULES the MINOR call
(LRN-046): revert ALL this run's patches (`git checkout -- <each
patched path>`), and report `SHAPE ESCALATION: <oracle stderr>` —
the dispatcher re-gates as SIGNIFICANT. Never keep an out-of-shape
auto-patch.
### OUTPUT
### OUTPUT (MODE: patch)
```
DOC SYNC COMPLETE
@@ -719,6 +760,9 @@ CREATED : <count> files
REMOVED : <count> files / sections
HUMAN PENDING: <count> items (see report above)
SKIPPED : <count> (user declined)
CHANGE SUMMARY: (one line per patched file — what changed and why; the
doc-commit step's rc-0 visible surface consumes THIS, LRN-126)
<path> — <one line: what changed>
PATCHED_FILES: (one real path per LINE below; "(none)" if no write)
<path created or modified this run>
<path created or modified this run>
@@ -788,46 +832,29 @@ Categorize:
artifact (Dockerfile, fly.toml, workflow) without DEPLOY.md update or
creation.
### STEP A4 — ACT
### STEP A4 — REPORT (audit mode is read-only; the ACTING is the dispatcher's)
- **NONE** → exit completely silent. No output (no `PATCHED_FILES` → the doc-commit step
sees an empty list and no-ops).
- **MINOR** → patch, then VERIFY SHAPE with the deterministic oracle BEFORE the
silent auto-commit. The LLM made the MINOR call; the oracle re-checks that the
patch's SHAPE actually holds, catching a SIGNIFICANT mislabeled MINOR (RISK-1):
```
bash "$HOME/.claude/lib/doc-shape.sh" check <every patched path> # all paths, ONE call
```
- **exit 0** (within the MINOR envelope) → genuine MINOR: keep the silent patch.
One-line confirmation per file: `doc-sync: patched <file> (<what changed>)`.
Proceed to `PATCHED_FILES` + the doc-commit step.
- **exit 1** (shape EXCEEDS — oracle stderr names the offender(s) and why) → the
deterministic oracle OVERRULES the LLM's MINOR call (LRN-046). Do NOT auto-commit.
ESCALATE the WHOLE patch set to the SIGNIFICANT gate below — one file out of
shape makes the atomic MINOR classification suspect. Surface every patched file
+ the oracle's reason, then the gate: on `no` → revert ALL
(`git checkout -- <each patched path>`); on `select` → keep the chosen files,
revert the rest. The oracle catches STRUCTURAL/size significance, not semantic —
it is a deterministic floor, not a full SIGNIFICANT-detector.
- **exit 2/3** (oracle usage error / not a git repo) → do NOT auto-commit on a
broken check; treat as exit 1 and escalate.
- **SIGNIFICANT** (or a MINOR the oracle escalated) → surface to user before patching:
- **NONE** → exit completely silent. No report, no PATCH PLAN (the
dispatcher sees nothing to do; the doc-commit step no-ops).
- **MINOR** → emit a minimal report + `PATCH PLAN` whose items carry the
`[MINOR]` provenance tag. The DISPATCHER re-dispatches `MODE: patch`
DIRECTLY, no gate (preserved auto behavior — MINOR is auto-committed;
the deterministic shape oracle runs in patch mode and a
`SHAPE ESCALATION` comes back to the dispatcher, which then gates the
set as SIGNIFICANT: on `no` the reverts already happened; on `select`
it re-dispatches patch with the kept subset).
- **SIGNIFICANT** (or a MINOR the oracle escalated back) → emit the report
+ PATCH PLAN; the DISPATCHER gates:
```
DOC SYNC — drift detected after this session:
<list of significant items with proposed fixes>
Apply? (yes / no / select)
```
Wait for approval.
then re-dispatches `MODE: patch` with the approved subset.
After writing in MINOR or approved-SIGNIFICANT, emit the machine-readable handle the
doc-commit step (`lib/doc-commit.md`) consumes — ONE real path PER LINE:
```
PATCHED_FILES:
<path created or modified this run>
<path created or modified this run>
```
Emit ONLY when something was written; NONE stays silent. Never lists `.claude/**` or
`CLAUDE.md` (never targets, BDR-022).
`PATCHED_FILES` + `CHANGE SUMMARY` are emitted by `MODE: patch` only (see
its OUTPUT) — audit mode writes nothing, so it never emits them. Neither
ever lists `.claude/**` or `CLAUDE.md` (never targets, BDR-022).
---
+25 -118
View File
@@ -1,71 +1,35 @@
---
name: plugin-advisor
description: Plugin-fit checker — dispatched by /plugin-check and orchestrator gates (init-project, ship-feature). Recommends enable/disable.
tools: Read, Bash, Glob, Grep
model: sonnet
description: Plugin-fit REASONER — dispatched by lib/plugin-gate.md with a PROBE REPORT (from plugin-probe). Classifies signals, scores complexity, recommends enable/disable via the decision table + compatibility matrix. Report-only.
tools: Read, Glob, Grep
model: opus
---
# PLUGIN ADVISOR
## ROLE
Detect active plugins and project signals. Recommend enable/disable. Apply compatibility matrix. Block or warn as needed.
Reason over the PROBE REPORT + request. Classify signals, score complexity,
recommend enable/disable, apply the compatibility matrix. Block or warn.
Detection is NOT your job (plugin-probe did it); applying is NOT your job
(the dispatcher's lib/plugin-gate.md apply gate does it).
---
## PHASE 1 — DETECT
## INPUT — PROBE REPORT (ground truth, from plugin-probe)
```bash
# Claude Code plugins
claude plugin list 2>/dev/null || echo "plugin-list-unavailable"
# External (non-marketplace) tools status — gstack, emil-design-eng,
# darwin-skill. Managed by lib/toggle-external.sh since
# `claude plugin enable|disable` does not apply to them.
bash "$HOME/.claude/lib/toggle-external.sh" list 2>/dev/null || echo "toggle-external-unavailable"
# Active skill profile — design / dev / qa / audit / minimal / custom.
# Profiles partition gstack + personal skills by purpose. See
# lib/profile.sh and lib/profiles/*.profile.
bash "$HOME/.claude/lib/profile.sh" current 2>/dev/null || echo "profile-unavailable"
# Context7 CLI
command -v ctx7 &>/dev/null && ctx7 --version 2>/dev/null | head -1 || echo "ctx7-not-installed"
# Standalone CLIs
command -v gsd &>/dev/null && gsd --version 2>/dev/null | head -1 || echo "gsd-not-installed"
command -v rtk &>/dev/null && rtk --version 2>/dev/null | head -1 || echo "rtk-not-installed"
# Project signals (run from project root)
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null | head -5
grep -rl "next\|react\|vue\|prisma\|supabase" package.json 2>/dev/null | head -3 || true
find . -name "*.tsx" -o -name "*.jsx" 2>/dev/null | head -3 | wc -l
find . -name "docker-compose*" -o -name "Dockerfile" 2>/dev/null | head -3 | wc -l
# Animation lib status (motion / motion-v) — read-only detection
if [ -f "$HOME/.claude/lib/animation-lib-check.sh" ]; then
source "$HOME/.claude/lib/animation-lib-check.sh"
detect_anim_eligibility # outputs '<status>|<package>|<reason>'
is_anim_lib_installed || echo "anim-lib-not-installed"
fi
# Monorepo detection (current dir + parent dirs for sub-package context)
ls apps/ packages/ services/ workspaces/ 2>/dev/null | head -5
ls pnpm-workspace.yaml turbo.json nx.json lerna.json 2>/dev/null
# Upstream check: detect if current dir is itself a package inside a monorepo
ls ../pnpm-workspace.yaml ../turbo.json ../nx.json ../../turbo.json ../../pnpm-workspace.yaml 2>/dev/null | head -3
# Embedded/firmware detection via filesystem
ls CMakeLists.txt platformio.ini 2>/dev/null
ls *.ld *.lds linker*.ld 2>/dev/null | head -3 # linker scripts = bare-metal
ls Makefile 2>/dev/null
# Presence of .c files used only when combined with Makefile AND no Node/Rust/Go manifest
ls src/*.c 2>/dev/null | head -3
ls package.json Cargo.toml go.mod pubspec.yaml setup.py pyproject.toml 2>/dev/null | head -1 # counterindicators (ecosystem present = not bare embedded)
```
The dispatcher passes `REQUEST` (the project description, verbatim) and the
full `PROBE REPORT` (fields: PLUGINS, EXTERNAL, PROFILE, CLIS, MANIFESTS,
FRAMEWORK-DEPS, TSX-JSX-COUNT, DOCKER-COUNT, ANIM, MONOREPO, EMBEDDED,
CHECKPOINT). Treat it as ground truth — never re-detect, never invent a
field. PROBE REPORT missing or a field absent → emit
`PLUGIN CHECK — VERDICT: ERROR(probe report missing/invalid: <what>)` and
STOP. Fail closed: no recommendations over invented detection.
---
## PHASE 2 — ANALYZE $ARGUMENTS
## PHASE 2 — ANALYZE
Detect signals from the project description and filesystem scan:
Detect signals from REQUEST + the PROBE REPORT fields:
| Signal | How to detect |
|---|---|
@@ -146,70 +110,11 @@ ACTION REQUIRED? YES / NO
> packages itself — it just states the status. Installation happens in
> `/init-project` STEP 5e (auto) or `/onboard` STEP 2.5 (opt-in).
## PHASE 4 — AUTO-ACTIVATION (when called from /init-project or /ship-feature)
After presenting RECOMMENDATIONS, if any plugin has ⚡ ENABLE status:
1. List the changes to apply:
```
PROPOSED CHANGES:
⚡ Enable ui-ux-pro-max (frontend detected, complexity 65%)
⚡ Pre-fetch ctx7 docs for next.js, prisma
Apply these changes? (yes / no / customize)
```
2. On "yes" → apply changes (rename .disabled dirs, update MCP config).
3. On "customize" → user picks which to apply.
4. On "no" → proceed with current config.
**Never auto-activate without showing the list and getting confirmation.**
### Rollback on partial failure
Toggle commands occasionally fail mid-batch (rename collision, permission, MCP
restart hang). Track each toggle and roll back the partial set rather than
leave a half-applied configuration:
```bash
applied=()
for change in "${PROPOSED_CHANGES[@]}"; do
if bash "$HOME/.claude/lib/toggle-external.sh" enable "$change"; then
applied+=("$change")
else
echo "❌ failed to enable $change — rolling back ${#applied[@]} prior change(s)"
for prior in "${applied[@]}"; do
bash "$HOME/.claude/lib/toggle-external.sh" disable "$prior" \
|| echo "⚠️ rollback of $prior also failed — manual cleanup required: see ~/.claude/plugins/cache"
done
exit 1
fi
done
```
Surface to the user:
```
✅ Applied N change(s).
```
Or, on failure:
```
⚠️ Toggle failed at change <name>. Rolled back the N prior change(s).
To inspect manually: ls ~/.claude/plugins/cache; bash ~/.claude/lib/toggle-external.sh list
Re-run /plugin-check after fixing the underlying cause (e.g. permissions).
```
### Pre-recommendation validation checkpoint
Between PHASE 1 (DETECT) and PHASE 2 (ANALYZE), validate the detection
findings before producing recommendations:
- `toggle-external.sh list` returned non-empty AND each listed plugin's
directory exists in `~/.claude/plugins/cache` or `~/.agents/skills/`.
- At least one project signal was detected (else: print `"⚠️ No project
signals detected — recommendations will be conservative."` and continue).
- If `toggle-external.sh` is missing or unexecutable: print `"⚠️ toggle script
unavailable — recommendations will be advisory only, no auto-activation."`
and skip PHASE 4 entirely.
> **Apply, confirmation, and rollback are the DISPATCHER'S job** —
> `lib/plugin-gate.md` steps 4-5 (main loop: present, ACTION-REQUIRED stop,
> PROPOSED-CHANGES confirmation, toggle + rollback). This agent only
> recommends and emits the EXACT toggle commands. It never applies, never
> asks the user (it cannot — it is dispatched).
---
@@ -418,4 +323,6 @@ or by applying a profile that lists it (e.g. `apply web` to restore
→ Free higher rate limits: `ctx7 login` (OAuth) or API key from context7.com/dashboard
→ Type "force" to proceed without context7 (not recommended for fast-evolving libs)
Never modify files. If action required → stop and wait. If not → say "proceed".
Never modify files. Never ask the user. Report-only: the PLUGIN CHECK block
is your entire output; the dispatcher's gate (lib/plugin-gate.md) owns the
stop/proceed decision and every state change.
+90
View File
@@ -0,0 +1,90 @@
---
name: plugin-probe
description: Mechanical detection probe — dispatched by lib/plugin-gate.md BEFORE the plugin-advisor reasoner. Runs the CLI/filesystem probes, reports raw facts as a PROBE REPORT. No analysis, no recommendations.
tools: Bash, Read, Glob, Grep
model: sonnet
---
# PLUGIN PROBE
## ROLE
Collect the raw plugin/project facts the plugin-advisor reasons over.
Facts only — no signals, no recommendations, no complexity scoring.
## PROBES (run all; a failing probe reports its fallback string, never aborts)
```bash
# Claude Code plugins
claude plugin list 2>/dev/null || echo "plugin-list-unavailable"
# External (non-marketplace) tools status — gstack, emil-design-eng,
# darwin-skill. Managed by lib/toggle-external.sh since
# `claude plugin enable|disable` does not apply to them.
bash "$HOME/.claude/lib/toggle-external.sh" list 2>/dev/null || echo "toggle-external-unavailable"
# Active skill profile — design / dev / qa / audit / minimal / custom.
bash "$HOME/.claude/lib/profile.sh" current 2>/dev/null || echo "profile-unavailable"
# Context7 CLI
command -v ctx7 &>/dev/null && ctx7 --version 2>/dev/null | head -1 || echo "ctx7-not-installed"
# Standalone CLIs
command -v gsd &>/dev/null && gsd --version 2>/dev/null | head -1 || echo "gsd-not-installed"
command -v rtk &>/dev/null && rtk --version 2>/dev/null | head -1 || echo "rtk-not-installed"
# Project signals (run from project root)
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null | head -5
grep -rl "next\|react\|vue\|prisma\|supabase" package.json 2>/dev/null | head -3 || true
find . -name "*.tsx" -o -name "*.jsx" 2>/dev/null | head -3 | wc -l
find . -name "docker-compose*" -o -name "Dockerfile" 2>/dev/null | head -3 | wc -l
# Animation lib status (motion / motion-v) — read-only detection
if [ -f "$HOME/.claude/lib/animation-lib-check.sh" ]; then
source "$HOME/.claude/lib/animation-lib-check.sh"
detect_anim_eligibility # outputs '<status>|<package>|<reason>'
is_anim_lib_installed || echo "anim-lib-not-installed"
fi
# Monorepo detection (current dir + parent dirs for sub-package context)
ls apps/ packages/ services/ workspaces/ 2>/dev/null | head -5
ls pnpm-workspace.yaml turbo.json nx.json lerna.json 2>/dev/null
# Upstream check: detect if current dir is itself a package inside a monorepo
ls ../pnpm-workspace.yaml ../turbo.json ../nx.json ../../turbo.json ../../pnpm-workspace.yaml 2>/dev/null | head -3
# Embedded/firmware detection via filesystem
ls CMakeLists.txt platformio.ini 2>/dev/null
ls *.ld *.lds linker*.ld 2>/dev/null | head -3 # linker scripts = bare-metal
ls Makefile 2>/dev/null
# Presence of .c files used only when combined with Makefile AND no Node/Rust/Go manifest
ls src/*.c 2>/dev/null | head -3
ls package.json Cargo.toml go.mod pubspec.yaml setup.py pyproject.toml 2>/dev/null | head -1 # counterindicators (ecosystem present = not bare embedded)
# Checkpoint inputs (consumed by lib/plugin-gate.md's validation checkpoint)
[ -x "$HOME/.claude/lib/toggle-external.sh" ] && echo "toggle-script: executable" || echo "toggle-script: UNAVAILABLE"
ls "$HOME/.claude/plugins/cache" 2>/dev/null | head -10
ls "$HOME/.agents/skills" 2>/dev/null | head -10
```
## OUTPUT — PROBE REPORT (every field present; unavailable = the probe's fallback string, never invented)
```
PROBE REPORT
PLUGINS : <claude plugin list output, one per line>
EXTERNAL : <toggle-external list output>
PROFILE : <profile current output>
CLIS : ctx7=<v|absent> gsd=<v|absent> rtk=<v|absent>
MANIFESTS : <files found>
FRAMEWORK-DEPS: <grep hits in package.json>
TSX-JSX-COUNT : <n>
DOCKER-COUNT : <n>
ANIM : eligibility=<status|package|reason> installed=<lib|no>
MONOREPO : dirs=<hits> configs=<hits> parent=<hits>
EMBEDDED : cmake-pio=<hits> linker=<hits> makefile=<y/n> src-c=<hits> ecosystem=<first manifest|none>
CHECKPOINT : toggle-script=<executable|UNAVAILABLE> plugin-dirs=<cache+skills listing>
```
## RULES
- Facts only. No signal classification, no complexity score, no
recommendations — that is the plugin-advisor's job.
- Never modify files. Never install anything. Never ask the user
(you cannot — report facts instead).
- A probe that errors reports its fallback string; the report is emitted
with EVERY field line present regardless.
+5 -11
View File
@@ -123,16 +123,10 @@ INSTALL : ✅ / ❌ <error>
BUILD : ✅ / ❌ <error>
DOCKER BUILD: ✅ / ⚠️ not verified / N/A
STRUCTURE: <tree>
READY: <N> v1 features | entry points ✅ | config ✅ | CLAUDE.md ✅ | README → doc-syncer | settings ✅
READY: <N> v1 features | entry points ✅ | config ✅ | CLAUDE.md ✅ | README → init-project STEP 5b | settings ✅
```
---
## PHASE 6 — DOC SYNC (automatic)
**INLINE-LOAD** `$HOME/.claude/agents/doc-syncer.md` — continue AS
doc-syncer in THIS SAME context (you *become* it). This is an inline load,
NOT a subagent dispatch: the `Agent` tool is not involved (which is why
this agent correctly omits `Agent` from its `tools:`). Execute in
automatic mode:
`auto-mode scope: <list of all files created during scaffolding>`
> No doc step here (BDR-077): the scaffolder produces NO docs. The README
> bootstrap is init-project STEP 5b's job — a doc-syncer `MODE: audit`
> (opus) → `MODE: patch` (sonnet) dispatch pipeline owned by the
> orchestrator, never an inline-load inside this executor.
+13 -8
View File
@@ -17,23 +17,28 @@ and any SIGNIFICANT-gated patch), with the code already committed.
- Orchestrators (ship-feature / init-project): run it BEFORE the FINISH step — otherwise
the doc commit strands outside the merge/PR (the exact bug this fixes). See ORDERING.
doc-syncer runs IN-THREAD (the orchestrator loads it), so the list of files it patched is
already in hand — surfaced as `PATCHED_FILES:` in doc-syncer's OUTPUT, ONE PATH PER LINE.
Pass each line as a SEPARATE argument (see DO step 3).
doc-syncer runs DISPATCHED (BDR-077: `MODE: audit` on opus → dispatcher gate
→ `MODE: patch` on sonnet); its patch-mode report hands the orchestrator BOTH
machine blocks: `PATCHED_FILES:` (ONE PATH PER LINE — pass each line as a
SEPARATE argument, see DO step 3) and `CHANGE SUMMARY` (one line per patched
file — the patch context that used to be in-thread now crosses the dispatch
boundary through this block, LRN-126).
## DO
1. Collect `PATCHED_FILES` — the public-doc paths doc-syncer wrote this run (its OUTPUT
block, ONE PATH PER LINE). Empty → nothing to commit; the helper no-ops.
2. Compose — from the patch context the AGENT holds (doc-syncer ran in-thread, so the
agent knows exactly what changed) — BOTH artifacts:
2. Compose — from doc-syncer's `CHANGE SUMMARY` block (the patcher held the
patch context and reported it; a dispatched patcher with NO summary block
in its report = incomplete report, re-dispatch rather than invent) —
BOTH artifacts:
- the COMMIT MESSAGE, repo style `docs: <summary> — <flow>`
(`docs: README features + USAGE flags — ship-feature dark-mode`);
- the CHANGE SUMMARY for the rc 0 surface (e.g. "README features section + USAGE
--export flag").
Both are the AGENT's to write — the helper produces NEITHER (its only stdout is the
hash). This is the load-bearing point of the visible surface: see the rc 0 row.
--export flag") — derived from the block, never a bare file count.
Both are the ORCHESTRATOR's to write — the helper produces NEITHER (its only stdout
is the hash). This is the load-bearing point of the visible surface: see the rc 0 row.
3. Commit surgically via the helper, passing EXACTLY the patched files — each path as a
SEPARATE argument (split `PATCHED_FILES` on NEWLINES only), capturing the hash:
+90
View File
@@ -0,0 +1,90 @@
# Plugin gate — shared consumer include (plugin-check, onboard, init-project, ship-feature STEP 0)
Runs in the CONSUMER'S MAIN LOOP. The detection and the reasoning are
dispatched (BDR-077 tiers); the validation checkpoint, the report
presentation, and the apply gate live HERE — a dispatched agent can neither
ask the user nor safely mutate plugin state.
## 1. PROBE (dispatch — sonnet)
```
Agent(subagent_type="plugin-probe", description="plugin gate — probe",
prompt="Run your probes from <PROJECT_ROOT>. Emit the PROBE REPORT.")
```
## 2. VALIDATION CHECKPOINT (main loop — between probe and reasoner)
Validate the PROBE REPORT before any reasoning:
- `EXTERNAL` non-empty AND each listed plugin's directory appears under
`CHECKPOINT plugin-dirs`.
- At least one project signal present (MANIFESTS / FRAMEWORK-DEPS /
TSX-JSX-COUNT > 0 / DOCKER-COUNT > 0 / EMBEDDED hits). Else print
`⚠️ No project signals detected — recommendations will be conservative.`
and continue.
- `CHECKPOINT toggle-script=UNAVAILABLE` → print `⚠️ toggle script
unavailable — recommendations will be advisory only, no auto-activation.`
and SKIP step 5 (apply) entirely.
- PROBE REPORT missing/unparsable → retry the probe ONCE fresh; a 2nd
failure → STOP and surface (never reason over invented detection).
## 3. REASON (dispatch — opus)
```
Agent(subagent_type="plugin-advisor", description="plugin gate — reason",
prompt="""
REQUEST: <the user's request / project description, verbatim>
PROBE REPORT (ground truth — do not re-detect):
<the full PROBE REPORT from step 1>
""")
```
## 4. PRESENT + BLOCKING GATE (main loop)
Show the returned PLUGIN CHECK block.
- `ACTION REQUIRED? YES` → offer: A) fix plugins B) type "force". STOP until
answered.
- OK → print `✅ Plugin check passed — [active plugins] — complexity: <score>%`.
## 5. APPLY GATE (main loop — only when the flow auto-activates)
If any plugin has ⚡ ENABLE status:
1. List the changes:
```
PROPOSED CHANGES:
⚡ Enable ui-ux-pro-max (frontend detected, complexity 65%)
⚡ Pre-fetch ctx7 docs for next.js, prisma
Apply these changes? (yes / no / customize)
```
2. "yes" → apply via the exact commands the advisor emitted. "customize" →
user picks. "no" → proceed with current config.
**Never auto-activate without showing the list and getting confirmation.**
### Rollback on partial failure
Track each toggle; roll back the partial set rather than leave a
half-applied configuration:
```bash
applied=()
for change in "${PROPOSED_CHANGES[@]}"; do
if bash "$HOME/.claude/lib/toggle-external.sh" enable "$change"; then
applied+=("$change")
else
echo "❌ failed to enable $change — rolling back ${#applied[@]} prior change(s)"
for prior in "${applied[@]}"; do
bash "$HOME/.claude/lib/toggle-external.sh" disable "$prior" \
|| echo "⚠️ rollback of $prior also failed — manual cleanup required: see ~/.claude/plugins/cache"
done
exit 1
fi
done
```
Surface: `✅ Applied N change(s).` — or on failure:
```
⚠️ Toggle failed at change <name>. Rolled back the N prior change(s).
To inspect manually: ls ~/.claude/plugins/cache; bash ~/.claude/lib/toggle-external.sh list
Re-run /plugin-check after fixing the underlying cause (e.g. permissions).
```
+44
View File
@@ -81,6 +81,50 @@ has "skills/ship-feature/SKILL.md" 'model: "opus"'
has "skills/init-project/SKILL.md" 'model: "opus"'
has "lib/model-gate.md" 'model: "fable"'
lacks "lib/model-gate.md" 'model: "sonnet" in the Agent call'
# 13) BDR-077 W2 — plugin split: probe (sonnet, facts only) + advisor
# reasoner (opus, PROBE REPORT is ground truth, fail-closed); gate
# include owns checkpoint + apply; 4 consumers run the include, none
# inline-loads the advisor anymore
has "agents/plugin-probe.md" 'model: sonnet'
lacks "agents/plugin-probe.md" 'AskUserQuestion'
has "agents/plugin-advisor.md" 'model: opus'
has "agents/plugin-advisor.md" 'PROBE REPORT'
lacks "agents/plugin-advisor.md" 'PHASE 1 — DETECT'
has "lib/plugin-gate.md" 'subagent_type="plugin-probe"'
has "lib/plugin-gate.md" 'subagent_type="plugin-advisor"'
for s in plugin-check onboard init-project ship-feature; do
has "skills/$s/SKILL.md" 'lib/plugin-gate.md'
# shellcheck disable=SC2016 # literal $HOME wanted: matching the exact inline-load string
lacks "skills/$s/SKILL.md" 'Load `$HOME/.claude/agents/plugin-advisor.md`'
done
# 14) BDR-077 W2/S2 — doc pipeline: ONE agent, TWO modes around the
# dispatcher's gate (audit = opus via call-site override — documented
# precedence over the sonnet pin; patch = sonnet pin). Gate hoisted out
# of the agent (a dispatched agent cannot ask); CHANGE SUMMARY crosses
# the dispatch boundary into doc-commit (LRN-126); scaffolder carries no
# doc step; no consumer inline-loads doc-syncer anymore.
has "agents/doc-syncer.md" 'MODE: audit'
has "agents/doc-syncer.md" 'MODE: patch'
has "agents/doc-syncer.md" 'CHANGE SUMMARY'
has "agents/doc-syncer.md" 'DISPATCHER PROTOCOL'
has "lib/doc-commit.md" 'CHANGE SUMMARY'
has "skills/doc/SKILL.md" 'model="opus"'
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'
# 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
# 15) BDR-077 W2 — last inline execution converted: scaffolder + onboarder
# are DISPATCHED (pins live); their gates/arbitration stay in the
# orchestrator loop
has "skills/init-project/SKILL.md" 'subagent_type="scaffolder"'
has "skills/onboard/SKILL.md" 'subagent_type="onboarder"'
# shellcheck disable=SC2016
lacks "skills/init-project/SKILL.md" 'Load `$HOME/.claude/agents/scaffolder.md`'
# shellcheck disable=SC2016
lacks "skills/onboard/SKILL.md" 'Load `$HOME/.claude/agents/onboarder.md`'
printf 'model-routing census: %d pass, %d fail\n' "$pass" "$fail"
[ "$fail" -eq 0 ]
+10 -3
View File
@@ -225,9 +225,16 @@ Parse the `BUGFIX-EXEC REPORT`:
## STEP 7 — DOC SYNC (automatic)
Load `$HOME/.claude/agents/doc-syncer.md`.
Execute in automatic mode:
`auto-mode scope: <list of files modified during this session>`
Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
sonnet pin, gate HERE):
1. `Agent(subagent_type="doc-syncer", model="opus")` — `MODE: audit` +
`auto-mode scope: <list of files modified during this session>`.
2. Silence (NONE) → done. `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan
verbatim (no gate — auto behavior preserved; a `SHAPE ESCALATION` in
its report comes back here, gated as SIGNIFICANT).
3. SIGNIFICANT → gate here (`Apply? yes / no / select`), then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
+23 -8
View File
@@ -18,13 +18,28 @@ allowed-tools:
- Agent
---
Dispatch the doc-syncer as a subagent so its `model: sonnet` pin takes
effect (doc-sync = execution, not the session's big model):
Run the two-mode doc pipeline (BDR-077 — audit judgment on opus, patch on
the sonnet pin, the validation gate in THIS loop; a dispatched agent cannot
hold a gate):
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."
1. AUDIT — dispatch:
`Agent(subagent_type="doc-syncer", model="opus")`
prompt: "MODE: audit. Audit public docs for this project. Context from
the user: $ARGUMENTS. Emit the DOC SYNC REPORT + PATCH PLAN — no writes."
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).
2. GATE — present the report and run the DOC SYNC — VALIDATION GATE from
the agent's DISPATCHER PROTOCOL (AUTO yes/select/cancel; HUMAN, CREATE,
CLEAN per-item; README CREATE has no skip). Wait for explicit approval.
`DOC SYNC: all docs current` → stop here.
3. PATCH — re-dispatch:
`Agent(subagent_type="doc-syncer")` (sonnet pin)
prompt: "MODE: patch." + the APPROVED PATCH PLAN verbatim (approved item
lines + rendered drafts for approved CREATE items). A `SHAPE ESCALATION`
in its report → re-gate the named set here, then re-dispatch patch with
the kept subset.
4. COMMIT — from THIS loop per `$HOME/.claude/lib/doc-commit.md` (surgical:
only the report's `PATCHED_FILES`, summary composed from its
`CHANGE SUMMARY` block, never `.claude/`/`CLAUDE.md`, no-op if nothing
patched).
+10 -3
View File
@@ -200,9 +200,16 @@ VERIFIED : <what was checked>
## STEP 6 — DOC SYNC (automatic)
Load `$HOME/.claude/agents/doc-syncer.md`.
Execute in automatic mode:
`auto-mode scope: <list of files modified during this session>`
Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
sonnet pin, gate HERE):
1. `Agent(subagent_type="doc-syncer", model="opus")` — `MODE: audit` +
`auto-mode scope: <list of files modified during this session>`.
2. Silence (NONE) → done. `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan
verbatim (no gate — auto behavior preserved; a `SHAPE ESCALATION` in
its report comes back here, gated as SIGNIFICANT).
3. SIGNIFICANT → gate here (`Apply? yes / no / select`), then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
+10 -3
View File
@@ -174,9 +174,16 @@ Parse the `HOTFIX-EXEC REPORT`:
## STEP 5 — DOC SYNC (automatic)
Load `$HOME/.claude/agents/doc-syncer.md`.
Execute in automatic mode:
`auto-mode scope: <list of files modified during this session>`
Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
sonnet pin, gate HERE):
1. `Agent(subagent_type="doc-syncer", model="opus")` — `MODE: audit` +
`auto-mode scope: <list of files modified during this session>`.
2. Silence (NONE) → done. `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan
verbatim (no gate — auto behavior preserved; a `SHAPE ESCALATION` in
its report comes back here, gated as SIGNIFICANT).
3. SIGNIFICANT → gate here (`Apply? yes / no / select`), then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
+21 -5
View File
@@ -36,7 +36,8 @@ so the user does not assume Claude has hung.
---
## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE
Load `$HOME/.claude/agents/plugin-advisor.md`. Feed request.
Run `$HOME/.claude/lib/plugin-gate.md`. Feed request (dispatch plugin-probe →
checkpoint → dispatch plugin-advisor; gates stay in this loop — BDR-077).
- ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) fix plugins B) type "force". STOP.
- PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm.
- OK → `✅ Plugin check passed — [active plugins] — complexity: <score>%`, continue.
@@ -91,12 +92,24 @@ contract, each tagged `[gated <date>]`. STEP 9's verifier judges against this
enriched contract.
## STEP 5 — SCAFFOLD
Load `$HOME/.claude/agents/scaffolder.md`. Pass: BRIEF + DESIGN + `~/.claude/templates/project-CLAUDE.md` + `~/.claude/CLAUDE.md`.
Dispatch `Agent(subagent_type="scaffolder")` (pin sonnet, effort high —
BDR-077 : le design est CLOS au gate #1, le scaffold est de l'exécution,
plus jamais inline sur le modèle de session). Pass IN THE PROMPT (LRN-126 —
every field the scaffolder consumes crosses the dispatch): BRIEF (verbatim)
+ DESIGN (verbatim) + paths `~/.claude/templates/project-CLAUDE.md` +
`~/.claude/CLAUDE.md`. A STOP (missing input) comes back as its report —
resolve here, re-dispatch. The ~30s liveness pings are THIS loop's job
while waiting.
Creates: CLAUDE.md, `.claude/settings.json`, `.claudeignore`, `.gitignore`, `.env.example`, empty entry points. NO README, NO features, NO `.claude/tasks/` or `.claude/memory/` (not bootstrapped by this flow — copy from `~/.claude/templates/memory/` manually if wanted before STEP 10b's memory commit).
Verify: `git init` + build passes.
## STEP 5b — CREATE README
Load `$HOME/.claude/agents/doc-syncer.md` (AUTO MODE, scope: full project). README.md missing → its README bootstrap creates it. No stop.
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).
## 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.):
@@ -285,8 +298,11 @@ 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).
Load `$HOME/.claude/agents/doc-syncer.md` (AUTO MODE, scope: files changed this session).
Detect drift, update cmds/vars/structure, add recent changes entry.
Dispatch the doc pipeline (BDR-077): `Agent(subagent_type="doc-syncer",
model="opus")` — `MODE: audit` + `auto-mode scope: <files changed this
session>`; 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.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv
+9 -3
View File
@@ -21,7 +21,7 @@ $ARGUMENTS
## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE
Load `$HOME/.claude/agents/plugin-advisor.md` with hint "onboarding existing project + $ARGUMENTS".
Run `$HOME/.claude/lib/plugin-gate.md` with hint "onboarding existing project + $ARGUMENTS" (dispatch plugin-probe → checkpoint → dispatch plugin-advisor → gates in this loop, BDR-077).
- ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) apply recos B) type "force". STOP.
- PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm.
@@ -89,7 +89,11 @@ STOP. La réponse détermine si STEP 1 tourne une fois (A) ou N fois (C) ou avec
## STEP 2 — BASELINE CONFIG (onboarder agent)
Load `$HOME/.claude/agents/onboarder.md`. Passer un BRIEF minimal issu du filesystem scan :
Dispatch `Agent(subagent_type="onboarder")` (pin sonnet — BDR-077 : config
templating = exécution, plus jamais inline sur le modèle de session). Un
BLOCAGE (clé manquante, CLAUDE.md existant) revient en rapport — l'agent ne
peut pas te demander ; TU arbitres ici puis re-dispatches. Passer un BRIEF
minimal issu du filesystem scan :
- `archetype` (depuis STEP 1)
- `project_name` (depuis package.json/pyproject.toml/README.md/dir name)
- `stack` (depuis manifests détectés)
@@ -531,9 +535,11 @@ flux de dev sont deux formes distinctes ([[BDR-050]] pipeline dev ≠ audit).
```
Agent(
subagent_type="doc-syncer",
model="opus",
description="Onboard — doc drift audit only",
prompt="""
REPORT-ONLY mode — NO edits, NO auto-sync.
MODE: audit — REPORT-ONLY, NO edits, NO auto-sync (no patch dispatch
follows: the report feeds the onboard backlog).
Target: full project at <PROJECT_ROOT>.
Scope:
1. README drift (build/test commands, install steps, usage examples vs actual code)
+10 -4
View File
@@ -2,13 +2,19 @@
name: plugin-check
description: 'Audit active plugins vs project needs. Read-only advisory recommending enable/disable. Triggers: "plugin-check", "quels plugins".'
argument-hint: '[ex: "React + FastAPI" or "Rust CLI, no frontend"]'
allowed-tools: Read, Bash, Glob, Grep
allowed-tools: Read, Bash, Glob, Grep, Agent
---
Load and follow strictly: `$HOME/.claude/agents/plugin-advisor.md`.
Run `$HOME/.claude/lib/plugin-gate.md` on the context below — dispatch
plugin-probe (sonnet) → validation checkpoint → dispatch plugin-advisor
(opus) → present the PLUGIN CHECK block (BDR-077: detection and reasoning
dispatched, gates in this loop).
Analyze active plugins + context below, produce PLUGIN ADVISOR REPORT.
/plugin-check is READ-ONLY advisory: SKIP the gate's step 5 (apply) — show
the advisor's exact toggle commands for the user instead. Never write; user
toggles via `claude plugin enable/disable`.
If `$HOME/.claude/agents/plugin-advisor.md` unreachable: emit `Plugin advisor agent missing.` and STOP. Never write — user toggles via `claude plugin enable/disable`.
If `$HOME/.claude/lib/plugin-gate.md` unreachable: emit `Plugin gate include
missing.` and STOP.
$ARGUMENTS
+9 -3
View File
@@ -20,7 +20,8 @@ $ARGUMENTS
---
## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE
Load `$HOME/.claude/agents/plugin-advisor.md`. Feed request.
Run `$HOME/.claude/lib/plugin-gate.md`. Feed request (dispatch plugin-probe →
checkpoint → dispatch plugin-advisor; gates stay in this loop — BDR-077).
- ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) fix plugins B) type "force". STOP.
- PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm.
- OK → `✅ Plugin check passed — [active plugins] — complexity: <score>%`, continue.
@@ -267,8 +268,13 @@ Run BEFORE STEP 9 FINISH. doc-syncer PATCHES public docs but does NOT commit the
uncommitted (or committed after) never reaches the merge/PR. Same PR-stranding class as the
STEP 7 capitalize fix (BDR-034).
Load `$HOME/.claude/agents/doc-syncer.md`. Execute in automatic mode:
`auto-mode scope: <list of files modified during this session>`
Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
sonnet pin, gate HERE): `Agent(subagent_type="doc-syncer", model="opus")` —
`MODE: audit` + `auto-mode scope: <list of files modified during this
session>`. NONE → done; `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan verbatim
(SHAPE ESCALATION comes back here, gated); SIGNIFICANT → gate here, then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv