simt-dev 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +169 -80
  3. package/docs/decisions/ADR-014-workspace-is-configuration.md +1 -1
  4. package/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +1 -1
  5. package/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +1 -1
  6. package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +1 -1
  7. package/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +1 -1
  8. package/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +1 -1
  9. package/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +1 -1
  10. package/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +1 -1
  11. package/docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md +1 -1
  12. package/docs/decisions/ADR-126-the-mcp-surface-stays-a-reader-so-there-is-no-write-path-to-build.md +68 -0
  13. package/docs/decisions/ADR-127-the-mcp-tool-list-is-written-by-hand-and-roots-is-not-asked-for.md +65 -0
  14. package/docs/decisions/ADR-128-a-research-plan-is-a-plan-and-the-tracker-reads-it.md +42 -0
  15. package/ecosystems/bun.json +6 -0
  16. package/ecosystems/cargo.json +25 -0
  17. package/ecosystems/composer.json +6 -0
  18. package/ecosystems/dart.json +24 -0
  19. package/ecosystems/deno.json +21 -0
  20. package/ecosystems/devcontainers.json +6 -0
  21. package/ecosystems/docker-compose.json +6 -0
  22. package/ecosystems/docker.json +6 -0
  23. package/ecosystems/dotnet.json +25 -0
  24. package/ecosystems/elixir.json +23 -0
  25. package/ecosystems/gitsubmodule.json +6 -0
  26. package/ecosystems/go.json +25 -0
  27. package/ecosystems/gradle.json +13 -0
  28. package/ecosystems/maven.json +23 -0
  29. package/ecosystems/node.json +25 -0
  30. package/ecosystems/python.json +20 -0
  31. package/ecosystems/ruby.json +23 -0
  32. package/ecosystems/swift.json +25 -0
  33. package/ecosystems/terraform.json +6 -0
  34. package/ecosystems/uv.json +6 -0
  35. package/harnesses/antigravity.json +162 -0
  36. package/harnesses/claude.json +206 -0
  37. package/harnesses/codex.json +136 -0
  38. package/harnesses/copilot.json +88 -0
  39. package/harnesses/cursor.json +140 -0
  40. package/harnesses/deepseek.json +99 -0
  41. package/harnesses/gemini.json +122 -0
  42. package/harnesses/opencode.json +173 -0
  43. package/package.json +3 -1
  44. package/rules/mdc/intake-questions.mdc +8 -0
  45. package/src/advice/advice.mjs +3 -2
  46. package/src/agenthooks/render.mjs +86 -180
  47. package/src/agents/render.mjs +15 -18
  48. package/src/api.mjs +7 -5
  49. package/src/argv.mjs +19 -0
  50. package/src/bench/ablate.mjs +3 -4
  51. package/src/bench/adapters.json +155 -113
  52. package/src/bench/adapters.mjs +466 -0
  53. package/src/bench/arms.mjs +333 -0
  54. package/src/bench/grade.mjs +12 -26
  55. package/src/bench/graders.mjs +42 -0
  56. package/src/bench/mine.mjs +17 -535
  57. package/src/bench/probe-ledger.mjs +149 -0
  58. package/src/bench/probes.catalog.mjs +918 -0
  59. package/src/bench/probes.mjs +15 -1037
  60. package/src/bench/render.mjs +111 -0
  61. package/src/bench/report.mjs +116 -971
  62. package/src/bench/run.mjs +164 -881
  63. package/src/bench/variants.mjs +4 -5
  64. package/src/bench/workspace.mjs +160 -0
  65. package/src/brief/assemble.mjs +11 -13
  66. package/src/ci/dependabot.mjs +25 -55
  67. package/src/commands/agent.mjs +1 -9
  68. package/src/commands/agenthook.mjs +66 -35
  69. package/src/commands/bench.mjs +2 -2
  70. package/src/commands/ci.mjs +11 -13
  71. package/src/commands/docs.mjs +88 -0
  72. package/src/commands/gate.mjs +10 -13
  73. package/src/commands/harness.mjs +3 -3
  74. package/src/commands/help.mjs +44 -3
  75. package/src/commands/hooks.mjs +20 -20
  76. package/src/commands/ignore.mjs +11 -7
  77. package/src/commands/impact.mjs +4 -8
  78. package/src/commands/index.mjs +49 -9
  79. package/src/commands/init-scan.mjs +90 -0
  80. package/src/commands/init.mjs +122 -104
  81. package/src/commands/lease.mjs +67 -18
  82. package/src/commands/map.mjs +10 -5
  83. package/src/commands/mcp.mjs +2 -17
  84. package/src/commands/repoindex.mjs +8 -8
  85. package/src/commands/rules.mjs +8 -4
  86. package/src/commands/shared.mjs +12 -16
  87. package/src/commands/skills.mjs +5 -6
  88. package/src/commands/skillscompile.mjs +3 -3
  89. package/src/commands/skillsinstall.mjs +5 -4
  90. package/src/commands/slice.mjs +199 -64
  91. package/src/commands/spec.mjs +78 -62
  92. package/src/commands/status.mjs +24 -12
  93. package/src/commands/sync.mjs +14 -14
  94. package/src/commands/tests.mjs +12 -12
  95. package/src/commands/tokens.mjs +19 -24
  96. package/src/commands/upgrade.mjs +8 -4
  97. package/src/commands/why.mjs +6 -13
  98. package/src/compare/hooksstate.mjs +2 -6
  99. package/src/compare/learn.mjs +3 -1
  100. package/src/decisions/index.mjs +3 -2
  101. package/src/docs/commands.mjs +65 -0
  102. package/src/ecosystem/records.mjs +219 -0
  103. package/src/explain.mjs +31 -0
  104. package/src/gates/index.mjs +385 -0
  105. package/src/graph/compile.mjs +8 -4
  106. package/src/graph/load.mjs +2 -1
  107. package/src/graph/spec.mjs +10 -8
  108. package/src/harness/records.mjs +143 -0
  109. package/src/hooks/capability.mjs +5 -3
  110. package/src/hooks/dispatcher.mjs +26 -32
  111. package/src/hooks/gates.mjs +12 -2
  112. package/src/hooks/resolve.mjs +2 -7
  113. package/src/hooks/where.mjs +2 -7
  114. package/src/identity.mjs +2 -11
  115. package/src/index/coverage.mjs +58 -0
  116. package/src/index/extract.mjs +6 -16
  117. package/src/index/gate.mjs +1 -1
  118. package/src/index/govern.mjs +26 -9
  119. package/src/index/impact.mjs +4 -8
  120. package/src/index/map.mjs +14 -15
  121. package/src/index/open.mjs +173 -0
  122. package/src/index/persist.mjs +224 -57
  123. package/src/index/store.mjs +82 -39
  124. package/src/index/tests.mjs +2 -6
  125. package/src/journal.mjs +16 -11
  126. package/src/kernel/card.mjs +37 -63
  127. package/src/kernel/checks.mjs +4 -10
  128. package/src/lease.mjs +102 -7
  129. package/src/ledger/index.mjs +124 -17
  130. package/src/mcp/paginate.mjs +33 -0
  131. package/src/mcp/schemas.mjs +512 -0
  132. package/src/mcp/server.mjs +49 -9
  133. package/src/mcp/tools-facts.mjs +326 -0
  134. package/src/mcp/tools-read.mjs +221 -0
  135. package/src/mcp/tools.mjs +4 -1005
  136. package/src/{bench → measure}/corpus.mjs +1 -1
  137. package/src/measure/metrics.mjs +236 -0
  138. package/src/measure/records.mjs +211 -0
  139. package/src/measure/stats.mjs +538 -0
  140. package/src/measure/task-schema.mjs +125 -0
  141. package/src/measure/tasks.mjs +69 -0
  142. package/src/memory/decide.mjs +6 -15
  143. package/src/memory/lesson-text.mjs +61 -0
  144. package/src/memory/lessons.mjs +29 -82
  145. package/src/name.mjs +7 -0
  146. package/src/output/index.mjs +122 -31
  147. package/src/packs/manage.mjs +15 -9
  148. package/src/plan/index.mjs +1 -1
  149. package/src/plugins.mjs +22 -16
  150. package/src/prbody.mjs +7 -11
  151. package/src/projection/loaders.mjs +7 -692
  152. package/src/projection/manifest.mjs +10 -435
  153. package/src/projection/mdc.mjs +7 -8
  154. package/src/projection/project.mjs +126 -221
  155. package/src/projection/region.mjs +561 -0
  156. package/src/projection/rules.mjs +16 -547
  157. package/src/projection/surfaces.mjs +43 -210
  158. package/src/projection/targets.mjs +5 -5
  159. package/src/repo-root.mjs +13 -1
  160. package/src/resolve/eval.mjs +2 -1
  161. package/src/resolve/fingerprint.mjs +0 -7
  162. package/src/resolve/resolve.mjs +1 -41
  163. package/src/review/critic.mjs +8 -12
  164. package/src/run.mjs +98 -0
  165. package/src/secrets.mjs +14 -22
  166. package/src/state/events.mjs +129 -0
  167. package/src/status-snapshot.mjs +233 -0
  168. package/src/status.mjs +2 -6
  169. package/src/sys/git.mjs +82 -0
  170. package/src/{bench/process.mjs → sys/proc.mjs} +28 -5
  171. package/src/testing/parsers.mjs +265 -0
  172. package/src/testing/patches.mjs +23 -0
  173. package/src/testing/paths.mjs +49 -0
  174. package/src/testing/runners.mjs +186 -0
  175. package/src/testing/vault.mjs +68 -0
  176. package/src/text/frontmatter.mjs +49 -0
  177. package/src/text/tokens.mjs +52 -0
  178. package/src/toolwords.mjs +1 -1
  179. package/src/util/fs.mjs +20 -0
  180. package/src/util/sort.mjs +12 -0
  181. package/src/workspace.mjs +3 -1
  182. package/src/write/plan.mjs +82 -0
  183. package/templates/plan/tracker.json +1 -0
  184. package/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +0 -82
  185. package/docs/decisions/ADR-102-the-two-queued-review-rows-are-closed-by-ruling.md +0 -49
  186. package/src/bench/tasks.mjs +0 -36
