cohorte 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: sonnet
2
3
  description: Apply the refactor backlog for one or more domains via the surface implementer agents (TDD-first, parallel across independent surfaces), then re-verify.
3
4
  argument-hint: <domain…> (one or more surface keys, "shared", or "all")
4
5
  ---
@@ -16,9 +17,18 @@ You are the **lead**. Refactor domain **$ARGUMENTS** to match `PIPELINE.md`, usi
16
17
 
17
18
  ## 2. Dispatch the surface's implementer agent — TDD-first, stateless
18
19
 
19
- Spawn the matching `<surface.agent>` with: the selected backlog items, the **exact file paths** of the
20
- items (never "find them"), and the current diff. Instruct it: **add the missing tests FIRST** (pin
21
- current behavior / cover the entry points), watch them pass, **then** refactor to clear each item.
20
+ Spawn the matching `<surface.agent>` with the template below. Keep it **byte-identical across loop
21
+ rounds** (only the final items slot varies — it sits at the end so repeats hit the prompt cache), and
22
+ never paste a diff — the agent computes its own, scoped to its tree:
23
+
24
+ > `subagent_type: <surface.agent>` — "Refactor pass on your surface (no feature spec). Read
25
+ > `PIPELINE.md` first. **Add the missing tests FIRST** (pin current behavior / cover the entry
26
+ > points), watch them pass, **then** refactor to clear each item. Preserve current public behavior
27
+ > unless an item marks it a bug. Migrations stay additive. Need the current state of your tree?
28
+ > Compute it yourself: `git diff <default_branch> -- <surface.path>`. Lint + format before handoff;
29
+ > return the handoff in the format your agent instructions define. Backlog items for YOUR surface
30
+ > (self-contained — clear exactly these, reading only the files they name): <the domain's open
31
+ > `- [ ]` lines verbatim, with exact file paths>."
22
32
 
23
33
  **Parallel when domains are independent:** if the human passed several domains (or `all`), dispatch
24
34
  their implementers **concurrently in a single message** — surface trees are disjoint by construction,
@@ -33,6 +43,9 @@ slices import it, so refactor it alone, first.
33
43
  ## 3. Verify & loop
34
44
 
35
45
  After each agent returns: its surface's `test_cmd`/`lint_cmd`, `commands.typecheck`,
36
- `commands.format --check`. Re-run `/audit` for that domain to confirm items cleared; check them off in
46
+ `commands.format --check` (redirect output to a file and grep it — don't print gate output into your
47
+ context). Then verify each backlog item cleared by checking its `file:line` directly — do NOT re-run
48
+ `/audit` per round (that repeats the repo-wide gates + a review dispatch you don't need); one optional
49
+ final `/audit` once ALL dispatched domains are clean is enough. Check cleared items off in
37
50
  `specs/refactor-backlog.md`. Loop until each dispatched domain is clean. Verification is per-domain
38
51
  even when the dispatch was parallel — one failing surface loops alone, the others don't redo work.
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: sonnet
2
3
  description: Dispatch the read-only review agent to audit the feature against its frozen spec.
3
4
  argument-hint: <feature_id>
4
5
  ---
@@ -6,9 +7,7 @@ argument-hint: <feature_id>
6
7
  You are the **lead**. Dispatch the review for feature **$ARGUMENTS**.
7
8
 
8
9
  > Read `PIPELINE.md` §`vcs.default_branch` (diff base) and the `surfaces`/`contract` fields.
9
- >
10
- > Template paths below (`.claude/templates/…`) resolve to `~/.claude/templates/…` when the core is
11
- > installed globally — read whichever exists.
10
+ > _Skip the re-read if it's already in your context this session and unmodified since._
12
11
  >
13
12
  > **Kanban** (SCHEMA.md §Kanban): move card `#$ARGUMENTS` → **Review**. No-op silently if no board.
14
13
 
@@ -21,33 +20,50 @@ You are the **lead**. Dispatch the review for feature **$ARGUMENTS**.
21
20
  `git diff <default_branch> --name-only -- . $(for p in <each surface.path>; do printf ':(exclude)%s ' "$p"; done)`.
22
21
  Attach the remainder to the most relevant surface's reviewer and say so in its dispatch. A surface whose
23
22
  `git diff` comes back empty gets no reviewer.
23
+ - **Stage the hunks** (reviewers are read-only — no Bash — so a staged diff file is the ONLY way they
24
+ can review hunks instead of re-reading whole files). Per touched surface, regenerated every round:
25
+ `git diff <default_branch> -- <surface.path> > specs/reports/$ARGUMENTS.<surface.key>.diff`
26
+ (same gitignored buffer dir as the reports). For the surface that carries the shared remainder,
27
+ append the remainder pathspecs to its command so its `.diff` includes them. Never print a diff into
28
+ your own context — redirect straight to the file.
24
29
 
25
30
  ## 2. Dispatch review agents — one per touched surface, IN PARALLEL
26
31
 
27
32
  Spawn ONE `review` agent per surface that has changed files, in a **single message** (one Task call
28
33
  each, like `/build`) so they run concurrently — NEVER serially: review wall-clock must be the
29
- slowest surface, not the sum. A diff touching a single surface ⇒ a single reviewer. For each:
34
+ slowest surface, not the sum. A diff touching a single surface ⇒ a single reviewer.
35
+
36
+ **Small-diff fast path (re-reviews only):** if a surface's staged diff is tiny (≤2 files and ≤~40
37
+ changed lines), touches no contract file, and every open finding it addresses is non-security
38
+ LOW/MEDIUM, skip the dispatch: verify the hunks yourself against the open Remediation items (did the
39
+ prescribed fixes land? — NOT a de-novo audit) and write the same REVIEW REPORT into the §3 flow.
40
+ First-round reviews, contract changes, and security findings always get a full reviewer. For each
41
+ dispatched surface:
30
42
 
31
43
  > `subagent_type: review` — "Review feature `$ARGUMENTS` — **scope: the `<surface.key>` surface
32
44
  > only**. Read `PIPELINE.md` first (its flags + §Conventions/§Testing for `<surface.key>`). Spec:
33
- > `specs/$ARGUMENTS.md` (source of truth). Contract: `<contract.path>/$ARGUMENTS.<ext>`. Changed
34
- > files in your scope (diff vs `<default_branch>`): <list, with the `--stat` counts>. Review the
35
- > diff hunks + immediate context — not whole files. Check spec conformance first, then correctness,
45
+ > `specs/$ARGUMENTS.md` (source of truth). Contract: `<contract.path>/$ARGUMENTS.<ext>`. Diff to
46
+ > review: `specs/reports/$ARGUMENTS.<surface.key>.diff` (staged hunks — read it FIRST; open a full
47
+ > source file only when a finding demands it). Check spec conformance first, then correctness,
36
48
  > security, conventions, RBAC/mobile-first _if the profile enables them_, and TDD coverage. Emit
37
- > the REVIEW REPORT per `.claude/templates/review-feedback.md` — every finding self-sufficient
38
- > (`file:line` · severity · type · concrete fix)."
49
+ > the REVIEW REPORT in the format your agent instructions define — every finding self-sufficient
50
+ > (`file:line` · severity · type · concrete fix). Changed files (`--stat`): <list>."
39
51
 
