simt-dev 0.6.3 → 0.7.1

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 (187) hide show
  1. package/CHANGELOG.md +162 -841
  2. package/README.md +21 -135
  3. package/agents/simt-agent.md +84 -0
  4. package/bin/simt.mjs +19 -55
  5. package/docs/decisions/ADR-009-skill-layout.md +1 -1
  6. package/docs/decisions/ADR-014-workspace-is-configuration.md +1 -1
  7. package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md +1 -1
  8. package/docs/decisions/ADR-099-the-release-verifies-served-bytes-and-the-formula-mirrors-them.md +1 -1
  9. package/docs/decisions/ADR-106-the-harness-hook-runs-through-a-repo-shim.md +46 -0
  10. package/docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md +51 -0
  11. package/docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md +59 -0
  12. package/docs/decisions/ADR-109-simt-dev-is-its-own-consumer-and-the-guards-that-kept-it-from-being-one-are-retired.md +47 -0
  13. package/docs/decisions/ADR-110-the-lock-file-follows-the-manifest-name-and-the-old-name-is-read-until-the-next-write.md +43 -0
  14. package/docs/decisions/ADR-111-the-tool-ships-one-agent-rendered-per-harness-and-installs-it-with-init-and-a-command.md +75 -0
  15. package/docs/decisions/ADR-112-the-registry-is-what-simt-dev-ships-and-a-repo-owns-its-own-skills.md +81 -0
  16. package/docs/decisions/ADR-113-a-pre-push-that-carries-only-documentation-skips-the-test-gate-and-says-so.md +87 -0
  17. package/docs/decisions/ADR-114-the-bench-pairs-at-the-task-level-a-missing-cost-is-not-zero-and-an-unasked-cross-group-compare-is-refused.md +74 -0
  18. package/docs/decisions/ADR-115-the-index-stamps-its-schema-version-migrates-older-databases-in-place-and-serves-newer-ones-from-a-sidecar.md +69 -0
  19. package/docs/decisions/ADR-116-rust-support-reaches-the-example-path-the-indent-and-the-bench-runner.md +100 -0
  20. package/docs/decisions/ADR-117-the-darwin-release-builds-on-supported-macos-15-runners-and-a-re-run-is-idempotent.md +81 -0
  21. package/docs/decisions/ADR-118-the-bench-corpus-is-keyed-on-history-is-append-only-and-is-portable-as-a-sealed-document.md +91 -0
  22. package/docs/decisions/ADR-119-the-environment-variable-points-into-a-workspace-the-repo-declared.md +90 -0
  23. package/docs/decisions/ADR-120-a-pre-push-whose-tree-is-already-proven-green-skips-the-test-gate-on-that-evidence.md +96 -0
  24. package/docs/decisions/ADR-122-the-shims-move-into-the-state-directory-and-the-legacy-directory-removes-itself.md +94 -0
  25. package/docs/decisions/ADR-123-a-surplus-positional-is-not-refused-because-arity-is-not-in-the-source.md +57 -0
  26. package/docs/decisions/ADR-124-the-manifest-has-a-schema-in-the-tool-and-its-unknown-keys-stay-the-repositorys.md +64 -0
  27. package/docs/decisions/ADR-125-a-shared-context-belongs-to-a-read-and-a-writer-reads-directly.md +65 -0
  28. package/package.json +13 -3
  29. package/rules/core.md +9 -2
  30. package/rules/mdc/slice-commit.mdc +57 -4
  31. package/rules/mdc/test-gate.mdc +2 -0
  32. package/rules/stacks/android.md +3 -3
  33. package/rules/stacks/connectiq.md +2 -2
  34. package/rules/stacks/dart.md +1 -1
  35. package/rules/stacks/deno.md +1 -1
  36. package/rules/stacks/dotnet.md +1 -1
  37. package/rules/stacks/elixir.md +1 -1
  38. package/rules/stacks/go.md +1 -1
  39. package/rules/stacks/jvm.md +3 -2
  40. package/rules/stacks/node.md +2 -2
  41. package/rules/stacks/python.md +7 -6
  42. package/rules/stacks/ruby.md +1 -1
  43. package/rules/stacks/rust.md +1 -1
  44. package/rules/stacks/swift.md +1 -1
  45. package/rules/stacks/typescript.md +2 -2
  46. package/rules/stacks/uv.md +3 -2
  47. package/skills/simt-commands/SKILL.md +70 -0
  48. package/src/advice/advice.mjs +7 -3
  49. package/src/agenthooks/decide.mjs +141 -9
  50. package/src/agenthooks/opencode.mjs +72 -0
  51. package/src/agenthooks/prompt.mjs +10 -3
  52. package/src/agenthooks/render.mjs +186 -19
  53. package/src/agenthooks/shim.mjs +55 -0
  54. package/src/agents/render.mjs +277 -0
  55. package/src/api.mjs +29 -0
  56. package/src/argv.mjs +37 -3
  57. package/src/bench/ablate.mjs +31 -14
  58. package/src/bench/adapters.json +16 -0
  59. package/src/bench/corpus.mjs +150 -0
  60. package/src/bench/grade.mjs +23 -49
  61. package/src/bench/mine.mjs +247 -36
  62. package/src/bench/probes.mjs +104 -68
  63. package/src/bench/process.mjs +128 -0
  64. package/src/bench/report.mjs +260 -84
  65. package/src/bench/run.mjs +251 -150
  66. package/src/bench/variants.mjs +112 -40
  67. package/src/brief/assemble.mjs +44 -3
  68. package/src/ci/workflow.mjs +46 -14
  69. package/src/cli.mjs +45 -0
  70. package/src/commands/agent.mjs +205 -0
  71. package/src/commands/agenthook.mjs +113 -20
  72. package/src/commands/bench.mjs +192 -18
  73. package/src/commands/brief.mjs +5 -4
  74. package/src/commands/ci.mjs +2 -1
  75. package/src/commands/compare.mjs +50 -49
  76. package/src/commands/completions.mjs +2 -6
  77. package/src/commands/decide.mjs +10 -9
  78. package/src/commands/distill.mjs +4 -3
  79. package/src/commands/doctor.mjs +11 -7
  80. package/src/commands/evidence.mjs +19 -12
  81. package/src/commands/explain.mjs +0 -1
  82. package/src/commands/gate.mjs +98 -28
  83. package/src/commands/graph.mjs +20 -8
  84. package/src/commands/harness.mjs +3 -2
  85. package/src/commands/help.mjs +160 -31
  86. package/src/commands/hooks.mjs +163 -50
  87. package/src/commands/ignore.mjs +2 -1
  88. package/src/commands/impact.mjs +8 -9
  89. package/src/commands/index.mjs +278 -180
  90. package/src/commands/init.mjs +171 -38
  91. package/src/commands/lease.mjs +206 -39
  92. package/src/commands/lessons.mjs +72 -15
  93. package/src/commands/local.mjs +3 -3
  94. package/src/commands/map.mjs +47 -27
  95. package/src/commands/mcp.mjs +0 -1
  96. package/src/commands/pack.mjs +9 -6
  97. package/src/commands/plan.mjs +6 -5
  98. package/src/commands/plugin.mjs +3 -3
  99. package/src/commands/prbody.mjs +3 -3
  100. package/src/commands/repoindex.mjs +5 -3
  101. package/src/commands/review.mjs +2 -2
  102. package/src/commands/rules.mjs +124 -33
  103. package/src/commands/shared.mjs +2 -3
  104. package/src/commands/skills.mjs +82 -45
  105. package/src/commands/skillscompile.mjs +12 -12
  106. package/src/commands/skillsinstall.mjs +14 -2
  107. package/src/commands/slice.mjs +396 -26
  108. package/src/commands/spec.mjs +151 -0
  109. package/src/commands/status.mjs +4 -7
  110. package/src/commands/sync.mjs +50 -33
  111. package/src/commands/tests.mjs +11 -11
  112. package/src/commands/tokens.mjs +2 -2
  113. package/src/commands/undo.mjs +29 -31
  114. package/src/commands/upgrade.mjs +40 -4
  115. package/src/commands/why.mjs +19 -12
  116. package/src/compare/hooksstate.mjs +23 -1
  117. package/src/compare/learn.mjs +82 -57
  118. package/src/compare/parallel.mjs +50 -0
  119. package/src/compare/row.mjs +26 -3
  120. package/src/completions.mjs +5 -0
  121. package/src/context.mjs +64 -0
  122. package/src/decisions/dir.mjs +84 -0
  123. package/src/decisions/index-doc.mjs +46 -0
  124. package/src/decisions/index.mjs +109 -11
  125. package/src/diff.mjs +1 -1
  126. package/src/distill/distill.mjs +17 -2
  127. package/src/distill/engine.mjs +5 -1
  128. package/src/ecosystem/node-runner.mjs +144 -0
  129. package/src/explain.mjs +18 -6
  130. package/src/graph/load.mjs +42 -20
  131. package/src/hooks/capability.mjs +17 -4
  132. package/src/hooks/dispatcher.mjs +513 -36
  133. package/src/hooks/gates.mjs +0 -43
  134. package/src/hooks/resolve.mjs +11 -10
  135. package/src/hooks/shim.mjs +17 -9
  136. package/src/identity.mjs +96 -15
  137. package/src/index/extract.mjs +192 -11
  138. package/src/index/persist.mjs +307 -47
  139. package/src/index/store.mjs +263 -60
  140. package/src/index/tests.mjs +6 -35
  141. package/src/journal.mjs +372 -84
  142. package/src/kernel/card.mjs +37 -12
  143. package/src/kernel/checks.mjs +44 -9
  144. package/src/kernel/variants.mjs +27 -16
  145. package/src/lease.mjs +97 -15
  146. package/src/ledger/index.mjs +120 -25
  147. package/src/local/client.mjs +4 -6
  148. package/src/locate.mjs +33 -9
  149. package/src/manifest/schema.mjs +213 -0
  150. package/src/mcp/resources.mjs +12 -13
  151. package/src/mcp/server.mjs +151 -38
  152. package/src/mcp/tools.mjs +90 -16
  153. package/src/measure/session.mjs +3 -2
  154. package/src/memory/decide.mjs +98 -43
  155. package/src/memory/lessons.mjs +168 -33
  156. package/src/name.mjs +9 -0
  157. package/src/node-version.mjs +37 -0
  158. package/src/output/index.mjs +111 -3
  159. package/src/packs/manage.mjs +17 -3
  160. package/src/packs.mjs +20 -2
  161. package/src/plan/index.mjs +31 -8
  162. package/src/plan/template.mjs +27 -5
  163. package/src/plugins.mjs +6 -2
  164. package/src/prbody.mjs +7 -5
  165. package/src/projection/baselines.mjs +1 -1
  166. package/src/projection/disclosure.mjs +2 -2
  167. package/src/projection/loaders.mjs +41 -1
  168. package/src/projection/lock.mjs +31 -14
  169. package/src/projection/manifest.mjs +89 -53
  170. package/src/projection/markers.mjs +43 -3
  171. package/src/projection/mdc.mjs +34 -7
  172. package/src/projection/project.mjs +162 -18
  173. package/src/projection/rules.mjs +58 -6
  174. package/src/projection/surfaces.mjs +9 -1
  175. package/src/repo-root.mjs +88 -0
  176. package/src/resolve/body.mjs +4 -0
  177. package/src/resolve/fingerprint.mjs +24 -15
  178. package/src/resolve/local.mjs +58 -0
  179. package/src/review/critic.mjs +6 -3
  180. package/src/secrets.mjs +165 -22
  181. package/src/state.mjs +76 -0
  182. package/src/status.mjs +9 -4
  183. package/src/toolwords.mjs +8 -0
  184. package/src/tsconfig.mjs +39 -99
  185. package/src/workspace.mjs +55 -10
  186. package/templates/plan/tracker.json +1 -1
  187. package/docs/decisions/README.md +0 -591
package/CHANGELOG.md CHANGED
@@ -6,787 +6,253 @@ All notable changes to this package are recorded here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- ## [0.6.3] — 2026-10-29
9
+ ## [0.7.1] — 2026-11-01
10
10
 
11
11
  ### Added
12
12
 
