# Setup Status

> Reports which brewcode setup skills are installed, stale, partial or missing in this project, compares the version each installed artifact was generated under against the installed plugin, and prints the exact command to run for each. Triggers: setup status, what is installed, what version is installed, что установлено.

- Skill: `gabrielmoreira/setup-status` (Agent Skill)
- Install (CLI): `npx skillmds@latest add gabrielmoreira/setup-status`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gabrielmoreira/setup-status/raw
- Safety review: pending (external: skill-scanner PASS, skillspector WARNING)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: gabrielmoreira (https://skillmd.com/u/gabrielmoreira)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/gabrielmoreira/setup-status

---


<instructions>

# Setup Status

Read-only cross-analysis over every **setup skill** in the brewcode suite. It answers one question:
*what is already set up in this project, what drifted, what was never installed* — and hands back the
exact command to run for each row.

**Every probe is read-only.** No file is created, edited or deleted by the report itself;
`allowed-tools` carries no `Write`, no `Edit`, no `Agent`. Each probe is an existence check, a `cmp`,
or a one-line grep. The single exception is Phase 1c's opt-in one-key merge into a `settings.json`,
which happens only after the user picks it — see *Why it does not run the setups*.

## Prompt contract

Position 1 of `$ARGUMENTS` is a **free-form prompt** (RU/EN). This skill has no modes to select —
report is the only behavior — so the prompt carries exactly one optional decision: which plugin or
skill to filter to.

1. There are no flags and no destructive path — nothing to strip.
2. Extract a plugin name (`brewcode`, `brewtools`, `brewdoc`) or a skill name (`semble-setup`,
   `docsync`, `task board`, ...) from the prompt if one is present; that becomes the filter. Prose
   naming neither is unrecognised text, not an error -> full report.
3. Empty arguments -> full cross-plugin report. Nothing here is chosen by asking.
4. `AskUserQuestion` fires at most ONCE per run, and only for Phase 1c's task-tools offer — the one
   outcome-changing choice this skill has. The filter is never asked about.
5. Prose that is not a plugin/skill name is still input: extract the name from it, never treat the
   first word as a positional filter.

Then print this block ONCE, right before the report (Phase 4) — the only possible mutation is the
Phase 1c env key, and it comes after the report:

```
PLAN — brewcode:setup-status
INPUT:  <arguments verbatim, or "(empty)">
MODE:   report — <full cross-plugin | filtered: <plugin/skill>>
SCOPE:  <resolved plugin(s)/skill(s) in scope>
DO:     <2-5 imperative bullets: resolve plugin roots, probe artifacts, read stamps, classify;
         add "offer to enable the task-graph tools" when Phase 1c's verdict is `off`>
RESULT: <the report the user ends up holding; name the settings.json write when one is offered>
```

Labels are literal; values follow the conversation language.

## Why it does not run the setups

Each setup skill is an interactive generator: it fans out subagents, analyses the repo and asks the
user real questions. Running two of them back-to-back in one session degrades both — the context
fills with the first one's analysis, and the second one's questions get answered against stale
findings. So the correct flow is:

> **This skill reports. The user runs each setup by hand, ideally one per fresh session.**

There is no `--run`, no `--fix`, no auto mode, and no plan to add one. If the user asks for one, say
this paragraph and print the run-list instead.

> **One carve-out, and it is not a setup.** Phase 1c's `CLAUDE_CODE_ENABLE_TODO_TOOLS` offer merges
> ONE key into ONE `settings.json`. It spawns no subagent, analyses nothing, generates nothing, and
> asks exactly one question — none of the properties that make batching setups harmful apply to it.
> The promise above is about the eleven setups and stays whole: this skill still runs none of them.

Outside that single env key, this skill creates, edits and deletes nothing.

**Arguments:** `$ARGUMENTS` — empty = full report. A plugin name (`brewcode`, `brewtools`, `brewdoc`)
filters to that plugin's rows. A skill name (`semble-setup`, `docsync`, `task board`) filters to
that one row and prints its detection rule in full. Unrecognised text = full report.

---

## Two questions, two signals

Every setup artifact in a project carries a `content_version` — the release in which its own content
last actually changed — alongside a `version` — the plugin release that produced this install. The
field contract (`version`, `content_version`, `generated_by`, `last_updated`, `doc_type`, and which
carrier each file type uses) lives in ONE place:
[`references/artifact-metadata.md`](references/artifact-metadata.md). This skill consumes it and
never restates it.

| Signal | The question it answers | Read from |
|--------|------------------------|-----------|
| **content_version stamp** — the headline | *has this artifact's own content moved since it was installed here?* | the artifact's own `content_version` field, compared against the plugin's current `content_version` for that same artifact |
| **owner stamp** — the third signal | *did the setup that OWNS this path actually write it?* | the artifact's own `generated_by`, compared against the roster row's `<plugin>:<skill>` |
| **`cmp` vs the plugin asset** — corroborating | *was this file actually re-copied after the plugin update?* | byte equality against `$BC` / `$BT` / `$BD` |

`version` — which plugin release produced this install — stays readable on every row and every
verdict; it answers a real question, just not this skill's headline one, since it bumps on every
plugin release whether or not this artifact's own content changed, which is exactly what was
producing false `stale` reports before `content_version` existed. Report `content_version` first,
`version` beside it as provenance, the owner when it disagrees, the byte verdict last.

None answers another's question. A hand-edited installed hook still carries the `X.Y.Z`
`content_version` stamp it was copied with, so the stamp cannot see body drift — `cmp` can. And `cmp`
is meaningless for a generated artifact, which is AI-authored per project and never byte-equal to any
asset — only the stamp reaches those. The owner is orthogonal to both: a file written by the WRONG
setup can be at the current `content_version` and byte-perfect, and nothing but `generated_by` will
say so.

Of the five fields in the contract, this skill reads three — `content_version` (headline),
`generated_by`, and `version` (secondary, displayed but never decisive). Why `last_updated` and
`doc_type` are deliberately unread is argued once, in Phase 2a; do not add a reader for them without
an actionable verdict to attach.

Two stamping moments, and the difference matters when reading a row:

| Artifact kind | Stamped | Consequence |
|---------------|---------|-------------|
| byte-copied asset (`.mjs`, `.sh`, `semble-first.md`) | **baked at release** by `bump-version.sh` | the installed copy stays byte-identical to the plugin asset, so `cmp` keeps working alongside the stamp |
| generated artifact (emitted `SKILL.md`, `team.md`, `config.json`) | **substituted at install** | no `cmp` partner exists; the stamp is the only version signal |
| byte-copied asset the install then FILLS (`memory-sync`'s `references/hard-sync.md`) | **baked at release**, then frozen | it ships as mechanism `a` and is `cp`d verbatim, but the generator's own Phase 3 writes project-specific tables into it afterwards, so the installed copy is legitimately never byte-equal and its stamp is never refreshed. **Neither signal reaches it** — no `cmp`, no stamp read |
| pristine template baseline (`.template-baseline/`) | **not stamped at all** | it holds the raw template, placeholders unresolved, on purpose — never read a version out of it |

---

## The Roster — SINGLE SOURCE OF TRUTH

Every fact this skill knows about a setup lives in this ONE table. Adding a future setup = adding
ONE row. Nothing else in this file, and no script, encodes the roster.

| # | Skill (command) | Plugin | Anchor artifact | Secondary artifacts | `content_version` stamp — carrier & how to read (headline; `version` rides along at the same carrier as provenance) | `cmp` corroboration |
|---|-----------------|--------|-----------------|---------------------|--------------------------------------|---------------------|
| 1 | `/brewcode:teams-setup` | brewcode | `.claude/teams/*/team.md` | `.claude/teams/*/trace.jsonl`, `.claude/teams/*/trace-ops.sh` | `team.md` header table: the `\| Content Version \| X.Y.Z \|` row of the `Field/Value` block is the headline, beside `\| Version \|` (provenance), `\| Generated by \|` and `\| Last update \|`. Generated, substituted at install. **The Agents table also carries a per-agent trailing `Version` column** — `upgrade` rewrites only the rows it touches, so a roster may legitimately mix versions; that per-agent column stays `version`, not `content_version` — it exists to show which write touched each agent, not content drift. The header's `Content Version` row is the headline (content_version of the last real change to `team.md`'s own template); if it is behind the plugin's current `content_version` for that template, say so in *found*. **Confirmed wired** — `scripts/detect-mode.sh:36` self-locates `CONTENT_VERSION` off `teams-setup/SKILL.md`'s own `brewcode-meta:` marker and `SKILL.md:417` writes it into the header row; `verify-team.sh:166` fails a surviving `{CONTENT_VERSION}` token. Compared against `skills/teams-setup/SKILL.md` (the `STAMPS` source), never against the release version. A `team.md` with no `Content Version` row is a pre-5.6 install -> `stale (legacy stamp)` | `trace-ops.sh` vs `$BC/skills/teams-setup/scripts/trace-ops.sh` (byte-copied, meta line baked at release). **Absence signal kept:** complete team with no `trace-ops.sh` = pre-standard install whose agents cannot trace -> `stale`. **Remedy check:** `upgrade`'s U4 rewrites the three header rows from the Phase 1 scalars, and C4 directs `upgrade` to re-`cp` the tracer ("Re-copy it in UPGRADE too (`cp` is idempotent) so a team created by an older version gains it") — so both the stamp and this absence clear. Per-agent `Version` cells move only for agents the run actually touched, which is why a mixed roster stays `installed` |
| 2 | `/brewcode:semble-setup` | brewcode | `.claude/rules/semble-first.md` | `.claude/hooks/semble-session.mjs`, `semble-prefetch.mjs`, `semble-stats.mjs`, `semble-reminder.mjs`, `semble-subagent.mjs`, `.claude/semble/state.json` | frontmatter `content_version:` of `.claude/rules/semble-first.md` — headline; `version:` rides along at the same carrier as provenance. It is a **pure byte-copy**: the template carries baked `doc_type: llm` + `version` + `generated_by` and deliberately no `last_updated`, and the installer does NOT restamp on copy — so the installed rule stays byte-identical to the template and `cmp` must read `SAME`. `DIFFERS` here means a hand-edit or a rule never re-copied after the plugin update, never a stamping artefact | **all FIVE live hooks** vs `$BC/skills/semble-setup/assets/*.mjs` (`semble-explore.mjs` is retired for good, superseded by `semble-subagent.mjs`; if that file is still in `.claude/hooks/` the install predates the migration - report it in *found* and prescribe `upgrade`, never `DIFFERS`), plus the rule vs `assets/semble-first.md.template` — and NOTHING else. **The repo-root `.sembleignore` is byte-copied but never byte-STABLE: it is carved out of the `cmp` set** (its `# brewcode-meta:` stamp IS still read — see the row-2 carve-out below). `.sembleignore` sits at the REPO ROOT, not under `.claude/`, and is absent from installs predating it: report that presence check in *found*, never as a `cmp` verdict. `.claude/semble/state.json` is runtime state — never a stamp source, and its `approvedVersion` is the semble **package** version, not ours. **Wiring is a separate signal from bytes, and it is the one that catches a v1-shaped repo:** `.claude/settings.json` can list a hook that no longer exists, or list five of the six the current version wants, while every file on disk is byte-current. `semble-status.sh` reads it (`guidance.hooks`: `retired[]`, `staleEntries`, `wiredCount`/`wantCount`) and downgrades its own `ready` to `partial` for any of the three; report the same way — retired hooks on disk, stale settings entries, or `wiredCount < wantCount` is `stale`, prescribing `install`, no matter how current the stamps are. `wantCount: 0` means the counts were not reported at all and is never a defect. **This row's setup carries its OWN version signal, and it now asks the same question this dashboard does:** `semble-guidance.sh` computes `rule.content_version` (the installed rule's frontmatter `content_version:`) beside `rule.templateContentVersion` (the plugin template's `content_version:`, read straight off `assets/semble-first.md.template` — the same file this row's `STAMPS` source names), and `semble-status.sh:791-800` drops `ready` -> `partial` on exactly that pair, with `nextStep: Run /brewcode:semble-setup upgrade`. `version` rides along informational-only there, as here. So the two agree by construction; a disagreement is a finding worth naming, not a known gap. **A `partial` from `semble-setup status` whose `nextStep` is `resume` is NOT a version signal** — that ladder means warm/smoke unproven (an offline `install`/`enable` legitimately exits 3 with `"status":"skipped"`) and it never bears on this row's stamp verdict. Both stamps empty (a pre-5.0 unstamped rule) is deliberately NOT stale there; this dashboard's `LEGACY-NONE` still covers it. **Remedy check:** `upgrade` unconditionally re-runs `semble-guidance.sh install --part all` (SKILL.md `### upgrade`), which is the ONLY writer of the rule's stamp — so it does clear `BEHIND`. It also `cp`s the five hooks with no user_modified guard (`install_hook_files`), so hook `DIFFERS` always clears. **`semble-first.md` is the exception:** `install_managed` re-syncs it only when the sole delta is the metadata block, and a real prose hand-edit is SKIPPED with a `diff -u` to stderr. Say so in *found* — that one needs `--force`, which is not a skill mode. `.sembleignore` takes the same skip branch inside the installer, but this dashboard never reaches that verdict for it: it is not `cmp`d at all, so **never prescribe `--force` on `.sembleignore`** — see the carve-out |
| 3 | `/brewcode:superreview-setup` | brewcode | `.claude/skills/superreview/SKILL.md` | `.claude/skills/superreview/references/agent-prompt.md`, `.../report-template.md`, `.../scope.md`, `.claude/skills/superreview/.template-baseline/` | frontmatter `content_version:` of the **emitted** `.claude/skills/superreview/SKILL.md` — headline; `version:` rides along at the same carrier as provenance — substituted at install from `{PLUGIN_VERSION}` / `{CONTENT_VERSION}`. **Confirmed wired** — `generate.sh` `_content_version()` (`:207-216`) self-locates it off `superreview-setup/SKILL.md`'s own marker and HARD-FAILS rather than stamping a placeholder; `:279` substitutes `{CONTENT_VERSION}`, `_restamp_meta` and `validate` (`:861`) both require a quoted `X.Y.Z`. Compared against `skills/superreview-setup/SKILL.md` (the `STAMPS` source). An emitted `SKILL.md` with no `content_version:` is a pre-5.6 install -> `stale (legacy stamp)`. **Never read either version out of `.template-baseline/`** — that dir is the pristine template and its stamps are the unresolved `{PLUGIN_VERSION}`/`{CONTENT_VERSION}` tokens by design, which is precisely why `upgrade` reports IDENTICAL across a version bump instead of a phantom diff. A placeholder in the *emitted* file means substitution never completed -> `partial`. **Remedy check:** `upgrade` restamps FIVE live files unconditionally — `SKILL.md` + `references/{agent-prompt,report-template,scope}.md` + the per-stack ref (`generate.sh` `_restamp_meta` loop) — and prints a `RESTAMP:` line for each even when the delta report says `IDENTICAL`, which is the normal outcome of a plain version bump. So `BEHIND` clears | the 4 baseline copies vs the plugin templates (mapping below) — answers "did the plugin's templates move since this project was tailored", which the emitted stamp cannot. Baseline dir absent -> pre-baseline install, report it in *found*, do not call it a version. **A baseline `DIFFERS` is NOT cleared by `upgrade` alone:** `upgrade` only stages the new templates and prints the promote command (`rm -rf <baseline> && mv <staging>/.template <baseline> && rm -rf <staging>`), which the user runs after porting the delta. Name both halves in the remedy |
| 4 | `/brewtools:task-board-setup` | brewtools | `.claude/features/board.md` | `.claude/agents/task-tracker.md`, `.claude/skills/task-board/SKILL.md`, `.claude/skills/task-spec/SKILL.md`, `.claude/rules/tasks.md`, `.claude/features/PROGRESS.md` | frontmatter `content_version:` of the anchor itself — headline; `version:` rides along at the same carrier as provenance — `board.md` opens with the key block, substituted at install from `{PLUGIN_VERSION}` / `{CONTENT_VERSION}`. Nine artifacts carry the block: the anchor + the 5 secondaries above, plus `TRACKER.md`, `INDEX.md`, `backlog/README.md`. `TASK_TEMPLATE.md` is deliberately UNSTAMPED — its frontmatter is copied into every task card — so its lack of a stamp is never a defect. **Confirmed wired** — `{CONTENT_VERSION}` is self-located off `task-board-setup/SKILL.md`'s own line-1 `brewcode-meta:` marker (stamped by `bump-version.sh`, kind `marker`), resolved beside `{PLUGIN_VERSION}` in the same "Resolving..." bash block, and substituted into all nine templates. **Remedy check:** `upgrade` step `U5b` (`references/10-upgrade.md:282`) restamps the quartet on all nine unconditionally, and `:109`/`:111` make it run even on the commonest path, where every content row is `SKIP` and the version stamp is the only thing out of date. So `BEHIND` clears | none copied verbatim. **Absence signal kept:** board present but `.claude/skills/task-spec/SKILL.md` missing = install predates the spec+design layer -> `stale`, the documented upgrade path was never run |
| 5 | `/brewtools:think-short-setup` | brewtools | `.claude/hooks/think-short-session.mjs` (project) or `~/.claude/hooks/think-short-session.mjs` (global) | in the same dir: `think-short-prompt-counter.mjs`, `think-short-subagent.mjs`, `think-short-prompt.md`; plus a `think-short` reference in the matching `settings.json` | the `content_version=` token in the `// brewcode-meta:` line right after the shebang of `think-short-session.mjs` is the headline (baked at release by `bump-version.sh`, alongside `version=` as provenance). `think-short-prompt.md` carries the same marker pair as an HTML comment on line 1, not frontmatter. There is no JSON carrier on this row. **Confirmed wired** — all 4 assets are `STAMPED_FILES` in `bump-version.sh`, which now stamps `content_version` into every kind it handles. **Remedy check:** `upgrade` re-emits all four assets from the current plugin version, keeping the disabled state (SKILL.md mode table), so both the stamp and any `DIFFERS` clear | all 4 vs `$BT/skills/think-short-setup/assets/` |
| 6 | `/brewtools:agent-deadline-setup` | brewtools | `.claude/hooks/agent-deadline-guard.mjs` (or the `~/.claude` twin) | `agent-deadline-cleanup.mjs` beside it, `.claude/agent-deadline.json`, `agent-deadline` in `settings.json` | the `content_version=` token in `agent-deadline-guard.mjs`'s `// brewcode-meta:` line (baked at release) is the headline, `version=` beside it as provenance. `.claude/agent-deadline.json` carries the same quartet (`version`, `content_version`, `generated_by`, `last_updated`), copied from `assets/INSTALL.md`'s own header marker at every write — read it too, it is the only carrier that moves when the user runs `enable`/`disable`. **Confirmed wired** — both `INSTALL.md` (its own `content_version=` marker, stamped by `bump-version.sh`) and the JSON write (reads that marker, stamps all 4 keys, verifies with a read-back) are in place. **Remedy check:** `upgrade` replays the install at the SAME budget (`defaultMinutes`/`byAgentType`/`hardStopRatio` read back out of the config), re-copying both hooks and rewriting the JSON trio; a disabled setup stays disabled | both `.mjs` vs `$BT/skills/agent-deadline-setup/assets/` |
| 7 | `/brewtools:agent-router-setup` | brewtools | `.claude/hooks/agent-router.mjs` | `.claude/brewtools/agent-router.json`, `agent-router.mjs` referenced in `.claude/settings.json` | the `content_version=` token in `agent-router.mjs`'s `// brewcode-meta:` line (baked at release) is the headline, `version=` beside it as provenance; `.claude/brewtools/agent-router.json` carries the same quartet (`version`, `content_version`, `generated_by`, `last_updated`), copied from `assets/INSTALL.md`'s own header marker and rewritten by `install`/`upgrade`/`enable`/`disable`/`level` — read it too, it is the only carrier that moves on those. **Confirmed wired** — `INSTALL.md` (its own `content_version=` marker, stamped by `bump-version.sh`) and the JSON write both stamp all 4 keys with a read-back. **Remedy check:** `upgrade` reads `level` back out of the config and replays the install at that level — fresh `agent-router.mjs`, behavior values preserved, metadata re-stamped — so it never asks a question and never changes a setting | the hook vs `$BT/skills/agent-router-setup/assets/agent-router.mjs` |
| 8 | `/brewtools:manager-setup` | brewtools | `.claude/brewtools/manager/state.json` | `.claude/brewtools/manager/hardmode-guard.mjs`, a `hardmode-guard.mjs` PreToolUse entry in `.claude/settings.local.json` | top-level `"content_version"` in the RAW `state.json` is the headline — **confirmed wired**: `writeState()` in `$BT/hooks/lib/manager-state.mjs` stamps it from that module's OWN `brewcode-meta:` marker (`resolveContentVersion()`, `:91-98`) and DELETES the key rather than inventing one when the marker cannot be read (`:273-280`), so it is compared against `hooks/lib/manager-state.mjs` (the `STAMPS` source). When the key is absent, read the RAW file's `"version"` as the fallback headline — read the file, never `resolveState`'s merged view, or a defaulted key would let an old state file inherit the current version and hide the staleness. `writeState()` resolves that `version` from `brewtools/.claude-plugin/plugin.json` and falls back to that module's OWN baked `brewcode-meta` line (`pluginVersion()`, `:69-81`), never a literal. `DEFAULT_STATE` deliberately carries no version — it is the answer to "no state file exists", and a version there would be a fake stamp; a project with no state file therefore resolves with no version key at all, which is the `missing` signal, not a version. Second precedence, only when the primary key is absent: the `content_version=` token in the copied `hardmode-guard.mjs`'s `// brewcode-meta:` line (baked at release by `bump-version.sh`, compared against `hooks/hardmode-guard.mjs`). **The precedence holds because the primary carrier MOVES:** `upgrade` calls `writeState('project', {}, cwd)` with an EMPTY partial, and `writeState` stamps `version`/`content_version`/`generated_by`/`last_updated` on every write while `hard`, `level`, `mode` and every unknown key merge through from the existing file. Reading the guard's meta line first would answer a question the state file already answers more precisely. **A second, rarer way the key goes absent, and the reason the fallback is load-bearing rather than historical:** when `pluginVersion()` cannot resolve, `writeState` DELETES `version` rather than stamping `unknown` (`:242-252`) — including a `version` inherited from the older file it is merging over. So an absent key on a freshly written state file is a resolver failure on a current install, NOT a legacy one; the guard's line beneath it is the answer, and re-running `enable`/`disable`/`upgrade` will not put it back. `references/artifact-metadata.md` records why this one writer omits instead of aborting | the copied guard vs `$BT/hooks/hardmode-guard.mjs` — `install` AND `upgrade` both overwrite it every run, so `DIFFERS` means exactly "neither was re-run since the plugin update", and `upgrade` clears it together with the stamp |
| 9 | `/brewdoc:memory-sync-setup` | brewdoc | `.claude/skills/memory-sync/SKILL.md` | `references/memory-guide.md`, `references/agent-audit.md`, `references/hard-sync.md` under it | frontmatter `content_version:` of the emitted `SKILL.md` is the headline — **confirmed wired**: `generate.sh` `resolve_content_version()` (`:38-44`) self-locates it off `memory-sync-setup/SKILL.md`'s own marker, `:84` HARD-FAILS on `unknown`, and it is written into the frontmatter quartet (`:193`) and refreshed by `restamp` (`:489`). Compared against `skills/memory-sync-setup/SKILL.md` (the `STAMPS` source). Read `version:` (quoted), carrying the **brewdoc plugin version**, as the fallback headline on a pre-5.6 anchor that carries no `content_version:`. Empty `version:` + a last line starting `<!-- memory-sync template v` = a **legacy stamp**; empty with no tail stamp = **unstamped**. What was retired from `generate.sh` is the hardcoded per-template counter (`VERSION=1.0.0`), not the `VERSION=` variable; a literal there would be the defect. A skill-specific `surface_files:` key TRAILS the four standard ones; ignore it. **The two byte-copied references are ahead of the anchor:** `memory-guide.md` and `agent-audit.md` ARE in `bump-version.sh`'s `STAMPED_FILES` (kind `md`), so they already carry a real `content_version=` token in their line-1 HTML comment — read it there as corroboration, but never promote a secondary's content_version to the row's headline | **the anchor is generated and has no `cmp` partner; TWO of the three references have one and `hard-sync.md` does NOT.** `generate.sh:398` `cp`s all three verbatim (`EMITTED_REFS`, `:38`) and they are mechanism-`a` byte copies stamped at release with a line-1 HTML-comment `brewcode-meta:` (now including `content_version=`), so exactly two pair up: `.claude/skills/memory-sync/references/memory-guide.md` and `agent-audit.md` vs `$BD/skills/memory-sync-setup/references/<same name>`. `DIFFERS` there = a SELF-SYNC hand-edit or references never re-copied after the plugin update. **`hard-sync.md` is byte-copied but never byte-STABLE — carved out of the `cmp` set and out of the `STAMPS` heredoc alike**; see the carve-out note below. For a deeper diff of the generated anchor, OFFER (never run) `generate.sh status`, see below |
| 10 | `/brewdoc:docsync-setup` | brewdoc | `.claude/docsync/config.json` | `.claude/docsync/state-<session_id>.json`, `.claude/hooks/docsync-track.mjs`, `docsync-watch.mjs`, `docsync-gate.mjs`, `docsync` in `.claude/settings.json` | top-level `"content_version"` of `.claude/docsync/config.json` is the headline — **confirmed wired**: install reads `docsync-setup/SKILL.md`'s own line-10 marker into `CV` and writes it (`docsync-setup/SKILL.md:231-257`), and `upgrade` (`:390-398`) and `enable`/`disable` (`:468-479`) re-stamp the same quartet while leaving `threshold_days`/`exclude` verbatim. Compared against `skills/docsync-setup/SKILL.md` (the `STAMPS` source). Read `"version"` as the fallback headline on a pre-5.6 config that carries no `content_version` key. **`config.json` present but carrying no `version` key either = a pre-standard install = `stale (legacy, unstamped)`**, never `missing`. Corroborating only: the `content_version=` token in the `// brewcode-meta:` line of `.claude/hooks/docsync-track.mjs`, which catches a hook set that was never re-copied. State is runtime and never carries a stamp — it is now ONE FILE PER SESSION, `state-<session_id>.json` (`docsync-track.mjs:60-64`, falling back to `state.json` when no session id), so probe the glob and never treat several of them, or none, as a defect | the 3 hooks vs `$BD/skills/docsync-setup/assets/` |
| 11 | `/brewtools:agent-return-setup` | brewtools | `.claude/hooks/agent-return-guard.mjs` (or the `~/.claude` twin) | `agent-return-contract.mjs` and `agent-return-budget.mjs` beside it, `.claude/agent-return.json`, `agent-return` in `settings.json` | the `content_version=` token in `agent-return-guard.mjs`'s `// brewcode-meta:` line (baked at release) is the headline, `version=` beside it as provenance. `.claude/agent-return.json` carries the same quartet (`version`, `content_version`, `generated_by`, `last_updated`) written by every mode that touches it (`install`, `upgrade`, `enable`, `disable` — `INSTALL.md:208`, `:536`), copied from `INSTALL.md`'s own header marker — read it too, it is the only carrier that moves on an `enable`/`disable`. **Confirmed wired** — `INSTALL.md`'s own marker (stamped by `bump-version.sh`) and the JSON write (stamps all 4 keys, verifies with a read-back) are in place. **Only TWO of the three `.mjs` are ever registered** — `agent-return-budget.mjs` is a shared module imported by both hooks, so it is a file-presence check and must NEVER appear in `settings.json`; a settings ref to it is a defect. `1/3` or `2/3` hook files is `partial`, not `stale`: ESM resolution precedes evaluation, so a missing sibling makes BOTH hooks exit 1 with a hook-error banner on every subagent spawn and return. **Remedy check:** `upgrade` (`INSTALL.md:455-486`) reads `passTokens`/`fileTokens` back out of the config and replays the install for that scope — all three files re-copied, both settings entries re-merged, the JSON trio re-stamped; a disabled setup stays disabled | all THREE `.mjs` vs `$BT/skills/agent-return-setup/assets/` |