@@ -0,0 +1,52 @@
1
+ // The token split and the two stopword lists, in one place.
2
+ //
3
+ // The two lists are deliberately NOT merged. The resolver's list is the English
4
+ // filler a task string carries ("fix the thing"), hand-authored because a resolver
5
+ // nobody can review is not a resolver; the index's list is the vocabulary a task
6
+ // about code carries ("file", "test", "line"), sized for a different signal. Merging
7
+ // them would change what each one ranks, and a ranking change is a behaviour change
8
+ // this deduplication was not asked to make.
9
+
10
+ /**
11
+ * Split a task string into comparable tokens.
12
+ *
13
+ * Length 2 is kept. The floor used to be 3, and it dropped the most specific words in
14
+ * this domain: `d1`, `r2`, `kv`, `ci` are two-letter vocabulary, not noise, and a task
15
+ * saying "fix d1 migration" lost the one word that identified the change. The cost is
16
+ * that six stopwords of two letters (`in on of or an so`) now reach the summary signal,
17
+ * which is S04's finding and not something this change can resolve on its own - the
18
+ * remedy there is a stopword list, not a longer minimum.
19
+ */
20
+ export function tokenize(text) {
21
+ return String(text ?? '')
22
+ .toLowerCase()
23
+ .split(/[^a-z0-9]+/)
24
+ .filter((t) => t.length > 1);
25
+ }
26
+
27
+ /**
28
+ * The English filler a task string carries. Hand-authored rather than derived, for the
29
+ * same reason the capability aliases are: a resolver nobody can review is not a
30
+ * resolver.
31
+ */
32
+ export const STOPWORDS = new Set([
33
+ 'about', 'after', 'all', 'also', 'and', 'an', 'any', 'are', 'as', 'at', 'be', 'because',
34
+ 'been', 'before', 'being', 'both', 'but', 'by', 'can', 'could', 'did', 'do', 'does', 'each',
35
+ 'for', 'from', 'had', 'has', 'have', 'he', 'her', 'here', 'his', 'how', 'if', 'in', 'into',
36
+ 'is', 'it', 'its', 'just', 'like', 'make', 'many', 'may', 'me', 'more', 'most', 'must', 'my',
37
+ 'no', 'not', 'now', 'of', 'off', 'on', 'one', 'only', 'onto', 'or', 'other', 'our', 'out',
38
+ 'over', 'own', 'per', 'same', 'see', 'set', 'she', 'should', 'so', 'some', 'such', 'than',
39
+ 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'those', 'to', 'too',
40
+ 'two', 'under', 'up', 'us', 'use', 'used', 'using', 'very', 'was', 'way', 'we', 'were', 'what',
41
+ 'when', 'where', 'which', 'while', 'who', 'why', 'will', 'with', 'within', 'without', 'would',
42
+ 'you', 'your',
43
+ ]);
44
+
45
+ /** The code vocabulary a task about a tree carries, for the index's term signal. */
46
+ export const INDEX_STOP = new Set([
47
+ 'this', 'that', 'with', 'from', 'have', 'been', 'into', 'only', 'when', 'what',
48
+ 'which', 'their', 'about', 'after', 'before', 'where', 'there', 'these', 'those',
49
+ 'would', 'could', 'should', 'every', 'other', 'than', 'then', 'them', 'they',
50
+ 'your', 'will', 'does', 'file', 'files', 'test', 'tests', 'line', 'lines', 'code',
51
+ 'also', 'just', 'more', 'some', 'such', 'each', 'same', 'over', 'used', 'using',
52
+ ]);
package/src/toolwords.mjs CHANGED
@@ -23,7 +23,7 @@ import { LESSON_KEYS } from './memory/lessons.mjs';
23
23
  import { DECISION_KEYS } from './memory/decide.mjs';
