From 99cdd8e87d9d8180018c022a29fde95e78566357 Mon Sep 17 00:00:00 2001 From: Griffin Long Date: Mon, 17 Aug 2026 20:32:59 -0400 Subject: [PATCH] feat: sync skills to canonical versions, add 11 new skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update 8 existing skills to their best current versions from the awa-private marketplace (awa-dev plugin) and agent-afk bundled plugin. Add 11 skills that previously existed only in those repos. Updated from awa-bundled (most developed copies): - contract, ground-state, research, ship, spec - devils-advocate, diagnose, gather, ground-claim, shadow-verify (new) Updated from awa-dev (most developed copies): - appmap, forge-friction, provideme, resolve Added from awa-bundled (previously bundled-only): - false-completion-gate, fix-pr, parallelize, polish, refactor, review, simplify Excluded: automate (intentional divergence — agent-framework keeps the launchd model, bundled/marketplace use afk-native scheduler). Skill count: 13 → 25 --- skills/contract/SKILL.md | 17 ++- skills/devils-advocate/SKILL.md | 67 ++++++++++ skills/diagnose/SKILL.md | 11 ++ skills/false-completion-gate/SKILL.md | 28 +++++ skills/fix-pr/SKILL.md | 111 +++++++++++++++++ skills/forge-friction/SKILL.md | 77 ++++++++---- skills/gather/SKILL.md | 43 +++++++ skills/ground-claim/SKILL.md | 148 ++++++++++++++++++++++ skills/ground-state/SKILL.md | 98 +++++++++++++-- skills/parallelize/SKILL.md | 10 ++ skills/polish/SKILL.md | 140 +++++++++++++++++++++ skills/provideme/SKILL.md | 2 +- skills/refactor/SKILL.md | 155 +++++++++++++++++++++++ skills/research/SKILL.md | 27 +++- skills/resolve/SKILL.md | 20 ++- skills/review/SKILL.md | 171 ++++++++++++++++++++++++++ skills/shadow-verify/SKILL.md | 58 +++++++++ skills/ship/SKILL.md | 66 +++++++--- skills/simplify/SKILL.md | 124 +++++++++++++++++++ skills/spec/SKILL.md | 25 +++- 20 files changed, 1340 insertions(+), 58 deletions(-) create mode 100644 skills/devils-advocate/SKILL.md create mode 100644 skills/diagnose/SKILL.md create mode 100644 skills/false-completion-gate/SKILL.md create mode 100644 skills/fix-pr/SKILL.md create mode 100644 skills/gather/SKILL.md create mode 100644 skills/ground-claim/SKILL.md create mode 100644 skills/parallelize/SKILL.md create mode 100644 skills/polish/SKILL.md create mode 100644 skills/refactor/SKILL.md create mode 100644 skills/review/SKILL.md create mode 100644 skills/shadow-verify/SKILL.md create mode 100644 skills/simplify/SKILL.md diff --git a/skills/contract/SKILL.md b/skills/contract/SKILL.md index 3e2bf39..be53115 100644 --- a/skills/contract/SKILL.md +++ b/skills/contract/SKILL.md @@ -1,6 +1,7 @@ --- name: contract -description: "Reference convention for sub-agent I/O schemas. Loaded by orchestrator skills via /contract and into agents (e.g., qualify) via the `skills:` field." +description: "Reference convention for sub-agent I/O schemas. Loaded by orchestrator skills via /contract and into agents via the `skills:` field." +context: load --- # Contract @@ -12,9 +13,23 @@ For each sub-agent you plan to dispatch, define a schema before the call: - `artifacts` — named structured fields expected back (not freeform prose) - `non_goals` — what the sub-agent must NOT do - `failure_modes` — how to report blocked or partial work +- `domain` *(optional)* — the knowledge domain for this task. Guides how research, specification, and verification adapt. Common values: `software`, `research`, `design`, `business` — but any freeform string works (e.g., `healthcare`, `legal`, `education`). When omitted, infer from context: git repo present → `software`; PDFs/papers/citations in working directory → `research`; design files/brand assets → `design`; financial models/strategy docs → `business`. Default fallback: `software`. Embed the schema at the top of every sub-agent's prompt and require results in that exact shape. Instruct each sub-agent explicitly: "Return ONLY the schema fields. No preamble, no analysis prose, no explanation — begin your response with the first schema field." When sub-agents return, validate field-by-field. If any artifact is missing, malformed, or wrapped in prose, re-dispatch only the failing sub-agent with the gap cited. Merge only schema-valid responses. +Also instruct each sub-agent to stop on non-convergence: if repeated attempts at the same sub-goal stop making progress after a few tries, do not keep retrying — return the best partial result through the schema's designated failure/partial channel (`failure_modes`, or whatever blocked/`unverified` field that agent's schema defines), naming what could not be resolved. Activity is not progress. + +## Epistemic confidence + +Recommended for all sub-agents. Add to your return schema: + +- `confidence` — low / medium / high — how confident is the sub-agent in the completeness and accuracy of its findings? +- `coverage_gaps` — what the sub-agent couldn't access, verify, or search (e.g., proprietary databases, paywalled sources, unpublished practitioner knowledge, subjective judgment areas) +- `boundary_flag` — if the sub-agent hit an epistemic boundary, name it: `non-falsifiable` (claim can't be tested), `low-coverage` (search was limited), `tacit-knowledge` (unwritten knowledge required), `unprecedented` (genuinely novel, no baseline), `time-sensitive` (answer depends on current state), or `none` +- `recommended_action` — what should happen next: `proceed` (findings solid, move ahead), `human-gate` (pause for human judgment before acting), `re-retrieve` (try different search strategy or sources), `elicit` (generate prompts to validate with domain experts) + +This is NOT required — skills that don't return it continue to work. But when present, coverage gaps and boundary flags surface automatically during merge, preventing silent failures. + ## Skip if - Single-agent dispatch diff --git a/skills/devils-advocate/SKILL.md b/skills/devils-advocate/SKILL.md new file mode 100644 index 0000000..47c1f1d --- /dev/null +++ b/skills/devils-advocate/SKILL.md @@ -0,0 +1,67 @@ +--- +name: devils-advocate +description: "Adversarially critique a proposal by generating alternatives. Dispatches 3 parallel critics (pragmatist, paranoid, architect lenses) — each invents one alternative approach — then a synthesis step ranks all 4 options and recommends the top choice. When the proposal was authored by someone other than the agent (inherited plan, someone else's PR, external review), a 4th steelman critic runs in the same parallel wave and strengthens the original first, so it is judged at its strongest rather than its weakest. Use when a plan, fix, scoping, decomposition, or named recommendation will drive decisions and you want structured alternative-generation before committing. Complements /shadow-verify — that skill re-derives factual claims; this one critiques whether the chosen approach itself is best." +context: load +--- + +## Sub-agent contract +/contract + +When a proposal — a plan, fix, decomposition, scoping, or named recommendation — will drive user decisions, file edits, or commits, do NOT act on it as-given. Run a devils-advocate critique wave **before** acting, and use the recommendation as input to the decision. + +**Wave 2 — Parallel critics (3 fixed lenses + 1 conditional, independent):** +1. Extract the **proposal** (the approach being critiqued) and the **goal** (what the proposal is trying to accomplish). Both should be plain prose. Do NOT include the original proposer's reasoning or evidence — critics must invent alternatives without anchoring on the chosen path. +2. Dispatch 3 critics in parallel. **Default `subagent_type: "research-agent"`** (mechanically locked to Read/Grep/Glob/WebFetch/WebSearch — cannot Edit/Write/commit). Each critic receives ONLY the proposal + goal + ONE lens: + - **pragmatist** — cheapest-path. "What is the cheapest approach that solves the goal? Argue why the proposal may be over-engineered." + - **paranoid** — safest-path. "What could go wrong with the proposal? Propose a safer alternative with narrower blast radius." + - **architect** — right-level. "Is the proposal addressing the right abstraction level? Propose an alternative one level up (systemic fix) or down (targeted fix)." +3. Each critic returns `{lens, alternative, tradeoff, strength}` where `strength ∈ {weak, medium, strong}` reflects the critic's confidence that its alternative beats the original. +4. **Conditional 4th critic — steelman.** Fires **only when the proposal is externally-authored**: an inherited plan, someone else's PR, an external review, or third-party text the user pasted in. It does **not** fire on a proposal the agent authored itself this session — there the proposer's reasoning is already in main context and strengthening is a no-op that taxes the hottest call path. When it fires, dispatch it **in the same parallel wave** as the other three (never before them), on the same `research-agent` base, receiving ONLY the proposal + goal + the steelman lens — the closed input set of step 2 is unchanged, and no critic ever sees another critic's output. + - **steelman** — strongest-version. "Restate this proposal as its strongest defensible version. Fill in assumptions its author left implicit, supply the evidence that would best support it, and drop claims too weak to defend. Do not critique it and do not propose an alternative." +5. The steelman returns `{strengthened_original, gaps_filled, weak_claims_dropped}` — deliberately **not** the `{lens, alternative, tradeoff, strength}` shape. It is an annotation on the original, never a competing option, so it does not enter the ranking as a 5th candidate and does not carry a `strength` score. + +**Invariant (why steelman sits *inside* Wave 2, not before it):** a steelman is by construction the proposer's reasoning and evidence, reconstructed and amplified — the single most anchoring artifact obtainable. Routing it upstream of the other critics would hand them a hardened target and violate step 1's prohibition, converting invention into rebuttal. Keeping it a peer in the parallel wave preserves critic independence, which is what makes convergence (Wave 3.5) and `dissent` (Wave 3) informative at all. Never promote it to a pre-wave. + +**Wave 3 — Synthesis (sequential, single agent):** +1. Dispatch one synthesis agent (same research-agent base). Input: original proposal + goal + all 3 critic outputs, plus the steelman annotation when Wave 2 produced one. +2. Rank all 4 options (original + 3 alternatives) along: **cost** (implementation + ongoing), **risk** (blast radius + reversibility), **scope-fit** (how cleanly it solves the stated goal, no more), **goal-fit** (how well it addresses the underlying intent, not just the surface goal). When a steelman annotation is present, score the **original at its strengthened form** — the point is to beat the proposal at its best, not to win against a version its author would disown. The candidate count stays 4: the annotation upgrades how `original` is judged, it does not add an option. +3. Recommend ONE top choice with a one-paragraph rationale. +4. Flag `dissent = true` when ≥2 critics returned `strong` alternatives disagreeing with the recommendation — signals the synthesizer is overruling well-argued dissent, so confidence is low. Include a `dissent_note` summarizing the strongest counter-argument. + +**Wave 3.5 — Composition-boundary check (fires on convergence):** +Critics that converge may all have evaluated the proposal in artifact-isolation — none read the boundaries where it composes with siblings. A convergent verdict reached in isolation can be confidently wrong (e.g., critics agree on a UI glyph asserting visual continuity, but none saw that parallel-branch flushes reorder it). When the synthesis recommendation is **convergent** — recommendation ≠ original with ≥2 critics having returned the same alternative — dispatch ONE context-injection verifier (**`subagent_type: "research-agent"`** — Read/Grep/Glob/WebFetch only, no Edit/commit) BEFORE surfacing: +1. Its job is NOT to re-evaluate the proposal in isolation. It reads the 3 nearest composition boundaries — upstream caller, downstream consumer, and the render/event/state pipeline that interleaves the proposal's target with siblings. +2. For each boundary: does the recommendation survive when the boundary varies? Check **temporal interleaving** (can flushes / parallel branches / sibling completions reorder it?), **state threading** (does it assume a point-of-use state upstream can break?), **adjacency assumptions** (does it presume render-tree / scrollback / call-graph adjacency that isn't load-bearing under recomposition?). +3. Returns `CONFIRMED` only if the recommendation survives all three; otherwise `OVERRIDE: `. (These verdicts are internal to Wave 3.5 — distinct from shadow-verify's verifier verdict vocabulary.) + +Until the verifier returns `CONFIRMED`, the convergent recommendation is a **candidate**, not a recommendation. On `OVERRIDE`, fold the named condition into the matrix and re-rank. **Cap:** if `OVERRIDE` recurs after 2 re-ranks, escalate the full composition failure to the user rather than cycling further — the matrix cannot resolve a boundary violation on its own. + +**Scope guard:** skip when the proposal is purely local with no composition surface, or is anchored to an external referent that survives independently of the system. Does not fire when `dissent = true` — that path surfaces the matrix directly; adding a Wave 3.5 gate on already-uncertain output adds friction without signal. Fires once per convergent verdict, not per critic. + +**Merge + surface:** +- Recommendation = `original` → the proposal survived critique; proceed with it. **With a steelman annotation present, the strengthened form is what survived and is therefore what you proceed with** — it is the artifact Wave 3 actually scored, and executing the verbatim text instead would run a version that never won: one that can omit a prerequisite the steelman made explicit, or reinstate a claim it dropped as indefensible. Rank and execute must name the same artifact. Surface it **as** the strengthened version, with `gaps_filled` (assumptions the proposal depends on but never stated) and `weak_claims_dropped` (claims too weak to defend) shown as an explicit delta against what the user wrote, so they can see exactly what changed and reject it if they disagree. The substitution must be **visible, never silent** — do not present strengthened text as if it were the user's verbatim proposal. With no steelman annotation, `original` is the verbatim proposal and proceeds unchanged. +- Recommendation ≠ `original`, `dissent = false`, ≥2 critics returned the same alternative → convergent path: run Wave 3.5, then surface the alternative with rationale (on `OVERRIDE`, re-rank first) before acting. +- Recommendation ≠ `original`, `dissent = false`, only 1 critic backed the winner → no convergence to guard: surface the alternative with rationale directly (Wave 3.5 does not fire). +- `dissent = true` → present the matrix to the user; do not act. Confidence is low. + +**When to invoke:** +Any time a proposal, plan, root-cause + fix, decomposition, or named recommendation will drive user decisions, file edits, commits, or external side-effects. Especially useful when the proposal "feels right" — that's when alternative-generation has the highest value. + +**Skip when:** +- Single-line edits or trivial fixes where alternative space is empty. +- User explicitly named the chosen approach by name (critiquing a directly-requested action is friction, not signal). +- An upstream orchestrator already produced comparative output on the same claim-space (`/diagnose`'s hypothesis ranking does not need a second opinion on its hypotheses — though the *final fix* it produces can still benefit). +- The **steelman critic specifically** no-ops when the agent authored the proposal itself this session — the common plan-mode path ("form a candidate plan → apply adversarial pressure"). The other three lenses still run; only the 4th is skipped. Strengthening your own just-written plan restates context you already hold. + +## Appendix: lens selection (non-binding) + +Three fixed adversarial lenses always run, plus the conditional steelman; domain-specific lens packs (software-perf, research-methodology, business-risk) are V2 work. When the proposal's domain is clear, the synthesis agent may weight dimensions accordingly — but the critic lenses themselves remain fixed. + +| Lens | Typical alternatives it surfaces | +|------|----------------------------------| +| pragmatist | narrower scope, simpler implementation, reuse-over-build | +| paranoid | smaller blast radius, reversibility, guardrails, staged rollout | +| architect | systemic fix one level up, targeted fix one level down, different subsystem ownership | +| steelman *(conditional)* | no alternative — returns the original at its strongest, plus the unstated assumptions it depends on and the weak claims worth dropping | + +Note that the three adversarial lenses are all oppositional: each asks some form of "what would be better?" The steelman is the only lens that moves the other way, which is why it returns a different shape and is scored differently. If a future lens pack adds more stances, check which direction each one points before assuming it can reuse the `{alternative, tradeoff, strength}` contract. diff --git a/skills/diagnose/SKILL.md b/skills/diagnose/SKILL.md new file mode 100644 index 0000000..fe68403 --- /dev/null +++ b/skills/diagnose/SKILL.md @@ -0,0 +1,11 @@ +--- +name: diagnose +description: "Parallel root-cause analysis for bugs and failing tests. Use when a test fails, a bug is reported, or behavior is unexplained — dispatches sub-agents to form and validate hypotheses in isolated worktrees." +context: fork +--- + +Gather context: read the failing test or bug description, relevant error output, and recent git changes. If no failing test exists yet, write a minimal reproducer test (or identify a concrete verification command) before proceeding — hypotheses need a pass/fail signal to validate against. Dispatch two sub-agents in parallel — one to search the codebase for code paths involved in the failure (`subagent_type: research-agent`, read-only), and one to check recent commits and diffs that could have introduced the regression (`subagent_type: general-purpose` — requires Bash for `git log`/`git diff`/`git show`). When both return, synthesize findings into 2–4 ranked hypotheses, each with a specific code location and proposed cause. + +For each hypothesis, dispatch a sub-agent with `isolation: "worktree"` to apply a minimal speculative fix, run the test or verification command, and then run the broader related test suite to check for regressions. Run all hypothesis-testing agents in parallel. Collect results: which fixes passed, which didn't, and any regressions surfaced by the broader suite. + +Report the validated root cause (the hypothesis whose fix passed), the speculative fix diff, and regression status from the broader test run. If no hypothesis passes, synthesize what was learned and form a second round of hypotheses. If the user approves the fix, apply it to the main worktree. diff --git a/skills/false-completion-gate/SKILL.md b/skills/false-completion-gate/SKILL.md new file mode 100644 index 0000000..3299ca4 --- /dev/null +++ b/skills/false-completion-gate/SKILL.md @@ -0,0 +1,28 @@ +--- +name: false-completion-gate +description: "Fires when a state-mutating task is about to be declared Done (success summary or completion verdict) — to catch silent success, the highest-frequency invisible agent failure where confident 'done' language hides work that never landed. Decomposes the completion into receipt-checkable assertions, then dispatches a reconciliation auditor that cross-references each claim against the session's tool-call LEDGER (did the producing action fire?) and a fresh postcondition READ-BACK of the named artifact (does the file/test/commit exist and match?), gating the Done behind a BACKED/UNBACKED/UNVERIFIABLE verdict plus a bounded repair loop. Distinct from shadow-verify, which RE-DERIVES an investigation finding's correctness — this reconciles a COMPLETION against execution receipts, never re-deriving the claim. Use before any Done/success in implementation, fix, refactor, migration, or multi-file write work. Skip text-terminal sessions (explanation, Q&A) and work that already failed loudly." +failure_modes: + - false completeness + - confident fabrication + - tool thrash +--- + +## Sub-agent contract +/contract + +This skill fires when a session (or a returning sub-agent) is about to declare a state-mutating task **complete** — a Done terminal state, a success summary, or a passing completion verdict. The core invariant: **a completion claim must be backed by execution receipts and a fresh artifact read-back, never by the assertive language of the claim itself.** Claim and evidence must be structurally separated — the base agent fuses them, narrating "done" straight from in-context memory of a tool result it never re-observed. That fusion is exactly how "silent success" propagates a false Done into downstream steps that then compound on a foundation that was never real. + +**Phase 1 — Claim decomposition.** Before emitting the Done, decompose the pending completion into a checklist of concrete, receipt-checkable assertions. Each assertion names: (a) the **deliverable** ("feature X implemented", "tests green", "branch pushed"), (b) the **producing action** that must have fired to make it true (an Edit/Write to a specific path, a specific test command, a `git push`), and (c) the **durable location** that would prove it (file path + expected content, test-output line, commit SHA). Add a **goal-substitution assertion**: if the original goal was diagnostic (interrogative — "why does X", "what causes Y") but the deliverables are all implementation, the diagnostic answer is itself a required assertion — its absence is an UNBACKED completion, because the question was silently swapped for a patch. + +**Phase 2 — Receipts reconciliation.** Dispatch one read-only reconciliation auditor (`subagent_type: "awa-private:research-agent"` — locked to Read/Grep/Glob; add a Bash-capable type with `isolation: "worktree"` only if a postcondition needs a command rerun, e.g. re-running the test). It receives ONLY the assertion checklist + the user's original goal — never the orchestrator's success narrative. For each assertion it independently establishes: **ledger_match** — did the producing action actually appear in this session's tool-call history? **postcondition** — read the durable artifact from source *now* (file content, fresh test output, `git log`/`git status`) and check it matches the claim. **durable_location** — confirm evidence is a real location, never transcript-only. The orchestrator hands the auditor this session's tool-call history as the ledger; if that history is unavailable or truncated (a hand-off, a compacted context), the auditor marks the assertion `UNVERIFIABLE` rather than assuming the action fired — the gate fails closed, never open. Returns a receipts table: `{assertion, ledger_match: yes|no, postcondition: pass|fail|unverifiable, durable_location, verdict: BACKED|UNBACKED|UNVERIFIABLE}`. + +**Gate verdict (merge):** +- All assertions **BACKED** → `VERIFIED`: emit the Done, attaching the receipts table as the evidence block (durable locations, not prose). +- Any **UNBACKED** (no producing action in the ledger, or read-back contradicts the claim) → `FALSE-COMPLETION`: do **not** emit Done. Surface the exact unbacked assertion(s) and what the read-back actually showed. +- Any **UNVERIFIABLE** (external side-effect with no fetchable receipt) → never pass as a confident Done; surface tagged `[needs-human-review]`. + +**Phase 3 — Bounded repair.** On `FALSE-COMPLETION`, route only the unbacked assertions to a targeted repair pass — re-execute the missing producing action or fix the failing postcondition — then re-run Phase 2 on just those assertions. Cap at **2 repair cycles**. If an assertion is still UNBACKED after 2 cycles, emit a **Blocked** terminal state naming the exact unbacked assertion and the missing receipt — never a Done. The asymmetry is safe by construction: the gate can only ever downgrade a false Done to an honest Blocked/needs-review; it cannot manufacture a completion that wasn't real. + +**When to invoke:** before any Done / success summary / completion verdict in state-mutating work (implementation, bug fix, refactor, migration, multi-file write, deployment) — especially when the belief that it worked rests on in-context memory of a tool result rather than a fresh read-back, or when the run spanned many steps and the early "success" was never re-observed. + +**Skip when:** the session is text-terminal (a pure explanation, architecture walkthrough, or Q&A that mutates no artifact — there are no receipts to reconcile); the work already failed loudly (no false-completion risk); or an orchestrator that already verifies its own completion (`ship`, `mint`, `heal`) is driving — invoke once at the outer Done, not per inner step. diff --git a/skills/fix-pr/SKILL.md b/skills/fix-pr/SKILL.md new file mode 100644 index 0000000..708edba --- /dev/null +++ b/skills/fix-pr/SKILL.md @@ -0,0 +1,111 @@ +--- +name: fix-pr +description: "One-verb pipeline for the operator's highest-frequency manual loop: fetch a PR's unresolved reviewer feedback (inline review comments, review-summary bodies, and issue-level conversation comments) and failing CI checks, fix them in an isolated managed worktree via a budget-bounded subagent, verify with the project's test gates, and push the fix back to the PR branch. Replaces the retyped recipe 'send a subagent in a worktree to fix , then push.' Use when a PR has review comments or red CI that needs addressing — e.g. 'fix pr 286', 'address the review on #215', 'CI is red on the worktree-sweep PR'. Never force-pushes, never touches main, fails closed on missing gh auth or un-pushable fork PRs." +argument-hint: " [--repo ] [--no-push] [--re-review]" +surface: "afk" +failure_modes: + - push to wrong branch + - nested /review max_depth self-collision + - silent partial fix (some comments addressed, done claimed for all) + - unmanaged worktree leak +--- + +## Sub-agent contract +/contract + +`fix-pr` turns "review feedback / red CI on PR N" into a pushed fix commit with test evidence, using worktree isolation so the operator's working tree is never disturbed. It is the composition the operator previously chained by hand: `/resolve`-style feedback interpretation + managed worktree + budget-bounded fix subagent + test gate + push. + +**Skip when:** the fix is a one-line suggestion the operator pointed at directly (apply inline); the PR is already green with all threads resolved (report and stop); or the work is local-only and unpushed (use `/ship`). + +--- + +### Phase 0 — Input gate & preflight (inline, fail closed) + +Parse `$ARGUMENTS`: +- **`pr`** — PR number or URL (required). If absent, stop: "fix-pr requires a PR number or URL." +- **`repo`** — repo path from `--repo`; default: current working directory's git root. +- **`no_push`** — from `--no-push`: produce the fix in a kept worktree + diff summary, no remote mutation. +- **`re_review`** — from `--re-review`: after pushing, re-trigger `/review` (top-level only — see Phase 6 guard). + +Preflight (all inline bash; any failure → **Blocked**, do not improvise): +1. `gh auth status` — must be authenticated. Fail closed if not. +2. `gh pr view --json state,headRefName,headRepositoryOwner,isCrossRepository,mergeable,url` — PR must be OPEN. +3. **Fork guard:** if `isCrossRepository` is true and the authenticated account cannot push to the head repo, emit **Blocked** naming the fork and stop. Never attempt workarounds. +4. Record `head_branch` — this is the ONLY branch this skill will ever push to. + +--- + +### Phase 1 — Feedback harvest (inline) + +Reviewer feedback lives in **three distinct GitHub stores** — miss any one and the fix is silently partial. Harvest all three, then the gates: + +1. **Inline review comments:** `gh api repos/{owner}/{repo}/pulls//comments` — comments anchored to a diff line (each carries `path`/`line`/`diff_hunk`). +2. **Review summary bodies:** `gh pr view --json reviews` — the top-level body of each APPROVE / REQUEST_CHANGES / COMMENT review submission. +3. **Issue-level conversation comments:** `gh pr view --json comments` (equivalently `gh api repos/{owner}/{repo}/issues//comments`). A PR is also an issue, and its plain conversation comments ("also update the docs", "rename this before merge") live on the **issues** endpoint — the `pulls//comments` endpoint does NOT return them. Skipping this source is the known gap: reviewers who leave feedback as normal PR comments are otherwise dropped entirely, and a PR can have actionable conversation comments with zero inline review comments. + +Then the gates: +4. **Failing CI checks:** `gh pr checks ` — for each failing check, pull the tail of its log (`gh run view --log-failed` when available). +5. **PR description acceptance criteria** if present. + +**Noise filter (apply to sources 1–3 before building the spec):** drop bot/automation chatter (vercel, github-actions, codecov, dependabot, deploy-preview posts) and non-actionable social comments ("LGTM", "thanks", 👍). Keep only unresolved, actionable requests. + +Routing rule: +- Any actionable reviewer feedback (inline comments, review bodies, or conversation comments) exists → it is the primary spec; failing checks are secondary gates. +- **No actionable feedback but CI is red → the failing checks ARE the spec** (acceptance criterion 3). +- Neither → report "PR is green with no unresolved feedback" and stop (Done, no mutation). + +Consolidate into a numbered fix spec: each item = source (comment URL or check name), file/line if known, and the requested change. This numbered list is the completeness contract — every item must be addressed or explicitly declared out-of-scope in the terminal report. + +--- + +### Phase 2 — Isolated worktree (inline, managed only) + +Create the worktree via the **`worktree` tool** (`action: create`, `name: pr-fix`, `base: ` after `git fetch`). **NEVER raw `git worktree add`** — unmanaged trees lack sweep metadata and leak (the 108-worktree/14GB sprawl was this failure mode). + +If a managed worktree for this PR already exists, reuse it only if clean; otherwise create a fresh one with a suffixed name. + +--- + +### Phase 3 — Fix dispatch (one subagent, budget-bounded) + +Size the dispatch per `/right-size-delegation` if available; defaults otherwise: + +Dispatch ONE implementation subagent (`agent` tool, `cwd: `, `max_turns: 25`, model right-sized to the diff — `sonnet` default, `haiku` never for code fixes): +- inputs: the numbered fix spec, `head_branch`, repo test/lint commands (inferred from package.json / Makefile / pyproject.toml and passed explicitly). +- goal: address every numbered item with minimal diffs; run the narrowest relevant tests per item; commit locally with a message referencing the PR (`fix(pr-): address review feedback`). +- non_goals: do NOT push, do NOT touch branches other than the checked-out one, do NOT invoke /review or any skill, do NOT expand scope beyond the numbered items. +- deliverable: per-item status table (`fixed` | `out-of-scope `), unified diff summary, targeted test output, local commit SHA. + +--- + +### Phase 4 — Verification gate (inline in the worktree) + +Run the project's full test/lint gates in the worktree yourself — do not trust the subagent's report alone. + +- All green → Phase 5. +- Failures → iterate: re-dispatch the fix subagent with the failure output as an updated spec (or hand off to `/heal` semantics if that skill is loadable), **≤2 iterations**. Cap reached → keep the worktree, emit **Blocked** naming the branch, the worktree path, the surviving failures, and the per-item status table. + +**Completeness check:** every numbered spec item must be `fixed` or explicitly `out-of-scope` with a reason. A partially addressed spec is never reported as Done. + +--- + +### Phase 5 — Push (guarded) + +- `no_push` set → `worktree keep` (reason: "fix-pr --no-push review pending"), emit the diff summary + per-item table, stop (Done, no remote mutation). +- Otherwise: `git push origin ` from the worktree. **Plain push only — never `--force`, never `--force-with-lease`, never any other ref.** If push is rejected (non-fast-forward because the PR moved), fetch + rebase the fix commits onto the new head, re-run Phase 4 gates, push again. If still rejected → Blocked. + +--- + +### Phase 6 — Optional re-review (top-level guard) + +If `re_review`: invoke `/review` **directly from this top-level session only — NEVER from inside a subagent** (known max_depth self-collision: 100+ `delegation.skipped reason:"max_depth" requested_name:"review"` entries in routing-decisions.jsonl). If the current session is itself a subagent (check `get_runtime_state` depth), skip re-review and note it in the terminal report instead. + +--- + +### Phase 7 — Cleanup & terminal state + +- Success: `worktree remove` (branch ref is preserved automatically). +- Failure/Blocked: keep the worktree, name its path and branch in the report. + +**Done** must cite: pushed commit SHA(s), the PR URL, the per-item fix table, and test-gate output location. +**Blocked** must cite: exact unblock condition (auth, fork perms, surviving test failures), worktree path, and everything already fixed. diff --git a/skills/forge-friction/SKILL.md b/skills/forge-friction/SKILL.md index d04373c..3390e63 100644 --- a/skills/forge-friction/SKILL.md +++ b/skills/forge-friction/SKILL.md @@ -1,17 +1,40 @@ --- name: forge-friction -description: "Surface recurring friction from Claude Code's native telemetry and identify actionable skill opportunities. Use when the user runs /forge-friction." +description: "Surface recurring friction from session telemetry and generate targeted skills. Use when the user runs /forge-friction." +argument-hint: "[--dry-run] [--auto]" --- Run the friction analyzer to get a summary of recent friction patterns: ```bash -python3 "${CLAUDE_PLUGIN_ROOT}/scripts/friction/analyzer.py" +python3 "${PLUGIN_ROOT}/scripts/friction/analyzer.py" ``` If no friction sessions exist, tell the user there are no friction patterns in the lookback window. -If friction data exists, review the output. It contains friction categories ranked by frequency, each with recent examples showing `friction_detail` (what went wrong) and `goal` (what the user was trying to do). +If friction data exists, review the output. It contains friction categories **ranked by confidence, then frequency**, each with recent examples showing `friction_detail` (what went wrong) and `goal` (what the user was trying to do). + +Tool-error categories carry a failure-class suffix. `:timeout`, `:truncated`, and `:slow` are **high-confidence** — real, specific friction a skill can address — and rank first. `:plain` is the **low-confidence residue**: a fast non-zero exit the trace cannot distinguish from a benign result (a `grep` no-match, a `test` that exits 1). It ranks last and should be treated with skepticism. + +## Dry-run mode + +If `$ARGUMENT` contains `--dry-run`: after the analyzer runs, identify themes using the filter criteria in "Your job" below, but instead of presenting them interactively, output a single markdown table: + +| Category | Count | Example friction_detail | +|---|---|---| +| ... | N | "truncated quote 1"; "truncated quote 2" | + +One row per theme. Truncate each `friction_detail` to ~80 chars. Up to 3 examples per row (semicolon-separated in the cell). If no themes pass the filter, print "No themes in lookback window." + +After the table (or "no themes" line), print: + +> dry-run: no /forge invocation, no telemetry written + +Stop. Do NOT proceed to the y/n prompts, `/forge` dispatch, or telemetry write described in "Your job" — those are skipped entirely in dry-run. + +## Auto mode + +If `$ARGUMENTS` contains `--auto`: skip all y/n prompts and auto-approve all qualifying themes. For each theme that passes the filter criteria (3+ sessions, recurring failure mode, skill-addressable), immediately run `/forge` with the theme as a seed. Chain the invocations sequentially, then log telemetry for each generated skill. `--auto` respects `--dry-run` (if both flags are present, dry-run takes precedence and no /forge invocation or telemetry occurs). ## Your job @@ -19,46 +42,52 @@ Read through the friction categories and their examples. For each category with Not all friction is fixable by a skill. Filter for themes where: - The same failure mode repeats across multiple sessions (not one-off issues) -- A skill could change Claude's default behavior to avoid the friction +- A skill could change the agent's default behavior to avoid the friction - The fix is a workflow shape change, not a reminder or checklist +- **Prefer high-confidence failure classes.** Pursue `:timeout` / `:truncated` / `:slow` categories first; they are real friction. Treat `:plain` categories as low-confidence — only pursue one if its `friction_detail` / `goal` examples make the recurring failure mode unmistakable. -Present your findings to the user: +**If `--auto` is present** in `$ARGUMENTS`: +- Auto-forge only high-confidence themes. **Skip `:plain` tool-error categories** — without human review, auto-forging the low-confidence residue produces noise skills. Pursue `:timeout` / `:truncated` / `:slow` and non-tool-error categories that pass the filter. +- For each actionable theme, immediately run `/forge` from this plugin, seeding it with: "Create a skill that addresses this recurring friction: [theme summary]. Examples: [2-3 friction_detail quotes]." +- Chain the invocations sequentially (wait for forge + qualify to complete before starting the next one). +- After each forge + qualify completes, log to telemetry (see telemetry format below). +- Do not ask for user confirmation; auto-approve all qualifying themes. -1. **For each actionable theme**: summarize it in one line with the count, then quote 2-3 representative `friction_detail` examples. Ask: "Draft a skill brief for this? (y/n)" +**If `--auto` is not present** (interactive mode): -2. **If the user says yes**: Output a structured skill brief for the theme (NOT an actual skill — that's a separate workflow) — a one-paragraph description of what the skill should do, the friction it addresses, and 2-3 representative examples. Also persist the brief to `~/.claude/agent-framework/briefs/-.md` (e.g., `2026-04-17T14-30-05-wrong-approach-refactor.md`), creating the directory if needed. Use this format: +1. **For each actionable theme**: summarize it in one line with the count, then quote 2-3 representative `friction_detail` examples. Ask: "Generate a skill for this? (y/n)" - ```markdown - --- - theme: - session_count: - created_at: - source: forge-friction - --- +2. **If the user says yes**: + - Run `/forge` from this plugin, seeding it with: "Create a skill that addresses this recurring friction: [theme summary]. Examples: [2-3 friction_detail quotes]." + - After forge + qualify complete, log to telemetry (see telemetry format below). - +3. **If the user says no**, skip it and move on. - ## Friction examples +**Telemetry** (logged after each skill generation, same in both auto and interactive modes). - - - - - - - ``` +Create the telemetry directory if needed and append **one JSONL line** to the SAME file `/forge` writes to — resolve the path identically so both records land together regardless of environment: - The user can feed the brief into their own skill creation workflow. Downstream tools in separate plugins (e.g., autonomous skill generators) can also consume the persisted briefs. +```bash +mkdir -p "${AFK_FRAMEWORK_DIR:-${CLAUDE_CONFIG_DIR:-$HOME/.claude}/agent-framework}" +printf '%s\n' '{"timestamp": "", "source": "friction", "gap": "", "theme": "", "friction_category": "", "session_count": , "generated_skill": "", "qualify_result": "", "iterations": , "target_scope": ""}' >> "${AFK_FRAMEWORK_DIR:-${CLAUDE_CONFIG_DIR:-$HOME/.claude}/agent-framework}/forge-telemetry.jsonl" +``` -3. **If the user says no**, skip it and move on. +Substitute the bracketed values, emit compact one-line JSON, and append it with the shell redirect above (or the file-write tool). **Do NOT use `python3 -c`** — the AFK interpreter guard blocks interpreter `-c`/`-e` eval, which silently drops the record. Field notes: +- `gap` mirrors `theme` so friction records join cleanly with `/forge`'s own `generated_skill` records (which key on `gap`); keep both fields. +- `iterations`: use the actual count `/forge` reported (not a hardcoded `1`). +- `target_scope`: where the skill was written (`user` or `plugin:`). +- The `${AFK_FRAMEWORK_DIR:-${CLAUDE_CONFIG_DIR:-$HOME/.claude}/agent-framework}` expression is byte-identical to the one in `/forge`'s telemetry step — do not substitute a hardcoded `~/.afk` path, which diverges from `/forge` under native Claude Code. You can also drill into a specific category: ```bash -python3 "${CLAUDE_PLUGIN_ROOT}/scripts/friction/analyzer.py" --category wrong_approach +python3 "${PLUGIN_ROOT}/scripts/friction/analyzer.py" --category wrong_approach ``` Or adjust the lookback window: ```bash -python3 "${CLAUDE_PLUGIN_ROOT}/scripts/friction/analyzer.py" --days 60 +python3 "${PLUGIN_ROOT}/scripts/friction/analyzer.py" --days 60 ``` After processing, summarize: how many themes found, how many the user approved, how many skills generated. diff --git a/skills/gather/SKILL.md b/skills/gather/SKILL.md new file mode 100644 index 0000000..c3e5bfb --- /dev/null +++ b/skills/gather/SKILL.md @@ -0,0 +1,43 @@ +--- +name: gather +description: "Parallel context-gathering for a code area. Use when you need to understand a module, feature, or subsystem and would otherwise read 3+ files sequentially — dispatches two agents in parallel to map structure and test coverage in one wave." +context: load +--- + +## Dispatch protocol + +You MUST emit **exactly two** `agent` tool_use blocks in a **single response turn** — both calls in the same assistant message, before either result arrives. Do not dispatch the second agent in a later turn after seeing the first agent's reply. Do not dispatch three agents. Do not dispatch one. + +Correct shape of your next response: + +``` + + Structure Agent prompt + Test Agent prompt + +``` + +If you find yourself about to send a single `agent` call and wait, stop — that is the failure mode this skill exists to prevent. + +## The two agents + +When understanding a task requires reading multiple related files (imports, callers, tests, configs, types), dispatch these two — concurrently, per the protocol above: + +1. **Structure Agent** (Explore, thoroughness matched to scope) — Find and read the target file(s), all direct imports, callers, and config references. Return: + - `files_read`: absolute paths examined + - `call_graph`: how components connect (one paragraph) + - `public_interfaces`: function signatures, types, or contracts that govern the area + - `entry_points`: where control flow enters + +2. **Test Agent** (Explore, "medium") — Find test files that exercise the target area, read them, identify what paths are covered and what's missing. Return: + - `test_files`: absolute paths of relevant tests + - `coverage_summary`: what behaviors/branches tests exercise + - `untested_paths`: code paths with no test coverage + +When both return, merge into a unified context map. If either agent's output has gaps (e.g., Structure Agent missed config, Test Agent found no tests), issue one targeted follow-up Read — do not re-dispatch. + +### When NOT to use + +- You already know exactly which 1–2 files to read — just read them directly. +- The task is a simple grep or symbol lookup — use Grep or Glob. +- You're mid-edit and need to check one adjacent file — a single Read is fine. diff --git a/skills/ground-claim/SKILL.md b/skills/ground-claim/SKILL.md new file mode 100644 index 0000000..a22d0bd --- /dev/null +++ b/skills/ground-claim/SKILL.md @@ -0,0 +1,148 @@ +--- +name: ground-claim +description: "Grounds capability claims with file-read evidence. Default mode answers meta-capability questions ('what does X enable') with path:line citations. Pass mode: runtime-wiring with a claims list to trace actual runtime execution paths — call sites, DI registration, middleware — and get CONFIRMED/UNVERIFIED/REFUTED verdicts per claim. Blocks sign-off on any non-CONFIRMED claim." +argument-hint: " | mode: runtime-wiring claims: [...]" +context: load +failure_modes: + - static_artifact_substitution + - routing_ambiguity +--- + +## Trigger + +**Mode: capability** (default) — Self-referential meta-capability questions about the current repository, framework, or system: +- "What does this repo enable?" +- "What are the orchestration patterns available?" +- "List the available skills." +- "What capabilities does the plugin provide?" + +**Mode: runtime-wiring** — Claims that require tracing actual execution paths, not static structure: +- "Verify that middleware Y intercepts all requests." +- "Confirm plugin Z is loaded on startup." +- "Validate that feature X is active in production." + +Skip both modes for: usage questions ("how do I use X?"), bug reports, feature requests. + +--- + +## Mode: capability (default) + +### Procedure + +1. **Extract capability nouns.** From the user's question, identify 2–5 concrete capability categories (e.g., skills, hooks, agents, orchestration patterns, CLI commands, verification methods). Write them down. + +2. **Locate and read evidence.** For each capability noun: + - Use Glob or Grep to locate source files (e.g., `skills/*/SKILL.md` for skills, `hooks/` for hooks, `agents/` for agents). + - Read at least one concrete source file per capability. Record the file path and specific line numbers. + - Do not rely on training data, model recall, or session-listing attachments. Evidence must come from Read tool output. + +3. **Build the answer inline.** Embed citations **within claims**, not in a separate appendix. Format: `path/to/file.md:line—`. + +4. **Tag ungrounded claims.** If a capability claim cannot be traced to a file read, prefix it with `[UNVERIFIED: what would be needed to verify this]`. Never present an unverified claim without the tag. + +5. **Declare sources read.** Explicitly name which files you read in the response. + +### Hard rules + +- Do not answer from model recall alone. +- Do not answer from session-listing attachments without reading the underlying SKILL.md or manifest files. +- Every capability claim must point to a source. Do not summarize without citation. +- Do not bury unverified claims. Use the `[UNVERIFIED]` prefix and state the evidence gap. +- At least one `path:line` citation per named capability. + +### Exit criteria + +- Response contains ≥1 `path:line` citation per capability mentioned. +- Every unverified claim is explicitly tagged with `[UNVERIFIED: …]`. +- Response explicitly lists which files were read. +- No claims rest on model recall or default knowledge. + +--- + +## Mode: runtime-wiring + +Activated when the caller provides a `claims:` list and `mode: runtime-wiring`. Validates capability claims by tracing **actual runtime wiring** — call sites, DI registration, middleware registration, config manifests — not type signatures or import presence. + +### Inputs + +``` +mode: runtime-wiring +claims: string[] # natural-language claims to verify (≤20 per batch; see batching) +entrypoints: string[] # known runtime entry files (e.g. main.ts, server.ts) +max_depth: number # max call-graph hops per chain (default: 8) +``` + +**Pre-flight gate:** If `entrypoints` is empty, abort immediately with `entrypoints_required` — do not dispatch any sub-agents. If `claims` exceeds 20 items, split into batches of 10 and run sequentially; the Qualifier aggregates across batches. + +### WireTracer sub-agent (one per claim, run in parallel) + +For each claim: + +1. Identify the claimed behavior's implementation symbol (function, class, middleware, plugin). +2. Search for **registration or injection sites** — not import statements. Targets: DI container bindings, `app.use(...)`, `router.register(...)`, config manifests, plugin loaders, factory calls. +3. Trace forward from the entrypoint, documenting each hop: `{ file, line, symbol, role }`. +4. If a hop is missing or conditional on an env var / feature flag, record the condition and stop the chain. +5. Return: + - `chain`: ordered `{ file, line, symbol, role }` list + - `last_confirmed`: deepest confirmed hop + - `gap`: missing-link description, or `null` if complete + - `verdict`: `CONFIRMED` | `UNVERIFIED` | `REFUTED` + +**Prohibited reasoning — these are NOT evidence of runtime wiring:** +- "The type implements the interface, therefore it is active." +- "The import exists, therefore it is called." +- "The function is exported, therefore it is used." + +If no chain can be constructed, return `UNVERIFIED` with `gap: "no_entry_found"`. Max **3 retries** per claim (narrowing search scope each time) before final escalation to `UNVERIFIED`. + +### Qualifier sub-agent + +Reviews all WireTracer reports. Applies: + +- **CONFIRMED** — unbroken chain from entrypoint to invocation site; no conditional gaps left unresolved. +- **UNVERIFIED** — chain breaks at an identifiable gap; return gap location and a resolution hint. +- **REFUTED** — positive evidence the symbol is excluded, overridden, or dead-code eliminated at runtime. + +If Qualifier verdict disagrees with WireTracer verdict, **Qualifier wins**; discrepancy is logged. + +Emits a machine-readable verdict table: + +``` +| Claim | Verdict | Last Confirmed Hop | Gap / Evidence | +|-------|---------|-------------------|----------------| +| ... | ... | ... | ... | +``` + +### Sign-off gate + +Any `UNVERIFIED` or `REFUTED` verdict **blocks downstream review sign-off** and is returned to the caller with the gap location. Only an all-`CONFIRMED` table clears sign-off. + +### Orchestration flow + +``` +entrypoints_required check → abort if empty + │ +claims (batched ≤10 if >20) + │ + ▼ + [WireTracer × N] ── parallel, one per claim + (≤3 retries per claim, narrowing scope) + │ + ▼ + [Qualifier] ── reviews all reports, assigns verdicts + │ + ┌────┴────────┐ +CONFIRMED UNVERIFIED / REFUTED + │ │ +sign-off OK return gaps, block sign-off +``` + +--- + +## Out of scope + +- Usage questions ("how do I use library X?") → normal research. +- Bug reports → `/diagnose`. +- Building new capability → `/mint`. +- Verification of sub-agent findings → `/shadow-verify`. +``` diff --git a/skills/ground-state/SKILL.md b/skills/ground-state/SKILL.md index a0a129e..2b0c1a3 100644 --- a/skills/ground-state/SKILL.md +++ b/skills/ground-state/SKILL.md @@ -1,30 +1,106 @@ --- name: ground-state -description: "Before starting any non-trivial implementation (multi-file edits, new features, config changes, anything that writes), dispatch a parallel pre-flight reconnaissance wave to triangulate git state, project infrastructure, and prior-session memory. Produces a 5-line ground-truth snapshot that grounds the implementation and catches wrong-branch edits, assumed-no-CI, stale origin, and missed memory context before the first edit." +description: "Before starting any non-trivial implementation, run a pre-flight reconnaissance pass to triangulate git state, project infrastructure, and prior-session memory. Auto-assembles a verified grounding preamble — a session-scoped artifact the orchestrator pastes verbatim into every subsequent sub-agent brief — eliminating stale-worktree reads and silent wrong-path errors before the first edit." +read-only: true +context: fork +failure_modes: + - stale_worktree_read + - wrong_branch_assumption + - path_drift_across_briefs --- ## Sub-agent contract /contract -Before any multi-step implementation (not single-file fixes, not pure Q&A), dispatch three parallel reconnaissance sub-agents, each with a narrow target: +**Constraint: read-only reconnaissance.** You MUST NOT call `edit_file`, `write_file`, or any mutating bash command (no `git commit`, `git push`, `git checkout`, `mv`, `rm`, file redirection, package installs, etc.). Read-only tools only: `read_file`, `grep`, `glob`, `list_directory`, `memory_search`, and read-only bash (`git status`, `git log`, `git diff`, `cat`, `ls`, `find`, etc.). `memory_search` is non-mutating and is the **only** way to reach the cross-session fact archive — it is in scope for this skill, do not strip it from this list. -**Git surveyor** -Return: current branch, `git log --oneline -5`, `git status -s`, diff-summary vs `origin/`, stash list. Flag: diverged, uncommitted changes, stale upstream. +If the survey reveals a fix that's tempting to apply, **return it as a recommendation in the snapshot** — the orchestrator decides whether to act. Even if the invoking brief sounds prescriptive ("draft the edit", "apply the change"), this skill stops at the snapshot and the preamble artifact. The orchestrator dispatches a separate implementation step afterward. -**Infrastructure surveyor** -Scan the project for: CI configs (`.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`), package scripts (`package.json`, `Makefile`, `pyproject.toml`), existing linters/formatters, and authoritative config file locations relevant to the task (e.g., `~/.claude.json` vs `~/.claude/settings.json` when the task touches Claude config). Return 5-bullet inventory. +## Inline reconnaissance -**Memory surveyor** -Grep the user's auto-memory store (`~/.claude/projects/-/memory/`) + any project CLAUDE.md for keywords from the user's current request. Return relevant memory file pointers with 1-line summaries, or "no relevant memory found." +Run the three surveys below **directly using your own tools**. Do NOT dispatch any sub-agents via the `agent` or `skill` tools — every lookup in this phase is a deterministic read that you execute yourself using `bash`, `glob`, `read_file`, `grep`, `list_directory`, and `memory_search`. Issue all three surveys in a single batched tool-use round where possible. -**Synthesize** into a 5-line ground-truth snapshot: +### State survey *(bash)* + +Issue these commands (combine into one or two bash calls): +- `git symbolic-ref --short HEAD` — current branch +- `git rev-parse HEAD` — HEAD SHA +- `git status -s` — uncommitted changes +- `git log --oneline -5` — recent commit history +- `git stash list` — stash state +- `git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null` — upstream divergence + +Adapt what you surface to the domain: + +| Domain | What to flag | +|--------|-------------| +| `software` | Branch, recent commits, uncommitted changes, stash, upstream divergence. Flag: diverged, uncommitted, stale. | +| `research` | Version-controlled artifact state, current phase, publication target/deadline if discoverable. | +| `design` | Design system version, component library state, current phase, recent file changes. | +| `business` | Financial model freshness, market data recency, current project phase. | +| *(other)* | Recent changes, current project phase, any state that could cause conflicts. | + +When domain is unspecified, infer from working directory contents. + +### Infrastructure survey *(bash/glob/read_file)* + +Check for relevant tooling and configs. Use the domain table below to decide which paths to probe, then probe them yourself. Return a **5-bullet inventory**. + +| Domain | What to scan | +|--------|-------------| +| `software` | CI configs (`ls .github/workflows/ 2>/dev/null`), package scripts (`package.json`, `Makefile`, `pyproject.toml`), linters/formatters, authoritative config files for the task. | +| `research` | Reference manager (.bib files), LaTeX setup, data analysis tools (Jupyter, R scripts), collaboration setup. | +| `design` | Design tool configs, prototyping tools, handoff configs (Storybook), asset pipeline scripts. | +| `business` | Modeling tool configs, presentation formats, data source configs, collaboration tool structure. | +| *(other)* | Tooling, build/export pipelines, collaboration infrastructure, config files relevant to the stated domain. | + +### Memory survey *(memory_search + read_file)* + +Call the **`memory_search` tool** with keywords from the user's current request — FTS5 syntax, so `term1 AND term2`, `"exact phrase"`, and `prefix*` all work. Run 2–3 query variants (different keyword angles) before concluding nothing is there; a single miss is not evidence of absence. Then read hot memory at `~/.afk/state/memory/HOT.md` and the project overlay — `AFK.md`, or `CLAUDE.md` on a Claude Code surface — for conventions bearing on this task. + +**Invariant: the cross-session memory archive is only reachable via the `memory_search` tool.** The backing store is SQLite (`~/.afk/state/memory/memory.db`) and is not greppable. Do not glob or grep any filesystem path looking for memory — `memory_search` is the only route in. + +Return: relevant facts with 1-line summaries, **plus the stores actually consulted** — e.g. `memory_search: 3 queries, 0 hits; HOT.md: read; AFK.md: read` — so the orchestrator can tell "no relevant memory exists" from "the fork never looked." If `memory_search` is unavailable on this surface, say so explicitly. + +## Synthesis + +Assemble the survey results into a **6-line ground-truth snapshot**: - Branch: ``, ``, upstream: `` - Recent work: last 3 commits or stash items - Infrastructure: CI present? package scripts? authoritative configs for this task -- Memory hits: file refs or "none" +- Memory hits: facts (1-line each) + which stores were consulted, or `none (consulted: …)` - Implementation risks: e.g. "branch is `main`, don't edit directly"; "CI runs on push"; "memory says prior attempt used approach X" +- Epistemic confidence: `` — based on how much state could be verified. Flag if working directory is sparse, if domain is unfamiliar, or if key artifacts may be missing. + +Surface the snapshot and stop. The orchestrator then uses these verified facts — not assumptions — to decide the next step. This skill never edits files. + +## Brief Anchor (auto-runs after synthesis) + +After the 6-line snapshot is assembled, construct the **Brief Anchor** — a path-verified grounding preamble the orchestrator pastes verbatim into every subsequent sub-agent brief. + +**Construction procedure:** + +1. From your state survey output, extract the verified `cwd` (absolute path from `pwd`), `branch` (from `git symbolic-ref --short HEAD`), and `HEAD` SHA (from `git rev-parse HEAD`). +2. From your infrastructure survey output, extract the 2–4 canonical file paths most relevant to the task. For each, run `stat ` — include the path only if `stat` exits 0. Paths that fail `stat` are omitted; if zero paths survive, set the list to `(none verified)`. +3. Assemble the preamble block: + +``` +## Orchestrator grounding — read this first +- **cwd**: +- **branch**: +- **HEAD**: +- **canonical paths** (stat each on entry; emit GROUNDING_FAILED: and abort if missing): + - + - ← omit line if not applicable +``` + +4. Append to the snapshot output under the heading **`## Brief Anchor`** so the orchestrator can copy it directly. + +**Orchestrator usage contract:** -Surface the snapshot. Implementation then uses these verified facts — not assumptions. +- Prepend the Brief Anchor verbatim to every sub-agent brief that reads files, runs `git`/`gh` commands, or references explicit paths. Skip for pure-reasoning tasks. +- Sub-agents receiving the anchor `stat` each listed path on entry. A missing path returns `GROUNDING_FAILED:` — the orchestrator re-dispatches once with the corrected path. If the retry also returns `GROUNDING_FAILED`, emit `BRIEF_GROUND_ABORT` with both the expected and actual paths **plus the corrective command** the operator should run — `git worktree list` to find the intended checkout, then `cd ` — and halt the wave. +- The anchor is session-scoped: one construction pass per `ground-state` invocation. Do not re-invoke `ground-state` mid-session to refresh it; instead pass the existing anchor through. **Skip when:** Task is Q&A only; single-line fix on an already-identified file; user says "skip pre-flight". diff --git a/skills/parallelize/SKILL.md b/skills/parallelize/SKILL.md new file mode 100644 index 0000000..be0fdee --- /dev/null +++ b/skills/parallelize/SKILL.md @@ -0,0 +1,10 @@ +--- +name: parallelize +description: "When finished creating the plan in plan mode, run /parallelize so Claude dispatches one planning agent to transform the current approach into a plan to orchestrate waves of parallel sub-agents." +context: load +--- + +## Sub-agent contract +/contract + +Dispatch a planning sub-agent to transform the current plan into a dependency-aware orchestration plan for waves of parallel sub-agents. Preserve the original goal, infer the necessary decomposition, identify sequential vs parallelizable work, embed TDD into implementation lanes, and avoid unsafe or redundant parallelization. Return an orchestration-ready revised plan. Ensure plan specifies to run tests. diff --git a/skills/polish/SKILL.md b/skills/polish/SKILL.md new file mode 100644 index 0000000..3d54ab4 --- /dev/null +++ b/skills/polish/SKILL.md @@ -0,0 +1,140 @@ +--- +name: polish +description: "Iteratively refines any artifact (prose, spec, prompt, API design, SKILL.md, code snippet) by running a decoupled evaluator → generator loop until explicit criteria are met or an iteration cap is reached. Locks criteria in Wave 1, isolates the evaluator from generation history to prevent sycophancy, and emits the best version with any remaining gaps flagged at cap." +--- + +## Sub-agent contract +/contract + +`polish` is a three-wave evaluator-optimizer loop that applies to any artifact at any workflow stage. Wave 1 locks concrete, testable criteria. Waves 2 and 3 alternate (evaluator → generator) until all criteria pass or the iteration cap is reached. The evaluator is always spawned stateless — it never sees prior generation history — eliminating the sycophancy failure mode that plagues shared-context review loops. + +The orchestrator coordinates but never edits the artifact directly. All revisions are produced by the Wave 3 generator, which receives only the `blocking_gaps` list from the evaluator (not its full reasoning). This constraint prevents scope-creep revisions and keeps each iteration focused on the delta between current state and the locked criteria. + +On cap exhaust the orchestrator emits the best-scoring version plus a structured `remaining_gaps` report so the caller can decide whether to extend the cap, escalate to a human, or ship with known gaps documented. + +--- + +## Inputs + +| Field | Required | Description | +|---|---|---| +| `artifact` | yes | The text, spec, prompt, design doc, or code snippet to refine | +| `goal` | yes | Natural-language quality intent ("make this safe for external stakeholders") | +| `criteria` | no | Pre-supplied testable criteria — skips Wave 1 extraction if provided | +| `threshold` | no | `pass_all` (default) or `pass_N` where N is an integer | +| `cap` | no | Max refinement iterations (default: 4, max: 8) | + +--- + +## Wave 1 — Criteria Extraction + +**Trigger:** Always, unless `criteria` were supplied by the caller. + +**Agent:** Single subagent. Reads `artifact` + `goal`. Produces: + +```json +{ + "criteria": [ + { "id": "C1", "text": "" }, + ... + ], + "threshold": "pass_all | pass_N", + "cap": 4 +} +``` + +**Rules:** +- Criteria must be falsifiable — "no jargon visible to end-users", not "improve clarity". +- Maximum 8 criteria. If the goal implies more, merge related ones. +- Criteria are **locked** after Wave 1. The evaluator may not add or modify them. + +**Exit:** Emit `criteria.json`. Proceed to Wave 2. + +--- + +## Wave 2 — Evaluation (stateless) + +**Agent:** Single subagent, spawned fresh with **no generation history**. Receives: +- Current artifact (text only) +- Locked `criteria.json` + +**Produces:** + +```json +{ + "scores": [ + { "criterion_id": "C1", "pass": true, "gap": "" }, + { "criterion_id": "C2", "pass": false, "gap": "" } + ], + "overall": "pass | fail", + "blocking_gaps": ["C2: ", ...] +} +``` + +**Rules:** +- If `overall: pass` (threshold met) → skip Wave 3, emit final artifact, terminate loop. +- If iteration count == cap → skip Wave 3, emit best version + `remaining_gaps`, terminate loop. +- Gap text must be specific enough for Wave 3 to act on without re-reading evaluator reasoning. + +--- + +## Wave 3 — Revision + +**Agent:** Single subagent. Receives: +- Current artifact +- `blocking_gaps` list (text only — NOT the evaluator's full `scores` object) +- Original `goal` (for orientation only, not as new criteria) + +**Produces:** Revised artifact (full text, not a diff). + +**Rules:** +- Address only the `blocking_gaps`. Do not make unrequested changes. +- Do not invent new criteria or second-guess passing scores. +- Output is the next artifact fed into Wave 2. + +--- + +## Loop Control + +``` +iteration = 0 +artifact = + +Wave 1 → criteria.json + +loop: + iteration += 1 + result = Wave 2(artifact, criteria.json) + if result.overall == "pass": + emit artifact, result.scores, iteration_count + DONE + if iteration == cap: + emit artifact, result.remaining_gaps, "CAP_REACHED" + DONE + artifact = Wave 3(artifact, result.blocking_gaps) +``` + +--- + +## Outputs + +| State | Emitted | +|---|---| +| Converged | Final artifact + `{status: "CONVERGED", iterations: N, scores: [...]}` | +| Cap reached | Best artifact + `{status: "CAP_REACHED", iterations: cap, remaining_gaps: [...]}` | + +`CAP_REACHED` is not a failure — it is an explicit signal for the caller to extend, escalate, or ship with documented gaps. The orchestrator never silently discards gap information. + +--- + +## Composition + +`polish` is designed to run *after* a generative skill and *before* `ship`: + +``` +spec → forge → polish → ship +research → mint → polish → ship + → polish → ship +``` + +It may also be called standalone on an existing artifact with explicit `criteria` supplied. \ No newline at end of file diff --git a/skills/provideme/SKILL.md b/skills/provideme/SKILL.md index 184c7e5..782413e 100644 --- a/skills/provideme/SKILL.md +++ b/skills/provideme/SKILL.md @@ -5,7 +5,7 @@ argument-hint: "" --- ## Sub-agent contract -!`awk ‘/^---$/{c++; next} c>=2’ "${CLAUDE_SKILL_DIR}/../contract/SKILL.md"` +/contract Dispatch parallel sub-agents: 1. **Research agent**: investigate the $ARGUMENT provider CLI — installation, invocation syntax, streaming support, output format, authentication/config requirements. diff --git a/skills/refactor/SKILL.md b/skills/refactor/SKILL.md new file mode 100644 index 0000000..f899134 --- /dev/null +++ b/skills/refactor/SKILL.md @@ -0,0 +1,155 @@ +--- +name: refactor +description: "Orchestrates safe, large-scale structural changes across a codebase — symbol renames, API migrations, pattern standardizations, layer restructurings. Enumerates all affected sites, groups them into dependency layers via the DAG executor, applies changes in parallel per layer with worktree isolation, and verifies behavioral preservation at each layer boundary before proceeding. Exits immediately on first regression, surfacing the exact site, diff, and behavioral delta before any commit." +argument-hint: " [--scope ]" +failure_modes: + - bad decomposition + - dependency blindness + - false completeness + - premature execution +context: fork +--- + +## Sub-agent contract +/contract + +**Skip when:** the change touches ≤3 files and no dependency ordering is required — just edit directly. Also skip when the codebase has no test coverage at all (behavioral preservation cannot be verified; ask the user to add tests first or proceed manually). + +--- + +### Triage (inline, before dispatching any sub-agent) + +Parse `$ARGUMENT` to extract: +- **Change type**: rename | api-migration | pattern-standardization | layer-restructure | dependency-upgrade | other +- **Target symbol / path / pattern** (what is changing) +- **Destination** (what it becomes) +- **Scope**: default to the entire repo; narrow if `--scope` is provided + +If `$ARGUMENT` is ambiguous on any of the above, stop and ask exactly one question. + +--- + +### Wave 1 — Scope enumeration (parallel, `subagent_type: "research-agent"`) + +Dispatch both agents simultaneously in a single response turn. + +**Site-finder agent** +- goal: Enumerate every file and symbol that must change for the refactor to be complete and correct. +- inputs: change type, target symbol/pattern, scope glob +- artifacts: + - `sites`: array of `{file, line_range, site_type: "definition"|"import"|"usage"|"type-ref"|"test", change_required: string}` + - `dependency_graph`: for each site, which other sites must be changed first (a site depends on its callers if it exports a symbol; a definition must be changed before its imports) + - `site_count`: integer + - `confidence`: low | medium | high + - `coverage_gaps`: what the agent couldn't search (generated files, third-party vendored code, etc.) +- non_goals: Do not apply any changes. Do not read unrelated files. +- failure_modes: If grep tooling is unavailable, return `confidence: low` and list what was searched manually. + +**Contract-extractor agent** +- goal: Identify the public interfaces and test commands that verify behavioral preservation for the affected API surface. +- inputs: site list (from site-finder, passed inline if available; if not yet available, derive from change type + scope) +- artifacts: + - `contracts`: array of `{symbol, signature_before, exported_by, consumed_by[]}` + - `test_commands`: array of shell commands that SCOPE to the affected contracts — target the specific test files or a name pattern, never the whole suite (e.g., `pnpm test src/auth/auth-service.test.ts` or `pnpm test -t "AuthService"`). Under pnpm, `pnpm test -- ` drops the file arg and runs the entire suite — never emit that form. + - `test_coverage_verdict`: "adequate" | "partial" | "absent" + - `confidence`: low | medium | high + - `coverage_gaps`: test paths not reachable by the identified commands +- non_goals: Do not run tests. Do not read unrelated files. + +**Scope gate (after Wave 1):** +- `site_count > 50` → warn the user: "This refactor touches N sites. Recommend scoping down with `--scope` or batching via `/parallelize`. Proceeding, but each layer will take longer." +- `test_coverage_verdict == "absent"` → hard stop: "No tests cover the affected contracts. Behavioral preservation cannot be verified. Add tests before proceeding, or explicitly confirm you accept unverified risk." +- Either agent returns `confidence: low` → surface the coverage gaps and ask the user to confirm before proceeding. + +--- + +### Synthesize: Build the layer map (inline) + +From `dependency_graph`, compute a topological sort into layers using Kahn's algorithm: +- **Layer 0** — leaf sites with no dependents (definitions that nothing imports, or sites that depend on nothing else changing first) +- **Layer N** — sites whose dependencies are all in layers 0…N-1 + +If the graph has cycles (rare but possible in circular-import codebases), surface them explicitly: "Cycle detected between [A, B, C]. Manual intervention required before automation can proceed." Do not continue. + +--- + +### Wave 2 — Layer-by-layer application (sequential between layers, parallel within each layer) + +For **each layer** (starting at Layer 0): + +Dispatch one sub-agent per site in this layer, all in parallel, each in a worktree-isolated environment: + +**Layer applicator agent** (per site, `isolation: "worktree"`) +- goal: Apply the transformation at exactly this site and verify it passes its targeted tests. +- inputs: site descriptor `{file, line_range, change_required}`, full transformation spec (target → destination), test commands that cover this site +- artifacts: + - `site`: `{file, line_range}` + - `status`: "green" | "red" | "skipped" + - `diff_applied`: the exact unified diff applied (≤30 lines; truncate with `...` if longer) + - `test_output`: truncated stdout of the targeted test run (last 20 lines) + - `failure_reason`: populated only if `status == "red"` — the first failing assertion + the file:line where it fired +- non_goals: Do not change any file other than the one in your site descriptor. Do not run the full test suite. +- IMPORTANT: this sub-agent MAY call `edit_file` and run bash commands; `isolation: "worktree"` is required. + +**Layer gate — hard stop before proceeding to the next layer:** +- All sites in this layer `status == "green"` → proceed to the next layer. +- Any site `status == "red"` → **abort the entire refactor immediately**. Do NOT apply any more changes. Surface: + - The failing site (`file:line_range`) + - The `diff_applied` for that site + - The `failure_reason` + - The current layer number and which layers (if any) already completed successfully + - Recommendation: "Revert layer N changes manually or run `git stash` in each affected worktree, then diagnose with `/diagnose`." + +--- + +### Wave 3 — Behavioral diff verification (after all layers green, `subagent_type: "research-agent"` or Bash-capable) + +Dispatch a single behavioral-diff agent: + +**Behavioral-diff agent** (`isolation: "worktree"`, Bash-capable) +- goal: Run the full test suite and compare observable behavior at the API boundary before vs. after. +- inputs: `test_commands` from the contract-extractor, plus any project-wide test command (inferred from `package.json`, `Makefile`, etc.) +- artifacts: + - `full_suite_status`: "all-green" | "failures" + - `failing_tests`: array of `{test_name, file, failure_reason}` (empty if all-green) + - `behavioral_deltas`: array of `{symbol, before_behavior, after_behavior, expected: true|false}` — note only deltas, not noise + - `unexpected_deltas`: subset of `behavioral_deltas` where `expected == false` + - `confidence`: low | medium | high +- non_goals: Do not commit. Do not push. + +**Post-Wave 3 decision:** +- `full_suite_status == "all-green"` AND `unexpected_deltas` is empty → proceed to synthesis, recommend commit. +- `full_suite_status == "failures"` OR `unexpected_deltas` is non-empty → surface the unexpected deltas for human review. Do NOT commit. Recommend: "Review unexpected behavioral changes before committing. If intentional, update tests to reflect new contracts. If not, route to `/diagnose`." + +--- + +### Synthesis: Change report + +Emit a structured change report: + +``` +## Refactor Report + +**Goal:** + +**Scope:** +- Sites enumerated: N +- Layers processed: M (of M total) +- Status: COMPLETE | ABORTED AT LAYER N + +**Changes applied:** +| File | Lines | Type | Status | +|------|-------|------|--------| + +**Tests run:** +- Targeted (per-site): N runs, N green, N red +- Full suite: all-green | N failures + +**Behavioral deltas:** +- Expected: N +- Unexpected: N (see below if non-zero) + +**Recommendation:** COMMIT | REVIEW REQUIRED | ABORT +``` + +If recommendation is COMMIT, offer to invoke `/ship` for the final commit + PR. If REVIEW REQUIRED or ABORT, do not invoke `/ship` and do not commit. diff --git a/skills/research/SKILL.md b/skills/research/SKILL.md index 02acce9..91466f9 100644 --- a/skills/research/SKILL.md +++ b/skills/research/SKILL.md @@ -1,9 +1,34 @@ --- name: research description: "Dispatches two sub-agents in parallel to gather external and local context for the current task." +context: load --- ## Sub-agent contract /contract -Dispatch two sub-agents in parallel. One sub-agent researches the web for external context relevant to the current task. The other inspects the current directory for local code, files, docs, patterns, and constraints relevant to the current task. Return a concise merged research brief highlighting relevant findings, conflicts, risks, and implications for the task. +Dispatch two sub-agents in parallel using the Agent tool. Prefer `subagent_type: "research-agent"`; fall back to `subagent_type: "Explore"` with thoroughness "very thorough" if the research-agent is not available. One researches the web for external context relevant to the current task. The other inspects the local working directory for domain-relevant artifacts. Return a concise merged research brief highlighting relevant findings, conflicts, risks, and implications for the task. + +**Web research agent** — always the same: search for external context, prior art, patterns, APIs, and comparable approaches relevant to the task. Domain-agnostic. + +**Local inspection agent** — adapt to the domain: + +| Domain | What to inspect | +|--------|----------------| +| `software` | Code, config files, package manifests, CI configs, test suites, git history, README/docs, existing patterns and conventions | +| `research` | Papers (PDF/LaTeX), notes, data files, citation databases (.bib), lab notebooks, analysis scripts, prior drafts | +| `design` | Design files (Figma exports, SVGs, mockups), brand guidelines, component libraries, user research docs, style guides | +| `business` | Financial models, strategy docs, market research, pitch decks, competitive analyses, KPI dashboards, stakeholder maps | +| *(other)* | Scan the working directory for any files relevant to the stated domain — documents, data, config, scripts — and describe what you find | + +When domain is unspecified, infer it: git repo → software; PDFs/LaTeX/.bib → research; design assets → design; spreadsheets/decks → business. If ambiguous, inspect broadly and note what you found. + +## Coverage reporting + +Both agents must end their response with a coverage assessment: + +- **Coverage confidence**: low / medium / high — how thoroughly could this domain be searched? +- **Known gaps**: what couldn't be accessed? (proprietary databases, paywalled papers, unpublished work, practitioner-only knowledge) +- **Tacit knowledge risk**: low / medium / high — is this a domain where critical knowledge is unwritten or not documented online? + +When merging results, surface coverage gaps prominently. If both agents report low coverage, flag: "Low epistemic coverage — findings may be incomplete. Consider consulting domain practitioners or providing access to private sources." diff --git a/skills/resolve/SKILL.md b/skills/resolve/SKILL.md index 79e2102..d74ca54 100644 --- a/skills/resolve/SKILL.md +++ b/skills/resolve/SKILL.md @@ -3,6 +3,24 @@ name: resolve description: "Resolves PR code review feedback. Use when the user asks to fix, address, or resolve issues from a code review or PR review." --- +## PR state pre-flight + +Before doing anything else, run: + +``` +gh pr view --json state,mergedAt,headRefName,baseRefName,mergeCommit +``` + +**If `state === "MERGED"`:** STOP. Do not investigate issues, do not edit files. Report to the user: + +> PR #N is already merged (merged at ``, commit `` into ``). Pushing fixes to the original branch would orphan them — the merged code on `` would remain unchanged. +> +> Recovery path: cherry-pick the fix commit(s) to a fresh branch off `origin/` and open a new PR. Offer to do this automatically if the user confirms. + +**If `state === "CLOSED"` (no `mergedAt`):** STOP. Report the PR is closed without merge. Ask the user whether they want to reopen it, branch off its head, or branch off `main` with a new PR. + +**If `state === "OPEN"`:** proceed below. + ## Sub-agent contract /contract @@ -12,4 +30,4 @@ Using parallel sub-agents – one per issue – for each of the issues pointed o 2. If valid, identifies the exact location and proposes the minimal fix (but does not apply it) 3. Reports back: issue summary, validity verdict, proposed fix, and any risks -When all sub-agents return, apply the fixes sequentially. Then run the full test suite. If green, create a single commit with a message summarizing all resolved issues, then push to update the PR. \ No newline at end of file +When all sub-agents return, apply the fixes sequentially. Then run the full test suite. If green, **before pushing**, re-run the same `gh pr view` check — the PR may have merged during the work. Apply the same MERGED / CLOSED / OPEN logic. If still OPEN, create a single commit with a message summarizing all resolved issues, then push to update the PR. \ No newline at end of file diff --git a/skills/review/SKILL.md b/skills/review/SKILL.md new file mode 100644 index 0000000..f32b436 --- /dev/null +++ b/skills/review/SKILL.md @@ -0,0 +1,171 @@ +--- +name: review +description: "Dispatches parallel dimension agents across a diff, PR (URL or number), commit SHA, branch, staged changes, or patch file — covering security, correctness, api-compat, test-coverage, and perf-observability — synthesizes findings by severity, and emits a merge recommendation. Use when changes are ready for review before merge. Read-only: this skill analyzes and reports only — it never edits files, commits, pushes, comments on a PR, or modifies the PR description." +argument-hint: "[diff|pr-url|pr-number|commit-sha|branch|--staged|--head] [--light] [--change-type hotfix|feature|refactor|dep-bump|new-service] [--post github|telegram] [--brief |--spec ]" +context: load +--- + +## Read-only — hard constraint + +This skill **analyzes and reports**; it never mutates the repository, the PR/MR, or anything external. After you emit the merge recommendation, **STOP**. + +Never — not for a real bug, not for a blocking defect, not even when there is no human reviewer and "someone has to fix it": +- edit, create, or delete files (no `write`/`edit`-style mutations); +- `git add` / `commit` / `stash` / `reset`, `git checkout` to discard changes, or `git push`; +- `gh pr comment` / `review` / `edit` / `merge` / `create`, or post or edit any PR/MR body, comment, or description; +- run any other write- or network-mutating shell command. + +The only shell permitted is **read-only inspection**: `git diff` / `git show` / `gh pr diff`, `grep` / `rg`, and file reads — plus dispatching the review sub-agents. Resolving findings, fixing bugs, resolving merge conflicts, and "making the branch mergeable" are explicitly **out of scope**: a fixable defect is a finding to report (`file:line` + a one-line fix in the `suggestion` field), never a license to act. + +## Sub-agent contract +/contract + +**Skip for:** lock files (`package-lock.json`, `go.sum`, `yarn.lock`), auto-generated files (`*.generated.*`), pure-docs diffs, vendored deps. + +**Resolve target → diff (inline).** The review target argument is: `$ARGUMENT` (empty = review working-tree/HEAD changes). Map this argument to a diff source, then capture the diff text plus a one-line target descriptor for the triage header. Also capture the **reviewed ref** (branch HEAD SHA or equivalent) — this is required for citation verification later: + +- `--staged` → `git diff --staged`; reviewed ref = `git write-tree` (snapshots the staged index to a throwaway tree so citations resolve against the staged content under review, not HEAD) +- `--head` or no arg → `git diff HEAD`; reviewed ref = `git stash create` (snapshots worktree + index to a throwaway commit so citations resolve against the content under review; empty output = no local changes → fall back to `git rev-parse HEAD`) +- arg matches `^https?://.*/pull/\d+` (GitHub/GitLab PR URL) → `gh pr diff ` (or `glab mr diff`); reviewed ref = head SHA from `gh pr view --json headRefOid -q .headRefOid`; record PR title + base/head refs +- arg matches `^#?\d+$` (bare PR number, optionally `#`-prefixed) → resolve in current repo with `gh pr diff `; reviewed ref = head SHA from `gh pr view --json headRefOid -q .headRefOid`; if `gh` is unavailable or repo has no PR matching, abort with `Asking` (one question: which repo/PR) +- arg matches `^[0-9a-f]{7,40}$` (commit SHA) → `git show `; reviewed ref = `` +- arg matches a known ref (`git rev-parse --verify ` succeeds) → `git diff ...` against the repo's default branch; reviewed ref = `git rev-parse ` +- arg is a path or `*.diff`/`*.patch` file → read file contents as the diff; reviewed ref = `unknown (patch file — no live ref available)` +- otherwise → abort with `Asking` naming the ambiguous arg + +**Capture stated intent (inline).** A reviewer that sees *what changed* but not *what it was meant to accomplish* cannot judge whether the change does its job — it silently redefines "the spec" as whatever the diff or the repo's global constraints imply, and rubber-stamps. Capture the change's stated intent into a `stated-intent` field passed to every agent: +- `--brief ""` / `--spec ` supplied → use that text / file contents verbatim (highest priority). +- PR URL or number → `gh pr view --json title,body -q '.title + "\n\n" + .body'` (or `glab mr view`); title + description are the intent. +- commit SHA → the full commit message: `git show -s --format=%B `. +- known ref / branch → the branch's PR body if one exists (`gh pr view --json body -q .body`), else the commit subjects: `git log --format=%s ..`. +- `--staged` / `--head` / working-tree / patch-file with no `--brief` → set `stated-intent = "(none supplied)"`. + +Never fabricate intent. When none is available the value is the literal `(none supplied)`; agents disclose its absence rather than guess. + +**Pre-fetch file contents at reviewed ref (inline).** After capturing `reviewed_ref` and the diff, and before dispatching any Wave 1 agent, the orchestrator (which has Bash) pre-reads every changed file at the reviewed ref and bundles the results for injection into sub-agent prompts. This is required because Wave 1 and Wave 1.5 agents are `research-agent` instances with no shell — they cannot run `git show` themselves. + +1. Extract the list of changed files from the diff: `git diff --name-only ..` (or parse `--- a/` / `+++ b/` headers from a patch-file diff). +2. For each file, run `git show :` and capture the full output. If the command fails (file does not exist at that ref — e.g. the file was added and the ref predates it, or the ref is `unknown` for a patch-file input), skip that file and note it as `unavailable at ref`. +3. Bundle as a **`prefetched-files`** block: `[{ file, content, status: "ok"|"unavailable" }]`. +4. Include this block verbatim in every Wave 1 and Wave 1.5 agent prompt alongside the diff. Agents verify `file-state` citations against the injected content — they do **not** call `read_file` for ref-anchored verification (the working tree may be on a different branch). + +When `reviewed_ref` is `unknown` (patch-file input), skip pre-fetch entirely and note `prefetched-files: none — patch-file input`. Agents fall back to `diff-context` citations only and tag any `file-state` citation `[UNVERIFIED: no live ref]`. + +**Triage (inline).** From the resolved diff extract: change type (hotfix | feature | refactor | dep-bump | new-service), files changed, total lines changed, summary. Classify regime: `light` if ≤300 lines or change type is hotfix/dep-bump; `full` otherwise. + +**Concurrency floor — declared, conditional, and enforced.** *Through synthesis*, a full-regime review peaks at **2 concurrent sub-agent sessions** (Wave 1's two dimension agents) and dispatches **3 in total** (Wave 1 ×2, then Wave 2 ×1, sequential). Wave 1.5 runs inline in the orchestrator and dispatches nothing. **No wave nests a child**: the sub-agents are shell-less by design, and nothing in this skill requires them to run a command, so none of them needs to nest a `git-investigator` to comply. If you add a requirement here that needs a shell, you have silently doubled this floor — put that requirement in Wave 1.5 instead. + +**The post-synthesis tail is the conditional half of that budget.** A review that surfaces a `critical`/`high` finding (or one whose `blocking` value departs from the default table — see **Post-synthesis** below) invokes `/shadow-verify`, which dispatches one verifier per claim in parallel, so the whole-run budget is **peak 2–3 concurrent, 4–6 total (1–3 verifiers)**, and it lands on exactly the high-stakes reviews most likely to hit a rate ceiling. Bound it: **at most 3 claims in a single round, no repeat rounds**, and hand the verifiers Wave 1.5's manifest so each re-derives the *claim* instead of re-locating evidence Wave 1.5 already pinned at the ref. Wave 1.5 verifies that a citation is real; shadow-verify re-derives whether the inference drawn from it holds — never substitute one for the other. + +**Wave 1 — Full review (regime=full, 2 parallel agents, `subagent_type: "research-agent"`).** Dispatch: +- **security · api-compat** — contracts, auth, injection, breaking changes, secret exposure. +- **correctness · spec-compliance · test-coverage · perf-observability** — logic bugs, regressions, whether the change satisfies its **stated intent** (unmet requirement or unrequested scope creep), missing tests, hot-path perf, logging gaps. + +Each agent receives: full diff + file tree + triage header + **reviewed ref (SHA)** + the **stated intent** (what the change is meant to accomplish, or `(none supplied)`), the severity rubric, **the `blocking` default table plus its overrides and assignment-order invariant**, and the finding schema. The blocking rules are not optional context: the finding schema mandates a `blocking` value per finding, so an agent that receives the schema without the table is being told to emit a field whose assignment rules it was never given. + +**Citation requirement (enforced per agent).** Wave 1 agents cite from the diff and from the **`prefetched-files` block injected by the orchestrator**. They do **not** call `read_file` for ref-anchored verification (the working tree may be on a different branch) and do **not** run git. Centralized verification runs in Wave 1.5, which checks every `blocking`/`critical`/`high` citation **and every `file-state` citation at any severity** against the same pre-fetched content, then drops fabricated ones. Each agent must: +1. State the reviewed ref it was given in each finding: `ref: `. +2. Classify every citation as `diff-context` (line visible in the diff hunk) or `file-state` (line in the post-merge file, not visible in the hunk). For `file-state` citations, look up the line in the injected `prefetched-files` content. If the file's status is `unavailable` in the bundle, tag the citation `[UNVERIFIED: file unavailable at ref]` so Wave 1.5 knows it could not be pre-fetched. +3. Never paraphrase or reconstruct a line from memory. If the line is not visible in the diff and not present in the pre-fetched content, cite it as `file-state [UNVERIFIED: file unavailable at ref]` — do not invent the content. + +**Invariant — why Wave 1 does not run git.** `research-agent` has no shell (`tools: Read, Grep, Glob, WebFetch, WebSearch, Agent(git-investigator)` — no `Bash`, and that single `Agent(...)` entry is the nesting path this rule closes), so a mandatory `git show` forces it to dispatch a nested `git-investigator` purely to run one command. That doubles the concurrent session count of *every* Wave 1 agent, and is the structural cause of the rate-limit cascade in #726. Ref-anchored verification is therefore performed once, centrally, by a shell-capable actor — never N times by shell-less ones. Do not reintroduce a per-agent re-read here. + +Banned words: "ensure", "consider", "may", "could". No `file:line` citation → omit the finding. + +**Spec-compliance assessment (mandatory framing for the spec-compliance dimension).** Judge the diff against the `stated-intent` — not against the diff's own apparent goals, and not against the repo's global constraints: +- `stated-intent` present → flag (a) **unmet intent**: a requirement named in the intent with no implementing change, and (b) **scope creep**: a substantive behavior change the intent does not call for. Cite the unmet clause for gaps; cite `file:line` for creep. +- `stated-intent` is `(none supplied)` → do **not** assess spec-compliance and do **not** substitute the global constraints for the spec. Emit exactly one line: `unverified — spec-compliance not assessed: no stated intent supplied (pass --brief/--spec, or review a PR/commit)`. Silently treating the diff or the constraints as "the spec" is the precise failure this rule prevents. + +**api-compat reachability pre-check (mandatory before surfacing any breaking-change finding).** +For every symbol flagged as a breaking change, search production source files with the `Grep` tool — which needs no shell, so this check never forces a nested dispatch — for imports or usages of that symbol, excluding `*.test.*`, `*.spec.*`, `__tests__/`, `__mocks__/`, `/test/`, `/tests/`. Decision table: +- Zero production importers → downgrade finding to `nit`, append `[UNVERIFIED: no production importers]`, set confidence `low`. +- One or more production importers → severity stands; include one importer path as evidence. + +If the `Grep` tool is unavailable, tag the finding `[UNVERIFIED: reachability not checked]` and downgrade one severity tier. + +**Absence-claim grounding (mandatory for any claim that something does not exist).** Before emitting a finding of the form "no test covers X", "no handler validates Y", "no caller invokes Z", "X is not tested": search the production tree with the `Grep` tool — which needs no shell, so this check never forces a nested dispatch — for plausible match strings. Decision table: +- Zero matches → finding stands; cite the search pattern in the evidence field. +- One or more matches → emit `unverified — absence claim refuted by :` instead of the finding. +- Grep tooling unavailable or claim cannot be reduced to a pattern → tag finding `[UNVERIFIED: absence not checked]` and downgrade one severity tier. + +This is the agent's first-line self-check; **Wave 1.5 Check B** independently re-verifies any surviving absence claims against the reviewed ref as a backstop. + +**Wave 1 — Light review (regime=light, 1 agent, `subagent_type: "research-agent"`).** Single agent covers all dimensions (including spec-compliance). Same `stated-intent` input, rubric, schema, and citation requirement. Through synthesis, the light regime peaks at **1 concurrent sub-agent session** and dispatches **2 in total** (Wave 1 ×1, then Wave 2 ×1, sequential). Its conditional post-synthesis `/shadow-verify` tail dispatches 1–3 verifiers in parallel when qualifying findings surface, making the whole-run budget **peak 1–3 concurrent, 3–5 total (1–3 verifiers)**; the same bound of at most 3 claims in one round with no repeat rounds applies. + +**Wave 1.5 — Citation + absence-claim verification (INLINE — run by the orchestrator, dispatches nothing).** Run after Wave 1 returns, before Wave 2 synthesis. The orchestrator already holds exactly the read-only shell this verification needs (`git show` / `git diff` / `gh pr diff` / `grep` / `rg` — see the shell grant above), so running it inline costs **zero** additional sessions and zero nesting. A shell-less sub-agent here would have to nest a `git-investigator` to run the very commands the orchestrator can already run. Two independent checks: + +**Check A — Citation verification.** Extracts (a) every `file:line` citation from any `blocking`, `critical`, or `high` finding, **and (b) every citation tagged `file-state` at any severity** — `medium`, `low`, and `nit` included — across all Wave 1 results. Both sets are checked, because Wave 1 never re-reads at the ref independently: an unverified `file-state` citation in a `low` finding is exactly as fabricable as one in a `high` finding, and quoting a line absent from the reviewed change is a defect at every tier. For each citation, looks up the file in the **`prefetched-files` block** (already captured by the orchestrator before Wave 1 ran) and checks whether the quoted evidence snippet actually appears at that line — not in main, not in diff context alone. If the file's status is `unavailable` in the bundle, falls back to `git show :` (the orchestrator has shell). Classifies each citation as: +- `verified` — content matches what is actually at that line on the reviewed ref. +- `diff-only` — line appears in the diff hunk but no longer exists at the reviewed ref HEAD (e.g., deleted block). Finding must be downgraded: the issue may already be resolved. +- `fabricated` — line does not exist at the reviewed ref and was not in the diff hunk; the evidence snippet is unverifiable. Finding is **dropped** from the report. + +**Check B — Absence-claim verification.** Extracts every **absence claim** across all Wave 1 results **at any severity** — `medium`, `low`, and `nit` included, for the same reason Check A checks every `file-state` citation: Wave 1's absence grounding is a self-check by an agent that never re-read at the ref, so an unverified absence claim in a `low` finding is exactly as fabricable as one in a `high` finding. These are claims of the form "no test covers X", "no handler validates Y", "no caller invokes Z", "X is not tested", "Y has no validation". Citations are not required for absence claims, so Check A cannot catch them; they need their own gate. For each, identify the asserted-absent symbol, test name, or behavior, then run `git grep -n ` (or `rg --no-heading -n ` if outside a git context) across the production tree (exclude the same paths as the api-compat reachability check: tests, mocks, fixtures, when the claim is about production code). Classify as: +- `confirmed-absent` — zero matches in the asserted scope; finding stands. +- `false-absent` — one or more matches in the asserted scope; finding is **dropped** (the asserted-absent entity exists at the reviewed ref). Name the matching path(s) in the dropped-findings manifest. +- `grep-unavailable` — tooling missing, symbol ambiguous, or absence claim cannot be reduced to a grep pattern; finding tagged `[UNVERIFIED: absence not checked]` and downgraded one severity tier. + +Returns a combined verification manifest: `[{type: citation|absence, claim, status, finding_id, evidence?}]`. Findings classified `fabricated` (citation) or `false-absent` (absence) are excluded from Wave 2 input. `diff-only` citations are passed to Wave 2 with a `⚠ diff-only citation — line absent at the reviewed ref` annotation and auto-downgraded one severity tier. `grep-unavailable` absence claims are passed through with their `[UNVERIFIED]` tag intact. + +**Wave 2 — Synthesis (1 agent, `subagent_type: "research-agent"`).** Receives: Wave 1 findings **after** citation-verification filtering + manifest of dropped/downgraded citations + **the merge-decision rule and its counts format below**. Wave 2 emits the verdict, so it needs that rule for exactly the reason Wave 1 needs the blocking table: an agent told to produce an output whose format and threshold it was never given will improvise both. Dedup by `(file, line_range, dimension)` — keep highest severity on exact match. Flag cross-agent conflicts as `CONFLICT` blocks (surface both rationales; do not auto-resolve). + +**Severity sort order within the blocking list:** findings tagged with semantics matching `invariant violation`, `defeats stated purpose`, `defeats refactor goal`, or `breaks stated contract` sort above all other `high` findings, even those with higher mechanical severity (e.g. test/build hygiene). Within that group, sort by tier (critical → high). Mechanical findings (missing test, build hygiene) sort last within their tier. + +Sort overall: critical → high → medium → low → nit; security first within tier; semantic/invariant findings above mechanical findings within tier. Template-fill summary block. + +**Merge-decision rule (mandatory — do not improvise a threshold).** + +Severity and disposition are **separate axes**. `severity` answers "how bad is this defect?" — it is a property of the finding. `blocking` answers "does this prevent merge?" — it is policy. Never let one silently encode the other. + +**Wave 1 assigns** an explicit `blocking: true|false` to every finding it emits, from this default table. Wave 2 carries each value through unchanged and never re-derives it — synthesis dedups and sorts, it does not re-adjudicate disposition: + +| severity | default `blocking` | +|---|---| +| `critical` | true | +| `high` | true | +| `medium` | true | +| `low` | false | +| `nit` | false | + +**Overrides (each requires a one-clause justification appended to the finding):** +- A `medium` may be marked `blocking: false` when it is a bounded, non-data-affecting defect the author can reasonably land and follow up — e.g. a rare-input formatting error with no downstream consumer. +- A `medium` in the `security` dimension is **never** overridable to `false`; today's narrow reachability is tomorrow's incident. +- A `medium` representing a material data-integrity risk or a likely production failure under normal usage is **never** overridable to `false` — a race that intermittently loses user state stays blocking even when its blast radius keeps it out of `high`. +- A `low` or `nit` may be marked `blocking: true` only for a stated external constraint (release gate, compliance requirement). Do not use this to smuggle a preference. + +**Invariant — assignment order.** `blocking` is assigned from the **pre-downgrade** severity. A finding later downgraded by **any** downgrade rule in this file — the api-compat reachability rule (which drops straight to `nit`, two tiers in one step), its grep-unavailable fallback, Wave 1's absence-grounding fallback, Wave 1.5's `diff-only` citation rule, Wave 1.5's `grep-unavailable` absence rule, or the confidence rule below — **keeps the `blocking` value its pre-downgrade severity earned**: a downgrade lowers severity, never disposition. Only an explicit, justified override from the list above may flip `blocking`. Without this ordering, a security or data-integrity `medium` would silently become non-blocking by being downgraded rather than waived, defeating the two never-overridable rules above through a path that requires no justification at all. + +A `blocking: true` that survives a downgrade this way is **not** an override and needs no justification clause: it carries `· blocking preserved from pre-downgrade ` instead, and the `low`/`nit` external-constraint rule above does not apply to it. Without this exemption the invariant and that rule contradict each other — every downgraded `medium` would land as a `low`/`nit` carrying `blocking: true` with no admissible reason to write, forcing the reviewer to either fabricate an external constraint or emit a schema-violating finding. + +Emit **DO NOT MERGE** when one or more findings carry `blocking: true` after Wave 1.5 filtering. Emit **MERGE** only when every surviving finding is `blocking: false`. + +State the counts that drove the decision on the same line, **with a dimension breakdown for any blocking medium**, e.g. `Decision: DO NOT MERGE — 1 high, 2 medium blocking (1 security, 1 correctness); 1 medium waived, 3 low.` or `Decision: MERGE — 0 blocking (2 medium waived, 3 low, 1 nit).` If zero findings survived, say `Decision: MERGE — 0 findings.` Never emit a bare verdict with no counts, and never waive a finding silently — a waived medium must appear in the count with its justification. + +This is the terminal step — after emitting the decision, STOP. Do not act on any finding: no edits, commits, pushes, or PR/MR mutations. A blocking bug is a finding to report, not a fix to apply. + +**Severity rubric (impact axis only — severity measures blast radius and reachability, never category):** +- `critical` — data loss, auth bypass, secret exposure, RCE. If it cannot cause unauthorized access or data loss, it is NOT critical. +- `high` — produces wrong output or an unsafe state under reachable conditions (reachable = called from production code, not tests-only) +- `medium` — produces wrong output or an unsafe state, but only under narrow, rare, or hard-to-reach conditions +- `low` — does not affect production behavior today. **Absent an impact claim**, these land here: missing test, unclear error message, doc/PR-body mismatch, dead code, stale comment, deprecated API with no removal date, perf concern with no load evidence. An instance that *does* carry an impact claim re-homes upward per the rules below — no category pins a finding to this tier, and a `security`-dimension finding is never parked here just because its category appears in this list +- `nit` — naming, formatting; no behavioral claim at stake + +**A category is never a tier by itself.** Re-home by impact, not by kind: +- "missing edge case" → `high` if reachable in production and wrong; `medium` if reachable but rare; `low` if only reachable from tests. +- "perf degraded under load" → `high` if unbounded or production-breaking; `medium` if bounded but measured; `low` if theoretical with no load evidence. "Measured" does not require running a benchmark — Wave 1 is shell-less by design — so a load number in the **stated intent**, or a committed benchmark or perf test `Read` from the diff or repo, qualifies. +- "deprecated API" → `low` by default; `high` only when a hard removal date will break a production call path. + +Confidence `low` → auto-downgrade one tier + append `[low confidence — verify with runtime context]`. + +**Output per dimension:** if you have read the relevant file(s) and have either real findings or a confirmed clean read, emit findings or `no issues found — read `. If evidence is insufficient — you could not read the file, the tool was unavailable, no production importers were found for the symbol, or no test file exists at the asserted path — emit `unverified — ` naming the missing evidence rather than invent a finding to fill the slot. Banned words from the hedging list (`ensure`, `consider`, `may`, `could`) remain banned **inside findings**; the `unverified` channel is the sanctioned path for uncertainty. + +**Finding schema:** `severity · blocking:(true|false) · confidence · dimension · file:line_range · ref: · citation-type:(diff-context|file-state) · finding (one concrete sentence naming the failure mode) · evidence (verbatim code ≤4 lines) · suggestion (one concrete fix)`. When `blocking` departs from the default table, append `· waived: ` (or `· escalated: `) naming the reason. + +**Epistemic scope disclosure (required in synthesis output).** The "What was not checked" section must include: +- Which ref citations were verified against in Wave 1.5 (list the SHA or `unknown` if patch-file input). Example: `Citations verified inline against branch HEAD abc1234.` +- If any citations could not be verified against a live ref (patch-file input): `Citation verification skipped — no live ref available; diff-context citations only.` +- Any topical gaps (e.g. 'did not review Telegram surface', 'did not run tests'). +- Whether a **stated intent** was available and spec-compliance was assessed. Example: `Stated intent: PR #123 title+body — spec-compliance assessed.` or `Stated intent: (none supplied) — spec-compliance not assessed.` + +**Post-synthesis:** if any `critical` or `high` finding is present, **or any finding whose `blocking` value departs from the default table** (a waived `medium`, an escalated `low`/`nit`), invoke `/shadow-verify` on those findings before surfacing to the user. Shadow-verify independently re-derives each claim against source; fabricated or unsupportable findings drop here before they reach the merge decision. An overridden finding is routed because the agent that found it also set its disposition and wrote its own justification — the waiver is otherwise the only judgement in this pipeline with no second reader. `medium` and below **at their default disposition** go straight through. + +The concurrency floor's bound still holds — **at most 3 claims in a single round, no repeat rounds**. When critical/high plus overridden findings exceed 3, verify the **overridden ones first**: an unreviewed waiver silently removes a blocker (fails open), while an unreviewed `critical` still blocks (fails closed). Name any claim that went unverified in the epistemic-scope section. diff --git a/skills/shadow-verify/SKILL.md b/skills/shadow-verify/SKILL.md new file mode 100644 index 0000000..72e25ae --- /dev/null +++ b/skills/shadow-verify/SKILL.md @@ -0,0 +1,58 @@ +--- +name: shadow-verify +description: "Dispatch a parallel adversarial verifier wave after any high-stakes sub-agent investigation (code reviews, audits, findings reports, large refactors) — or whenever a sub-agent asserts a claim with high-confidence language (\"confident\", \"certain\", \"clearly\", \u226580%), since confidence is a trigger, not a verdict. Shadow verifiers independently re-derive 2–3 key claims from scratch using tool calls only, returning CONFIRMED/REFUTED/UNVERIFIABLE, and flag disagreements before the user acts. Use when sub-agent output will drive decisions, file changes, commits, or external side-effects." +context: load +--- + +## Sub-agent contract +/contract + +When a sub-agent (or wave) returns investigation findings, code-review conclusions, audit claims, refactor plans, or counts that will drive user decisions or file changes, do NOT surface the report. Instead, run a shadow verification wave **before** merging. + +**Wave 2 — Adversarial verifiers (parallel, independent):** +1. Extract 2–3 concrete, re-checkable claims from the returned report (e.g., "X function is unused", "file Y exceeds 300 lines", "PR targets main", "no tests cover Z"). +2. Dispatch one shadow sub-agent per claim, in parallel. Each receives the claim + the user's original goal + **the search surface** — the inventory of files, directories, or URLs the original investigation touched. It must NOT receive the original agent's reasoning, verdict, confidence language, or the specific line/region it concluded from. **Withhold the conclusion, not the map.** Withholding the map too does not buy extra independence — the verifier still has to reach the same evidence, it just spends its budget guessing paths to get there. Measured: one verifier denied the inventory spent 70 `grep` + 18 `read_file` calls re-locating files the parent already had paths for, guessed 5 nonexistent paths on the way, and hit its tool-loop ceiling before finishing. The independence that matters is epistemic (re-deriving the verdict), not navigational. + - The inventory is a **starting surface, not a boundary**: it does not satisfy the composition-axis guard below, and a verifier that reads only inside it still returns `evidence_base: artifact-internal`. At least one primary source outside that surface is still required for `independent-rederivation`. + - **Default to `subagent_type: "research-agent"` (mechanically locked to Read/Grep/Glob/WebFetch/WebSearch — cannot Edit/commit/push).** If the claim requires Bash to verify (running a failing test, `gh pr view`, `git log origin/...`), fall back to a Bash-capable subagent type with `isolation: "worktree"` and prepend this prefix to the prompt: *"Verifier sub-agent — do not Edit, Write, commit, push, `gh pr create`, or `curl`. Return findings only."* + - **Every verifier dispatch carries an explicit budget** — `max_tool_use_iterations` (a wave of 2–3 claim checks needs ~15–25 rounds each, not 50) plus the cheapest sufficient model. An unbudgeted verifier does not fail loudly: it exhausts the default tool-round ceiling, terminates `stopReason: "tool_use_loop_capped"`, and emits its verdict from a tools-stripped wind-down round built on partial evidence. A `CONFIRMED` produced that way is indistinguishable from a real one and silently defeats the entire point of the wave. Check each returned verifier's stop reason before merging its verdict; treat a capped or wind-down verifier as `UNVERIFIABLE`, not as a verdict. +3. Each verifier re-derives the verdict independently using tool calls only — never re-reading the original report's reasoning. Returns `{claim, verifier_verdict, evidence_pointer, evidence_base}`, where `verifier_verdict` is one of `CONFIRMED`, `REFUTED`, or `UNVERIFIABLE`, and `evidence_base` is `independent-rederivation` (read primary sources *outside* the cited artifact's boundary) or `artifact-internal` (re-read only the cited file/region). On `REFUTED`, the verifier also emits a corrected finding. + +**Merge:** +- `CONFIRMED` → surface the claim as validated. +- `REFUTED` → replace the claim with the verifier's corrected finding, annotated `[was: confident, now: refuted]`, and show it alongside the original with evidence. Do not act until the conflict is resolved. +- `UNVERIFIABLE` → surface with a `[needs-human-review]` tag rather than passing it through silently. +- **Budget-exhausted verifier** (`stopReason` of `tool_use_loop_capped` / `soft_deadline_wind_down`, or a `timeout`/`429` failure) → its verdict was produced without finishing the evidence gathering, so it is not a verdict. Downgrade to `UNVERIFIABLE [budget-exhausted]` and re-dispatch that one claim with a narrower scope and the search surface attached; this re-dispatch counts as the next verification round (and must respect the invoking workflow's round budget — e.g. `/review` permits one round with no repeats). If the loop cap (3 rounds total) is already reached, or the caller's budget forbids another round, escalate to the user instead of re-dispatching. Never merge a capped `CONFIRMED`. + +*The two verdicts below are **not** emitted by individual verifiers — they are produced by the Composition-axis guard (defined below) and handled here:* +- `UNVERIFIED-COMPOSITION` → surface with `[needs-human-review: composition boundary unchecked]`; do not act until a boundary read confirms or refutes the claim. +- `UNVERIFIED-ECHO-CHAMBER` → surface with `[needs-human-review: echo-chamber suspected]`; require at least one verifier to re-derive from outside the cited artifact before acting. + +Bound the loop: at most 3 verification rounds per session. Claims still unresolved after 3 rounds are escalated to the user, never silently dropped. + +**Composition-axis guard (echo-chamber check):** +A verifier that re-derives a claim by re-reading the *same* file/region the original sub-agent cited has confirmed the citation, not the claim — it can be blind to composition-boundary failures (temporal interleaving, state threading, render/event-pipeline ordering, scrollback/call-graph adjacency) that only manifest outside the artifact's boundary. Before accepting a `CONFIRMED`: +1. Read each verifier's `evidence_base`. +2. For any **artifact-internal `CONFIRMED`**, require one composition-boundary read (≥1 upstream caller + ≥1 downstream consumer, plus the pipeline that interleaves the artifact with siblings) before merging. If a missed boundary surfaces, downgrade to `UNVERIFIED-COMPOSITION` and tag `[needs-human-review]`. (An artifact-internal `REFUTED` is intentionally exempt: a refutation already halts action under the Merge rule above, so its boundary-blindness cannot drive a wrong commit — the asymmetry is safe by construction.) +3. **Echo-chamber guard:** if ≥2 verifiers cite the *same* in-repo artifact as primary evidence with no external referent, flag `UNVERIFIED-ECHO-CHAMBER` regardless of verdict and require one verifier to read outside that artifact's boundary. If the 3-round loop cap is already exhausted when this fires, escalate to the user as `UNVERIFIED-ECHO-CHAMBER [loop-cap-reached]` — do not dispatch a new round. + +**Scope guard:** skip the composition check when the claim cites an external referent (RFC, spec, threat model, upstream-API contract) that survives independently of the repo, or when the artifact is purely local with no composition surface. Runs once per artifact, not on every cite. + +**When to invoke:** +Any time sub-agent output will drive user decisions, file edits, commits, external side-effects, or is the basis of a user-facing summary. Treat **high-confidence language as a trigger in its own right**: when a review/audit sub-agent asserts a claim with markers like "confident", "certain", "clearly", "obviously", "must be", or a stated probability ≥ 80%, verify it as if it were decision-driving regardless of stakes. Confidence is a trigger, not a verdict. + +**Skip when:** +Sub-agent ran inside an orchestrator skill that already verifies (`resolve`, `diagnose`, `appmap`); sub-agent returned explicit failure; work was purely exploratory and no decision follows; or the session is **text-terminal** — a pure explanation, architecture walkthrough, onboarding Q&A, or capability map that names no mutated artifact (file/PR/commit/test), where there are no re-checkable state claims for adversarial verifiers to re-derive (assess coverage, coherence, and citation density instead of dispatching re-derivation sub-agents). + +## Appendix: verification methods by domain (non-binding) + +Reference aid for choosing re-derivation methods when dispatching a verifier. Consult when the claim's domain isn't obvious. + +| Domain | Re-derivation methods | +|--------|----------------------| +| `software` | Grep, Read, test runs, git commands (`gh pr view`, `git log`, `git diff`), build output | +| `research` | Web search for citation verification, independent literature re-search, replication/methodology audit, cross-reference checks | +| `design` | Competitive audit via web search, heuristic evaluation against stated criteria, accessibility/usability re-assessment | +| `business` | Market comp search, independent financial/metric re-derivation, assumption stress-test via web research | +| *(other)* | Web search re-derivation, independent source verification, assumption audit — use whatever tools can independently check the claim | + +When domain is unspecified, infer from the claim content. diff --git a/skills/ship/SKILL.md b/skills/ship/SKILL.md index 7bb9395..3443a01 100644 --- a/skills/ship/SKILL.md +++ b/skills/ship/SKILL.md @@ -1,12 +1,13 @@ --- name: ship -description: "Release pipeline for already-done local work. Dispatches /ground-state pre-flight, runs the project test suite, drafts a commit message for user approval, pushes, and opens a PR with a structured verification summary. Use when local changes are ready to hand off to review — e.g. 'ship this', 'push and open a PR', 'release this work'. Add --verify to trigger an adversarial verifier wave on the diff before a human reads the PR." +description: "Release pipeline for already-done local work. Dispatches /ground-state pre-flight, runs the project test suite, drafts a commit message, pushes, and opens a PR with a structured verification summary. Use when local changes are ready to hand off to review — e.g. 'ship this', 'push and open a PR', 'release this work'. Add --verify to trigger an adversarial verifier wave on the diff before a human reads the PR." +context: fork --- ## Sub-agent contract /contract -Release pipeline for work that is **already done locally**. This skill does NOT build, implement, or fix — if the task needs code, or a bug needs diagnosis, remind the user to chose another skill. This skill's job is the hand-off from "done locally" to "visible in a PR." +Release pipeline for work that is **already done locally**. This skill does NOT build, implement, or fix — if the task needs code, route to `/mint`; if a bug needs diagnosis, route to `/diagnose`. This skill's job is the hand-off from "done locally" to "visible in a PR." **Skip when:** - Working tree is clean (nothing to ship — tell the user). @@ -20,8 +21,18 @@ Release pipeline for work that is **already done locally**. This skill does NOT --- -**Phase 1 — Pre-flight (mandatory).** -Invoke `agent-workflow-amplifiers:ground-state` via the Skill tool. Abort and surface the blocker with remediation steps if ground-state reports any of: +**Hard rules (non-negotiable — these override any inferred convention):** + +- **Branch lock.** Whatever branch is checked out when `/ship` starts is the only branch you may commit and push from. **NEVER** `git checkout` to a different branch during this skill — not to `main`, not to `master`, not to a sibling feature branch. If pre-flight finds you on the default branch, abort. Do not "recover" by switching branches yourself. +- **Never push to the default branch.** Phase 5 pushes the CURRENT (feature) branch. Phase 8 opens a PR. There is no path through this skill that pushes to `main`/`master` or merges without a PR. If you find yourself about to run `git push origin main` (or equivalent), stop. +- **User intent is absolute.** If the user said "make a PR," "open a PR," "submit for review," or anything synonymous, you MUST complete Phase 8. "Ship" / "release" alone also implies PR — there is no interpretation under which `/ship` means "skip the PR step." +- **Do not invent project convention.** Never assert "this project uses direct-to-main flow," "this repo merges direct," or any equivalent justification for bypassing the PR step. If you genuinely cannot tell whether the project uses PRs, run `gh pr list --state merged --limit 5`; ≥3 merged PRs in the last week ⇒ PR is the convention. If still ambiguous, ask the user. Default to PR — it is the safer choice. +- **Anchor cwd.** When `/ship` is invoked, `pwd` at that moment is the only working directory you operate in. All git commands and file reads must run inside that directory — never `cd` to a sibling worktree, the parent repo, or any other path during this skill. If you find yourself reading state from a path other than the invocation cwd (e.g. `git log` returning unrelated commits, or a file diff that doesn't match what was just edited), stop and re-anchor on the original cwd. Worktrees and sibling repos look almost identical; cwd is the only disambiguator. + +--- + +**Phase 1 — Pre-flight (mandatory, runs BEFORE any git mutation).** +Invoke `ground-state` via the Skill tool. Abort and surface the blocker with remediation steps if ground-state reports any of: - Uncommitted changes in files unrelated to the declared scope - Branch behind `origin/` (stale — user needs to rebase/merge first) - Current branch IS the default branch @@ -39,31 +50,33 @@ If found → run it. Non-zero exit → **abort**; surface the failing output and If no harness detected → **warn the user:** "No test harness detected. Ship without running tests?" Require explicit yes. Do not silently skip. -**Phase 3 — Draft commit message (user-approval gate).** +**Phase 3 — Draft commit message.** Read the cumulative diff: `git diff --stat origin/...HEAD` + full `git diff`. Synthesize a commit message: - **Subject (≤70 chars):** imperative, Conventional Commits format (`feat`, `fix`, `chore`, `refactor`, `docs`, etc.) - **Body:** 2–5 bullets on WHY — the motivation, constraint, or trade-off. Skip the WHAT — the diff speaks for itself. -Before committing, confirm the **file list** to stage with the user — don't sweep in untracked config/scratch/secret files. Surface the draft message + file list; wait for explicit approval. If the user edits the message, use the edited version. +Before committing, review the **file list** to stage — don't sweep in untracked config/scratch/secret files. Print the draft message + file list to the user as info-only output, then **immediately** invoke Phase 4. **This is not a gate. Do not ask "does this look good?" Do not wait for approval.** The user surface is one continuous turn: draft → commit → push → PR URL. **Phase 4 — Commit.** -Stage only the confirmed files. Commit via HEREDOC to preserve formatting: +Stage only the confirmed files, then write the commit message to a temp file with your file-writing tool — **NOT** a shell heredoc — and commit with `-F`: ``` -git commit -m "$(cat <<'EOF' - - - -EOF -)" +# 1. Write the subject + body to a temp file (e.g. .git/COMMIT_BODY.txt) using +# your file-writing tool. The content never passes through the shell. +# 2. Commit from that file: +git commit -F .git/COMMIT_BODY.txt ``` +Going through a file keeps backticks, `$(...)`, and quotes in the message literal. **Never** assemble the message inline as `git commit -m "$(cat <<'EOF' … EOF)"` — a backtick or `$(` in the body is parsed by the shell *before* `git` runs, so the commit fails or records a mangled message. `.git/` is never staged, so the temp file can't sneak into the commit (in a linked worktree `.git` is a file, not a dir — write to the path printed by `git rev-parse --git-dir` instead). + Never `--amend` (creates a new commit each time). Never bypass hooks (`--no-verify`). **Phase 5 — Push.** +- **Assertion (before any push):** `git rev-parse --abbrev-ref HEAD` must NOT equal the default branch. If it does, abort with the same error as Phase 1 — Branch lock was violated somewhere upstream. - Upstream unset → `git push -u origin ` - Upstream set → `git push` - Non-fast-forward rejection → **abort**; do not force-push. Surface and let the user decide. +- **Never** `git push origin main` (or `master`). Pushing the feature branch is the only allowed form. **Phase 6 — Optional adversarial verifier wave.** Trigger when `$ARGUMENT` contains `--verify`, OR when the cumulative diff exceeds **100 changed lines** (rough proxy for "big enough that a human reviewer will miss something"). Skip for diffs under 100 lines unless `--verify` is explicit. @@ -95,18 +108,34 @@ Structure: ``` **Phase 8 — Open PR.** +Write the PR body (from Phase 7) to a temp file with your file-writing tool — **NOT** a shell heredoc — then pass it with `--body-file`: + ``` +# 1. Write the Phase 7 PR body to a temp file (e.g. .git/PR_BODY.md) using your +# file-writing tool. The body never passes through the shell. +# 2. Open the PR from that file: gh pr create \ --title "" \ - --body "$(cat <<'EOF' - -EOF -)" \ + --body-file .git/PR_BODY.md \ --base \ [--draft] ``` -Return the PR URL to the user. Done. +PR bodies are markdown: they routinely carry backticks (inline code), `$(...)`, and quotes. **Never** inline the body as `--body "$(cat <<'EOF' … EOF)"` — the shell parses backticks/`$(` inside the command substitution before `gh` ever runs, so the call fails (or worse, opens the PR with a truncated/garbled body and you don't notice). `--body-file` reads the file verbatim: no shell quoting, no escaping. Only `--title` stays inline — keep it a single plain line with no backticks. + +**Phase 9 — Reclaim the worktree.** +Skip entirely unless the work you just shipped lives under `.afk-worktrees/` (check the invocation cwd from Phase 1 — if the path has no `.afk-worktrees/` segment, there is nothing to reclaim; go straight to the PR URL). + +A worktree is scaffolding, not an artifact. Once Phase 8 succeeds the branch is pushed and the PR holds the work, so the checkout has no remaining job. Do not leave it for the background sweep — the sweep never reaps a *locked* tree, and a preserved commits-ahead tree is exactly the kind that gets locked, so "the sweep will get it" is false for the common case. + +Two cases, and they behave differently: + +- **A worktree you (or a sub-agent) created for this work, that you are NOT currently inside** — reclaim it now, using the `worktree` tool rather than `git worktree` in bash. Two refusals to expect, in this order: a **locked** tree must be `release`d first (`remove` refuses a locked tree, so the order is mandatory, not stylistic), and a tree with **commits ahead of base** is refused unless you pass `force: true`. After a successful push that `force` is safe and correct: the tool never deletes the branch ref, so the commits remain on the branch *and* on the remote — you are discarding a directory, not history. Do not pass `force` before the push has landed. +- **The worktree you are running in (your own cwd)** — **do not remove it.** Deleting your own working directory strands every subsequent tool call on a path that no longer exists, and `/ship` still has output to produce. Session-end cleanup already removes a clean worktree on exit. Say so instead, in one line: which branch carries the work, and that the checkout is reclaimed when the session ends. + +Never delete the branch as part of this phase — the open PR depends on that ref. + +Return the PR URL to the user, plus a one-line worktree disposition (`reclaimed ` / `preserved — reclaimed at session end` / omit if not in a worktree). Done. --- @@ -114,3 +143,4 @@ Return the PR URL to the user. Done. - `/ground-state` unavailable → skip Phase 1 with a loud warning; do not proceed silently. - `gh` CLI not authenticated → abort Phase 8 with the exact `gh auth login` command. - Detached HEAD or shallow clone → abort at Phase 1; these are edge cases `/ship` should not try to auto-recover. +- Worktree reclaim fails in Phase 9 → the PR is already open and that is the deliverable; report the failure and the path, never retry-loop or walk back the PR. diff --git a/skills/simplify/SKILL.md b/skills/simplify/SKILL.md new file mode 100644 index 0000000..c41a304 --- /dev/null +++ b/skills/simplify/SKILL.md @@ -0,0 +1,124 @@ +--- +name: simplify +description: "Discovers incidental complexity, duplication, and dead code in a codebase and produces a ranked, behavior-preserving reduction plan — optionally applying safe changes. Dispatches four parallel read-only discovery lenses (clone detection, dead code, complexity hotspots, wrong abstraction), synthesizes into a prioritized reduction plan, and gates apply mode behind /refactor and a hard test check." +argument-hint: "[target] [--apply] [--all]" +context: load +--- + +## Sub-agent contract +/contract + +### Overview + +`/simplify` is a discovery-and-prioritization skill, not a correctness auditor (that is `/review`'s job) and not a blind executor (that is `/refactor`'s job). It answers: *what incidental complexity, duplication, and dead weight exists in this code, and what is safe to remove?* Default mode is **read-only**: a ranked reduction plan is emitted but nothing is changed. Writes only happen when `--apply` is explicitly passed. + +Scope is **diff-bounded by default** (`git diff origin/main`, or working-tree/HEAD if no remote). Pass `--all` for a whole-repo sweep (where duplication and dead-code analysis pay off most). Pass an explicit `[target]` path or PR reference to override both. Code with no test coverage is safe to *analyze* but must never be *auto-applied* — the skill surfaces that gap and recommends `/simplify [target] --all` after adding tests, or produces the plan and stops. + +--- + +## Argument parsing + +| Argument | Effect | +|---|---| +| *(none)* | Scope = `git diff origin/main` (working-tree fallback) | +| `[target]` | Explicit path, glob, or PR ref | +| `--all` | Whole-repo sweep; excludes `node_modules/`, `dist/`, generated files | +| `--apply` | Opt-in write mode; default is read-only plan | + +Parse arguments before dispatching Wave 1. Resolve scope to a concrete file list or diff stat and attach it to every sub-agent prompt. + +--- + +## Wave 1 — Parallel discovery (read-only) + +Dispatch all four lenses simultaneously as `research-agent` sub-agents. All are strictly read-only — no writes, no installs that modify `package.json`. Optional tooling (`jscpd`, `knip`) is an **accelerant only**; degrade to grep/structural reasoning when absent or network-gated. + +### (a) Duplication / clone detection +Find exact and near-duplicate logic blocks across modules within scope. Optionally seed with: +``` +npx jscpd --reporters json --silent +``` +If `jscpd` is absent, use grep for literal repetition and structural reasoning for semantic clones. Output: **clone clusters** with `file:line` ranges, estimated token overlap, and a suggested extraction site. + +### (b) Dead code / unused exports +Find module-graph-dead exports, unreachable branches, and obsolete feature flags — beyond what `tsc --noEmit --noUnusedLocals` already catches. Optionally seed with: +``` +npx knip --reporter json +``` +**MANDATORY GROUNDING**: every "X is unused/dead" claim MUST carry: +1. A `file:line` citation for the symbol. +2. A structural evidence path — *who would import it, and why nothing does*. + +Dynamic imports, reflection, string-keyed access, and barrel re-exports are common false-positive vectors. Never assert dead code without grounding. Flag uncertain cases as **POSSIBLE-DEAD** rather than **DEAD**. + +### (c) Complexity hotspots +Identify over-long functions (>40 lines), deep nesting (>3 levels), boolean-flag parameters, primitive obsession, and sprawling switch/if chains. **The target repo has NO ESLint** — this lens is pure agent reasoning over the code, not lint output. Rank by cyclomatic complexity estimate × call-site frequency. Output: hotspot list with `file:line`, smell label, and a plain-language refactor suggestion. + +### (d) Reuse / wrong-abstraction-level +Find code that reimplements an existing helper, sits at the wrong layer (leaky abstraction, unnecessary wrapper), or is a thin pass-through with no added value. Cross-reference the project's existing utilities before flagging. Output: reuse candidates with `file:line`, what existing construct could replace them, and estimated call-site count. + +--- + +## Synthesis — inline, after Wave 1 + +1. **Dedup**: one site often trips multiple lenses. Merge duplicate entries; annotate each with the lens(es) that flagged it. +2. **SANDI-METZ WRONG-ABSTRACTION GUARD**: flag but **do NOT propose collapsing** two code paths if unifying them requires introducing a new boolean/flag parameter. Record these as *"defer — duplication is cheaper than the wrong abstraction"* with an explanation. +3. **Rank by impact × safety**: + - **HIGH IMPACT**: multi-site duplication, provably dead exports, complexity hotspots at high call frequency. + - **LOW RISK**: purely local changes, full test coverage, no public API surface. + - Deprioritize: single-use private helpers, style preferences, anything touching uncovered code. +4. **Emit the reduction plan** — one row per finding: + +| # | Location (`file:line`) | Smell | Proposed simplification | Risk note | Behavior-preservation note | +|---|---|---|---|---|---| + +5. Append a **Coverage gate summary**: list files in scope with no test coverage where apply was requested — recommend tests first. + +**In default (read-only) mode: STOP here.** Print the plan and exit. + +--- + +## Wave 2 — Gated apply (only when `--apply` is passed) + +Before any write, verify the test suite is green: +``` +pnpm lint && pnpm test +``` +If the gate is red before any change, **abort apply and report**. Do not attempt to fix pre-existing failures. + +**Delegation decision** (per ranked item, high-to-low): + +- **Multi-site mechanical changes** (extract duplicated block into a shared helper used at N≥2 sites): delegate to `/refactor` if available. `/refactor` handles DAG-layered, worktree-isolated parallel application with a hard test gate at each layer boundary and a behavioral diff. Pass the reduction plan item as the refactor spec. +- **`/refactor` NOT available, OR single-site local simplifications**: apply directly inside a git worktree. One change at a time; run `pnpm lint && pnpm test` after each. On regression → route to `/diagnose`; revert the change; continue with remaining items. +- **Items touching uncovered code**: skip apply, preserve in plan output as *"plan-only — no test coverage"*. + +On full success, chain to `/ship` for commit + PR. + +--- + +## Guardrails + +- **Behavior-preserving only** — never change observable behavior under the guise of cleanup. +- Exclude `node_modules/`, `dist/`, vendored code, and auto-generated files from all analysis. +- Optional tooling (`jscpd`, `knip`) must not modify `package.json` or lock files — use `npx` with no persistent side effects. +- Diff-scoped by default; whole-repo only on `--all` — avoids signal-drowning noise on large codebases. +- Max 3 apply-layer retries per item before skipping and continuing. + +--- + +## Failure modes to surface explicitly + +| Failure mode | Mitigation | +|---|---| +| Over-DRYing / wrong abstraction | Sandi-Metz guard (see Synthesis step 2) | +| False dead-code positives | Mandatory grounding rule on lens (b) | +| Behavior change disguised as cleanup | Pre/post test gate; worktree isolation | +| Scope creep on large repos | Diff-scope default; `--all` is explicit opt-in | + +--- + +## Chains to + +- `/refactor` — multi-site mechanical apply +- `/ship` — commit + PR after successful apply +- `/diagnose` — when an applied simplification breaks a test diff --git a/skills/spec/SKILL.md b/skills/spec/SKILL.md index f78ae44..e074da3 100644 --- a/skills/spec/SKILL.md +++ b/skills/spec/SKILL.md @@ -2,6 +2,29 @@ name: spec description: "Takes a loose idea and transforms it into a structured, actionable spec ready for implementation. Use when the user passes an idea, feature request, or problem description that needs scoping before building." argument-hint: "" +context: fork --- -Dispatch two sub-agents in parallel. One researches the web for prior art, APIs, and patterns relevant to $ARGUMENT. The other inspects the local codebase for conventions, dependencies, and integration points. When both return, synthesize a concise spec covering: problem, goals, non-goals, approach, key decisions, interface, file plan, test plan, and open questions. Present to the user for confirmation before proceeding. +## Triage: bugs route to /diagnose + +Before speccing, detect bug-shaped inputs: crashes, error stacks, regression reports ("worked yesterday", "used to work"), platform-specific failures ("broken on X", "doesn't work when…"), or user-report framing that implies root-cause-first (not design-first). If detected, stop and redirect: *"This is a debugging task, not a spec task. Route to /diagnose instead — it will isolate the root cause, then /spec can scope the fix."* Do not emit a spec. + +--- + +Dispatch two sub-agents in parallel. One researches the web for prior art, comparable approaches, and patterns relevant to $ARGUMENT. The other inspects the local working directory for conventions, existing artifacts, and integration points relevant to the domain. When both return, synthesize a concise spec using the domain-appropriate schema below. Present to the user for confirmation before proceeding. + +**Output schema by domain:** + +| Domain | Spec fields | +|--------|-------------| +| `software` | problem, goals, non-goals, approach, key decisions, interface, file plan, test plan, open questions | +| `research` | problem, hypothesis, methodology, prior art positioning, expected results, publication plan, open questions | +| `design` | problem, user needs, constraints, solution space, prototype plan, success metrics, open questions | +| `business` | opportunity, risk analysis, competitive landscape, go/no-go criteria, resource plan, open questions | +| *(other)* | problem, goals, non-goals, approach, key decisions, deliverables, validation plan, open questions | + +When domain is unspecified, infer from $ARGUMENT and the working directory. If ambiguous, use the generic *(other)* schema. + +## Epistemic confidence + +Include an **Epistemic confidence** section at the end of every spec: summarize coverage gaps from research, flag claims that will be hard to verify, and note where human judgment will be needed.