simt-dev 0.2.2 → 0.3.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.
- package/CHANGELOG.md +100 -0
- package/README.md +129 -75
- package/bin/simt-dev.mjs +21 -2
- package/docs/decisions/ADR-073-a-kernel-is-a-smaller-projection-the-repository-asks-for.md +1 -1
- package/docs/decisions/ADR-074-a-rule-and-a-decision-are-readable-by-uri.md +99 -0
- package/docs/decisions/ADR-075-nothing-leaves-the-machine.md +56 -0
- package/docs/decisions/ADR-076-a-change-to-what-agents-read-is-measured.md +70 -0
- package/docs/decisions/ADR-077-the-command-card-leads-the-region-and-reads-committed-evidence.md +91 -0
- package/docs/decisions/ADR-078-a-line-the-repository-already-proves-does-not-ship.md +117 -0
- package/docs/decisions/ADR-079-the-rules-are-enforced-at-the-tool-call-from-one-policy-table.md +162 -0
- package/docs/decisions/ADR-080-the-tools-own-names-are-not-repositories-and-neither-reader-decides-by-shape.md +99 -0
- package/docs/decisions/ADR-081-a-tracker-is-read-through-the-repositorys-template.md +36 -0
- package/docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md +35 -0
- package/docs/decisions/ADR-083-a-budget-is-met-by-cutting-the-kernel-and-hard-is-the-floor.md +153 -0
- package/docs/decisions/ADR-084-a-workspace-carries-its-repeated-rules-once.md +100 -0
- package/docs/decisions/ADR-085-the-region-ends-with-the-names-it-kept-out.md +72 -0
- package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md +69 -0
- package/docs/decisions/ADR-087-a-run-is-evidence-for-one-tree.md +67 -0
- package/docs/decisions/ADR-088-a-marked-line-is-enforced-by-a-checker.md +59 -0
- package/docs/decisions/ADR-089-the-local-index-is-a-cache-of-git-blobs.md +52 -0
- package/docs/decisions/ADR-090-an-optional-adapter-is-off-until-named.md +55 -0
- package/docs/decisions/ADR-091-the-map-ranks-files-a-task-mentions.md +60 -0
- package/docs/decisions/ADR-092-tests-are-imports-conventions-and-coverage.md +60 -0
- package/docs/decisions/ADR-093-why-names-what-governs-a-file.md +58 -0
- package/docs/decisions/ADR-094-a-change-runs-the-tests-that-import-it.md +44 -0
- package/docs/decisions/ADR-095-gate-output-keeps-the-failures.md +31 -0
- package/docs/decisions/ADR-096-impact-names-who-a-change-reaches.md +38 -0
- package/package.json +2 -1
- package/rules/core.md +1 -1
- package/src/agenthooks/decide.mjs +123 -0
- package/src/agenthooks/policy.mjs +78 -0
- package/src/agenthooks/render.mjs +240 -0
- package/src/argv.mjs +54 -1
- package/src/bench/ablate.mjs +630 -0
- package/src/bench/adapters.json +149 -0
- package/src/bench/grade.mjs +229 -0
- package/src/bench/mine.mjs +811 -0
- package/src/bench/probes.mjs +1350 -0
- package/src/bench/report.mjs +899 -0
- package/src/bench/run.mjs +1002 -0
- package/src/ci/workflow.mjs +11 -1
- package/src/commands/agenthook.mjs +282 -0
- package/src/commands/bench.mjs +371 -0
- package/src/commands/evidence.mjs +96 -0
- package/src/commands/gate.mjs +165 -0
- package/src/commands/harness.mjs +156 -5
- package/src/commands/help.mjs +89 -1
- package/src/commands/hooks.mjs +45 -5
- package/src/commands/impact.mjs +205 -0
- package/src/commands/index.mjs +60 -1
- package/src/commands/map.mjs +91 -0
- package/src/commands/plan.mjs +99 -0
- package/src/commands/repoindex.mjs +52 -0
- package/src/commands/shared.mjs +398 -0
- package/src/commands/sync.mjs +72 -3
- package/src/commands/tests.mjs +192 -0
- package/src/commands/tokens.mjs +61 -1
- package/src/commands/why.mjs +177 -0
- package/src/decisions/index.mjs +23 -5
- package/src/graph/spec.mjs +1 -1
- package/src/hooks/dispatcher.mjs +15 -0
- package/src/hooks/shim.mjs +31 -0
- package/src/index/compact.mjs +228 -0
- package/src/index/extract.mjs +331 -0
- package/src/index/gate.mjs +101 -0
- package/src/index/govern.mjs +222 -0
- package/src/index/impact.mjs +186 -0
- package/src/index/map.mjs +393 -0
- package/src/index/persist.mjs +172 -0
- package/src/index/store.mjs +301 -0
- package/src/index/tests.mjs +294 -0
- package/src/kernel/budget.mjs +100 -0
- package/src/kernel/card.mjs +254 -0
- package/src/kernel/checks.mjs +378 -0
- package/src/kernel/inferability.mjs +188 -0
- package/src/ledger/index.mjs +112 -0
- package/src/mcp/resources.mjs +157 -0
- package/src/mcp/server.mjs +37 -6
- package/src/mcp/tools.mjs +84 -0
- package/src/measure/context.mjs +184 -11
- package/src/measure/loaderView.mjs +435 -0
- package/src/measure/session.mjs +267 -0
- package/src/plan/index.mjs +650 -0
- package/src/plan/template.mjs +331 -0
- package/src/projection/disclosure.mjs +92 -0
- package/src/projection/loaders.mjs +689 -0
- package/src/projection/lock.mjs +93 -0
- package/src/projection/manifest.mjs +78 -3
- package/src/projection/markers.mjs +56 -17
- package/src/projection/project.mjs +24 -17
- package/src/projection/rules.mjs +452 -43
- package/src/projection/shared.mjs +428 -0
- package/src/projection/surfaces.mjs +22 -8
- package/src/projection/targets.mjs +2 -1
- package/src/toolwords.mjs +71 -0
- package/templates/plan/tracker.json +36 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,104 @@ All notable changes to this package are recorded here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.1] — 2026-10-18
|
|
10
|
+
|
|
11
|
+
### Who a change reaches
|
|
12
|
+
|
|
13
|
+
`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).
|
|
14
|
+
|
|
15
|
+
### A gate report keeps the failures and counts the passes
|
|
16
|
+
|
|
17
|
+
`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).
|
|
18
|
+
|
|
19
|
+
### A change runs the tests that import it
|
|
20
|
+
|
|
21
|
+
`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).
|
|
22
|
+
|
|
23
|
+
### what governs a file is the decision, rule, skill, gate or lesson that names it
|
|
24
|
+
|
|
25
|
+
`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).
|
|
26
|
+
|
|
27
|
+
### The tests of a file are its importers, its conventional neighbour, and its coverage
|
|
28
|
+
|
|
29
|
+
`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).
|
|
30
|
+
|
|
31
|
+
### The map ranks the files a task is likely to touch
|
|
32
|
+
|
|
33
|
+
`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).
|
|
34
|
+
|
|
35
|
+
### The index names the exports and the imports
|
|
36
|
+
|
|
37
|
+
`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).
|
|
38
|
+
|
|
39
|
+
### The local index is a cache of git blobs
|
|
40
|
+
|
|
41
|
+
`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).
|
|
42
|
+
|
|
43
|
+
### A marked line is enforced by a checker
|
|
44
|
+
|
|
45
|
+
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).
|
|
46
|
+
|
|
47
|
+
### A run is evidence for one tree
|
|
48
|
+
|
|
49
|
+
`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).
|
|
50
|
+
|
|
51
|
+
### A workspace carries its repeated rules once
|
|
52
|
+
|
|
53
|
+
`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).
|
|
54
|
+
|
|
55
|
+
### The region ends with the names of what it kept out
|
|
56
|
+
|
|
57
|
+
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).
|
|
58
|
+
|
|
59
|
+
### The stable prefix comes first
|
|
60
|
+
|
|
61
|
+
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).
|
|
62
|
+
|
|
63
|
+
### Kernel mode packs to the budget the repository declared
|
|
64
|
+
|
|
65
|
+
`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).
|
|
66
|
+
|
|
67
|
+
### Vulnerability reports
|
|
68
|
+
|
|
69
|
+
`SECURITY.md` names the two places a report goes: a GitHub issue on this repository, or `security@simt.space`.
|
|
70
|
+
|
|
71
|
+
## [0.3.0] — 2026-10-17
|
|
72
|
+
|
|
73
|
+
### The rules and the decisions are readable as MCP resources
|
|
74
|
+
|
|
75
|
+
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.
|
|
76
|
+
|
|
77
|
+
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).
|
|
78
|
+
|
|
79
|
+
### A plan is resumed one slice at a time
|
|
80
|
+
|
|
81
|
+
`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.
|
|
82
|
+
|
|
83
|
+
### The rules are enforced at the tool call
|
|
84
|
+
|
|
85
|
+
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).
|
|
86
|
+
|
|
87
|
+
### A line the repository already proves does not ship
|
|
88
|
+
|
|
89
|
+
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).
|
|
90
|
+
|
|
91
|
+
### The region leads with this repository's commands
|
|
92
|
+
|
|
93
|
+
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).
|
|
94
|
+
|
|
95
|
+
### A change to what an agent reads can be measured
|
|
96
|
+
|
|
97
|
+
`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).
|
|
98
|
+
|
|
99
|
+
### What each reader loads, and a transcript split
|
|
100
|
+
|
|
101
|
+
`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.
|
|
102
|
+
|
|
103
|
+
### A name this tool owns is not reported as a repository
|
|
104
|
+
|
|
105
|
+
`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).
|
|
106
|
+
|
|
9
107
|
## [0.2.2] — 2026-10-14
|
|
10
108
|
|
|
11
109
|
### A repository can ask for a smaller projection
|
|
@@ -527,6 +625,8 @@ never ran `simt hooks install` again will find nothing until the dependency is r
|
|
|
527
625
|
and `simt hooks install --yes` is re-run. That is a real upgrade step and it is called out
|
|
528
626
|
here rather than discovered.
|
|
529
627
|
|
|
628
|
+
[0.3.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.3.1
|
|
629
|
+
[0.3.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.3.0
|
|
530
630
|
[0.2.2]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.2
|
|
531
631
|
[0.2.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.1
|
|
532
632
|
[0.2.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.0
|
package/README.md
CHANGED
|
@@ -1,54 +1,115 @@
|
|
|
1
1
|
# simt-dev
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
`simt` adopts a repository for the agents that work in it.
|
|
4
|
+
|
|
5
|
+
It reads what the repository actually runs, projects the rules that are not already
|
|
6
|
+
obvious into the files those agents read, and installs only the gates that repository
|
|
7
|
+
can run. Around that projection it keeps the bytes stable, replaces an instruction
|
|
8
|
+
with a check where a check can decide, answers questions about the tree from a local
|
|
9
|
+
index, and records whether a run still applies to the tree on disk.
|
|
10
|
+
|
|
11
|
+
The aim of the whole design is a higher share of tasks resolved, at a lower cost per
|
|
12
|
+
resolved task. Those targets are hypotheses. A change that touches what an agent reads
|
|
13
|
+
ships with a measurement, or its record says the measurement was not run. `simt rules
|
|
14
|
+
why ADR-NNN` prints the record, including whether it is still in force.
|
|
15
|
+
|
|
16
|
+
The default path is deterministic: no model, no network, no runtime dependencies.
|
|
17
|
+
Anything that needs a parser grammar, a local model, or another server is a separate
|
|
18
|
+
package, off until the repository names it, and it proposes rows or text for a person
|
|
19
|
+
to merge. It does not write the projection.
|
|
20
|
+
|
|
21
|
+
## Today
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
simt init # adopt or scaffold; prints a plan; --yes writes
|
|
25
|
+
simt doctor # what this repo is, and whether its config is healthy
|
|
26
|
+
simt check # projection drift, the manifest, and declared checkers
|
|
27
|
+
simt sync --yes # regenerate the projection
|
|
28
|
+
simt upgrade --yes # move every pin of this tool to this version
|
|
29
|
+
|
|
30
|
+
simt rules why ADR-042 # the decision behind a rule, and what was rejected
|
|
31
|
+
simt skills brief --task # a short brief for one task
|
|
32
|
+
simt tokens # bytes a reader loads at session start
|
|
33
|
+
simt shared plan # rules a workspace repeats, and what carrying them once saves
|
|
34
|
+
simt harness loaders # what each harness loads, and what its cap drops
|
|
35
|
+
|
|
36
|
+
simt hooks install --yes # the git dispatcher, for gates this repo can run
|
|
37
|
+
simt agent-hooks install --yes # the same policy before a tool call
|
|
38
|
+
simt evidence # runs recorded against this tree
|
|
39
|
+
simt index # files, exports and imports, from git blob hashes
|
|
40
|
+
simt plan next # the next tracker row that is ready
|
|
41
|
+
|
|
42
|
+
simt bench report # resolved rate, quality, cost, across arms
|
|
43
|
+
simt compare # every sibling checkout that has adopted
|
|
44
|
+
simt mcp # the read-only surface, over stdio
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`init`, `sync`, `upgrade`, `hooks install`, `agent-hooks install`, `shared install`
|
|
48
|
+
and `plan done` print a plan and write nothing without `--yes`. None of them commit
|
|
49
|
+
or push. `simt index` writes its cache without asking: the file is gitignored and
|
|
50
|
+
`git ls-files` can rebuild it.
|
|
51
|
+
|
|
52
|
+
`check` runs the checkers a rule names. A marked line leaves the projection as
|
|
53
|
+
`Enforced:` and the checker's name. The file-length line warns past 400 lines and
|
|
54
|
+
fails past 1000. `simt evidence check` warns when a commit message claims a passing
|
|
55
|
+
run this tree does not have, and the commit fails when `gates.evidence` is `block`.
|
|
56
|
+
The commit-msg hook is installed only when that setting is `warn` or `block`.
|
|
57
|
+
|
|
58
|
+
## Where the plan finishes
|
|
59
|
+
|
|
60
|
+
The work above is the part that runs. The plan still adds the rest of the loop, in
|
|
61
|
+
this order of purpose:
|
|
62
|
+
|
|
63
|
+
- **Gates on the change.** A check selects the tests that cover the diff, and the
|
|
64
|
+
tool's own gate output is shortened without dropping a failure line.
|
|
65
|
+
- **Answers instead of exploration.** A ranked map of the repository, the impact of
|
|
66
|
+
a file or a symbol, the tests that cover a change, and the decisions, rules and
|
|
67
|
+
skills that govern a path. An external code graph can answer for a language it
|
|
68
|
+
covers, and the answer names that source.
|
|
69
|
+
- **A brief for the task in front of the agent.** At most 2 KB, pulled at submit or
|
|
70
|
+
pushed by a hook, built from the map, the tests and the governing records.
|
|
71
|
+
- **A memory that has to earn its place.** A lesson is a candidate when a failing run
|
|
72
|
+
is followed by a fix. It is recalled on a later task that looks like it, and it
|
|
73
|
+
expires. Variant wording of a rule ships only when the bench says it won.
|
|
74
|
+
- **Local models, off unless asked.** Distillation and a critic propose. They do not
|
|
75
|
+
write projected text. With no model configured, nothing opens a socket.
|
|
76
|
+
- **The small doors.** One finding code explained, a safe `doctor` fix, a one-line
|
|
77
|
+
status, an undo journal, a diff of every planned write, shell completions, one
|
|
78
|
+
ignore list for every harness plus a secret scan, a worktree per slice, a file
|
|
79
|
+
lease when two agents would edit the same path, and a pull-request body taken from
|
|
80
|
+
the evidence.
|
|
19
81
|
|
|
20
82
|
## Install
|
|
21
83
|
|
|
22
|
-
Requires **Node >= 22.20.0
|
|
84
|
+
Requires **Node >= 22.20.0**. The current published release is **0.3.1**. Pin it.
|
|
85
|
+
The tool writes a content digest into the files it generates, and an unpinned upgrade
|
|
86
|
+
changes them under you.
|
|
23
87
|
|
|
24
88
|
```sh
|
|
25
|
-
|
|
26
|
-
npm i -D simt-dev@0.2.1
|
|
89
|
+
npm i -D simt-dev@0.3.1
|
|
27
90
|
|
|
28
|
-
# or globally
|
|
29
|
-
npm i -g simt-dev@0.
|
|
91
|
+
# or globally
|
|
92
|
+
npm i -g simt-dev@0.3.1
|
|
30
93
|
|
|
31
|
-
# or with no install
|
|
32
|
-
npx -y simt-dev@0.
|
|
94
|
+
# or once, with no install
|
|
95
|
+
npx -y simt-dev@0.3.1 check
|
|
33
96
|
```
|
|
34
97
|
|
|
35
98
|
### From PyPI
|
|
36
99
|
|
|
37
|
-
The same
|
|
38
|
-
|
|
39
|
-
fetched when you install it.
|
|
100
|
+
The wheel carries the same bytes as the npm tarball. It does not fetch them from npm
|
|
101
|
+
at install time.
|
|
40
102
|
|
|
41
103
|
```sh
|
|
42
104
|
pip install simt-dev
|
|
43
105
|
|
|
44
|
-
#
|
|
106
|
+
# when this machine has no node
|
|
45
107
|
pip install "simt-dev[node]"
|
|
46
108
|
```
|
|
47
109
|
|
|
48
|
-
`simt` is the command on both channels.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
you get a message naming the remedy rather than a placeholder node path.
|
|
110
|
+
`simt` is the command on both channels. Node is resolved in this order, and the first
|
|
111
|
+
one that answers wins: `SIMT_DEV_NODE`, the node bundled with the `node` extra, then
|
|
112
|
+
`node` on `PATH`.
|
|
52
113
|
|
|
53
114
|
### From Homebrew
|
|
54
115
|
|
|
@@ -56,65 +117,58 @@ you get a message naming the remedy rather than a placeholder node path.
|
|
|
56
117
|
brew install QuazarMedia/homebrew-tap/simt-dev
|
|
57
118
|
```
|
|
58
119
|
|
|
59
|
-
The formula
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Pin the version. The tool writes a content digest into the files it generates, and an
|
|
63
|
-
unpinned upgrade changes them under you.
|
|
120
|
+
The formula depends on `node`, so the command works on a machine that has never had
|
|
121
|
+
Node installed.
|
|
64
122
|
|
|
65
|
-
##
|
|
123
|
+
## What it writes
|
|
66
124
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
simt
|
|
70
|
-
simt
|
|
71
|
-
simt
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
simt
|
|
75
|
-
```
|
|
125
|
+
| Path | What |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `AGENTS.md` | the projection, between `<!-- simt:begin -->` and `<!-- simt:end -->`. The old `simt-dev:` markers are still read, and a region migrates on its next write |
|
|
128
|
+
| `simt.json` | the manifest: gates, substitutions, budget, shared rules, surfaces |
|
|
129
|
+
| `simt-dev.lock.json` | the digest of the inputs and the digest of the projection |
|
|
130
|
+
| `.cursor/rules/*.mdc`, `.claude/rules/*.md`, `.agents/rules/*.md`, `.github/instructions/*.instructions.md` | one file per rule, and only for a harness that reads that directory |
|
|
131
|
+
| `.editorconfig`, `.gitattributes` | baseline regions, placed first so a later setting still wins |
|
|
132
|
+
| `.simt-hooks/` and `core.hooksPath` | the shared dispatcher. It fails closed |
|
|
76
133
|
|
|
77
|
-
|
|
134
|
+
Text outside the markers is left alone. A regeneration does not revert it.
|
|
78
135
|
|
|
79
|
-
|
|
136
|
+
These are caches, gitignored, and rebuildable. They are not repository content:
|
|
80
137
|
|
|
81
138
|
| Path | What |
|
|
82
139
|
| --- | --- |
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
| `.
|
|
86
|
-
| `.editorconfig`, `.gitattributes` | baseline regions |
|
|
87
|
-
| `.simt-hooks/` and `core.hooksPath` | the shared dispatcher, fail-closed |
|
|
140
|
+
| `.simt-dev/index.sqlite` | files, symbols, import edges. JSON-lines at `.simt-dev/index.jsonl` when this Node cannot open `node:sqlite` with FTS5 |
|
|
141
|
+
| `.simt-dev/ledger.jsonl` | one line per gate the dispatcher actually ran, against one git tree |
|
|
142
|
+
| `.simt-dev/bench/runs/` | benchmark runs |
|
|
88
143
|
|
|
89
|
-
|
|
144
|
+
`simt shared install --yes` writes a user-level copy of the rules a workspace
|
|
145
|
+
repeats, into the file that harness documents, and records the ids on each
|
|
146
|
+
repository's manifest. The next `sync` drops those rules from the region and names
|
|
147
|
+
them.
|
|
90
148
|
|
|
91
|
-
##
|
|
149
|
+
## How a rule is chosen
|
|
92
150
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
151
|
+
Detection reads dependency manifests, build files and CI workflows, and ranks rules
|
|
152
|
+
by path, keyword, name, capability and stack. The ranking is a score. A pull request
|
|
153
|
+
can diff it.
|
|
96
154
|
|
|
97
|
-
A
|
|
98
|
-
|
|
99
|
-
|
|
155
|
+
A repository whose ecosystem has no profile yet is still adopted: it gets the core
|
|
156
|
+
rules and no gates, until gate templates exist for it. A hook that would run nothing
|
|
157
|
+
is not installed.
|
|
100
158
|
|
|
101
|
-
|
|
159
|
+
Every behaviour that is a choice has a numbered record. `simt rules why <id>` is the
|
|
160
|
+
path from a rule you did not expect to the reason it is there.
|
|
102
161
|
|
|
103
|
-
|
|
104
|
-
CI workflows — and resolves that against a registry of capabilities. Resolution is a
|
|
105
|
-
scored ranking over path, keyword, name, capability and stack: no model, no network, no
|
|
106
|
-
guessing, so a pull request can meaningfully diff the answer.
|
|
162
|
+
## Platform
|
|
107
163
|
|
|
108
|
-
**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
agent and no pre-commit hook — better than one with a hook that runs nothing, but not full
|
|
112
|
-
support, and this README will not pretend otherwise.
|
|
164
|
+
**macOS and Linux.** The hooks are POSIX `sh`. There is no Windows build. `npm i` on
|
|
165
|
+
Windows fails with npm's `EBADPLATFORM`, which npm will not let a package explain, so
|
|
166
|
+
the answer is here: **use WSL2**, and the gate works inside it.
|
|
113
167
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
did not expect, that command is the shortest path from the symptom to the reason.
|
|
168
|
+
A Windows checkout without WSL2 can still read the projection. It is plain text. The
|
|
169
|
+
hooks and the CI gate need the POSIX host.
|
|
117
170
|
|
|
118
171
|
## Licence
|
|
119
172
|
|
|
120
|
-
Apache-2.0. See [LICENSE](LICENSE).
|
|
173
|
+
Apache-2.0. See [LICENSE](LICENSE). Report a vulnerability through
|
|
174
|
+
[SECURITY.md](SECURITY.md).
|
package/bin/simt-dev.mjs
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
// POSIX only (ADR-012). package.json declares os: [darwin, linux], so npm refuses
|
|
5
5
|
// the install elsewhere rather than letting a hook fail obscurely.
|
|
6
6
|
|
|
7
|
-
import { dispatch } from '../src/commands/index.mjs';
|
|
8
|
-
import { EXIT, fail } from '../src/output/index.mjs';
|
|
9
7
|
import { parse } from '../src/argv.mjs';
|
|
8
|
+
import { EXIT, fail, jsonFailMode, jsonIndentDefault } from '../src/output/index.mjs';
|
|
10
9
|
|
|
11
10
|
try {
|
|
12
11
|
const argv = process.argv.slice(2);
|
|
@@ -22,6 +21,26 @@ try {
|
|
|
22
21
|
process.exit(fail('no command given', EXIT.USAGE));
|
|
23
22
|
}
|
|
24
23
|
|
|
24
|
+
jsonIndentDefault(flags.pretty === true);
|
|
25
|
+
jsonFailMode(flags.json === true);
|
|
26
|
+
|
|
27
|
+
// The one command that runs on somebody else's hot path, and therefore the one that
|
|
28
|
+
// does not load the registry. `agent-hook` is called before every tool call an agent
|
|
29
|
+
// makes, so its latency is a product requirement rather than a nicety (plan 0003
|
|
30
|
+
// R15), and the registry's imports - detection, projection, the bench corpus - cost
|
|
31
|
+
// tens of milliseconds before the command itself starts. Measured on the machine
|
|
32
|
+
// this was written on: 127.7 ms through the registry against 91.5 ms with only this
|
|
33
|
+
// module's imports, where the bare runtime is 86.9 ms of it.
|
|
34
|
+
//
|
|
35
|
+
// Only the exact spelling the renderers write takes this path. Anything else -
|
|
36
|
+
// including `agent-hook --help` - loads the registry, so help stays the one list of
|
|
37
|
+
// every command and this shortcut cannot become a second, thinner command surface.
|
|
38
|
+
if (positional[0] === 'agent-hook' && !flags.help && !flags.version) {
|
|
39
|
+
const { agentHookCommand } = await import('../src/commands/agenthook.mjs');
|
|
40
|
+
process.exit((await agentHookCommand(positional.slice(1), flags)) ?? EXIT.OK);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { dispatch } = await import('../src/commands/index.mjs');
|
|
25
44
|
process.exit((await dispatch(argv)) ?? EXIT.OK);
|
|
26
45
|
} catch (error) {
|
|
27
46
|
process.exit(fail(error?.message ?? String(error), EXIT.FAIL));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ADR-073 — A kernel is a smaller projection the repository asks for, not one it is given
|
|
2
2
|
|
|
3
|
-
Status: **accepted** · 2026-10-14 · filed as S38 (R07)
|
|
3
|
+
Status: **accepted** · 2026-10-14 · filed as S38 (R07) · ruling 6 narrowed by ADR-084
|
|
4
4
|
**Affects:** `src/projection/manifest.mjs`, `src/projection/rules.mjs`, `src/measure/context.mjs`,
|
|
5
5
|
`rules/core.md`, `rules/stacks/*.md`, `test/kernel.test.mjs`
|
|
6
6
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# ADR-074 — A rule and a decision are addressable, so they are readable by URI
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-14 · filed as S43
|
|
4
|
+
**Affects:** `src/mcp/resources.mjs`, `src/mcp/server.mjs`, `test/mcp.test.mjs`
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Review 0002 filed that the MCP server is two protocol revisions behind and returns text
|
|
9
|
+
only. ADR-068 closed the revision half and every tool now declares an output schema and
|
|
10
|
+
annotations. What remained was the other half of the finding: the server has no
|
|
11
|
+
**resources**, so the two things this tool is actually authoritative about — the rules it
|
|
12
|
+
projects and the decisions it settles — are reachable only through a tool call with an
|
|
13
|
+
argument in it.
|
|
14
|
+
|
|
15
|
+
That matters for a client that wants to browse rather than query. A tool call asks a
|
|
16
|
+
question and gets an answer; a resource is something a client can list, show in a picker,
|
|
17
|
+
attach to a context window and re-read on demand. The CLI already answers both by id
|
|
18
|
+
(`rules show`, `rules why`); what was missing was the addressable form.
|
|
19
|
+
|
|
20
|
+
So the specification was fetched and read for both revisions rather than recalled, and it
|
|
21
|
+
turned out the two disagree in a way that decides an observable behaviour: **a missing
|
|
22
|
+
resource is `-32602` (Invalid Params) in 2026-07-28 and `-32002` (Resource not found) in
|
|
23
|
+
2025-06-18.** 2026-07-28 keeps `-32002` only as something a *client* should still accept.
|
|
24
|
+
Both revisions forbid an empty `contents` array for a resource that does not exist, because
|
|
25
|
+
empty is ambiguous — it reads as "here is a resource with no body".
|
|
26
|
+
|
|
27
|
+
Both revisions also require the capability to be declared, and both permit the empty
|
|
28
|
+
object for a server with no subscriptions and no change notifications. The newer one adds
|
|
29
|
+
`ttlMs` and `cacheScope` to resource results, which is why a resource list is cacheable on
|
|
30
|
+
the newest revision and is a bare list on the two older ones — the same rule ADR-068
|
|
31
|
+
already applies to tools.
|
|
32
|
+
|
|
33
|
+
## Rulings
|
|
34
|
+
|
|
35
|
+
1. **Two collections and no others: the rules and the decisions.**
|
|
36
|
+
`simt://rules/{id}` and `simt://decisions/{adr}`. A resource set that grew with the
|
|
37
|
+
registry would be a second index to keep honest, and the registry is workspace data
|
|
38
|
+
(ADR-061) — the package ships none of it, so a client browsing this server in one
|
|
39
|
+
repository and another would see different sets anyway.
|
|
40
|
+
|
|
41
|
+
2. **The capability is declared unconditionally, as an empty object.** Every rule and every
|
|
42
|
+
decision record ships with the package, so there is always something to read; and
|
|
43
|
+
`{}` is the documented way to say "no subscriptions, no change notifications", which
|
|
44
|
+
are the two things this server has no way to honour. The previous code gated the
|
|
45
|
+
capability on a property hung on the tools array that nothing ever set — a promise a
|
|
46
|
+
client waits on forever, only quieter.
|
|
47
|
+
|
|
48
|
+
3. **A missing resource is an error, and the code is the revision's own.** `-32602` from
|
|
49
|
+
2026-07-28, `-32002` from the two older revisions, and never an empty `contents`. A
|
|
50
|
+
client that reads a URI and gets silence cannot tell a bug from an empty rule, and the
|
|
51
|
+
two revisions disagree about which code that silence should wear.
|
|
52
|
+
|
|
53
|
+
4. **Every listed resource carries the bytes it will cost.** `size` is in the specification
|
|
54
|
+
and it is the one number a client cannot get without reading: an agent assembling a
|
|
55
|
+
context window needs to know what a resource costs before it spends it. This is the
|
|
56
|
+
same argument as `simt tokens`, arriving at a different surface.
|
|
57
|
+
|
|
58
|
+
5. **The frontmatter is rebuilt, unprefixed, and the body's own heading is not repeated.**
|
|
59
|
+
A rule on disk opens with frontmatter this tool's namespace owns (`simt-`); a resource
|
|
60
|
+
is read by a client that knows nothing about that namespace, so the identity is
|
|
61
|
+
reconstructed under plain keys. The loader no longer carries the H1, so the title lives
|
|
62
|
+
in the frontmatter rather than being re-added by hand in two places.
|
|
63
|
+
|
|
64
|
+
6. **A decision body is redacted on the way out.** `redactInternalPaths(redactEstate(...))`,
|
|
65
|
+
the same pass the CLI applies. A resource is readable by anyone holding the connection,
|
|
66
|
+
which is a wider audience than the repository the CLI is aimed at, and a record whose
|
|
67
|
+
argument names an estate repository should not travel further just because it became
|
|
68
|
+
addressable.
|
|
69
|
+
|
|
70
|
+
7. **Every URI is validated, and a rules id cannot contain a slash.** Custom schemes are
|
|
71
|
+
allowed, which is precisely why the server checks: a URI naming another scheme, a
|
|
72
|
+
`..` segment, an empty id or a nested path is refused with the same revision-correct
|
|
73
|
+
error as one that simply does not exist. A refusal that is distinguishable from a miss
|
|
74
|
+
is a way to probe the filesystem.
|
|
75
|
+
|
|
76
|
+
## Consequences
|
|
77
|
+
|
|
78
|
+
`resources/list` answers 97 entries in this checkout — 20 rules, the process rules, and 73
|
|
79
|
+
decision records — and a client can attach any of them without a tool call. The templates
|
|
80
|
+
list publishes the two shapes, so a client can build the URI for an id it has not listed
|
|
81
|
+
yet.
|
|
82
|
+
|
|
83
|
+
What this does not do: subscribe to a rule changing, because there is no stream to
|
|
84
|
+
subscribe to and the capability says so. That is a feature, not an omission — a client
|
|
85
|
+
that believes it is subscribed to a projection that only changes on `sync` would be
|
|
86
|
+
waiting for an event this tool will never emit.
|
|
87
|
+
|
|
88
|
+
## Rejected
|
|
89
|
+
|
|
90
|
+
- **Serving resources only through tool calls.** That is what the server already did, and
|
|
91
|
+
the finding is that a client cannot browse what it can only ask about.
|
|
92
|
+
- **An empty `contents` array for a URI that does not exist.** Both revisions treat it as
|
|
93
|
+
ambiguous, and ambiguity here is what makes a client retry forever.
|
|
94
|
+
- **Exposing the registry as resources too.** It is workspace data and this package ships
|
|
95
|
+
none of it (ADR-061); a resource list that differed per repository would be the
|
|
96
|
+
same definition-of-one-thing-twice defect, one layer up.
|
|
97
|
+
- **One error code for all three revisions.** `-32602` on the older two would be a code
|
|
98
|
+
that revision never defined, and `-32002` on the newest would be advice to clients
|
|
99
|
+
rather than the specification's own answer.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# ADR-075 — Nothing leaves the machine: no telemetry, local transcripts, loopback models by default
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-14 · filed as D7 (R06)
|
|
4
|
+
**Affects:** `src/measure/session.mjs`, `src/measure/context.mjs`, `src/commands/tokens.mjs`, `src/argv.mjs`, `test/tokens.test.mjs`
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The context meter (`simt tokens`) now goes beyond static repository files to inspect
|
|
9
|
+
recorded agent transcripts (`simt tokens --session <path>`), and later slices in the
|
|
10
|
+
research programme connect optional local model servers for embeddings, distillation and
|
|
11
|
+
pre-review critique.
|
|
12
|
+
|
|
13
|
+
Session transcripts are among the most sensitive files on a developer's machine: they hold
|
|
14
|
+
every file an agent read, every command output it saw, uncommitted diffs, environment
|
|
15
|
+
diagnostics and prompts written in the middle of debugging. A tool that fetched remote
|
|
16
|
+
session URLs, phoned home usage metrics, or defaulted model calls to a hosted endpoint
|
|
17
|
+
would turn a local measurement command into an exfiltration surface.
|
|
18
|
+
|
|
19
|
+
## Rulings
|
|
20
|
+
|
|
21
|
+
1. **No command emits telemetry, analytics or background network traffic.** Every command
|
|
22
|
+
operates over the local filesystem and stdio alone unless the user explicitly invokes a
|
|
23
|
+
documented network flag in a later adapter.
|
|
24
|
+
|
|
25
|
+
2. **`simt tokens --session <path>` accepts local filesystem paths only and refuses URLs.**
|
|
26
|
+
Any argument carrying a URI scheme (`http://`, `https://`, `ws://`, `ssh://` or any
|
|
27
|
+
`<scheme>://`) is rejected at argument validation with a configuration exit code and a
|
|
28
|
+
remedy naming a local file path, before any socket or filesystem read is attempted.
|
|
29
|
+
|
|
30
|
+
3. **Session attribution is computed deterministically in process with zero dependencies.**
|
|
31
|
+
Transcript JSON and JSONL records from Claude Code and Codex are split into `rules`
|
|
32
|
+
(system instructions, inline instruction blocks, and reads of instruction, rule or skill
|
|
33
|
+
files), `fileReads` (reads of repository source and config files) and `toolOutput`
|
|
34
|
+
(command and tool execution results) by inspecting the local file alone.
|
|
35
|
+
|
|
36
|
+
4. **Future local-model adapters default to loopback (`127.0.0.1` / `localhost`) and refuse
|
|
37
|
+
remote hosts unless explicitly overridden.** When no local model is configured in
|
|
38
|
+
`simt-dev.json`, no socket is opened at all.
|
|
39
|
+
|
|
40
|
+
## Consequences
|
|
41
|
+
|
|
42
|
+
`simt tokens --session <path>` can be run safely on production incident transcripts and
|
|
43
|
+
private repositories because the parser cannot open a network connection. And coupling the
|
|
44
|
+
static meter to the harness loader table (`R14`) means `simt tokens` now counts what each
|
|
45
|
+
reader actually loads at session start — flat `.agents/rules/` scans for Antigravity,
|
|
46
|
+
`alwaysApply: true` for Cursor, `@path` imports for Claude Code, and root-to-cwd chain
|
|
47
|
+
caps for Codex — without leaving the machine.
|
|
48
|
+
|
|
49
|
+
## Rejected
|
|
50
|
+
|
|
51
|
+
- **Fetching remote session URLs or cloud share links in `simt tokens --session`.** Convenience
|
|
52
|
+
for a shared link is also an outbound request from a tool whose contract is that it
|
|
53
|
+
only reads local state.
|
|
54
|
+
- **Anonymous opt-out telemetry for context budget numbers.** Even aggregate byte counts
|
|
55
|
+
reveal repository names, adoption state and working hours, and an opt-out flag still
|
|
56
|
+
ships a network client in the default path.
|