diff --git a/.claude/commands/critic-round.md b/.claude/commands/critic-round.md index daf51ea..acb7df4 100644 --- a/.claude/commands/critic-round.md +++ b/.claude/commands/critic-round.md @@ -1,46 +1,35 @@ --- -description: Read-only multi-agent critic review of the current localhost app state — returns a severity-classified findings matrix, makes no code changes. Use when you want an assessment without implementing. +description: Run the multi-agent critic round as a deterministic Workflow — critics fan out as parallel subagents (own context each), each blocker/important finding is adversarially verified by a separate skeptic, then a synthesized severity matrix is returned. Read-only; changes no code. argument-hint: [focus, e.g. "checkout flow on localhost"] --- -Run a multi-agent critic review of the current local web app state. +Run a **genuine multi-agent critic round** — not a single agent simulating critics. +Use the **Workflow tool** so each critic runs as its own subagent in its own context +and every blocker/important finding is verified by a *separate* skeptic. Focus: $ARGUMENTS -Use these critics: - -1. First-Time User Critic -2. UX Flow Critic -3. Designer Critic -4. Artistic Direction Critic -5. Frontend Architecture Critic -6. QA / E2E Critic -7. Accessibility Critic -8. Performance Critic -9. Security Critic -10. Regression Critic - -Procedure: - -1. Inspect relevant code. -2. Start or reuse localhost. -3. Open the relevant page in Chrome or Playwright MCP. -4. Test the flow as a real user. -5. Check console errors. -6. Check failed network requests. -7. Ask each critic to independently review. -8. Return a critic matrix. - -Output format: - -| Critic | Severity | Finding | Evidence | Recommended action | -|---|---|---|---|---| - -Classify severity: - -- blocker: must fix before task is complete -- important: should fix in this task if related -- nice-to-have: record only, do not fix unless asked - -Do not edit files unless explicitly asked. +## Procedure +1. Make sure context exists for the critics: confirm `CLAUDE.md` and (if a UI flow + is in scope) that localhost is running for the browser-driven critics. +2. **Invoke the Workflow tool** with the predefined critic-panel workflow: + - `scriptPath: .claude/workflows/critic-panel.js` + - `args: { "focus": "$ARGUMENTS" }` + This command explicitly opts into multi-agent orchestration — calling Workflow + here is expected. +3. The workflow returns `{ focus, counts, confirmed, niceToHaves, refuted }`. + Render `confirmed` as the critic matrix and list `niceToHaves` separately: + + | Critic | Severity | Finding | Evidence | Recommended action | + |---|---|---|---|---| + +4. Note any `refuted` findings (a skeptic could not substantiate them) so they are + not actioned. Do **not** edit files — this is review only. + +## Notes +- This is the heavier, deterministic path (the workflow spawns ~10 critic agents + + one verifier per blocker/important finding). That is intended: real independent + review, not one context role-playing ten critics. +- Other commands (`/multi-agent-dev`, `/multi-agent-e2e`, `/qa-pass`) reuse this same + workflow for their critic rounds. diff --git a/.claude/commands/multi-agent-dev.md b/.claude/commands/multi-agent-dev.md index c215cc6..10e5b78 100644 --- a/.claude/commands/multi-agent-dev.md +++ b/.claude/commands/multi-agent-dev.md @@ -8,53 +8,37 @@ Run a full multi-agent development loop for this local web app. User task: $ARGUMENTS -You are the Lead Agent. - -You must use the following critic agents before implementation: - -1. First-Time User Critic -2. UX Flow Critic -3. Designer Critic -4. Artistic Direction Critic -5. Frontend Architecture Critic -6. QA / E2E Critic -7. Accessibility Critic -8. Performance Critic -9. Security Critic -10. Regression Critic +You are the Lead Agent. The critic rounds run as a **deterministic multi-agent +Workflow** (critics fan out as parallel subagents in their own contexts; each +blocker/important finding is verified by a separate skeptic) — **not** one agent +simulating critics. Invoking the Workflow tool here is expected (this command opts +into multi-agent orchestration). Procedure: -1. Read CLAUDE.md. -2. Read package.json. -3. Inspect the relevant routes, components, API handlers, tests, and styles. -4. Start or reuse the local dev server. -5. Open the relevant localhost page using Chrome or Playwright MCP. -6. Observe the current behavior. -7. Run the critic round before implementation. - -For each critic, produce: - -- severity: blocker / important / nice-to-have -- finding -- evidence -- recommended fix - -Then: - -8. Synthesize findings as Lead Agent. -9. Choose the smallest safe implementation plan. -10. Implement only blockers and important issues directly related to the task. -11. Run the smallest relevant automated checks first. -12. Verify in browser on localhost. -13. Check console errors. -14. Check failed network requests. -15. Add or update Playwright E2E tests for the verified flow. -16. Run relevant E2E tests. -17. Run typecheck, lint, unit tests, and build if appropriate. -18. Run a second critic round after implementation. -19. Fix remaining blockers. -20. Report final evidence. +1. Read CLAUDE.md and package.json. +2. Inspect the relevant routes, components, API handlers, tests, and styles. +3. Start or reuse the local dev server; open the relevant localhost page (Chrome/ + Playwright MCP) and observe current behavior. +4. **Pre-implementation critic round** — invoke the Workflow tool with + `scriptPath: .claude/workflows/critic-panel.js`, + `args: { "focus": "$ARGUMENTS (current behavior + changed files)" }`. + Render the returned `confirmed` findings as the critic matrix; record + `niceToHaves`; ignore `refuted`. +5. As Lead Agent, choose the smallest safe plan from the confirmed blockers + + directly-related important findings. +6. Implement only those. Keep the **maker/checker split**: implementation is your + pass; the post-implementation workflow below is the independent checker. +7. Run the smallest relevant automated checks first. +8. Verify in browser on localhost; check console errors and failed network requests. +9. Add or update Playwright E2E tests for the verified flow; run them. +10. Run typecheck, lint, unit tests, and build if appropriate. +11. **Post-implementation critic round** — invoke the same Workflow again with + `args.focus` set to the changed flow and `args.priorEvidence` set to what you + already gathered (git diff summary + test/lint/build results + browser/console + findings) so the critics verify against it instead of re-running everything. + Fix any remaining confirmed blockers (re-run the workflow until none remain). +12. Report final evidence. Never mark complete unless: diff --git a/.claude/commands/multi-agent-e2e.md b/.claude/commands/multi-agent-e2e.md index 304f2c2..01d63e9 100644 --- a/.claude/commands/multi-agent-e2e.md +++ b/.claude/commands/multi-agent-e2e.md @@ -3,40 +3,27 @@ description: Design and run Playwright E2E tests for a user flow via a critic wo argument-hint: [target flow, e.g. "onboarding"] --- -Design and run E2E tests using a multi-agent critic workflow. +Design and run E2E tests using a **deterministic multi-agent Workflow** (the critic +panel runs as parallel subagents in their own contexts) — not one agent simulating +critics. Target flow: $ARGUMENTS -Roles: - -1. QA / E2E Critic - - Defines critical paths and edge cases. - -2. First-Time User Critic - - Defines what a new user would try first. - -3. UX Flow Critic - - Defines success, failure, loading, and recovery states. - -4. Accessibility Critic - - Defines keyboard and screen-reader relevant checks. - -5. Regression Critic - - Identifies existing flows that could break. - Procedure: -1. Inspect existing Playwright tests. -2. Inspect app routes and components. -3. Start or reuse localhost. -4. Manually verify the target flow in browser. -5. Draft E2E test cases from critic findings. -6. Implement Playwright tests. -7. Run the specific E2E tests. -8. Fix test or app failures, but do not weaken assertions. -9. Run browser verification again. -10. Report evidence. +1. Inspect existing Playwright tests, app routes, and components. +2. Start or reuse localhost; manually verify the target flow in browser. +3. **Enumerate test cases via a Workflow** — invoke the Workflow tool with + `scriptPath: .claude/workflows/e2e-design.js`, `args: { "flow": "$ARGUMENTS" }`. + One agent per path category (happy, failure, edge, accessibility, regression) + enumerates concrete cases in its own context; results are deduped. (Invoking + Workflow here is expected.) It returns `{ flow, count, cases }`. +4. Implement Playwright tests from the returned `cases` (cover the happy path + at + least one failure/edge path); run the specific specs. +5. Fix test or app failures, but do not weaken assertions. +6. Run browser verification again. +7. Report evidence. Required output: diff --git a/.claude/commands/qa-pass.md b/.claude/commands/qa-pass.md index b9f0135..731022c 100644 --- a/.claude/commands/qa-pass.md +++ b/.claude/commands/qa-pass.md @@ -24,13 +24,13 @@ Checklist: - buttons have accessible names - forms have labels - keyboard navigation works for critical flow -12. Run a post-implementation critic round using: - - First-Time User Critic - - UX Flow Critic - - Designer Critic - - QA / E2E Critic - - Accessibility Critic - - Regression Critic -13. Report blockers, non-blockers, and recommended next actions. +12. Run a **post-implementation critic round as a Workflow** — invoke the Workflow + tool with `scriptPath: .claude/workflows/critic-panel.js` and + `args: { "focus": "the changed flows for $ARGUMENTS", "priorEvidence": "" }`. + The critics run as parallel subagents and verify against that evidence; each + blocker/important finding is checked by a separate skeptic (no single agent + simulating the panel). +13. Report the workflow's `confirmed` blockers/non-blockers and recommended next + actions. Never mark as complete if build, tests, or critical browser flow fails. diff --git a/.claude/commands/scan-project-docs.md b/.claude/commands/scan-project-docs.md index 8df1056..3d27bbd 100644 --- a/.claude/commands/scan-project-docs.md +++ b/.claude/commands/scan-project-docs.md @@ -38,8 +38,15 @@ Procedure: pile stories into a single file. 6. Split large stories into independently testable stories. 7. Add measurable acceptance criteria. -8. Search the codebase for implementation evidence. -9. Search the test suite for unit, integration, and E2E evidence. +8. **Gather evidence in parallel via a Workflow** — once you have the story list, + invoke the Workflow tool with `scriptPath: .claude/workflows/scan-docs.js` and + `args: { "stories": [ {id, title, source, acceptanceCriteria}, ... ] }`. It + pipelines each story through an evidence-gathering agent (searches code + tests) + and a *separate* status-verifier agent (evidence-based status, no overclaiming), + returning one record per story. This is real multi-agent orchestration, not one + agent judging every story in a single context. (Invoking Workflow here is expected.) +9. Use the returned records as the source of truth for status; do browser + verification yourself where it raises a status to `Browser verified`/`Done`. 10. Start or reuse localhost when browser verification is possible. 11. Open relevant pages using Chrome or Playwright MCP. 12. Verify implemented user flows in browser when possible. diff --git a/.claude/commands/story-gap-analysis.md b/.claude/commands/story-gap-analysis.md index 02ea837..31ec7b5 100644 --- a/.claude/commands/story-gap-analysis.md +++ b/.claude/commands/story-gap-analysis.md @@ -13,27 +13,21 @@ the topic pages relevant to the gaps you're assessing (e.g. `topics/testing.md`, `topics/build-and-verify.md`) so "tested / not tested" and "edge cases" judgments reflect the project's real conventions. -Produce: - -1. Requirements found in docs but not implemented. -2. Implemented features not described in docs. -3. Stories implemented but not unit-tested. -4. Stories implemented but not E2E-tested. -5. Stories E2E-tested but missing important edge cases. -6. Stories that work functionally but fail UX/design/artistic direction review. -7. Ambiguous or contradictory requirements. -8. Recommended next implementation order. - -Use critic agents: - -- Product Requirements Critic -- Code Evidence Critic -- Unit Test Evidence Critic -- E2E Test Evidence Critic -- UX / First-Time User Critic -- Designer Critic -- Artistic Direction Critic -- Regression Critic -- Documentation Consistency Critic - -Do not edit files unless explicitly asked. +**Run the analysis as a deterministic multi-agent Workflow** — invoke the Workflow +tool with `scriptPath: .claude/workflows/gap-analysis.js`, +`args: { "scope": "$ARGUMENTS" }`. One agent analyzes each gap dimension in its own +context, in parallel; a final agent synthesizes the recommended order. (Invoking +Workflow here is expected.) + +The workflow returns `{ scope, totalGaps, byDimension, recommendedOrder }` covering: + +1. Requirements found in docs but not implemented (`docs-not-impl`). +2. Implemented features not described in docs (`impl-not-docs`). +3. Stories implemented but not unit-tested (`impl-not-unit`). +4. Stories implemented but not E2E-tested (`impl-not-e2e`). +5. Stories E2E-tested but missing important edge cases (`e2e-missing-edges`). +6. Stories that work but fail UX/design/artistic-direction review (`ux-design`). +7. Ambiguous or contradictory requirements (`ambiguous`). +8. Recommended next implementation order (`recommendedOrder`). + +Render the dimensions + the recommended order. Do not edit files unless explicitly asked. diff --git a/.claude/loop.md b/.claude/loop.md index 0db50e3..51cf619 100644 --- a/.claude/loop.md +++ b/.claude/loop.md @@ -22,16 +22,22 @@ budget, denylist, escalation, stop conditions). Before running, fix the loop's 4. Start or reuse the dev server. 5. Open the relevant localhost page in Chrome or Playwright MCP. 6. Check browser console errors and failed network requests. -7. Run a critic round (First-Time User, UX Flow, Designer, Artistic Direction, - Frontend Architecture, QA / E2E, Accessibility, Performance, Security, - Regression) and produce a critic matrix. -8. **Maker/checker:** the critic that verifies a fix must be a *separate* pass from - the one that made it — the implementer never marks its own work "done". +7. Run the critic round as the **critic-panel Workflow** + (`.claude/workflows/critic-panel.js`, `args.focus` = the current task/flow): + critics fan out as parallel subagents and findings are adversarially verified. + Render the returned `confirmed` matrix. +8. **Maker/checker:** the pass that verifies a fix must be *separate* from the one + that made it — the implementer never marks its own work "done". 9. Act **only within the trust level**: - L1: record findings only. - - L2/L3: fix blockers directly related to the current task; fix important issues - only if low-risk and in scope; re-run the smallest relevant test; re-check - browser. Never fix nice-to-haves automatically; never start unrelated refactors. + - L2/L3: fix the confirmed blockers + directly-related important findings via the + **loop-iteration Workflow** (`.claude/workflows/loop-iteration.js`, + `args.items` = the triaged, denylist-cleared items): an implementer subagent + fixes each in an isolated worktree and a *separate* verifier reviews the real + diff and approves/rejects (a hard denylist gate also forces escalation). For + each **approved** item, apply its returned `diff` patch and re-run the smallest + relevant check before committing; escalate `rejected`/`escalate` items. Never + fix nice-to-haves automatically; never start unrelated refactors. 10. **Append a run entry** to `.claude/memory/loop-run-log.md` (see format below). ## Denylist — never touch autonomously (escalate instead) diff --git a/.claude/memory/log.md b/.claude/memory/log.md index 571e30e..740f361 100644 --- a/.claude/memory/log.md +++ b/.claude/memory/log.md @@ -76,3 +76,40 @@ change), `rotate` (archived old entries). Format: `## [YYYY-MM-DD] | 500-line rotation rule). Added the maker/checker no-self-approve rule and `Agent isolation: worktree` note to CLAUDE.md. Filed the principle in topics/workflow.md. + +## [2026-06-29] migrate | Critic rounds → deterministic multi-agent Workflows +- Replaced "simulate critics" (one agent role-playing the panel) with real + orchestration: added `.claude/workflows/critic-panel.js` (parallel critics → + adversarial verify → matrix) and `.claude/workflows/scan-docs.js` (per-story + parallel evidence → separate status verifier). Rewired `/critic-round`, + `/multi-agent-dev`, `/multi-agent-e2e`, `/qa-pass`, `/scan-project-docs` to invoke + them via the Workflow tool. Named the workflow `critic-panel` (not `critic-round`) + to avoid colliding with the command in the skills list. + +## [2026-06-29] ingest | More orchestration workflows + review fixes +- Added `gap-analysis.js` (→ `/story-gap-analysis`), `e2e-design.js` (→ + `/multi-agent-e2e`), and `loop-iteration.js` (maker/checker for `/loop` L2/L3). + Wired the commands + `.claude/loop.md`; updated README, CLAUDE.md, skills-index, + adoption-guide (new "Multi-agent orchestration (Workflows)" section). +- From PR #10 review: verifier agents now fail safe (null verdict ⇒ not confirmed), + parallel agents get unique labels (map index), schema-required fields get fallback + strings, and `scan-docs` statuses are capped at "E2E tested" (browser pass is the + caller's job). Filed the workflow-authoring gotchas in topics/workflow.md. + +## [2026-06-29] lint | Ran critic-panel-style Workflow on the workflow scripts +- Dogfooded a real multi-agent review (18 agents) over `.claude/workflows/*`: 1 + blocker + 10 important confirmed, 0 refuted. Fixes applied: + - scan-docs: guard the first-stage `evidence` result (blocker — unguarded deref + crashed the pipeline); fail CLOSED on a missing verdict (cap the maker's + self-proposed status) instead of accepting it. + - critic-panel: `.filter(Boolean)` the verified array; MERGE duplicate findings + (keep corroborating critics) instead of dropping; optional `args.uiInScope` + (skip the 5 UI critics for non-UI changes) and `args.priorEvidence` (verify + against already-gathered evidence, not a re-run). + - loop-iteration: implementer returns the REAL `git diff`; verifier reviews the + actual diff (not a self-report) and the caller applies the returned patch; added + a hard programmatic DENYLIST gate on changedFiles (defense-in-depth). + - e2e-design: dedup key includes category; title coalesced before `.trim()`. +- Lesson: a maker/checker that hands the checker only the maker's self-report is + weak — give the checker the real artifact (diff). Exact-title dedup also let one + bug appear as two findings; merge, don't drop. diff --git a/.claude/memory/topics/workflow.md b/.claude/memory/topics/workflow.md index c568494..19b191f 100644 --- a/.claude/memory/topics/workflow.md +++ b/.claude/memory/topics/workflow.md @@ -19,6 +19,27 @@ routing); the grouped "when to use" catalog is `.claude/skills-index.md`. A catalog file must NOT live in `.claude/commands/` — anything there auto-registers as its own `/command`. +- **Fan-out commands run as deterministic multi-agent Workflows** under + `.claude/workflows/`, not one agent simulating critics: `critic-panel` (critics → + adversarial verify → matrix), `scan-docs` (per-story evidence → status verifier), + `gap-analysis` (per-dimension gaps → synth order), `e2e-design` (per-category + cases → dedup), `loop-iteration` (worktree implementer → separate verifier). + Commands invoke them via the Workflow tool (the sanctioned opt-in). Name a workflow + distinctly from any command (e.g. `critic-panel` vs the `/critic-round` command) to + avoid a duplicate skills-list entry. Don't force non-fan-out commands + (`/fix-localhost`, `/write-goal`, `/dream`, `/memory-audit`) into workflows. +- Workflow scripts use a DSL (`agent()/parallel()/pipeline()/phase()`, top-level + `await`/`return`); validate syntax by wrapping the body in `async function(){…}` + before `node --check` (bare `node --check` reports a false "illegal return"). + Make verifier agents **fail safe** (a null/failed verdict must NOT confirm), give + parallel agents **unique labels** that **always include the map index** (so + duplicate caller-supplied ids can't collide — `${id || 'x'}-${i}`, not `${id||i}`), + and provide **fallback strings** for schema-required fields. Also guard the + *first* pipeline stage's result before deref (`const ev = stage1 || {}`), fail + **closed** (don't accept a maker's self-proposed value when the checker is + missing), and give a maker/checker checker the **real artifact** (the diff), not + the maker's self-report. The DSL auto-caps concurrency (~min(16, cores-2)), so a + large fan-out won't spawn unbounded agents — no manual limit needed. - A command's `description` also lets Claude **auto-invoke** it. Add `disable-model-invocation: true` to heavy/code-changing commands that should be manual-only. diff --git a/.claude/skills-index.md b/.claude/skills-index.md index f2a3691..ea685e8 100644 --- a/.claude/skills-index.md +++ b/.claude/skills-index.md @@ -27,6 +27,13 @@ catalog so both you and Claude can route to the right command. `.claude/memory/loop-run-log.md`) ## Development loop + +> Fan-out commands run as real multi-agent **Workflows** under `.claude/workflows/` +> (parallel subagents + adversarial/maker-checker verification), invoked via the +> Workflow tool — not one agent simulating critics: `critic-panel` (critic rounds), +> `scan-docs` (`/scan-project-docs`), `gap-analysis` (`/story-gap-analysis`), +> `e2e-design` (`/multi-agent-e2e`), `loop-iteration` (`/loop` L2/L3). + | Command | When to use | Args | |---|---|---| | `/write-goal` | Start of a non-trivial task: turn a described outcome into a production-grade `/goal` (or `/loop`) prompt to review and run | `[outcome]` (say "loop" for a /loop) | diff --git a/.claude/workflows/critic-panel.js b/.claude/workflows/critic-panel.js new file mode 100644 index 0000000..522ea73 --- /dev/null +++ b/.claude/workflows/critic-panel.js @@ -0,0 +1,137 @@ +export const meta = { + name: 'critic-panel', + description: + 'Deterministic multi-agent critic round: fan out the critics as parallel subagents (each in its own context), adversarially verify every blocker/important finding with a separate skeptic, then return a synthesized severity matrix.', + phases: [ + { title: 'Review', detail: 'each critic reviews independently, in its own context' }, + { title: 'Verify', detail: 'a separate skeptic tries to refute each blocker/important finding' }, + ], +} + +// What to review (focus) + how deep, passed via the Workflow `args`. +const focus = + (args && (args.focus || (typeof args === 'string' ? args : null))) || + 'the current app/flow and the changed files' +// Optional: evidence the Lead already gathered (test/lint/build output, browser +// findings, git diff). Critics verify against it instead of re-deriving from scratch. +const priorEvidence = (args && args.priorEvidence) || null +// Optional: skip the 5 UI/browser critics for non-UI (backend/docs/config) changes. +// Defaults to including them (safe). Callers pass `uiInScope: false` to save fan-out. +const uiInScope = !(args && args.uiInScope === false) + +// The standard critic roster. `ui: true` marks browser-facing critics. +const ALL_CRITICS = [ + { key: 'first-time-user', ui: true, label: 'First-Time User Critic', lens: 'Can a new user understand the first screen and complete the flow without help? Labels, errors, hidden assumptions.' }, + { key: 'ux-flow', ui: true, label: 'UX Flow Critic', lens: 'Beginning/middle/end of the journey, feedback on every action, confirm destructive actions, loading/success/recovery states.' }, + { key: 'designer', ui: true, label: 'Designer Critic', lens: 'Layout hierarchy, spacing, alignment, typography, color, button/empty/loading/error states, mobile responsiveness.' }, + { key: 'artistic-direction', ui: true, label: 'Artistic Direction Critic', lens: 'Mood, visual identity, beauty, coherence — is it intentional and memorable or generic?' }, + { key: 'frontend-arch', ui: false, label: 'Frontend Architecture Critic', lens: 'Component boundaries, state, hooks, server/client split, TypeScript correctness, SOLID/DRY/KISS, no duplicated logic.' }, + { key: 'qa-e2e', ui: false, label: 'QA / E2E Critic', lens: 'Critical paths, missing Playwright tests, meaningful vs superficial assertions, edge cases, did we actually verify localhost?' }, + { key: 'accessibility', ui: true, label: 'Accessibility Critic', lens: 'Keyboard nav, focus states, accessible names, labels, form-error linkage, contrast, semantic HTML, dialog a11y.' }, + { key: 'performance', ui: false, label: 'Performance Critic', lens: 'Unnecessary re-renders, large client bundles, heavy images, blocking fetches, overuse of client components.' }, + { key: 'security', ui: false, label: 'Security Critic', lens: 'Unsafe input handling, secrets exposed to the client, auth-bypass risk, missing server-side validation, unsafe redirects, XSS, insecure storage.' }, + { key: 'regression', ui: false, label: 'Regression Critic', lens: 'Existing routes/flows affected, shared components/APIs changed, snapshots, styling side effects, backward compatibility.' }, +] +const CRITICS = ALL_CRITICS.filter((c) => uiInScope || !c.ui) + +const FINDINGS_SCHEMA = { + type: 'object', + additionalProperties: false, + required: ['findings'], + properties: { + findings: { + type: 'array', + items: { + type: 'object', + additionalProperties: false, + required: ['severity', 'title', 'evidence', 'recommendation'], + properties: { + severity: { type: 'string', enum: ['blocker', 'important', 'nice-to-have'] }, + title: { type: 'string' }, + evidence: { type: 'string', description: 'Concrete observation: file:line, command output, or browser observation.' }, + recommendation: { type: 'string' }, + }, + }, + }, + }, +} + +const VERDICT_SCHEMA = { + type: 'object', + additionalProperties: false, + required: ['real', 'reason'], + properties: { + real: { type: 'boolean', description: 'true if the finding is genuine after attempting to refute it' }, + reason: { type: 'string' }, + }, +} + +// Phase 1 — every critic reviews independently and in parallel (a barrier: we want +// the full set before deduping and verifying). +phase('Review') +const reviews = await parallel( + CRITICS.map((c) => () => + agent( + `You are the **${c.label}** for this project. Read CLAUDE.md and the relevant memory topic page(s), inspect ${focus} (read the code; if a localhost flow is in scope, drive it via Playwright/Chrome MCP and check console + network), then review strictly through your lens:\n${c.lens}\n${priorEvidence ? `\nEvidence already gathered by the Lead — verify against it instead of re-deriving from scratch:\n${typeof priorEvidence === 'string' ? priorEvidence : JSON.stringify(priorEvidence)}\n` : ''}\nReturn only findings you can back with concrete evidence. An empty list is a valid, honest answer.`, + { label: `critic:${c.key}`, phase: 'Review', schema: FINDINGS_SCHEMA }, + ).then((r) => ({ critic: c.key, label: c.label, findings: (r && r.findings) || [] })), + ), +) + +// Flatten, then dedup on (severity + exact normalized title). On a collision, MERGE +// (keep the corroborating critic + its evidence) rather than dropping it — two +// critics agreeing is a stronger signal, not a duplicate to discard. +const all = reviews + .filter(Boolean) + .flatMap((r) => r.findings.map((f) => ({ ...f, critic: r.critic, criticLabel: r.label }))) +const byKey = new Map() +for (const f of all) { + const k = `${f.severity}::${String(f.title || '').trim().toLowerCase()}` + const existing = byKey.get(k) + if (existing) { + existing.alsoFlaggedBy = existing.alsoFlaggedBy || [] + existing.alsoFlaggedBy.push({ critic: f.critic, evidence: f.evidence }) + } else { + byKey.set(k, { ...f }) + } +} +const deduped = [...byKey.values()] +log(`${deduped.length} findings from ${CRITICS.length} critics (${all.length} before merge-dedup)`) + +// Phase 2 — adversarially verify each blocker/important finding with a SEPARATE +// skeptic agent (the reviewer never confirms its own finding). Nice-to-haves are +// recorded as-is, not verified. +phase('Verify') +const toVerify = deduped.filter((f) => f.severity === 'blocker' || f.severity === 'important') +const verified = await parallel( + toVerify.map((f, i) => () => + agent( + `Adversarially verify this ${f.severity} finding from the ${f.criticLabel}. Try to REFUTE it by inspecting the actual code/flow — default to real:false if the evidence does not hold up.\n\nTitle: ${f.title}\nEvidence: ${f.evidence}\nRecommendation: ${f.recommendation}`, + { label: `verify:${f.critic}:${i}`, phase: 'Verify', schema: VERDICT_SCHEMA }, + ).then((v) => ({ + ...f, + // Fail safe: a missing/failed verdict must NOT confirm the finding. + real: v ? v.real : false, + verifyReason: (v && v.reason) || 'verifier returned no verdict — not confirmed', + })), + ), +) + +const niceToHaves = deduped.filter((f) => f.severity === 'nice-to-have') +const settled = verified.filter(Boolean) +const confirmed = settled.filter((f) => f.real !== false) +const refuted = settled.filter((f) => f.real === false) + +return { + focus, + counts: { + critics: CRITICS.length, + confirmedBlockers: confirmed.filter((f) => f.severity === 'blocker').length, + confirmedImportant: confirmed.filter((f) => f.severity === 'important').length, + niceToHave: niceToHaves.length, + refuted: refuted.length, + }, + confirmed, // verified blocker/important findings — the actionable matrix + niceToHaves, // recorded only + refuted, // findings a skeptic could not substantiate +} diff --git a/.claude/workflows/e2e-design.js b/.claude/workflows/e2e-design.js new file mode 100644 index 0000000..8d8f539 --- /dev/null +++ b/.claude/workflows/e2e-design.js @@ -0,0 +1,57 @@ +export const meta = { + name: 'e2e-design', + description: + 'Deterministic E2E test design: fan out one agent per path category (happy, failure, edge, accessibility, regression) to enumerate concrete Playwright test cases for a flow, then dedup. The caller implements the cases.', + phases: [{ title: 'Design', detail: 'one agent per path category enumerates test cases in parallel' }], +} + +const flow = (args && (args.flow || (typeof args === 'string' ? args : null))) || 'the target flow' + +const CATEGORIES = [ + { key: 'happy', prompt: 'the happy path(s) a user follows when everything works' }, + { key: 'failure', prompt: 'validation/failure paths and error states' }, + { key: 'edge', prompt: 'boundary/edge cases (limits, empty input, special characters, reload/persistence)' }, + { key: 'accessibility', prompt: 'keyboard navigation and accessible-name/role assertions' }, + { key: 'regression', prompt: 'existing flows this change could break' }, +] + +const CASES_SCHEMA = { + type: 'object', additionalProperties: false, required: ['cases'], + properties: { + cases: { + type: 'array', + items: { + type: 'object', additionalProperties: false, required: ['title', 'steps', 'expected'], + properties: { + title: { type: 'string' }, + steps: { type: 'string' }, + expected: { type: 'string', description: 'visible outcome to assert — not just page load' }, + }, + }, + }, + }, +} + +phase('Design') +const results = await parallel( + CATEGORIES.map((c) => () => + agent( + `Enumerate concrete Playwright E2E test cases for "${flow}" covering ${c.prompt}. Inspect the routes/components and existing specs first. Each case needs: title, steps, and the visible expected outcome to assert.`, + { label: `e2e:${c.key}`, phase: 'Design', schema: CASES_SCHEMA }, + ).then((r) => ((r && r.cases) || []).map((x) => ({ ...x, category: c.key }))), + ), +) + +const all = results.filter(Boolean).flat() +const seen = new Set() +const cases = all.filter((c) => { + // Key on category + title so a failure-path case isn't dropped for sharing a + // title with a happy-path case. + const k = `${c.category}::${String(c.title || '').trim().toLowerCase()}` + if (seen.has(k)) return false + seen.add(k) + return true +}) +log(`${cases.length} test cases (${all.length} before dedup) for "${flow}"`) + +return { flow, count: cases.length, cases } diff --git a/.claude/workflows/gap-analysis.js b/.claude/workflows/gap-analysis.js new file mode 100644 index 0000000..fe8ff8f --- /dev/null +++ b/.claude/workflows/gap-analysis.js @@ -0,0 +1,77 @@ +export const meta = { + name: 'gap-analysis', + description: + 'Deterministic gap analysis: fan out one agent per gap dimension (docs↔code↔tests, edge cases, UX/design, ambiguity) in parallel, then synthesize a recommended next-implementation order. Read-only.', + phases: [ + { title: 'Dimensions', detail: 'one agent per gap dimension, in parallel' }, + { title: 'Synthesize', detail: 'merge into a prioritized next-implementation order' }, + ], +} + +const scope = (args && (args.scope || (typeof args === 'string' ? args : null))) || 'the whole project' + +const DIMENSIONS = [ + { key: 'docs-not-impl', prompt: 'Requirements in docs (docs/prd.md, docs/stories/*, README) with NO implementation evidence in the codebase.' }, + { key: 'impl-not-docs', prompt: 'Implemented features/behaviors NOT described in any product doc.' }, + { key: 'impl-not-unit', prompt: 'Stories/behaviors implemented but lacking meaningful unit tests.' }, + { key: 'impl-not-e2e', prompt: 'Stories/flows implemented but lacking Playwright E2E coverage.' }, + { key: 'e2e-missing-edges', prompt: 'Flows with E2E coverage that miss important failure/edge cases.' }, + { key: 'ux-design', prompt: 'Flows that work functionally but would fail a UX / Designer / Artistic Direction review.' }, + { key: 'ambiguous', prompt: 'Ambiguous or contradictory requirements across the docs.' }, +] + +const GAP_SCHEMA = { + type: 'object', additionalProperties: false, required: ['gaps'], + properties: { + gaps: { + type: 'array', + items: { + type: 'object', additionalProperties: false, required: ['item', 'detail', 'priority'], + properties: { + item: { type: 'string' }, + detail: { type: 'string', description: 'concrete evidence: file/story/test reference' }, + priority: { type: 'string', enum: ['high', 'medium', 'low'] }, + }, + }, + }, + }, +} + +const ORDER_SCHEMA = { + type: 'object', additionalProperties: false, required: ['order'], + properties: { + order: { + type: 'array', + items: { + type: 'object', additionalProperties: false, required: ['next', 'why'], + properties: { next: { type: 'string' }, why: { type: 'string' } }, + }, + }, + }, +} + +phase('Dimensions') +const results = await parallel( + DIMENSIONS.map((d) => () => + agent( + `Analyze the gap for "${scope}" along this dimension by reading docs/, src/, e2e/, and the story map:\n${d.prompt}\nReturn only gaps backed by concrete references; an empty list is a valid, honest answer.`, + { label: `gap:${d.key}`, phase: 'Dimensions', schema: GAP_SCHEMA }, + ).then((r) => ({ dimension: d.key, gaps: (r && r.gaps) || [] })), + ), +) + +const byDimension = {} +let total = 0 +for (const r of results.filter(Boolean)) { + byDimension[r.dimension] = r.gaps + total += r.gaps.length +} +log(`${total} gaps across ${DIMENSIONS.length} dimensions`) + +phase('Synthesize') +const synth = await agent( + `Given these gaps grouped by dimension, propose the recommended NEXT-implementation order (highest-leverage first), each with a one-line "why". Be concrete.\n\n${JSON.stringify(byDimension)}`, + { label: 'synthesize', phase: 'Synthesize', schema: ORDER_SCHEMA }, +) + +return { scope, totalGaps: total, byDimension, recommendedOrder: (synth && synth.order) || [] } diff --git a/.claude/workflows/loop-iteration.js b/.claude/workflows/loop-iteration.js new file mode 100644 index 0000000..02dddd0 --- /dev/null +++ b/.claude/workflows/loop-iteration.js @@ -0,0 +1,86 @@ +export const meta = { + name: 'loop-iteration', + description: + 'Maker/checker for an L2/L3 loop iteration: for each already-triaged, denylist-cleared work item, an implementer subagent (isolated git worktree) applies the smallest safe fix and runs the relevant checks there; a SEPARATE verifier subagent adversarially reviews the reported change and approves or rejects. Returns per-item outcomes; the caller applies approved changes and escalates the rest.', + phases: [ + { title: 'Implement', detail: 'implementer per item, in an isolated worktree' }, + { title: 'Verify', detail: 'a separate verifier reviews the change and approves/rejects' }, + ], +} + +// args.items: [{ id, description }] — already triaged and denylist-cleared by the loop. +const items = (args && args.items) || [] +const level = (args && args.level) || 'L2' +if (!items.length) { + return { error: 'No work items. Triage + denylist-check first, then pass args.items = [{id, description}].', applied: [], rejected: [] } +} + +const IMPL_SCHEMA = { + type: 'object', additionalProperties: false, required: ['changedFiles', 'diff', 'checksPassed', 'notes'], + properties: { + changedFiles: { type: 'array', items: { type: 'string' } }, + // The ACTUAL unified diff (from `git diff` in the worktree) — the durable + // artifact the verifier reviews and the caller applies. Not a prose summary. + diff: { type: 'string', description: 'the real unified diff / patch text (git diff output)' }, + checksPassed: { type: 'boolean', description: 'did the smallest relevant checks pass in the worktree' }, + notes: { type: 'string' }, + }, +} + +const VERDICT_SCHEMA = { + type: 'object', additionalProperties: false, required: ['pass', 'reason'], + properties: { + pass: { type: 'boolean' }, + reason: { type: 'string' }, + }, +} + +// Defense-in-depth: a hard, deterministic denylist gate on the actual changed +// files — independent of the verifier's judgment. Any match forces escalation. +const DENYLIST = [ + /(^|\/)\.env(\.|$)/i, /(^|\/)auth(\/|\.|$)/i, /payment/i, /secret/i, + /(^|\/)migrations?(\/|$)/i, /\.github\/workflows\//i, /(^|\/)(infra|deploy)(\/|$)/i, +] +const hitsDenylist = (files) => (files || []).some((f) => DENYLIST.some((re) => re.test(f))) + +phase('Implement') +// Pipeline: each item flows implement → verify independently. +const outcomes = await pipeline( + items, + (item, i) => + agent( + `Implement the SMALLEST safe fix for this loop work item in your isolated worktree and run the smallest relevant checks (tests/typecheck/lint). Stay in scope; NEVER touch denylist paths (auth, payments, secrets/.env, infra, CI config, migrations). Then capture the ACTUAL change with \`git diff\` and return it as \`diff\` (the real patch, not a summary), the changed files, and whether the checks passed.\n\n${JSON.stringify(item)}`, + { label: `impl:${item.id || 'item'}-${i}`, phase: 'Implement', schema: IMPL_SCHEMA, isolation: 'worktree' }, + ), + (impl, item, i) => { + const im = impl || {} + // Hard denylist gate — independent of the verifier. + if (hitsDenylist(im.changedFiles)) { + return { + id: item.id, applied: false, status: 'escalated-denylist', + changedFiles: im.changedFiles || [], diff: im.diff || '', checksPassed: !!im.checksPassed, + verdict: 'changed files match the denylist — forced escalation regardless of verdict', + } + } + return agent( + `You are a SEPARATE verifier (not the implementer). Adversarially review the ACTUAL diff below for correctness, scope creep, and denylist violations, and judge whether the change is safe to apply. Default to pass:false if you cannot confirm it from the diff.\n\nItem: ${JSON.stringify(item)}\nChanged files: ${JSON.stringify(im.changedFiles || [])}\nchecksPassed (self-reported): ${!!im.checksPassed}\nDiff:\n${im.diff || '(no diff returned)'}`, + { label: `verify:${item.id || 'item'}-${i}`, phase: 'Verify', schema: VERDICT_SCHEMA }, + ).then((v) => ({ + id: item.id, + // Fail safe: no verdict / verifier died → not approved. + applied: !!(v && v.pass), + status: v && v.pass ? 'applied' : 'rejected', + changedFiles: im.changedFiles || [], + diff: im.diff || '', // the patch the caller applies for approved items + checksPassed: !!im.checksPassed, + verdict: (v && v.reason) || 'verifier returned no verdict — rejected', + })) + }, +) + +const settled = outcomes.filter(Boolean) +const applied = settled.filter((o) => o.applied) +const rejected = settled.filter((o) => !o.applied) +// Approved items carry their `diff` — the caller applies the patch (worktrees are +// ephemeral) and re-runs the smallest relevant checks before committing. +return { level, applied, rejected, escalate: rejected.map((o) => o.id) } diff --git a/.claude/workflows/scan-docs.js b/.claude/workflows/scan-docs.js new file mode 100644 index 0000000..529576f --- /dev/null +++ b/.claude/workflows/scan-docs.js @@ -0,0 +1,87 @@ +export const meta = { + name: 'scan-docs', + description: + 'Deterministic doc-scanner: for each user story (passed in), pipeline an evidence-gathering agent then a separate status-verifier agent. Returns evidence-based per-story status records; the caller writes the docs/stories files.', + phases: [ + { title: 'Evidence', detail: 'gather implementation + test evidence per story' }, + { title: 'Verify', detail: 'a separate agent confirms an evidence-based status (no overclaiming)' }, + ], +} + +// args.stories: [{ id, title, source, acceptanceCriteria? }] +// The caller extracts the story list from the PRD/docs first, then runs this. +const stories = (args && args.stories) || [] +if (!stories.length) { + return { error: 'No stories provided. Extract stories from docs first, then pass args.stories = [{id,title,source}].', records: [] } +} + +// This workflow only inspects the repo (code + tests). Statuses that require a +// human/MCP browser pass — "Browser verified" and "Done" — are intentionally OUT of +// scope: the caller raises a story to those after verifying it in a browser. +const REPO_STATUSES = [ + 'Not started', 'Partially implemented', 'Implemented', + 'Unit tested', 'E2E tested', 'Blocked', 'Deprecated', +] + +const EVIDENCE_SCHEMA = { + type: 'object', + additionalProperties: false, + required: ['implementationFiles', 'unitTests', 'e2eTests', 'proposedStatus', 'notes'], + properties: { + implementationFiles: { type: 'array', items: { type: 'string' }, description: 'files that implement this story (empty if none found)' }, + unitTests: { type: 'string', enum: ['Missing', 'Partial', 'Present'] }, + e2eTests: { type: 'string', enum: ['Missing', 'Partial', 'Present'] }, + proposedStatus: { type: 'string', enum: REPO_STATUSES }, + notes: { type: 'string' }, + }, +} + +const VERDICT_SCHEMA = { + type: 'object', + additionalProperties: false, + required: ['finalStatus', 'justification'], + properties: { + finalStatus: { type: 'string', enum: REPO_STATUSES }, + justification: { type: 'string', description: 'why the evidence supports this status; downgrade if the proposed status overclaims' }, + }, +} + +// On a missing verifier verdict, fail CLOSED: cap the maker's self-proposed status +// at "Implemented" (strip unverified test claims) rather than accepting it as-is. +const UNVERIFIED_CAP = ['Not started', 'Partially implemented', 'Implemented', 'Blocked', 'Deprecated'] +function conservativeStatus(proposed) { + return UNVERIFIED_CAP.includes(proposed) ? proposed : 'Implemented' +} + +phase('Evidence') +// Pipeline: each story flows through evidence → verify independently (no barrier). +const records = await pipeline( + stories, + (story, i) => + agent( + `Gather IMPLEMENTATION and TEST evidence for this user story by searching the codebase and tests (do not write any files):\n${JSON.stringify(story)}\n\nReport which files implement it, whether unit and E2E tests cover it, and the most defensible status. "Not started" is correct when no implementation is found.`, + { label: `evidence:${story.id || 'story'}-${i}`, phase: 'Evidence', schema: EVIDENCE_SCHEMA }, + ), + (evidence, story, i) => { + // Guard the first-stage result like every sibling workflow does — a null/failed + // evidence agent must not throw and abort the pipeline. + const ev = evidence || {} + return agent( + `You are a strict status verifier for story ${story.id} (${story.title}). Confirm a status that is justified by REPO evidence only (code + tests). From repo evidence you can confirm at most "E2E tested"; "Browser verified" and "Done" require a separate browser pass and are OUT of scope here — never assign them. Downgrade if the proposed status overclaims.\n\nProposed: ${JSON.stringify(ev)}`, + { label: `verify:${story.id || 'story'}-${i}`, phase: 'Verify', schema: VERDICT_SCHEMA }, + ).then((v) => ({ + id: story.id, + title: story.title, + source: story.source, + implementationFiles: ev.implementationFiles || [], + unitTests: ev.unitTests || 'Missing', + e2eTests: ev.e2eTests || 'Missing', + // Fail closed: no verdict ⇒ conservative cap on the maker's self-proposed status. + finalStatus: v ? v.finalStatus : conservativeStatus(ev.proposedStatus || 'Not started'), + justification: (v && v.justification) || 'verifier returned no verdict — capped to a conservative status; needs manual review', + notes: ev.notes || '', + })) + }, +) + +return { count: records.filter(Boolean).length, records: records.filter(Boolean) } diff --git a/CLAUDE.md b/CLAUDE.md index 59cfda0..a183267 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -124,6 +124,25 @@ Before implementation, the Lead Agent must consult these critic perspectives: 9. Security Critic 10. Regression Critic +**Critic rounds and other fan-out work run as deterministic multi-agent Workflows, +not one agent simulating critics.** The commands invoke `Workflow`-tool scripts in +`.claude/workflows/` — parallel subagents (own context each) with adversarial / +maker-checker verification — and that command-driven invocation is the sanctioned +multi-agent opt-in: + +- `critic-panel.js` — fan-out critics → adversarial verify → matrix + (`/critic-round`, `/multi-agent-dev`, `/qa-pass`, `/loop`). +- `scan-docs.js` — per-story parallel evidence → separate status verifier + (`/scan-project-docs`). +- `gap-analysis.js` — per-dimension gaps → synthesized order (`/story-gap-analysis`). +- `e2e-design.js` — per-category E2E case enumeration → dedup (`/multi-agent-e2e`). +- `loop-iteration.js` — worktree implementer → separate verifier per item + (`/loop` at L2/L3). + +This is heavier (many subagents) by design; it buys genuine independent review +instead of one context role-playing the panel. Commands that don't fan out +(`/fix-localhost`, `/write-goal`, `/dream`, `/memory-audit`) stay single-agent. + ## Development loop For every task: diff --git a/README.md b/README.md index 3a55f2b..a987219 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,12 @@ CLAUDE.md Project rules + multi-agent workflow + doc-sca loop-run-log.md Append-only /loop run history (the loop's durable state) topics/ One page per subject, read on demand settings.local.json Allowed commands for the workflow + workflows/ Deterministic multi-agent orchestration scripts (Workflow tool) + critic-panel.js Fan-out critics → adversarial verify → severity matrix + scan-docs.js Per-story parallel evidence-gathering → status verifier + gap-analysis.js Per-dimension gap analysis → synthesized next-order + e2e-design.js Per-category E2E test-case enumeration → dedup + loop-iteration.js Maker/checker: worktree implementer → separate verifier commands/ Each command has a description used for routing write-goal.md Write a production-grade /goal (or /loop) prompt for a task multi-agent-dev.md Full critic-led development pass @@ -107,6 +113,23 @@ The documentation-scanner workflow adds: Product Requirements, User Story, Acceptance Criteria, Code Evidence, Unit Test Evidence, E2E Test Evidence, and Documentation Consistency critics. +**These run as real multi-agent orchestration, not one agent simulating critics.** +The critic-bearing commands invoke deterministic `Workflow`-tool scripts under +`.claude/workflows/` — parallel subagents (own context each) with adversarial / +maker-checker verification: + +| Workflow | Backs | What it does | +|---|---|---| +| `critic-panel.js` | `/critic-round`, `/multi-agent-dev`, `/qa-pass`, `/loop` | fan-out critics → adversarial verify → severity matrix | +| `scan-docs.js` | `/scan-project-docs` | per-story parallel evidence → separate status verifier | +| `gap-analysis.js` | `/story-gap-analysis` | per-dimension gap analysis → synthesized next-order | +| `e2e-design.js` | `/multi-agent-e2e` | per-category E2E case enumeration → dedup | +| `loop-iteration.js` | `/loop` (L2/L3) | worktree implementer → separate verifier per item | + +It's heavier by design — independent review beats a single context role-playing the +panel. Commands that don't fan out (`/fix-localhost`, `/write-goal`, `/dream`, +`/memory-audit`) stay single-agent on purpose. + ## Everyday workflow Let Claude write the `/goal` for you — it knows the project's gates better than a diff --git a/docs/adoption-guide.md b/docs/adoption-guide.md index fb2e86f..f470003 100644 --- a/docs/adoption-guide.md +++ b/docs/adoption-guide.md @@ -23,8 +23,9 @@ The portable part is everything **except** the starter app. The starter app ```txt CLAUDE.md Project rules, critic roster, completion gates .claude/commands/* Slash commands (/multi-agent-dev, /critic-round, /dream, ...) -.claude/loop.md Recurring watchdog loop (used by /loop) -.claude/memory/ Knowledge wiki (index.md + topics/ + log.md) +.claude/workflows/* Multi-agent orchestration scripts (critic-panel, scan-docs, ...) +.claude/loop.md loop-checklist.md Recurring watchdog loop + readiness rubric (used by /loop) +.claude/memory/ Knowledge wiki (index.md + topics/ + log.md + loop-run-log.md) .claude/settings.local.json Allowed commands (adapt to your tools) docs/*.md docs/stories/ Story-map scaffolds (user-stories index, stories/ per-story files, ...) ``` @@ -491,6 +492,31 @@ Adapt it to your stack: --- +## Multi-agent orchestration (Workflows) + +The critic-bearing commands don't simulate critics in one context — they invoke +deterministic **`Workflow`-tool scripts** under `.claude/workflows/` that fan out +parallel subagents (own context each) with adversarial / maker-checker verification: +`critic-panel`, `scan-docs`, `gap-analysis`, `e2e-design`, and `loop-iteration`. + +To adapt them: + +- **They're project-aware via prompts, not config** — the agents read `CLAUDE.md`, + the memory wiki, and your `src/`/`e2e/`/`docs/` paths. Adjust those paths in the + scripts if your layout differs (e.g. a monorepo package dir). +- **Cost is real.** A critic round spawns ~10 critic agents + a verifier per + blocker/important finding; `/multi-agent-dev` runs that twice (pre + post). That's + the price of genuine independent review — scale the roster down if you want it + lighter, or reserve the workflows for substantive changes. +- **Name workflows distinctly from commands** — a workflow whose `meta.name` + matches a command shows up as a duplicate in the skills list (that's why the + critic workflow is `critic-panel`, not `critic-round`). +- **Requires a Claude Code build with the `Workflow` tool.** If yours lacks it, the + commands still work as a single Lead agent simulating the panel — just without the + independent contexts. + +--- + ## Tune the critics to your product The 10 critics are general-purpose. Adjust emphasis to your product type: