From 251d82ecdc289c025c32d0a47d2c4eca82104693 Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Wed, 9 Sep 2026 12:22:10 -0700 Subject: [PATCH] skills: decompose prove-it into a principle plus four evidence products MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context - Problem: catstack ships three hooks that enforce prove-it (prove-it-ship-gate, hedge-runs-prove-it, diu-stop) and eight skills that cite it, but does not own the rule. diu-stop tells the agent to read "skills/prove-it/SKILL.md in the Invoker repo"; ~/.claude/skills/ invoker-prove-it is a real directory written by Invoker's installer, so ./install.sh cannot update it. Engine-only mode cites a file it does not install. - Trigger: reading lauren (@poteto)'s pstack guide (mirrored at github.com/cursor/plugins/pstack/docs/guide) surfaced /how and /why as understand-before-edit skills catstack has no equivalent of. The gap is real: our only git-history rule is one bullet in principle-fix-root-causes:22, not a procedure. ## Considerations - Chose: split the rule from the procedures. principle-prove-it holds the claim gate (corpus, because check_ecosystem_boundaries.py:206 requires principle-* live there). how / why / alternatives-considered / spike-and-validate hold the fan-outs (product, human-authored portable workflows per docs/ecosystem.md). - Chose: principle-prove-it is the one principle that does NOT carry disable-model-invocation. The other 25 are steering vocabulary you invoke by name; this one is a gate whose job is to intercept a claim as it forms. A gate that only fires when someone remembers to name it is not a gate. prove-it-ship-gate already auto-fires from corpus, so there is precedent. - Rejected: reusing independent-judge-swarm's board shape. That grades a finished artifact (verdict/score/blocking_issues); these four return findings about work that has not happened yet, so there is nothing to grade. Shared shape lives in principle-prove-it/references/ finding-shape.md instead, reusing the fan-out mechanic and the document-the-null rule from reflect/references/lenses.md. - Rejected: treating alternatives-considered as symmetric with how/why. how and why read what exists and can return an honest null; alternatives generates, so it can always produce three plausible options and manufacture evidence for the very gate it feeds. Every option is labeled considered (cited) or invented (untested), and an invented option may not decide anything until spike-and-validate turns it into a run with real output. - Not done here: retiring invoker-prove-it, repointing diu-stop's message at the new principle, and documenting/enforcing which skills may auto-fire. The last one is the next slice in this stack — it found that product/skills/admin-bypass-sweep (force-merge past required checks) is auto-invocable today and held back only by prose in its description. ## Blast Radius - Adds only. No existing file modified, no hook logic touched. - New: corpus/skills/principle-prove-it/ (SKILL.md, references/ finding-shape.md, 2 test fixtures); product/skills/{how,why, alternatives-considered,spike-and-validate}/ (SKILL.md + 2 fixtures each). - principle-prove-it auto-fires, so it can now load on claim-shaped turns that previously loaded nothing. Cost is context on exactly the turns where the rule applies. - Risk: `how` and `why` are generic names in a flat installed skill namespace. Verified no collision in ~/.claude/skills at authoring time. - Revertable with git revert; ./install.sh prunes the symlinks on rerun. ## Verification - python3 scripts/check_ecosystem_boundaries.py -> ok - python3 scripts/check_skill_file_refs.py -> ok - python3 scripts/check_skills_three_harnesses.py -> ok - python3 scripts/check_skill_trigger_mechanism.py -> ok - python3 scripts/check_skill_test_coverage.py --base origin/main --head HEAD -> ok (rerun post-commit; the pre-commit run compared origin/main to an unchanged HEAD and passed vacuously) - check_codify_has_code.py run with --allow-prose-only: this slice adds rule prose whose enforcement already ships in engine/hooks/prove-it-ship-gate, engine/hooks/hedge-runs-prove-it, and engine/hooks/diu-stop. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JQMWSLRArEfEm1psa7RKdD --- corpus/skills/principle-prove-it/SKILL.md | 66 +++++++++++++ .../references/finding-shape.md | 45 +++++++++ .../principle-prove-it/tests/fires_example.md | 15 +++ .../tests/stays_silent_example.md | 12 +++ .../skills/alternatives-considered/SKILL.md | 87 +++++++++++++++++ .../tests/fires_example.md | 12 +++ .../tests/stays_silent_example.md | 8 ++ product/skills/how/SKILL.md | 77 +++++++++++++++ product/skills/how/tests/fires_example.md | 11 +++ .../skills/how/tests/stays_silent_example.md | 10 ++ product/skills/spike-and-validate/SKILL.md | 80 ++++++++++++++++ .../spike-and-validate/tests/fires_example.md | 12 +++ .../tests/stays_silent_example.md | 10 ++ product/skills/why/SKILL.md | 94 +++++++++++++++++++ product/skills/why/tests/fires_example.md | 12 +++ .../skills/why/tests/stays_silent_example.md | 10 ++ 16 files changed, 561 insertions(+) create mode 100644 corpus/skills/principle-prove-it/SKILL.md create mode 100644 corpus/skills/principle-prove-it/references/finding-shape.md create mode 100644 corpus/skills/principle-prove-it/tests/fires_example.md create mode 100644 corpus/skills/principle-prove-it/tests/stays_silent_example.md create mode 100644 product/skills/alternatives-considered/SKILL.md create mode 100644 product/skills/alternatives-considered/tests/fires_example.md create mode 100644 product/skills/alternatives-considered/tests/stays_silent_example.md create mode 100644 product/skills/how/SKILL.md create mode 100644 product/skills/how/tests/fires_example.md create mode 100644 product/skills/how/tests/stays_silent_example.md create mode 100644 product/skills/spike-and-validate/SKILL.md create mode 100644 product/skills/spike-and-validate/tests/fires_example.md create mode 100644 product/skills/spike-and-validate/tests/stays_silent_example.md create mode 100644 product/skills/why/SKILL.md create mode 100644 product/skills/why/tests/fires_example.md create mode 100644 product/skills/why/tests/stays_silent_example.md diff --git a/corpus/skills/principle-prove-it/SKILL.md b/corpus/skills/principle-prove-it/SKILL.md new file mode 100644 index 00000000..c3fe5bfd --- /dev/null +++ b/corpus/skills/principle-prove-it/SKILL.md @@ -0,0 +1,66 @@ +--- +name: principle-prove-it +description: "Apply before any claim about code, systems, or history. No claim without same-turn evidence; a hedge is a trigger to verify, never a place to stop. Routes the four investigation products that build the evidence. Unlike the other principles this one auto-fires: it must intercept a claim as it forms, not be named after the fact." +--- + +# Prove It + +A claim is settled only when the evidence for it is in the same message. +Everything else is a hypothesis and must say so. + +**Why:** the cheap signal always stands in for the expensive one. A build +passes and gets reported as "it works." A file exists and gets reported as +"it's wired up." A name matches and gets reported as "that's the bug." Each +substitution is individually reasonable and collectively how wrong answers +reach the user with confidence attached. + +## The gate + +Before writing any claim of the form "this is fixed," "this works," "the +cause is X," "it's merged," "N are running" — you must already have, in the +same message, one of: + +1. A command run this turn, with its real pasted output. Not summarized. +2. A `file:line` read this turn, named with the ref it was read at. +3. A test name plus its real pass/fail line from the runner. + +Otherwise write `UNVERIFIED:` immediately before the claim. There is no +softer wording. The full evidence rules live in `engine/CLAUDE.core.md` and +are always loaded; this file is the judgment half plus the routing below. + +**A hedge is a trigger.** "I think," "probably," "should work" mean run the +check now, not lower the confidence and continue. + +**Absence of output is not proof of success.** A command that printed +nothing needs its exit code shown. + +## What builds the evidence + +The gate says what counts. It does not gather anything. Four product skills +do that, each fanning out to parallel subagents and returning the shape in +`corpus/skills/principle-prove-it/references/finding-shape.md`: + +| Product | Question | Returns | +| --- | --- | --- | +| `how` | How does this work now? | The mechanism you must exercise | +| `why` | Why is it shaped this way? | The constraint you must not break | +| `alternatives-considered` | What else could this be? | Options, each labeled considered or invented | +| `spike-and-validate` | Does the invented one hold? | A throwaway that ran, and its real output | + +They compose in that order and each is optional. A one-line fix needs none +of them. A claim about a subsystem nobody on the team has read needs `how` +before anything else. + +The pairing that matters: `alternatives-considered` **generates**, so it can +always produce three plausible options and none of them are evidence. +`spike-and-validate` is its gate. An invented option that reaches a decision +without a spike is exactly the manufactured evidence this principle exists +to stop. + +## Related + +- `prove-it-ship-gate` — the done/shipped auto-route for live side effects. +- `principle-fix-root-causes` — reproduce before explaining. +- `principle-sequence-verifiable-units` — end each unit in a check. +- Mechanical enforcement: `engine/hooks/prove-it-ship-gate`, + `engine/hooks/hedge-runs-prove-it`, `engine/hooks/diu-stop`. diff --git a/corpus/skills/principle-prove-it/references/finding-shape.md b/corpus/skills/principle-prove-it/references/finding-shape.md new file mode 100644 index 00000000..28379a16 --- /dev/null +++ b/corpus/skills/principle-prove-it/references/finding-shape.md @@ -0,0 +1,45 @@ +# Shared finding shape + +Every investigation product (`how`, `why`, `alternatives-considered`, +`spike-and-validate`) fans out to parallel subagents and merges their +returns. They share this shape so `principle-prove-it` can consume any of +them without a per-skill adapter. + +This is **not** the judge board in `product/skills/independent-judge-swarm`. +That board grades a finished artifact (`verdict`, `score`, +`blocking_issues`). These skills return findings about work that has not +happened yet, so there is nothing to grade. + +## Fields + +| Field | Shape | +| --- | --- | +| `lens` | string — which angle this subagent owned | +| `claim` | string — one sentence, the thing found | +| `grounding` | `read-confirmed` \| `name-matched` \| `inferred` \| `invented` | +| `evidence` | `file:line` + ref, a command + its real output, or a URL | +| `ref` | which ref `evidence` was read at: working tree, `HEAD`, `origin/`, installed bundle | +| `null_result` | string — what was searched and came back empty | + +## Rules + +- A subagent that found nothing still returns a row with `null_result` set. + Silence is not the same as absence, and a skipped source is not a null — + see the Evidence rules in `engine/CLAUDE.core.md`. +- `grounding: read-confirmed` means the subagent opened the actual + reference and traced it. `name-matched` means the path or symbol name + looked right. A working-tree read under a dirty path is `name-matched`. +- `grounding: inferred` and `grounding: invented` rows carry no authority + on their own. They are hypotheses. Route an `invented` row that matters + to `spike-and-validate` before it reaches a decision. +- The merge step reports disagreement rather than resolving it silently. + +## Merge + +1. Group rows by `claim`. +2. Two subagents reaching the same `read-confirmed` claim independently is + the strongest signal available here. Check they were not handed the same + anchor and prompt first — template-identical attempts converge on the + same wrong answer exactly like independent ones agree. +3. Any `read-confirmed` row beats any number of `inferred` rows. +4. Report every `null_result` in the output. Do not drop it for brevity. diff --git a/corpus/skills/principle-prove-it/tests/fires_example.md b/corpus/skills/principle-prove-it/tests/fires_example.md new file mode 100644 index 00000000..69bdc4aa --- /dev/null +++ b/corpus/skills/principle-prove-it/tests/fires_example.md @@ -0,0 +1,15 @@ +An agent has just edited a retry handler and is about to write "this fixes +the duplicate-write bug" into a PR body. Nothing ran this turn: no repro, no +test, no command output. The agent never types a slash command. + +This skill fires on the claim shape itself. Unlike the other `principle-*` +skills, it does not carry `disable-model-invocation: true`, so its +`description:` is loaded and the model can match it — which is the whole +point: a gate that only works when someone remembers to name it is not a +gate. A fix claim with no evidence in the same message is exactly what the +description targets, and once loaded the skill supplies both the three +accepted evidence forms and the routing table that sends the agent to `how` +to find which artifact to exercise. + +The same auto-fire path covers the hedge case: "this should work" is a +trigger to run the check, not a softer way to state the claim. diff --git a/corpus/skills/principle-prove-it/tests/stays_silent_example.md b/corpus/skills/principle-prove-it/tests/stays_silent_example.md new file mode 100644 index 00000000..a2a32262 --- /dev/null +++ b/corpus/skills/principle-prove-it/tests/stays_silent_example.md @@ -0,0 +1,12 @@ +A user asks which name reads better for a local variable in a helper they are +writing, `retryCount` or `attempts`, and the agent gives an opinion. + +This skill stays silent. Auto-firing is driven by the presence of a claim to +hold evidence against, and there is none here: a naming preference asserts +nothing about behavior, system state, history, or a cause. There is no +"this is fixed", no "the cause is X", no hedge about code that could be +checked by running something. + +Firing here would be the skill misbehaving rather than working — demanding a +command run and pasted output before answering a style question is the +false-positive this fixture exists to pin down. diff --git a/product/skills/alternatives-considered/SKILL.md b/product/skills/alternatives-considered/SKILL.md new file mode 100644 index 00000000..ee64b395 --- /dev/null +++ b/product/skills/alternatives-considered/SKILL.md @@ -0,0 +1,87 @@ +--- +name: alternatives-considered +description: >- + Produce the real option set for a decision before committing to the first + shape that came to mind. Use before a design, a schema, a naming or format + choice, or any change expensive to reverse. Every option is labeled + considered (cited) or invented (untested); invented options route to + `spike-and-validate` before they can decide anything. +--- + +# Alternatives considered + +One attempt at a hard design locks in the first shape the model thought of. +This produces the option set, with each option honest about where it came +from. + +## The asymmetry that governs this skill + +`how` and `why` read what exists. They can come back empty, and an empty +result is informative. + +This skill **generates**. It can always produce three plausible options, and +plausibility is free. Left ungated it manufactures evidence for +`principle-prove-it` — which is the exact failure that principle exists to +stop. + +So every option carries a label and the label is load-bearing: + +| Label | Means | Authority | +| --- | --- | --- | +| `considered` | Someone actually weighed this — cite the PR comment, commit, doc, or rejected design | Evidence | +| `invented` | Produced here, never tried | Hypothesis only | + +An `invented` option may not decide anything until `spike-and-validate` +turns it into a run with real output. Saying "we considered X and rejected +it" about an option nobody ran is a false claim about the record. + +## Step 1. Bound the decision + +State in one line: what is being chosen, and what makes it expensive to +reverse. A decision that is cheap to reverse does not need this skill — +make it, and move on. + +Pull **Avoid** from `why` if it ran. Options already tried and failed are +`considered` with a citation, not fresh ideas. + +## Step 2. Fan out + +2–4 subagents, spawned in **one message**, each given the same brief and a +different constraint to optimize for (fewest moving parts, easiest to +delete later, best for the caller, cheapest to migrate). Different +constraints, not different phrasings — same-brief subagents converge. + +Each returns rows in the shape at +`corpus/skills/principle-prove-it/references/finding-shape.md`, with +`grounding` set to `invented` unless it can cite where the option was +already weighed. + +Give each one its own worktree if it writes anything. Scope wording is not +filesystem isolation. + +## Step 3. Merge into an option set + +For each surviving option: + +- **Shape** — what the caller writes, first. Then types and boundaries. +- **Label** — `considered` with citation, or `invented`. +- **Cost to reverse** — the real question behind most of these decisions. +- **What would kill it** — the check that would rule it out. This line is + the spike brief. + +Include the status quo as an option. It is `considered` by definition and +frequently wins. + +## Step 4. Route + +- All options `considered` → decide now, cite the record. +- The leading option is `invented` and expensive to reverse → + `spike-and-validate` before committing. +- Subagents disagree on which constraint matters → that is the real + decision. Surface it instead of picking quietly. + +## Do not + +- Pad the set. Two real options beat four where two are strawmen. +- Present an `invented` option as prior art. +- Run this on a reversible decision. diff --git a/product/skills/alternatives-considered/tests/fires_example.md b/product/skills/alternatives-considered/tests/fires_example.md new file mode 100644 index 00000000..d0940ca0 --- /dev/null +++ b/product/skills/alternatives-considered/tests/fires_example.md @@ -0,0 +1,12 @@ +A user is designing a cache key format for a service and says: "this is +expensive to change once it's in production — what are our real options +here?" + +This skill fires. It is a design decision that is expensive to reverse, +which is the stated trigger, and the user is explicitly asking for the +option set rather than a recommendation. Step 2 fans out subagents each +optimizing a different constraint (fewest moving parts, cheapest to migrate, +best for the caller), and Step 3 labels every returned option `considered` +with a citation or `invented`. Because the leading option is expensive to +reverse, Step 4 routes it to `spike-and-validate` before it can decide +anything. diff --git a/product/skills/alternatives-considered/tests/stays_silent_example.md b/product/skills/alternatives-considered/tests/stays_silent_example.md new file mode 100644 index 00000000..0c668690 --- /dev/null +++ b/product/skills/alternatives-considered/tests/stays_silent_example.md @@ -0,0 +1,8 @@ +A user asks the agent to rename a local variable inside one function from +`res` to `response`, and confirms the function is not exported. + +This skill stays silent. The decision is cheap to reverse — a rename inside +one unexported function is a single edit undone by a single edit — and the +skill's Step 1 says a cheap-to-reverse decision does not need it. There is +no option set worth generating, and producing three labeled alternatives for +a local rename would be the padding its own "do not" list forbids. diff --git a/product/skills/how/SKILL.md b/product/skills/how/SKILL.md new file mode 100644 index 00000000..44ebc6fc --- /dev/null +++ b/product/skills/how/SKILL.md @@ -0,0 +1,77 @@ +--- +name: how +description: >- + Trace how a subsystem actually works before changing it. Use for "how does + X work", a walkthrough before an edit, or placement questions ("where + should this live", "which package owns this"). Fans out read-only + explorers for a subsystem, one explainer for a narrow question. Use `why` + for motivation, `alternatives-considered` for options. +--- + +# How + +Build a working mental model of code you are about to change. The output is +the mechanism, not annotated source. + +Feeds `principle-prove-it`: you cannot exercise the real artifact until you +know which artifact is real. + +## Step 1. Size the question + +State your reading of the scope in one line, then proceed. The user can +redirect. + +- **Narrow** — one module, one function, one file. No fan-out. Read and + explain in a single pass. Go to Step 3. +- **Broad** — a subsystem across files or packages, a cross-cutting flow, an + architectural overview. Fan out first. Go to Step 2. + +When it is ambiguous, take the narrow path. A second pass is cheaper than +four subagents answering a question that had one file in it. + +## Step 2. Fan out (broad only) + +Split the question into 2–4 angles that do not overlap. Spawn all explorers +in **one message** so they run concurrently: + +- `subagent_type: Explore` (or `general-purpose` where Explore is absent) +- read-only; no edits, no worktree needed + +Each explorer owns one angle and returns rows in the shape at +`corpus/skills/principle-prove-it/references/finding-shape.md`. + +**Prompt hygiene:** give each explorer the question and the entry points, +not your working theory. A primed explorer finds roughly what you already +suspected. + +**Every explorer states grounding per row.** `read-confirmed` means it +opened the actual import or call site and traced it. `name-matched` means +the path looked right. Two files can hold the same symbol name; a subagent +reasoning by name-proximity hands back a confident wrong file. + +## Step 3. Explain + +Merge the rows and write the explanation yourself. Sections, dropping any +that do not apply: + +- **Overview** — what this does, in two sentences. +- **Runtime flow** — the actual call path, entry point to effect. +- **Key types** — the data structures the logic is written against. +- **Where things live** — `file:line` per claim, with the ref. +- **Gotchas** — the non-obvious parts, and what looks wrong but isn't. +- **Not traced** — every angle that came back empty or was not covered. + +Do not drop the last section for brevity. A gap you name is a gap the reader +can fill; a gap you omit reads as coverage. + +## Hand-off + +When `how` precedes a change, end with the mechanism that must still work +afterward, phrased as a command or check. That line is what +`principle-prove-it` will demand output from later. + +## Do not + +- Explain from a grep hit. A name match is not a read. +- Fan out on a question one file answers. +- Report a flow you inferred from function names as if you traced it. diff --git a/product/skills/how/tests/fires_example.md b/product/skills/how/tests/fires_example.md new file mode 100644 index 00000000..682c0f2f --- /dev/null +++ b/product/skills/how/tests/fires_example.md @@ -0,0 +1,11 @@ +A user is about to add a new notification channel and asks: "how does the +dedupe path work before I touch it? is there an n+1 when we look up +subscribers?" + +This skill fires. The question is a mechanism question about a subsystem the +agent has not read, asked ahead of an edit — the skill's stated trigger. It +spans more than one file (dedupe plus subscriber lookup), so Step 1 sizes it +broad and Step 2 fans out read-only explorers on non-overlapping angles. +Each returned row carries `read-confirmed` or `name-matched` grounding, and +the merged explanation ends with the mechanism that must still work after +the change — the line `principle-prove-it` will later demand output from. diff --git a/product/skills/how/tests/stays_silent_example.md b/product/skills/how/tests/stays_silent_example.md new file mode 100644 index 00000000..0a3f2a01 --- /dev/null +++ b/product/skills/how/tests/stays_silent_example.md @@ -0,0 +1,10 @@ +A user asks to bump a timeout constant from 30 to 60 seconds in a file they +are currently looking at, and pastes the line. The agent makes the one-token +edit. + +This skill stays silent. There is no mechanism to trace: the target is a +single literal in a file already in context, the user supplied the code, and +nothing about the change depends on understanding a call path. Step 1 would +size this narrow and the skill's own "do not" list rules out fanning +subagents at a question one file answers. Running `how` here would spend +subagents to re-explain a line the user just read. diff --git a/product/skills/spike-and-validate/SKILL.md b/product/skills/spike-and-validate/SKILL.md new file mode 100644 index 00000000..fc0e71b8 --- /dev/null +++ b/product/skills/spike-and-validate/SKILL.md @@ -0,0 +1,80 @@ +--- +name: spike-and-validate +description: >- + Turn an untested assumption into real output by building the smallest + throwaway that would kill it, running it, then discarding the code and + keeping the finding. Use before committing to an invented option, an + unfamiliar library or API, or a performance assumption. The gate on + `alternatives-considered`. +--- + +# Spike and validate + +A spike is an experiment, not a draft of the real change. Its only product +is a run with real output. The code goes in the bin. + +This is the gate that makes `alternatives-considered` safe: an option nobody +ran is a hypothesis, and a spike is the cheapest way to stop it from +becoming a decision. + +## Step 1. Write the kill condition first + +Before any code, in one line: **what result would rule this out?** + +If you cannot write that line, you are not spiking, you are starting the +implementation. Stop and say so. + +The kill condition is falsifiable and specific. "See if the library works" +is not one. "Parses our 40MB fixture in under 2s on this machine" is. + +## Step 2. Build the smallest thing that could fail + +- Own worktree or scratch directory. Never the live checkout. +- Hardcode everything not under test. Config, auth, error handling, and + edge cases are not what you are learning. +- No tests, no cleanup, no comments. This code is not going to review. +- Timebox it and say the box out loud. A spike that outgrows its box has + become the implementation without anyone deciding that. + +## Step 3. Run it and paste the output + +The whole point. Real command, real output, in the same message as the +verdict — `principle-prove-it`, applied to your own experiment. + +A spike that only passes proves less than you think. Where the assumption +has a failing side, show both: the case that works and the case that +breaks. A run that could not fail did not test anything. + +## Step 4. Discard the code, keep the finding + +**Delete the spike.** A spike promoted to production carries every shortcut +in Step 2 with it, and nobody remembers which lines were deliberate. + +What survives is one row in the shape at +`corpus/skills/principle-prove-it/references/finding-shape.md`: + +- `claim` — the assumption, now resolved. +- `grounding` — `read-confirmed`, because you ran it. +- `evidence` — the command and its real output. +- `null_result` — what the spike did **not** cover. Always fill this in. A + spike proves one thing on one machine with one fixture. + +Then say plainly: **validated**, **killed**, or **inconclusive**. +Inconclusive is a real outcome and much better than a spike quietly +reported as a pass. + +## When not to spike + +- The answer is in the code — that is `how`. +- The answer is in the history — that is `why`. +- The decision is cheap to reverse. Build it and change it later. +- You already know the answer and want cover for it. That is not an + experiment. + +## Do not + +- Spike in the live checkout. +- Skip the kill condition and decide after the fact what the run showed. +- Ship the spike. If it turns out to be the right shape, rebuild it with + the shortcuts removed. +- Report a spike that never ran as evidence for anything. diff --git a/product/skills/spike-and-validate/tests/fires_example.md b/product/skills/spike-and-validate/tests/fires_example.md new file mode 100644 index 00000000..edfa935d --- /dev/null +++ b/product/skills/spike-and-validate/tests/fires_example.md @@ -0,0 +1,12 @@ +A plan needs a streaming ZIP reader for uploads up to 2GB. Nobody on the +team has used the candidate library, and `alternatives-considered` returned +it labeled `invented` — plausible, never run here. + +This skill fires. An untested assumption is about to decide an expensive +choice, which is the stated trigger and the gate +`alternatives-considered` routes to. Step 1 writes the kill condition first +("streams a 2GB fixture without loading it into memory, under 4GB RSS"), +Step 2 builds a hardcoded throwaway in its own scratch directory, Step 3 +runs it and pastes the real output including the case that breaks, and Step +4 deletes the code, keeps one finding row with `null_result` naming what the +spike did not cover, and reports validated, killed, or inconclusive. diff --git a/product/skills/spike-and-validate/tests/stays_silent_example.md b/product/skills/spike-and-validate/tests/stays_silent_example.md new file mode 100644 index 00000000..1343dfc1 --- /dev/null +++ b/product/skills/spike-and-validate/tests/stays_silent_example.md @@ -0,0 +1,10 @@ +A user asks why an existing export job writes duplicate rows after a retry. +The behavior is already implemented, already reproducible, and the answer is +in the code. + +This skill stays silent. Its own "when not to spike" section rules this out +directly: the answer is in the code, so this is `how`, and the causal +question about an existing defect belongs to the repro-then-fix path in +`principle-fix-root-causes`. There is no untested assumption about something +that does not exist yet, and building a throwaway would not answer a +question the real code already answers. diff --git a/product/skills/why/SKILL.md b/product/skills/why/SKILL.md new file mode 100644 index 00000000..85c6a37c --- /dev/null +++ b/product/skills/why/SKILL.md @@ -0,0 +1,94 @@ +--- +name: why +description: >- + Recover why code is shaped the way it is before changing it. Use for "why + does X work this way", "why was this threshold picked", design rationale, + and dead-code questions. Anchors in git history and PR bodies first, then + fans out one investigator per available evidence source and reports every + null. Use `how` for runtime behavior. +--- + +# Why + +`how` answers what the code does. `why` answers what forces produced it. + +Feeds `principle-prove-it`: a constraint you cannot see is a constraint you +will delete. Most "we changed it back a week later" bugs are this. + +## Operating posture + +Careful and explicit about the line between what the record says and what +you are inferring from it. Never smooth an inference into a fact because it +reads better. + +## Step 1. Anchor in the code + +Build this inline **before** spawning anything. Every investigator starts +from it, so a weak anchor wastes the whole fan-out. + +```sh +git blame -L , -- # last-touch commits +git log --oneline -20 -- # recent commits, PR numbers visible +git log --follow -p -- # full history through renames +gh pr view --json title,body,author,mergedAt,comments,reviews +``` + +Capture: file paths, line ranges, key symbols, commit SHAs, PR numbers, and +any ticket IDs those PR bodies reference. Read the PR bodies yourself. Review +discussion is where implementation-time rationale actually lives, and it is +usually the answer. + +## Step 2. Fan out over the sources that exist + +List the evidence sources actually available in this environment before +assigning any. Typical set: git and `gh` (always), the issue tracker, +long-form docs, team chat, error tracking, observability, analytics — +whichever have a working MCP or CLI here. + +One investigator per source, spawned in **one message**. Do not hand one +subagent two sources; it will search the easy one and summarize the other. + +Each returns rows in the shape at +`corpus/skills/principle-prove-it/references/finding-shape.md`. + +**Document the null.** A source that returned nothing gets a row with +`null_result` set and a line in Sources Consulted. Skipping a source needs a +written reason in the output — "no MCP available here" or "provably +irrelevant, this is a build-time script with no runtime path." "Probably +irrelevant" is not a reason. + +This is the same rule as the projection rule in `engine/CLAUDE.core.md`: +absence from a projection is not proof of absent state. + +## Step 3. Report with the confidence separated + +- **The question** — restated, and the code it points at. +- **What the record says** — cited. Commit, PR, ticket, message, with the ref. +- **What we can reasonably infer** — clearly marked as inference. +- **Competing explanations** — when two readings both fit, give both. +- **What we don't know** — including every null and every skipped source. +- **Sources consulted** — one line each, including the empty ones. + +"Nobody wrote it down" is a real answer and often the most useful one: it +means the constraint is unprotected and the next person will break it too. + +## Step 4. Convert to constraints (when a change follows) + +If the `why` precedes an edit, end with: + +- **Preserve** — behavior the history says was paid for. +- **Change** — what the original reason no longer justifies. +- **Avoid** — approaches the record shows were already tried and failed. +- **Risk** — what breaks if the recovered reason is wrong. + +Hand that set to the plan. `alternatives-considered` treats **Avoid** as +already-explored ground. + +## Do not + +- Treat the most recent commit as authoritative. The current shape is + usually accretion, not a decision. +- Report a PR body's claim as a fact about today's code without checking the + code still matches it. +- Answer inline from one commit unless you can say why every other available + source would have been redundant. diff --git a/product/skills/why/tests/fires_example.md b/product/skills/why/tests/fires_example.md new file mode 100644 index 00000000..02e9a3c6 --- /dev/null +++ b/product/skills/why/tests/fires_example.md @@ -0,0 +1,12 @@ +A user is cleaning up a retry helper and asks: "why is the retry limit set +to five? does that reason still hold, or can I raise it?" + +This skill fires. It is a rationale question about an existing constant, not +a behavior question, and it precedes a change — the skill's stated trigger. +Step 1 anchors inline with `git blame -L` on the constant, `git log +--oneline` for the PR number, and `gh pr view` on the merge commit before +any subagent spawns. Step 2 fans out one investigator per source actually +available here, and a source with no MCP gets a null row plus a written +reason rather than being dropped. The report separates what the record says +from what is inferred, and Step 4 converts the answer into +Preserve / Change / Avoid / Risk for the edit that follows. diff --git a/product/skills/why/tests/stays_silent_example.md b/product/skills/why/tests/stays_silent_example.md new file mode 100644 index 00000000..a7f6ce9c --- /dev/null +++ b/product/skills/why/tests/stays_silent_example.md @@ -0,0 +1,10 @@ +A user asks the agent to add a `--json` flag to a CLI command that has no +existing output-format handling, on a file created in this same session. + +This skill stays silent. There is no lineage to recover: the code was +written minutes ago in the current session, there is no blame history, no +merged PR, and no prior decision anyone could have written down. Step 1's +anchor commands would return the session's own commit or nothing at all, and +fanning investigators across an issue tracker and team chat for a file that +never existed before today would produce nothing but null rows. The +motivation is in the user's request, already stated.