@jjanczur/tyran 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.claude-plugin/marketplace.json +22 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/CHANGELOG.md +245 -0
  4. package/LICENSE +201 -0
  5. package/README.md +468 -0
  6. package/agents/.gitkeep +0 -0
  7. package/agents/implementer.md +60 -0
  8. package/agents/retro.md +88 -0
  9. package/agents/reviewer.md +55 -0
  10. package/agents/scout.md +60 -0
  11. package/bin/tyran.mjs +172 -0
  12. package/hooks/HOOK-CONTRACT-MEASURED.md +377 -0
  13. package/hooks/hooks.json +83 -0
  14. package/hooks/scripts/.gitkeep +0 -0
  15. package/hooks/scripts/evidence-gate.mjs +705 -0
  16. package/hooks/scripts/hook-io.mjs +813 -0
  17. package/hooks/scripts/policy-gate.mjs +1402 -0
  18. package/hooks/scripts/pre-compact.mjs +211 -0
  19. package/hooks/scripts/retro-gate.mjs +191 -0
  20. package/hooks/scripts/secrets-gate.mjs +1683 -0
  21. package/hooks/scripts/session-start.mjs +358 -0
  22. package/hooks/scripts/write-guard.mjs +475 -0
  23. package/package.json +52 -0
  24. package/scripts/desc-budget.mjs +139 -0
  25. package/scripts/doctor.mjs +1267 -0
  26. package/scripts/hooks-check.mjs +1312 -0
  27. package/scripts/invisible.mjs +346 -0
  28. package/scripts/journal.mjs +747 -0
  29. package/scripts/project.mjs +981 -0
  30. package/scripts/scan-control-chars.mjs +547 -0
  31. package/scripts/scan-repo.mjs +287 -0
  32. package/scripts/schema.mjs +467 -0
  33. package/scripts/stop-check.mjs +89 -0
  34. package/scripts/tiers.mjs +324 -0
  35. package/scripts/yaml-lite.mjs +383 -0
  36. package/skills/browser-check/SKILL.md +118 -0
  37. package/skills/code-review/SKILL.md +83 -0
  38. package/skills/deslop/SKILL.md +97 -0
  39. package/skills/doctor/SKILL.md +35 -0
  40. package/skills/fidelity-gate/SKILL.md +132 -0
  41. package/skills/hello/SKILL.md +16 -0
  42. package/skills/pr-feedback/SKILL.md +85 -0
  43. package/skills/prompt-tuning/SKILL.md +102 -0
  44. package/skills/retro/SKILL.md +69 -0
  45. package/skills/root-cause/SKILL.md +89 -0
  46. package/skills/run/SKILL.md +285 -0
  47. package/skills/setup/SKILL.md +79 -0
  48. package/skills/skill-writing/SKILL.md +99 -0
  49. package/skills/status/SKILL.md +31 -0
  50. package/templates/.gitkeep +0 -0
  51. package/templates/config.yaml +44 -0
  52. package/templates/knowledge.yaml +23 -0
  53. package/templates/policies/autonomy.yaml +61 -0
  54. package/templates/project-command/SKILL.md +16 -0