`$BC` / `$BT` / `$BD` = the resolved plugin roots from Phase 0.

> **Rows 8 and 10 share one precedence rule:** the setup's own generated JSON is the preferred
> carrier, and the byte-copied companion's `brewcode-meta:` line is the documented fallback for as
> long as that JSON goes unstamped. Read the JSON first; fall through silently; never report `LEGACY`
> on a row whose fallback carrier answered.

> **Row 2 — the `.sembleignore` carve-out.** Like `hard-sync.md` it is shipped as mechanism `a`
> (`STAMPED_FILES`, kind `marker`) and copied verbatim by `install_managed`, so it LOOKS like a `cmp`
> partner. It is not one. `install_ignore_apply` (`semble-guidance.sh:564-585`, reached through the
> `install_ignore` dispatcher at `:618-620`) runs `install_candidates` **after** `install_managed`
> has copied the template, and that function appends a `# --- brewcode:semble measured candidates
> ---` block measured from THIS repo (`:500-555`) — every line commented out, proposals only. So a
> HEALTHY, freshly-installed `.sembleignore` differs from `assets/sembleignore.template` **by
> construction**, exactly like row 9. Fed to Phase 2b it prints `DIFFERS` beside a `CURRENT` stamp,
> which Phase 3 rule 9 turns into `stale (bytes drifted)` — and row 2's only two readings of that
> (`metadata-only re-sync` or `prose hand-edit needing --force`) are both wrong. **`--force` there
> takes a backup and overwrites the user's own uncommented exclusions.** Never `cmp` it, never
> prescribe `--force` for it, never name it in a remedy for byte drift.
>
> **Unlike row 9, its STAMP is still read.** The installer's comparison strips both the
> `# brewcode-meta:` line and the whole candidates block from both sides (`sg_strip_metaline`,
> `:401-411`), so a template update still writes through on the metadata-only branch and the stamp
> genuinely moves. Phase 2a on `.sembleignore` is valid; Phase 2b on it is not. The two carve-outs
> differ exactly there: `hard-sync.md` loses BOTH signals, `.sembleignore` loses only `cmp`.
>
> **`unchanged` from the installer does NOT mean byte-equal to the template.** `install_ignore_apply`
> snapshots the file around both halves and collapses a clean net-zero run — re-sync then a
> byte-identical re-append — back to `ignore: up to date` (`:576-584`; the dry-run path predicts the
> same verdict by simulating into a temp dir, `install_ignore_dry:591-616`). So a back-to-back re-run
> over an unchanged repo reports `ignore: up to date` — while the file on disk still differs from
> `assets/sembleignore.template` by the whole candidates block. Do not read the installer's
> `unchanged` as licence to `cmp` the pair; the two answer different questions.
>
> A re-run over a repo whose FILE SET moved does report `changed`, because the scan re-measures and
> the block genuinely changes. That is real byte movement, still not a drift signal, and still not
> something `cmp` against the template can express.

