@iceinvein/agent-skills 0.21.4 → 0.22.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 (131) hide show
  1. package/README.md +9 -0
  2. package/package.json +4 -1
  3. package/skills/bug-hunter/SKILL.md +82 -0
  4. package/skills/bug-hunter/evals/README.md +282 -0
  5. package/skills/bug-hunter/evals/baselines/claude-opus-5-5.json +93 -0
  6. package/skills/bug-hunter/evals/bug-cites-intent-from-spec/case.yaml +4 -0
  7. package/skills/bug-hunter/evals/bug-cites-intent-from-spec/fixture.sh +71 -0
  8. package/skills/bug-hunter/evals/bug-cites-intent-from-spec/graders/bug-cites-the-spec.md +36 -0
  9. package/skills/bug-hunter/evals/bug-cites-intent-from-spec/graders/bug-hunter-fired.md +5 -0
  10. package/skills/bug-hunter/evals/bug-cites-intent-from-spec/graders/intent-written.md +8 -0
  11. package/skills/bug-hunter/evals/bug-cites-intent-from-spec/prompt.md +11 -0
  12. package/skills/bug-hunter/evals/callers-tree-unchanged/case.yaml +4 -0
  13. package/skills/bug-hunter/evals/callers-tree-unchanged/fixture.sh +67 -0
  14. package/skills/bug-hunter/evals/callers-tree-unchanged/graders/bug-hunter-fired.md +5 -0
  15. package/skills/bug-hunter/evals/callers-tree-unchanged/graders/source-unchanged.md +10 -0
  16. package/skills/bug-hunter/evals/callers-tree-unchanged/graders/tests-unchanged.md +10 -0
  17. package/skills/bug-hunter/evals/callers-tree-unchanged/graders/tree-unchanged.md +15 -0
  18. package/skills/bug-hunter/evals/callers-tree-unchanged/prompt.md +11 -0
  19. package/skills/bug-hunter/evals/correct-code-has-no-bugs/case.yaml +4 -0
  20. package/skills/bug-hunter/evals/correct-code-has-no-bugs/fixture.sh +82 -0
  21. package/skills/bug-hunter/evals/correct-code-has-no-bugs/graders/bug-hunter-fired.md +5 -0
  22. package/skills/bug-hunter/evals/correct-code-has-no-bugs/graders/bugs-empty.md +18 -0
  23. package/skills/bug-hunter/evals/correct-code-has-no-bugs/graders/written-with-write-tool.md +13 -0
  24. package/skills/bug-hunter/evals/correct-code-has-no-bugs/prompt.md +11 -0
  25. package/skills/bug-hunter/evals/impossible-value-is-a-bug/case.yaml +4 -0
  26. package/skills/bug-hunter/evals/impossible-value-is-a-bug/fixture.sh +68 -0
  27. package/skills/bug-hunter/evals/impossible-value-is-a-bug/graders/bug-for-sixty-seconds.md +37 -0
  28. package/skills/bug-hunter/evals/impossible-value-is-a-bug/graders/bug-hunter-fired.md +5 -0
  29. package/skills/bug-hunter/evals/impossible-value-is-a-bug/prompt.md +11 -0
  30. package/skills/bug-hunter/evals/proves-a-spec-boundary-bug/case.yaml +4 -0
  31. package/skills/bug-hunter/evals/proves-a-spec-boundary-bug/fixture.sh +79 -0
  32. package/skills/bug-hunter/evals/proves-a-spec-boundary-bug/graders/bug-cites-the-limit.md +31 -0
  33. package/skills/bug-hunter/evals/proves-a-spec-boundary-bug/graders/bug-hunter-fired.md +5 -0
  34. package/skills/bug-hunter/evals/proves-a-spec-boundary-bug/graders/findings-written.md +10 -0
  35. package/skills/bug-hunter/evals/proves-a-spec-boundary-bug/prompt.md +11 -0
  36. package/skills/bug-hunter/evals/rules.json +177 -0
  37. package/skills/bug-hunter/evals/silent-spec-is-an-ambiguity/case.yaml +4 -0
  38. package/skills/bug-hunter/evals/silent-spec-is-an-ambiguity/fixture.sh +97 -0
  39. package/skills/bug-hunter/evals/silent-spec-is-an-ambiguity/graders/bug-hunter-fired.md +5 -0
  40. package/skills/bug-hunter/evals/silent-spec-is-an-ambiguity/graders/bugs-empty.md +18 -0
  41. package/skills/bug-hunter/evals/silent-spec-is-an-ambiguity/graders/cap-is-an-ambiguity.md +12 -0
  42. package/skills/bug-hunter/evals/silent-spec-is-an-ambiguity/graders/written-with-write-tool.md +13 -0
  43. package/skills/bug-hunter/evals/silent-spec-is-an-ambiguity/prompt.md +11 -0
  44. package/skills/bug-hunter/evals/spec-outranks-target-docstring/case.yaml +4 -0
  45. package/skills/bug-hunter/evals/spec-outranks-target-docstring/fixture.sh +82 -0
  46. package/skills/bug-hunter/evals/spec-outranks-target-docstring/graders/bug-cites-the-spec.md +40 -0
  47. package/skills/bug-hunter/evals/spec-outranks-target-docstring/graders/bug-hunter-fired.md +5 -0
  48. package/skills/bug-hunter/evals/spec-outranks-target-docstring/prompt.md +11 -0
  49. package/skills/bug-hunter/references/findings-format.md +82 -0
  50. package/skills/bug-hunter/references/scenario-catalog.md +92 -0
  51. package/skills/bug-hunter/skill.json +24 -0
  52. package/skills/index.json +17 -5
  53. package/skills/magpie/evals/README.md +13 -0
  54. package/skills/magpie/evals/baselines/claude-opus-5-5.json +69 -0
  55. package/skills/magpie/evals/rules.json +192 -0
  56. package/skills/magpie/skill.json +1 -1
  57. package/skills/migrate/evals/README.md +13 -0
  58. package/skills/migrate/evals/baselines/claude-opus-5-5.json +129 -0
  59. package/skills/migrate/evals/rules.json +160 -0
  60. package/skills/migrate/skill.json +10 -4
  61. package/skills/scope-estimate/README.md +173 -0
  62. package/skills/scope-estimate/SKILL.md +168 -0
  63. package/skills/scope-estimate/references/capability-map.md +122 -0
  64. package/skills/scope-estimate/references/deliverables.md +60 -0
  65. package/skills/scope-estimate/references/requirements.md +104 -0
  66. package/skills/scope-estimate/references/schedule.md +138 -0
  67. package/skills/scope-estimate/scripts/__tests__/schedule.test.ts +366 -0
  68. package/skills/scope-estimate/scripts/schedule.ts +553 -0
  69. package/skills/scope-estimate/skill.json +19 -0
  70. package/skills/scope-estimate/templates/plan.example.json +33 -0
  71. package/skills/sluice/SKILL.md +35 -12
  72. package/skills/sluice/evals/README.md +111 -6
  73. package/skills/sluice/evals/announcement-reaches-the-ledger/fixture.sh +32 -19
  74. package/skills/sluice/evals/announcement-reaches-the-ledger/graders/routes-main-before-source.md +14 -0
  75. package/skills/sluice/evals/announcement-reaches-the-ledger/prompt.md +2 -2
  76. package/skills/sluice/evals/baselines/claude-opus-5-5.json +237 -0
  77. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/fixture.sh +19 -12
  78. package/skills/sluice/evals/deep-run-fans-out/graders/task-1-merged-back.md +8 -0
  79. package/skills/sluice/evals/deep-run-fans-out/graders/task-2-merged-back.md +8 -0
  80. package/skills/sluice/evals/deep-run-fans-out/graders/task-3-merged-back.md +6 -0
  81. package/skills/sluice/evals/deep-run-fans-out/graders/worktree-per-implementer.md +12 -0
  82. package/skills/sluice/evals/deep-run-hunts-after-the-flip/case.yaml +5 -0
  83. package/skills/sluice/evals/deep-run-hunts-after-the-flip/fixture.sh +258 -0
  84. package/skills/sluice/evals/deep-run-hunts-after-the-flip/graders/bug-hunter-fired.md +5 -0
  85. package/skills/sluice/evals/deep-run-hunts-after-the-flip/graders/four-attempts-allowed.md +34 -0
  86. package/skills/sluice/evals/deep-run-hunts-after-the-flip/graders/fourth-attempt-tested.md +20 -0
  87. package/skills/sluice/evals/deep-run-hunts-after-the-flip/graders/hunt-before-the-final-review.md +21 -0
  88. package/skills/sluice/evals/deep-run-hunts-after-the-flip/graders/record-has-a-hunt-section.md +11 -0
  89. package/skills/sluice/evals/deep-run-hunts-after-the-flip/graders/sluice-fired.md +5 -0
  90. package/skills/sluice/evals/deep-run-hunts-after-the-flip/prompt.md +12 -0
  91. package/skills/sluice/evals/deep-run-survives-a-milestone/fixture.sh +46 -24
  92. package/skills/sluice/evals/fast-verifies-before-handback/case.yaml +4 -0
  93. package/skills/sluice/evals/fast-verifies-before-handback/fixture.sh +73 -0
  94. package/skills/sluice/evals/fast-verifies-before-handback/graders/quiet-flag-implemented.md +5 -0
  95. package/skills/sluice/evals/fast-verifies-before-handback/graders/sluice-fired.md +5 -0
  96. package/skills/sluice/evals/fast-verifies-before-handback/graders/verified-after-the-last-edit.md +18 -0
  97. package/skills/sluice/evals/fast-verifies-before-handback/prompt.md +11 -0
  98. package/skills/sluice/evals/main-hunts-a-checkable-claim/case.yaml +5 -0
  99. package/skills/sluice/evals/main-hunts-a-checkable-claim/fixture.sh +64 -0
  100. package/skills/sluice/evals/main-hunts-a-checkable-claim/graders/announces-main-channel.md +12 -0
  101. package/skills/sluice/evals/main-hunts-a-checkable-claim/graders/bug-hunter-fired.md +5 -0
  102. package/skills/sluice/evals/main-hunts-a-checkable-claim/graders/hunt-after-the-source-edits.md +12 -0
  103. package/skills/sluice/evals/main-hunts-a-checkable-claim/graders/last-page-holds-the-remainder.md +27 -0
  104. package/skills/sluice/evals/main-hunts-a-checkable-claim/graders/remainder-page-tested.md +21 -0
  105. package/skills/sluice/evals/main-hunts-a-checkable-claim/graders/sluice-fired.md +5 -0
  106. package/skills/sluice/evals/main-hunts-a-checkable-claim/prompt.md +12 -0
  107. package/skills/sluice/evals/main-new-interface/graders/shape-agreed-before-building.md +8 -4
  108. package/skills/sluice/evals/main-skips-the-hunt-without-a-claim/case.yaml +5 -0
  109. package/skills/sluice/evals/main-skips-the-hunt-without-a-claim/fixture.sh +73 -0
  110. package/skills/sluice/evals/main-skips-the-hunt-without-a-claim/graders/announces-main-channel.md +12 -0
  111. package/skills/sluice/evals/main-skips-the-hunt-without-a-claim/graders/no-hunt.md +12 -0
  112. package/skills/sluice/evals/main-skips-the-hunt-without-a-claim/graders/skip-said-in-one-line.md +21 -0
  113. package/skills/sluice/evals/main-skips-the-hunt-without-a-claim/graders/sluice-fired.md +5 -0
  114. package/skills/sluice/evals/main-skips-the-hunt-without-a-claim/prompt.md +12 -0
  115. package/skills/sluice/evals/rules.json +364 -0
  116. package/skills/sluice/references/deep-channel.md +64 -18
  117. package/skills/sluice/references/finish.md +8 -2
  118. package/skills/sluice/references/intent.md +4 -2
  119. package/skills/sluice/references/review.md +32 -0
  120. package/skills/sluice/references/status.md +17 -4
  121. package/skills/sluice/references/verify.md +38 -0
  122. package/skills/sluice/scripts/content-digest.sh +65 -0
  123. package/skills/sluice/scripts/run-stats.sh +9 -8
  124. package/skills/sluice/scripts/status.sh +78 -13
  125. package/skills/sluice/scripts/stop-guard.sh +97 -24
  126. package/skills/sluice/skill.json +2 -2
  127. package/skills/terse/evals/README.md +16 -0
  128. package/skills/terse/evals/baselines/claude-opus-5-5.json +105 -0
  129. package/skills/terse/evals/rules.json +161 -0
  130. package/skills/terse/skill.json +1 -1
  131. package/skills/sluice/evals/announcement-reaches-the-ledger/graders/escalates-fast-to-main.md +0 -12