@@ -0,0 +1,35 @@
1
+ ---
2
+ description: Check that Tyran itself is healthy in this repo - gates that cannot fire, journal and projection drift, orphaned leases, dead policy rules, config that fails its schema. Reports findings with the fix for each. Use for /tyran:doctor or when a gate seems not to be working.
3
+ ---
4
+
5
+ # Doctor
6
+
7
+ Three checks, in this order. Run all three even if the first is clean — they
8
+ fail independently.
9
+
10
+ ```bash
11
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/doctor.mjs" --hooks
12
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/doctor.mjs" --state
13
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/schema.mjs" validate config .tyran/config.yaml
14
+ ```
15
+
16
+ **`--hooks` is the one that matters most**, and it is worth saying why. Hooks
17
+ fail OPEN: a gate whose file is missing, whose execute bit was lost, or whose
18
+ matcher can never match does not degrade into a weaker check — the action it
19
+ was meant to stop simply proceeds, with nothing printed anywhere. A dead gate
20
+ and a healthy gate look identical from inside a session. This check is the
21
+ only thing that can tell them apart.
22
+
23
+ **`--state` compares the journal against its projections** and finds drift,
24
+ leases held by agents that have long since reported, and policy rules that
25
+ match no path in the repo.
26
+
27
+ ## Reporting
28
+
29
+ Paste the raw output. For each finding give the fix the tool named, and say
30
+ plainly which findings are errors and which are informational — a report that
31
+ blurs the two trains the operator to skim the next one.
32
+
33
+ If everything is clean, say so in one line with the counts, and resist adding
34
+ advice. A clean doctor run that arrives with a list of suggestions reads as a
35
+ failed one.
@@ -0,0 +1,132 @@
1
+ ---
2
+ description: Build a UI against a frozen visual reference without drift. An inventory extracted from the reference before any code, a relics list for what exists but should not, and a gate that MEASURES rather than judges - computed styles on a fixture carrying the reference's own data. Use when a mockup, design spec or screenshot is the contract.
3
+ ---
4
+
5
+ # Fidelity gate — building against a frozen visual reference
6
+
7
+ > Drift that goes uncaught on the first screen multiplies onto every screen
8
+ > after it. Measured on one initiative that skipped this: four waves of
9
+ > rework plus a reconciliation audit, because "looks right" had been accepted
10
+ > as a verdict fourteen times.
11
+
12
+ ## The division of labour, which is the whole idea
13
+
14
+ **Appearance is read by a MACHINE. Intent is described in WORDS.**
15
+
16
+ Text, typography, colour, geometry and icons are extracted from the
17
+ reference *mechanically*. Never let a model retype them into prose: every
18
+ restatement is a **lossy copy**, and the source is more precise than any
19
+ description of it — `22px/700/#0a2540` has exactly one interpretation, and
20
+ "large bold dark-blue heading" has hundreds.
21
+
22
+ Words describe only what a static reference cannot contain: which content is
23
+ an example and which is binding, where the data comes from, visibility
24
+ conditions, the empty state, and the states that do not render (hover, focus,
25
+ error).
26
+
27
+ ## The overriding rule
28
+
29
+ **The reference is the only source of visual truth.** Handoffs, tickets and
30
+ prose describe BEHAVIOUR; appearance is settled by the reference alone. When
31
+ prose disagrees with the reference about how something looks, **the reference
32
+ wins**, you decide without asking, and you record the divergence.
33
+
34
+ - An element **not** in the reference **does not exist**. Relics of older
35
+ code are not carried over without an owner's decision.
36
+ - An element **in** the reference **must be built** — if necessary as an
37
+ honest-empty version in the reference's own geometry.
38
+
39
+ ## Step 0 — the reference lives in the repo, verified
40
+
41
+ Pull it into the repo and check it is whole before trusting it: size, the
42
+ file actually ends where it should, and it renders. Work only from the
43
+ in-repo copy. An incomplete reference is a STOP and a report, not something
44
+ to work around — half a contract silently becomes "use your judgement".
45
+
46
+ ## Step 1 — the inventory, before the first line of code
47
+
48
+ Extract a machine-readable checklist from the reference. One row per element:
49
+
50
+ | # | Element | Literal text (with capitalisation) | Typography | Colours | Geometry | Icon | States and conditions |
51
+
52
+ Hard requirements, each of which has been violated at real cost:
53
+
54
+ - **Literal text**, including format, capitalisation and separators. A 1px
55
+ rule between two phrases is an element and gets a row.
56
+ - **Icons are carried over VERBATIM** — the reference's own markup or asset.
57
+ Substituting an icon library because a glyph "looks the same" is
58
+ forbidden. A similar icon is not that icon.
59
+ - **Every surface with a specific colour, and every line with its extent** —
60
+ where it starts and where it stops.
61
+ - **Conditional elements carry the condition** in the row.
62
+
63
+ If the repo has a comparator, it GENERATES the inventory and you only add the
64
+ states column. Write it by hand only where no tool exists yet.
65
+
66
+ The conductor spot-checks five rows against the rendered reference before the
67
+ work starts. A wrong inventory produces a confidently wrong screen.
68
+
69
+ ## Step 2 — the diff, when the view already exists
70
+
71
+ Before writing code, every inventory row gets **MATCHES / DRIFT / MISSING**,
72
+ and every element present in the code but absent from the reference goes on a
73
+ **RELICS** list with a disposition: remove (the default) or an owner gate when
74
+ it is load-bearing.
75
+
76
+ "Apply the differences" without a list of the differences is forbidden. That
77
+ list *is* the plan for the work.
78
+
79
+ ## Step 3 — implement the inventory, and only the inventory
80
+
81
+ If the reference uses a token, the code uses **that** token. If the reference
82
+ defines a token and never uses it, the code does not use it either. A
83
+ deviation from the reference is only ever a recorded owner decision, never an
84
+ implementer's taste.
85
+
86
+ ## Step 4 — the gate is a MEASUREMENT, not an opinion
87
+
88
+ 1. **A fixture carrying the reference's own data.** Same entity, same period,
89
+ same numbers. Then every difference in a screenshot is a difference of
90
+ appearance, not of content — which is the entire reason this step exists.
91
+ 2. **Side-by-side captures** at the reference's own widths.
92
+ 3. **Computed styles dumped to JSON** for the inventory's selectors and
93
+ compared against the inventory's values. A disagreement about whether
94
+ something is 9px or 10.5px is settled by the dump, never by looking.
95
+ `browser-check` carries how — the capture, the dump, the deterministic
96
+ waits, and the rule that a zero-size box is a failure rather than a pass.
97
+ This gate owns the inventory and the verdict; it does not own the browser.
98
+ 4. **The verdict is the filled-in checklist**, row by row, attached to the
99
+ report. A narrative verdict — *"looks consistent"*, *"differences are
100
+ data-only"* — is REJECTED without it. This is the single rule that decides
101
+ whether the gate stays a gate.
102
+ 5. Failures are fixed in the same piece of work. It does not merge with
103
+ failures unless the owner says so.
104
+
105
+ Three things that turn this gate back into an opinion, all observed:
106
+
107
+ - **A deliberate skip is not a match.** Every deviation carries a reason and
108
+ lands in an explicit **debt** section of the report.
109
+ - **Raising a tolerance until the run goes green** is forbidden. That is
110
+ changing the measurement to fit the answer.
111
+ - **A structural selector without a text assertion** silently measures the
112
+ wrong element when the markup is renumbered. Anchor by an explicit
113
+ attribute where you can, and always assert the text — a renumbering must
114
+ produce a loud MISSING, never a quiet wrong reading.
115
+
116
+ Warm the routes up before a batch run. A cold compile produces failures that
117
+ have nothing to do with fidelity, and chasing them costs a round.
118
+
119
+ ## Step 5 — when the data cannot fill the design
120
+
121
+ - The element **stays**, honest-empty, in the reference's geometry and style.
122
+ Hiding a whole section because data is missing needs an **owner decision** —
123
+ a hidden section is indistinguishable from drift.
124
+ - Missing data is a backend item raised in the report. "Pretty" versus raw
125
+ values is also a data question; the front end must not hardcode a mask over
126
+ it without a decision.
127
+
128
+ ## Step 6 — owner acceptance
129
+
130
+ At the phase gate the owner sees paired captures of full views. The checklist
131
+ catches what is measurable; the owner catches the rest. Neither replaces the
132
+ other, and offering only one of them is how a gate becomes a formality.
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: Smoke-test skill that verifies the Tyran plugin is installed and namespaced correctly. Invoke with /tyran:hello.
3
+ disable-model-invocation: true
4
+ ---
5
+
6
+ # Hello (installation smoke test)
7
+
8
+ Reply with exactly one short paragraph:
9
+
10
+ 1. Confirm the Tyran plugin is loaded and state its version by reading
11
+ `${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json`.
12
+ 2. State the resolved value of `${CLAUDE_PLUGIN_ROOT}` so the user can see
13
+ where the plugin is installed from.
14
+
15
+ Do not perform any other action. This skill exists only to verify
16
+ installation, namespacing, and path resolution.
@@ -0,0 +1,85 @@
1
+ ---
2
+ description: Work a reviewer's comments on a pull request to the end. Enumerate all three comment surfaces before triaging any, give every comment a disposition - fixed, declined with a reason, or ticketed - push before you reply, and resolve only what you addressed. Use when a PR comes back with review feedback or a red check.
3
+ ---
4
+
5
+ # PR feedback — closing the loop with a human reviewer
6
+
7
+ > The safest autonomy class, and the default, ends with a change on a branch
8
+ > and a person merging it. So the ordinary last step of this plugin's work is a
9
+ > pull request somebody reviews — and it was the one step with no protocol.
10
+ > `tyran:reviewer` governs our own internal review; this governs theirs.
11
+
12
+ ## The three surfaces, and why one of them is a trap
13
+
14
+ GitHub keeps pull-request feedback in three separate resources:
15
+
16
+ | Surface | Endpoint | What lives there |
17
+ |---|---|---|
18
+ | Conversation | `/issues/{n}/comments` | Plain comments on the PR |
19
+ | Inline review comments | `/pulls/{n}/comments` | Anchored to a line of the diff |
20
+ | Reviews | `/pulls/{n}/reviews` | The submitted verdict **and its body text** |
21
+
22
+ **A review's body is not in the inline-comments collection.** Reviewers write
23
+ their summary there, and bots put whole findings there — anything that could not
24
+ be anchored to a changed line, which is exactly the "outside diff range" class.
25
+
26
+ Measured, against a public repository: `cli/cli` PR **#13944** has one review
27
+ carrying a written body and **zero** inline comments. An agent that reads only
28
+ `/pulls/{n}/comments` sees nothing on that PR and reports that there was nothing
29
+ to address. The report is true about what it looked at and false about what it
30
+ claims, which is the failure shape this whole plugin exists to refuse.
31
+
32
+ So: **fetch all three, paginated, before triaging any of them.** Print the
33
+ counts. `3 inline · 2 review bodies · 1 conversation` is the first line of the
34
+ work, and it is what makes a missed surface visible.
35
+
36
+ ## Every comment gets a disposition
37
+
38
+ No comment is silently dropped. Each one ends as exactly one of:
39
+
40
+ - **Fixed** — with the commit that did it.
41
+ - **Declined** — with the reason, in one sentence. Disagreeing is allowed;
42
+ disagreeing silently is not. If it is a product or scope decision rather than
43
+ a technical one, it goes to the conductor instead of being decided here.
44
+ - **Ticketed** — real, out of this PR's scope, captured where it will be found
45
+ again. A promise in a thread is not a ticket.
46
+
47
+ Nitpicks are answered too, briefly. An unanswered comment reads as unread, and
48
+ the reviewer's next move is to check whether you read the others.
49
+
50
+ **A comment that names a defect gets pinned as a test**, not just patched. The
51
+ same rule the reviewer applies internally: a finding that cannot be verified as
52
+ fixed will be found again by someone else, later, at more cost.
53
+
54
+ ## Order of operations
55
+
56
+ 1. **Fix, then push, then reply.** "Fixed in `abc1234`" posted before the push
57
+ is a claim about a commit the reviewer cannot fetch. Push first; quote the
58
+ real sha.
59
+ 2. **Resolve only threads you actually addressed.** Resolving a thread you
60
+ declined hides the disagreement — say why, and leave it for them to close.
61
+ Never resolve someone else's open question to tidy the view.
62
+ 3. **A red check is feedback.** Read the failing job's own output, not the
63
+ summary. And gate on the runner's exit code: a pipeline that pipes a test
64
+ run into `grep` and pushes on success pushes on a red suite too, because
65
+ `grep` was satisfied.
66
+ 4. **Re-check the three surfaces after you push.** Reviews arrive while you
67
+ work, and CI posts after it finishes. A loop that reads once at the start
68
+ ends by declaring victory over a snapshot.
69
+
70
+ ## When to stop and ask
71
+
72
+ - The comment asks for a change that crosses a boundary the story did not own —
73
+ an API shape, a schema, someone else's module.
74
+ - Two reviewers ask for opposite things. Do not pick; put both in front of the
75
+ conductor with a recommendation.
76
+ - The review asks for a rewrite large enough to be its own story. Say so with a
77
+ size, rather than quietly starting it inside a feedback round.
78
+
79
+ ## The report
80
+
81
+ Counts per surface, the disposition of every comment with its thread, the shas
82
+ that carry the fixes, the checks that are now green with their raw output, and
83
+ anything still open with the reason. If you declined something, that line is the
84
+ most important one in the report — it is the only part the reviewer cannot see
85
+ by looking at the diff.
@@ -0,0 +1,102 @@
1
+ ---
2
+ description: Iterate on a prompt, or on anything whose quality is measured by non-deterministic model output, without chasing noise. A noise baseline before the first edit, medians over repeated runs, enforcement AFTER generation rather than in the wording, and detectors that surface candidates rather than defects.
3
+ ---
4
+
5
+ # Prompt tuning — working with non-deterministic output
6
+
7
+ > Every rule here has a measured cost behind it. The first one alone accounts
8
+ > for three tuning rounds spent moving a number that had never moved.
9
+
10
+ ## Measure before you change anything
11
+
12
+ **1. Take a noise baseline BEFORE the first edit.** Same prompt, same inputs,
13
+ at least two runs; record the spread of every metric you care about. A delta
14
+ smaller than the spread is noise, not the effect of your change. To compare
15
+ two versions, run each at least three times per input and compare **medians**.
16
+
17
+ Measured: ±20% output length and a jumping paragraph count **with no change
18
+ to the prompt at all**. Three rounds of tuning were spent chasing that before
19
+ anyone ran the same prompt twice.
20
+
21
+ **2. Measure conversion factors between representations; never assume them.**
22
+ One initiative assumed a 1.15× expansion between two languages and measured
23
+ 1.3–1.4×. The assumption would have invalidated the entire budget built on
24
+ it. When a factor's spread is wider than your target band, no source-side
25
+ budget can guarantee the target — catch the tail with a gate **after** the
26
+ transformation instead of tightening the budget before it.
27
+
28
+ **3. After three failed rewordings of the same defect on the same case, name
29
+ it a KNOWN LIMITATION** and hand it to a human. A fourth blind iteration is
30
+ chasing noise with extra steps.
31
+
32
+ ## Writing the prompt
33
+
34
+ **4. Every numeric example becomes a quota; every template phrase becomes a
35
+ verbatim anchor.** Describe the CRITERION, not the illustration. "Two threads
36
+ means two paragraphs" produced two-paragraph output in six samples out of
37
+ six, where an example of a two-paragraph answer had not. State criteria in
38
+ **both directions** — a one-sided formulation works only in the direction you
39
+ named.
40
+
41
+ **5. A rule in a prompt is a request. Enforcement is a mechanism that runs
42
+ AFTER generation** — a gate plus regeneration with a targeted nudge.
43
+ Confirmed on four separate properties: length ceiling, presence of figures,
44
+ language of a recommendation, and post-translation condensation.
45
+
46
+ Put a safety catch on the source side: when the source genuinely lacks the
47
+ property you are demanding, do not demand it. A gate that requires figures
48
+ from a source containing none is an instruction to fabricate.
49
+
50
+ **6. Volume minimums are SOFT (log them), ceilings are hard.** Regenerating
51
+ because output was "too short" produces padding, reliably.
52
+
53
+ **7. The target quoted to the model sits below the threshold the gate
54
+ enforces.** A sample two characters over the ceiling cost two paid
55
+ regenerations. Both numbers come from ONE constants module — every hand-copy
56
+ of a constant into a script, a prompt, a test or a message to the model is a
57
+ future lie, and it will be discovered by the number that did not get updated.
58
+
59
+ ## Building the gate
60
+
61
+ **8. On fail-open, return the BEST candidate — not the last one, and not the
62
+ current one.** Rank by *weighted* severity: regulatory beats content beats
63
+ length, ties broken by length. Counting violations without weights is a fake
64
+ ranking, and it has a predictable failure: text with no figures is shorter,
65
+ so it wins. The accepted sample must be exactly what reaches the payload
66
+ **and** the cache; watch for "last one wins" when steps are written, or you
67
+ poison the cache with a candidate you rejected.
68
+
69
+ **9. Measure the gate's own cost before wiring it in.** A regex reading a
70
+ whole source was a hot path at 835 ms; fixing its quantifiers took it to
71
+ 0.2 ms. Add a performance test with a threshold, or the next one will not be
72
+ noticed either.
73
+
74
+ **10. A prompt in a database plus code with placeholders forces a deployment
75
+ ORDER**: the code that supplies the variables ships first, the prompt second.
76
+ Add a version guard so the gate is inert until the prompt reaches the version
77
+ that feeds it, and log any placeholder the build did not supply.
78
+
79
+ ## Judging output, and detectors
80
+
81
+ **11. A detector finds CANDIDATES, not defects.** Especially across languages
82
+ or formats. Confront every hit with the source before reporting it, or you
83
+ will report your own regex's shortcomings as the product's. Three false
84
+ alarms were avoided exactly this way. Choose the direction of the check with
85
+ the lower false-positive background: for translations, look for *invented*
86
+ content rather than *lost* content.
87
+
88
+ **12. Judge against the SOURCE, not against world knowledge.** "Correcting" a
89
+ fact to match what you know is a grounding violation, not a fix. One
90
+ fabrication charge collapsed when the source turned out to use the disputed
91
+ name 63 times.
92
+
93
+ **13. Audit samples with the strongest tier available.** A cheap tier
94
+ produced false negatives and demanded content the prompt explicitly forbade.
95
+ And remember the audit is itself non-deterministic: a borderline rejection is
96
+ a signal for a HUMAN auditor, not a trigger for another prompt iteration.
97
+
98
+ **14. Sweep for residue by CLASS, not by known spellings.** Search for the
99
+ value — the old ceiling `35` — not for the wording of a label. Check also
100
+ whether a test is PINNING an incorrect label into place. Pin a reviewer's
101
+ counterexamples as MUST-PASS tests: one such test caught a fix that was only
102
+ half done, before it came back around.
@@ -0,0 +1,69 @@
1
+ ---
2
+ description: Close an initiative by learning from it. Spawns the retrospective agent over the ledger, the notes and the agents reports, records what it changed and what it rejected, and writes durable facts about this repo into .tyran/knowledge/. Runs automatically at the end of an initiative; also available as /tyran:retro.
3
+ ---
4
+
5
+ # Retro
6
+
7
+ The only loop through which Tyran gets better at *this* repo instead of
8
+ repeating the same mistake every initiative.
9
+
10
+ **You normally do not have to invoke this.** The `Stop` gate refuses one turn
11
+ when an initiative has all its tickets merged and no retrospective recorded
12
+ since the last merge. This skill is what you run when it does — or when you
13
+ want one early.
14
+
15
+ ## 1. Spawn the retrospector
16
+
17
+ Spawn `tyran:retro` at the tier `node "${CLAUDE_PLUGIN_ROOT}/scripts/tiers.mjs"
18
+ --role retro` resolves, over the initiative's `PLAN.md`, `NOTES.md`, the
19
+ agents' reports, and the initiative's git log.
20
+
21
+ Do not do this work yourself in the conducting session. The retrospector's
22
+ value comes from reading the record cold; a conductor reviewing its own
23
+ initiative rates its own decisions, and rates them well.
24
+
25
+ ## 2. Record the outcome — including "nothing"
26
+
27
+ Every retro ends with a journal entry, whatever it concluded:
28
+
29
+ ```bash
30
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/journal.mjs" append "$J" retro.entry "$INIT" \
31
+ --actor retro --data '{"kind":"skill","target":"skills/run/SKILL.md","confidence":"medium"}'
32
+ ```
33
+
34
+ `kind` is `skill`, `agent`, `script`, `doc`, `knowledge` — or **`skipped`**
35
+ with the reason, when the judgement is that this initiative has nothing worth
36
+ changing. That is a correct outcome and it settles the gate exactly like any
37
+ other. A retro that always finds something is not learning, it is padding.
38
+
39
+ **When `kind` is `skill`, follow `skill-writing`.** New skills are AUTO class —
40
+ you may commit one without asking — and that is only safe against a standard.
41
+ It carries the admission test (a skill nothing already points at is a library
42
+ entry), the description budget you are spending on every future session, and
43
+ the activation test that proves the thing fires at all. A skill that never
44
+ triggers costs its description on every turn and returns nothing.
45
+
46
+ ## 3. Write down what is true about THIS repo
47
+
48
+ Durable facts go to `.tyran/knowledge/<topic>.yaml`, which is AUTO class —
49
+ the retrospector may write it without asking:
50
+
51
+ ```bash
52
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/schema.mjs" validate knowledge .tyran/knowledge/<topic>.yaml
53
+ ```
54
+
55
+ An entry is a fact, convention, gotcha, command or decision, with a
56
+ confidence, its provenance, and the paths it applies to. The counters
57
+ (`used`, `helpful`, `outdated_reports`) exist so a later retrospective can
58
+ retire an entry that stopped earning its keep — knowledge that only ever
59
+ grows is a context tax with a good story attached.
60
+
61
+ Write what a competent newcomer would get wrong, not what the code already
62
+ says. "The test suite takes 9 minutes, so do not run it per file" is worth an
63
+ entry. "This is a Next.js app" is not: the next agent can see that.
64
+
65
+ ## 4. Report
66
+
67
+ Five lines: candidates considered, implemented, rejected and why, what was
68
+ deleted or merged, what is left for next time. Rejections are as valuable as
69
+ changes — they stop the next retrospective relitigating the same idea.
@@ -0,0 +1,89 @@
1
+ ---
2
+ description: Find the mechanism behind a failure instead of patching its symptom. Reproduce first, change one variable per experiment with the prediction written before the run, bisect rather than re-read, and exit by naming the mechanism and pinning it with a failing test. Use for a bug, an unexplained red test, or a failure that will not reproduce.
3
+ ---
4
+
5
+ # Root cause — working a failure down to its mechanism
6
+
7
+ > This is the situation the conductor is told to spend its most expensive
8
+ > reasoning on: a diagnosis, a failure nobody can reproduce, two agents who
9
+ > disagree. Spending more thinking on an undisciplined search just produces a
10
+ > more confident wrong answer, so here is the discipline.
11
+
12
+ ## Reproduce first. Everything else is downstream of this
13
+
14
+ **A fix for a bug you cannot reproduce is a guess with a diff attached.** You
15
+ cannot tell whether it worked, and neither can the reviewer — the bug's absence
16
+ after the change is indistinguishable from the bug's absence before it.
17
+
18
+ Get to the smallest reliable reproduction you can, and write down the exact
19
+ command, input and environment. If it reproduces only sometimes, capture the
20
+ rate — "3 of 20 runs" is a fact you can measure against later, "it is flaky" is
21
+ not. If it will not reproduce at all, that is now the task: what does the
22
+ failing environment have that yours does not — version, data, timezone,
23
+ concurrency, permissions, a warm cache?
24
+
25
+ ## Read the whole error
26
+
27
+ The first line names where it surfaced, not where it went wrong. Read the full
28
+ stack, the frames from your own code in particular, and everything logged in the
29
+ seconds before. The answer is stated outright more often than anyone expects,
30
+ which is why "I read the error" and "I read the first line of the error" have to
31
+ be different sentences.
32
+
33
+ ## One variable, and write the prediction first
34
+
35
+ Change ONE thing per experiment, and **write down what you expect to happen
36
+ before you run it.** An experiment whose outcome you did not predict teaches
37
+ almost nothing: whatever comes back, it confirms something you already believed.
38
+ A prediction that turns out wrong is the single most informative event available
39
+ to you, and you only get it by committing first.
40
+
41
+ Two changes at once and you have learned nothing about either.
42
+
43
+ ## Halve the surface
44
+
45
+ Do not re-read the code hoping to spot it. **Bisect.**
46
+
47
+ - Over time: `git bisect` against a command that exits non-zero on the failure.
48
+ A scriptable check turns an afternoon into a few minutes.
49
+ - Over the input: cut it in half, keep the half that still fails, repeat.
50
+ - Over the system: disable, stub or bypass half the pipeline. Where does the
51
+ value stop being correct? Instrument at the boundary between two components
52
+ before you go inside either.
53
+
54
+ ## It is your code before it is the library
55
+
56
+ The base rate favours the code changed most recently by the fewest people —
57
+ yours. Suspect the dependency only after the evidence points there, and then
58
+ **read the installed version's source**, not your memory of the API and not the
59
+ documentation for a version you might not have. `node_modules` is on disk; the
60
+ answer is a `grep` away, and a remembered signature has already sent people
61
+ down a day of the wrong road.
62
+
63
+ ## When three hypotheses have died
64
+
65
+ The wrong assumption is one you have not written down. Stop testing and list
66
+ what you believe is true about the system — which file is loaded, which branch
67
+ is running, which build is deployed, which database you are pointed at, that the
68
+ change you made is even in the process you are running. Then test the cheapest
69
+ item on that list.
70
+
71
+ Almost every long debugging session ends here: something everyone was certain
72
+ of was not true. `prompt-tuning` has the sibling rule for non-deterministic
73
+ work — after three failed attempts at the same defect, name it a known
74
+ limitation and hand it to a person rather than taking a fourth blind swing.
75
+
76
+ ## Exiting
77
+
78
+ 1. **Name the MECHANISM, not the symptom.** "The cache key omits the locale, so
79
+ the second request serves the first request's body" is a root cause. "The
80
+ page showed the wrong language" is the symptom you started with.
81
+ 2. **Pin it with a failing test before the fix.** Written after the fix, a test
82
+ only proves the code's current behaviour; written before, it proves the bug
83
+ existed and is gone. This is also what the reviewer will ask for.
84
+ 3. **Check for siblings.** The same mechanism usually has other victims —
85
+ another cache key, another unhandled path. Find them now, while you
86
+ understand it.
87
+ 4. **Report the mechanism, the evidence, the fix and the blast radius**, plus
88
+ the hypotheses you killed. Those save the next person from re-running your
89
+ dead ends, and they are the part everyone omits.