> **Row 9 — why the dashboard parses instead of executing.** `bash "$BD/skills/memory-sync-setup/scripts/generate.sh" status`
> prints a richer verdict (`STAMP_FORMAT`, `META_*`, `DRIFTS`, `VERDICT`) and is genuinely read-only —
> its `status_report` only reads and echoes, and `resolve_root` only `cd`s. It is still the wrong
> default here: it re-scans the whole memory surface (real work, not a probe), it `exit 1`s when the
> cwd is not a repo root, and it returns a private verdict vocabulary that would have to be
> translated into this skill's states — which puts roster knowledge inside a foreign script and
> breaks "no script encodes the roster". So: read the frontmatter with the Phase 2a block like every
> other row, and put `generate.sh status` in the **Command** column as something the USER may run
> when row 9 is not green.

> **Row 9 — the `hard-sync.md` carve-out.** It is shipped as mechanism `a` (`STAMPED_FILES` row,
> kind `md`) and `cp`d verbatim, so it LOOKS like a `cmp` partner. It is not one, and listing it as
> one made this dashboard report drift on every correctly-installed project. The emitted
> `references/hard-sync.md` carries two of the generator's twelve BLOCK placeholders —
> `{PATHS_PRECISION_TABLE}` and `{OBVIOUS_VS_DOMAIN_TABLE}` — which the generator's own Phase 3
> fills with project-specific tables via `Edit`, and `generate.sh validate` FAILS while either is
> unfilled. So a HEALTHY install differs from the plugin source by construction: `cmp` `DIFFERS` is
> the success state, not drift, and no mode can ever clear it without destroying the user's tables.
> Its baked stamp is frozen at the release that emitted it for the same reason — `refresh_refs`
> refuses to re-copy a file whose content differs — so feeding it to Phase 2a would print a
> permanent `BEHIND`. **Never `cmp` it, never stamp-read it, never name it in a remedy.** The row's
> version signal is the anchor's frontmatter and nothing else.
>
> The two references that DO pair up are cleared by `upgrade`, and only by it.
> `generate.sh:514 refresh_refs()` (reached from `restamp`, which is the mandatory last step of
> `upgrade` — `memory-sync-setup/SKILL.md` mode `upgrade` step 3) re-copies a reference **only when
> that is provably lossless**, and prints which case fired:
>
> | Line | Condition | Clears a `cmp` `DIFFERS`? |
> |------|-----------|---------------------------|
> | `REF OK:` | already byte-identical | n/a — the row was `SAME` |
> | `REF RESTORED:` | the file was absent; nothing local to lose | yes |
> | `REF RECOPIED:` | the ONLY difference is the `brewcode-meta:` release-stamp line | yes — this is the plugin-update case |
> | `REF DIFFERS:` | real content differs (hand-edit, filled BLOCKs, prose moved in a newer release) | **no — the file is left untouched, by design** |
>
> A `REF DIFFERS:` on `memory-guide.md` or `agent-audit.md` after an `upgrade` therefore means a
> genuine local edit: report `stale (bytes drifted)`, say `upgrade` will NOT overwrite it, and tell
> the user to diff against `$BD/skills/memory-sync-setup/references/<name>` and port by hand. Do not
> prescribe a mode that would silently discard their edit — there is none.