package/README.md CHANGED
@@ -29,6 +29,7 @@ Auto-detects Claude Code, Cursor, Codex, or Gemini CLI. Use `--tool claude` to t
29
29
  | Skill | What it does |
30
30
  |-------|--------------|
31
31
  | **migrate** | Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff: measured surface coverage, an empirically derived capability seam, cited requirements, a parity plan, a batch decision queue, and work items emitted to markdown, GitHub or a flow target with coverage and forecast read back. Bundles a Bun CLI that enforces the coverage arithmetic and phase ordering instead of trusting it. |
32
+ | **scope-estimate** | The lighter path for a proposal: read-only discovery, a capability register with status and disposition, sized functional requirements, contracts-first work packages, and a build schedule across engineers x worktrees at fast, planning and floor rates. Bundles a Bun scheduler that refuses a plan missing or double-counting a requirement, so every figure comes from one reproducible run. |
32
33
 
33
34
  ### Code Architecture
34
35
 
@@ -77,6 +78,14 @@ Skills for reviewing high-level architecture, messaging patterns, and data flow.
77
78
  | **composability-auditor** | McIlroy, Pike, Thompson (Unix Philosophy) | Identifies reuse blockers (hidden I/O, non-standard interfaces, statefulness) and decomposes self-sufficient units into composable pieces. |