40
- Note the epoch (`date +%s`) just before dispatching — §3's metrics lines need the wall-clock.
52
+ The staging command in §1 already gave you the wall-clock start (`date +%s` in the same call) — §3's
53
+ metrics line needs it.
41
54
 
42
55
  ## 3. Merge & relay the verdict
43
56
 
44
57
  Merge the returned reports into **one** REVIEW REPORT (same template): findings concatenated and
45
58
  re-ordered by severity, counts summed, duplicates collapsed, verdict = the worst returned
46
- (`BLOCK` > `REVISE` > `SHIP`). Append one line per reviewer to `.claude/pipeline-metrics.jsonl`
47
- (gitignored): `{"ts":"<ISO>","feature":"$ARGUMENTS","phase":"review","surface":"<key>","seconds":<wall-clock>,"result":"<verdict>:<finding count>"}`.
48
- Print the report **and stage it to `specs/reports/$ARGUMENTS.md`** (overwrite) — a gitignored buffer so
49
- a `/fix` after a `/clear` can still read the findings; the `specs/reports/` subfolder is skipped by the
50
- non-recursive `specs/*.md` glob, so it's never mistaken for a spec (no phantom card, no bogus stage). Then:
59
+ (`BLOCK` > `REVISE` > `SHIP`). Append ONE metrics line for the batch to `.claude/pipeline-metrics.jsonl`
60
+ (gitignored): `{"ts":"<ISO>","feature":"$ARGUMENTS","phase":"review","seconds":<wall-clock>,"surfaces":{"<key>":"<verdict>:<finding count>",…}}`.
61
+ **Stage the full report to `specs/reports/$ARGUMENTS.md`** (overwrite) — a gitignored buffer so a
62
+ `/fix` after a `/clear` can still read the findings; the `specs/reports/` subfolder is skipped by the
63
+ non-recursive `specs/*.md` glob, so it's never mistaken for a spec (no phantom card, no bogus stage).
64
+ In chat print ONLY: the verdict, the severity-count table, a one-line digest of each CRITICAL/security
65
+ finding, and `Full report: specs/reports/$ARGUMENTS.md` — never echo the findings body into chat (it
66
+ would sit in this session's history, re-sent every turn). Then:
51
67
 
52
68
  - **SHIP** → a SHIP verdict *is* the pipeline's statement that the feature meets its Definition of
53
69
  Done, so **tick the DoD**: in `specs/$ARGUMENTS.md` §`Acceptance criteria / DoD`, flip each `- [ ]`
@@ -60,9 +76,14 @@ non-recursive `specs/*.md` glob, so it's never mistaken for a spec (no phantom c
60
76
  spec front-matter `reviewed_base: $BASE` plus
61
77
  `reviewed_digest: $(git diff $BASE -- . ':(exclude)specs/' | sha256sum | cut -c1-16)` — the fingerprint
62
78
  of exactly the source you just reviewed (specs excluded, so DoD ticks + the ship status flip don't
63
- trip it). Then tell the human they can `/ship` — _the handoff is fully on disk, so `/clear` first is safe._
64
- - **REVISE / BLOCK**, or any finding of any severity → tell the human to run **`/fix $ARGUMENTS`** —
65
- it appends the report to the spec's `## Remediation` and re-dispatches ONLY the surfaces with
66
- findings. The full path (`/spec` Mode B then `/build`) remains for findings that change the
67
- contract in ways that ripple into clean surfaces. _The report is staged to `specs/reports/$ARGUMENTS.md`,
68
- so you can `/clear` before `/fix` — it reads the findings back from disk._
79
+ trip it). Then tell the human they can `/ship` — **recommend a `/clear` first**, the handoff is
80
+ fully on disk. **SHIP with leftover LOW findings** (or LOW+MEDIUM at the human's call) does NOT
81
+ force a fix cycle for nits: park them in `specs/refactor-backlog.md` tagged `deferred:<id>` (NOT as
82
+ open `## Remediation` items, which would re-trigger the fix loop), keep the SHIP verdict and the
83
+ freshness stamp, and let the human ship.
84
+ - **REVISE / BLOCK**, or any CRITICAL/HIGH/security finding → tell the human to run
85
+ **`/fix $ARGUMENTS`** — it appends the report to the spec's `## Remediation` and re-dispatches ONLY
86
+ the surfaces with findings. The full path (`/spec` Mode B then `/build`) remains for findings that
87
+ change the contract in ways that ripple into clean surfaces. _The report is staged to
88
+ `specs/reports/$ARGUMENTS.md`, so you can `/clear` before `/fix` — it reads the findings back from
89
+ disk._
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: sonnet
2
3
  description: At a SHIP verdict, dispatch the release agent to commit, push, and open the PR (with your confirmation).
3
4
  argument-hint: <feature_id>
4
5
  ---
@@ -54,9 +55,10 @@ Confirm `specs/$ARGUMENTS.md` was committed as `status: shipped` (part of the re
54
55
  `shipped` column (SCHEMA.md §Kanban "Move a card") and **append the PR number** so the line reads
55
56
  `- [ ] <title> #$ARGUMENTS — PR #<num>`. Take `<num>` from the PR URL (`…/pull/13` ⇒ `13`); **always write
56
57
  it when a PR was created** (the `gh` path) — it is what the dashboard turns into a PR link. If only a
57
- compare URL was emitted (no PR yet), move the card without a number. Then **re-read the board** and confirm
58
- the card sits under the `shipped` heading with no stale copy left under its previous column. No board ⇒
59
- skip silently.
58
+ compare URL was emitted (no PR yet), move the card without a number. Then verify with a **grep for
59
+ `#$ARGUMENTS`** on the board (with surrounding heading context — `grep -B20 '#$ARGUMENTS' | grep '^##'`
60
+ or an offset-limited Read around the match): exactly one card, under the `shipped` heading — never
61
+ re-read the whole board into context. No board ⇒ skip silently.
60
62
 
61
63
  ## 5. After the PR — CI gate + teardown
62
64
 
@@ -66,3 +68,5 @@ skip silently.
66
68
  `scripts/remove-feature.sh $ARGUMENTS` (add `--drop-db` to also drop the feature db; kept by
67
69
  default). It removes the worktree, deletes the merged branch, frees the slot. Never run it before
68
70
  the merge is confirmed, and only with the human's go-ahead (the gate will ask anyway).
71
+ - Feature closed — **recommend a `/clear`** before starting the next one; nothing from this session
72
+ is needed again (spec `shipped`, PR merged, board updated).
@@ -1,55 +1,39 @@
1
1
  ---
2
+ model: sonnet
2
3
  description: Exercise the built feature end-to-end in its worktree — infra up, migrations, contract endpoints, key UI flows, visual check vs design — before /review.
3
4
  argument-hint: <feature_id>
4
5
  ---
5
6
 
6
- You are the **lead**. Smoke-test feature **$ARGUMENTS** — actually run it. `/review` audits the code
7
- read-only; nobody has executed it yet. You verify it *works*, you never fix it here (failures go
8
- through `/fix`). Observe honestly: report what happened, not what should have happened.
7
+ You are the **lead**. Dispatch the smoke run for feature **$ARGUMENTS** — the `smoke` agent actually
8
+ runs it so the bulky output (curl bodies, server logs, screenshots, design payloads) never enters
9
+ your own context, which is re-sent every turn.
9
10
 
10
- > Read `PIPELINE.md` §`pipeline-profile`: `commands` (migrate/dev), `isolation` (worktree, slot
11
- > ports, db), `contract`, `design`, `surfaces`, and the spec `specs/$ARGUMENTS.md` (§5 contract,
12
- > §9 acceptance).
11
+ > Read `PIPELINE.md` §`pipeline-profile`: `isolation` (worktree, slot ports, db) and `contract.path`.
12
+ > _Skip the re-read if it's already in your context this session and unmodified since._
13
13
  >
14
- > **Kanban** (SCHEMA.md §Kanban): move card `#$ARGUMENTS` → **Review**. No-op silently if no board.
15
-
16
- ## 1. Bring the feature up
17
-
18
- - Work in the feature's checkout: with `isolation.enabled`, the sibling worktree
19
- (`../<slug>-$ARGUMENTS`, its slot's ports + db from `.worktrees/slots.tsv`); otherwise the main
20
- checkout on the feature branch.
21
- - Infra as needed: the compose stack if one is declared (the gate will ask — that's expected),
22
- then `commands.migrate`, then `commands.dev` **in the background**. Wait for ready (poll the
23
- ports), don't assume.
24
-
25
- ## 2. Exercise the contract (the real server, not the tests)
26
-
27
- - Hit a representative set of spec §5 endpoints with `curl`: every route domain, every auth level,
28
- at least one error case per class (validation `422`, unauthenticated `401`, wrong-role `403`,
29
- conflict `409`). Compare status + response envelope against the contract.
30
- - If `rbac.enabled`: verify at least one denial per role boundary the spec declares.
31
- - A mismatch is a FAIL entry with the exact command, expected, and actual — precise enough for a
32
- stateless `/fix` agent.
33
-
34
- ## 3. Exercise the UI (only if a touched surface has `uses_design`)
35
-
36
- - Drive the spec §8 flows against the running app, **mobile viewport first** (375px), then desktop.
37
- - If a browser/screenshot tool is available (a project driver, playwright, an agent browser), capture
38
- each §8 screen and compare against the feature's design pages: each `design_files` entry is a full
39
- `https://claude.ai/design/p/<projectId>?file=<file>` link — extract its `<projectId>` (the `/p/…`
40
- segment) + `<file>` (the `?file=` query) and fetch read-only via `DesignSync get_file(<projectId>,
41
- <file>)`. Compare layout, states (empty/loading/error/suppressed…), copy language. Note deviations.
42
- - No browser tooling available ⇒ **say so and skip the visual diff** — never claim a visual check
43
- you didn't perform.
44
-
45
- ## 4. SMOKE REPORT
46
-
47
- - One line per check: ✅/❌ · what was exercised · (on ❌) command → expected vs actual.
48
- - **Stage the full report to `specs/reports/$ARGUMENTS.md`** (overwrite) — the same gitignored buffer
49
- `/review` uses (subfolder ⇒ skipped by the non-recursive `specs/*.md` glob), so a `/fix` after a
50
- `/clear` still has the failures.
51
- - Verdict **PASS** (all green) → tell the human to run `/review $ARGUMENTS`.
52
- Verdict **FAIL** → the failures are findings: feed them to `/fix $ARGUMENTS`, re-run `/smoke`
53
- after. _Either way the handoff is on disk — `/clear` before the next command is safe._
54
- - Append one metrics line to `.claude/pipeline-metrics.jsonl` (see `/build` §4, `phase: "smoke"`).
55
- - Tear down what you started (kill the dev server); leave shared infra as you found it.
14
+ > **Kanban:** none here — `/review` owns the → **Review** move (running both duplicated it).
15
+
16
+ ## 1. Resolve the checkout
17
+
18
+ With `isolation.enabled`: the sibling worktree (`../<slug>-$ARGUMENTS`, its slot's ports + db from
19
+ `.worktrees/slots.tsv`); otherwise the main checkout on the feature branch. Note the epoch
20
+ (`date +%s`) in the same Bash call that reads the slot — §3's metrics line needs it.
21
+
22
+ ## 2. Dispatch ONE `smoke` agent
23
+
24
+ > `subagent_type: smoke` — "Smoke-test feature `$ARGUMENTS`. Read `PIPELINE.md` first. Spec:
25
+ > `specs/$ARGUMENTS.md`. Contract: `<contract.path>/$ARGUMENTS.<ext>`. Checkout: `<worktree path or
26
+ > main checkout>` · ports/db: `<slot info, or defaults>`. Bring it up, exercise the contract and the
27
+ > §8 UI flows, stage the full SMOKE REPORT to `specs/reports/$ARGUMENTS.md`, tear down, and return
28
+ > only the verdict + failure lines."
29
+
30
+ The gate hooks fire on the agent's Bash calls too — compose/migrate confirmations still reach the
31
+ human; that's expected.
32
+
33
+ ## 3. Relay the verdict
34
+
35
+ - Print the agent's return as-is (verdict + ❌ lines + report path) — it is already minimal.
36
+ - Append ONE metrics line to `.claude/pipeline-metrics.jsonl` (see `/build` §4, `phase: "smoke"`).
37
+ - **PASS** → tell the human to run `/review $ARGUMENTS`. **FAIL** → the failures are findings: feed
38
+ them to `/fix $ARGUMENTS`, re-run `/smoke` after. Either way the report is on disk —
39
+ **recommend a `/clear`** before the next command.
@@ -20,16 +20,27 @@ Detect the mode from the pasted content:
20
20
 
21
21
  ## Mode A — new spec (input is a brainstorm return, or empty)
22
22
 
23
- 1. If empty, ask the human to paste the brainstorm return (or describe the feature) and wait.
23
+ 1. If empty, look for a staged brainstorm return first — `specs/reports/*-brainstorm.md` (where
24
+ `/brainstorm` stages its output); one match ⇒ read it and confirm, several ⇒ ask which. None ⇒
25
+ ask the human to paste the return (or describe the feature) and wait.
24
26
  2. Derive a `feature_id` (kebab-case slug). Confirm it.
27
+ 2b. **Size budget — a spec is a contract, not a novel.** Target ≤ ~300 lines; hard-think at 500.
28
+ Every implementer re-reads the whole spec on every first build, so each extra line is paid
29
+ `surfaces × dispatches` times. If the feature genuinely needs more, that's the signal it is TWO
30
+ features: propose a split (e.g. `<id>-core` + `<id>-admin`, each independently shippable, the
31
+ second consuming the first's contract) and let the human pick. Trim the usual bloat before
32
+ writing: exhaustive UI walkthroughs (the design brief carries those), restated conventions
33
+ (PIPELINE.md carries those), speculative edge cases nobody asked for.
25
34
  3. Walk the human through each section of `specs/_template.md`, **section by section**, with focused
26
35
  questions. The critical one is **§5 API CONTRACT** — pin down every endpoint/interface (method, path,
27
36
  auth/role, request fields with types/validation, success envelope + data shape, and every error case),
28
37
  and name the exact schema/types that will live in the contract file (`contract.path/<id>.<ext>` in the
29
38
  profile's `mechanism`). Don't move on until frontend and backend could each build from it with zero
30
39
  further questions. _If `contract.enabled` is false, capture the interface precisely in prose instead._
31
- 4. Fill **§8 Design brief** (only if `design.enabled` / the feature has UI) so it's self-contained for the
32
- design step: screens, states, components, responsive notes.
40
+ 4. Capture the **design brief** content (only if `design.enabled` / the feature has UI): screens,
41
+ states, components, responsive notes — it will be authored to `specs/design/<id>.md` in step 6;
42
+ spec §8 carries only a short summary + the pointer `> full brief: specs/design/<id>.md` (so
43
+ non-design surfaces never re-read the full brief on every dispatch).
33
44
  4b. **New-surface heads-up.** If the feature clearly introduces an area no existing `surfaces[].path`
34
45
  owns (a new service/app/top-level module), note it in the spec (a line in the relevant task section:
35
46
  `> needs new surface: <proposed key/path>`). Don't render agents here — `/build` §1.5 auto-reconciles
@@ -37,31 +48,34 @@ Detect the mode from the pasted content:
37
48
  5. When the human validates, **freeze**: write `specs/<id>.md` (`status: frozen`, front-matter filled).
38
49
  Create the file — do not ask the human to. **Postcondition:** `grep -q '^status: frozen' specs/<id>.md`
39
50
  — if it fails the freeze didn't land; fix it before pointing the human at `/build`.
40
- 6. Emit the **spec return** — the §8 design brief rendered via `.claude/templates/design-brief.md`.
51
+ 6. Author the **design brief** — `specs/design/<id>.md`, rendered via
52
+ `.claude/templates/design-brief.md` (resolves to `~/.claude/templates/…` on a global install).
41
53
  _Only if `design.enabled` / the feature has UI; skip entirely for a backend-only feature._
42
- - **Write it to `specs/design/<id>.md`** (a standalone `.md`, versioned with the spec). Create the
43
- file — do not ask the human to. Keep it in the `specs/design/` subfolder, **not** `specs/<id>....md`:
44
- the `specs/*.md` glob that drives the kanban backfill and `/doctor` is non-recursive, so a brief in
45
- the subfolder never gets mistaken for a spec (no phantom card, no bogus stage). This is a derived
46
- artifact that mirrors §8 — overwrite it on every freeze so it never drifts from the spec.
47
- - Then print the same content in a copy-paste block and tell the human: paste it into the design tool
48
- (if any — typically a fresh design project for this feature) or just open `specs/design/<id>.md`,
49
- then run `/build <id>` and hand its design gate the resulting page link(s) — a full
50
- `https://claude.ai/design/p/<projectId>?file=<file>` link carries its own project + page, no profile
51
- change needed. (They can also paste the links into the spec's `design_files`
52
- themselves.) _The frozen spec + `specs/design/<id>.md` are the whole handoff — `/clear` before
53
- `/build` is safe._
54
+ - **Write it to `specs/design/<id>.md`** (the authored artifact, versioned with the spec; spec §8
55
+ holds the summary + pointer). Create the file — do not ask the human to. Keep it in the
56
+ `specs/design/` subfolder, **not** `specs/<id>....md`: the `specs/*.md` glob that drives the
57
+ kanban backfill and `/doctor` is non-recursive, so a brief in the subfolder never gets mistaken
58
+ for a spec (no phantom card, no bogus stage). Overwrite it on every freeze.
59
+ - Print ONLY the path + a one-line summary — never echo the brief into chat (it would sit in this
60
+ session's history; echo it only if the human asks). Tell the human: copy it from
61
+ `specs/design/<id>.md` into the design tool (if any — typically a fresh design project for this
62
+ feature), then run `/build <id>` and hand its design gate the resulting page link(s) — a full
63
+ `https://claude.ai/design/p/<projectId>?file=<file>` link carries its own project + page, no
64
+ profile change needed. (They can also paste the links into the spec's `design_files` themselves.)
65
+ **Recommend a `/clear` before `/build`** — the frozen spec + `specs/design/<id>.md` are the whole
66
+ handoff.
54
67
 
55
68
  ## Mode B — review return (input is a REVIEW REPORT)
56
69
 
57
- 1. Read the report — pasted as input, or (if the context was cleared) read from `specs/reports/<id>.md`,
58
- where `/review` stages its last report. Identify `feature_id` from its header; open `specs/<id>.md`.
70
+ 1. Read the report — pasted as input, or (whenever nothing is pasted) read from
71
+ `specs/reports/<id>.md`, where `/review` stages its last report. Identify `feature_id` from its
72
+ header; open `specs/<id>.md`.
59
73
  2. Append each finding to the spec's **`## Remediation`**, one per line:
60
74
  `- [ ] <severity> · <file:line> · <spec-violation|quality|security> · <concrete fix>`
61
75
  (Keep prior items; add the new round under a dated/numbered subheading.)
62
76
  3. If a finding implies the **contract** must change, update §5 and flag it so the lead re-authors the
63
77
  contract file.
64
78
  4. Set `status: in-review`. Tell the human to run `/build <id>` to re-dispatch fresh agents.
65
- _The spec is the whole handoff — `/clear` before `/build` is safe._
79
+ **Recommend a `/clear` before `/build`** — the spec is the whole handoff.
66
80
 
67
81
  In both modes the spec is the single source of truth; agents are stateless and read only it + the diff.
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: sonnet
2
3
  description: Refresh the pipeline core (global ~/.claude, or a repo's bundled .claude) to the latest published cohorte version, then reconcile this repo's generated files to it — /init-pipeline stays one-time.
3
4
  argument-hint: [path-to-local-checkout]
4
5
  ---
@@ -1,29 +1,23 @@
1
1
  # HANDOFF — <surface> · `<feature_id>`
2
2
 
3
- Branch: <feature_branch_prefix><feature_id>
4
- Commit/diff base: <default_branch...HEAD or "uncommitted working tree">
3
+ <!-- Keep it tight: the lead only acts on mismatches, test failures, remediation ticks, and TODOs.
4
+ Never list files one by one — the lead has `git diff --stat`. -->
5
5
 
6
6
  ## Summary
7
7
 
8
8
  <2–4 lines: what you built and the approach>
9
9
 
10
- ## Files touched
10
+ ## Migrations / schema (only if any)
11
11
 
12
- - `path/to/file` — <what & why>
13
-
14
- ## Migrations / schema (if any)
15
-
16
- - `<name>` — <additive change> · run with <PIPELINE.md commands.migrate>
12
+ - `<name>` — <additive change>
17
13
 
18
14
  ## Tests
19
15
 
20
- - Added: <test files>
21
16
  - Run: <this surface's test_cmd> · result: <pass/fail + counts>
22
17
 
23
- ## Contract adherence
18
+ ## Contract mismatches / assumptions
24
19
 
25
- - [ ] Implemented exactly to the frozen contract (`<contract.path>/<feature_id>.<ext>`)
26
- - Mismatches / assumptions: <none, or describe — DO NOT edit the contract; report instead>
20
+ <none, or describe — NEVER edit the contract; report here instead>
27
21
 
28
22
  ## Remediation addressed (fix loops only)
29
23
 
@@ -31,4 +25,4 @@ Commit/diff base: <default_branch...HEAD or "uncommitted working tree">
31
25
 
32
26
  ## TODO / not done
33
27
 
34
- - <anything deferred, blocked, or out of scope>
28
+ - <anything deferred, blocked, or out of scope — or "none">
@@ -6,16 +6,12 @@
6
6
 
7
7
  <2–4 lines: what this ships and the value, from the spec goal>
8
8
 
9
- ### Contract
10
-
11
- <the interface + contract types added/changed — from spec §5 (omit if contract.enabled is false)>
12
-
13
9
  ### Changes
14
10
 
15
11
  > One bullet per surface (from `PIPELINE.md` §surfaces), plus the shared contract.
16
12
 
17
13
  - **<surface.key>:** <what changed>
18
- - **Shared:** `<contract.path>/<feature_id>.<ext>`
14
+ - **Shared:** `<contract.path>/<feature_id>.<ext>` <(new/changed types: names only; omit if contract.enabled is false)>
19
15
 
20
16
  ### Tests
21
17
 
@@ -1,36 +1,32 @@
1
1
  # REVIEW REPORT
2
2
 
3
+ <!-- Emitted-report shape. Everything in an HTML comment is guidance — do NOT emit it.
4
+ Verdict rules live in the review agent's instructions (SHIP = no CRITICAL/security;
5
+ REVISE = ≥1 CRITICAL; BLOCK = security vulnerability). -->
6
+
3
7
  feature_id: <feature_id>
4
8
  Feature branch: <feature_branch_prefix><feature_id>
5
9
  Commit SHA: <first 12 chars>
6
10
 
7
- | Severity | Count | Status |
8
- | -------- | ----- | ------ |
9
- | CRITICAL | 0 | pass |
10
- | HIGH | 0 | noted |
11
- | MEDIUM | 0 | noted |
12
- | LOW | 0 | - |
11
+ | Severity | Count |
12
+ | -------- | ----- |
13
+ | CRITICAL | 0 |
14
+ | HIGH | 0 |
15
+ | MEDIUM | 0 |
16
+ | LOW | 0 |
13
17
 
14
18
  Verdict: <SHIP | REVISE | BLOCK>
15
19
 
16
- ## Verdict rules
17
-
18
- - **SHIP** — no CRITICAL and no security issue. Cleared for the human's manual QA + `/ship`.
19
- - **REVISE** — one or more CRITICAL (spec violation / correctness). Must fix.
20
- - **BLOCK** — a security vulnerability. Must fix immediately.
21
-
22
20
  ## Findings
23
21
 
24
- > Every finding is self-sufficient: a stateless agent must be able to act on it with no other
25
- > context. Order by severity. If none, write "None."
22
+ <!-- Every finding self-sufficient for a stateless agent; order by severity; "None." if none.
23
+ The line format pastes straight into the spec's `## Remediation`. -->
26
24
 
27
25
  - **[CRITICAL]** `<surface.path>/...:42` · spec-violation · <what's wrong vs spec §X> → **Fix:** <concrete change>
28
26
  - **[HIGH]** `<path>:88` · quality · <issue> → **Fix:** <concrete change>
29
27
  - **[BLOCK/security]** `<path>:line` · security · <vuln> → **Fix:** <concrete change>
30
28
 
31
- Each finding line format (so it pastes straight into the spec's `## Remediation`):
32
- `[<SEVERITY>] <file:line> · <spec-violation|quality|security> · <problem> → Fix: <concrete fix>`
33
-
34
29
  ## Notes
35
30
 
36
- <optional: patterns to watch, things verified clean; RBAC / mobile-first assessment if the profile enables them>
31
+ <!-- ONLY the RBAC / mobile-first assessment when the profile enables them; omit the section
32
+ otherwise. Never list things verified clean. -->
@@ -1 +1 @@
1
- :root{--bg: #0e0f13;--panel: #16181f;--panel-2: #1c1f28;--border: #262a35;--text: #e6e8ee;--muted: #8b90a0;--accent: #6ea8fe;--ok: #3fb950;--warn: #d9a441;--bad: #f85149;--dev: #a371f7;--mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font-family:var(--mono);font-size:14px}.app{min-height:100vh}.topbar{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--border);position:sticky;top:0;z-index:20;background:#0e0f13e6;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}.brand{font-weight:600;letter-spacing:.2px;display:flex;align-items:center;gap:8px}.dot{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent)}.muted{color:var(--muted)}.small{font-size:12px}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:16px;padding:20px;max-width:1100px;margin:0 auto}@media(max-width:720px){.grid{grid-template-columns:1fr}}.panel{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px 18px}.panel h2{margin:0 0 12px;font-size:13px;text-transform:uppercase;letter-spacing:1px;color:var(--muted)}.panel.placeholder{opacity:.7}.panel.error{grid-column:1 / -1;border-color:var(--bad);color:var(--bad)}.panel-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.panel-head h2{margin:0}.badge{font-size:11px;padding:3px 9px;border-radius:999px;border:1px solid var(--border);white-space:nowrap}.badge.ok{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent);background:color-mix(in srgb,var(--ok) 12%,transparent)}.badge.warn{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 40%,transparent);background:color-mix(in srgb,var(--warn) 12%,transparent)}.badge.bad{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 40%,transparent);background:color-mix(in srgb,var(--bad) 12%,transparent)}.badge.dev{color:var(--dev);border-color:color-mix(in srgb,var(--dev) 40%,transparent);background:color-mix(in srgb,var(--dev) 12%,transparent)}.badge.neutral{color:var(--muted)}.rows{display:flex;flex-direction:column;gap:2px}.row{display:flex;justify-content:space-between;gap:12px;padding:6px 0;border-bottom:1px dashed var(--border)}.row:last-child{border-bottom:none}.row-label{color:var(--muted)}.row-value{text-align:right}.row-value.strong{font-weight:600}.row-value.mono{font-family:var(--mono);font-size:12px}.actions{display:flex;align-items:center;gap:10px;margin-top:16px}.fresh-hint{margin:14px 0 0;padding-top:12px;border-top:1px dashed var(--border)}.fresh-hint strong{color:var(--accent)}button{font-family:var(--mono);font-size:13px;padding:7px 12px;border-radius:8px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);cursor:pointer}button:disabled{opacity:.4;cursor:not-allowed}button.primary{background:color-mix(in srgb,var(--accent) 22%,var(--panel-2));border-color:color-mix(in srgb,var(--accent) 40%,transparent)}button.ghost{background:transparent}button:not(:disabled):hover{border-color:var(--accent)}.topbar-right{display:flex;align-items:center;gap:12px}.span2{grid-column:span 2}@media(max-width:720px){.span2{grid-column:1 / -1}}.summary{display:flex;gap:6px;flex-wrap:wrap}.checks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}.check{display:flex;gap:10px;padding:9px 0;border-bottom:1px solid var(--border)}.check:last-child{border-bottom:none}.check.skip{opacity:.55}.check-icon{flex:none;width:20px;height:20px;border-radius:6px;display:grid;place-items:center;font-size:12px;font-weight:700;margin-top:1px}.check-icon.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 15%,transparent)}.check-icon.warn{color:var(--warn);background:color-mix(in srgb,var(--warn) 15%,transparent)}.check-icon.bad{color:var(--bad);background:color-mix(in srgb,var(--bad) 15%,transparent)}.check-icon.skip{color:var(--muted);background:var(--panel-2)}.check-body{flex:1;min-width:0}.check-line{display:flex;gap:10px;justify-content:space-between;flex-wrap:wrap}.check-label{font-weight:600}.check-detail{color:var(--muted);text-align:right}.check-fix{margin-top:4px;font-size:12px;color:var(--muted)}.check-fix code{color:var(--accent);background:var(--panel-2);padding:1px 6px;border-radius:5px}.surfaces{display:flex;flex-direction:column;gap:10px}.surface{border:1px solid var(--border);border-radius:9px;padding:10px 12px;background:var(--panel-2)}.surface-top{display:flex;align-items:center;gap:8px}.surface-key{font-weight:600}.surface-meta{display:flex;justify-content:space-between;gap:10px;margin-top:4px;font-size:12px}.surface-tools{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}.tool{font-size:11px;color:var(--muted);border:1px solid var(--border);border-radius:5px;padding:1px 6px}.chip{font-size:11px;padding:1px 8px;border-radius:999px;border:1px solid var(--border)}.chip.design{color:var(--dev);border-color:color-mix(in srgb,var(--dev) 40%,transparent)}.chip.model-sonnet{color:var(--accent)}.chip.model-haiku{color:var(--ok)}.chip.model-inherit{color:var(--warn)}.fleet{max-width:1100px;margin:0 auto;padding:20px}.core-banner{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;background:linear-gradient(180deg,var(--panel-2),var(--panel));border:1px solid var(--border);border-radius:12px;padding:14px 18px;margin-bottom:20px}.cb-left{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.cb-title{text-transform:uppercase;letter-spacing:1px;font-size:12px;color:var(--muted)}.cb-version{font-weight:600;font-size:15px}.cb-actions{display:flex;gap:8px}.fleet-head{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:14px;flex-wrap:wrap}.fleet-head h2{margin:0;font-size:14px;display:flex;align-items:center;gap:8px}.add-wrap{flex:1;max-width:560px}.add-form{display:flex;gap:8px}.path-input{flex:1;font-family:var(--mono);font-size:13px;padding:7px 12px;background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:8px}.path-input:focus{outline:none;border-color:var(--accent)}.path-input.invalid{border-color:var(--bad)}.add-error{margin-top:6px;font-size:12px;color:var(--bad);word-break:break-word}.fleet-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}.project-card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:14px 16px;cursor:pointer;transition:border-color .12s,transform .12s}.project-card:hover{border-color:var(--accent);transform:translateY(-1px)}.project-card.gone{opacity:.6;cursor:default}.pc-head{display:flex;align-items:center;justify-content:space-between}.pc-name{font-weight:600}.pc-path{margin:2px 0 10px}.pc-badges{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}.pc-health{display:flex;align-items:center;gap:5px}.pc-counts{margin-left:auto}.icon-btn{border:none;background:transparent;color:var(--muted);padding:2px 6px;border-radius:6px;font-size:13px}.icon-btn:hover{color:var(--bad);background:var(--panel-2)}.pill{font-size:11px;min-width:20px;text-align:center;padding:1px 7px;border-radius:999px;font-weight:600}.pill.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 14%,transparent)}.pill.warn{color:var(--warn);background:color-mix(in srgb,var(--warn) 14%,transparent)}.pill.bad{color:var(--bad);background:color-mix(in srgb,var(--bad) 14%,transparent)}.pill.neutral{color:var(--muted);background:var(--panel-2)}.detail-crumb{grid-column:1 / -1;margin-bottom:-4px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.detail-tools{display:flex;gap:8px;flex-wrap:wrap}.tool-btn{font-size:12px;padding:5px 10px;border:1px solid color-mix(in srgb,var(--accent) 35%,var(--border));color:var(--accent);background:transparent}.tool-btn:hover{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 10%,transparent)}.confirm-text p{margin:0 0 8px;font-size:13px;line-height:1.6}.confirm-text code{background:var(--panel-2);padding:1px 5px;border-radius:4px;color:var(--accent)}.warn-line{color:var(--warn)!important;background:color-mix(in srgb,var(--warn) 10%,transparent);border-radius:8px;padding:8px 10px}.warn-line code{color:var(--text)!important}.danger-ghost{background:transparent;border:1px solid color-mix(in srgb,var(--bad) 40%,var(--border));color:var(--bad);font-size:12px;padding:5px 10px}.danger-ghost:hover{background:color-mix(in srgb,var(--bad) 12%,transparent);border-color:var(--bad)}button.danger{background:color-mix(in srgb,var(--bad) 20%,var(--panel-2));border-color:color-mix(in srgb,var(--bad) 45%,transparent);color:#ffd7d3}button.danger:hover{border-color:var(--bad)}.reset-list{margin:8px 0;padding-left:18px;font-size:13px;line-height:1.7}.reset-list code{color:var(--accent)}.reset-note{font-size:12.5px;color:var(--muted);background:var(--panel-2);border-radius:8px;padding:10px 12px}.reset-note code{color:var(--text)}.reset-check{display:flex;align-items:center;gap:8px;margin:12px 0 0;font-size:13px}.reset-check code{color:var(--accent)}.board-scroll{max-height:500px;overflow:auto}.board{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}.col{background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:8px;min-width:140px}.col-head{display:flex;justify-content:space-between;font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);padding:2px 4px 8px;position:sticky;top:0;background:var(--bg);z-index:1}.col-shipped{opacity:.85}.col-other{border-color:color-mix(in srgb,var(--warn) 40%,var(--border))}.spec-card{background:var(--panel-2);border:1px solid var(--border);border-radius:7px;padding:8px 9px;margin-bottom:7px}.spec-card.bad{border-color:color-mix(in srgb,var(--warn) 45%,transparent)}.spec-title{font-size:13px;font-weight:600}.spec-meta,.spec-branch{margin-top:3px}@media(max-width:640px){.board{grid-template-columns:repeat(2,1fr)}}.kanban-board{display:flex;gap:10px}.kanban-col{flex:0 0 210px;background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:8px}.kanban-col.empty{opacity:.5}.kanban-card{background:var(--panel-2);border:1px solid var(--border);border-radius:7px;padding:8px 9px;margin-bottom:7px}.kanban-card.done{opacity:.6}.kanban-card.done .kc-text{text-decoration:line-through}.kc-text{font-size:12.5px;line-height:1.4}.kc-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.kc-tag{font-size:10px;color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);border-radius:4px;padding:1px 5px}.kc-pr{font-size:10px;border-radius:4px;padding:1px 5px;text-decoration:none;border:1px solid var(--border);color:var(--muted)}a.kc-pr:hover{filter:brightness(1.25)}.kc-pr.state-open{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent);background:color-mix(in srgb,var(--ok) 12%,transparent)}.kc-pr.state-merged{color:var(--dev);border-color:color-mix(in srgb,var(--dev) 40%,transparent);background:color-mix(in srgb,var(--dev) 12%,transparent)}.kc-pr.state-closed{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 40%,transparent);background:color-mix(in srgb,var(--bad) 12%,transparent)}.kc-pr.draft{color:var(--muted);border-color:var(--border);background:var(--panel-2)}.kc-status{font-size:10px;margin-top:5px;color:var(--muted);text-transform:capitalize}.kc-status.state-open{color:var(--ok)}.kc-status.state-merged{color:var(--dev)}.kc-status.state-closed{color:var(--bad)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;display:grid;place-items:center;z-index:50;padding:20px}.modal{background:var(--panel);border:1px solid var(--border);border-radius:12px;width:min(720px,100%);max-height:80vh;display:flex;flex-direction:column;padding:18px}.modal-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.modal-head h3{margin:0;font-size:15px}.modal-actions{display:flex;gap:8px;margin-top:14px}.cmd{background:#000;color:var(--accent);padding:10px 12px;border-radius:8px;font-size:13px;overflow-x:auto}.run-log{background:#000;color:#d6d9e0;padding:12px;border-radius:8px;font-size:12.5px;line-height:1.5;overflow:auto;white-space:pre-wrap;word-break:break-word;flex:1;min-height:200px;max-height:55vh;margin:0}.picker{max-height:74vh}.picker-path{background:var(--panel-2);border:1px solid var(--border);border-radius:7px;padding:7px 10px;margin-bottom:10px;word-break:break-all}.picker-list{flex:1;overflow:auto;border:1px solid var(--border);border-radius:8px;padding:6px;min-height:220px;max-height:48vh}.picker-row{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:transparent;border:none;border-radius:6px;padding:7px 9px;color:var(--text);font-size:13px}.picker-row:hover{background:var(--panel-2);border-color:transparent}.picker-row.up{color:var(--muted)}.picker-icon{color:var(--muted);width:14px}.picker-row .badge.small{font-size:10px;padding:0 6px;margin-left:auto}.picker-empty{padding:12px}.add-form .ghost[type=button]{white-space:nowrap}
1
+ :root{--bg: #0e0f13;--panel: #16181f;--panel-2: #1c1f28;--border: #262a35;--text: #e6e8ee;--muted: #8b90a0;--accent: #6ea8fe;--ok: #3fb950;--warn: #d9a441;--bad: #f85149;--dev: #a371f7;--mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font-family:var(--mono);font-size:14px}.app{min-height:100vh}.topbar{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--border);position:sticky;top:0;z-index:20;background:#0e0f13e6;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}.brand{font-weight:600;letter-spacing:.2px;display:flex;align-items:center;gap:8px}.dot{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent)}.muted{color:var(--muted)}.small{font-size:12px}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:16px;padding:20px;max-width:1100px;margin:0 auto}@media(max-width:720px){.grid{grid-template-columns:1fr}}.panel{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px 18px}.panel h2{margin:0 0 12px;font-size:13px;text-transform:uppercase;letter-spacing:1px;color:var(--muted)}.panel.placeholder{opacity:.7}.panel.error{grid-column:1 / -1;border-color:var(--bad);color:var(--bad)}.panel-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.panel-head h2{margin:0}.badge{font-size:11px;padding:3px 9px;border-radius:999px;border:1px solid var(--border);white-space:nowrap}.badge.ok{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent);background:color-mix(in srgb,var(--ok) 12%,transparent)}.badge.warn{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 40%,transparent);background:color-mix(in srgb,var(--warn) 12%,transparent)}.badge.bad{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 40%,transparent);background:color-mix(in srgb,var(--bad) 12%,transparent)}.badge.dev{color:var(--dev);border-color:color-mix(in srgb,var(--dev) 40%,transparent);background:color-mix(in srgb,var(--dev) 12%,transparent)}.badge.neutral{color:var(--muted)}.rows{display:flex;flex-direction:column;gap:2px}.row{display:flex;justify-content:space-between;gap:12px;padding:6px 0;border-bottom:1px dashed var(--border)}.row:last-child{border-bottom:none}.row-label{color:var(--muted)}.row-value{text-align:right}.row-value.strong{font-weight:600}.row-value.mono{font-family:var(--mono);font-size:12px}.actions{display:flex;align-items:center;gap:10px;margin-top:16px}.fresh-hint{margin:14px 0 0;padding-top:12px;border-top:1px dashed var(--border)}.fresh-hint strong{color:var(--accent)}button{font-family:var(--mono);font-size:13px;padding:7px 12px;border-radius:8px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);cursor:pointer}button:disabled{opacity:.4;cursor:not-allowed}button.primary{background:color-mix(in srgb,var(--accent) 22%,var(--panel-2));border-color:color-mix(in srgb,var(--accent) 40%,transparent)}button.ghost{background:transparent}button:not(:disabled):hover{border-color:var(--accent)}.topbar-right{display:flex;align-items:center;gap:12px}.span2{grid-column:span 2}@media(max-width:720px){.span2{grid-column:1 / -1}}.summary{display:flex;gap:6px;flex-wrap:wrap}.checks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}.check{display:flex;gap:10px;padding:9px 0;border-bottom:1px solid var(--border)}.check:last-child{border-bottom:none}.check.skip{opacity:.55}.check-icon{flex:none;width:20px;height:20px;border-radius:6px;display:grid;place-items:center;font-size:12px;font-weight:700;margin-top:1px}.check-icon.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 15%,transparent)}.check-icon.warn{color:var(--warn);background:color-mix(in srgb,var(--warn) 15%,transparent)}.check-icon.bad{color:var(--bad);background:color-mix(in srgb,var(--bad) 15%,transparent)}.check-icon.skip{color:var(--muted);background:var(--panel-2)}.check-body{flex:1;min-width:0}.check-line{display:flex;gap:10px;justify-content:space-between;flex-wrap:wrap}.check-label{font-weight:600}.check-detail{color:var(--muted);text-align:right}.check-fix{margin-top:4px;font-size:12px;color:var(--muted)}.check-fix code{color:var(--accent);background:var(--panel-2);padding:1px 6px;border-radius:5px}.surfaces{display:flex;flex-direction:column;gap:10px}.surface{border:1px solid var(--border);border-radius:9px;padding:10px 12px;background:var(--panel-2)}.surface-top{display:flex;align-items:center;gap:8px}.surface-key{font-weight:600}.surface-meta{display:flex;justify-content:space-between;gap:10px;margin-top:4px;font-size:12px}.surface-tools{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}.tool{font-size:11px;color:var(--muted);border:1px solid var(--border);border-radius:5px;padding:1px 6px}.chip{font-size:11px;padding:1px 8px;border-radius:999px;border:1px solid var(--border)}.chip.design{color:var(--dev);border-color:color-mix(in srgb,var(--dev) 40%,transparent)}.chip.model-sonnet{color:var(--accent)}.chip.model-haiku{color:var(--ok)}.chip.model-inherit{color:var(--warn)}.fleet{max-width:1100px;margin:0 auto;padding:20px}.core-banner{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;background:linear-gradient(180deg,var(--panel-2),var(--panel));border:1px solid var(--border);border-radius:12px;padding:14px 18px;margin-bottom:20px}.cb-left{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.cb-title{text-transform:uppercase;letter-spacing:1px;font-size:12px;color:var(--muted)}.cb-version{font-weight:600;font-size:15px}.cb-actions{display:flex;gap:8px}.fleet-head{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:14px;flex-wrap:wrap}.fleet-head h2{margin:0;font-size:14px;display:flex;align-items:center;gap:8px}.add-wrap{flex:1;max-width:560px}.add-form{display:flex;gap:8px}.path-input{flex:1;font-family:var(--mono);font-size:13px;padding:7px 12px;background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:8px}.path-input:focus{outline:none;border-color:var(--accent)}.path-input.invalid{border-color:var(--bad)}.add-error{margin-top:6px;font-size:12px;color:var(--bad);word-break:break-word}.fleet-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}.project-card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:14px 16px;cursor:pointer;transition:border-color .12s,transform .12s}.project-card:hover{border-color:var(--accent);transform:translateY(-1px)}.project-card.gone{opacity:.6;cursor:default}.pc-head{display:flex;align-items:center;justify-content:space-between}.pc-name{font-weight:600}.pc-path{margin:2px 0 10px}.pc-badges{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}.pc-health{display:flex;align-items:center;gap:5px}.pc-counts{margin-left:auto}.icon-btn{border:none;background:transparent;color:var(--muted);padding:2px 6px;border-radius:6px;font-size:13px}.icon-btn:hover{color:var(--bad);background:var(--panel-2)}.pill{font-size:11px;min-width:20px;text-align:center;padding:1px 7px;border-radius:999px;font-weight:600}.pill.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 14%,transparent)}.pill.warn{color:var(--warn);background:color-mix(in srgb,var(--warn) 14%,transparent)}.pill.bad{color:var(--bad);background:color-mix(in srgb,var(--bad) 14%,transparent)}.pill.neutral{color:var(--muted);background:var(--panel-2)}.detail-crumb{grid-column:1 / -1;margin-bottom:-4px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.detail-tools{display:flex;gap:8px;flex-wrap:wrap}.tool-btn{font-size:12px;padding:5px 10px;border:1px solid color-mix(in srgb,var(--accent) 35%,var(--border));color:var(--accent);background:transparent}.tool-btn:hover{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 10%,transparent)}.confirm-text p{margin:0 0 8px;font-size:13px;line-height:1.6}.confirm-text code{background:var(--panel-2);padding:1px 5px;border-radius:4px;color:var(--accent)}.warn-line{color:var(--warn)!important;background:color-mix(in srgb,var(--warn) 10%,transparent);border-radius:8px;padding:8px 10px}.warn-line code{color:var(--text)!important}.danger-ghost{background:transparent;border:1px solid color-mix(in srgb,var(--bad) 40%,var(--border));color:var(--bad);font-size:12px;padding:5px 10px}.danger-ghost:hover{background:color-mix(in srgb,var(--bad) 12%,transparent);border-color:var(--bad)}button.danger{background:color-mix(in srgb,var(--bad) 20%,var(--panel-2));border-color:color-mix(in srgb,var(--bad) 45%,transparent);color:#ffd7d3}button.danger:hover{border-color:var(--bad)}.reset-list{margin:8px 0;padding-left:18px;font-size:13px;line-height:1.7}.reset-list code{color:var(--accent)}.reset-note{font-size:12.5px;color:var(--muted);background:var(--panel-2);border-radius:8px;padding:10px 12px}.reset-note code{color:var(--text)}.reset-check{display:flex;align-items:center;gap:8px;margin:12px 0 0;font-size:13px}.reset-check code{color:var(--accent)}.board-scroll{max-height:500px;overflow:auto}.board{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}.col{background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:8px;min-width:140px}.col-head{display:flex;justify-content:space-between;font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);padding:2px 4px 8px;position:sticky;top:0;background:var(--bg);z-index:1}.col-shipped{opacity:.85}.col-other{border-color:color-mix(in srgb,var(--warn) 40%,var(--border))}.spec-card{background:var(--panel-2);border:1px solid var(--border);border-radius:7px;padding:8px 9px;margin-bottom:7px}.spec-card.bad{border-color:color-mix(in srgb,var(--warn) 45%,transparent)}.spec-title{font-size:13px;font-weight:600}.spec-meta,.spec-branch{margin-top:3px}@media(max-width:640px){.board{grid-template-columns:repeat(2,1fr)}}.kanban-board{display:flex;gap:10px}.kanban-col{flex:0 0 210px;background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:8px}.kanban-col.empty{opacity:.5}.kanban-card{background:var(--panel-2);border:1px solid var(--border);border-radius:7px;padding:8px 9px;margin-bottom:7px}.kanban-card.done{opacity:.6}.kanban-card.done .kc-text{text-decoration:line-through}.kc-text{font-size:12.5px;line-height:1.4}.kc-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.kc-tag{font-size:10px;color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);border-radius:4px;padding:1px 5px}.kc-pr{font-size:10px;border-radius:4px;padding:1px 5px;text-decoration:none;border:1px solid var(--border);color:var(--muted)}a.kc-pr:hover{filter:brightness(1.25)}.kc-pr.state-open{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent);background:color-mix(in srgb,var(--ok) 12%,transparent)}.kc-pr.state-merged{color:var(--dev);border-color:color-mix(in srgb,var(--dev) 40%,transparent);background:color-mix(in srgb,var(--dev) 12%,transparent)}.kc-pr.state-closed{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 40%,transparent);background:color-mix(in srgb,var(--bad) 12%,transparent)}.kc-pr.draft{color:var(--muted);border-color:var(--border);background:var(--panel-2)}.kc-status{font-size:10px;margin-top:5px;color:var(--muted);text-transform:capitalize}.kc-status.state-open{color:var(--ok)}.kc-status.state-merged{color:var(--dev)}.kc-status.state-closed{color:var(--bad)}.metrics-list{display:flex;flex-direction:column;gap:12px}.metric-feature{background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:10px 12px}.mf-head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-bottom:8px}.mf-name{font-weight:600}.mf-badges{display:flex;gap:6px;flex-wrap:wrap}.phase-bars{display:flex;flex-direction:column;gap:4px}.phase-row{display:grid;grid-template-columns:56px 1fr 90px;align-items:center;gap:10px}.phase-label{color:var(--muted);font-size:12px}.phase-track{height:12px;border-radius:4px;background:var(--panel-2);overflow:hidden}.phase-fill{display:block;height:100%;min-width:2px;border-radius:4px;background:color-mix(in srgb,var(--accent) 65%,var(--panel-2))}.phase-value{text-align:right;white-space:nowrap}.surface-table{width:100%;border-collapse:collapse;margin-top:10px;font-size:12px}.surface-table th{text-align:left;color:var(--muted);font-weight:400;text-transform:uppercase;letter-spacing:.6px;font-size:10px;padding:4px 8px 6px 0;border-bottom:1px dashed var(--border)}.surface-table td{padding:5px 8px 5px 0;border-bottom:1px dashed var(--border)}.surface-table tr:last-child td{border-bottom:none}.st-key{font-weight:600}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;display:grid;place-items:center;z-index:50;padding:20px}.modal{background:var(--panel);border:1px solid var(--border);border-radius:12px;width:min(720px,100%);max-height:80vh;display:flex;flex-direction:column;padding:18px}.modal-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.modal-head h3{margin:0;font-size:15px}.modal-actions{display:flex;gap:8px;margin-top:14px}.cmd{background:#000;color:var(--accent);padding:10px 12px;border-radius:8px;font-size:13px;overflow-x:auto}.run-log{background:#000;color:#d6d9e0;padding:12px;border-radius:8px;font-size:12.5px;line-height:1.5;overflow:auto;white-space:pre-wrap;word-break:break-word;flex:1;min-height:200px;max-height:55vh;margin:0}.picker{max-height:74vh}.picker-path{background:var(--panel-2);border:1px solid var(--border);border-radius:7px;padding:7px 10px;margin-bottom:10px;word-break:break-all}.picker-list{flex:1;overflow:auto;border:1px solid var(--border);border-radius:8px;padding:6px;min-height:220px;max-height:48vh}.picker-row{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:transparent;border:none;border-radius:6px;padding:7px 9px;color:var(--text);font-size:13px}.picker-row:hover{background:var(--panel-2);border-color:transparent}.picker-row.up{color:var(--muted)}.picker-icon{color:var(--muted);width:14px}.picker-row .badge.small{font-size:10px;padding:0 6px;margin-left:auto}.picker-empty{padding:12px}.add-form .ghost[type=button]{white-space:nowrap}