> **Every secondary must be EXCLUSIVE to its row.** A shared artifact — `.claude/agents/*.md`,
> `.claude/agents/intent-guard.md` (superreview *and* teams both emit it), any hand-written agent —
> is not evidence that THIS setup ran, and listing one makes Phase 3 rule 3 report a `partial`
> install in every project that merely has an agent file. If a setup owns no exclusive secondary,
> leave the cell empty and let the anchor decide.

**Row 3 baseline mapping** (the only non-obvious `cmp` pairing):

| Project baseline copy | Plugin template |
|-----------------------|-----------------|
| `.claude/skills/superreview/.template-baseline/SKILL.md` | `$BC/skills/superreview-setup/references/SKILL.md.template` |
| `.../.template-baseline/references/agent-prompt.md` | `$BC/skills/superreview-setup/references/agent-prompt.md` |
| `.../.template-baseline/references/report-template.md` | `$BC/skills/superreview-setup/references/report-template.md` |
| `.../.template-baseline/references/scope.md` | `$BC/skills/superreview-setup/references/scope.md.template` |
| `.../.template-baseline/references/<stack>.md` | `$BC/skills/superreview-setup/references/<stack>.md` |

`<stack>` is the one per-stack reference the install picked — `go.md`, `java-kotlin.md`,
`python.md` or `typescript-react.md`. It is substituted and baseline-copied exactly like the
other four; only its filename varies. Absent from an install that predates it: report the other
four and say the per-stack ref is missing, never `DIFFERS`.