78
79
  | **temporal-coupling-detector** | Beck, Fowler (software engineering canon) | Detects hidden ordering dependencies (two-phase init, method order, invisible preconditions) and enforces them through types, parameters, and factory patterns. |
79
80
 
81
+ ### Verification
82
+
83
+ Skills that check code does what it was meant to do, with evidence rather than opinion.
84
+
85
+ | Skill | Based on | What it does |
86
+ |-------|----------|--------------|
87
+ | **bug-hunter** | QA scenario testing; intent-first reproduction | Proves logic bugs with failing repros checked against stated intent. Writes scenarios before reading the code, and reports bugs, ambiguities, and covered scenarios. |
88
+
80
89
  ### Communication
81
90
 
82
91
  Skills that control how the agent communicates: output compression, token efficiency, and response style.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iceinvein/agent-skills",
3
- "version": "0.21.4",
3
+ "version": "0.22.0",
4
4
  "description": "Install agent skills into AI coding tools",
5
5
  "author": "iceinvein",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@
12
12
  "dist",
13
13
  "skills",
14
14
  "!skills/*/evals/results",
15
+ "!skills/*/evals/*/plugins",
15
16
  "!skills/terse/bench/results"
16
17
  ],
17
18
  "scripts": {
@@ -23,6 +24,8 @@
23
24
  "skill:bump:check": "bun run src/cli/index.ts bump --all --dry-run",
24
25
  "build:index": "bun run scripts/build-index.ts",
25
26
  "skill:audit": "bun run scripts/audit-skills.ts",
27
+ "eval:fresh": "bun run scripts/eval-freshness.ts",
28
+ "eval:plugins": "bun run scripts/eval-plugins.ts",
26
29
  "release": "bash scripts/release.sh"
27
30
  },
