simt-dev 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +191 -870
- package/README.md +188 -213
- package/bin/simt.mjs +19 -55
- package/docs/decisions/ADR-014-workspace-is-configuration.md +2 -2
- package/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +1 -1
- package/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +1 -1
- package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +1 -1
- package/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +1 -1
- package/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +1 -1
- package/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +1 -1
- package/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +1 -1
- package/docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md +1 -1
- package/docs/decisions/ADR-099-the-release-verifies-served-bytes-and-the-formula-mirrors-them.md +1 -1
- package/docs/decisions/ADR-117-the-darwin-release-builds-on-supported-macos-15-runners-and-a-re-run-is-idempotent.md +81 -0
- 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
- package/docs/decisions/ADR-119-the-environment-variable-points-into-a-workspace-the-repo-declared.md +90 -0
- package/docs/decisions/ADR-120-a-pre-push-whose-tree-is-already-proven-green-skips-the-test-gate-on-that-evidence.md +96 -0
- package/docs/decisions/ADR-122-the-shims-move-into-the-state-directory-and-the-legacy-directory-removes-itself.md +94 -0
- package/docs/decisions/ADR-123-a-surplus-positional-is-not-refused-because-arity-is-not-in-the-source.md +57 -0
- package/docs/decisions/ADR-124-the-manifest-has-a-schema-in-the-tool-and-its-unknown-keys-stay-the-repositorys.md +64 -0
- package/docs/decisions/ADR-125-a-shared-context-belongs-to-a-read-and-a-writer-reads-directly.md +65 -0
- package/docs/decisions/ADR-126-the-mcp-surface-stays-a-reader-so-there-is-no-write-path-to-build.md +68 -0
- package/docs/decisions/ADR-127-the-mcp-tool-list-is-written-by-hand-and-roots-is-not-asked-for.md +65 -0
- package/docs/decisions/ADR-128-a-research-plan-is-a-plan-and-the-tracker-reads-it.md +42 -0
- package/ecosystems/bun.json +6 -0
- package/ecosystems/cargo.json +25 -0
- package/ecosystems/composer.json +6 -0
- package/ecosystems/dart.json +24 -0
- package/ecosystems/deno.json +21 -0
- package/ecosystems/devcontainers.json +6 -0
- package/ecosystems/docker-compose.json +6 -0
- package/ecosystems/docker.json +6 -0
- package/ecosystems/dotnet.json +25 -0
- package/ecosystems/elixir.json +23 -0
- package/ecosystems/gitsubmodule.json +6 -0
- package/ecosystems/go.json +25 -0
- package/ecosystems/gradle.json +13 -0
- package/ecosystems/maven.json +23 -0
- package/ecosystems/node.json +25 -0
- package/ecosystems/python.json +20 -0
- package/ecosystems/ruby.json +23 -0
- package/ecosystems/swift.json +25 -0
- package/ecosystems/terraform.json +6 -0
- package/ecosystems/uv.json +6 -0
- package/harnesses/antigravity.json +162 -0
- package/harnesses/claude.json +206 -0
- package/harnesses/codex.json +136 -0
- package/harnesses/copilot.json +88 -0
- package/harnesses/cursor.json +140 -0
- package/harnesses/deepseek.json +99 -0
- package/harnesses/gemini.json +122 -0
- package/harnesses/opencode.json +173 -0
- package/package.json +12 -2
- package/rules/core.md +7 -0
- package/rules/mdc/intake-questions.mdc +8 -0
- package/rules/mdc/slice-commit.mdc +57 -4
- package/src/advice/advice.mjs +3 -2
- package/src/agenthooks/prompt.mjs +5 -2
- package/src/agenthooks/render.mjs +86 -180
- package/src/agenthooks/shim.mjs +1 -1
- package/src/agents/render.mjs +15 -18
- package/src/api.mjs +31 -0
- package/src/argv.mjs +48 -3
- package/src/bench/ablate.mjs +3 -4
- package/src/bench/adapters.json +163 -105
- package/src/bench/adapters.mjs +466 -0
- package/src/bench/arms.mjs +333 -0
- package/src/bench/grade.mjs +33 -73
- package/src/bench/graders.mjs +42 -0
- package/src/bench/mine.mjs +121 -475
- package/src/bench/probe-ledger.mjs +149 -0
- package/src/bench/probes.catalog.mjs +918 -0
- package/src/bench/probes.mjs +79 -1064
- package/src/bench/render.mjs +111 -0
- package/src/bench/report.mjs +120 -962
- package/src/bench/run.mjs +185 -802
- package/src/bench/variants.mjs +4 -5
- package/src/bench/workspace.mjs +160 -0
- package/src/brief/assemble.mjs +48 -15
- package/src/ci/dependabot.mjs +25 -55
- package/src/ci/workflow.mjs +30 -7
- package/src/cli.mjs +45 -0
- package/src/commands/agent.mjs +4 -13
- package/src/commands/agenthook.mjs +70 -40
- package/src/commands/bench.mjs +191 -18
- package/src/commands/brief.mjs +5 -4
- package/src/commands/ci.mjs +13 -14
- package/src/commands/compare.mjs +42 -42
- package/src/commands/completions.mjs +2 -6
- package/src/commands/decide.mjs +4 -4
- package/src/commands/distill.mjs +4 -3
- package/src/commands/docs.mjs +88 -0
- package/src/commands/doctor.mjs +11 -7
- package/src/commands/evidence.mjs +6 -9
- package/src/commands/explain.mjs +0 -1
- package/src/commands/gate.mjs +26 -25
- package/src/commands/graph.mjs +2 -3
- package/src/commands/harness.mjs +6 -5
- package/src/commands/help.mjs +168 -19
- package/src/commands/hooks.mjs +119 -49
- package/src/commands/ignore.mjs +13 -8
- package/src/commands/impact.mjs +8 -15
- package/src/commands/index.mjs +318 -193
- package/src/commands/init-scan.mjs +90 -0
- package/src/commands/init.mjs +124 -105
- package/src/commands/lease.mjs +73 -25
- package/src/commands/lessons.mjs +10 -9
- package/src/commands/local.mjs +3 -3
- package/src/commands/map.mjs +24 -14
- package/src/commands/mcp.mjs +2 -18
- package/src/commands/pack.mjs +9 -6
- package/src/commands/plan.mjs +6 -5
- package/src/commands/plugin.mjs +2 -2
- package/src/commands/prbody.mjs +3 -3
- package/src/commands/repoindex.mjs +12 -11
- package/src/commands/review.mjs +2 -2
- package/src/commands/rules.mjs +37 -16
- package/src/commands/shared.mjs +14 -19
- package/src/commands/skills.mjs +33 -32
- package/src/commands/skillscompile.mjs +7 -8
- package/src/commands/skillsinstall.mjs +7 -6
- package/src/commands/slice.mjs +220 -74
- package/src/commands/spec.mjs +167 -0
- package/src/commands/status.mjs +27 -18
- package/src/commands/sync.mjs +27 -25
- package/src/commands/tests.mjs +18 -21
- package/src/commands/tokens.mjs +21 -26
- package/src/commands/undo.mjs +5 -15
- package/src/commands/upgrade.mjs +15 -7
- package/src/commands/why.mjs +15 -21
- package/src/compare/hooksstate.mjs +2 -6
- package/src/compare/learn.mjs +3 -1
- package/src/compare/row.mjs +0 -2
- package/src/context.mjs +64 -0
- package/src/decisions/index-doc.mjs +46 -0
- package/src/decisions/index.mjs +3 -2
- package/src/diff.mjs +1 -1
- package/src/distill/distill.mjs +14 -1
- package/src/distill/engine.mjs +5 -1
- package/src/docs/commands.mjs +65 -0
- package/src/ecosystem/records.mjs +219 -0
- package/src/explain.mjs +31 -0
- package/src/gates/index.mjs +385 -0
- package/src/graph/compile.mjs +8 -4
- package/src/graph/load.mjs +6 -2
- package/src/graph/spec.mjs +10 -8
- package/src/harness/records.mjs +143 -0
- package/src/hooks/capability.mjs +7 -5
- package/src/hooks/dispatcher.mjs +209 -60
- package/src/hooks/gates.mjs +10 -43
- package/src/hooks/resolve.mjs +4 -14
- package/src/hooks/shim.mjs +5 -2
- package/src/hooks/where.mjs +2 -7
- package/src/identity.mjs +31 -12
- package/src/index/coverage.mjs +58 -0
- package/src/index/extract.mjs +6 -16
- package/src/index/gate.mjs +1 -1
- package/src/index/govern.mjs +26 -9
- package/src/index/impact.mjs +4 -8
- package/src/index/map.mjs +14 -15
- package/src/index/open.mjs +173 -0
- package/src/index/persist.mjs +224 -57
- package/src/index/store.mjs +82 -39
- package/src/index/tests.mjs +2 -6
- package/src/journal.mjs +94 -20
- package/src/kernel/card.mjs +37 -63
- package/src/kernel/checks.mjs +4 -10
- package/src/lease.mjs +102 -7
- package/src/ledger/index.mjs +150 -14
- package/src/local/client.mjs +4 -6
- package/src/locate.mjs +33 -9
- package/src/manifest/schema.mjs +213 -0
- package/src/mcp/paginate.mjs +33 -0
- package/src/mcp/resources.mjs +7 -10
- package/src/mcp/schemas.mjs +512 -0
- package/src/mcp/server.mjs +119 -25
- package/src/mcp/tools-facts.mjs +326 -0
- package/src/mcp/tools-read.mjs +221 -0
- package/src/mcp/tools.mjs +4 -967
- package/src/measure/corpus.mjs +150 -0
- package/src/measure/metrics.mjs +236 -0
- package/src/measure/records.mjs +211 -0
- package/src/measure/session.mjs +3 -2
- package/src/measure/stats.mjs +538 -0
- package/src/measure/task-schema.mjs +125 -0
- package/src/measure/tasks.mjs +69 -0
- package/src/memory/decide.mjs +6 -15
- package/src/memory/lesson-text.mjs +61 -0
- package/src/memory/lessons.mjs +29 -82
- package/src/name.mjs +7 -0
- package/src/node-version.mjs +37 -0
- package/src/output/index.mjs +209 -10
- package/src/packs/manage.mjs +32 -12
- package/src/packs.mjs +20 -2
- package/src/plan/index.mjs +1 -1
- package/src/plugins.mjs +22 -16
- package/src/prbody.mjs +7 -11
- package/src/projection/loaders.mjs +7 -692
- package/src/projection/lock.mjs +11 -1
- package/src/projection/manifest.mjs +29 -468
- package/src/projection/mdc.mjs +7 -8
- package/src/projection/project.mjs +126 -221
- package/src/projection/region.mjs +561 -0
- package/src/projection/rules.mjs +20 -550
- package/src/projection/surfaces.mjs +43 -210
- package/src/projection/targets.mjs +5 -5
- package/src/repo-root.mjs +31 -0
- package/src/resolve/eval.mjs +2 -1
- package/src/resolve/fingerprint.mjs +19 -22
- package/src/resolve/resolve.mjs +1 -41
- package/src/review/critic.mjs +10 -13
- package/src/run.mjs +98 -0
- package/src/secrets.mjs +14 -22
- package/src/state/events.mjs +129 -0
- package/src/status-snapshot.mjs +233 -0
- package/src/status.mjs +2 -6
- package/src/sys/git.mjs +82 -0
- package/src/sys/proc.mjs +151 -0
- package/src/testing/parsers.mjs +265 -0
- package/src/testing/patches.mjs +23 -0
- package/src/testing/paths.mjs +49 -0
- package/src/testing/runners.mjs +186 -0
- package/src/testing/vault.mjs +68 -0
- package/src/text/frontmatter.mjs +49 -0
- package/src/text/tokens.mjs +52 -0
- package/src/toolwords.mjs +3 -0
- package/src/util/fs.mjs +20 -0
- package/src/util/sort.mjs +12 -0
- package/src/workspace.mjs +58 -11
- package/src/write/plan.mjs +82 -0
- package/templates/plan/tracker.json +1 -0
- package/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +0 -82
- package/docs/decisions/ADR-102-the-two-queued-review-rows-are-closed-by-ruling.md +0 -49
- package/docs/decisions/README.md +0 -600
- package/src/bench/tasks.mjs +0 -36
package/docs/decisions/ADR-125-a-shared-context-belongs-to-a-read-and-a-writer-reads-directly.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["src/context.mjs", "src/commands/hooks.mjs", "test/hooks-context.test.mjs"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-125 — a shared context belongs to a read, and a writer reads directly
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-31
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
`repoContext` holds one pending promise per question for the length of a command. It
|
|
13
|
+
was threaded through the reads — `check`, the four skills reads, `diagnose`, the rules
|
|
14
|
+
catalogue — and Q67's last clause was to finish the job, which raised the question the
|
|
15
|
+
row did not answer: should a command that *writes* hold a context too?
|
|
16
|
+
|
|
17
|
+
The two cases are not the same. A reader asks several questions about an unchanging
|
|
18
|
+
tree and pays once. A writer changes the tree between its questions: `init` writes a
|
|
19
|
+
manifest and then renders against it, `upgrade` replaces files and then reports drift,
|
|
20
|
+
`sync` rewrites the region and then computes the lock. A context that survived those
|
|
21
|
+
writes would answer the second question with the first answer, and the command would
|
|
22
|
+
report a repository it had already changed.
|
|
23
|
+
|
|
24
|
+
## Ruling
|
|
25
|
+
|
|
26
|
+
### 1. A read command may hold one context for its whole run
|
|
27
|
+
|
|
28
|
+
A command that only reads creates one `repoContext` at its entry and passes it to
|
|
29
|
+
every reader beneath it. Two questions with the same answer cost one computation, and
|
|
30
|
+
the cache reports its own hits so a context that is wired but never used cannot pass
|
|
31
|
+
for a fast machine.
|
|
32
|
+
|
|
33
|
+
### 2. A writer reads its repository directly
|
|
34
|
+
|
|
35
|
+
A writer calls the readers as it always has. It is not an optimisation left undone: a
|
|
36
|
+
shared context would make a stale answer the reward for a correct write, and the
|
|
37
|
+
failure would surface as a drifted manifest in a repository that had just been fixed.
|
|
38
|
+
|
|
39
|
+
### 3. The hook path keeps its dynamic import
|
|
40
|
+
|
|
41
|
+
`planHooksInstall` resolves the node runner from a fingerprint, and it imports the
|
|
42
|
+
fingerprint module dynamically so that loading the hooks command does not load it.
|
|
43
|
+
Static-importing a context into `src/commands/hooks.mjs` costs about 3 ms of module
|
|
44
|
+
graph on the shim path, which Q64 measured and removed once. A caller that holds a
|
|
45
|
+
context passes it and shares the walk; a caller that does not keeps the light path.
|
|
46
|
+
|
|
47
|
+
## Consequences
|
|
48
|
+
|
|
49
|
+
- Q67 closes with the reads threaded and the writers explained rather than converted.
|
|
50
|
+
- The 3 ms module cost is paid only when a command actually uses a context; `hooks
|
|
51
|
+
run` keeps the dynamic path.
|
|
52
|
+
- A future writer that wants a context has this record to argue against, instead of
|
|
53
|
+
discovering the staleness as a bug report.
|
|
54
|
+
|
|
55
|
+
## Rejected
|
|
56
|
+
|
|
57
|
+
- **Giving every writer a context and invalidating entries after each write.** The
|
|
58
|
+
invalidation is the whole bug surface: forget one entry and the failure is a stale
|
|
59
|
+
answer that looks like a correct one, in the command whose job is to make the tree
|
|
60
|
+
correct.
|
|
61
|
+
- **A process-lifetime context.** `bench` writes manifests mid-run; a process-wide
|
|
62
|
+
memo would answer a benchmark for a repository it had just changed.
|
|
63
|
+
- **Making the context required on every reader signature.** It would put the cache in
|
|
64
|
+
the contract of functions that do not need one, and a caller with no context would
|
|
65
|
+
have to invent an empty one.
|
package/docs/decisions/ADR-126-the-mcp-surface-stays-a-reader-so-there-is-no-write-path-to-build.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["src/mcp/server.mjs", "src/mcp/tools.mjs", "docs/reviews/0003-pre-plan-0004-review.md"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-126 — the MCP surface stays a reader, so there is no write path to build
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-11-01
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
Review 0003's Q66 lists "the two-step write path" among the clauses still open for the
|
|
13
|
+
MCP surface. The clause reads as work to do; the file says otherwise. `src/mcp/server.mjs`
|
|
14
|
+
opens with three decisions, the third of which is that no mutating tool is exposed —
|
|
15
|
+
`init`, `sync`, `hooks install`, `skills install` and `upgrade` are absent on purpose,
|
|
16
|
+
because an editor that can call a tool can call it without a human reading a diff first.
|
|
17
|
+
|
|
18
|
+
Those two statements have stood in the same repository for months without meeting. The
|
|
19
|
+
question the row asks and the rule the file states are the same question, and the answer
|
|
20
|
+
was written down where the row's reader would not look.
|
|
21
|
+
|
|
22
|
+
The argument for exposure is real and worth stating: a client that can read a repository
|
|
23
|
+
can also want to fix it, and a plan-then-apply pair would let it see what would change
|
|
24
|
+
before changing anything. What it would not do is make the change reviewed. `--yes` on
|
|
25
|
+
the command line is a human decision with a preview in front of it; the same flag arriving
|
|
26
|
+
inside a tool call is a model deciding to pass it.
|
|
27
|
+
|
|
28
|
+
## Ruling
|
|
29
|
+
|
|
30
|
+
### 1. No tool on this surface writes
|
|
31
|
+
|
|
32
|
+
Every tool returns a value and changes nothing. The capability block advertises no write
|
|
33
|
+
capability, and this is not a gap to be filled later: a tool that writes is a different
|
|
34
|
+
surface with a different review obligation, and it is not this one.
|
|
35
|
+
|
|
36
|
+
### 2. A caller that wants to write runs the command
|
|
37
|
+
|
|
38
|
+
The commands already do plan-then-apply properly: they print a preview, refuse without
|
|
39
|
+
`--yes`, journal what they write, and `undo` restores it. A client that wants that power
|
|
40
|
+
runs `simt <command>` as a subprocess, where the human who opened the editor sees the
|
|
41
|
+
preview and passes the flag — or does not.
|
|
42
|
+
|
|
43
|
+
### 3. Exposing a writer needs a superseding record, not a patch
|
|
44
|
+
|
|
45
|
+
If a future surface must write, it arrives with its own decision: what the plan token
|
|
46
|
+
proves, who approves the apply, how a half-applied run is undone, and what the journal
|
|
47
|
+
says. Approving a diff in an editor is a claim the server cannot verify, and a
|
|
48
|
+
confirmation prompt is a user interface the server does not own.
|
|
49
|
+
|
|
50
|
+
## Consequences
|
|
51
|
+
|
|
52
|
+
- Q66's write-path clause closes as withdrawn, not deferred. The row names this record so
|
|
53
|
+
the next reader does not reopen it as unfinished work.
|
|
54
|
+
- The read-only guarantee in the server's header, the capability block and the tests now
|
|
55
|
+
have one record behind them instead of three comments.
|
|
56
|
+
- A tool that returns `{ written: false }` for every input is not a compromise: it is the
|
|
57
|
+
shape that makes a client believe a write happened.
|
|
58
|
+
|
|
59
|
+
## Rejected
|
|
60
|
+
|
|
61
|
+
- **A plan/apply pair behind a confirmation prompt.** The prompt is rendered by the client,
|
|
62
|
+
which may not render one, and the server cannot tell an approved apply from an unapproved
|
|
63
|
+
one. A safety property that depends on another program's UI is not a property.
|
|
64
|
+
- **A write tool gated on `--yes` passed through the arguments.** The flag would be asserted
|
|
65
|
+
by the same model that decided to write; the preview would be read by nobody.
|
|
66
|
+
- **Exposing `sync` alone because it is idempotent.** It rewrites the generated region of
|
|
67
|
+
every repository in a workspace. Idempotence says a second run changes nothing, not that
|
|
68
|
+
the first run was reviewed.
|
package/docs/decisions/ADR-127-the-mcp-tool-list-is-written-by-hand-and-roots-is-not-asked-for.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["src/mcp/tools.mjs", "src/commands/spec.mjs", "test/mcp-spec.test.mjs", "docs/reviews/0003-pre-plan-0004-review.md"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-127 — the MCP tool list is written by hand and held to the spec, and roots is not asked for
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-11-01
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
Two clauses of review 0003's Q66 read as work still to do: "`tools/list` generated from
|
|
13
|
+
spec rows with `mcp` set", and "`roots`". Both were written before the spec rows existed,
|
|
14
|
+
when the four lists this review found disagreeing were the live problem and generation
|
|
15
|
+
looked like the answer to all of them.
|
|
16
|
+
|
|
17
|
+
The spec rows now exist, and they carry `path`, `summary`, `flags` and, for the commands
|
|
18
|
+
the surface exposes, `mcp: {name, flags}`. That is enough to say *which* flags a tool
|
|
19
|
+
exposes and not enough to say what they are: no types, no per-argument descriptions, and
|
|
20
|
+
no vocabulary shared with the transport. `check` declares no `mcp` flags and its tool
|
|
21
|
+
takes `root`; `compare` declares none and its tool takes `root`, `limit`, `cursor` and
|
|
22
|
+
`concurrency`. An `inputSchema` derived from the rows would describe `--dry-run` as a
|
|
23
|
+
property and omit `root`, which is a contract a client cannot call.
|
|
24
|
+
|
|
25
|
+
`roots` is the other direction. It is a client capability: the server sends `roots/list`
|
|
26
|
+
and the client answers with the directories it may touch. `src/mcp/server.mjs` answers
|
|
27
|
+
requests and never sends them — the only thing it writes unprompted is a notification for
|
|
28
|
+
a progress token the client supplied. A server that asks a question needs a correlation
|
|
29
|
+
table for replies, a timeout for a client that never answers, and a fallback for the
|
|
30
|
+
clients that do not implement the capability; and every tool here already takes its root
|
|
31
|
+
as an argument (`root`, `workspace`, or `--paths`), which is the same information stated
|
|
32
|
+
once, by the caller, where it can be read in the call rather than inferred from a
|
|
33
|
+
handshake.
|
|
34
|
+
|
|
35
|
+
## Ruling
|
|
36
|
+
|
|
37
|
+
### 1. The tool list stays written by hand, and tests hold it to the spec
|
|
38
|
+
|
|
39
|
+
The spec row is the source of the *command*: its path, its summary and its flags. It is
|
|
40
|
+
not the source of the transport's schema, which carries descriptions, types and defaults
|
|
41
|
+
the row does not have, and arguments the command line does not spell as flags.
|
|
42
|
+
|
|
43
|
+
What generation was meant to prevent — a tool with no command, a command nobody can reach
|
|
44
|
+
over the transport, an advertised flag the tool refuses — is enforced in
|
|
45
|
+
`test/mcp-spec.test.mjs`, in all three directions, plus `MCP_ONLY` for the one tool that is
|
|
46
|
+
deliberately not a command (`federation_query`, which names its reason). A hand-written
|
|
47
|
+
list under a test that fails on every kind of drift is the same guarantee as a generated
|
|
48
|
+
one, without a schema nobody would trust enough to keep descriptions in.
|
|
49
|
+
|
|
50
|
+
### 2. `roots` is withdrawn, not deferred
|
|
51
|
+
|
|
52
|
+
No tool is changed to accept an implicit root, and the server gains no request path. A
|
|
53
|
+
client that wants a tool to run against a directory passes it as the argument the schema
|
|
54
|
+
already names. If a future capability genuinely needs an answer from the client, that is
|
|
55
|
+
a different surface with its own correlation and timeout design, and it needs its own
|
|
56
|
+
record.
|
|
57
|
+
|
|
58
|
+
## Consequences
|
|
59
|
+
|
|
60
|
+
- Reading `mcp: {name, flags}` tells a reader which flags reach the transport; reading
|
|
61
|
+
`src/mcp/tools.mjs` tells them what a caller sees. Both are checked against each other.
|
|
62
|
+
- Adding a second MCP-only tool means a `MCP_ONLY` entry with a reason, which is a place a
|
|
63
|
+
reviewer looks, rather than a hole in the table.
|
|
64
|
+
- The 2 KB `tools/list` budget keeps being spent on descriptions a reader can act on,
|
|
65
|
+
because nothing regenerates them from a summary phrase.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["templates/plan/tracker.json", "src/plan/index.mjs", "src/projection/manifest.mjs", "test/rules.test.mjs", "test/plan.test.mjs"]
|
|
3
|
+
simt-supersedes: ["ADR-082"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-128 — A research plan is a plan, and the tracker reads it
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-11-01 · supersedes ADR-082 ruling 1
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
The four founding plans left `docs/plans` for `docs/research`: completed work of a
|
|
13
|
+
different kind — research — but plans nonetheless, with tracker tables, slice ids and wave
|
|
14
|
+
dependencies. The tracker reads only the directories its template lists, so the move
|
|
15
|
+
de-indexed all four at once. A wave dependency that resolved through them broke with it:
|
|
16
|
+
`R01` in the research plan 0003 depends on `review 0002 Wave 0`, and the slices it names
|
|
17
|
+
live in the de-indexed documents.
|
|
18
|
+
|
|
19
|
+
The directory set was never code. ADR-081 and ADR-082 settled the mechanism — a template
|
|
20
|
+
lists any number of directories, each with a `kind`, a `word` and a `path`, and the parser,
|
|
21
|
+
the help text and `plan status` all read it — and ADR-082 ruling 1 fixed the *delivered*
|
|
22
|
+
set at two. Fixing the delivered set is a ruling, and this is the new one.
|
|
23
|
+
|
|
24
|
+
## Ruling
|
|
25
|
+
|
|
26
|
+
1. **The delivered template lists `docs/research` as a third directory, kind `research`,
|
|
27
|
+
word `research`.** A repository that sets nothing gets all three; a repository that
|
|
28
|
+
overrides `plans` replaces the `directories` section wholesale, exactly as before, so no
|
|
29
|
+
existing override is disturbed.
|
|
30
|
+
2. **The directory is data, not a special case.** Nothing in `src/` names `research`; the
|
|
31
|
+
parser, help and status keep reading the template. A fourth directory arrives the same
|
|
32
|
+
way: one entry in the file, one record here.
|
|
33
|
+
|
|
34
|
+
## Consequences
|
|
35
|
+
|
|
36
|
+
- `simt plan status` names three directories, and the research plans are indexed again:
|
|
37
|
+
their slices count, and a dependency may name one as `research 0003 R01`.
|
|
38
|
+
- Template order is index order. `research` sits between `plans` and `reviews`, which keeps
|
|
39
|
+
the moved documents ahead of the reviews they depend on — the same relative order they
|
|
40
|
+
had while they were in `docs/plans`.
|
|
41
|
+
- The tests that walk the tracker's documents (`test/rules.test.mjs`) read the template's
|
|
42
|
+
directories instead of a hardcoded pair, so a future entry reaches them without an edit.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cargo",
|
|
3
|
+
"facts": ["has-cargo-toml"],
|
|
4
|
+
"tool": "cargo",
|
|
5
|
+
"manifestOrder": 1,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format", "lint"],
|
|
8
|
+
"prePush": ["test"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "cargo fmt --check",
|
|
11
|
+
"lint": "cargo clippy -- -D warnings",
|
|
12
|
+
"test": "cargo test"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"card": {
|
|
16
|
+
"build": { "order": 1, "condition": { "fact": "has-cargo-toml" }, "command": "cargo build" },
|
|
17
|
+
"focused": [
|
|
18
|
+
{ "order": 5, "kind": "Rust", "condition": { "fact": "has-cargo-toml" }, "template": "cargo test {name}" }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"dependabot": [
|
|
22
|
+
{ "order": 6, "fact": "has-cargo-toml", "value": "cargo", "manifest": "Cargo.toml" }
|
|
23
|
+
],
|
|
24
|
+
"languages": [{ "lang": "rust", "exts": [".rs"] }]
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dart",
|
|
3
|
+
"facts": ["has-pubspec"],
|
|
4
|
+
"tool": "dart",
|
|
5
|
+
"manifestOrder": 7,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format", "lint"],
|
|
8
|
+
"prePush": ["test"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "dart format --output=none --set-exit-if-changed .",
|
|
11
|
+
"lint": "dart analyze",
|
|
12
|
+
"test": "dart test"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"card": {
|
|
16
|
+
"focused": [
|
|
17
|
+
{ "order": 12, "kind": "Dart", "condition": { "fact": "has-pubspec" }, "template": "dart test {file}" }
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"dependabot": [
|
|
21
|
+
{ "order": 16, "fact": "has-pubspec", "value": "pub", "manifest": "pubspec.yaml" }
|
|
22
|
+
],
|
|
23
|
+
"languages": [{ "lang": "dart", "exts": [".dart"] }]
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "deno",
|
|
3
|
+
"facts": ["has-deno-config"],
|
|
4
|
+
"tool": "deno",
|
|
5
|
+
"manifestOrder": 5,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format", "lint"],
|
|
8
|
+
"prePush": ["test", "typecheck"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "deno fmt --check",
|
|
11
|
+
"lint": "deno lint",
|
|
12
|
+
"test": "deno test",
|
|
13
|
+
"typecheck": "deno check ."
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"card": {
|
|
17
|
+
"focused": [
|
|
18
|
+
{ "order": 10, "kind": "Deno", "condition": { "fact": "has-deno-config" }, "template": "deno test {file}" }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dotnet",
|
|
3
|
+
"facts": ["has-nuget"],
|
|
4
|
+
"tool": "dotnet",
|
|
5
|
+
"manifestOrder": 3,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format"],
|
|
8
|
+
"prePush": ["test", "typecheck"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "dotnet format --verify-no-changes",
|
|
11
|
+
"test": "dotnet test",
|
|
12
|
+
"typecheck": "dotnet build -warnaserror"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"card": {
|
|
16
|
+
"build": { "order": 3, "condition": { "fact": "has-nuget" }, "command": "dotnet build" },
|
|
17
|
+
"focused": [
|
|
18
|
+
{ "order": 8, "kind": ".NET", "condition": { "fact": "has-nuget" }, "template": "dotnet test --filter {test}" }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"dependabot": [
|
|
22
|
+
{ "order": 15, "fact": "has-nuget", "value": "nuget", "manifest": "*.csproj" }
|
|
23
|
+
],
|
|
24
|
+
"languages": [{ "lang": "csharp", "exts": [".cs"] }]
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "elixir",
|
|
3
|
+
"facts": ["has-mix"],
|
|
4
|
+
"tool": "mix",
|
|
5
|
+
"manifestOrder": 8,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format"],
|
|
8
|
+
"prePush": ["test"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "mix format --check-formatted",
|
|
11
|
+
"test": "mix test"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"card": {
|
|
15
|
+
"build": { "order": 5, "condition": { "fact": "has-mix" }, "command": "mix compile" },
|
|
16
|
+
"focused": [
|
|
17
|
+
{ "order": 13, "kind": "Elixir", "condition": { "fact": "has-mix" }, "template": "mix test {file}" }
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"dependabot": [
|
|
21
|
+
{ "order": 18, "fact": "has-mix", "value": "mix", "manifest": "mix.exs" }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "go",
|
|
3
|
+
"facts": ["has-go-mod"],
|
|
4
|
+
"tool": "go",
|
|
5
|
+
"manifestOrder": 2,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format", "lint"],
|
|
8
|
+
"prePush": ["test"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "test -z \"$(gofmt -l .)\"",
|
|
11
|
+
"lint": "go vet ./...",
|
|
12
|
+
"test": "go test ./..."
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"card": {
|
|
16
|
+
"build": { "order": 2, "condition": { "fact": "has-go-mod" }, "command": "go build ./..." },
|
|
17
|
+
"focused": [
|
|
18
|
+
{ "order": 4, "kind": "Go package", "condition": { "fact": "has-go-mod" }, "template": "go test ./{pkg} -run {Test}" }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"dependabot": [
|
|
22
|
+
{ "order": 5, "fact": "has-go-mod", "value": "gomod", "manifest": "go.mod" }
|
|
23
|
+
],
|
|
24
|
+
"languages": [{ "lang": "go", "exts": [".go"] }]
|
|
25
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "gradle",
|
|
3
|
+
"card": {
|
|
4
|
+
"build": { "order": 6, "condition": { "stack": "gradle" }, "command": "./gradlew build" },
|
|
5
|
+
"focused": [
|
|
6
|
+
{ "order": 1, "kind": "Android", "condition": { "fact": "has-android-manifest" }, "template": "./gradlew :{module}:testDebugUnitTest --tests {class}" },
|
|
7
|
+
{ "order": 2, "kind": "JVM", "condition": { "stack": "gradle" }, "template": "./gradlew test --tests {class}" }
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"dependabot": [
|
|
11
|
+
{ "order": 13, "stack": "gradle", "value": "gradle", "manifest": "build.gradle" }
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "maven",
|
|
3
|
+
"facts": ["has-maven-pom"],
|
|
4
|
+
"tool": "mvnw",
|
|
5
|
+
"file": true,
|
|
6
|
+
"manifestOrder": 9,
|
|
7
|
+
"gates": {
|
|
8
|
+
"prePush": ["test"],
|
|
9
|
+
"commands": { "test": "./mvnw -q verify" }
|
|
10
|
+
},
|
|
11
|
+
"card": {
|
|
12
|
+
"focused": [
|
|
13
|
+
{ "order": 3, "kind": "Maven", "condition": { "fact": "has-maven-pom" }, "template": "./mvnw test -Dtest={class}" }
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"dependabot": [
|
|
17
|
+
{ "order": 9, "fact": "has-maven-pom", "value": "maven", "manifest": "pom.xml" }
|
|
18
|
+
],
|
|
19
|
+
"languages": [
|
|
20
|
+
{ "lang": "java", "exts": [".java"], "testConventions": { "base": ["Tests?\\.(?:java|kt)$"] } },
|
|
21
|
+
{ "lang": "kotlin", "exts": [".kt", ".kts"], "testConventions": { "base": ["Tests?\\.(?:java|kt)$"] } }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "node",
|
|
3
|
+
"card": {
|
|
4
|
+
"focused": [
|
|
5
|
+
{
|
|
6
|
+
"order": 14,
|
|
7
|
+
"kind": "Node",
|
|
8
|
+
"condition": { "stack": "node" },
|
|
9
|
+
"template": {
|
|
10
|
+
"static": "node --test {file}",
|
|
11
|
+
"ifScriptMatches": "vitest|jest",
|
|
12
|
+
"dynamic": "{runner} test -- {file}"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"dependabot": [
|
|
18
|
+
{ "order": 1, "stack": "node", "value": "npm", "manifest": "package.json" },
|
|
19
|
+
{ "order": 2, "stack": "typescript", "value": "npm", "manifest": "package.json" }
|
|
20
|
+
],
|
|
21
|
+
"languages": [
|
|
22
|
+
{ "lang": "javascript", "exts": [".js", ".mjs", ".cjs", ".jsx", ".ts", ".tsx"], "canonical": [".js", ".mjs", ".cjs", ".jsx"] },
|
|
23
|
+
{ "lang": "typescript", "exts": [".ts", ".tsx", ".js", ".mjs", ".jsx"], "canonical": [".ts", ".tsx"] }
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "python",
|
|
3
|
+
"card": {
|
|
4
|
+
"focused": [
|
|
5
|
+
{ "order": 6, "kind": "Python", "condition": { "fact": "has-uv-lock" }, "template": "uv run -m pytest {file}::{test}" },
|
|
6
|
+
{ "order": 7, "kind": "Python", "condition": { "facts": ["has-pyproject", "has-requirements"] }, "template": "pytest {file}::{test}" }
|
|
7
|
+
],
|
|
8
|
+
"fallback": [
|
|
9
|
+
{ "order": 1, "condition": { "fact": "has-uv-lock" }, "commands": { "test": "uv run -m pytest" } },
|
|
10
|
+
{ "order": 2, "condition": { "facts": ["has-pyproject", "has-requirements"] }, "commands": { "test": "pytest" } }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"dependabot": [
|
|
14
|
+
{ "order": 3, "fact": "has-pyproject", "value": "pip", "manifest": "pyproject.toml" },
|
|
15
|
+
{ "order": 4, "fact": "has-requirements", "value": "pip", "manifest": "requirements.txt" }
|
|
16
|
+
],
|
|
17
|
+
"languages": [
|
|
18
|
+
{ "lang": "python", "exts": [".py"], "testConventions": { "base": ["^test_[^/]+\\.py$"] } }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "ruby",
|
|
3
|
+
"facts": ["has-gemfile"],
|
|
4
|
+
"tool": "bundle",
|
|
5
|
+
"manifestOrder": 6,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format"],
|
|
8
|
+
"prePush": ["test"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "bundle exec rubocop",
|
|
11
|
+
"test": "bundle exec rspec"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"card": {
|
|
15
|
+
"focused": [
|
|
16
|
+
{ "order": 11, "kind": "Ruby", "condition": { "fact": "has-gemfile" }, "template": "bundle exec rspec {file}" }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"dependabot": [
|
|
20
|
+
{ "order": 7, "fact": "has-gemfile", "value": "bundler", "manifest": "Gemfile" }
|
|
21
|
+
],
|
|
22
|
+
"languages": [{ "lang": "ruby", "exts": [".rb"] }]
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "swift",
|
|
3
|
+
"facts": ["has-swift-package"],
|
|
4
|
+
"tool": "swift",
|
|
5
|
+
"manifestOrder": 4,
|
|
6
|
+
"gates": {
|
|
7
|
+
"preCommit": ["format"],
|
|
8
|
+
"prePush": ["test", "typecheck"],
|
|
9
|
+
"commands": {
|
|
10
|
+
"format": "swift format lint -r .",
|
|
11
|
+
"test": "swift test",
|
|
12
|
+
"typecheck": "swift build"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"card": {
|
|
16
|
+
"build": { "order": 4, "condition": { "fact": "has-swift-package" }, "command": "swift build" },
|
|
17
|
+
"focused": [
|
|
18
|
+
{ "order": 9, "kind": "Swift", "condition": { "fact": "has-swift-package" }, "template": "swift test --filter {test}" }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"dependabot": [
|
|
22
|
+
{ "order": 17, "fact": "has-swift-package", "value": "swift", "manifest": "Package.swift" }
|
|
23
|
+
],
|
|
24
|
+
"languages": [{ "lang": "swift", "exts": [".swift"] }]
|
|
25
|
+
}
|