13
- `simt explain <CODE>` names what a `check` finding means, the decision behind
14
- it, how to fix it, and a concrete trigger — one table row per finding code, so
15
- every code `check` can emit has an entry.
13
+ - `simt --help` answers one command at a time from the spec table, and a core page that fits a terminal; `--all` prints everything.
14
+ - Every command declares the flags it reads in one spec table that owns the command list, the group defaults and the MCP tool surface; the parser accepts exactly what the table declares.
15
+ - The `simt.json` manifest has a schema in the tool: the loader names an unknown key, and the keys the tool generates come from the table, so a repository's own keys stay the repository's. — [ADR-124](docs/decisions/ADR-124-the-manifest-has-a-schema-in-the-tool-and-its-unknown-keys-stay-the-repositorys.md)
16
+ - The `simt.lock.json` lock records its own format version, and namespaces register the keys they own.
17
+ - The read commands are a library imported by name: `check`, `fingerprint`, `skills`, `doctor` and `rules` read the repository through one shared context, so a read that asked five questions walks the tree once. — [ADR-125](docs/decisions/ADR-125-a-shared-context-belongs-to-a-read-and-a-writer-reads-directly.md)
18
+ - The `Co-Authored-By` trailer is written into the commit template and into `slice finish` commits.
19
+ - `SIMT_DEV_WORKSPACE` points into a workspace the repository declared. — [ADR-119](docs/decisions/ADR-119-the-environment-variable-points-into-a-workspace-the-repo-declared.md)
20
+
21
+ ### Changed
22
+
23
+ The git shims move from the top-level `.simt-hooks/` directory into the state
24
+ directory at `.simt/hooks/`, and `core.hooksPath` follows (ADR-122). Every
25
+ piece of per-repo state now has one root, and the ignore line that existed
26
+ only for the shims is dropped. A repository installed before the move
27
+ self-heals on its next `hooks install --yes`: the routing re-points, the new
28
+ shims land, and the legacy directory is removed — journaled, so `undo`
29
+ brings it back — but only when every file in it carries a simt-dev marker; a
30
+ file the tool cannot recognise as its own leaves the directory in place.
31
+ Because the shims land under the state directory, the journal records writes
32
+ to the shim directory: it is the one named exception to the unjournaled state
33
+ directory, since the undo that restores `core.hooksPath` must restore the
34
+ shims with it.
35
+
36
+ Journal sets that predate the move into `.simt/local/journal` live in the
37
+ fallback directory `.simt/journal`, which `undo` can only see while the
38
+ current directory is absent — a repository that accumulated history under the
39
+ old path lost it to `undo` on its first write after the move. The next
40
+ journal write now moves the legacy sets into the current directory, and the
41
+ fallback directory goes with its last file: a name present in both keeps
42
+ both records when the bytes differ, and a crash mid-move is finished by the
43
+ next write, so the migration is idempotent.
44
+
45
+ - The CLI loads only the command module the matched path needs, so a run pays for one command, not the registry.
46
+ - The `--json` envelope carries the canonical command path and the run's exit code, and `ok` agrees with the exit code.
47
+ - Run warnings travel in the JSON document, not only on stderr, and run notices are named distinctly from the warnings count the commands already report.
48
+ - A pre-push whose tree already passed the full suite skips the test gate on that evidence and says so, naming the push kind; the hook runs in a clone, and main is gated with a timeout that fits the run. — [ADR-120](docs/decisions/ADR-120-a-pre-push-whose-tree-is-already-proven-green-skips-the-test-gate-on-that-evidence.md)
49
+ - The decisions index is rendered from the decision records and leaves the shipped records directory, so it cannot drift from what the records say.
50
+ - The bench corpus is keyed on history, append-only, and portable as a sealed document; a re-mine no longer silently replaces it, and the Gradle and Maven graders read nothing from the corpus. — [ADR-118](docs/decisions/ADR-118-the-bench-corpus-is-keyed-on-history-is-append-only-and-is-portable-as-a-sealed-document.md)
51
+ - The publish workflow's version input may be `latest`, resolved before the guard runs, and the darwin release targets build on supported macOS 15 runners. — [ADR-117](docs/decisions/ADR-117-the-darwin-release-builds-on-supported-macos-15-runners-and-a-re-run-is-idempotent.md)
52
+ - The npm tarball excludes the decisions index, and the shipped surface is linted before publish.
16
53
 
17
- `simt doctor --fix` applies only the remedies flagged safe (local, idempotent,
18
- journaled) and lists the rest; a fixture with three safe and two unsafe findings
19
- fixes exactly three.
54
+ ### Fixed
55
+
56
+ - The parser refuses a value a flag does not accept, a second value for a flag that is not repeatable, and the tolerated flags once the release that removes them ships.
57
+ - The tool no longer lets a repository-local `user.*` setting shadow the shared `git.config` identity.
58
+ - The MCP protocol validates the envelope and the arguments it advertised.
59
+ - A gate is bounded in time, and its output is read as a tail, so a hung gate cannot hang the hook.
60
+ - The runtime floor is checked before the CLI loads, so a Node that is too old fails before the tool does.
61
+ - `simt brief` names only what this tool can resolve.
62
+ - `simt packs`: a preview stays offline, a link is pinned or refused, and the positional argument wins over the flag.
63
+ - `simt bench`: a run cannot outlive its deadline, a failed run is a record, and the small bugs report what they measured.
64
+ - `simt ledger` names the tree when `.gitignore` ignores the state directory.
65
+ - The upgrade verdict and the exit code are one thing, so `ok` and the exit code cannot disagree.
20
66
 
21
- `simt status --line` prints the gate state from the last `check` in under
22
- 50 ms from cache — `simt PASS ✓rules ✓hooks 3/3gates` — for shell prompts, tmux
23
- and agent status lines.
67
+ ## [0.7.0] — 2026-10-31
24
68
 
25
- Every write records `{path, beforeDigest, afterDigest, beforeBytes}` in
26
- `.simt/journal/`, and `simt undo` restores the last set when every file in it
27
- is untouched since.
69
+ ### Added
28
70
 
29
- `--diff` on every writer prints a unified diff of the plan next to the report,
30
- changing nothing about what the run writes.
71
+ - `simt agent-hooks install --harness <id>` writes the policies into one harness instead of all of them; an unknown id is a usage error that names the valid ones. — [ADR-107](docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md)
72
+ - `simt init --yes` builds the repository index as part of the adoption, so the first `simt map` or `simt brief` in a fresh repository answers from real files. — [ADR-108](docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md)
73
+ - The rule text names the manifest as `simt.json`, the name it has had since the rename; four rule files still said `simt-dev.json`, which resolves in no repository.
31
74
 
32
- `simt completions <bash|zsh|fish>` prints a completion script generated from the
33
- command registry, so the three shells complete exactly the commands the binary
34
- dispatches.
75
+ ### Changed
35
76
 
36
- One ignore list (`.simtignore`) renders `.cursorignore`, `.aiderignore`,
37
- `.geminiignore` and Claude `permissions.deny`, and a zero-dependency
38
- staged-diff secret scan (vendor patterns plus Shannon entropy over added lines
39
- only) blocks a commit carrying a probable secret without printing the bytes.
77
+ - The projection lock is `simt.lock.json`, the name that follows the command and the manifest. — [ADR-110](docs/decisions/ADR-110-the-lock-file-follows-the-manifest-name-and-the-old-name-is-read-until-the-next-write.md)
78
+ - This repository is now a consumer of itself: `simt init` ran on its own source, so the two tests that asserted it was *not* a consumer are rewritten to assert the new state, and `AGENTS.md`'s "not self-adopted" section now says what is true — [ADR-109](docs/decisions/ADR-109-simt-dev-is-its-own-consumer-and-the-guards-that-kept-it-from-being-one-are-retired.md)
40
79
 
41
- `simt slice start <id>` isolates the work in a linked worktree on branch
42
- `slice/<id>` with the brief preloaded and flips the tracker row In progress;
43
- `simt slice finish <id>` runs the declared gates, commits a Conventional
44
- subject from the row title, and records the SHA and test command on the row.
80
+ ## [0.6.3] — 2026-10-29
45
81
 
46
- `simt lease <paths> --session <id>` claims files until a TTL in
47
- `.simt/leases.json`, and the pre-edit hook denies an edit leased by another
48
- session (ADR-101).
82
+ ### Added
49
83
 
50
- `simt pr-body [--base <ref>]` assembles the pull request body from the evidence
51
- ledger, the decision records the diff touches, the gate state from the last
52
- check, and the residual risk in ADR-008 vocabulary. It is also exposed as the
53
- `pr_body` MCP tool; the two longest tool descriptions were shortened to hold
54
- the 2 KB `tools/list` budget.
84
+ - `simt explain <CODE>` names what a `check` finding means, the decision behind it, how to fix it, and a concrete trigger — one table row per finding code, so every code `check` can emit has an entry.
85
+ - `simt doctor --fix` applies only the remedies flagged safe (local, idempotent, journaled) and lists the rest; a fixture with three safe and two unsafe findings fixes exactly three.
86
+ - `simt status --line` prints the gate state from the last `check` in under 50 ms from cache — `simt PASS ✓rules ✓hooks 3/3gates` — for shell prompts, tmux and agent status lines.
87
+ - Every write records `{path, beforeDigest, afterDigest, beforeBytes}` in `.simt/journal/`, and `simt undo` restores the last set when every file in it is untouched since.
88
+ - `--diff` on every writer prints a unified diff of the plan next to the report, changing nothing about what the run writes.
89
+ - `simt completions <bash|zsh|fish>` prints a completion script generated from the command registry, so the three shells complete exactly the commands the binary dispatches.
90
+ - One ignore list (`.simtignore`) renders `.cursorignore`, `.aiderignore`, `.geminiignore` and Claude `permissions.deny`, and a zero-dependency staged-diff secret scan (vendor patterns plus Shannon entropy over added lines only) blocks a comm
91
+ - `simt slice start <id>` isolates the work in a linked worktree on branch `slice/<id>` with the brief preloaded and flips the tracker row In progress; `simt slice finish <id>` runs the declared gates, commits a Conventional subject from the
92
+ - `simt lease <paths> --session <id>` claims files until a TTL in `.simt/leases.json`, and the pre-edit hook denies an edit leased by another session (ADR-101). — [ADR-101](docs/decisions/ADR-101-a-file-lease-belongs-to-one-session-and-the-pre-edit-hook-refuses-another.md)
93
+ - `simt pr-body [--base <ref>]` assembles the pull request body from the evidence ledger, the decision records the diff touches, the gate state from the last check, and the residual risk in ADR-008 vocabulary. — [ADR-008](docs/decisions/ADR-008-verdict-vocabulary.md)
55
94
 
56
95
  ## [0.6.2] — 2026-10-27
57
96
 
58
97
  ### Fixed
59
98
 
60
- The v0.6.1 tag run failed its own tests step: two decision-corpus lists in
61
- `test/rules.test.mjs` were not moved with ADR-099 (the partial-supersession
62
- shapes and the leaking-paths count, both designed to move with the corpus).
63
- Same product as v0.6.1; this tag exists so the release automation is proven by
64
- a green run rather than described by a red one.
99
+ - The v0.6.1 tag run failed its own tests step: two decision-corpus lists in `test/rules.test.mjs` were not moved with ADR-099 (the partial-supersession shapes and the leaking-paths count, both designed to move with the corpus). — [ADR-099](docs/decisions/ADR-099-the-release-verifies-served-bytes-and-the-formula-mirrors-them.md)
65
100
 
66
101
  ## [0.6.1] — 2026-10-27 (tagged, never published to npm)
67
102
 
68
103
  ### Fixed
69
104
 
70
- The release automation, which the v0.6.0 tag proved broken in two places. The
71
- tag workflow compared CI-built binaries against locally-built committed
72
- checksums, but the build is deterministic per host family, not across them —
73
- the check could never pass, and darwin binaries cannot be finished on ubuntu
74
- runners for lack of `codesign` anyway. Each target now builds on its native
75
- runner, the release verifies the served bytes equal the built bytes, and a
76
- `sync-formula` job re-renders the committed formula from the served bytes
77
- (ADR-099). The formula description no longer starts with an article, which is
78
- what failed the Homebrew channel's `brew style` on v0.6.0. No product change.
105
+ - The release automation, which the v0.6.0 tag proved broken in two places. — [ADR-099](docs/decisions/ADR-099-the-release-verifies-served-bytes-and-the-formula-mirrors-them.md)
79
106
 
80
107
  ## [0.6.0] — 2026-10-27
81
108
 
82
109
  ### Added
83
110
 
