simt-dev 0.6.3 → 0.7.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 +35 -0
- package/README.md +2 -2
- package/agents/simt-agent.md +84 -0
- package/docs/decisions/ADR-009-skill-layout.md +1 -1
- package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md +1 -1
- package/docs/decisions/ADR-106-the-harness-hook-runs-through-a-repo-shim.md +46 -0
- package/docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md +51 -0
- package/docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md +59 -0
- 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
- 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
- package/docs/decisions/ADR-111-the-tool-ships-one-agent-rendered-per-harness-and-installs-it-with-init-and-a-command.md +75 -0
- package/docs/decisions/ADR-112-the-registry-is-what-simt-dev-ships-and-a-repo-owns-its-own-skills.md +81 -0
- package/docs/decisions/ADR-113-a-pre-push-that-carries-only-documentation-skips-the-test-gate-and-says-so.md +87 -0
- 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
- 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
- package/docs/decisions/ADR-116-rust-support-reaches-the-example-path-the-indent-and-the-bench-runner.md +100 -0
- package/docs/decisions/README.md +9 -0
- package/package.json +4 -2
- package/rules/core.md +2 -2
- package/rules/mdc/test-gate.mdc +2 -0
- package/rules/stacks/android.md +3 -3
- package/rules/stacks/connectiq.md +2 -2
- package/rules/stacks/dart.md +1 -1
- package/rules/stacks/deno.md +1 -1
- package/rules/stacks/dotnet.md +1 -1
- package/rules/stacks/elixir.md +1 -1
- package/rules/stacks/go.md +1 -1
- package/rules/stacks/jvm.md +3 -2
- package/rules/stacks/node.md +2 -2
- package/rules/stacks/python.md +7 -6
- package/rules/stacks/ruby.md +1 -1
- package/rules/stacks/rust.md +1 -1
- package/rules/stacks/swift.md +1 -1
- package/rules/stacks/typescript.md +2 -2
- package/rules/stacks/uv.md +3 -2
- package/skills/simt-commands/SKILL.md +70 -0
- package/src/advice/advice.mjs +7 -3
- package/src/agenthooks/decide.mjs +141 -9
- package/src/agenthooks/opencode.mjs +72 -0
- package/src/agenthooks/prompt.mjs +6 -2
- package/src/agenthooks/render.mjs +186 -19
- package/src/agenthooks/shim.mjs +55 -0
- package/src/agents/render.mjs +277 -0
- package/src/argv.mjs +8 -0
- package/src/bench/ablate.mjs +31 -14
- package/src/bench/mine.mjs +48 -1
- package/src/bench/probes.mjs +5 -6
- package/src/bench/report.mjs +243 -80
- package/src/bench/run.mjs +12 -11
- package/src/bench/variants.mjs +112 -40
- package/src/brief/assemble.mjs +6 -0
- package/src/ci/workflow.mjs +16 -7
- package/src/commands/agent.mjs +206 -0
- package/src/commands/agenthook.mjs +109 -15
- package/src/commands/bench.mjs +2 -1
- package/src/commands/compare.mjs +8 -7
- package/src/commands/decide.mjs +6 -5
- package/src/commands/evidence.mjs +13 -3
- package/src/commands/gate.mjs +85 -19
- package/src/commands/graph.mjs +18 -5
- package/src/commands/help.mjs +35 -14
- package/src/commands/hooks.mjs +68 -25
- package/src/commands/impact.mjs +4 -2
- package/src/commands/index.mjs +17 -4
- package/src/commands/init.mjs +169 -37
- package/src/commands/lease.mjs +203 -35
- package/src/commands/lessons.mjs +65 -9
- package/src/commands/map.mjs +33 -18
- package/src/commands/plugin.mjs +1 -1
- package/src/commands/repoindex.mjs +1 -0
- package/src/commands/rules.mjs +92 -18
- package/src/commands/skills.mjs +60 -25
- package/src/commands/skillscompile.mjs +8 -7
- package/src/commands/skillsinstall.mjs +12 -0
- package/src/commands/slice.mjs +377 -18
- package/src/commands/sync.mjs +37 -22
- package/src/commands/tests.mjs +5 -2
- package/src/commands/undo.mjs +24 -16
- package/src/commands/upgrade.mjs +33 -1
- package/src/commands/why.mjs +10 -4
- package/src/compare/hooksstate.mjs +23 -1
- package/src/compare/learn.mjs +82 -57
- package/src/compare/parallel.mjs +50 -0
- package/src/compare/row.mjs +26 -1
- package/src/completions.mjs +5 -0
- package/src/decisions/dir.mjs +84 -0
- package/src/decisions/index.mjs +109 -11
- package/src/distill/distill.mjs +3 -1
- package/src/ecosystem/node-runner.mjs +144 -0
- package/src/explain.mjs +18 -6
- package/src/graph/load.mjs +38 -19
- package/src/hooks/capability.mjs +14 -1
- package/src/hooks/dispatcher.mjs +335 -13
- package/src/hooks/resolve.mjs +9 -3
- package/src/hooks/shim.mjs +12 -7
- package/src/identity.mjs +67 -14
- package/src/index/extract.mjs +192 -11
- package/src/index/persist.mjs +307 -47
- package/src/index/store.mjs +263 -60
- package/src/index/tests.mjs +6 -35
- package/src/journal.mjs +296 -77
- package/src/kernel/card.mjs +37 -12
- package/src/kernel/checks.mjs +44 -9
- package/src/kernel/variants.mjs +27 -16
- package/src/lease.mjs +97 -15
- package/src/ledger/index.mjs +91 -25
- package/src/mcp/resources.mjs +5 -3
- package/src/mcp/server.mjs +81 -22
- package/src/mcp/tools.mjs +45 -9
- package/src/memory/decide.mjs +98 -43
- package/src/memory/lessons.mjs +168 -33
- package/src/name.mjs +9 -0
- package/src/plan/index.mjs +31 -8
- package/src/plan/template.mjs +27 -5
- package/src/plugins.mjs +6 -2
- package/src/prbody.mjs +7 -5
- package/src/projection/baselines.mjs +1 -1
- package/src/projection/disclosure.mjs +2 -2
- package/src/projection/loaders.mjs +41 -1
- package/src/projection/lock.mjs +21 -14
- package/src/projection/manifest.mjs +69 -19
- package/src/projection/markers.mjs +43 -3
- package/src/projection/mdc.mjs +34 -7
- package/src/projection/project.mjs +162 -18
- package/src/projection/rules.mjs +54 -3
- package/src/projection/surfaces.mjs +9 -1
- package/src/repo-root.mjs +69 -0
- package/src/resolve/body.mjs +4 -0
- package/src/resolve/fingerprint.mjs +5 -0
- package/src/resolve/local.mjs +58 -0
- package/src/review/critic.mjs +4 -2
- package/src/secrets.mjs +165 -22
- package/src/state.mjs +76 -0
- package/src/status.mjs +9 -4
- package/src/toolwords.mjs +5 -0
- package/src/tsconfig.mjs +39 -99
- package/templates/plan/tracker.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,41 @@ All notable changes to this package are recorded here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.7.0] — 2026-10-31
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
`simt agent-hooks install --harness <id>` writes the policies into one
|
|
14
|
+
harness instead of all of them; an unknown id is a usage error that names
|
|
15
|
+
the valid ones. The readers the run skipped are named in the report
|
|
16
|
+
(`SKIPPED`) and in the JSON document's `notWritten`, so a narrowed install
|
|
17
|
+
cannot read as full coverage (ADR-107).
|
|
18
|
+
|
|
19
|
+
`simt init --yes` builds the repository index as part of the adoption, so
|
|
20
|
+
the first `simt map` or `simt brief` in a fresh repository answers from real
|
|
21
|
+
files. Commands that read the index (`map`, `brief`, `slice`) now refresh it
|
|
22
|
+
from the git index on every read — unchanged blobs are not re-read, so a
|
|
23
|
+
current repository pays for one `git ls-files -s` — and the prompt hook and
|
|
24
|
+
MCP surface still report a cold index rather than build one (ADR-108).
|
|
25
|
+
|
|
26
|
+
The rule text names the manifest as `simt.json`, the name it has had since
|
|
27
|
+
the rename; four rule files still said `simt-dev.json`, which resolves in no
|
|
28
|
+
repository.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
The projection lock is `simt.lock.json`, the name that follows the command
|
|
33
|
+
and the manifest. A lock written under the old `simt-dev.lock.json` is still
|
|
34
|
+
read — including the `variants` winners nothing else records — and migrates
|
|
35
|
+
on the next write (ADR-110).
|
|
36
|
+
|
|
37
|
+
This repository is now a consumer of itself: `simt init` ran on its own
|
|
38
|
+
source, so the two tests that asserted it was *not* a consumer are rewritten
|
|
39
|
+
to assert the new state, and `AGENTS.md`'s "not self-adopted" section now
|
|
40
|
+
says what is true (ADR-109). A gate finding whose `detail` is absent is now
|
|
41
|
+
`null` rather than `undefined`, so the CLI's JSON and the MCP structured
|
|
42
|
+
surface of one answer deep-equal.
|
|
43
|
+
|
|
9
44
|
## [0.6.3] — 2026-10-29
|
|
10
45
|
|
|
11
46
|
### Added
|
package/README.md
CHANGED
|
@@ -152,7 +152,7 @@ simt bench run --yes # evaluate arms (bare, current, kernel, brie
|
|
|
152
152
|
simt bench report [--compare a b] # resolved rate, quality, cost, CPI, bootstrap CIs, McNemar/Wilcoxon
|
|
153
153
|
simt bench ablate --yes # leave-one-out rule ablation ranked by value per KB
|
|
154
154
|
simt bench probes --yes # 12 deterministic rule-compliance probes
|
|
155
|
-
simt bench variants --yes # evaluate simt-variants slots and lock winners in simt
|
|
155
|
+
simt bench variants --yes # evaluate simt-variants slots and lock winners in simt.lock.json
|
|
156
156
|
simt review [--local] # an advisory ADR-008 verdict skeleton over the staged diff
|
|
157
157
|
simt distill <skill|doc> # a budgeted proposal summary with line citations, for a person to merge
|
|
158
158
|
simt pack new <name> --source <spec> # scaffold a versioned pack: pack.json, bench.json, one rule
|
|
@@ -369,7 +369,7 @@ history. All remain off until named and propose rather than write projected text
|
|
|
369
369
|
| --- | --- |
|
|
370
370
|
| `AGENTS.md` (and `<package>/AGENTS.md` in monorepos) | the projection, between `<!-- simt:begin -->` and `<!-- simt:end -->`. The old `simt-dev:` markers are still read, and a region migrates on its next write |
|
|
371
371
|
| `simt.json` | the manifest: `gates`, `substitutions`, `budget`, `mode`, `shared`, `surfaces`, `plans`, `packs`, `federation`, `local` |
|
|
372
|
-
| `simt
|
|
372
|
+
| `simt.lock.json` | the digest of the inputs, the digest of the projection, and locked `variants` winners from `simt bench variants`. The old `simt-dev.lock.json` is still read, and a lock migrates on its next write |
|
|
373
373
|
| `.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 |
|
|
374
374
|
| `.editorconfig`, `.gitattributes` | baseline regions, placed first so a later setting still wins |
|
|
375
375
|
| `.simt-hooks/` and `core.hooksPath` | the shared dispatcher. It fails closed |
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Simt-Agent
|
|
3
|
+
description: Operates a repository with simt-dev: creates plan and review docs from the template, plans the next slice from the tracker, builds it, tests it, and commits it with evidence.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the simt-dev operator for this repository. You do three jobs in one:
|
|
7
|
+
you **create the plan or review doc** when starting new work, you **plan** the
|
|
8
|
+
next piece of work from the tracker, and you **build** it. You act through the
|
|
9
|
+
`simt` commands the repository already runs, and you never claim a step you
|
|
10
|
+
did not take.
|
|
11
|
+
|
|
12
|
+
## Orient before you act
|
|
13
|
+
|
|
14
|
+
- On a new task, run `simt brief "<task>"` before exploring. The packet tells
|
|
15
|
+
you how to run the tests, which files the task touches, and what governs them
|
|
16
|
+
— so you stop re-discovering what the repository already knows.
|
|
17
|
+
- Before editing a file, run `simt why <file>`. It lists the decisions, rules,
|
|
18
|
+
skills, gates and lessons that govern that file. Edit with those in mind.
|
|
19
|
+
|
|
20
|
+
## Plan the next slice
|
|
21
|
+
|
|
22
|
+
- Read the tracker with `simt plan status` and `simt plan next`. `next` names
|
|
23
|
+
the smallest slice that is independently valuable, in dependency order.
|
|
24
|
+
- Resolve the open material decisions for that slice before building: ask 1–5
|
|
25
|
+
questions, recommended option first, and only where the authority documents
|
|
26
|
+
leave a real choice. Apply documented defaults without asking — operational
|
|
27
|
+
and procedural choices are not questions.
|
|
28
|
+
- State the slice you will take, the owning repository, the tracker task id,
|
|
29
|
+
and the acceptance checks, before you start.
|
|
30
|
+
|
|
31
|
+
## Create the plan or review doc
|
|
32
|
+
|
|
33
|
+
When starting a new feature or a review, the deliverable is a file, not a
|
|
34
|
+
conversation. Write it before you build:
|
|
35
|
+
|
|
36
|
+
- **Plan:** `docs/plans/NNNN-slug.md` — the next number after the highest one
|
|
37
|
+
already in that directory. Open with `# Plan NNNN — Title`, a `Status:` line
|
|
38
|
+
(`**queued**` · created date), scope, and a tracker table with one row per
|
|
39
|
+
slice carrying an id, a state, and an evidence column. Each slice gets its
|
|
40
|
+
own section below the table.
|
|
41
|
+
- **Review:** `docs/reviews/NNNN-slug.md` — same numbering rule. Open with
|
|
42
|
+
`# Review NNNN — Title`, a `Status:` line, scope and method, severity
|
|
43
|
+
levels, and a tracker table or a pointer to the plan that owns the slices.
|
|
44
|
+
- Do not display the plan in the conversation and stop. The file is the
|
|
45
|
+
deliverable; the conversation is the working session.
|
|
46
|
+
- After the doc exists, track it with `simt plan status` and drive its rows
|
|
47
|
+
with `simt slice start` / `simt slice finish` / `simt plan done`.
|
|
48
|
+
|
|
49
|
+
## Build one slice
|
|
50
|
+
|
|
51
|
+
- Start it with `simt slice start <id>`. This isolates the tracker row in its
|
|
52
|
+
own worktree with a task brief. Work only in that worktree.
|
|
53
|
+
- Make the smallest change that is independently valuable on its own. Do not
|
|
54
|
+
refactor outside the slice's blast radius.
|
|
55
|
+
- Run the focused test the change needs, through the repository's own runner,
|
|
56
|
+
naming the exact command and the exact test file. Every test must pass; a
|
|
57
|
+
partial pass is a blocker, not a result to quote.
|
|
58
|
+
- Stage only the files the slice touched. Leave unrelated work alone and say so.
|
|
59
|
+
Read the staged diff — not the working tree — before you commit.
|
|
60
|
+
- Finish it with `simt slice finish <id>`. This runs the declared gates, commits
|
|
61
|
+
with a Conventional Commits subject drawn from the row, and records the SHA.
|
|
62
|
+
- Record the slice complete with `simt plan done <id> <sha> --test "<command>"`,
|
|
63
|
+
carrying the evidence for the test you ran.
|
|
64
|
+
|
|
65
|
+
## Dry run
|
|
66
|
+
|
|
67
|
+
Most simt-dev commands that write to the repository default to **dry-run**:
|
|
68
|
+
they print what they would change and exit without writing. Add `--yes` to
|
|
69
|
+
apply. Read the dry-run output first — it is the plan — then re-run with
|
|
70
|
+
`--yes`. A dry-run that shows an unexpected file is a reason to stop, not to
|
|
71
|
+
add `--yes` and hope.
|
|
72
|
+
|
|
73
|
+
For the full command reference — which commands are mutating vs read-only, and
|
|
74
|
+
the scenario-to-command mapping — read the `simt-commands` skill.
|
|
75
|
+
|
|
76
|
+
## Boundaries you do not cross
|
|
77
|
+
|
|
78
|
+
- Never claim a test you did not run, a check you did not execute, or a commit
|
|
79
|
+
you did not make.
|
|
80
|
+
- Commit locally and report the SHA. Pushing is a separate decision that needs
|
|
81
|
+
its own approval; a finished slice is not a reason to push.
|
|
82
|
+
- Never use `--no-verify`, and never amend, rebase, reset or rewrite history
|
|
83
|
+
unless explicitly asked.
|
|
84
|
+
- A gate that cannot run blocks the commit; do not skip it to get a green line.
|
package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ADR-086 — The stable prefix comes first, and the lock records both digests
|
|
2
2
|
|
|
3
|
-
Status: **accepted** · 2026-10-18 · filed as R13 (plan 0003)
|
|
3
|
+
Status: **accepted** · 2026-10-18 · filed as R13 (plan 0003) · ruling 3's file name is superseded by [ADR-110](ADR-110-the-lock-file-follows-the-manifest-name-and-the-old-name-is-read-until-the-next-write.md)
|
|
4
4
|
|
|
5
5
|
**Affects:** `src/projection/rules.mjs`, `src/projection/lock.mjs`, `src/commands/sync.mjs`,
|
|
6
6
|
`src/argv.mjs`, `test/lock.test.mjs`, `test/card.test.mjs`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["src/agenthooks/shim.mjs", "src/agenthooks/render.mjs", "src/commands/agenthook.mjs"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-106 — the harness hook runs through a repo shim, and `failClosed` only with it
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-29
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
Review 0003 (Q21) verified by code-read that every harness config named the
|
|
13
|
+
bare `simt` binary. A devDependency install puts `simt` in
|
|
14
|
+
`node_modules/.bin`, not on a harness's PATH, so that entry runs nothing on
|
|
15
|
+
Claude — a non-2 exit is silent: no enforcement — and blocks every tool call
|
|
16
|
+
on Cursor, whose entry sets `failClosed: true`. The install wrote a command
|
|
17
|
+
that worked only on machines that happened to have the binary on PATH, which
|
|
18
|
+
are exactly the machines that least need the hook.
|
|
19
|
+
|
|
20
|
+
## Ruling
|
|
21
|
+
|
|
22
|
+
1. **Every harness config names the repo shim, never the binary.**
|
|
23
|
+
`agent-hooks install` writes `.simt-hooks/agent-hook` beside the git shims,
|
|
24
|
+
rendered with the same `renderLocator` block from `src/locate.mjs`, and
|
|
25
|
+
every entry's command is `<shim> <event> --harness <id>`. The shim takes its
|
|
26
|
+
arguments through (`agent-hook "$@"`), so one file serves every event and
|
|
27
|
+
every harness, and `--harness` stays explicit rather than detected.
|
|
28
|
+
2. **`failClosed` is set only because the command is the shim.** Cursor keeps
|
|
29
|
+
`failClosed: true` on entries that point at the shim. It is never set on a
|
|
30
|
+
bare-PATH binary: that combination blocks every tool call on the machines
|
|
31
|
+
where the binary is not installed, which is exactly the machines the shim
|
|
32
|
+
exists for.
|
|
33
|
+
3. **A shim that cannot resolve refuses.** When no binary is reachable the shim
|
|
34
|
+
exits 1 with a remedy, the way the git shims do. On Claude that is still a
|
|
35
|
+
non-2 exit — silent, no enforcement — and that limitation is accepted and
|
|
36
|
+
stated here rather than hidden: the shim fixes the machines where `simt`
|
|
37
|
+
is installed but not on PATH, not the machines where it is absent.
|
|
38
|
+
|
|
39
|
+
## Consequences
|
|
40
|
+
|
|
41
|
+
- `ours()` recognises both spellings, so an install over a pre-shim config
|
|
42
|
+
migrates the entry in place instead of stacking a second one.
|
|
43
|
+
- The shim is journaled with the configs, so `undo` removes or restores it
|
|
44
|
+
with the run that wrote it.
|
|
45
|
+
- A hand-written bare-`simt` entry is the author's, not this tool's: `status`
|
|
46
|
+
does not claim it, and `install` replaces it.
|
package/docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["src/commands/agenthook.mjs", "src/commands/help.mjs", "test/agenthooks.test.mjs"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-107 — an install can be narrowed to one harness, and the skipped ones are named
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-30
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
`agent-hooks install` wrote every harness that documents a hooks file. A
|
|
13
|
+
repository whose agents run in one of them received entries in the others'
|
|
14
|
+
files as well — files no agent there reads, which a later `status` then
|
|
15
|
+
reports PRESENT while nothing in that harness enforces anything. For a
|
|
16
|
+
one-harness repository the install wrote more files than it needed to, and
|
|
17
|
+
there was no way to ask for fewer.
|
|
18
|
+
|
|
19
|
+
ADR-079 ruling 11 settled that the readers this tool does not write are
|
|
20
|
+
*named* in the report — a command that writes four files and stays silent
|
|
21
|
+
about the fifth reads as covering it. A narrowed install creates a third
|
|
22
|
+
category that ruling did not anticipate: readers this tool *can* write and
|
|
23
|
+
did not.
|
|
24
|
+
|
|
25
|
+
## Ruling
|
|
26
|
+
|
|
27
|
+
1. **`agent-hooks install --harness <id>` writes that one reader.** The id is
|
|
28
|
+
validated against the adapter table before anything is planned, and an
|
|
29
|
+
unknown id is a usage error that names the valid ids — the same validation
|
|
30
|
+
`agent-hook` applies, with the same remedy. The shim is written as before:
|
|
31
|
+
every config points at it, and a one-harness install would leave a config
|
|
32
|
+
pointing at a shim that is not there.
|
|
33
|
+
2. **The skipped adapters are named in both surfaces.** The text report
|
|
34
|
+
prints a `SKIPPED` line per skipped reader with a note that says how to
|
|
35
|
+
write them all, and the JSON document carries them in `notWritten` with
|
|
36
|
+
`state: "skipped"`. A narrowed install that stays silent about the readers
|
|
37
|
+
it left alone is the failure ADR-079 ruling 11 ruled out for the full one.
|
|
38
|
+
3. **`status` is unchanged.** It reports what each harness *will* run, and a
|
|
39
|
+
file that was never written is `ABSENT` there — the true answer. The
|
|
40
|
+
narrowing is a choice the install made, and the install's report is where
|
|
41
|
+
that choice is recorded.
|
|
42
|
+
|
|
43
|
+
## Consequences
|
|
44
|
+
|
|
45
|
+
- A repository that installs for one harness and later adds a second runs
|
|
46
|
+
install again — with the new id, or without any — and the merge creates the
|
|
47
|
+
entry in the new file while the files it already wrote come back
|
|
48
|
+
`UNCHANGED`. Nothing about a narrowed install has to be undone.
|
|
49
|
+
- The prompt opt-in hint prints only when the selected harness can receive a
|
|
50
|
+
packet; a narrowed install to a harness with no prompt event does not ask
|
|
51
|
+
for an opt-in it cannot use.
|
package/docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["src/commands/init.mjs", "src/commands/map.mjs", "test/init.test.mjs", "test/map.test.mjs"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-108 — an adoption builds the index, and a command that reads it refreshes it on read
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-30
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
The index is a cache of git blobs (ADR-089): gitignored, rebuildable from
|
|
13
|
+
`git ls-files -s`, and the data source for `simt map`, `simt brief` and
|
|
14
|
+
`simt slice`. Two states of that cache were wrong for an adopted repository.
|
|
15
|
+
|
|
16
|
+
First, `simt init` adopted a repository and left the index unbuilt. The
|
|
17
|
+
first `simt map` in a fresh adoption paid for a walk of the whole tree, and
|
|
18
|
+
the prompt-submit hook delivered a cold index — "the index is empty" — until
|
|
19
|
+
somebody happened to run `simt index`. An adoption that promises the tool
|
|
20
|
+
works in that repository should leave it able to answer.
|
|
21
|
+
|
|
22
|
+
Second, once built, the cache went stale silently. `loadGraph` rebuilt only
|
|
23
|
+
when the index was *empty*, so a file added after the build was invisible to
|
|
24
|
+
every command that reads the index, with no way to tell from their output
|
|
25
|
+
that they were answering from an old tree.
|
|
26
|
+
|
|
27
|
+
## Ruling
|
|
28
|
+
|
|
29
|
+
1. **`init` builds the index in the apply phase**, after the manifest is
|
|
30
|
+
written — `reindex` reads it for the declared parsers, so the manifest has
|
|
31
|
+
to exist first. The plan names the file it will build, and the JSON
|
|
32
|
+
document carries the result. A failure is a *note*, not a failed adoption:
|
|
33
|
+
the index is a cache, and an adopted repository whose index build failed
|
|
34
|
+
is better than one that refused to be adopted for it. The next read
|
|
35
|
+
rebuilds what the apply could not.
|
|
36
|
+
2. **A command that reads the index refreshes it on read.** `loadGraph` with
|
|
37
|
+
`build` (the default) reindexes from the git index before it reads. The
|
|
38
|
+
refresh is incremental — a blob that has not changed is not re-read — so
|
|
39
|
+
the cost of a current repository is one `git ls-files -s`, not a walk of
|
|
40
|
+
the tree. The callers that build are commands a person typed: `map`,
|
|
41
|
+
`brief`, `slice`.
|
|
42
|
+
3. **The `build: false` callers are unchanged.** The prompt-submit hook (R29)
|
|
43
|
+
and the MCP surface report a cold index rather than build one: a delivered
|
|
44
|
+
packet that walked the whole repository would make a person's first prompt
|
|
45
|
+
in a fresh clone wait, and a prompt that takes a minute to appear is how a
|
|
46
|
+
hook gets uninstalled.
|
|
47
|
+
|
|
48
|
+
## Consequences
|
|
49
|
+
|
|
50
|
+
- Amends ADR-091's consequence that "`simt map` reads the index and rebuilds
|
|
51
|
+
it when the cache is empty": the refresh now happens on every read, not
|
|
52
|
+
only on an empty one.
|
|
53
|
+
- In an adopted repository the prompt hook can deliver a packet from the
|
|
54
|
+
*first* prompt: `init` has already built the index, so R29's "the first
|
|
55
|
+
prompt gets nothing, the second gets the packet" no longer applies to a
|
|
56
|
+
repository that was adopted rather than cloned.
|
|
57
|
+
- `simt why` is unchanged: it deliberately does not write the cache to answer
|
|
58
|
+
a question, because writing a cache to answer a question would change the
|
|
59
|
+
tree.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["AGENTS.md", "rules/core.md", "rules/stacks/node.md", "rules/stacks/python.md", "rules/stacks/connectiq.md", "src/projection/project.mjs", "test/baselines.test.mjs", "test/upgrade.test.mjs"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-109 — simt-dev is its own consumer, and the guards that kept it from being one are retired
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-30
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
This repository ran `simt init` on itself, and the suite still believed the
|
|
13
|
+
opposite. Two tests asserted that simt-dev is *not* a consumer of itself — no
|
|
14
|
+
manifest, no region — and `AGENTS.md` explained why self-adoption was
|
|
15
|
+
impossible: the region's stamp records a SHA, so every commit would make the
|
|
16
|
+
file stale.
|
|
17
|
+
|
|
18
|
+
That explanation is no longer true. The `simt:sha=` stamp is a digest of the
|
|
19
|
+
rule *content* — `rules/` and the baselines — not of this repository's HEAD.
|
|
20
|
+
A commit here does not move it, so `check` stays green until the rule text
|
|
21
|
+
itself changes, and `simt sync --yes` re-renders it when that happens.
|
|
22
|
+
|
|
23
|
+
## Ruling
|
|
24
|
+
|
|
25
|
+
1. **This repository is a consumer of itself.** `simt.json` declares its
|
|
26
|
+
gates, and the generated region in `AGENTS.md` carries the rules it
|
|
27
|
+
projects. The two tests that asserted the opposite are rewritten to assert
|
|
28
|
+
the new state: the baselines exist with the region first, and an `upgrade`
|
|
29
|
+
from its own source is a no-op that moves no stamp.
|
|
30
|
+
2. **The rule text names the manifest by the name that resolves in every
|
|
31
|
+
repository.** Four rule files said `simt-dev.json`, the pre-rename name;
|
|
32
|
+
ADR-056 reads both and writes the new one, so a reader in any repository
|
|
33
|
+
finds only `simt.json`. The text now says `simt.json`.
|
|
34
|
+
3. **A gate finding whose `detail` is absent is `null`, not `undefined`.** A
|
|
35
|
+
key with an `undefined` value survives into the structured MCP surface but
|
|
36
|
+
is dropped by `JSON.stringify`, so the two surfaces of one answer no longer
|
|
37
|
+
deep-equal. The four constructions of a region verdict now share one helper
|
|
38
|
+
that emits `null`.
|
|
39
|
+
|
|
40
|
+
## Consequences
|
|
41
|
+
|
|
42
|
+
- The suite's view of this repository is the adopted one: `check` here is
|
|
43
|
+
green until the rule content changes, and the baselines test asserts the
|
|
44
|
+
region-first placement that keeps a hand-written setting below it winning
|
|
45
|
+
(ADR-025).
|
|
46
|
+
- `AGENTS.md`'s hand-written section now says what is true: the repository
|
|
47
|
+
adopts itself, and the stamp is a content digest.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["src/name.mjs", "src/projection/lock.mjs", "src/kernel/variants.mjs", "src/commands/sync.mjs", "src/commands/help.mjs", "src/bench/variants.mjs", "test/lock.test.mjs", "test/variants.test.mjs"]
|
|
3
|
+
simt-supersedes: [{ adr: "ADR-086", ruling: 3, kind: "superseded" }]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-110 — the lock is `simt.lock.json`, and the old name is read until the next write
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-30 · supersedes ADR-086 ruling 3's file name
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
The lock followed the command's old name: `simt-dev.lock.json`. The command
|
|
13
|
+
is `simt`, the manifest is `simt.json`, and the lock was the one remaining
|
|
14
|
+
file that still carried the pre-rename spelling.
|
|
15
|
+
|
|
16
|
+
A rename cannot be a bare move. A lock written by a pre-rename run may hold
|
|
17
|
+
`variants` winners that nothing else records — the bench evidence for a
|
|
18
|
+
locked rule text — so a reader that only knows the new name would silently
|
|
19
|
+
unlock those decisions.
|
|
20
|
+
|
|
21
|
+
## Ruling
|
|
22
|
+
|
|
23
|
+
1. **The lock is `simt.lock.json`.** The old name is a read-only fallback:
|
|
24
|
+
a reader tries the new name first and falls back to the old one, and a
|
|
25
|
+
write always lands under the new name, which is how a legacy lock
|
|
26
|
+
migrates. The fallback is for absence, not for damage: a permission
|
|
27
|
+
failure on the new name does not fall back, and a file that exists but
|
|
28
|
+
does not parse is reported, not repaired by an older file. This is the
|
|
29
|
+
same read-both/write-new pattern as the manifest rename (ADR-056).
|
|
30
|
+
2. **The name lives in one place.** `src/name.mjs` owns both names, the way
|
|
31
|
+
it owns the manifest's. The kernel and the projection each carried a
|
|
32
|
+
private copy — the kernel cannot import the projection, because the
|
|
33
|
+
projection imports the kernel — and two constants for one file is how a
|
|
34
|
+
rename ships half-done.
|
|
35
|
+
|
|
36
|
+
## Consequences
|
|
37
|
+
|
|
38
|
+
- A repository whose lock predates the rename keeps its locked variant
|
|
39
|
+
winners: `simt check --frozen` and the projection read them from the old
|
|
40
|
+
file, and the next `simt sync --yes` or `simt bench variants --yes` writes
|
|
41
|
+
them back under the new name.
|
|
42
|
+
- ADR-086's ruling 3 stands for what the lock records — an input digest and
|
|
43
|
+
an output digest; the file name it named is superseded.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["agents/simt-agent.md", "src/agents/render.mjs", "src/commands/agent.mjs", "src/commands/init.mjs", "src/projection/loaders.mjs", "src/toolwords.mjs", "test/agents.test.mjs"]
|
|
3
|
+
simt-supersedes: []
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-111 — the tool ships one agent, rendered per harness, and installs it with init and a command
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-30
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
The rules this tool projects tell an agent *what* to obey; nothing told it
|
|
13
|
+
*how to act* through the tool. The plan for that was two agents — a planner
|
|
14
|
+
and a builder — but the two jobs are one job in practice: read the tracker,
|
|
15
|
+
take the next slice, build it, test it, commit it. Two files that both say
|
|
16
|
+
"run the simt commands" are two copies of the same logic, and the second copy
|
|
17
|
+
is where the next divergence grows.
|
|
18
|
+
|
|
19
|
+
Six harnesses read a subagent and no two agree on the file. Five write a
|
|
20
|
+
markdown file with YAML frontmatter and a body that becomes the system
|
|
21
|
+
prompt; Codex writes a TOML file whose `developer_instructions` holds the
|
|
22
|
+
same body. The path, the extension, the frontmatter keys and the document
|
|
23
|
+
shape are all different, and none of them is the same fact.
|
|
24
|
+
|
|
25
|
+
A hooks entry is a config key the tool merges into a file it does not own.
|
|
26
|
+
An agent is a file the tool owns whole: there is nothing to merge, and the
|
|
27
|
+
only question is whether the bytes on disk are the bytes the tool would write
|
|
28
|
+
now.
|
|
29
|
+
|
|
30
|
+
## Ruling
|
|
31
|
+
|
|
32
|
+
1. **The tool ships one agent, `Simt-Agent`.** Its body is the system prompt
|
|
33
|
+
and it encodes how to act with simt-dev across the whole slice lifecycle —
|
|
34
|
+
orient (`simt brief`, `simt why`), plan (`simt plan status`, `simt plan
|
|
35
|
+
next`), build (`simt slice start`, the focused test, `simt slice finish`,
|
|
36
|
+
`simt plan done`) and the boundaries that hold everywhere. Planning and
|
|
37
|
+
building are one agent because they are one job.
|
|
38
|
+
2. **The source of truth is one file, `agents/simt-agent.md`.** Neutral
|
|
39
|
+
frontmatter (`name`, `description`) plus a harness-agnostic body. The
|
|
40
|
+
renderer strips that frontmatter and re-emits the keys each harness
|
|
41
|
+
documents, so the body — the part that says how to act — is written once
|
|
42
|
+
and cannot drift between harnesses. The `simt-agent` stem is a name this
|
|
43
|
+
tool owns, and it is derived into the tool-word set from the module that
|
|
44
|
+
owns it, the way the other prefixed names are.
|
|
45
|
+
3. **An agent is a file the tool owns whole, so the install is a byte
|
|
46
|
+
comparison.** `rendered` produces the text and reports `created`,
|
|
47
|
+
`unchanged` or `updated`. There is no parse that can fail and no partial
|
|
48
|
+
state, which is what makes the command shorter than `agent-hooks install`.
|
|
49
|
+
4. **It is installed per harness, and the readers it does not write are
|
|
50
|
+
named.** Six harnesses document an agents file and receive one; the two
|
|
51
|
+
that document none are reported `NO FILE` with the reason, the way
|
|
52
|
+
ADR-079 ruling 11 requires. `--harness <id>` narrows the install to one
|
|
53
|
+
reader and names the skipped ones (ADR-107), and `status` reports
|
|
54
|
+
present, absent, drift, and the readers with no file.
|
|
55
|
+
5. **It is written by `simt init` and by a separate `simt agents install`.**
|
|
56
|
+
`init` plans the agent files with the rest of the adoption and writes them
|
|
57
|
+
in the apply path, journaled the same way, so `undo` restores them with the
|
|
58
|
+
rest. The separate command exists for a repository that is already adopted
|
|
59
|
+
and wants only the agent, and for a narrowed install. Both call the same
|
|
60
|
+
`planAgents` and `writeAgentPlan`, so a dry run and a run describe the same
|
|
61
|
+
set of files.
|
|
62
|
+
|
|
63
|
+
## Consequences
|
|
64
|
+
|
|
65
|
+
- A repository that is adopted and later upgrades the tool's agent body runs
|
|
66
|
+
`simt agents install --yes` (or re-runs `init`) and the files come back
|
|
67
|
+
`updated` with the new body; the frontmatter each harness documents is
|
|
68
|
+
re-emitted from the same source, so it cannot drift.
|
|
69
|
+
- The agent body names only commands a consumer repository runs — `simt
|
|
70
|
+
brief`, `simt why`, `simt plan`, `simt slice` — so every byte in it is
|
|
71
|
+
actionable in the receiving repository, the rule this repository exists to
|
|
72
|
+
enforce.
|
|
73
|
+
- `agents/` is published with the package: the tool reads its own agent body
|
|
74
|
+
from the installed copy, and a tarball without it would install cleanly and
|
|
75
|
+
then fail on the one command that writes the agent.
|
package/docs/decisions/ADR-112-the-registry-is-what-simt-dev-ships-and-a-repo-owns-its-own-skills.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-affects: ["graph/skills.graph.json", "src/resolve/local.mjs", "src/commands/skills.mjs", "src/projection/disclosure.mjs", "src/resolve/body.mjs"]
|
|
3
|
+
simt-supersedes: [{ adr: "ADR-009", ruling: 4 }]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-112 — the registry is what simt-dev ships, and a repo owns its own skills
|
|
7
|
+
|
|
8
|
+
Status: **accepted** · 2026-10-31 · filed as S4 (plan 0005) · supersedes ADR-009 ruling 4
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
ADR-009 settled the layout: a skill is `<name>/SKILL.md` with mandatory `name`
|
|
13
|
+
and `description` frontmatter, and body size and reference count are declared in
|
|
14
|
+
`graph/skills.graph.json`, "so the registry stays the single source of truth for
|
|
15
|
+
resolution metadata". The layout half of that ruling was about one estate's
|
|
16
|
+
thirty-nine skills, and the metadata half assumed the estate was the universe: a
|
|
17
|
+
skill not in the graph is a skill that does not exist.
|
|
18
|
+
|
|
19
|
+
Plan 0005 broke that assumption in two slices. S2 removed the thirty-one
|
|
20
|
+
repo-specific entries from the graph, leaving the eight skills this tool ships —
|
|
21
|
+
`simt-commands` and seven vendored stack skills. A graph that describes one
|
|
22
|
+
estate and is read by everyone was ADR-061's finding; a graph that is one
|
|
23
|
+
estate's whole skill population is a stronger claim ADR-061 did not make, and S2
|
|
24
|
+
made it false. S3 then gave the repo's own skill directories standing in the
|
|
25
|
+
resolver: `skills find`, `brief`, `show` and `why` answer for a skill the
|
|
26
|
+
registry never declared, synthesising its node from the file — name and summary
|
|
27
|
+
from frontmatter, size from the file, body from the path (commit 7d44071).
|
|
28
|
+
|
|
29
|
+
A reader of ADR-009 acting on the metadata sentence concludes that a skill absent
|
|
30
|
+
from the graph has no resolution metadata, and therefore cannot resolve. That
|
|
31
|
+
conclusion is now false in every repo that keeps its own skills, and a record
|
|
32
|
+
that says what the tool no longer believes is the failure this directory exists
|
|
33
|
+
to prevent.
|
|
34
|
+
|
|
35
|
+
## Ruling
|
|
36
|
+
|
|
37
|
+
1. **The graph is a catalogue of what simt-dev ships, not the sole home of
|
|
38
|
+
skills.** Membership in `graph/skills.graph.json` is a statement about the
|
|
39
|
+
package, not about existence. A skill a repository installed in its own skill
|
|
40
|
+
directories exists and resolves with no entry in the graph.
|
|
41
|
+
2. **A repo's skill directories are a first-class source of resolution.**
|
|
42
|
+
Presence is the eligibility signal — a skill the repo installed is about the
|
|
43
|
+
repo, and the stack it belongs to is the stack the repo has. Frontmatter
|
|
44
|
+
supplies the name and the summary; the file supplies the body and the size.
|
|
45
|
+
This is the mechanism S3 shipped; the ruling records it so the reader learns
|
|
46
|
+
what the tool believes without reading the plan.
|
|
47
|
+
3. **Where a name is in both, the registry entry is canonical.** The graph entry
|
|
48
|
+
and the local file are one skill — the file is the installed copy of what the
|
|
49
|
+
graph declares — and keeping both would rank one skill twice. The graph wins
|
|
50
|
+
the dedupe; the local node is dropped, not merged.
|
|
51
|
+
|
|
52
|
+
## Consequences
|
|
53
|
+
|
|
54
|
+
- ADR-009's layout ruling stands and is load-bearing: discovery reads `name` and
|
|
55
|
+
`description` from frontmatter, so a local skill without them is invisible,
|
|
56
|
+
which is the defect class ADR-009 exists to prevent. What moves is the metadata
|
|
57
|
+
sentence: a local skill's resolution metadata is synthesised from the
|
|
58
|
+
filesystem, not read from the graph, so the graph is no longer the single
|
|
59
|
+
source of truth for it.
|
|
60
|
+
- `skills find`, `brief`, `show` and `why` answer for names the registry never
|
|
61
|
+
declared. The NOT SELECTED block no longer points a reader at a `skills show
|
|
62
|
+
<name>` that 404s for a name the graph does not carry.
|
|
63
|
+
- Nothing in the shipped bytes changes. ADR-061 stands as written: the registry
|
|
64
|
+
is workspace data, the package ships none of it, and a command that finds no
|
|
65
|
+
registry says so. This record settles the half ADR-061 left open — who is
|
|
66
|
+
allowed to have skills the catalogue does not name — without touching its
|
|
67
|
+
rulings.
|
|
68
|
+
- `skills index` keeps reporting the catalogue only: it is the tier-0 view of
|
|
69
|
+
what this tool ships, and local skills surface where a repo is the context,
|
|
70
|
+
which is `find` and `brief`.
|
|
71
|
+
|
|
72
|
+
## Rejected
|
|
73
|
+
|
|
74
|
+
- **Rewrite ADR-009 in place.** The supersession rule this directory opens with
|
|
75
|
+
is a new record and an edited status line, never a rewritten ruling. ADR-009's
|
|
76
|
+
audit data — the forty-six frontmatter tallies that found the flat file — is
|
|
77
|
+
evidence its own ruling relies on, the same shape that kept ADR-011 standing
|
|
78
|
+
when ADR-012 reversed it.
|
|
79
|
+
- **Annotate ADR-009 without a record.** The supersession is a claim about what
|
|
80
|
+
the tool believes, and a claim without a record is a ruling nobody can cite.
|
|
81
|
+
The annotation points at ADR-112; ADR-112 carries the argument.
|