### NOT setups — never appear in the report

Recurring tools, not one-time installs. They are correct to run repeatedly and have no installed
state to report: `brewcode:agents`, `skills`, `rules`, `convention`, `e2e`;
`brewtools:text-optimize`, `text-human`, `secrets-scan`, `ssh`, `deploy`, `plugin-update`,
`provider-switch`; `brewdoc:md-to-pdf`, `my-claude`, `publish`.

---

## Phase 0 — Resolve plugin roots

A plugin that is not installed makes every one of its rows `n/a` — never `missing`. Do not assume
all four are present.

The plugin version is the number every stamp is compared against, so resolve it with the SAME
precedence the brewcode SessionStart hook uses (`brewcode/hooks/session-start.mjs`, `parseVersion`):
**the cache directory basename first, `.claude-plugin/plugin.json` `.version` second.** One
precedence, two consumers — do not invent a third.

**EXECUTE** using Bash tool:

```bash
for p in brewcode brewdoc brewtools brewui; do
  r=$({ ls -d "$HOME/.claude/plugins/cache/claude-brewcode/$p"/*/ 2>/dev/null || true; } | sort -V | tail -1 | sed 's:/*$::')
  if [ -n "$r" ] && [ -d "$r" ]; then
    v=$(basename "$r")
    case "$v" in
      [0-9]*.[0-9]*.[0-9]*) : ;;
      *) v=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$r/.claude-plugin/plugin.json" 2>/dev/null | head -1 | sed 's/.*:[[:space:]]*"//; s/"$//' || true) ;;
    esac
    echo "$p ROOT=$r VERSION=${v:-unknown}"
  else echo "$p ROOT=none VERSION=none"; fi
done
echo "PROJECT=$PWD"
test -d "$PWD/.claude" && echo "DOTCLAUDE=yes" || echo "DOTCLAUDE=no"
echo "OK"
```