24
24
  import { PROMPT_WORDS } from './mcp/prompts.mjs';
25
25
  import { AGENT_STEM, SKILL_STEM } from './agents/render.mjs';
26
- import { CORPUS_KIND } from './bench/corpus.mjs';
26
+ import { CORPUS_KIND } from './measure/corpus.mjs';
27
27
 
28
28
  /**
29
29
  * `simt-` names that belong to this tool and never to a repository.
@@ -0,0 +1,20 @@
1
+ // Small file and output helpers with two or more call sites, too small to earn
2
+ // a module apiece.
3
+
4
+ import { readFile } from 'node:fs/promises';
5
+
6
+ /** Read a file as UTF-8, answering null when it is not there. */
7
+ export async function readIfExists(path) {
8
+ try {
9
+ return await readFile(path, 'utf8');
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+
15
+ /** `1234567`, never `1,234,567`: two machines must print the same bytes (ADR-013).
16
+ * `toLocaleString` output is locale-dependent, and these commands exist so that two
17
+ * machines produce two numbers a pull request can compare. */
18
+ export function plainNumber(n) {
19
+ return String(n);
20
+ }
@@ -0,0 +1,12 @@
1
+ // The one string comparison for every sort that orders paths, names and ids.
2
+ //
3
+ // Seven copies of the same three-line comparator grew up under src/, spelled two
4
+ // different ways, and a divergence in any one of them would quietly reorder a list
5
+ // a reader has memorized. One definition, one order.
6
+
7
+ /** Deterministic string order: -1, 0 or 1. */
8
+ export function cmp(a, b) {
9
+ if (a < b) return -1;
10
+ if (a > b) return 1;
11
+ return 0;
12
+ }
package/src/workspace.mjs CHANGED
@@ -21,6 +21,8 @@
21
21
  // confidently wrong.
22
22
 
23
23
  import { readFile } from 'node:fs/promises';
24
+
25
+ import { namedRoot } from './repo-root.mjs';
24
26
  import { existsSync } from 'node:fs';
25
27
  import { homedir, platform as osPlatform } from 'node:os';
26
28
  import { dirname, isAbsolute, join, resolve } from 'node:path';
@@ -311,7 +313,7 @@ function absolute(p, base = process.cwd()) {
311
313
  * answers it from the filesystem rather than from a guess.
312
314
  */
313
315
  export async function targetRoot(flags = {}, positional = []) {
314
- const explicit = flags.root ?? positional[0];
316
+ const explicit = namedRoot(flags, positional);
315
317
  if (explicit) {
316
318
  return { root: absolute(explicit), source: 'flag', detail: explicit, ws: null };
317
319
  }
@@ -0,0 +1,82 @@
1
+ // One change record, and the one function that applies it (review 0003 Q68).
2
+ //
3
+ // Fourteen writers hand-roll the same five steps: read the before bytes, decide create or
4
+ // update, write through the journal, commit the set, print the diff. They do not all do
5
+ // them in the same order, and the differences are defects rather than decisions - a
6
+ // writer that skipped the journal (the tracker, the lock, `skills compile`) could not be
7
+ // undone, and one that read the before bytes after writing produced a diff against
8
+ // itself.
9
+ //
10
+ // A change is data: what the path will hold, what it holds now, who owns it and why it
11
+ // changes. `applyPlan` is the only place that knows about journals, directories and
12
+ // `--yes`, so a writer states the change and nothing else.
13
+ //
14
+ // The record carries no `action` from the caller. Create, update, remove and unchanged
15
+ // are derived from `before` and `after`, because a writer that passes an action it
16
+ // computed by hand is the bug this replaces.
17
+ import { isAbsolute, join } from 'node:path';
18
+
19
+ import { commitJournal, journaledRemoveFile, journaledWriteFile } from '../journal.mjs';
20
+
21
+ /**
22
+ * One change to one path.
23
+ *
24
+ * `kind: 'remove'` is a deletion, and `after` is then null. `owner` and `reason` are for
25
+ * the report a person reads: which writer owns the file and why it is changing. `mode` is
26
+ * permission bits for a file that must be executable, and rides on the same entry so
27
+ * `undo` restores the mode it found.
28
+ */
29
+ export function change({ path, kind = 'file', before, after, owner, reason, mode }) {
30
+ if (kind !== 'file' && kind !== 'remove') throw new Error(`unknown change kind: ${kind}`);
31
+ return {
32
+ path,
33
+ kind,
34
+ before,
35
+ after,
36
+ owner: owner ?? null,
37
+ reason: reason ?? null,
38
+ mode,
39
+ action: actionOf(kind, before, after),
40
+ };
41
+ }
42
+
43
+ function actionOf(kind, before, after) {
44
+ if (kind === 'remove') return before === null ? 'unchanged' : 'remove';
45
+ if (before === after) return 'unchanged';
46
+ if (before === null) return 'create';
47
+ return 'update';
48
+ }
49
+
50
+ /** The changes that would do something, in the order the caller listed them. */
51
+ export function pending(changes) {
52
+ return (changes ?? []).filter((c) => c.action !== 'unchanged');
53
+ }
54
+
55
+ /**
56
+ * Apply the plan, or report it.
57
+ *
58
+ * Without `mode.apply` nothing touches the disk and the pending paths come back as
59
+ * `pending`: a rehearsal and a real run answer the same question with the same plan, and
60
+ * the only difference is whether the bytes moved. With it, every change is journaled into
61
+ * one set under `label`, so `undo` restores the run rather than one file of it.
62
+ *
63
+ * A writer whose run is more than one plan - `init` writes a manifest, an identity and a
64
+ * set of targets and commits once - passes its own `journal` and `commit: false`. The
65
+ * entries land in the caller's set, so the run stays one undoable step rather than one
66
+ * step per plan.
67
+ */
68
+ export async function applyPlan(root, changes, { mode, label, journal: shared, commit = true } = {}) {
69
+ const todo = pending(changes);
70
+ if (todo.length === 0) return { applied: false, written: [], pending: [], journal: [] };
71
+ if (!mode?.apply) return { applied: false, written: [], pending: todo.map((c) => c.path), journal: [] };
72
+
73
+ const journal = shared ?? [];
74
+ const first = journal.length;
75
+ for (const c of todo) {
76
+ const path = isAbsolute(c.path) ? c.path : join(root, c.path);
77
+ if (c.kind === 'remove') await journaledRemoveFile(root, journal, path);
78
+ else await journaledWriteFile(root, journal, path, c.after, c.mode === undefined ? {} : { mode: c.mode });
79
+ }
80
+ if (commit && journal.length > first) await commitJournal(root, label, journal);
81
+ return { applied: true, written: todo.map((c) => c.path), pending: [], journal };
82
+ }
@@ -3,6 +3,7 @@
3
3
  "name": "tracker",
4
4
  "directories": [
5
5
  { "kind": "plans", "word": "plan", "path": "docs/plans" },
6
+ { "kind": "research", "word": "research", "path": "docs/research" },
6
7
  { "kind": "reviews", "word": "review", "path": "docs/reviews" }
7
8
  ],
8
9
  "id": {
@@ -1,82 +0,0 @@
1
- # ADR-033 — three plan rows closed by decision, not by authoring
2
-
3
- Status: accepted · 2026-10-08
4
- **Affects:** `docs/plans/0001-centralise-agent-rules.md` §3.1 row 3, §6 phase 6, §6 phase 7.
5
- Ships no rule text. **Refuses:** the seven agent policies row 3 asks for, the twin collapse
6
- phase 6 asks for, and untracking the two committed `local.properties` files phase 7 does not
7
- mention but whose existence made the sweep look unfinished.
8
-
9
- ## Context
10
-
11
- Closing plan 0001 meant auditing each row against the tree rather than against the plan's own
12
- prose, because the plan was written on 2026-10-05 and three things changed underneath it. This
13
- record is the audit's residue: three items that read as unfinished work and are not.
14
-
15
- Row 3 asked for eight agent policies as separate files. Seven were never authored. A search for
16
- their source material across every tracked `*.md` and `*.mdc` in the estate returns two files,
17
- neither of which is a policy: they are a progress ledger and a completed UX plan that happen to
18
- contain the phrases. The three most specific of the eight — the 70/30 quality-versus-performance
19
- split, the 12-state coverage list, the matrix-trigger heuristic — have no source anywhere. Writing
20
- them would mean inventing doctrine this estate never agreed to, into `rules/core.md`, from where
21
- every adopted repo would inherit it.
22
-
23
- Phase 6 asked to "collapse the 7 drifted Social skill twins to canonical + stub". The registry's
24
- `sources.role` mechanism landed the same evening, two hours after the plan was authored, and
25
- answers the row directly. All three resolvers select `role === 'canonical'` and nothing else —
26
- `src/commands/skills.mjs` twice, `src/commands/skillsinstall.mjs:291`, `src/mcp/tools.mjs:192` — so
27
- a copy declared `duplicate` or `variant` is never installed. The one pair that genuinely had to be
28
- collapsed, `add-country-galaxy`, is already a stub in `repo-14` pointing at the API repo.
29
-
30
- Phase 7 asked for a sweep of hardcoded absolute paths. The sweep found 17 tracked files. Six were
31
- fixed. The two Android `local.properties` files were not, and were nearly fixed by reflex.
32
-
33
- ## Rulings
34
-
35
- 1. **A row is closed by ruling on it when the thing it asks for was never a decision anyone made.**
36
- Row 3's eight policies are closed: the four claims `rules/core.md` already carries — the
37
- minimum viable team, never weakening a security control, anti-overclaim, and a stated agent
38
- budget that never converts a failure into an approval — are the doctrine, and they ship. The
39
- other four, including the 70/30 split, the 12-state list and the matrix-trigger heuristic, are
40
- recorded here as unmade. If one is wanted it needs someone who held the original
41
- conversation, not someone reconstructing it from a table cell.
42
-
43
- 2. **A mechanism that supersedes a row retires the row, and the deletion is the wrong move.** The
44
- `sources.role` vocabulary already distinguishes `canonical`, `duplicate`, `variant`, `stub` and
45
- `deprecated`, and `graph lint` passes with all of them in use. Deleting a `duplicate` copy would
46
- remove a version-pinned tool from a repo that can still run it, and nothing would detect the loss
47
- because resolution never read those bytes. Phase 6's twin collapse is therefore closed as
48
- already answered, at `a9983c8`.
49
-
50
- 3. **`local.properties` stays tracked.** Both `repo-05` and `repo-03` commit it, and both
51
- `.gitignore` files have the conventional `# local.properties` line commented out at lines 22 and
52
- 24 — a deliberate override of Android's own convention, not an oversight. Untracking them would
53
- be a house-style change made while closing a plan, and a build machine that checks out without
54
- an SDK would stop building. It stays, and plan 0001 says so, so the next sweep does not
55
- rediscover it as a defect.
56
-
57
- ## Consequences
58
-
59
- Row 3 reads "done, four of eight" rather than "partial". Phase 6's twin item reads "superseded"
60
- rather than "outstanding". The estate keeps two `local.properties` files whose `sdk.dir` names a
61
- developer's home directory, by decision.
62
-
63
- The registry's byte counts moved with the two skill bodies corrected in the same pass, because a
64
- declared source's identity is its size (ADR-015): `cloudflare` 384277 → 384869 and
65
- `production-quality-review` 5869 → 5904. That changed the provenance digest (ADR-032), so all
66
- fourteen adopted repos were re-stamped afterwards rather than left pinning a value this tool can no
67
- longer produce.
68
-
69
- ## Rejected
70
-
71
- **Authoring the seven agent policies.** The plan's one-line summaries are not enough to write seven
72
- files of rule text, and these rules reach every adopted repo. This is ADR-030's exact shape: an
73
- unmade decision is not a gap to fill by invention.
74
-
75
- **Collapsing `graphify` and `parallel-feature-team`.** Both are declared `duplicate` with matching
76
- byte counts, so they look like the obvious candidates. `graphify` is `vendored: true` — an
77
- 84 534-byte, ten-file, version-pinned tool present in both one repository on purpose. Deleting
78
- repo-14's copy would leave nothing to detect the loss, because resolution only ever read the
79
- canonical copy.
80
-
81
- **Untracking `local.properties`.** An Android SDK path is machine state, and two repos chose to
82
- share it. That is the repo's call to reverse, not a plan's defect sweep.
@@ -1,49 +0,0 @@
1
- ---
2
- simt-affects: ["docs/reviews/0002-full-review-adoption-and-public-release.md"]
3
- simt-supersedes: []
4
- ---
5
-
6
- # ADR-102 — the two rows review 0002 left queued are closed by ruling
7
-
8
- Status: **accepted** · 2026-10-28
9
-
10
- ## Context
11
-
12
- Review 0002's §3 tracker is the only tracker of slice state, and two rows in it
13
- still read Queued while everything around them shipped: S38 (workspace token
14
- cost) and S39 (estate policy as a pack). Both rows' evidence cells are
15
- palimpsests — notes from three different landings disagreeing about what is
16
- still open — so reading them as a work list sends the next reader to build what
17
- is already built. Closing plan 0001 meant the same audit, and ADR-033 is the
18
- precedent: rows closed by saying what shipped, what was refused by ruling, and
19
- where the remainder lives, not by authoring what they asked for.
20
-
21
- ## Ruling
22
-
23
- 1. **S38 is Complete.** The instrument shipped (`simt tokens`, ADR-064), the
24
- declaration shipped (`budget.bytes` beside `limits` and `gates`, ADR-072),
25
- and the gate raises it as a finding through one `budgetFinding` read from
26
- one place by both `tokens` and `check` — the "still open" clause the row's
27
- first note ends on. Kernel-line packing shipped with it (R07's `mode:
28
- kernel`, R08's budget compilation). Blocking enforcement is refused, not
29
- outstanding: a gate that fails a repository for exceeding a ceiling it set
30
- itself, when nothing was cut, has no repair except lowering the declared
31
- number (ADR-072). The one genuine remainder is the kernel-default
32
- compliance study, and it is owned by plan 0003 R07's own note, not by this
33
- row — the study gates making `kernel` the default, which no row asks to
34
- decide here.
35
-
36
- 2. **S39 is Complete.** The mechanism shipped (packs vendored and digest-pinned,
37
- ADR-065; measured ΔR, plan 0003 R45). P4's pack-carries-the-registry line was
38
- superseded by ADR-061, which made the registry workspace data, so there is
39
- no registry-carrying pack to build. The remaining half — moving estate
40
- policy into a pack — is the estate's own commit: no estate content may enter
41
- this repository, and a row that waits on another repository's commit is
42
- closed by recording whose commit it waits on.
43
-
44
- ## Consequences
45
-
46
- - The §3 tracker reads Complete on both rows, with this record as the evidence,
47
- so the next audit starts from rulings rather than from mid-flight notes.
48
- - A reader asking for a blocking budget gate or a registry-carrying pack gets
49
- the refusal and its reason instead of an open row that looks like a promise.
@@ -1,36 +0,0 @@
1
- // Mined task descriptors, read back from the tool state's `bench/tasks/` directory (plan 0003 R01).
2
- //
3
- // Extracted from bench/run.mjs on its second real reader (ADR-002): the R40 advice engine
4
- // needs the two facts a run record does not carry — the prompt, which classifies the task,
5
- // and the gold stat, which says how many files the change touched. A second private loader
6
- // beside the first would drift the first time one learned a filter the other lacked.
7
- //
8
- // Deterministic by construction: the directory is read sorted, the filter is exact, and no
9
- // part of this touches a model, a network or a clock.
10
- import { readdir, readFile } from 'node:fs/promises';
11
- import { join, resolve } from 'node:path';
12
- import { STATE_DIR } from '../name.mjs';
13
-
14
- /**
15
- * Load mined benchmark task descriptors.
16
- *
17
- * @param {string} root the repository
18
- * @param {string[]} [filterIds] exact task ids to keep; empty means load them all
19
- * @returns descriptor objects sorted by id (id, base, sourceCommit, prompt, testCmd,
20
- * f2p, p2p, gold {files, linesAdded, linesRemoved}, vault)
21
- */
22
- export async function loadTasks(root, filterIds = []) {
23
- const tasksDir = join(resolve(root), STATE_DIR, 'bench', 'tasks');
24
- const entries = (await readdir(tasksDir).catch(() => []))
25
- .filter((f) => f.endsWith('.json'))
26
- .sort();
27
-
28
- const filterSet = new Set(filterIds);
29
- const tasks = [];
30
- for (const entry of entries) {
31
- const data = JSON.parse(await readFile(join(tasksDir, entry), 'utf8'));
32
- if (filterSet.size > 0 && !filterSet.has(data.id)) continue;
33
- tasks.push(data);
34
- }
35
- return tasks.sort((a, b) => a.id.localeCompare(b.id));
36
- }