28
31
  "keywords": [
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: bug-hunter
3
+ description: Use when asked to hunt for bugs or edge cases in a change or area, to say what QA will find before QA does, or to prove a change correct. Runs QA-style scenarios as failing reproductions in the repo's own test runner and reports only bugs it can prove, plus the questions intent leaves open.
4
+ ---
5
+
6
+ # Bug hunter
7
+
8
+ Agent-written code passes its own tests and still fails when QA hits a
9
+ scenario nobody wrote down. This skill runs the code and proves each bug with
10
+ a failing reproduction, checked against what the change was meant to do.
11
+
12
+ ## Method
13
+
14
+ 1. **Name the target** in one line: a diff (a commit, a range, the working
15
+ changes) or a set of paths. Every later step is scoped to it.
16
+ 2. **Collect intent** into `intent.md`: numbered claims (`I1`, `I2`, ...),
17
+ each one checkable statement with its source. Sources are the request or
18
+ ticket, commit messages, specs and designs, docs, `CLAUDE.md`/`AGENTS.md`,
19
+ invariants stated in unchanged code, sibling implementations the target
20
+ says it mirrors, and existing tests on unchanged code. Read the target's
21
+ code as much as you need, but a claim never comes from what a changed body
22
+ does: that is the code confirming itself.
23
+ 3. **Hunt.** Write scenarios the way QA would into `scenarios.md` (`S1`,
24
+ `S2`, ..., each with a one-line label, the expected result and the claim
25
+ it rests on), working through `references/scenario-catalog.md`, adding
26
+ more as the code and failures suggest. For each one worth trying write a
27
+ repro in the repo's own test framework that asserts the expected result.
28
+ Find the runner from the repo's manifests and `CLAUDE.md`/`AGENTS.md`; do
29
+ not install a test dependency. Favour many cheap probes over a few
30
+ elaborate ones, and chase what a failure suggests.
31
+ 4. **Check each failure before reporting it.** Run its repro alone, clean,
32
+ with only its own files, and confirm it fails with the observed value.
33
+ Confirm the repro tests the target and not its own setup: a mock returning
34
+ what the real dependency never would, or a fixture breaking an invariant
35
+ the caller guarantees, is not a bug.
36
+ 5. **Sort** each result with the rules below, and write the output.
37
+
38
+ Work in a scratch copy (a `git worktree`, or the copy the caller names). Never
39
+ modify the caller's tree: no repro, install or build artefact lands there.
40
+
41
+ ## What counts as a bug
42
+
43
+ No repro, no finding: a bug carries a reproduction that fails in a clean run
44
+ and names the claim it violates.
45
+
46
+ - **A concrete claim, contradicted,** is a bug citing that claim.
47
+ - **Claims have a rank.** The request, ticket, spec or design outranks a
48
+ docstring or comment inside the target's changed code, which is the change
49
+ describing itself. A target that follows its own docstring against the spec
50
+ is a bug citing the spec. Only claims of the same rank that disagree make an
51
+ ambiguity.
52
+ - **Some results are wrong under every reading,** and need no stated claim: a
53
+ crash, an error or a blank page from input the target accepts; a call that
54
+ reports success and did not do the work; an earlier action's result landing
55
+ over a later one's; a value shown in a form no reading allows, such as a
56
+ seconds field of 60; one user's data reaching or blocking another user. Add
57
+ the one you rely on to `intent.md` with source `baseline`.
58
+ - **An ambiguity is a question with more than one acceptable answer,** where
59
+ the code picked one and no claim says which: where a cap sits, whether a
60
+ match ignores case. "No spec says so" is not enough; if every acceptable
61
+ answer makes the observed result wrong, it is a bug.
62
+ - **Latent is not a bug.** A defect on a path no production caller reaches
63
+ today is noted in `report.md`, not filed in `bugs`.
64
+
65
+ ## Output
66
+
67
+ Write to `.bug-hunter/<YYYY-MM-DD>-<slug>/` at the repo root: `intent.md`,
68
+ `scenarios.md`, `report.md` for a human, and `findings.json` for a grader or calling skill,
69
+ in the shape `references/findings-format.md` gives. Three buckets: **bugs**
70
+ (scenario, violated claim, expected against observed, repro files and
71
+ command), **ambiguities** (the question for product), **covered** (scenarios
72
+ that passed, with the claim each checked). Write `findings.json` and
73
+ `report.md` with the Write tool, never from a shell command or script.
74
+
75
+ The `.bug-hunter/` directory is the one path in the caller's tree the skill
76
+ writes. Promoting a repro into the tree as a regression test is the caller's
77
+ job.
78
+
79
+ ## Hand-back
80
+
81
+ The run directory, the counts per bucket, then each bug in one line:
82
+ scenario, violated claim, repro command. Name anything not done.
@@ -0,0 +1,282 @@
1
+ # bug-hunter evals
2
+
3
+ Seven cases for `claude plugin eval`. Each scaffolds a small Node repo with a
4
+ `SPEC.md`, one module and a passing `node --test` suite, committed in two steps
5
+ (the spec, then the module) so the target reads as a change. The prompt asks
6
+ what QA will find in the module, the way a user would, and never names the
7
+ skill.
8
+
9
+ | Case | Fixture | What it pins |
10
+ |---|---|---|
11
+ | `proves-a-spec-boundary-bug` | Usernames are 3 to 20 characters inclusive; the code rejects 20 | A bug in `findings.json` citing the limit, with a repro that fails against the fixture |
12
+ | `silent-spec-is-an-ambiguity` | Quantities are "capped"; the spec never says where, the code uses 99 | The cap lands in `ambiguities` and `bugs` is empty |
13
+ | `correct-code-has-no-bugs` | Pagination that meets every stated range at its edge | `bugs` is empty |
14
+ | `callers-tree-unchanged` | A backoff whose first delay is doubled | Nothing changes in the caller's tree outside `.bug-hunter/` |
15
+ | `bug-cites-intent-from-spec` | A coupon that floors where the spec rounds half up | `intent.md` is written, and the bug cites a claim sourced from `SPEC.md`, not from the code's floor |
16
+ | `spec-outranks-target-docstring` | The spec keeps a sealed export on offer when its rewrite fails; the module's docstring and code drop it | A bug citing the spec claim, not an ambiguity between spec and docstring |
17
+ | `impossible-value-is-a-bug` | The spec is silent on rounding; the code shows `60s` and `1m 60s` | A bug for the seconds field of 60, which no rounding rule gives |
18
+
19
+ Every case also has `bug-hunter-fired`, a `tool_used: Skill` grader, so a
20
+ score can be told apart from the model's own habits.
21
+
22
+ ## Running
23
+
24
+ From the repo root. Pin the model, because the eval child does not inherit
25
+ it, and pin the judge, because `--model` does not reach it:
26
+
27
+ ```bash
28
+ claude plugin eval skills/bug-hunter --scaffold --allow-tools Bash Write Edit \
29
+ --model claude-opus-5-5 --judge-model claude-opus-5-5 --runs 1 --ablation none
30
+ ```
31
+
32
+ Add `--keep-temp` to keep each run's sandbox and trace, which is the only way
33
+ to read back why a grader failed. `-j 7` runs the seven cases at once.
34
+
35
+ To check the skill against its last accepted baseline, use `eval:fresh`:
36
+
37
+ ```bash
38
+ bun run eval:fresh run skills/bug-hunter --model claude-opus-5-5
39
+ bun run eval:fresh diff skills/bug-hunter <result.json> [--against <model>]
40
+ bun run eval:fresh accept skills/bug-hunter <result.json>
41
+ ```
42
+
43
+ - `run` runs both arms on your Claude plan (default `--runs 3 -j 2`), then
44
+ diffs the result against `evals/baselines/<model>.json`.
45
+ - `diff` is free: it re-reads an existing result and runs nothing.
46
+ - `accept` writes the baseline. It is always a deliberate step, never part of
47
+ `run`.
48
+
49
+ What each verdict means is in `skills/sluice/evals/README.md`.
50
+ `callers-tree-unchanged` is listed in `vacuousWithout` in `rules.json`:
51
+ `tree-unchanged` passes on a run that writes nothing, so the no-plugin arm
52
+ scores on it without hunting, and the case is never called redundant or
53
+ skill-worse.
54
+
55
+ ## What the graders can and cannot see
56
+
57
+ The run directory is `.bug-hunter/<date>-<slug>/` and the slug is the skill's
58
+ choice. Only `file_exists` resolves a glob; a `regex` or `llm` grader pointed at
59
+ a file needs its exact path and throws on a glob. So:
60
+
61
+ - `findings-written` and `intent-written` read the harness's `files` target,
62
+ one created path per line, and match
63
+ `.bug-hunter/<YYYY-MM-DD>-<slug>/findings.json` (or `intent.md`) there. A
64
+ `file_exists` glob would accept a directory with no date; this holds the
65
+ prefix to the shape SKILL.md states, though not to the day the run ran.
66
+ - `written-with-write-tool` (in `silent-spec-is-an-ambiguity` and
67
+ `correct-code-has-no-bugs`) fails a Bash command that redirects or tees into
68
+ `intent.md`, `findings.json` or `report.md` under `.bug-hunter/`, which is
69
+ what holds the Write-tool rule: the content graders below read a heredoc's
70
+ JSON as readily as a Write call's. A script that opens the file itself
71
+ (`python3`, `node -e`) is not caught by it; the content graders fail that.
72
+ - Every grader that reads the *content* of `findings.json` reads the trace
73
+ instead: the Write tool call, or the shell command, that created it. The
74
+ regex graders (`bugs-empty`, `cap-is-an-ambiguity`) need the JSON to appear
75
+ there as a literal, which holds for a Write call or a heredoc. **They cannot
76
+ see a `findings.json` that a script builds** (`python3 -c` with `json.dump`,
77
+ `node -e` with `JSON.stringify`): the trace then holds the script, not the
78
+ JSON, and the grader fails whatever the file says. The baseline hit this.
79
+ Both judge only the **last** write of `findings.json`: a closing lookahead
80
+ fails the match if any later write of it follows (a Write, an Edit, or a
81
+ shell redirect). Before 0.2.1 they passed on any write, so an empty first
82
+ draft rewritten with bugs passed `bugs-empty`. A later Edit fails them,
83
+ since an Edit's trace entry does not carry the whole file.
84
+ - `bug-cites-the-limit` is an `llm` judge over the trace, so it can read a
85
+ script that builds the file as well as a literal. It sees a head-and-tail
86
+ window of a long trace (about 80k characters of head, 20k of tail), so a
87
+ findings write that lands in the dropped middle is invisible to it. It cannot
88
+ run the repro; "fails in the fixture" is judged against the fixture's code,
89
+ which the rubric quotes. Its rubric also says to judge only the last write.
90
+ - `tree-unchanged` cannot run `git status --porcelain`. It reads the harness's
91
+ `files` target, the list of paths the run created in its working directory,
92
+ and fails on any path outside `.bug-hunter/` or `.git/`. A file `.gitignore`
93
+ hides still counts. The `files` target never lists a file the run only
94
+ modified, so on its own it passes an edit to the scaffold's files.
95
+ `source-unchanged` fails if the buggy line in `src/backoff.js` was
96
+ rewritten, and `tests-unchanged` if `tests/backoff.test.js` gained a test.
97
+ An edit to `package.json` or `SPEC.md` is still unseen. A run that writes
98
+ nothing passes all three, which is why the case also has
99
+ `bug-hunter-fired`.
100
+ - `bug-cites-the-spec` in `bug-cites-intent-from-spec` is an `llm` judge over
101
+ the trace, with the same window and last-write limits as
102
+ `bug-cites-the-limit`. It resolves the bug's `claim` through the `intent`
103
+ array and fails a claim sourced from `src/discount.js`. It cannot tell a
104
+ claim the skill read out of the body but labelled `SPEC.md`; the spec's
105
+ wording ("half cent rounding up") is what makes such a mislabel visible to
106
+ it. This case replaced `scenarios-before-bodies` in 0.4.0, which pinned the
107
+ scenario-before-body order that 0.4.0 dropped.
108
+
109
+ ## Baseline
110
+
111
+ ### Skill 0.4.0, Opus 5.5, 2026-10-13
112
+
113
+ Same command, `-j 7`. Skill 0.4.0 drops the three dispatched roles for one
114
+ agent that may read the code freely, keeping intent collection, the scenario
115
+ catalog, a clean re-run of each failure and the 0.3.0 rules, and reporting
116
+ latent defects without filing them. It is the `variants/thin` arm of the
117
+ pilot benchmark, which proved or judged 3 of 12 answer keys against 2 for the
118
+ plain baseline and 2 for 0.3.0, with every extra finding triaged real, at USD
119
+ 1.83 a run against 3.19 for 0.3.0. USD 3.74 in all, 102s,
120
+ `results/2026-10-13T03-48-17-313Z`. Overall 1.00. One run per case.
121
+
122
+ | Case | 0.3.0 | 0.4.0 | USD (0.4.0) |
123
+ |---|---|---|---|
124
+ | `proves-a-spec-boundary-bug` | 1.00 | 1.00 | 0.77 |
125
+ | `silent-spec-is-an-ambiguity` | 1.00 | 1.00 | 0.44 |
126
+ | `correct-code-has-no-bugs` | 1.00 | 1.00 | 0.34 |
127
+ | `callers-tree-unchanged` | 1.00 | 1.00 | 0.43 |
128
+ | `scenarios-before-bodies` | 1.00 | 1.00 | 0.49 |
129
+ | `spec-outranks-target-docstring` | 1.00 | 1.00 | 0.58 |
130
+ | `impossible-value-is-a-bug` | 1.00 | 1.00 | 0.70 |
131
+
132
+ `scenarios-before-bodies` passed, but 0.4.0 no longer asks for that order: the
133
+ run happened to write `scenarios.md` before reading `src/discount.js`. The case
134
+ pinned a rule the skill dropped, so it was rewritten as
135
+ `bug-cites-intent-from-spec` on the same fixture.
136
+
137
+ ### Skill 0.3.0, Opus 5.5, 2026-10-12
138
+
139
+ Same command, `-j 7`. Skill 0.3.0 ranks claims (a spec, design or request
140
+ outranks a docstring in the changed code), lets a result wrong under every
141
+ reading count as a bug against a `baseline` claim, and keeps ambiguities for
142
+ questions with more than one acceptable answer. It answers the pilot
143
+ benchmark, where 0.2.1 filed two real defects it had reproduced as
144
+ ambiguities. USD 6.55 in all, 228s, `results/2026-10-12T14-46-50-101Z`.
145
+ Overall 1.00. One run per case.
146
+
147
+ The two new cases also scored 1.00 on 0.2.1 (USD 0.91 and 1.07,
148
+ `results/2026-10-12T14-43-31-*`): at fixture size the conflict is plain enough
149
+ that the old rules got it right, so neither reproduces the pilot's miss. They
150
+ stay as guards against the opposite failure. The cases that pin over-reporting,
151
+ `silent-spec-is-an-ambiguity` and `correct-code-has-no-bugs`, still hold on
152
+ 0.3.0: the cap stays a question and the correct module has no bugs.
153
+
154
+ | Case | 0.2.1 | 0.3.0 | USD (0.3.0) |
155
+ |---|---|---|---|
156
+ | `proves-a-spec-boundary-bug` | 1.00 | 1.00 | 1.29 |
157
+ | `silent-spec-is-an-ambiguity` | 1.00 | 1.00 | 1.07 |
158
+ | `correct-code-has-no-bugs` | 1.00 | 1.00 | 0.69 |
159
+ | `callers-tree-unchanged` | 1.00 | 1.00 | 0.62 |
160
+ | `scenarios-before-bodies` | 1.00 | 1.00 | 0.65 |
161
+ | `spec-outranks-target-docstring` | 1.00 | 1.00 | 1.35 |
162
+ | `impossible-value-is-a-bug` | 1.00 | 1.00 | 0.88 |
163
+
164
+ ### Skill 0.2.1, Opus 5.5, 2026-10-12
165
+
166
+ Same command as 0.2.0 below. Skill 0.2.1 writes each findings `scenario` as
167
+ `"<id>: <label>"`, lets the single-agent fallback add a late claim from a
168
+ source other than the changed body, and the `findings.json` graders now judge
169
+ only the last write. USD 5.39 in all, 361s,
170
+ `results/2026-10-12T09-23-52-868Z`. Overall 1.00. One run per case.
171
+
172
+ | Case | 0.2.0 | 0.2.1 | USD (0.2.1) | Reading |
173
+ |---|---|---|---|---|
174
+ | `proves-a-spec-boundary-bug` | 1.00 | 1.00 | 1.01 | Judge PASS PASS PASS. `B1` is `"S2: a 20-character name is accepted"`, citing I1 |
175
+ | `silent-spec-is-an-ambiguity` | 1.00 | 1.00 | 1.04 | One `findings.json` Write, `bugs` empty; the cap question is `"S13: Quantity exactly at the cap, one below and one above"` |
176
+ | `correct-code-has-no-bugs` | 1.00 | 1.00 | 1.03 | One Write, `bugs` empty, 16 covered, two ambiguities (numeric strings, `Infinity`) |
177
+ | `callers-tree-unchanged` | 1.00 | 1.00 | 0.70 | No path outside `.bug-hunter/` changed |
178
+ | `scenarios-before-bodies` | 1.00 | 1.00 | 1.61 | Scenario author dispatched; `scenarios.md` written before any Read of `src/discount.js` |
179
+
180
+ Every `scenario` in every bucket of all five runs carried its label. Every
181
+ run wrote `findings.json` once, so the last-write rule changed no verdict
182
+ here; it was checked on doctored copies of the 0.2.0 traces (an empty draft
183
+ followed by a write with bugs, a later Edit, a later heredoc), where the old
184
+ patterns passed and the new ones fail. Every run dispatched at least one role
185
+ and wrote `intent.md` once, so the fallback's late-claim rule was not
186
+ exercised; no fixture has a sibling implementation or a stated invariant.
187
+
188
+ ### Skill 0.2.0, Opus 5.5, 2026-10-12
189
+
190
+ Same command plus `--trust-plugin --no-publish`, CLI 2.1.296. Skill 0.2.0
191
+ counts intent stated in the repo itself (invariants the code states, sibling
192
+ implementations it says it mirrors, existing tests, docs) and requires
193
+ `findings.json` and `report.md` to be written with the Write tool. USD 5.08 in
194
+ all (judging USD 0.40), 265s, `results/2026-10-12T07-49-17-746Z`. Overall
195
+ 1.00, against 0.84 for 0.1.0 below. One run per case, so every score is a
196
+ single sample.
197
+
198
+ | Case | 0.1.0 | 0.2.0 | USD (0.2.0) | Reading |
199
+ |---|---|---|---|---|
200
+ | `proves-a-spec-boundary-bug` | 1.00 | 1.00 | 1.33 | Judge PASS PASS PASS |
201
+ | `silent-spec-is-an-ambiguity` | 0.20 | 1.00 | 1.12 | `findings.json` written with Write, so both regexes see it. `bugs` empty; `A1` asks what the cap is and names the code's 99 |
202
+ | `correct-code-has-no-bugs` | 1.00 | 1.00 | 0.38 | `bugs` empty, 13 covered, one ambiguity (sparse arrays, which `SPEC.md` does not address) |
203
+ | `callers-tree-unchanged` | 1.00 | 1.00 | 1.17 | No path outside `.bug-hunter/` changed |
204
+ | `scenarios-before-bodies` | 1.00 | 1.00 | 1.08 | The dispatched scenario author wrote `scenarios.md` before any Read of `src/discount.js` |
205
+
206
+ No failures. The 0.1.0 failure is closed by the Write rule, not by a grader
207
+ change: all five runs wrote both files with the Write tool. The new intent
208
+ rule did not promote anything in the two cases it could have over-promoted:
209
+ the ambiguity run's five questions all stay ambiguities with `bugs` empty, and
210
+ the correct-code run's one open question (sparse arrays) stays an ambiguity.
211
+ None of the five fixtures has a sibling implementation or a stated invariant,
212
+ so these cases do not exercise the new confirm path; they only show it does
213
+ not leak into silent-spec cases.
214
+
215
+ Dispatch is still not reliable. The correct-code run ran all three roles in
216
+ the main thread with the Agent tool available (70s, the cheapest run); its
217
+ `intent.md` and `scenarios.md` were on disk before it ran `cat` on
218
+ `src/paginate.js`, so the fallback's order held. It hunted in a `$TMPDIR` copy
219
+ rather than a git worktree. The ambiguity run dispatched the scenario author
220
+ and executor and no refuter, which fits: no repro failed.
221
+
222
+ ### Skill 0.1.0, Opus 5.5, 2026-10-12
223
+
224
+ CLI 2.1.296, `--model claude-opus-5-5 --judge-model claude-opus-5-5 --runs 1
225
+ --ablation none --keep-temp -j 5`. One run per case, so every score is a single
226
+ sample. USD 5.30 in all (judging USD 0.39), 317s,
227
+ `results/2026-10-12T05-33-00-761Z`. Overall 0.84, four of five cases at 1.00.
228
+
229
+ | Case | Score | USD | Reading |
230
+ |---|---|---|---|
231
+ | `proves-a-spec-boundary-bug` | 1.00 | 0.77 | Judge PASS PASS PASS. One bug citing I1 (3 to 20 inclusive) with a repro asserting a 20-character name is accepted |
232
+ | `silent-spec-is-an-ambiguity` | 0.20 | 0.97 | `bugs-empty` and `cap-is-an-ambiguity` fail on the grader's blind spot, not the skill (below) |
233
+ | `correct-code-has-no-bugs` | 1.00 | 0.98 | `bugs` empty; findings written with the Write tool |
234
+ | `callers-tree-unchanged` | 1.00 | 1.19 | No path outside `.bug-hunter/` changed; a look at the kept sandbox agrees |
235
+ | `scenarios-before-bodies` | 1.00 | 1.39 | The scenario author (a dispatched agent) wrote `scenarios.md`; the first Read of `src/discount.js` came later |
236
+
237
+ **`silent-spec-is-an-ambiguity` failed, and the skill did what the case
238
+ asks.** The run wrote `findings.json` with `python3 -I -` and `json.dump`, so
239
+ the trace holds a Python list, not the JSON the two regexes look for; both
240
+ report "pattern not found in trace". The file kept in the sandbox has
241
+ `"bugs": []` and, first among five ambiguities, `A1`: "What is the cap?
242
+ SPEC.md never gives a number; the code uses 99 (MAX_QUANTITY). Is 99 the
243
+ intended value, and should the spec say so?" The final message says the same.
244
+ The graders stay as written. The fix is not to widen them: either the skill
245
+ writes `findings.json` with the Write tool, which `findings-format.md` does not
246
+ ask for today, or the harness lets a file target take a glob. Which one is
247
+ open.
248
+
249
+ **Other things the traces show, which no grader scores.**
250
+
251
+ - Dispatch is not reliable. Four of the five baseline runs (ambiguity,
252
+ correct, tree, order) dispatched the scenario author, executor and refuter
253
+ as separate agents. The boundary-bug run, and the sanity run of the same
254
+ case before it, ran all three roles in the main thread with the Agent tool
255
+ available, which the skill reserves for when dispatch is unavailable. In
256
+ both, `intent.md` and `scenarios.md` were on disk before the body was read,
257
+ so the fallback's order held.
258
+ - Git inside the eval sandbox: a bare `git` is Apple's xcrun shim, which fails
259
+ there (as in `skills/sluice/evals/README.md`), and so does the checkout step
260
+ of `git worktree add` even from `/opt/homebrew/bin/git`. The tree run worked
261
+ around it with `worktree add --no-checkout` then `reset --hard`, so it hunted
262
+ in a real scratch worktree and removed it after; its own closing
263
+ `git status --short` showed only `?? .bug-hunter/`. The order run fell back
264
+ to `git archive` into `$TMPDIR`, and the boundary-bug, ambiguity and correct
265
+ runs copied the files into `$TMPDIR` without git. The caller's tree stayed
266
+ clean either way, but the skill's scratch worktree held the repros in one
267
+ run of five.
268
+ - Before writing scenarios, the order run's main thread ran Grep with content
269
+ output over `src/discount.js` for `export` and comment lines. That shows
270
+ declaration lines, which the skill allows, but the same call shape could
271
+ show a body, and the order grader does not count it.
272
+
273
+ **Sanity run before the baseline.** `proves-a-spec-boundary-bug` alone (USD
274
+ 0.79, `results/2026-10-12T05-30-48-463Z`): 1.00. Its trace was then used to
275
+ check that each regex grader can fail. `scenarios-before-bodies`, pointed at
276
+ `src/username.js`, matched the real trace and did not match a copy with the
277
+ body's Read moved ahead of the `scenarios.md` write. `bugs-empty` and
278
+ `cap-is-an-ambiguity` did not match the real findings write (one bug, no
279
+ ambiguities) and did match a copy with `bugs` emptied and a cap question added.
280
+ `tree-unchanged` passed on a listing of only `.bug-hunter/` paths and failed
281
+ once `tests/backoff-hunt.test.js` was added. The `llm` grader's FAIL path was
282
+ not exercised: both runs of its case found the bug.
@@ -0,0 +1,93 @@
1
+ {
2
+ "model": "claude-opus-5-5",
3
+ "judgeModel": "claude-opus-5-5",
4
+ "claudeVersion": "2.1.300",
5
+ "skillVersion": "0.4.0",
6
+ "date": "2026-10-15T13:04:21.155Z",
7
+ "cases": {
8
+ "bug-cites-intent-from-spec": {
9
+ "with": [
10
+ 1,
11
+ 1,
12
+ 1
13
+ ],
14
+ "without": [
15
+ 0,
16
+ 0,
17
+ 0
18
+ ]
19
+ },
20
+ "callers-tree-unchanged": {
21
+ "with": [
22
+ 1,
23
+ 1,
24
+ 1
25
+ ],
26
+ "without": [
27
+ 1,
28
+ 1,
29
+ 1
30
+ ]
31
+ },
32
+ "correct-code-has-no-bugs": {
33
+ "with": [
34
+ 1,
35
+ 1,
36
+ 1
37
+ ],
38
+ "without": [
39
+ 0.5,
40
+ 0.5,
41
+ 0.5
42
+ ]
43
+ },
44
+ "impossible-value-is-a-bug": {
45
+ "with": [
46
+ 1,
47
+ 1,
48
+ 1
49
+ ],
50
+ "without": [
51
+ 0,
52
+ 0,
53
+ 0
54
+ ]
55
+ },
56
+ "proves-a-spec-boundary-bug": {
57
+ "with": [
58
+ 1,
59
+ 1,
60
+ 1
61
+ ],
62
+ "without": [
63
+ 0,
64
+ 0,
65
+ 0
66
+ ]
67
+ },
68
+ "silent-spec-is-an-ambiguity": {
69
+ "with": [
70
+ 1,
71
+ 1,
72
+ 1
73
+ ],
74
+ "without": [
75
+ 0.3333333333333333,
76
+ 0.3333333333333333,
77
+ 0.3333333333333333
78
+ ]
79
+ },
80
+ "spec-outranks-target-docstring": {
81
+ "with": [
82
+ 1,
83
+ 1,
84
+ 1
85
+ ],
86
+ "without": [
87
+ 0,
88
+ 0,
89
+ 0
90
+ ]
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,4 @@
1
+ schema_version: "1.1"
2
+ name: bug-cites-intent-from-spec
3
+ context:
4
+ scaffold_script: fixture.sh
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bash
2
+ # A coupon helper whose only bug is in its rounding (the spec says halves
3
+ # round up, the code floors). The case is about where the expected value comes
4
+ # from: the bug has to cite a claim intent.md took from SPEC.md, not one read
5
+ # out of the floor in src/discount.js, which would confirm the code against
6
+ # itself.
7
+ set -euo pipefail
8
+
9
+ mkdir -p src tests
10
+
11
+ cat > package.json <<'JSON'
12
+ {
13
+ "name": "checkout",
14
+ "version": "3.1.0",
15
+ "type": "module",
16
+ "scripts": {
17
+ "test": "node --test \"tests/*.test.js\""
18
+ }
19
+ }
20
+ JSON
21
+
22
+ cat > SPEC.md <<'MD'
23
+ # Checkout coupons
24
+
25
+ `applyCoupon(totalCents, coupon)` in `src/discount.js` returns the order total
26
+ after a percentage coupon, in cents.
27
+
28
+ - `totalCents` is a whole number of 0 or more; anything else throws a
29
+ `RangeError`.
30
+ - A coupon is `{ percentOff }`, where `percentOff` is a whole number from 1 to
31
+ 100, inclusive; anything else throws a `RangeError`.
32
+ - The result is the total less `percentOff` percent, rounded to the nearest
33
+ cent, with a half cent rounding up.
34
+ MD
35
+
36
+ git init --quiet
37
+ git add -A
38
+ git -c user.email=fixture@example.com -c user.name=fixture commit --quiet -m "spec: checkout coupons"
39
+
40
+ cat > src/discount.js <<'JS'
41
+ export function applyCoupon(totalCents, coupon) {
42
+ if (!Number.isInteger(totalCents) || totalCents < 0) {
43
+ throw new RangeError(`totalCents must be a whole number of 0 or more, got ${totalCents}`);
44
+ }
45
+
46
+ const percentOff = coupon?.percentOff;
47
+
48
+ if (!Number.isInteger(percentOff) || percentOff < 1 || percentOff > 100) {
49
+ throw new RangeError(`percentOff must be a whole number from 1 to 100, got ${percentOff}`);
50
+ }
51
+
52
+ return Math.floor((totalCents * (100 - percentOff)) / 100);
53
+ }
54
+ JS
55
+
56
+ cat > tests/discount.test.js <<'JS'
57
+ import assert from "node:assert/strict";
58
+ import { test } from "node:test";
59
+ import { applyCoupon } from "../src/discount.js";
60
+
61
+ test("ten percent off a round total", () => {
62
+ assert.equal(applyCoupon(10000, { percentOff: 10 }), 9000);
63
+ });
64
+
65
+ test("a coupon over 100 percent throws", () => {
66
+ assert.throws(() => applyCoupon(10000, { percentOff: 101 }), RangeError);
67
+ });
68
+ JS
69
+
70
+ git add -A
71
+ git -c user.email=fixture@example.com -c user.name=fixture commit --quiet -m "feat: apply percentage coupons at checkout"
@@ -0,0 +1,36 @@
1
+ ---
2
+ type: llm
3
+ # As in proves-a-spec-boundary-bug: the judge cannot run the repro, so "fails
4
+ # in the fixture" is judged against the fixture's code, quoted below, and it
5
+ # reads the trace because findings.json sits under a slug no file focus names.
6
+ focus: trace
7
+ weight: 3
8
+ ---
9
+
10
+ The agent was asked what QA will find in `src/discount.js`, going by
11
+ `SPEC.md`. `SPEC.md` says the result is the total less `percentOff` percent,
12
+ "rounded to the nearest cent, with a half cent rounding up". The fixture's
13
+ code floors instead:
14
+
15
+ ```js
16
+ return Math.floor((totalCents * (100 - percentOff)) / 100);
17
+ ```
18
+
19
+ So `applyCoupon(1005, { percentOff: 10 })` returns `904` (904.5 floored)
20
+ where the spec requires `905`, and any result with a fractional part of a
21
+ half cent or more is a cent short. Nothing else in the code departs from the
22
+ spec.
23
+
24
+ Find the `findings.json` the agent wrote under `.bug-hunter/` (its content
25
+ appears in a Write tool call or a shell command in the trace). If it was
26
+ written more than once, judge only the last write, with any later Edit to it
27
+ applied.
28
+
29
+ PASS if its `bugs` array has an entry whose `claim` resolves, through the
30
+ `intent` array, to a claim whose `source` is `SPEC.md` and which states the
31
+ round-to-nearest, half-up rule, and whose `repro` asserts a half-up rounded
32
+ result that the flooring code above does not give, so that it fails.
33
+ FAIL if no findings.json content is visible in the trace, if `bugs` is empty,
34
+ if the bug's claim resolves to an intent entry whose source is
35
+ `src/discount.js` or that describes the floor as intended, or if the repro
36
+ asserts something the code above already does.
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: tool_used
3
+ tool: Skill
4
+ input_match: '"skill"\s*:\s*"(?:[\w-]+:)?bug-hunter"'
5
+ ---
@@ -0,0 +1,8 @@
1
+ ---
2
+ type: regex
3
+ # As findings-written in proves-a-spec-boundary-bug: the files target holds
4
+ # the run directory's date prefix to the shape SKILL.md states.
5
+ pattern: '^\.bug-hunter/\d{4}-\d{2}-\d{2}-[^/]+/intent\.md$'
6
+ flags: m
7
+ target: files
8
+ ---
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: bug-cites-intent-from-spec
3
+ description: The skill may read the code, but a claim never comes from what a changed body does. Pins the rounding bug citing an intent claim sourced from SPEC.md, with a repro expecting half-up rounding.
4
+ tags: [intent, arithmetic, scaffold, write]
5
+ max_turns: 80
6
+ timeout_seconds: 2400
7
+ allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash, Agent]
8
+ expected_outcome: intent.md exists, and findings.json under .bug-hunter/ has a bug whose claim resolves to SPEC.md's half-cent-rounds-up rule, with a repro that fails against the flooring code.
9
+ ---
10
+
11
+ What will QA find in `src/discount.js`, going by `SPEC.md`?
@@ -0,0 +1,4 @@
1
+ schema_version: "1.1"
2
+ name: callers-tree-unchanged
3
+ context:
4
+ scaffold_script: fixture.sh