> **STOP if FAILED** — cannot resolve the cache; report it and stop rather than calling everything
> `missing`. All four roots `none` also means stop: nothing installed, nothing to report.

Bind `$BC`, `$BT`, `$BD` and their versions from the output. `DOTCLAUDE=no` is a legitimate answer —
every row is `missing`, print the table anyway. `VERSION=unknown` on a plugin that IS installed means
no comparison is possible for its rows: report `installed (plugin version unresolved)` and say why.

## Phase 1 — Probe artifacts

One generic block, fed from the roster. Paste the anchor + secondary paths of the rows in scope
(after the `$ARGUMENTS` filter) into the heredoc — relative to the project root, one per line, a
trailing `/` for a directory, globs allowed.

**EXECUTE** using Bash tool:

```bash
cd "$PWD" || exit 1
while IFS= read -r rel; do
  [ -z "$rel" ] && continue
  case "$rel" in
    */)    [ -d "$rel" ] && echo "DIR  $rel" || echo "MISS $rel" ;;
    *"*"*) n=$({ find . -path "./$rel" 2>/dev/null || true; } | wc -l | tr -d ' '); n=${n:-0}; [ "$n" -gt 0 ] && echo "GLOB $rel ($n)" || echo "MISS $rel" ;;
    *)     if [ -f "$rel" ]; then echo "FILE $rel"
           elif [ -f "$rel.disabled" ]; then echo "PARK $rel.disabled"
           else echo "MISS $rel"; fi ;;
  esac
done <<'PATHS'
.claude/teams/*/team.md
.claude/teams/*/trace-ops.sh
.claude/rules/semble-first.md
PATHS
echo "OK"
```

`PARK` is **present**, not missing. Five setups `disable` by renaming their entry file to
`<name>.disabled` (Phase 1b), and the body stays byte-identical — so a `PARK` line never feeds
Phase 3's `missing` or `partial` rules, and the file it names is still a readable version stamp.

Then the `setting

…(truncated)
