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
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# ADR-083 — A budget is met by cutting the kernel, and the hard constraints are the floor
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-17 · filed as R08 (plan 0003) · ruling 7 narrowed by ADR-084
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/kernel/budget.mjs`, `src/projection/rules.mjs`, `src/projection/manifest.mjs`,
|
|
6
|
+
`src/measure/context.mjs`, `src/projection/project.mjs`, `src/commands/sync.mjs`,
|
|
7
|
+
`test/kernel.test.mjs`, `test/tokens.test.mjs`, `test/surfaces.test.mjs`
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
ADR-072 shipped the number: `budget.bytes` at the top level of the manifest, 30 000 bytes by
|
|
12
|
+
default, reported by `simt tokens` and raised by `check`, both non-blocking. It recorded what had
|
|
13
|
+
not landed and why — the packing needed a kernel, and the kernel was R07.
|
|
14
|
+
|
|
15
|
+
ADR-073 shipped the kernel. A repository declares `mode: kernel`, every rule declares its class
|
|
16
|
+
in its own frontmatter, and the mode packs `hard` and `command` while naming everything else in
|
|
17
|
+
the region with the command that fetches it. R08's accept criterion — the budget is never
|
|
18
|
+
exceeded — is now reachable, and this record is the part that reaches it.
|
|
19
|
+
|
|
20
|
+
Three facts about the number made the shape of this a decision rather than a loop.
|
|
21
|
+
|
|
22
|
+
The first is that the budget is one number and the meter bills more than the region. A generated
|
|
23
|
+
rule file is billed whole, a managed skill by its name and description, the region by the span
|
|
24
|
+
between its markers. A repository whose rules live in per-rule files has paid for those files
|
|
25
|
+
whether or not the region fits; packing the region against the whole declared number ships a
|
|
26
|
+
projection that fits on its own and is over the line the moment its rule files are written. On a
|
|
27
|
+
fixture with a Cursor surface, three `.mdc` files billed 6,552 B beside a 15,725 B region.
|
|
28
|
+
|
|
29
|
+
The second is the region's own span. It is the one number that moves when the projection is
|
|
30
|
+
packed, so a ceiling derived from it would make `sync` rewrite the file it had just written and
|
|
31
|
+
then report drift on a clean tree.
|
|
32
|
+
|
|
33
|
+
The third is what ADR-072 already ruled and R08's text has not caught up with: the key is
|
|
34
|
+
`budget.bytes`, not `projection.budget.bytes`. The plan is not edited. `projection` is a
|
|
35
|
+
generated key, `upgrade` replaces it wholesale, and a budget written there would disappear on a
|
|
36
|
+
green gate.
|
|
37
|
+
|
|
38
|
+
## Rulings
|
|
39
|
+
|
|
40
|
+
1. **One reader for the declared number.** `declaredBudget(manifest)` in `manifest.mjs` is the
|
|
41
|
+
only place `budget.bytes` is interpreted, and both the packer and `measureRoot` call it. A
|
|
42
|
+
finite non-negative value is taken as declared, including `0` — "this repository wants an
|
|
43
|
+
empty projection" is a decision somebody can make — and anything else (a string, `NaN`, a
|
|
44
|
+
negative) falls back to the review figure, because the one reading that cannot silently
|
|
45
|
+
produce compliance is the one where a typo is not a ceiling.
|
|
46
|
+
|
|
47
|
+
2. **Nothing is cut in `full` mode.** ADR-072's finding already tells the reader "nothing was cut:
|
|
48
|
+
the projection is still in full mode", and a full projection that quietly lost a rule would
|
|
49
|
+
make the tool lie about itself in its own words. `desiredRegion` returns `budget: null` there,
|
|
50
|
+
not an empty cut list, so a reader of the field cannot conclude a cut happened when the mode
|
|
51
|
+
made none possible.
|
|
52
|
+
|
|
53
|
+
3. **`hard` is the floor.** A priority class exists to say what a reader loses by not having the
|
|
54
|
+
line, and a byte count is not a reader. The packer skips `hard` instead of cutting it, and the
|
|
55
|
+
leftover is a distinct fact — `floorHeld` — reported as such: the notes say the kernel is still
|
|
56
|
+
over after every rule outside the hard class went, and that a hard constraint is not cut to
|
|
57
|
+
reach a byte count. The levers that remain are the repository's own, which is exactly what
|
|
58
|
+
ADR-072's kernel wording already tells the reader. One case gets its own sentence: when the
|
|
59
|
+
reserve alone reaches the declared budget the ceiling is zero or less, which is arithmetically
|
|
60
|
+
true and unreadable — it does not say that the files loading beside the region already spent
|
|
61
|
+
the whole budget — so the notes say that, because a reader left to infer it from a subtraction
|
|
62
|
+
is a reader who demotes a hard rule to fix a number they set too low.
|
|
63
|
+
|
|
64
|
+
4. **The ceiling is the declared budget minus a reserve, and the region's own span is in
|
|
65
|
+
neither term.** The reserve is the larger of what the meter already bills outside the region
|
|
66
|
+
(what is installed) and what this projection is about to write as rule files (what a
|
|
67
|
+
repository that has not run `sync` yet will pay). Both are lower bounds; the larger is taken
|
|
68
|
+
because the failure each would otherwise cause is the one that must not happen — under-
|
|
69
|
+
reserving reports compliance for a projection that is over. The region's span is excluded
|
|
70
|
+
deliberately, per the second fact above. A folded single-surface repository reserves nothing,
|
|
71
|
+
which is correct rather than untested: `opencode` reads `AGENTS.md` and writes no rule files,
|
|
72
|
+
so there is nothing beside the region to pay for.
|
|
73
|
+
|
|
74
|
+
5. **The cut order is the region's own order reversed, and it is fixed before any cost is
|
|
75
|
+
computed.** Class, then rule id — the comparator the deferred section already uses, least
|
|
76
|
+
valuable first. Which class a reader can best afford to lose is declared by the rules
|
|
77
|
+
themselves (ADR-073 ruling 3); what is left here is arithmetic, so the order is handed in and
|
|
78
|
+
the module that packs does not second-guess it.
|
|
79
|
+
|
|
80
|
+
6. **A cut costs what the document then weighs, not the rule's own length.** Three things move
|
|
81
|
+
when a rule leaves: the provenance header drops a source, the deferred section gains a line
|
|
82
|
+
naming what left, and both move again as the cut list grows. So each attempt re-renders the
|
|
83
|
+
region and weighs it — `costs(cut)` returning `Buffer.byteLength(compose(...))`, markers and
|
|
84
|
+
provenance header included, one byte conservative because the file's trailing newline falls
|
|
85
|
+
outside the meter's span and conservative is the only direction a ceiling can afford. The loop
|
|
86
|
+
stops the moment it fits, so a budget never costs a rule it did not have to. This is the
|
|
87
|
+
command card's `CARD_LIMIT_BYTES` idiom (ADR-077) applied to the region instead of the card.
|
|
88
|
+
|
|
89
|
+
7. **One list, two reasons.** Class-deferred and budget-cut rules share the region's existing
|
|
90
|
+
`## Deferred rules` section, and each line says which it was: `deferred in kernel mode` or
|
|
91
|
+
`cut to fit the declared budget of N bytes`, both naming `simt rules show <id>`. A second
|
|
92
|
+
heading would spend region bytes on a distinction the reader acts on identically, and a cut
|
|
93
|
+
line that did not say why would be indistinguishable from a kernel decision nobody made.
|
|
94
|
+
|
|
95
|
+
8. **`BUDGET-CUT` is a finding, non-blocking in both commands, and carries the numbers a reader
|
|
96
|
+
needs to judge it.** The detail gives `before → after`, the declared budget, the reserve when
|
|
97
|
+
it is not zero, and every cut rule by id and class; the remedy is a decision
|
|
98
|
+
(`raise budget.bytes to <before + reserve>`, the budget that would have admitted the region
|
|
99
|
+
as it stood — `before` alone would leave the cut in place, because the ceiling it was cut
|
|
100
|
+
against is the budget less the reserve), not a place to go and look. Non-blocking for ADR-072's
|
|
101
|
+
reason: the cut is the tool doing what the repository asked, and a gate that failed here would
|
|
102
|
+
fail a repository for shipping a smaller projection. `sync` prints the same two facts as notes
|
|
103
|
+
so the cut is never silent from either command.
|
|
104
|
+
|
|
105
|
+
9. **`simt tokens` keeps no second opinion.** ADR-064 kept the meter to one measurement, and the
|
|
106
|
+
packer's arithmetic is not substituted for it anywhere: the meter reports the bytes it finds
|
|
107
|
+
against the declared line, and the end-to-end test judges the cut with the meter's number
|
|
108
|
+
rather than the packer's. A packer that reports compliance for a projection the meter says is
|
|
109
|
+
over budget is the failure this arrangement is arranged against, so the test asserts the
|
|
110
|
+
meter's number even where the packer's own numbers are already visible in the same object.
|
|
111
|
+
|
|
112
|
+
## Consequences
|
|
113
|
+
|
|
114
|
+
A repository that declares `mode: kernel` and a budget its rules do not fit gets a smaller
|
|
115
|
+
projection, a list of what left and why, and a note from both `sync` and `check`. The accept
|
|
116
|
+
criterion is checkable end to end rather than asserted: a fixture declares a budget a thousand
|
|
117
|
+
bytes under its own projection, syncs, and `simt tokens` reports `over: false` with the declared
|
|
118
|
+
line intact — and a second fixture, whose rule files are billed beside the region, is cut further
|
|
119
|
+
than the region alone would have needed, which is the only way the reserve can be seen to work.
|
|
120
|
+
|
|
121
|
+
The costs are accepted rather than avoided. In kernel mode a projection is now measured
|
|
122
|
+
(`measureRoot`) and its rule targets rendered a second time (`ruleTargets`) on every
|
|
123
|
+
`desiredRegion`, where previously the region's bytes were composed once. `measureRoot` is reached
|
|
124
|
+
through `await import` inside `budgetReserve` because `measure/context.mjs` already imports
|
|
125
|
+
`readLimitFindings` from `projection/project.mjs`; a static import from `rules.mjs` would close a
|
|
126
|
+
three-module loop that has to survive being loaded in any order.
|
|
127
|
+
|
|
128
|
+
Determinism is structural and partly argued. `packToBudget` takes its cost function as an
|
|
129
|
+
argument and reads no clock, no network and no PATH, so the cut set is a function of the rules,
|
|
130
|
+
the classes, the ids and the declared number; the test that renders one repository twice catches
|
|
131
|
+
the failure a test can catch, which is state leaking between calls. Two machines is the claim
|
|
132
|
+
R08 actually makes and no test in this repository can execute it — the argument is the one ADR-073
|
|
133
|
+
ruling 7 already makes.
|
|
134
|
+
|
|
135
|
+
What has not landed, and is not this slice's to land: R02's evaluation, which is still what would
|
|
136
|
+
justify `kernel` as a default rather than a repository's own choice; and the cross-machine half
|
|
137
|
+
of the accept criterion, which is argued from the code's inputs rather than demonstrated.
|
|
138
|
+
|
|
139
|
+
## Rejected
|
|
140
|
+
|
|
141
|
+
- **Cutting in `full` mode.** The mode is a promise about what a reader gets, and the number in
|
|
142
|
+
ADR-072's finding says nothing was cut; making that sentence untrue to save bytes is the tool
|
|
143
|
+
misreporting itself.
|
|
144
|
+
- **Cutting `hard` to reach the line.** It would remove the one class whose claim is that its
|
|
145
|
+
absence is a defect, and report the result as compliance.
|
|
146
|
+
- **A second `## Cut` section beside `## Deferred rules`.** Two headings for one reader-facing
|
|
147
|
+
fact, and the second one spends bytes to be distinguished from the first.
|
|
148
|
+
- **Subtracting each rule's own length instead of re-rendering.** The provenance header and the
|
|
149
|
+
deferred line both change when a rule leaves, so the arithmetic would either overshoot or stop
|
|
150
|
+
short — and it would be a second definition of the region's size next to the meter's.
|
|
151
|
+
- **A binary search or two-pass packer.** The cost function is called once per candidate and the
|
|
152
|
+
candidate list is the length of the kernel; a cleverer search would add a failure mode to a
|
|
153
|
+
loop whose whole value is that it is the same computation every time.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# ADR-084 — A workspace carries its repeated rules once, and the region does not read the reader's home
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-17 · filed as R11 (plan 0003)
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/projection/shared.mjs`, `src/commands/shared.mjs`, `src/projection/rules.mjs`,
|
|
6
|
+
`src/projection/manifest.mjs`, `src/projection/markers.mjs`, `src/commands/tokens.mjs`,
|
|
7
|
+
`src/commands/index.mjs`
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
The same rules are projected into every repository of a workspace, and each repository's
|
|
12
|
+
region carries the full text. The plan asked for the intersection to be installed once, at
|
|
13
|
+
user level, and for a repository to switch to the remainder when its manifest said
|
|
14
|
+
`projection.shared`. It also named home paths for four harnesses, and said CI should keep
|
|
15
|
+
rendering the full region so a teammate without the user file loses nothing in review.
|
|
16
|
+
|
|
17
|
+
Two of those would not survive contact with the rest of the tool. `projection` is generated:
|
|
18
|
+
`upgrade` replaces it, which is why a repository's own budget already lives beside it rather
|
|
19
|
+
than inside it. And a region that is full in CI and short on a reader's machine is a region
|
|
20
|
+
`check` cannot agree with, because `check` renders the same function the reader gets.
|
|
21
|
+
|
|
22
|
+
## Rulings
|
|
23
|
+
|
|
24
|
+
1. **The unit that is shared is a rule, and only a rule every repository renders with the
|
|
25
|
+
same bytes.** The meter can report a repeated paragraph; it cannot store one, and
|
|
26
|
+
`simt rules show` prints one rule. A rule one repository does not ship, or renders
|
|
27
|
+
differently, stays in every region. The comparison is the rendered text, because one
|
|
28
|
+
repository can drop a line the others keep.
|
|
29
|
+
|
|
30
|
+
2. **The declaration is `shared` on the manifest, beside `budget` and `mode`.** `level` is
|
|
31
|
+
`user` and `rules` is the list of ids. It is not a key of `projection`, because that
|
|
32
|
+
object is replaced on upgrade. A missing key, a level other than `user`, or an empty
|
|
33
|
+
list means the region carries everything it carried before. A value that is not that
|
|
34
|
+
shape is an error from the one reader the region, `doctor` and `simt shared` share.
|
|
35
|
+
|
|
36
|
+
3. **The file that is written is the one the loader table says the harness reads.** The
|
|
37
|
+
plan named paths. A path the table does not record would be written and never loaded, so
|
|
38
|
+
a surface with no user-level row, and a pointer surface with no loader row, gets a block
|
|
39
|
+
to paste and no path. Where the table names a flat user rules directory, the file is
|
|
40
|
+
`simt.md`. Where a harness reads one of two files and the reader already has a non-empty
|
|
41
|
+
one, that file is the one written. A cap the table states in tokens is reported, not
|
|
42
|
+
compared with a byte count, and a copy over its cap is reported rather than refused.
|
|
43
|
+
|
|
44
|
+
4. **`simt shared plan` writes nothing. `simt shared install` writes only with `--yes`.**
|
|
45
|
+
The write is the user-level file, keeping whatever prose sits outside the generated
|
|
46
|
+
region, and the same declaration in every repository that already has a manifest. A
|
|
47
|
+
repository without one is skipped, with the command that adopts it. A workspace with
|
|
48
|
+
nothing to share writes neither an empty home file nor a declaration. The regions
|
|
49
|
+
change on the next `simt sync`, not during the install.
|
|
50
|
+
|
|
51
|
+
5. **The region is a function of the declaration, not of the reader's home directory.** CI
|
|
52
|
+
and the reader therefore render the same delta. A teammate without the user file loses
|
|
53
|
+
the shared rules from the region; `simt doctor` says so — current, stale, absent,
|
|
54
|
+
unreadable, or no target — and does not fail the gate. The index line names the rule,
|
|
55
|
+
why it is not in the file, and `simt rules show <id>`. It does not name a home path,
|
|
56
|
+
because that path is not true on both machines.
|
|
57
|
+
|
|
58
|
+
6. **One section holds every reason a rule is missing, and its heading names the fact.**
|
|
59
|
+
Class-deferred, cut to a budget, and carried once are three decisions and one fact for
|
|
60
|
+
the reader: the rule is not in this file. The heading is `## Rules not in this region`.
|
|
61
|
+
This narrows ADR-073 ruling 6 and ADR-083 ruling 7, which named that section
|
|
62
|
+
`## Deferred rules` when a class deferral was the only reason to be in it. The rest of
|
|
63
|
+
both rulings stands: one section, each line says which reason, and the remedy is the
|
|
64
|
+
same command.
|
|
65
|
+
|
|
66
|
+
7. **A repository's own profile or pack is not shareable.** It is policy that repository
|
|
67
|
+
owns. Putting it in a home file would change one repository by editing a file every
|
|
68
|
+
sibling reads. Only rules this package ships are candidates, and the shareable set is
|
|
69
|
+
computed before the declaration drops them, so a second install does not shrink the
|
|
70
|
+
intersection against an already-reduced list.
|
|
71
|
+
|
|
72
|
+
8. **`simt tokens` counts the user-level copy once.** `tokens --repos` adds the whole file
|
|
73
|
+
to the estate total, prose outside the region included, because that prose is also
|
|
74
|
+
always-on for the reader. A repository on its own counts the same file, so a single
|
|
75
|
+
root does not look cheaper than the workspace it belongs to.
|
|
76
|
+
|
|
77
|
+
## Consequences
|
|
78
|
+
|
|
79
|
+
`simt shared plan` is the default of `simt shared`, for the same reason `simt skills` lists
|
|
80
|
+
before it installs: the read answers what would happen, and the write needs `--yes`.
|
|
81
|
+
|
|
82
|
+
The plan's accept bar — always-on bytes for the seven-root estate at most 30 KB, and no R
|
|
83
|
+
loss in bench — is **unmeasured**. The tests prove the mechanism on fixtures: identical
|
|
84
|
+
rules leave the region, a divergent rule stays, the reader's own prose survives, and the
|
|
85
|
+
meter counts the copy once. They do not measure the estate, and they do not run the bench.
|
|
86
|
+
|
|
87
|
+
## Rejected
|
|
88
|
+
|
|
89
|
+
- **`projection.shared`.** The next upgrade would delete it, and the region would silently
|
|
90
|
+
grow back.
|
|
91
|
+
- **CI renders the full region while a reader renders the delta.** `check` would report
|
|
92
|
+
drift on every machine that had installed the copy, or would have to read a home
|
|
93
|
+
directory the projection is forbidden to depend on.
|
|
94
|
+
- **The union of rules across repositories.** A rule one repository does not ship is that
|
|
95
|
+
repository's absence, and installing it at user level would give the rule to a reader of
|
|
96
|
+
a repository that declined it.
|
|
97
|
+
- **Writing the paths the plan named when the loader table records a different file, or
|
|
98
|
+
none.** A file the harness does not read is a second copy the reader never sees.
|
|
99
|
+
- **An empty shared file for a workspace with nothing to share.** It would look like a
|
|
100
|
+
decision and contain none.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# ADR-085 — The region ends with the names of what it kept out
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-18 · filed as R12 (plan 0003)
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/projection/disclosure.mjs`, `src/projection/rules.mjs`, `src/graph/spec.mjs`,
|
|
6
|
+
`test/disclosure.test.mjs`, `test/scoped.test.mjs`
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
A profile that declares a scope already leaves the always-on region when every selected
|
|
11
|
+
surface can load it narrowly, and the region keeps a pointer (ADR-071). A package in a
|
|
12
|
+
monorepo already gets its own region (ADR-066). What a reader still cannot see is an
|
|
13
|
+
installed skill: the body lives in its own file, and a surface with no skill channel never
|
|
14
|
+
mentions the name, so the reader has no reason to open it.
|
|
15
|
+
|
|
16
|
+
The plan asked for one index at the end of the kernel, one line per scoped rule and per
|
|
17
|
+
skill, carrying the name and the trigger. The accept is that the scoped body is absent
|
|
18
|
+
from what loads at session start and present in the file a matching path loads, checked
|
|
19
|
+
per surface.
|
|
20
|
+
|
|
21
|
+
## Rulings
|
|
22
|
+
|
|
23
|
+
1. **The index is the last section of the region, and it is one section.** Its heading is
|
|
24
|
+
`## Disclosure`. A scoped profile keeps the pointer ADR-071 ruling 7 requires: the
|
|
25
|
+
patterns, and the files the profile moved to. A skill keeps its name and its
|
|
26
|
+
description, which is the trigger. A second heading for the second kind would spend
|
|
27
|
+
region bytes on a distinction the reader acts on the same way — open that thing when
|
|
28
|
+
the line says to.
|
|
29
|
+
|
|
30
|
+
2. **A skill contributes nothing but that line.** The body stays in the skill file. The
|
|
31
|
+
description is read with the same frontmatter reader the skill lint uses, so a folded
|
|
32
|
+
description is the sentence the author wrote and not the marker in front of it. A
|
|
33
|
+
skill with no description says so and names `simt skills show`, because a line that
|
|
34
|
+
invents a trigger would send the reader to the wrong file.
|
|
35
|
+
|
|
36
|
+
3. **The directories are the four a skill is installed into.** They are the directory
|
|
37
|
+
`skills install` chooses and the directories a loader row records, which together are
|
|
38
|
+
four and not a walk of the repository. A symlink to a skill already counted is not a
|
|
39
|
+
second skill. Two different files that share a name are both listed, with the
|
|
40
|
+
directory, because keeping one would hide a trigger.
|
|
41
|
+
|
|
42
|
+
4. **The index is region text, so every selected surface sees the same lines.** A surface
|
|
43
|
+
that already shows a skill's name and description at session start sees the line
|
|
44
|
+
twice. The alternative is a region that depends on which surface is reading it, and
|
|
45
|
+
`check` would then disagree with a reader whose surface has no skill channel. The
|
|
46
|
+
body is the copy that stays out. ADR-071 ruling 1 still decides the profile itself:
|
|
47
|
+
it leaves the region only when every selected surface can narrow it.
|
|
48
|
+
|
|
49
|
+
5. **The index is not a candidate for a budget cut.** Cutting it would remove the only
|
|
50
|
+
mention of the rules a cut had already removed. It is weighed, because it does cost
|
|
51
|
+
bytes, and it is kept.
|
|
52
|
+
|
|
53
|
+
## Consequences
|
|
54
|
+
|
|
55
|
+
On a surface that can narrow, a fixture's scoped profile is absent from the region and
|
|
56
|
+
present in the file that surface loads when a matching path is in play. On a surface that
|
|
57
|
+
cannot, the profile stays in the region. That is the accept, checked by rendering each
|
|
58
|
+
surface rather than by driving the harness: this tool writes the file, and the harness
|
|
59
|
+
is what opens it.
|
|
60
|
+
|
|
61
|
+
A repository with no installed skill and no scoped profile gains no section. An empty
|
|
62
|
+
heading would be a line about nothing.
|
|
63
|
+
|
|
64
|
+
## Rejected
|
|
65
|
+
|
|
66
|
+
- **Putting the skill body in the region.** That is the cost the index exists to avoid,
|
|
67
|
+
and it is what a surface with a skill channel already refuses to do.
|
|
68
|
+
- **Omitting the line on a surface that shows skill metadata itself.** The region is one
|
|
69
|
+
document. A line present for one reader and absent for another is drift.
|
|
70
|
+
- **Truncating a description to keep the line short.** The description is the trigger. A
|
|
71
|
+
shorter line that no longer says when to open the skill is a line that cannot do its
|
|
72
|
+
job.
|
package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# ADR-086 — The stable prefix comes first, and the lock records both digests
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-18 · filed as R13 (plan 0003)
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/projection/rules.mjs`, `src/projection/lock.mjs`, `src/commands/sync.mjs`,
|
|
6
|
+
`src/argv.mjs`, `test/lock.test.mjs`, `test/card.test.mjs`
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
A reader caches the instruction file. A change in the first bytes throws the rest away,
|
|
11
|
+
including the rules that did not change. The plan asks for the stable content first —
|
|
12
|
+
core, then stack, then what this repository added — and for a lock that fails when the
|
|
13
|
+
output moves and the input did not.
|
|
14
|
+
|
|
15
|
+
The command card was the first bytes of the region (ADR-077 ruling 1), because the
|
|
16
|
+
commands are what a reader cannot infer. They are also the bytes a repository edit is
|
|
17
|
+
most likely to move: a script change would then invalidate the core that follows them.
|
|
18
|
+
|
|
19
|
+
The content digest stays in the generated header. It is a digest of what this package
|
|
20
|
+
ships (ADR-032), so a repository edit does not move it, and `check` uses it to tell a
|
|
21
|
+
hand-edit from a version mismatch. Taking it out of the file would make those two
|
|
22
|
+
failures look the same. No date and no absolute path is written into the region.
|
|
23
|
+
|
|
24
|
+
## Rulings
|
|
25
|
+
|
|
26
|
+
1. **Shipped rules, then this repository's own bytes.** Core first, then the other
|
|
27
|
+
profiles this package ships, by id. Then the command card, then profiles the
|
|
28
|
+
repository added, then the index of what the region does not carry. This narrows
|
|
29
|
+
ADR-077 ruling 1: the card still leads the repository's own bytes, and it no longer
|
|
30
|
+
leads the region. The rest of that record stands, including that the card is derived
|
|
31
|
+
from committed evidence only.
|
|
32
|
+
|
|
33
|
+
2. **The order inside the shipped prefix is by id, not by directory order.** A directory
|
|
34
|
+
listing is not a fact about the rules. Two machines rendering one repository produce
|
|
35
|
+
the same bytes; the test renders one repository twice, which is the half a single
|
|
36
|
+
process can see. The other half is the same argument ADR-073 ruling 7 makes: the
|
|
37
|
+
inputs are the files, and nothing in the render reads the clock, the home directory
|
|
38
|
+
or the network.
|
|
39
|
+
|
|
40
|
+
3. **`simt-dev.lock.json` records an input digest and an output digest.** The input is
|
|
41
|
+
the package digest, the detected signals, the manifest keys that change the region,
|
|
42
|
+
the repository's own profiles and its `package.json` scripts. The output is the
|
|
43
|
+
region body. `sync --yes` rewrites the lock when it differs. `check` without
|
|
44
|
+
`--frozen` does not require one, because every repository adopted before this record
|
|
45
|
+
would otherwise fail a gate it had not been asked to keep.
|
|
46
|
+
|
|
47
|
+
4. **`check --frozen` fails in two ways, and names which.** No lock, or a lock whose
|
|
48
|
+
input digest differs, is a stale lock: rewrite it with `sync --yes`. A lock whose
|
|
49
|
+
input matches and whose output does not is unexplained: the projection changed and
|
|
50
|
+
nothing it read did. An output change that an input change explains is the stale
|
|
51
|
+
case, not the unexplained one.
|
|
52
|
+
|
|
53
|
+
## Consequences
|
|
54
|
+
|
|
55
|
+
Editing a repository profile leaves the bytes before that profile identical. A frozen
|
|
56
|
+
check on a matching lock passes, a tampered output fails as unexplained, and a changed
|
|
57
|
+
profile fails as a stale lock.
|
|
58
|
+
|
|
59
|
+
## Rejected
|
|
60
|
+
|
|
61
|
+
- **Removing the content digest from the header.** The digest does not move when the
|
|
62
|
+
repository edits a line, and without it a hand-edit and a version mismatch are one
|
|
63
|
+
finding.
|
|
64
|
+
- **Hashing the whole manifest, or the whole `package.json`.** A comment, or a
|
|
65
|
+
dependency bump that the projection does not read, would fail the lock for a region
|
|
66
|
+
that had not changed.
|
|
67
|
+
- **Failing an ordinary `check` when the lock is absent.** The lock is new. The
|
|
68
|
+
repositories this package already adopted do not have one, and a gate that turned red
|
|
69
|
+
for that absence would be red everywhere until someone ran `sync`.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# ADR-087 — A run is evidence for one tree
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-18 · filed as R17 (plan 0003)
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/ledger/index.mjs`, `src/commands/evidence.mjs`, `src/hooks/dispatcher.mjs`,
|
|
6
|
+
`test/ledger.test.mjs`
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
The rule that a test was run is otherwise a sentence in a commit message. Nothing checks
|
|
11
|
+
it, so a message can say the tests passed for a tree they were never run against, and
|
|
12
|
+
for a tree that has been edited since. The plan asks for a ledger, a proof block, and a
|
|
13
|
+
commit-message check that warns unless the repository asks it to block.
|
|
14
|
+
|
|
15
|
+
The tree has to be named without staging. `git add -A` on the real index would take the
|
|
16
|
+
author's unstaged work into the commit that is about to happen, which is the opposite of
|
|
17
|
+
what a hook is allowed to do.
|
|
18
|
+
|
|
19
|
+
## Rulings
|
|
20
|
+
|
|
21
|
+
1. **The tree is everything on disk, taken with a private index.** `git add -A` and
|
|
22
|
+
`git write-tree` run against a temporary `GIT_INDEX_FILE`, which is deleted
|
|
23
|
+
afterwards. The repository's own index is not read for this and not written. A file
|
|
24
|
+
the author left unstaged stays unstaged. The ledger file is left out of that add,
|
|
25
|
+
because the line is a record of the tree and writing it must not replace the tree
|
|
26
|
+
it names.
|
|
27
|
+
|
|
28
|
+
2. **A ledger line records the run, not its output.** The command, the exit code, the
|
|
29
|
+
duration, a one-line summary, and a digest of the output. The output itself is not
|
|
30
|
+
stored. The line is appended when the dispatcher actually runs a gate. A gate that
|
|
31
|
+
does not run leaves no line. A tree that cannot be named leaves no line, and the
|
|
32
|
+
claim check then finds nothing.
|
|
33
|
+
|
|
34
|
+
3. **A verification claim is a passing line for this tree.** The phrases are `tests
|
|
35
|
+
pass`, `tests passed`, `test pass`, `test passed`, and `verified`. `unverified` and
|
|
36
|
+
`not verified` are not claims. A line with a non-zero exit does not support the
|
|
37
|
+
claim. Editing any file changes the tree, so the line stops supporting it.
|
|
38
|
+
|
|
39
|
+
4. **The check warns, and blocks only when the repository says so.** `gates.evidence`
|
|
40
|
+
set to `block` fails the commit. Unset, or `warn`, prints the warning and exits 0.
|
|
41
|
+
Any other value is an error, because a misspelt `block` must not become a warning.
|
|
42
|
+
`simt evidence` prints the lines for the current tree and writes nothing.
|
|
43
|
+
`simt hooks install --yes` writes a commit-msg shim only when `gates.evidence` is
|
|
44
|
+
`warn` or `block`. The shim forwards the message file. Unset installs nothing,
|
|
45
|
+
and removing the setting removes the shim, so a hook the repository did not ask
|
|
46
|
+
for does not stay behind and replace git's own.
|
|
47
|
+
|
|
48
|
+
## Consequences
|
|
49
|
+
|
|
50
|
+
A green run followed by an edit of any file leaves the new tree with no evidence. The
|
|
51
|
+
old line is still in the ledger, and it names the tree it was true for. A commit
|
|
52
|
+
message that claims the tests passed is blocked only in a repository that set
|
|
53
|
+
`gates.evidence` to `block`.
|
|
54
|
+
|
|
55
|
+
## Rejected
|
|
56
|
+
|
|
57
|
+
- **Staging with the real index in order to name the tree.** The next commit would
|
|
58
|
+
contain files the author had not staged.
|
|
59
|
+
- **Storing the command's output in the ledger.** A test log is not evidence the next
|
|
60
|
+
reader needs, and it is where a secret printed by a failing command would be kept.
|
|
61
|
+
- **Treating any ledger line as proof the tests passed.** A failing run is a line. It
|
|
62
|
+
is not a pass.
|
|
63
|
+
- **Blocking every repository by default.** The check is new. A repository that has
|
|
64
|
+
not asked for it gets a warning, which it can read, and its commit still happens.
|
|
65
|
+
- **Adding commit-msg to the dispatcher events.** Those shims do not forward git's
|
|
66
|
+
arguments, because a pre-push remote would be read as one. The message file is
|
|
67
|
+
the argument, so the claim check is its own shim.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ADR-088 — A marked line is enforced by a checker
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-18 · filed as R19 (plan 0003) · ruling 3 narrowed by ADR-090
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/kernel/checks.mjs`, `rules/core.md`, `src/commands/sync.mjs`,
|
|
6
|
+
`test/checks.test.mjs`
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
A line an agent is trusted to follow is a line the next agent can skip. The file-length
|
|
11
|
+
rule is already two numbers with two meanings, and the check that was supposed to apply
|
|
12
|
+
them was never run. The plan asks for a line to name a checker, for the projection to
|
|
13
|
+
keep the name, and for the checker to be what actually decides.
|
|
14
|
+
|
|
15
|
+
One of the named checkers, `import-direction`, needs a module graph this tool does not
|
|
16
|
+
have yet. Removing that instruction now would leave a name and no check.
|
|
17
|
+
|
|
18
|
+
## Rulings
|
|
19
|
+
|
|
20
|
+
1. **A list item may name one checker.** The marker is `simt-check`, on the item,
|
|
21
|
+
including the lines wrapped under it. The checkers are `max-file-lines`,
|
|
22
|
+
`forbid-regex`, `forbidden-path-edit`, `required-file`, and `import-direction`.
|
|
23
|
+
Anything else is a broken rule. A marker inside a fenced sample is not a checker.
|
|
24
|
+
|
|
25
|
+
2. **A checker that can run replaces the item with its name.** The projection says
|
|
26
|
+
`Enforced:` and the checker. The prose does not. `simt check` runs the checker
|
|
27
|
+
against the rule as written, so the line that left the prompt is the line that
|
|
28
|
+
still decides. `max-file-lines` reads `limits.fileLines`: over the trigger warns,
|
|
29
|
+
over the ceiling fails. A path is exempt when `limits.fileLines.generated` names
|
|
30
|
+
it, or when the file carries the generated-region marker this tool writes, which
|
|
31
|
+
is this tool naming itself as the generator. The numbers stay 400 and 1000 where
|
|
32
|
+
a repository has not named its own.
|
|
33
|
+
|
|
34
|
+
3. **`import-direction` does not replace its item.** The module graph it reads is not
|
|
35
|
+
built. The instruction stays, the marker does not reach the reader, and `simt check`
|
|
36
|
+
says the checker is not built. It does not fail the repository for that.
|
|
37
|
+
|
|
38
|
+
4. **A diff checker reads the change.** `forbid-regex` and `forbidden-path-edit` look
|
|
39
|
+
at added lines and changed paths, including untracked files. A match already in the
|
|
40
|
+
last commit is not a new match. A directory that is not a git repository is not a
|
|
41
|
+
failure of these checkers: there is no tree to name.
|
|
42
|
+
|
|
43
|
+
## Consequences
|
|
44
|
+
|
|
45
|
+
The file-length sentence no longer occupies the region. The bytes it occupied are
|
|
46
|
+
smaller by the length of that sentence. Whether agents still comply with the rule is
|
|
47
|
+
unmeasured: the probes were not run.
|
|
48
|
+
|
|
49
|
+
`simt check` now fails a tracked file past the ceiling in every repository that
|
|
50
|
+
selects the core rules, which is every repository.
|
|
51
|
+
|
|
52
|
+
## Rejected
|
|
53
|
+
|
|
54
|
+
- **Dropping `import-direction` before it can run.** The name would say the line is
|
|
55
|
+
enforced. Nothing would be enforcing it.
|
|
56
|
+
- **Failing `simt check` outside a git repository.** There is no tracked tree to count
|
|
57
|
+
and no diff to read. A missing repository is not a long file.
|
|
58
|
+
- **Exempting a file because it looks generated.** The repository names the generator.
|
|
59
|
+
A guess would hide the ceiling from the file that most needs it.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# ADR-089 — The local index is a cache of git blobs
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-18 · filed as R20 (plan 0003)
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/index/store.mjs`, `src/commands/repoindex.mjs`, `test/index.test.mjs`
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Later slices ask which file defines a name, which test covers a change, and which
|
|
10
|
+
decision governs a path. Those questions need a store, and the store has to be
|
|
11
|
+
rebuildable: a cache that cannot be reconstructed from the repository is a second
|
|
12
|
+
source of truth. The plan names `node:sqlite`, with a fallback for a Node that does
|
|
13
|
+
not have it, and it names the blob hash from `git ls-files` as the unit of change.
|
|
14
|
+
|
|
15
|
+
The floor this package declares is older than the Node that first shipped that module
|
|
16
|
+
as something a program can rely on. A store that only works on the machine that
|
|
17
|
+
happened to build it is not the store the plan describes.
|
|
18
|
+
|
|
19
|
+
## Rulings
|
|
20
|
+
|
|
21
|
+
1. **The store is sqlite when this process can open it and create an FTS5 table.**
|
|
22
|
+
The file is `.simt-dev/index.sqlite`. The tables are the ones the plan names:
|
|
23
|
+
files, symbols, edges, an FTS5 table, and meta. When the module is absent or FTS5
|
|
24
|
+
cannot be created, the same file rows are written to `.simt-dev/index.jsonl`.
|
|
25
|
+
Nothing is added to the dependency set.
|
|
26
|
+
|
|
27
|
+
2. **A row is keyed by the git blob.** `git ls-files -s` supplies the path and the
|
|
28
|
+
blob. A path whose blob has not changed is not read again, and its row is not
|
|
29
|
+
rewritten. A path that left the index leaves the store, and so does anything
|
|
30
|
+
recorded against that path. Two indexes of the same blobs produce the same file
|
|
31
|
+
rows. The language comes from the extension, and the package comes from the
|
|
32
|
+
nearest manifest walking up from the file.
|
|
33
|
+
|
|
34
|
+
3. **`simt index` rebuilds the cache.** It does not preview, because the file it
|
|
35
|
+
writes is not repository content. It says when the path is not gitignored. A
|
|
36
|
+
directory that is not a git repository cannot be indexed.
|
|
37
|
+
|
|
38
|
+
## Consequences
|
|
39
|
+
|
|
40
|
+
Reindexing one changed file reads that blob and leaves the others. The symbols and
|
|
41
|
+
edges tables exist and are cleared for a path whose blob changed. Nothing fills them
|
|
42
|
+
until a later slice extracts names from the bytes.
|
|
43
|
+
|
|
44
|
+
## Rejected
|
|
45
|
+
|
|
46
|
+
- **Requiring sqlite.** The floor this package already ships does not promise the
|
|
47
|
+
module. A repository on that floor still gets an index.
|
|
48
|
+
- **Hashing the worktree instead of the blob.** An unstaged edit is not what
|
|
49
|
+
`git ls-files` reports. The index follows the index, so two machines with the same
|
|
50
|
+
committed blobs agree.
|
|
51
|
+
- **Treating the cache as a writer that previews.** A preview that does not write
|
|
52
|
+
leaves the reader with no index. The command's only effect is the cache.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ADR-090 — An optional adapter is off until a repository names it
|
|
2
|
+
|
|
3
|
+
Status: **accepted** · 2026-10-18 · filed as R21 (plan 0003)
|
|
4
|
+
|
|
5
|
+
**Affects:** `src/index/extract.mjs`, `src/index/store.mjs`, `src/index/persist.mjs`,
|
|
6
|
+
`test/extract.test.mjs`
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
The index can say which file defines a name only if something reads the blob. A
|
|
11
|
+
structural parser does that more accurately than a scan of lines, and it is also a
|
|
12
|
+
dependency: grammars, a WASM runtime, a package this tool would then require. The
|
|
13
|
+
floor this package ships is zero dependencies. An adapter that is always loaded is
|
|
14
|
+
not optional, and an adapter this package names for one organisation is a name the
|
|
15
|
+
next repository does not have.
|
|
16
|
+
|
|
17
|
+
The module graph of imports now exists. ADR-088 ruling 3 kept `import-direction` in
|
|
18
|
+
the prose because that graph did not. The graph still does not say which way an
|
|
19
|
+
import is allowed to point.
|
|
20
|
+
|
|
21
|
+
## Rulings
|
|
22
|
+
|
|
23
|
+
1. **The built-in extractors are the index.** They read one line at a time and
|
|
24
|
+
record definitions, exports and imports for the languages a map needs. The same
|
|
25
|
+
blob produces the same symbol rows and the same import edges. A blob that has
|
|
26
|
+
not changed is not read again. Recall against a hand-labelled set of exported
|
|
27
|
+
names, for TypeScript, Kotlin, Python and Go, is at least 90%. A comparison
|
|
28
|
+
against a structural parser was not run.
|
|
29
|
+
|
|
30
|
+
2. **An optional adapter is a separate package, and it is off until named.**
|
|
31
|
+
`index.parsers` is the package name. Absent, nothing is imported. This package
|
|
32
|
+
does not depend on a parser and does not ship a default name for one. When the
|
|
33
|
+
named package does not load, the built-in rows are the cache and the index says
|
|
34
|
+
so. When it loads, its symbols and edges replace the built-in rows for that
|
|
35
|
+
blob. It writes those rows into the cache. It does not edit a source file.
|
|
36
|
+
|
|
37
|
+
3. **`import-direction` still does not replace its line.** ADR-088 ruling 3 stands,
|
|
38
|
+
except for the sentence that the module graph is not built. The edges are
|
|
39
|
+
recorded. No rule names the direction they must follow, so the instruction stays
|
|
40
|
+
and `simt check` does not fail a repository for the missing direction.
|
|
41
|
+
|
|
42
|
+
## Consequences
|
|
43
|
+
|
|
44
|
+
`simt index` now reports how many symbols and edges the cache holds. Whether the
|
|
45
|
+
line extractor misses a name a parser would have found, on a real repository, is
|
|
46
|
+
unmeasured: the bar above is the labelled fixtures, not a live parser.
|
|
47
|
+
|
|
48
|
+
## Rejected
|
|
49
|
+
|
|
50
|
+
- **Depending on a parser, or probing one this package names.** The dependency set
|
|
51
|
+
would no longer be empty, and a name that only one organisation publishes is not
|
|
52
|
+
a default for every repository.
|
|
53
|
+
- **Treating the import edges as a direction check.** An edge says that A imports B.
|
|
54
|
+
It does not say that A was allowed to. Dropping the instruction would report the
|
|
55
|
+
line as enforced.
|