84
- `simt plugin <claude|gemini|cursor> --out <dir>` generates one of the three
85
- harness packages from the same tables `agent-hooks install` writes — the hooks
86
- file, the MCP registration and, for cursor, the kernel rules — each carrying a
87
- self-contained copy of the runtime so it installs into a clean profile. Dry-run
88
- unless `--yes`. The Gemini reader row gains its hooks file (`.gemini/settings.json`),
89
- which extends `agent-hooks install` and `status` to a fifth adapter.
90
-
91
- The MCP server answers `prompts/list` and `prompts/get` with two templates:
92
- `simt-brief` starts a task from the pre-assembled brief packet, and `simt-slice`
93
- implements one slice of the working plan (the next one whose dependencies are
94
- complete, or a named one) — reading the repo's rules through `simt://kernel`
95
- first, testing per the slice's acceptance check, committing locally and pushing
96
- only on explicit approval. A prompt names the tools; the server stays read-only.
97
-
98
- Two fixed resources join `simt://rules/{id}` and `simt://decisions/{adr}`:
99
- `simt://kernel` serves the collaborating region rendered in kernel mode for
100
- whatever repository the server runs in — the URI promises the kernel's bytes,
101
- whatever `mode` the manifest declares — and `simt://plan/next` serves the
102
- section of the next ready plan slice, listed only while one is ready. A listed
103
- resource's size is the size its read returns.
104
-
105
- The tool descriptions in `tools/list` are cut from 2,783 bytes to 1,949,
106
- inside the 2 KB budget plan 0003 R41 sets for a cacheable tools list; the
107
- input and output schemas stay full, per ADR-068.
108
-
109
- `harness detect` reports the MCP registration files the loader table records —
110
- `.mcp.json` (Claude Code), `.codex/config.toml`, `.cursor/mcp.json`,
111
- `.agents/mcp_config.json`, `.gemini/settings.json`, `.vscode/mcp.json` — as
112
- `kind: 'mcp'` rows whose provenance is the vendor page each path was read from.
113
- A row rests on the file existing, never on a directory's presence, and never
114
- counts toward the no-rule-channel note: a config file doesn't claim one.
115
-
116
- The loader table gains the Gemini CLI reader, whose configuration reference the
117
- table previously recorded as unavailable: it is live again (read 2026-10-26), so
118
- the row carries the GEMINI.md chain with `.git` boundary markers, the
119
- `context.fileName` rename (the only documented way this reader picks up
120
- AGENTS.md), depth-5 `@path` imports, no published cap, and the `mcpServers`
121
- registration. It stays a facts row — the `gemini-md` pointer remains the one
122
- rule channel for that reader.
123
-
124
- Each release now carries a fourth artefact beside the tarball, the wheel and the
125
- formula: one single-file executable per target (`darwin-arm64`, `darwin-x64`,
126
- `linux-x64`), built from the same `files` set with a pinned Node runtime, published to
127
- the GitHub release with a `.sha256` beside it and a build-provenance attestation. It is
128
- the channel for a machine that has no toolchain to install one with — the binary lands
129
- as one file in `.simt/bin/` and carries its own runtime — and its acceptance is
130
- executable: a Gradle fixture with every directory containing a node removed from `PATH`
131
- runs `check` and the hook shim from the built bytes.
132
-
133
- `simt pack new <name> --source <npm:<package>|git+https://…>` scaffolds a
134
- versioned pack — `pack.json`, an unmeasured `bench.json`, one stack rule — and
135
- `simt pack lint [dir]` validates it and states its measured ΔR, rejecting a pack
136
- with no bench summary under `--require-measured`. `simt pack publish [dir]` lints
137
- first, publishes an npm-sourced pack idempotently (a live version is skipped),
138
- and names the tag-and-push for a git-sourced one. The summary binds to every
139
- byte but itself, so recording a measurement cannot invalidate it.
111
+ - `simt plugin <claude|gemini|cursor> --out <dir>` generates one of the three harness packages from the same tables `agent-hooks install` writes — the hooks file, the MCP registration and, for cursor, the kernel rules — each carrying a self-c
112
+ - The MCP server answers `prompts/list` and `prompts/get` with two templates: `simt-brief` starts a task from the pre-assembled brief packet, and `simt-slice` implements one slice of the working plan (the next one whose dependencies are compl
113
+ - Two fixed resources join `simt://rules/{id}` and `simt://decisions/{adr}`: `simt://kernel` serves the collaborating region rendered in kernel mode for whatever repository the server runs in — the URI promises the kernel's bytes, whatever `m
114
+ - The tool descriptions in `tools/list` are cut from 2,783 bytes to 1,949, inside the 2 KB budget plan 0003 R41 sets for a cacheable tools list; the input and output schemas stay full, per ADR-068. — [ADR-068](docs/decisions/ADR-068-the-mcp-revision-list-cites-what-was-read.md)
115
+ - `harness detect` reports the MCP registration files the loader table records — `.mcp.json` (Claude Code), `.codex/config.toml`, `.cursor/mcp.json`, `.agents/mcp_config.json`, `.gemini/settings.json`, `.vscode/mcp.json` — as `kind: 'mcp'` ro
116
+ - The loader table gains the Gemini CLI reader, whose configuration reference the table previously recorded as unavailable: it is live again (read 2026-10-26), so the row carries the GEMINI.md chain with `.git` boundary markers, the `context.
117
+ - Each release now carries a fourth artefact beside the tarball, the wheel and the formula: one single-file executable per target (`darwin-arm64`, `darwin-x64`, `linux-x64`), built from the same `files` set with a pinned Node runtime, publish
118
+ - `simt pack new <name> --source <npm:<package>|git+https://…>` scaffolds a versioned pack — `pack.json`, an unmeasured `bench.json`, one stack rule — and `simt pack lint [dir]` validates it and states its measured ΔR, rejecting a pack with n
140
119
 
141
120
  ### Changed
142
121
 
143
- `harness detect`'s "some readers read AGENTS.md" rows no longer say bare "some"
144
- when the row knows which one: the `readsAgentsMdNote` travels to the text
145
- output, and `.gemini` graduates from unverified candidate to verified detection
146
- facts (presence implies Gemini CLI; AGENTS.md only when `context.fileName`
147
- names it).
148
-
149
- The one-line description the three channels publish (npm, PyPI and Homebrew) now
150
- names the goal plan `0003` is building toward — an intelligence amplifier for AI
151
- coding agents — instead of the adoption-only story it grew out of.
152
-
153
- The per-repository state directory is renamed from `.simt-dev/` to `.simt/`: the
154
- index, ledger, lessons, bench artifacts, local-model proposals (`distilled/`,
155
- `reviews/`) and repo-local profiles all live under `.simt/` now. Existing state is
156
- not migrated — a repository that carries a `.simt-dev/` directory moves it once by
157
- hand (`mv .simt-dev .simt`); everything in it is cache or ledger, regenerable from
158
- git. The names this tool writes into files it does not own stay put: the `simt.json`
159
- manifest, the `simt-dev-check.yml` gate, the user-level config directory and the
160
- `SIMT_DEV_*` environment variables keep their names.
161
-
162
- The skill registry leaves the published set again, as ADR-061 ruled when it
163
- first left in 0.2.1. It is a catalogue of one workspace's skills — the
164
- repositories whose bodies it resolves — and an installed copy serving that
165
- catalogue to an adopting repository is a confident wrong answer: the skills
166
- it names do not live in the reader's workspace. 0.5.0 carried the registry
167
- because a fresh install's `simt skills *` and `simt distill` otherwise found
168
- none; the fix is the honest absence the tiered loader already reports — the
169
- path it would have read, and the `simt graph lint <path>` remedy — with a
170
- workspace that owns the registry naming it through `--workspace`,
171
- `SIMT_DEV_WORKSPACE` or the user config file. Custom skills were never part
172
- of the registry: they live in each repository, and the disclosure scan
173
- projects them into AGENTS.md.
174
-
175
- The entry point file is renamed from `bin/simt-dev.mjs` to `bin/simt.mjs`,
176
- matching the command it installs: the command has been `simt` for a long
177
- time, the package is still `simt-dev`, and the file path was the last place
178
- that carried the old spelling. `BIN_REL` in `src/name.mjs` is the single
179
- source of truth, so the generated hook shim and CI workflow pick up the new
180
- path on their next regeneration; the channels (npm, PyPI, Homebrew), the
181
- repository's own CI and the test harnesses follow.
182
-
183
- The hook shim's remedy and the generated CI workflow now install the release binary
184
- for a repository without npm, instead of the pinned global npm install (`npm i -g`)
185
- that needed the npm that repository kind is defined by not having. The remedy is the
186
- one-line download the binary channel names, with its platform derived from `uname`;
187
- the CI step verifies the download against the published `.sha256` and runs with no
188
- `setup-node` step at all — a file that runs without a Node toolchain should not have
189
- one installed to run it.
190
-
191
- The locator resolves `.simt/bin/simt` between `node_modules` and `PATH`, so the bytes
192
- a repository downloaded are the bytes its gate runs and a machine-wide install cannot
193
- shadow them. The dispatcher's version check is unchanged and still refuses a stale
194
- copy, whichever path found it.
195
-
196
- Homebrew's formula downloads the release binaries rather than the npm tarball — one
197
- `url`/`sha256` pair per target, chosen at install time — and no longer declares
198
- `depends_on "node"`. `scripts/release-formula.mjs` patches each pair by content, and
199
- the release refuses to publish a formula whose checksums are not the bytes the tagged
200
- tree builds (ADR-098).
122
+ - `harness detect`'s "some readers read AGENTS.md" rows no longer say bare "some" when the row knows which one: the `readsAgentsMdNote` travels to the text output, and `.gemini` graduates from unverified candidate to verified detection facts
123
+ - The one-line description the three channels publish (npm, PyPI and Homebrew) now names the goal plan `0003` is building toward — an intelligence amplifier for AI coding agents — instead of the adoption-only story it grew out of.
124
+ - The per-repository state directory is renamed from `.simt-dev/` to `.simt/`: the index, ledger, lessons, bench artifacts, local-model proposals (`distilled/`, `reviews/`) and repo-local profiles all live under `.simt/` now.
125
+ - The skill registry leaves the published set again, as ADR-061 ruled when it first left in 0.2.1. — [ADR-061](docs/decisions/ADR-061-the-skill-registry-is-workspace-data.md)
126
+ - The entry point file is renamed from `bin/simt-dev.mjs` to `bin/simt.mjs`, matching the command it installs: the command has been `simt` for a long time, the package is still `simt-dev`, and the file path was the last place that carried the
127
+ - The hook shim's remedy and the generated CI workflow now install the release binary for a repository without npm, instead of the pinned global npm install (`npm i -g`) that needed the npm that repository kind is defined by not having.
128
+ - The locator resolves `.simt/bin/simt` between `node_modules` and `PATH`, so the bytes a repository downloaded are the bytes its gate runs and a machine-wide install cannot shadow them.
129
+ - Homebrew's formula downloads the release binaries rather than the npm tarball — one `url`/`sha256` pair per target, chosen at install time — and no longer declares `depends_on "node"`. — [ADR-098](docs/decisions/ADR-098-the-fourth-channel-is-one-file-and-it-needs-no-toolchain.md)
201
130
 
202
131
  ## [0.5.0] — 2026-10-25
203
132
 
204
- ### An offline distiller that proposes, never projects
205
-
206
- `simt distill <skill|doc>` writes a budgeted summary of a skill body or repository
207
- document with citations to source line ranges (`[L12-L15]`). The default engine is a
208
- deterministic outline pass with no model and no network; `--local` uses the configured
209
- local model endpoint from plan 0003 R37. Proposals are cached by content digest in
210
- `.simt-dev/distilled/<digest>.md` and carry provenance frontmatter (source digest,
211
- engine, prompt hash, `verified`), so a human merges them into the pack or repository:
212
- model output never lands in projected or committed text (ADR-090). Every citation is
213
- verified against the source, and a fabricated one makes the proposal
214
- `verified: false` with the fault named.
215
-
216
- ### An advisory critic over the staged diff
217
-
218
- `simt review` gathers the brief — the governing decision records and scoped rules for
219
- the staged files — with no model and no network. `simt review --local` sends that brief
220
- plus the staged diff to the local model and receives an ADR-008 verdict skeleton
221
- (`BLOCK`, `APPROVE WITH RESIDUAL RISK` or `APPROVE`) that cites rule ids with an
222
- `@` marker; a citation to an id that governs nothing is named a fabrication, a
223
- `BLOCK` carries no weight without a file:line or command finding, and a residual-risk
224
- approval must list what the critic did not review. Verdicts land as proposals in
225
- `.simt-dev/reviews/<digest>.md` and stay advisory: they become a gate only when
226
- `simt bench critic --labels <path>` measures precision of at least 0.8 (with recall
227
- reported) on at least 30 historical diffs human-labelled in a JSONL file the tool
228
- consumes but never writes.
229
-
230
- ### Model and effort advice in the brief header
231
-
232
- `simt brief` now carries an `ADVICE` header, such as `small model, no plan mode` or
233
- `frontier model, plan first`, derived from the estate's own bench history: the brief's
234
- task kind and estimated file size pick a neighbourhood of similar bench tasks, the
235
- cheapest model class that actually resolved them sets the tier on a thirds ladder of
236
- the classes the estate has observed (no vendor names in code), and median tool calls in
237
- the neighbourhood set the effort. Nothing is selected: a human still names the model.
238
- With no history the brief says nothing, and with a thin one it prints a caution rather
239
- than a guess. The advice is advisory until a held-out bench shows a cost cut of at
240
- least 25% at a resolved-rate loss of at most 2 points, so the unmeasured bar travels
241
- in every packet the advice appears in.
133
+ - **An offline distiller that proposes, never projects** — `simt distill <skill|doc>` writes a budgeted summary of a skill body or repository document with citations to source line ranges (`[L12-L15]`). — [ADR-090](docs/decisions/ADR-090-an-optional-adapter-is-off-until-named.md)
134
+
135
+ - **An advisory critic over the staged diff** — `simt review` gathers the brief — the governing decision records and scoped rules for the staged files — with no model and no network. — [ADR-008](docs/decisions/ADR-008-verdict-vocabulary.md)
136
+
137
+ - **Model and effort advice in the brief header** — `simt brief` now carries an `ADVICE` header, such as `small model, no plan mode` or `frontier model, plan first`, derived from the estate's own bench history: the brief's task kind and estimated file size pick a neighbourhood of similar ben
242
138
 
243
139
  ### Fixed
244
140
 
245
- `package.json` gained the `repository` field npm's provenance verification requires:
246
- the sigstore bundle is checked against the public source repository, and the missing
247
- URL rejected the 0.4.0 upload with `E422`.
141
+ - `package.json` gained the `repository` field npm's provenance verification requires: the sigstore bundle is checked against the public source repository, and the missing URL rejected the 0.4.0 upload with `E422`.
248
142
 
249
143
  ## [0.4.0] — 2026-10-20
250
144
 
251
- ### Hybrid retrieval fuses BM25, the code graph, and the resolver
252
-
253
- `simt index` indexes full-text tokens across `code`, `docs`, `skills`, `decisions` and
254
- `lessons` into the SQLite FTS5 `fts` table (with an Okapi BM25 fallback when the index is
255
- JSON-lines). `simt map` and `simt brief` fuse FTS5 `bm25()`, PageRank graph proximity and
256
- resolver signals with reciprocal-rank fusion (`RRF_K = 40`), raising gold-file recall@10 on
257
- the 20 mined tasks here from 54.3% to 61.3% (above the 60% bar in ADR-091). With `--embed`
258
- and a configured `local.embed` endpoint, `simt index` stores `Float32Array` vector blobs in
259
- the `embeddings` table and `simt map --embed` adds brute-force cosine similarity; embeddings
260
- stay off by default unless they raise recall@10 by at least 10 percentage points.
261
-
262
- ### A local model adapter that opens zero sockets until named
263
-
264
- `simt local doctor` probes the OpenAI-compatible `local.chat` and `local.embed` endpoints
265
- declared in `simt.json` (`/v1/models`, `/v1/chat/completions`, `/v1/embeddings` for Ollama,
266
- `llama-server`, LM Studio and MLX servers) and reports reachability, model presence and
267
- latency. Non-loopback URLs are refused unless `--allow-remote` is given (ADR-075, ADR-090),
268
- and when `local` is unset nothing opens a socket.
269
-
270
- ### Estate learning and ADR drafts across a workspace
271
-
272
- `simt compare --learn` aggregates benchmark uplift (`ΔR`, `ΔC`) per rule section across
273
- adopted sibling repositories, promotes lessons repeated across at least two repositories as
274
- shared candidates, and flags gates that fail more than 20% of the time as `NOISY-GATE`.
275
- `simt compare --draft-adr <pattern>` writes a proposed ADR draft under `docs/decisions/`
276
- carrying `Origin: simt compare --draft-adr` and the workspace evidence behind it.
277
-
278
- ### Rule text variants have to win on the bench
279
-
280
- A rule may declare candidate wordings under `simt-variants:` in its frontmatter (or supply
281
- them with `--variants-file`). `simt bench variants <rule-id>` evaluates each candidate on
282
- paired benchmark runs with McNemar on resolved rate and Wilcoxon signed-rank on cost,
283
- promotes a wording only when `ΔR > 0` (or `ΔR == 0` and `ΔC < 0`) at `p < 0.05`, and records
284
- the winning variant and its `p`-value in `simt-dev.lock.json`.
285
-
286
- ### Decision records carry structured supersession frontmatter
287
-
288
- `simt decide "<title>"` scaffolds the next numbered ADR in `docs/decisions/` with structured
289
- frontmatter (`Status`, `Date`, `Supersedes`, `Superseded-by`, `Affects`) and updates the
290
- records it supersedes when `--supersedes` and `--yes` are given. `simt rules lint` reports
291
- `PARTIAL-SUPERSEDE` when prose claims to supersede part of an earlier record without
292
- matching frontmatter links.
293
-
294
- ### Lessons are captured from failure-to-fix pairs, recalled on match, and expire
295
-
296
- When a failing gate or test run in `.simt-dev/ledger.jsonl` is followed by a passing run on
297
- the same package, `simt lessons` records a deterministic candidate in
298
- `.simt-dev/lessons-candidates.jsonl`. `simt lessons promote <id> --yes` writes a reviewed
299
- lesson to `docs/lessons/<id>.md` with `Paths`, `Check`, `Expires` and `ADR` fields, and
300
- `simt check` reports `LESSON-EXPIRED` once the date passes or the linked ADR is superseded.
301
- Matching lessons (at most two) are recalled in `simt brief` and by `simt agent-hook
302
- after-tool` when a tool call fails on the same package or error signature.
303
-
304
- ### Skills compile to the Agent Skills standard and report their triggers
305
-
306
- `simt skills compile` validates and compiles skill sources against the Agent Skills standard
307
- (frontmatter `name` and `description`, description under 1024 characters, `SKILL.md` body at
308
- most 500 lines, and on-demand `references/` and `scripts/` links). `simt skills triggers
309
- "<prompt>"` previews which skills match a task and the score breakdown behind each match.
310
-
311
- ### A task brief fits in the bytes a prompt can carry
312
-
313
- `simt brief "<task>"` assembles what a task needs into one packet: the kind of the task
314
- and the commands that kind needs, the files the map ranks highest, the tests the index
315
- knows for them, the decisions that govern them, the skills the resolver selects, the
316
- lessons that name those paths, and the cautions — dirty files to leave alone, generated
317
- paths, files no test covers. The packet stays inside `--budget` bytes (2048 by default,
318
- `0` for all of it), ends with the number of items it had to cut, and prints the same bytes
319
- on the same tree; `--explain` prints every item with the reason it was kept or dropped.
320
- The accept figure it was designed against — 20% fewer turns, 15% cheaper than the kernel
321
- arm at no loss of resolved rate — is unmeasured here, because it needs a bench run and an
322
- installed agent CLI and neither is on this machine. `simt skills brief` keeps its own
323
- behaviour for now: retiring it changes two accepted decisions and an MCP output schema,
324
- and this slice carries no decision to do it.
325
-
326
- ### The packet arrives without being asked for
327
-
328
- A repository that declares a `brief` key in its manifest gets the packet injected as context
329
- before every prompt: `simt agent-hooks install` writes a `UserPromptSubmit` hook into Claude
330
- Code's and Codex's own config, and `simt agent-hook prompt-submit --harness <id>` answers in
331
- that harness's dialect with `hookSpecificOutput.additionalContext`. It never decides and never
332
- exits 2 — no prompt text, no opt-in, a cold index or an assembly error all print why on stderr
333
- and exit 0. The injection is capped at 2048 bytes; `brief.bytes` may lower that and may not
334
- raise it. Cursor and Gemini CLI are not in the table: Cursor's prompt event can only block a
335
- prompt, and Gemini CLI's replacement is not in the reader table. The pre-tool path pays nothing
336
- for this — the repository is reached through a dynamic import only a prompt event opens. MCP
337
- gains a `brief` tool so a client can pull the same packet, assembled by the same code as the
338
- command.
339
-
340
- ### A brief can cite a code graph it did not build
341
-
342
- A repository may declare a `federation` block in `simt.json` naming external code-graph
343
- servers, each with the command that starts it and, optionally, the MCP tool to ask, the
344
- argument the question goes in, the languages it covers and the timeout to wait. `simt brief`
345
- and the MCP `brief` tool then ask each declared server one structural question for the task
346
- and add the answer to a `FEDERATION` section that names its source; a `covers <languages>`
347
- line records what a server claims without yet skipping the index's own extraction of those
348
- languages. The list is the repository's own committed declaration, the same trust model as
349
- `gates.commands`, and nothing is started that the manifest did not name. A malformed entry, a
350
- spawn failure, a protocol error or a timeout all become a caution and the brief still exits
351
- `0`; a server that never answers is bounded by its own per-server timeout. MCP also gains a
352
- `federation_query` tool, which asks a named server (or every declared one) one structural
353
- question on demand and returns the answer with its source; like the brief's fan-out it goes
354
- only to the tool and argument the manifest declared. The packet delivered at prompt submit
355
- does not federate — starting someone else's process is not a wait a 300 ms submit may take.
145
+ - **Hybrid retrieval fuses BM25, the code graph, and the resolver** — `simt index` indexes full-text tokens across `code`, `docs`, `skills`, `decisions` and `lessons` into the SQLite FTS5 `fts` table (with an Okapi BM25 fallback when the index is JSON-lines). — [ADR-091](docs/decisions/ADR-091-the-map-ranks-files-a-task-mentions.md)
356
146
 
357
- ## [0.3.1] — 2026-10-18
147
+ - **A local model adapter that opens zero sockets until named** — `simt local doctor` probes the OpenAI-compatible `local.chat` and `local.embed` endpoints declared in `simt.json` (`/v1/models`, `/v1/chat/completions`, `/v1/embeddings` for Ollama, `llama-server`, LM Studio and MLX servers) and reports rea — [ADR-075](docs/decisions/ADR-075-nothing-leaves-the-machine.md), [ADR-090](docs/decisions/ADR-090-an-optional-adapter-is-off-until-named.md)
358
148
 
359
- ### Who a change reaches
149
+ - **Estate learning and ADR drafts across a workspace** — `simt compare --learn` aggregates benchmark uplift (`ΔR`, `ΔC`) per rule section across adopted sibling repositories, promotes lessons repeated across at least two repositories as shared candidates, and flags gates that fail more than 20% o
360
150
 
361
- `simt impact` takes a tracked path or the name of one export. It lists the files that import it, the tests of that closure, the decisions and rules that govern the file, and the last matching CODEOWNERS line. In a workspace it also lists another repository that references an OpenAPI document or a JSON Schema file this change touches. Each list stops at 64 (ADR-096).
151
+ - **Rule text variants have to win on the bench** — A rule may declare candidate wordings under `simt-variants:` in its frontmatter (or supply them with `--variants-file`).
362
152
 
363
- ### A gate report keeps the failures and counts the passes
153
+ - **Decision records carry structured supersession frontmatter** — `simt decide "<title>"` scaffolds the next numbered ADR in `docs/decisions/` with structured frontmatter (`Status`, `Date`, `Supersedes`, `Superseded-by`, `Affects`) and updates the records it supersedes when `--supersedes` and `--yes` are
364
154
 
365
- `simt gate` compacts a report it knows how to read: TAP, JUnit XML, pytest's junit file, vitest or jest JSON, and `go test -json`. Failures come first, with the name, the assertion and the location. Passing tests are a count. The raw log path is printed. Anything else is printed unchanged (ADR-095).
155
+ - **Lessons are captured from failure-to-fix pairs, recalled on match, and expire** — When a failing gate or test run in `.simt-dev/ledger.jsonl` is followed by a passing run on the same package, `simt lessons` records a deterministic candidate in `.simt-dev/lessons-candidates.jsonl`.
366
156
 
367
- ### A change runs the tests that import it
157
+ - **Skills compile to the Agent Skills standard and report their triggers** — `simt skills compile` validates and compiles skill sources against the Agent Skills standard (frontmatter `name` and `description`, description under 1024 characters, `SKILL.md` body at most 500 lines, and on-demand `references/` and `scrip
368
158
 
369
- `simt gate` selects the test files that import a changed file, directly or through other files, and runs only those with the package's own runner. Paths name the change. `--changed` reads the diff. A clean diff runs nothing. Changing `src/resolve/resolve.mjs` in this checkout selects 51 files, the hand-built list, in about 40 ms. The bar is 5 seconds (ADR-094).
159
+ - **A task brief fits in the bytes a prompt can carry** — `simt brief "<task>"` assembles what a task needs into one packet: the kind of the task and the commands that kind needs, the files the map ranks highest, the tests the index knows for them, the decisions that govern them, the skills the re
370
160
 
371
- ### what governs a file is the decision, rule, skill, gate or lesson that names it
161
+ - **The packet arrives without being asked for** — A repository that declares a `brief` key in its manifest gets the packet injected as context before every prompt: `simt agent-hooks install` writes a `UserPromptSubmit` hook into Claude Code's and Codex's own config, and `simt agent-hook pr
372
162
 
373
- `simt why` reads an Affects line as the tokens in backticks, including a line the paragraph wraps onto. A token that is a command, a path, or a glob is an edge. A token that is none of those is printed as `UNRESOLVED-AFFECTS`. Rules, skills, gates and lessons contribute the path lists they already publish. At `4c27514` every one of the sixteen Affects tokens resolves or is reported, and ADR-017 is the only record that names the dispatcher (ADR-093).
163
+ - **A brief can cite a code graph it did not build** — A repository may declare a `federation` block in `simt.json` naming external code-graph servers, each with the command that starts it and, optionally, the MCP tool to ask, the argument the question goes in, the languages it covers and the t
374
164
 
375
- ### The tests of a file are its importers, its conventional neighbour, and its coverage
165
+ ## [0.3.1] — 2026-10-18
376
166
 
377
- `simt tests` lists the tests that exercise a file. An import counts, including one whose specifier is on the next line and a dynamic `import()`. So does the conventional source beside the test, and a coverage record that names both sides. Files that only share commits are a separate list, added with `--cochange`. On 30 hand-labelled files in this checkout every predicted test is in the label. The bar is 0.8 (ADR-092).
167
+ - **Who a change reaches** — `simt impact` takes a tracked path or the name of one export. — [ADR-096](docs/decisions/ADR-096-impact-names-who-a-change-reaches.md)
378
168
 
379
- ### The map ranks the files a task is likely to touch
169
+ - **A gate report keeps the failures and counts the passes** — `simt gate` compacts a report it knows how to read: TAP, JUnit XML, pytest's junit file, vitest or jest JSON, and `go test -json`. — [ADR-095](docs/decisions/ADR-095-gate-output-keeps-the-failures.md)
380
170
 
381
- `simt map` orders the index by the task's words and by the import and call edges around them. It renders paths, exported names, or signatures, inside a byte budget. `simt bench report` prints recall@10: the mean fraction of a mined task's gold files that land in the top 10. On the 20 mined tasks measured here that figure is 55.6%. The bar is 60% (ADR-091).
171
+ - **A change runs the tests that import it** — `simt gate` selects the test files that import a changed file, directly or through other files, and runs only those with the package's own runner. — [ADR-094](docs/decisions/ADR-094-a-change-runs-the-tests-that-import-it.md)
382
172
 
383
- ### The index names the exports and the imports
173
+ - **what governs a file is the decision, rule, skill, gate or lesson that names it** — `simt why` reads an Affects line as the tokens in backticks, including a line the paragraph wraps onto. — [ADR-017](docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md), [ADR-093](docs/decisions/ADR-093-why-names-what-governs-a-file.md)
384
174
 
385
- `simt index` fills symbols and import edges from the blob it already hashes. The built-in extractors are line-oriented, for the languages a map needs, and on the labelled fixtures they recall at least 90% of the exported names for TypeScript, Kotlin, Python and Go. A structural parser is a separate package named by `index.parsers`; with that key absent, nothing is loaded. It writes cache rows and does not edit the repository. A live comparison against tree-sitter is unmeasured (ADR-090).
175
+ - **The tests of a file are its importers, its conventional neighbour, and its coverage** — `simt tests` lists the tests that exercise a file. — [ADR-092](docs/decisions/ADR-092-tests-are-imports-conventions-and-coverage.md)
386
176
 
387
- ### The local index is a cache of git blobs
177
+ - **The map ranks the files a task is likely to touch** — `simt map` orders the index by the task's words and by the import and call edges around them. — [ADR-091](docs/decisions/ADR-091-the-map-ranks-files-a-task-mentions.md)
388
178
 
389
- `simt index` rebuilds `.simt-dev/index.sqlite` from `git ls-files`. A blob that has not changed is not read again, and the same blobs produce the same rows. When this Node cannot open `node:sqlite` with FTS5, the same rows are written to `.simt-dev/index.jsonl` instead (ADR-089).
179
+ - **The index names the exports and the imports** — `simt index` fills symbols and import edges from the blob it already hashes. — [ADR-090](docs/decisions/ADR-090-an-optional-adapter-is-off-until-named.md)
390
180
 
391
- ### A marked line is enforced by a checker
181
+ - **The local index is a cache of git blobs** — `simt index` rebuilds `.simt-dev/index.sqlite` from `git ls-files`. — [ADR-089](docs/decisions/ADR-089-the-local-index-is-a-cache-of-git-blobs.md)
392
182
 
393
- A rule line may name a checker with `simt-check`. The projection keeps `Enforced:` and the checker's name, and `simt check` runs the checker. The file-length line is `max-file-lines`: past 400 lines warns, past 1000 fails, and a path is exempt only when `limits.fileLines.generated` names it. A path under `node_modules` is not counted: a committed dependency is not a file this repository wrote, and counting it failed every install of this package. A file-length warning and a checker that is not built yet are reported and do not fail `check` or `sync`. `import-direction` stays in the prose until the module graph exists. Probe compliance is unmeasured (ADR-088).
183
+ - **A marked line is enforced by a checker** — A rule line may name a checker with `simt-check`. — [ADR-088](docs/decisions/ADR-088-a-marked-line-is-enforced-by-a-checker.md)
394
184
 
395
- ### A run is evidence for one tree
185
+ - **A run is evidence for one tree** — `simt evidence` prints the runs recorded against the tree on disk. — [ADR-087](docs/decisions/ADR-087-a-run-is-evidence-for-one-tree.md)
396
186
 
397
- `simt evidence` prints the runs recorded against the tree on disk. A gate the dispatcher runs appends a line: the command, the exit, a short summary, and a digest of the output. Editing any file changes the tree, so that line no longer applies. `simt evidence check` warns when a commit message says the tests passed and this tree has no passing line, and fails the commit when `gates.evidence` is `block`. `simt hooks install --yes` writes the commit-msg shim only when that setting is present (ADR-087).
187
+ - **A workspace carries its repeated rules once** — `simt shared plan` names the rules every repository in a workspace renders with the same bytes, and where each harness would load that copy. — [ADR-084](docs/decisions/ADR-084-a-workspace-carries-its-repeated-rules-once.md)
398
188
 
399
- ### A workspace carries its repeated rules once
189
+ - **The region ends with the names of what it kept out** — The generated region ends with a `## Disclosure` section: one line per scoped profile, with the patterns and the files it moved to, and one line per installed skill, with its name and the description that says when to open it. — [ADR-085](docs/decisions/ADR-085-the-region-ends-with-the-names-it-kept-out.md)
400
190
 
401
- `simt shared plan` names the rules every repository in a workspace renders with the same bytes, and where each harness would load that copy. `simt shared install --yes` writes the copy into the user-level file the harness actually reads, and records the ids on each repository's manifest under `shared`. The next `simt sync` drops those rules from the region and names them, with the command that shows each one. `simt doctor` says when the copy is missing or stale, and does not fail the repository for it. The 30 KB estate bar and the bench comparison are unmeasured (ADR-084).
191
+ - **The stable prefix comes first** — Shipped rules lead the region, and the command card follows them, so an edit this repository makes changes only the tail. — [ADR-086](docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md)
402
192
 
403
- ### The region ends with the names of what it kept out
193
+ - **Kernel mode packs to the budget the repository declared** — `mode: kernel` reads `budget.bytes`, holds back a reserve for the files that load beside the region, and drops rules least-valuable-first until the region fits. — [ADR-083](docs/decisions/ADR-083-a-budget-is-met-by-cutting-the-kernel-and-hard-is-the-floor.md)
404
194
 
405
- The generated region ends with a `## Disclosure` section: one line per scoped profile, with the patterns and the files it moved to, and one line per installed skill, with its name and the description that says when to open it. The skill body stays in its file. A surface that cannot narrow a profile still carries that profile in the region (ADR-085).
195
+ - **Vulnerability reports** — `SECURITY.md` names the two places a report goes: a GitHub issue on this repository, or `security@simt.space`.
406
196
 
407
- ### The stable prefix comes first
197
+ ## [0.3.0] — 2026-10-17
408
198
 
409
- Shipped rules lead the region, and the command card follows them, so an edit this repository makes changes only the tail. `simt sync --yes` writes `simt-dev.lock.json` with an input digest and an output digest. `simt check --frozen` fails when the output moves and the inputs did not, and names a stale lock separately (ADR-086).
199
+ - **The rules and the decisions are readable as MCP resources** — A client that wants to browse this tool's rules and rulings had no way to: every answer came back from a tool call with an argument in it. — [ADR-074](docs/decisions/ADR-074-a-rule-and-a-decision-are-readable-by-uri.md)
410
200
 
411
- ### Kernel mode packs to the budget the repository declared
201
+ - **A plan is resumed one slice at a time** — `simt plan next` returns the next ready slice and only its section. — [ADR-081](docs/decisions/ADR-081-a-tracker-is-read-through-the-repositorys-template.md), [ADR-082](docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md)
412
202
 
413
- `mode: kernel` reads `budget.bytes`, holds back a reserve for the files that load beside the region, and drops rules least-valuable-first until the region fits. Every cut rule is named, with the command that shows it. `full` mode cuts nothing. A repository whose hard rules already exceed the budget is told so, and those rules stay (ADR-083).
203
+ - **The rules are enforced at the tool call** — Three rules that text does not hold — a `git push`, a `--no-verify`, an edit inside a generated region — are now a decision before the tool call. — [ADR-079](docs/decisions/ADR-079-the-rules-are-enforced-at-the-tool-call-from-one-policy-table.md)
414
204
 
415
- ### Vulnerability reports
205
+ - **A line the repository already proves does not ship** — A rule line may say when the repository already proves it. — [ADR-078](docs/decisions/ADR-078-a-line-the-repository-already-proves-does-not-ship.md)
416
206
 
417
- `SECURITY.md` names the two places a report goes: a GitHub issue on this repository, or `security@simt.space`.
207
+ - **The region leads with this repository's commands** — The projected region opens with the commands this repository actually runs, derived from its committed gates and runner, in both the full projection and kernel mode (ADR-077). — [ADR-077](docs/decisions/ADR-077-the-command-card-leads-the-region-and-reads-committed-evidence.md)
418
208
 
419
- ## [0.3.0] — 2026-10-17
209
+ - **A change to what an agent reads can be measured** — `simt bench mine`, `run`, `report`, `ablate` and `probes` measure a change to projected context against tasks taken from this repository's own history. — [ADR-076](docs/decisions/ADR-076-a-change-to-what-agents-read-is-measured.md)
420
210
 
421
- ### The rules and the decisions are readable as MCP resources
211
+ - **What each reader loads, and a transcript split** — `simt harness loaders` reports what each reader loads at session start, in bytes, and what its size cap drops.
422
212
 
423
- A client that wants to browse this tool's rules and rulings had no way to: every answer came back from a tool call with an argument in it. They are addressable now, as one URI per rule and one per decision record, with the byte size each will cost stated up front — the one number a client cannot get without spending the read.
213
+ - **A name this tool owns is not reported as a repository** — `simt rules why` keeps this tool's own names (`simt-priority`, `simt-hooks`, and the rest) and still redacts a repository the reader cannot open (ADR-080). — [ADR-080](docs/decisions/ADR-080-the-tools-own-names-are-not-repositories-and-neither-reader-decides-by-shape.md)
424
214
 
425
- Both resource revisions of the specification were read rather than recalled, and they disagree about the behaviour that decides whether a client retries or gives up: a missing resource is `-32602` on the newest revision and `-32002` on the two older ones, and neither lets a server answer with an empty body. A decision record is redacted on the way out, because a resource travels further than the CLI did (ADR-074).
215
+ ## [0.2.2] — 2026-10-14
426
216
 
427
- ### A plan is resumed one slice at a time
217
+ - **A repository can ask for a smaller projection** — `simt tokens` measured the cost and `check` now holds the line the repository draws: `budget.bytes` in `simt.json` is how many always-on bytes that repository has agreed to carry, and both the meter and the gate report the overflow with the — [ADR-073](docs/decisions/ADR-073-a-kernel-is-a-smaller-projection-the-repository-asks-for.md)
428
218
 
429
- `simt plan next` returns the next ready slice and only its section. `simt plan done` rewrites that one row, and previews the change unless `--yes` is given. The directories and the table format come from the `plans` key in `simt.json` when a repository sets one; otherwise the tracker template delivered with the tool is used, which reads `docs/plans` and `docs/reviews` (ADR-081, ADR-082). `init` keeps a manifest key it does not write, which is what lets `plans` survive a re-init.
219
+ - **A repository can declare how many always-on bytes it is willing to carry** — `simt tokens` has reported that number since the meter landed, against a constant nobody could argue with. — [ADR-072](docs/decisions/ADR-072-the-budget-is-a-declaration-beside-the-gates.md)
430
220
 
431
- ### The rules are enforced at the tool call
221
+ - **A repository can declare the always-on bytes it intends to carry** — `simt tokens` measured the number; it could not be held to one. — [ADR-072](docs/decisions/ADR-072-the-budget-is-a-declaration-beside-the-gates.md)
432
222
 
433
- Three rules that text does not hold — a `git push`, a `--no-verify`, an edit inside a generated region — are now a decision before the tool call. `simt agent-hooks install` writes one policy into the harnesses that read a hooks file, and keeps every key it does not own. `simt agent-hook` is the entry point those harnesses call (ADR-079).
223
+ - **A stack profile loads only when a matching file is in play** — A stack profile used to be always-on everywhere, because profiles reached a reader through the generated region in AGENTS.md and through nothing else. — [ADR-071](docs/decisions/ADR-071-a-rule-that-can-be-narrowed-is-written-narrow.md)
434
224
 
435
- ### A line the repository already proves does not ship
225
+ - **Every `--json` payload says which version it is** — An agent that parses `--json` had no way to notice that a field had moved: the parse succeeds, the lookup returns `undefined`, and the answer is quietly wrong. — [ADR-070](docs/decisions/ADR-070-the-json-payload-carries-its-own-version.md)
436
226
 
437
- A rule line may say when the repository already proves it. `simt sync` drops that line, and `--explain` names the line and the fact, so a removal is visible rather than silent (ADR-078).
227
+ - **`graph lint` now checks skills against the published specification** — The Agent Skills specification sets limits nobody here was checking: a name of lowercase letters, digits and single hyphens that equals the directory it lives in; a description under 1,024 characters; a compatibility note under 500; a body
438
228
 
439
- ### The region leads with this repository's commands
229
+ - **The MCP surface speaks three protocol revisions, and answers in the shape each asks for** — The server was pinned to one revision and returned pretty JSON in a text block, so a client could read the answer but could not validate it, could not cache it, and could not tell a read-only tool from one that writes.
440
230
 
441
- The projected region opens with the commands this repository actually runs, derived from its committed gates and runner, in both the full projection and kernel mode (ADR-077).
231
+ - **Which harnesses a repository is set up for is now answerable** — `simt-dev harness detect` looks at the configuration directories that exist and says which harnesses this repository — and, with `--user`, its reader — is set up for.
442
232
 
443
- ### A change to what an agent reads can be measured
233
+ - **A monorepo gets rules for each of its packages** — One repository, one projection: every package under the root was given the root's rules, so a React application inside a node workspace was handed the node profile and nothing else, and the profile that should have applied to it was applied
444
234
 
445
- `simt bench mine`, `run`, `report`, `ablate` and `probes` measure a change to projected context against tasks taken from this repository's own history. A change that cannot be run says `unmeasured` in its record rather than implying a result (ADR-076).
235
+ - **One rule set, shared by the repositories that agreed on it** — Several repositories want the same rules and each kept its own copy, so a policy change was several edits that could be several-minus-one, and the repository that missed it carried instructions nobody there wrote. — [ADR-065](docs/decisions/ADR-065-a-pack-is-one-shared-rule-set-pinned-by-digest.md)
446
236
 
447
- ### What each reader loads, and a transcript split
237
+ - **`simt tokens` counts what a reader loads** — Review 0002 filed the estate's always-on context at 90 KB per turn, 72% of it a paragraph an earlier repository had already loaded — a number produced by reading seven repositories by hand, which ages the moment a rule is edited and cannot — [ADR-064](docs/decisions/ADR-064-the-context-meter-counts-what-the-tool-wrote.md)
448
238
 
449
- `simt harness loaders` reports what each reader loads at session start, in bytes, and what its size cap drops. DeepSeek is one of those readers. `simt tokens --session` splits a local Claude Code or Codex transcript into rules, file reads and tool output.
239
+ ## [0.2.1] — 2026-10-13
450
240
 
451
- ### A name this tool owns is not reported as a repository
241
+ - **`init` writes in place; the backup tag and adopt branch are opt-in** — `simt init --yes` used to always tag the pre-adoption commit and branch onto a dedicated adopt branch before writing. — [ADR-063](docs/decisions/ADR-063-the-adoption-escape-hatch-is-opt-in.md)
452
242
 
453
- `simt rules why` keeps this tool's own names (`simt-priority`, `simt-hooks`, and the rest) and still redacts a repository the reader cannot open (ADR-080).
243
+ - **A fresh adoption seeds the gates it can run — and only those** — The eight common ecosystems outside the JavaScript world — Rust, Go, .NET, Swift, Deno, Ruby, Dart and Elixir — were recognised by the fingerprint and served by nothing: no profile selected them, no gate was proposed for them. — [ADR-062](docs/decisions/ADR-062-a-fresh-adoption-seeds-the-gates-it-can-run.md)
454
244
 
455
- ## [0.2.2] — 2026-10-14
245
+ - **The resolver is measured against a gold corpus** — `simt skills eval` scores the skill resolver against a corpus of real tasks × real repositories, each case carrying a gold list of the skills it should select and a list of the ones it must not.
456
246
 
457
- ### A repository can ask for a smaller projection
458
-
459
- `simt tokens` measured the cost and `check` now holds the line the repository draws: `budget.bytes`
460
- in `simt.json` is how many always-on bytes that repository has agreed to carry, and both the meter and
461
- the gate report the overflow with the two numbers in them. It sits beside `gates` rather than inside
462
- `projection`, because that key belongs to this tool and an upgrade replaces it.
463
-
464
- `mode: kernel` is the other half. It keeps the rules a reader cannot do without — the ones a hard
465
- constraint or a command depends on — and defers the rest behind a `## Deferred rules` section that
466
- names each one and the command that shows it. A Python fixture projects 16% smaller. `full` is still
467
- the default, because nobody has measured yet whether the smaller projection loses compliance, and a
468
- tool that made that trade silently would be deciding it on the reader's behalf (ADR-073).
469
-
470
- ### A repository can declare how many always-on bytes it is willing to carry
471
-
472
- `simt tokens` has reported that number since the meter landed, against a constant nobody
473
- could argue with. A repository can now say what it will carry — `budget: { bytes: N }`
474
- beside `limits` and `gates` in the manifest — and both `simt tokens` and `simt-dev check`
475
- measure against that line. One declaration, one meter, one function that words the finding.
476
-
477
- The declaration is deliberately *not* inside `projection`, which is the one block this tool
478
- owns and `upgrade` is allowed to replace: a number written there would be deleted silently
479
- on the next upgrade and never reported as drift. And a repository that declares nothing is
480
- reported as declaring nothing, rather than being quietly handed the default as though it
481
- had chosen it.
482
-
483
- Over budget is reported and never fails the gate. Nothing was cut — the projection is
484
- still full mode, every rule loaded as written — so the honest thing to say is "you are
485
- over your line", not to fail a repository over a ceiling it wrote down itself. Which rules
486
- move behind skills stays with the evaluation that has to decide it (ADR-072).
487
-
488
- ### A repository can declare the always-on bytes it intends to carry
489
-
490
- `simt tokens` measured the number; it could not be held to one. A manifest now carries
491
- `budget.bytes` — a declaration beside `limits` and `gates`, not inside `projection`,
492
- because that block is regenerated on every upgrade and nothing in the repair would notice
493
- its budget disappearing. A repository that declares nothing is measured against the
494
- review's 30 KB and reports `declared: false` rather than looking like it chose it.
495
-
496
- Over budget is reported, never enforced, and the report says what was not done: nothing
497
- was cut, because the projection is still in full mode. Which rules move behind skills is a
498
- measurement that has not been taken yet, and a budget that quietly dropped rules would be
499
- enforcing a decision nobody made (ADR-072).
500
-
501
- ### A stack profile loads only when a matching file is in play
502
-
503
- A stack profile used to be always-on everywhere, because profiles reached a reader through
504
- the generated region in AGENTS.md and through nothing else. A profile can now declare
505
- `simt-scope`, and a repository on Cursor, Copilot or Antigravity gets it as a file the
506
- surface attaches on patterns: `.cursor/rules/python.mdc` with `globs` and
507
- `alwaysApply: false`, an Antigravity rule with `trigger: glob`, a Copilot
508
- `.instructions.md` with `applyTo`. AGENTS.md keeps one line naming the profile, the
509
- patterns and the file it moved to — a profile that vanishes from the region reads as one
510
- that was deleted.
511
-
512
- The narrowing happens only when every selected surface can do it. Claude Code's `paths:`
513
- key is documented and silently fails to lazy-load for quoted values, and opencode has no
514
- per-rule channel at all, so a repository carrying either keeps its profiles in the region
515
- and is told which surface stopped it. A rule that never loads is worse than a rule that
516
- loads eagerly.
517
-
518
- Reading the two vendors' docs changed the shape of this: Antigravity requires patterns
519
- starting with `*` to be quoted or YAML reads them as an alias anchor and the rule is
520
- discarded with no message, and it documents a 20,000-token budget for always_on rules past
521
- which they are demoted to pointers — so eager profiles cost the reader a real budget. A
522
- `simt-scope` that matches everything is now refused, since that is the same behaviour with
523
- a narrowing-looking spelling (ADR-071).
524
-
525
- ### Every `--json` payload says which version it is
526
-
527
- An agent that parses `--json` had no way to notice that a field had moved: the parse
528
- succeeds, the lookup returns `undefined`, and the answer is quietly wrong. Every payload now
529
- opens with `schemaVersion`, and a version in a comment is not a version, so it travels in
530
- the document every reader is guaranteed to see.
531
-
532
- The same document is now one line rather than four columns of whitespace, because the reader
533
- of `--json` is a program and it pays for every byte. `--pretty` asks for the other form, and
534
- a terminal gets it without being asked, because there the reader is a person.
535
-
536
- Writing the contract test found two things the review that asked for this missed. Eight
537
- commands — `doctor`, `mcp --list`, `graph lint`, `rules list`, and the `skills` ones — had a
538
- private `emit(JSON.stringify(..., null, 2))` instead of the shared helper, so `--json` meant
539
- something different depending on which command you asked. And a command that *failed* under
540
- `--json` wrote only to stderr, so a scripted caller got an empty document and a non-zero
541
- exit and no way to learn what went wrong. It now answers in the shape it was asked in (ADR-070).
542
-
543
- ### `graph lint` now checks skills against the published specification
544
-
545
- The Agent Skills specification sets limits nobody here was checking: a name of lowercase
546
- letters, digits and single hyphens that equals the directory it lives in; a description
547
- under 1,024 characters; a compatibility note under 500; a body recommended under 5,000
548
- tokens because an agent loads all of it once the skill activates. `graph lint --spec
549
- agentskills` checks every skill against those, and the specification was fetched rather
550
- than recalled — reading it turned up four rules the review that asked for this did not
551
- list, including the one that breaks installs.
552
-
553
- What the specification calls a must fails the exit code; what it calls a recommendation
554
- warns. A lint that fails on advice is a lint that gets skipped. `skills install` now asks
555
- the same name rules, since the directory is created from the name and an unaddressable
556
- skill would be the last thing anyone notices.
557
-
558
- ### The MCP surface speaks three protocol revisions, and answers in the shape each asks for
559
-
560
- The server was pinned to one revision and returned pretty JSON in a text block, so a
561
- client could read the answer but could not validate it, could not cache it, and could not
562
- tell a read-only tool from one that writes. All three published revisions were read rather
563
- than guessed at, and each is answered in its own shape: the newest drops the handshake for
564
- a version carried on every request, adds a discovery method servers must implement,
565
- requires a result-type field and a cache lifetime on list results, and deleted the liveness
566
- method — which older clients still get, and newest clients are told is gone by name.
567
-
568
- Every tool now declares what it returns and says it is a reader, and every call returns
569
- the answer in both shapes: the text block for a client that predates structured content,
570
- the structured block for one that validates against the declared schema. The byte ceiling
571
- bounds the text only, so the block a client can check still checks. The schema checker
572
- that makes this testable lives in the package rather than in a dependency.
573
-
574
- ### Which harnesses a repository is set up for is now answerable
575
-
576
- `simt-dev harness detect` looks at the configuration directories that exist and says
577
- which harnesses this repository — and, with `--user`, its reader — is set up for. It
578
- claims presence and nothing else: a directory found is not a claim about what its owner
579
- reads, because the tool cannot answer that without a vendor document, and a guess here
580
- would be the reader's problem the first time it was wrong. A harness that reads
581
- AGENTS.md is served by the standard surface whatever else it reads, and every surface row
582
- now records that in three values rather than two, because "reads AGENTS.md" and "is
583
- covered for this harness" are not the same sentence — Copilot's CLI reads it and its
584
- editor, code review and cloud agent do not.
585
-
586
- No new harness rows ship with it. The candidates are recorded as directory names, which
587
- claim presence and not coverage; a row still requires the vendor document it cites, and
588
- one that reads nothing has to be added on purpose.
589
-
590
- ### A monorepo gets rules for each of its packages
591
-
592
- One repository, one projection: every package under the root was given the root's rules,
593
- so a React application inside a node workspace was handed the node profile and nothing
594
- else, and the profile that should have applied to it was applied by nobody. The rule that
595
- package needed was not wrong — it was missing.
596
-
597
- Packages are now discovered from the declarations that already list them (npm and pnpm
598
- workspaces, turbo, Cargo members, `go.work`, Gradle includes) rather than by walking the
599
- tree, because a declaration states which directories are members and a marker file only
600
- says there is a build here. The root keeps what every package shares; each package that
601
- selects something new gets a generated region carrying exactly its own delta, and a
602
- package that selects nothing new gets no file at all. `check` reports every package
603
- region and fails on any one of them, and `compare` counts the drifted regions rather than
604
- the repositories that hold them.
605
-
606
- A gate can also name the directory it runs in, which is what a monorepo with no root test
607
- script needs: `gates.cwd` maps a gate to a directory, a directory that does not exist or
608
- resolves outside the repository fails the event and is reported by `check` before the
609
- commit rather than at it, and a directory named for a gate that is not declared is
610
- reported as the inert entry it is.
611
-
612
- ### One rule set, shared by the repositories that agreed on it
613
-
614
- Several repositories want the same rules and each kept its own copy, so a policy change
615
- was several edits that could be several-minus-one, and the repository that missed it
616
- carried instructions nobody there wrote. A pack is that shared set: `packs` in the manifest
617
- declares it, the bytes live in the repository, and a digest of them is recorded so nobody
618
- has to trust that the two agree.
619
-
620
- A pack's rules are selected by the same matcher as any profile and appear in the region's
621
- source list under the pack's own directory. Precedence is the repository, then a pack it
622
- declares, then what the tool ships, and every replacement is reported rather than silent.
623
-
624
- A pack that disagrees with its pin still projects — the gate blocks and prints both
625
- digests, because dropping it would let the repair delete the rules it is reporting. One
626
- that is missing blocks with a remedy that restores it, and one with no digest is reported
627
- without failing anything, since its rules render correctly. `compare` lists every pack and
628
- the digest it is pinned to, so a repository behind a shared change is visible before
629
- anyone follows stale instructions. Nothing is fetched: the bytes are in the repository,
630
- which is what makes the rendered region reproducible from what the reader has (ADR-065).
631
-
632
- ### `simt tokens` counts what a reader loads
633
-
634
- Review 0002 filed the estate's always-on context at 90 KB per turn, 72% of it a paragraph
635
- an earlier repository had already loaded — a number produced by reading seven
636
- repositories by hand, which ages the moment a rule is edited and cannot be re-derived by
637
- the reader who has to act on it.
638
-
639
- `simt tokens` is that reading, as a command. It reports the bytes this tool wrote per
640
- channel (region, rule files, skill metadata), an approximate token figure, and — with
641
- `--repos` — the same figure across a workspace, with every repeated paragraph attributed
642
- to the first root that carries it. A region that ends past a harness's read limit is
643
- reported with its byte offset, using the same numbers the existing check uses.
644
-
645
- Three choices keep the figure honest, and all three are recorded in ADR-064: the meter
646
- counts what the projection wrote and reports hand-written bytes beside it rather than
647
- folding them into the budget; a region is the span between its two markers, so the blank
648
- line a person left above the marker is billed to nobody; and an over-budget repository is
649
- reported rather than failed, because a meter that went red would train people to ignore
650
- the number.
651
-
652
- S38 stays open — this is the instrument. Whether the estate gets under 30 KB is R07's
653
- kernel, R08's budget field and R11's dedupe, measured with this.
247
+ - **The skill registry is workspace data; the package ships none of it** — The npm tarball and the wheel carried `graph/skills.graph.json` — the directory of skill sources for one workspace, naming the repositories whose bodies it resolves. — [ADR-061](docs/decisions/ADR-061-the-skill-registry-is-workspace-data.md)
654
248
 
655
- ## [0.2.1] — 2026-10-13
249
+ - **No published file may name a repository from this estate** — The decision records ship in all three channels — npm, the wheel and the sdist — so a private repository name written into one is published to three registries.
656
250
 
657
- ### `init` writes in place; the backup tag and adopt branch are opt-in
658
-
659
- `simt init --yes` used to always tag the pre-adoption commit and branch onto a dedicated
660
- adopt branch before writing. The safety argument was sound, but consumers kept reporting
661
- the branch switch as a surprise — a well-announced surprise is still a surprise.
662
-
663
- The default now writes in place on the branch you already have: nothing is tagged, nothing
664
- is branched, and the run says so in an `in place` line. The reviewable diff survives —
665
- `git diff` shows the whole adoption, and discarding it is a checkout and a clean.
666
-
667
- The old behaviour moves behind `--backup`: `simt init --yes --backup` tags the pre-adoption
668
- commit and branches as before, for readers who want the named undo path. ADR-063 records
669
- the decision and the one behaviour it invalidates: consumers who scripted against the old
670
- default add `--backup`.
671
-
672
- ### A fresh adoption seeds the gates it can run — and only those
673
-
674
- The eight common ecosystems outside the JavaScript world — Rust, Go, .NET, Swift, Deno,
675
- Ruby, Dart and Elixir — were recognised by the fingerprint and served by nothing: no
676
- profile selected them, no gate was proposed for them. A fresh Rust repository received the
677
- core essay and a manifest that runs nothing, while the ecosystems that emit stack tags got
678
- the full treatment.
679
-
680
- Each now has a profile that selects on its own `has-*` fact, so one marker file reaches
681
- both the rules the repository reads and the gates its commits run. Maven's profile predates
682
- this; its row is gates-only, wrapper-first, like `gradlew`.
683
-
684
- `simt init` now seeds the canonical commands for a detected ecosystem — `cargo fmt
685
- --check`, `go vet ./...`, `dotnet test`, and the rest of the table — into the manifest's
686
- gates, but only where a probe found the tool: a binary on PATH, or a file in the repository
687
- for the Maven wrapper. A marker says the ecosystem is here; the probe says its program can
688
- run. A declared gate that cannot run fails closed on every commit, so the seed fires only
689
- when both are true — and where the probe fails, the gate is dropped with a remedy that
690
- names the install instead of blocking the first commit.
691
-
692
- A seed is a default: a command the repository already declared is never re-seeded, and an
693
- npm runner keeps its own scripts, because `package.json` is the repository's own answer to
694
- "how do I test and format". ADR-062 rules the shape of all of it.
695
-
696
- ### The resolver is measured against a gold corpus
697
-
698
- `simt skills eval` scores the skill resolver against a corpus of real tasks × real
699
- repositories, each case carrying a gold list of the skills it should select and a list
700
- of the ones it must not. It reports precision and recall, prints a PASS/FAIL line per
701
- case naming the skills it missed or selected that it should not have, and exits
702
- non-zero on a regression — so it can be run from CI the way the gate is run.
703
-
704
- The scoring lives in `src/resolve/eval.mjs`, which the command and
705
- `test/eval.test.mjs` both call, so a test cannot drift from what the command measures.
706
- The corpus lives under `test/` because test data does not ship in the package: an
707
- installed copy names the path it would have read and says so instead of scoring the
708
- author's.
709
-
710
- ### The skill registry is workspace data; the package ships none of it
711
-
712
- The npm tarball and the wheel carried `graph/skills.graph.json` — the directory
713
- of skill sources for one workspace, naming the repositories whose bodies it
714
- resolves. Every installed copy served that catalogue to readers who have none
715
- of those skills, and `skills install` pointed at paths that exist only in the
716
- workspace that wrote them. ADR-061 rules the registry out of the published set:
717
- it is data about one workspace, not content of the package.
718
-
719
- The default registry path now resolves in tiers, and every tier is file
720
- existence plus configuration — nothing a pull request cannot diff:
721
-
722
- 1. an explicit path, the positional of `simt graph lint`,
723
- 2. the package's own `graph/skills.graph.json` — present in a source checkout,
724
- absent from an installed copy,
725
- 3. the member of a configured workspace that holds one — naming the workspace
726
- it came from, and refusing when several members hold one, because picking
727
- would be a guess.
728
-
729
- An installed copy in a workspace with no registry says so, and names the three
730
- ways to configure one (`--workspace`, `SIMT_DEV_WORKSPACE`, the user config
731
- file) instead of failing on a path it does not have. `simt graph lint`'s
732
- no-registry remedy names the path explicitly, because its default is the
733
- registry it lints.
734
-
735
- The provenance digest stops walking `graph/` for the same reason it no longer
736
- leaves the package: the digest covers what a published copy ships, and the
737
- registry is no longer among it.
738
-
739
- ### No published file may name a repository from this estate
740
-
741
- The decision records ship in all three channels — npm, the wheel and the sdist — so a
742
- private repository name written into one is published to three registries. That happened:
743
- the records were pseudonymised once, across 28 files, and nothing stopped the next one. The
744
- sweep's own commit says why, and it is the right reason: the redaction it used runs on the
745
- way *out*, at print time, and the bytes on disk are what get published.
746
-
747
- `test/shipped.test.mjs` now fails on an estate name anywhere in a published file, and
748
- asserts its own scope against `package.json` in **both** directions — a directory added to
749
- `files` without being guarded fails, and so does one guarded that nothing publishes, because
750
- a guard nobody can fail is one somebody eventually deletes. It is scoped to the published
751
- set rather than a hand-written list for the same reason: the sweep took 28 files to undo by
752
- hand, and the guard should not need undoing at all.
753
-
754
- Names are not removed, they are described. The records that use a repository as *evidence* —
755
- a table of how three repositories answer the same question — carry stable `repo-NN` labels,
756
- so the comparison survives. Everything else is better off naming what the repository *is*:
757
- "a Gradle module with no wrapper", "a repository with no package.json". That reads to
758
- someone outside the estate without a lookup table they do not have.
759
-
760
- The one place estate names remain is `docs/reviews/`, which is not published by either
761
- channel. A review whose subject is fourteen repositories cannot pseudonymise them without
762
- becoming a document about `repo-01` that never existed.
763
-
764
- ### Four shipped strings named a command this package does not install
765
-
766
- `0.2.0` installs one binary, `simt`, and `package.json`'s `bin` map says so. Four
767
- places still said `simt-dev`, which a reader types and is met with `unknown command`:
768
-
769
- - the default remedy on every bad invocation, `run: simt-dev --help` — so the advice
770
- printed after a bad command could not be followed
771
- - the usage line, `usage: simt-dev <command>`, sitting above a command list that spelled
772
- the same tool `simt`
773
- - `.github/dependabot.yml`'s header, which said `Yours from here: simt-dev` while the
774
- gate beside it said `Yours from here: simt`
775
- - the hook shim, whose own resolution list said `simt-dev on PATH` directly above the
776
- `command -v simt` that does the looking
777
-
778
- `test/naming.test.mjs` was supposed to prevent this and did not: its rule required a
779
- verb after the project name, and none of the four is followed by one. It now also
780
- catches a flag, a `<placeholder>`, the end of a line, and a program name in a
781
- resolution list — while still accepting the sentences where the project is the
782
- subject, which is the spelling that is correct. The two `.github` templates get their
783
- own assertion, because the fourth of these was not a wrong word in a sentence; it was
784
- two templates disagreeing with each other in the same position.
251
+ - **Four shipped strings named a command this package does not install** — `0.2.0` installs one binary, `simt`, and `package.json`'s `bin` map says so.
785
252
 
786
253
  ### Fixed
787
254
 
788
255
  - `hooks install` removes a shim whose gate was dropped from the manifest instead of leaving it to run nothing. A repository that removed its last gate was refused outright, with a remedy telling it to put the gate back; only un-routing git was left to the reader. install also no longer chains a hook to itself.
789
-
790
256
  - **Dead exports and duplicated frontmatter, with a test that stops them returning (S21).**
791
257
  Eight functions were exported and called by nothing (`gateKind`, `gateWhy`, `gateAdr`,
792
258
  `resetWorkspace`, `tracksIdentity`, `isTty`, `surfaceFrontmatter`, `workspaceOrNull`),
@@ -797,7 +263,6 @@ two templates disagreeing with each other in the same position.
797
263
  had already drifted: it carried `doctor`, a bare command the group lookup can never
798
264
  reach. `mdcFrontmatter` was a second construction of frontmatter the writer now derives
799
265
  itself, with a test asserting the copy rather than the behaviour.
800
-
801
266
  - **"Did you mean" offered the shortest fragment first, and a misspelt skill was
802
267
  suggested for a correctly spelled one (S22).** `nearMatches` had no test at all.
803
268
  Both directions of substring scored 0 — the same score as an exact match — so the
@@ -808,7 +273,6 @@ two templates disagreeing with each other in the same position.
808
273
  counts only when it is at least half the query, and the edit bound scales with the
809
274
  query's length — three edits is a typo in `slice-commmit` and is most of a
810
275
  four-character query.
811
-
812
276
  - **The conflict scanner skipped two of the five rule surfaces, and one pointer file
813
277
  (S22).** `rulePaths` enumerated filenames by hand, so `.claude/rules/` and
814
278
  `.agents/rules/` — added to the surface table after the list was written — were
@@ -818,167 +282,24 @@ two templates disagreeing with each other in the same position.
818
282
 
819
283
  ## [0.2.0] — 2026-10-11
820
284
 
821
- ### Publishable from PyPI and Homebrew as well as npm
822
-
823
- simt-dev is a *repository* tool, and the people who adopt a repository are not
824
- all Node developers. Somebody running `simt init` inside a repository with no
825
- `package.json` has no reason to install a package manager, and a Homebrew user
826
- has no reason to install Node to install a tool that needs Node.
285
+ - **Publishable from PyPI and Homebrew as well as npm** — simt-dev is a *repository* tool, and the people who adopt a repository are not all Node developers. — [ADR-058](docs/decisions/ADR-058-one-tool-three-channels-and-the-order-is-forced.md)
827
286
 
828
- So it now publishes to three channels, from one version number:
829
-
830
- ```sh
831
- npm i -D simt-dev # a tarball of .mjs files
832
- pip install simt-dev # a wheel carrying those same bytes
833
- brew install QuazarMedia/tap/simt-dev
834
- ```
835
-
836
- The wheel is not a wrapper around `npm install`. It carries the tool's own
837
- source, and `scripts/verify-wheel.mjs` builds both artefacts and hashes every
838
- file in both — a release fails when a single byte differs between them. That
839
- check exists because the obvious defect in this arrangement is the wheel and the
840
- tarball quietly shipping different code.
841
-
842
- `simt` is the one command, on every channel. Where node comes from is
843
- `SIMT_DEV_NODE`, then the bundled node from `pip install "simt-dev[node]"`,
844
- then `node` on `PATH` — and if none of those resolve you get the remedy rather
845
- than a placeholder path. There are no required dependencies on any channel.
846
- *(ADR-058)*
287
+ ## [0.1.1] — 2026-10-11
847
288
 
848
- The channels are published in a fixed order rather than in parallel, and the
849
- reason is not taste: the Homebrew formula downloads the npm tarball at install
850
- time, so a formula naming a version still sitting in npm's staging area
851
- installs a 404. Both jobs fail with that stated, and are run again after
852
- `npm stage approve`.
289
+ - **A foreign hook on an event this repo does not gate is chained** — `0.1.0` chained the wrong set of hooks and is worth replacing rather than recommending. — [ADR-018](docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md)
853
290
 
854
- ## [0.1.1] — 2026-10-11
291
+ ## [0.1.0] — 2026-10-11
855
292
 
856
- ### A foreign hook on an event this repo does not gate is chained
293
+ - **The command is `simt`** — The package is `simt-dev` and the command is `simt`.
857
294
 
858
- `0.1.0` chained the wrong set of hooks and is worth replacing rather than
859
- recommending. It chained on the event *vocabulary* — anything that was not
860
- `pre-commit` or `pre-push` — when the question worth asking is whether **this
861
- repository** declares a gate for that event.
295
+ - **The manifest is `simt.json`** — The manifest was called `simt-dev.json`; it is now `simt.json`. — [ADR-056](docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md)
862
296
 
863
- The difference is a repository whose `pre-push` is `git-lfs`. `pre-push` is an
864
- event this tool gates in general, so `0.1.0` displaced that hook; and because
865
- that repository declares no `pre-push` gate there was no shim for the event
866
- either, so git would have found no `pre-push` hook at all. LFS objects would have
867
- stopped being uploaded, with nothing to notice it — the exact silent failure
868
- chaining exists to prevent, reached by fixing the wrong half of it.
297
+ - **The region markers are `simt:`** — The generated region was stamped `<!-- simt-dev:begin -->` / `<!-- simt-dev:end -->` — and so was the provenance header inside it; every one of those lines is now `simt:`. — [ADR-056](docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md)
869
298
 
870
- `0.1.1` asks the manifest. An event the repo declares a gate for is left to
871
- `gates.commands`, where the hook runs as a gate and its verdict appears in the
872
- report; every other foreign hook is chained. That also picks up a static
873
- site's Lighthouse pre-push, which declares no gates at all.
874
- *(ADR-018, as amended)*
299
+ - **A hook you already had keeps running** — `hooks install` points `core.hooksPath` at `.simt-hooks/`, and git reads **every** hook from that one directory — not just the two events this tool gates. — [ADR-018](docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md)
875
300
 
876
- Upgrade with `npm i -D simt-dev@0.1.1`, then `simt hooks install --yes`. Any
877
- repository already routed with `0.1.0` should re-run it: the shims it writes
878
- will differ where a gated event was previously displaced.
301
+ - **What a first release has to say out loud** — These are the behaviours a reader is most likely to trip over. — [ADR-010](docs/decisions/ADR-010-runner-invocation.md), [ADR-012](docs/decisions/ADR-012-drop-windows-support.md), [ADR-017](docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md), [ADR-018](docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md), [ADR-021](docs/decisions/ADR-021-one-surface-per-repo.md), [ADR-035](docs/decisions/ADR-035-one-write-decision-and-one-json-document.md), [ADR-036](docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md), [ADR-046](docs/decisions/ADR-046-one-rule-one-copy.md)
879
302
 
880
- ## [0.1.0] — 2026-10-11
303
+ - **Packaging** — - Licence set to Apache-2.0; the `LICENSE` file is the canonical, unmodified text.
881
304
 
882
- The first release with real contents in it. `1.0.0` was an empty reservation of the name
883
- and `0.0.0-stage` was its rehearsal; neither shipped a byte anyone could run, so this entry
884
- is the whole history. The decisions behind it are readable in the package under
885
- `docs/decisions/`, and from any checkout with `simt rules why <id>`.
886
-
887
- ### The command is `simt`
888
-
889
- The package is `simt-dev` and the command is `simt`. They are not the same thing and were
890
- never required to be: the command appears only in text the tool itself writes, and that
891
- text migrates on the next write. The package name, the `.simt-hooks/` directory and the
892
- `simt-dev-check.yml` gate already exist in every repository that adopted this tool, and
893
- they did not move. The manifest and the region markers did — see below.
894
-
895
- ### The manifest is `simt.json`
896
-
897
- The manifest was called `simt-dev.json`; it is now `simt.json`. The move is a
898
- read-both/write-new, so nothing in an adopting repository has to be done:
899
-
900
- - Every read takes `simt.json` if it exists, else `simt-dev.json`, so a repository that
901
- has not written since the upgrade is still seen as adopted.
902
- - Every write goes to `simt.json`, and a lone `simt-dev.json` is moved, not shadowed.
903
- - The old name keeps working until the first `init` or manifest write in each repository,
904
- and that write closes it. *(ADR-056)*
905
-
906
- ### The region markers are `simt:`
907
-
908
- The generated region was stamped `<!-- simt-dev:begin -->` / `<!-- simt-dev:end -->` — and
909
- so was the provenance header inside it; every one of those lines is now `simt:`. The move
910
- is the same read-both/write-new as the manifest:
911
-
912
- - Every read accepts either spelling, so a repository that has not written since the
913
- upgrade is still seen as adopted.
914
- - Every write emits `simt:` only, so the first `sync` in each repository migrates its
915
- regions in place, and that write closes the window.
916
- - A file holding two regions — one of each spelling — is a state only a hand edit can
917
- make, and it is reported malformed rather than merged. *(ADR-056)*
918
-
919
- ### A hook you already had keeps running
920
-
921
- `hooks install` points `core.hooksPath` at `.simt-hooks/`, and git reads **every** hook from
922
- that one directory — not just the two events this tool gates. So adopting a repository that
923
- had its own `post-commit`, `post-checkout` or `post-merge` would have quietly stopped them:
924
- nothing fails, nothing is logged, and the hook is still on disk looking intact.
925
-
926
- Those hooks are now **chained**. Install writes a shim for them that runs the repository's
927
- own hook and forwards git's arguments, so nothing is lost and the reason is in the file. If
928
- the hook is gone, the shim refuses and names the path instead of passing quietly. A hook on
929
- an event this tool *does* gate (`pre-commit`, `pre-push`) is deliberately not chained —
930
- declare it under `gates.commands` so the dispatcher runs it and its verdict appears in the
931
- gate report. *(ADR-018, as amended)*
932
-
933
- ### What a first release has to say out loud
934
-
935
- These are the behaviours a reader is most likely to trip over. Each one is deliberate,
936
- and each has a numbered decision record behind it — but a behaviour nobody can discover
937
- without reading the source is not a documented one.
938
-
939
- - **`sync` and `init` write nothing without `--yes`.** Both print a plan and exit. `sync`
940
- exits **1** when the plan is non-empty, so bare `sync` doubles as a check. Neither ever
941
- commits or pushes. *(ADR-035, ADR-036)*
942
- - **`upgrade` moves a pin and nothing else.** It never adopts a repository, never repairs
943
- one, and never rewrites a manifest. Every consumer moves to the same version as one set.
944
- - **A pre-commit hook with no reachable binary fails closed.** The refusal is the correct
945
- behaviour, not a bug; the remedy it prints is the command that obtains one.
946
- *(ADR-010, ADR-018)*
947
- - **A gate whose tool is not installed is dropped, with a remedy naming the package.**
948
- It is never declared and silently unrunnable. *(ADR-017)*
949
- - **Two rules are never loaded twice.** Each rule projects to exactly one place, chosen by
950
- what the harness actually reads. *(ADR-021, ADR-046)*
951
- - **macOS and Linux only.** Windows `npm i` fails with npm's `EBADPLATFORM`, which npm
952
- will not let a package explain; use WSL2. See the platform section of the README.
953
- *(ADR-012)*
954
-
955
- ### Packaging
956
-
957
- - Licence set to Apache-2.0; the `LICENSE` file is the canonical, unmodified text.
958
- - `files` no longer lists `skills`, `agents`, `tooling` or `hooks`, none of which exist,
959
- and no longer ships `docs/plans`. The tarball is the tool and the decisions behind it,
960
- and nothing else.
961
- - The package `description` no longer names an internal ecosystem. It is the first line
962
- anybody outside the project reads.
963
- - Unscoped, so the install command is `npm i -D simt-dev` rather than a name under an
964
- organisation. The `@simt` and `@quazarmedia` scopes were both free; an unscoped name is
965
- the one a reader can guess.
966
-
967
- ### Notes for anyone upgrading a hook
968
-
969
- A shim installed by an earlier version looks for `SIMT_DEV_BIN`, then for
970
- `node_modules/simt-dev/bin/simt-dev.mjs`, then for a program named `simt` on `PATH`. The
971
- last of those is new, so a machine that installed the package before this release and
972
- never ran `simt hooks install` again will find nothing until the dependency is refreshed
973
- and `simt hooks install --yes` is re-run. That is a real upgrade step and it is called out
974
- here rather than discovered.
975
-
976
- [0.5.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.5.0
977
- [0.4.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.4.0
978
- [0.3.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.3.1
979
- [0.3.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.3.0
980
- [0.2.2]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.2
981
- [0.2.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.1
982
- [0.2.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.0
983
- [0.1.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.1.1
984
- [0.1.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.1.0
305
+ - **Notes for anyone upgrading a hook** — A shim installed by an earlier version looks for `SIMT_DEV_BIN`, then for `node_modules/simt-dev/bin/simt-dev.mjs`, then for a program named `simt` on `PATH`.