simt-dev 0.2.0__py3-none-any.whl
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.
- python/simt_dev/__init__.py +32 -0
- python/simt_dev/cli.py +225 -0
- simt_dev/__init__.py +32 -0
- simt_dev/cli.py +225 -0
- simt_dev/vendored/CHANGELOG.md +166 -0
- simt_dev/vendored/bin/simt-dev.mjs +28 -0
- simt_dev/vendored/docs/decisions/ADR-001-push-requires-approval.md +42 -0
- simt_dev/vendored/docs/decisions/ADR-002-refactor-blast-radius.md +45 -0
- simt_dev/vendored/docs/decisions/ADR-003-test-breadth.md +46 -0
- simt_dev/vendored/docs/decisions/ADR-004-hook-owns-format.md +45 -0
- simt_dev/vendored/docs/decisions/ADR-005-file-length.md +42 -0
- simt_dev/vendored/docs/decisions/ADR-006-coverage-bar.md +59 -0
- simt_dev/vendored/docs/decisions/ADR-007-section-skeleton.md +54 -0
- simt_dev/vendored/docs/decisions/ADR-008-verdict-vocabulary.md +59 -0
- simt_dev/vendored/docs/decisions/ADR-009-skill-layout.md +48 -0
- simt_dev/vendored/docs/decisions/ADR-010-runner-invocation.md +56 -0
- simt_dev/vendored/docs/decisions/ADR-011-windows-support.md +52 -0
- simt_dev/vendored/docs/decisions/ADR-012-drop-windows-support.md +59 -0
- simt_dev/vendored/docs/decisions/ADR-013-genericity-by-facts-and-local-profiles.md +114 -0
- simt_dev/vendored/docs/decisions/ADR-014-workspace-is-configuration.md +211 -0
- simt_dev/vendored/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +117 -0
- simt_dev/vendored/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +180 -0
- simt_dev/vendored/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +89 -0
- simt_dev/vendored/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +183 -0
- simt_dev/vendored/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +110 -0
- simt_dev/vendored/docs/decisions/ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md +100 -0
- simt_dev/vendored/docs/decisions/ADR-021-one-surface-per-repo.md +94 -0
- simt_dev/vendored/docs/decisions/ADR-022-a-key-simt-dev-reads-is-prefixed.md +98 -0
- simt_dev/vendored/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +106 -0
- simt_dev/vendored/docs/decisions/ADR-024-simt-dev-overwrites-its-own-gate-file.md +111 -0
- simt_dev/vendored/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +153 -0
- simt_dev/vendored/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +117 -0
- simt_dev/vendored/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +130 -0
- simt_dev/vendored/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +131 -0
- simt_dev/vendored/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +120 -0
- simt_dev/vendored/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +114 -0
- simt_dev/vendored/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +129 -0
- simt_dev/vendored/docs/decisions/ADR-032-provenance-is-a-content-digest-not-a-commit.md +141 -0
- simt_dev/vendored/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +82 -0
- simt_dev/vendored/docs/decisions/ADR-034-a-staged-tool-refuses-a-partially-staged-file.md +91 -0
- simt_dev/vendored/docs/decisions/ADR-035-one-write-decision-and-one-json-document.md +119 -0
- simt_dev/vendored/docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md +103 -0
- simt_dev/vendored/docs/decisions/ADR-037-unchanged-means-writing-would-not-change-a-byte.md +100 -0
- simt_dev/vendored/docs/decisions/ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md +95 -0
- simt_dev/vendored/docs/decisions/ADR-039-a-ci-step-runs-a-form-that-can-fail.md +101 -0
- simt_dev/vendored/docs/decisions/ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md +158 -0
- simt_dev/vendored/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +92 -0
- simt_dev/vendored/docs/decisions/ADR-042-a-declared-glob-must-match-a-path-built-from-it.md +94 -0
- simt_dev/vendored/docs/decisions/ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md +90 -0
- simt_dev/vendored/docs/decisions/ADR-044-compatibility-is-not-evidence.md +116 -0
- simt_dev/vendored/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +119 -0
- simt_dev/vendored/docs/decisions/ADR-046-one-rule-one-copy.md +140 -0
- simt_dev/vendored/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +141 -0
- simt_dev/vendored/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +150 -0
- simt_dev/vendored/docs/decisions/ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md +153 -0
- simt_dev/vendored/docs/decisions/ADR-050-a-byte-count-is-not-a-region-and-updated-is-not-a-change-of-rules.md +102 -0
- simt_dev/vendored/docs/decisions/ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md +134 -0
- simt_dev/vendored/docs/decisions/ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md +101 -0
- simt_dev/vendored/docs/decisions/ADR-053-the-node-floor-is-22-20-0.md +49 -0
- simt_dev/vendored/docs/decisions/ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md +100 -0
- simt_dev/vendored/docs/decisions/ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md +98 -0
- simt_dev/vendored/docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md +160 -0
- simt_dev/vendored/docs/decisions/ADR-057-an-identity-is-per-developer-and-a-repository-does-not-own-it.md +115 -0
- simt_dev/vendored/docs/decisions/ADR-058-one-tool-three-channels-and-the-order-is-forced.md +148 -0
- simt_dev/vendored/docs/decisions/README.md +530 -0
- simt_dev/vendored/graph/skills.graph.json +1622 -0
- simt_dev/vendored/package.json +43 -0
- simt_dev/vendored/rules/core.md +76 -0
- simt_dev/vendored/rules/mdc/intake-questions.mdc +50 -0
- simt_dev/vendored/rules/mdc/preview-postflight.mdc +61 -0
- simt_dev/vendored/rules/mdc/slice-commit.mdc +52 -0
- simt_dev/vendored/rules/mdc/test-gate.mdc +54 -0
- simt_dev/vendored/rules/stacks/android.md +63 -0
- simt_dev/vendored/rules/stacks/cloudflare-workers.md +65 -0
- simt_dev/vendored/rules/stacks/connectiq.md +62 -0
- simt_dev/vendored/rules/stacks/container.md +31 -0
- simt_dev/vendored/rules/stacks/jvm.md +54 -0
- simt_dev/vendored/rules/stacks/node.md +45 -0
- simt_dev/vendored/rules/stacks/python.md +48 -0
- simt_dev/vendored/rules/stacks/react-native.md +58 -0
- simt_dev/vendored/rules/stacks/react-web.md +61 -0
- simt_dev/vendored/rules/stacks/typescript.md +21 -0
- simt_dev/vendored/rules/stacks/uv.md +32 -0
- simt_dev/vendored/src/argv.mjs +116 -0
- simt_dev/vendored/src/ci/dependabot.mjs +268 -0
- simt_dev/vendored/src/ci/workflow.mjs +360 -0
- simt_dev/vendored/src/commands/ci.mjs +285 -0
- simt_dev/vendored/src/commands/compare.mjs +195 -0
- simt_dev/vendored/src/commands/graph.mjs +73 -0
- simt_dev/vendored/src/commands/help.mjs +108 -0
- simt_dev/vendored/src/commands/hooks.mjs +471 -0
- simt_dev/vendored/src/commands/index.mjs +241 -0
- simt_dev/vendored/src/commands/init.mjs +632 -0
- simt_dev/vendored/src/commands/mcp.mjs +63 -0
- simt_dev/vendored/src/commands/rules.mjs +374 -0
- simt_dev/vendored/src/commands/skills.mjs +647 -0
- simt_dev/vendored/src/commands/skillsinstall.mjs +515 -0
- simt_dev/vendored/src/commands/sync.mjs +331 -0
- simt_dev/vendored/src/commands/upgrade.mjs +202 -0
- simt_dev/vendored/src/compare/discover.mjs +212 -0
- simt_dev/vendored/src/compare/hooksstate.mjs +159 -0
- simt_dev/vendored/src/compare/row.mjs +299 -0
- simt_dev/vendored/src/decisions/index.mjs +536 -0
- simt_dev/vendored/src/graph/load.mjs +541 -0
- simt_dev/vendored/src/hooks/capability.mjs +129 -0
- simt_dev/vendored/src/hooks/dispatcher.mjs +268 -0
- simt_dev/vendored/src/hooks/gates.mjs +144 -0
- simt_dev/vendored/src/hooks/resolve.mjs +194 -0
- simt_dev/vendored/src/hooks/shim.mjs +140 -0
- simt_dev/vendored/src/hooks/where.mjs +118 -0
- simt_dev/vendored/src/identity.mjs +218 -0
- simt_dev/vendored/src/locate.mjs +75 -0
- simt_dev/vendored/src/mcp/server.mjs +167 -0
- simt_dev/vendored/src/mcp/tools.mjs +410 -0
- simt_dev/vendored/src/name.mjs +95 -0
- simt_dev/vendored/src/output/index.mjs +96 -0
- simt_dev/vendored/src/projection/baselines.mjs +209 -0
- simt_dev/vendored/src/projection/conflicts.mjs +205 -0
- simt_dev/vendored/src/projection/manifest.mjs +478 -0
- simt_dev/vendored/src/projection/markers.mjs +349 -0
- simt_dev/vendored/src/projection/mdc.mjs +327 -0
- simt_dev/vendored/src/projection/project.mjs +504 -0
- simt_dev/vendored/src/projection/rules.mjs +640 -0
- simt_dev/vendored/src/projection/surfaces.mjs +398 -0
- simt_dev/vendored/src/projection/targets.mjs +221 -0
- simt_dev/vendored/src/resolve/fingerprint.mjs +553 -0
- simt_dev/vendored/src/resolve/near.mjs +61 -0
- simt_dev/vendored/src/resolve/resolve.mjs +457 -0
- simt_dev/vendored/src/resolve/source.mjs +105 -0
- simt_dev/vendored/src/toolchain.mjs +202 -0
- simt_dev/vendored/src/tsconfig.mjs +314 -0
- simt_dev/vendored/src/upgrade/plan.mjs +203 -0
- simt_dev/vendored/src/workspace.mjs +289 -0
- simt_dev-0.2.0.dist-info/METADATA +150 -0
- simt_dev-0.2.0.dist-info/RECORD +138 -0
- simt_dev-0.2.0.dist-info/WHEEL +4 -0
- simt_dev-0.2.0.dist-info/entry_points.txt +2 -0
- simt_dev-0.2.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
# Decisions
|
|
2
|
+
|
|
3
|
+
Architecture decision records for the SIMT development ecosystem. One canonical home per
|
|
4
|
+
decision; nothing here is duplicated into a repo's `AGENTS.md`.
|
|
5
|
+
|
|
6
|
+
A decision is superseded by writing a new ADR with a higher number and editing the old
|
|
7
|
+
record's status line — never by editing the ruling in place.
|
|
8
|
+
|
|
9
|
+
| ADR | Decision | Status |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| [001](ADR-001-push-requires-approval.md) | Push requires explicit approval | accepted |
|
|
12
|
+
| [002](ADR-002-refactor-blast-radius.md) | Blast radius bounds refactoring; a third copy is a defect | accepted |
|
|
13
|
+
| [003](ADR-003-test-breadth.md) | Focused tests by default; a full suite needs a named trigger | accepted |
|
|
14
|
+
| [004](ADR-004-hook-owns-format.md) | The hook owns format and lint for staged files | accepted |
|
|
15
|
+
| [005](ADR-005-file-length.md) | File length: 400 review trigger, 1000 hard ceiling | accepted |
|
|
16
|
+
| [006](ADR-006-coverage-bar.md) | Evidence discipline is central; the numeric bar is per-repo | accepted |
|
|
17
|
+
| [007](ADR-007-section-skeleton.md) | One canonical section skeleton | accepted |
|
|
18
|
+
| [008](ADR-008-verdict-vocabulary.md) | One review verdict vocabulary: three values | accepted |
|
|
19
|
+
| [009](ADR-009-skill-layout.md) | Skills are `<name>/SKILL.md` with mandatory frontmatter | accepted |
|
|
20
|
+
| [010](ADR-010-runner-invocation.md) | Invoke through the repo's own runner, never a package runner | accepted |
|
|
21
|
+
| [011](ADR-011-windows-support.md) | Windows is supported from v1 for the CLI | **superseded by 012** |
|
|
22
|
+
| [012](ADR-012-drop-windows-support.md) | Windows support dropped; POSIX only | accepted |
|
|
23
|
+
| [013](ADR-013-genericity-by-facts-and-local-profiles.md) | Genericity comes from emitted facts and repo-local profiles | accepted |
|
|
24
|
+
| [014](ADR-014-workspace-is-configuration.md) | The workspace is configuration, and a single repo is a supported shape | accepted |
|
|
25
|
+
| [015](ADR-015-a-source-is-content-not-a-location.md) | A source is identified by its content; the repo is a location, not a precondition | accepted |
|
|
26
|
+
| [016](ADR-016-a-divergent-rule-is-retired-not-merged.md) | A divergent rule is retired by a union, not merged by a variant | accepted |
|
|
27
|
+
| [017](ADR-017-a-declared-gate-must-be-runnable.md) | A declared gate must be one the repo can actually run | accepted |
|
|
28
|
+
| [018](ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md) | An unset hooks path is not an absent hook | accepted |
|
|
29
|
+
| [019](ADR-019-a-declared-command-is-the-runner.md) | A declared command is the runner; the remedy must be one the reader can take | accepted |
|
|
30
|
+
| [020](ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md) | A rule is written per surface, and pointed at — never copied | accepted |
|
|
31
|
+
| [021](ADR-021-one-surface-per-repo.md) | One surface per repo, chosen or defaulted to opencode | accepted |
|
|
32
|
+
| [022](ADR-022-a-key-simt-dev-reads-is-prefixed.md) | A key simt-dev reads is prefixed `simt-`; an editor's key is not | accepted |
|
|
33
|
+
| [023](ADR-023-the-gate-ships-as-a-file-the-repo-owns.md) | The gate ships as bytes the repo owns, written once where Actions already runs | accepted · ruling 2 superseded by 024 |
|
|
34
|
+
| [024](ADR-024-simt-dev-overwrites-its-own-gate-file.md) | simt-dev overwrites its own gate file, and no repository else's | accepted |
|
|
35
|
+
| [025](ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md) | A baseline is a region in a file the repo owns, and only a file that can hold one | accepted · rulings 1 and 2 narrowed by 026 · ruling 8 withdrawn by 048 |
|
|
36
|
+
| [026](ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md) | A config whose grammar cannot hold a region is a file the repo owns | accepted |
|
|
37
|
+
| [027](ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md) | The one gitattributes line that is neither dangerous nor empty | accepted |
|
|
38
|
+
| [028](ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md) | A base file is a floor, so the disagreement it cannot repair becomes a check | accepted |
|
|
39
|
+
| [029](ADR-029-the-pins-a-repository-declares-are-its-own.md) | The pins a repository declares are its own, so a check reports them and never writes them | accepted |
|
|
40
|
+
| [030](ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md) | What a plan does not ship is a decision with a record, not a silence | accepted |
|
|
41
|
+
| [031](ADR-031-the-suite-stages-the-environment-it-needs.md) | The suite stages the environment it needs, because a test that inherits the author's directory is not evidence | accepted |
|
|
42
|
+
| [032](ADR-032-provenance-is-a-content-digest-not-a-commit.md) | Provenance is a digest of the content that leaves this package, not a commit id | accepted |
|
|
43
|
+
| [033](ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md) | Three plan rows are closed by ruling on them, not by authoring what they asked for | accepted |
|
|
44
|
+
| [034](ADR-034-a-staged-tool-refuses-a-partially-staged-file.md) | A staged tool refuses a partially staged file rather than staging around it | accepted |
|
|
45
|
+
| [035](ADR-035-one-write-decision-and-one-json-document.md) | One place decides whether a run may write, and one document says whether it did | accepted |
|
|
46
|
+
| [036](ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md) | A command that writes says so, and every instruction that names it moves with it | accepted |
|
|
47
|
+
| [037](ADR-037-unchanged-means-writing-would-not-change-a-byte.md) | `unchanged` means writing would not change a byte, not that the body matches | accepted |
|
|
48
|
+
| [038](ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md) | The gate filter belongs to every writer of a manifest, not to `init` | accepted |
|
|
49
|
+
| [039](ADR-039-a-ci-step-runs-a-form-that-can-fail.md) | A gate a CI step runs must have a form that can fail, and a refusal is a failure | accepted |
|
|
50
|
+
| [040](ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md) | The last writer writing by default was not the one the finding named | accepted |
|
|
51
|
+
| [041](ADR-041-the-suite-installs-the-package-in-order-to-test-it.md) | The suite installs the package in order to test it | accepted |
|
|
52
|
+
| [042](ADR-042-a-declared-glob-must-match-a-path-built-from-it.md) | A declared glob must match a path built from it | accepted |
|
|
53
|
+
| [043](ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md) | A keyword is a phrase, and two letters is a word | accepted |
|
|
54
|
+
| [044](ADR-044-compatibility-is-not-evidence.md) | Compatibility is not evidence, and a resolver nobody measured is merely felt | accepted |
|
|
55
|
+
| [045](ADR-045-a-dashboard-that-counts-a-defect.md) | A dashboard that counts a defect and will not name it is worse than one that does not count it | accepted |
|
|
56
|
+
| [046](ADR-046-one-rule-one-copy.md) | One rule, one copy: a reader that reads AGENTS.md must not also get the file | accepted |
|
|
57
|
+
| [047](ADR-047-a-profile-selects-on-what-a-repository-runs-on.md) | A profile selects on what a repository runs on, not on what it is written in | accepted · refines ADR-013 r1 |
|
|
58
|
+
| [048](ADR-048-a-baseline-carries-only-keys-a-reader-honours.md) | A baseline carries only keys a reader honours | accepted · withdraws ADR-025 r8 |
|
|
59
|
+
| [049](ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md) | A ruling that a later record moved says so where it is printed | accepted |
|
|
60
|
+
| [050](ADR-050-a-byte-count-is-not-a-region-and-updated-is-not-a-change-of-rules.md) | A byte count is not a region, and `updated` is not a change of rules | accepted |
|
|
61
|
+
| [051](ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md) | A pointer is the reader's only route, so it is not a convenience | accepted |
|
|
62
|
+
| [052](ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md) | A dependency is counted against the budget, and there is one budget | accepted |
|
|
63
|
+
| [053](ADR-053-the-node-floor-is-22-20-0.md) | The Node floor is 22.20.0 | accepted |
|
|
64
|
+
| [054](ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md) | The digest covers every byte that leaves the package, and a cause is read off the body | accepted · amends ADR-032 |
|
|
65
|
+
| [055](ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md) | A status line travels, so it may not name a location; prose is the record's own argument | accepted |
|
|
66
|
+
| [056](ADR-056-three-names-one-of-which-is-cheap-to-move.md) | The command, the package and the project are three names, and only one of them is cheap to move | accepted · amends ADR-023 |
|
|
67
|
+
| [057](ADR-057-an-identity-is-per-developer-and-a-repository-does-not-own-it.md) | An identity is per-developer, and a repository does not own it | accepted |
|
|
68
|
+
| [058](ADR-058-one-tool-three-channels-and-the-order-is-forced.md) | One tool, three channels, and the order is forced | accepted |
|
|
69
|
+
|
|
70
|
+
ADR-001 to ADR-010 resolve the contradictions found across the 16 repos in the 2026-10-05
|
|
71
|
+
audit; each record names the repos whose text it replaces. ADR-011 and ADR-012 are the
|
|
72
|
+
platform decision for the CLI described in
|
|
73
|
+
[plan 0002](../plans/0002-simt-dev-cli.md), and they are a worked example of the
|
|
74
|
+
supersession rule above: 011 was accepted, 012 reversed it the same day, and 011's record was
|
|
75
|
+
kept rather than rewritten because the cost table it contains is the evidence 012 relies on.
|
|
76
|
+
ADR-013 is the same rule applied to the tool rather than to the repos: the mechanism carries
|
|
77
|
+
no product taxonomy, and adding an ecosystem is a profile file rather than a code change.
|
|
78
|
+
ADR-014 and ADR-015 are the same rule applied to the **filesystem**. ADR-014 made the workspace
|
|
79
|
+
optional; ADR-015 found that a repository is itself a location for a declared source, so a clone of
|
|
80
|
+
one repo is self-sufficient with no configuration at all. Both were found by running the tool the
|
|
81
|
+
way a consumer runs it, not the way its author does: the tool's own directory layout turned out to
|
|
82
|
+
be part of its interface, and every test that ran from the checkout could not see that it only
|
|
83
|
+
worked from the checkout.
|
|
84
|
+
|
|
85
|
+
ADR-016 applies the rule to rule content itself. The estate's three always-on `.mdc` families
|
|
86
|
+
existed in eight copies, none byte-identical, the worst pair differing by one byte while hiding
|
|
87
|
+
three semantic losses. The canonical body is therefore the **union** of what every copy needed —
|
|
88
|
+
not a per-stack variant, because two of the differences were rules each repo was missing rather
|
|
89
|
+
than a choice either repo made.
|
|
90
|
+
|
|
91
|
+
ADR-031 is the same lesson arriving from the other end. ADR-014 and ADR-015 fixed the tool's
|
|
92
|
+
dependence on the checkout; ADR-031 fixes the **suite's**, which had quietly reintroduced it —
|
|
93
|
+
nineteen tests read skill bodies out of sibling repositories and passed only on a machine that
|
|
94
|
+
had them. The gate had been red on every push since the first baseline record landed. What makes
|
|
95
|
+
this one worth a paragraph rather than a table row is the third cause it found: two tests that
|
|
96
|
+
passed for a reason that had nothing to do with the code, because the checkout's directory
|
|
97
|
+
happened to be named `simt-dev`. Those failed on nobody's machine and would have failed for
|
|
98
|
+
anyone who cloned the repository under a different name. A green test that asserts a folder's
|
|
99
|
+
name is worse than a red one, because it looks like evidence.
|
|
100
|
+
|
|
101
|
+
ADR-032 is that lesson inside the tool rather than around it. The provenance stamp was read
|
|
102
|
+
with `git rev-parse HEAD`, run in the package root — and for anyone who installed the package
|
|
103
|
+
into a repository, which is the entire audience, git walks *up* out of `node_modules` and
|
|
104
|
+
answers with the **consumer's** HEAD. It succeeded, so no fallback could catch it. Every
|
|
105
|
+
commit a consumer made moved the stamp, including commits touching nothing the tool projects,
|
|
106
|
+
so `check` reported drift on all three generated files and exited 1 after an unrelated commit,
|
|
107
|
+
and `upgrade` named a cause that was false. Where git could not answer at all the answer was
|
|
108
|
+
`unknown`, which the marker grammar cannot read back and `upgrade` compared against a real pin
|
|
109
|
+
as though the two were versions. The stamp now identifies the content that leaves the package,
|
|
110
|
+
which is the one thing every install can compute.
|
|
111
|
+
|
|
112
|
+
ADR-033 is the same instinct applied to a plan instead of a tool. Closing plan 0001 meant auditing
|
|
113
|
+
each row against the tree, and three rows asked for things that had never been decided: seven agent
|
|
114
|
+
policies with no surviving source anywhere in the estate, a skill-collapse step that a mechanism
|
|
115
|
+
landed two hours after the plan was written already answered, and two `local.properties` files that
|
|
116
|
+
were committed on purpose. Each is closed by saying so, because the failure the plan's own text
|
|
117
|
+
invites is a reader concluding a mechanism is outstanding and going to build it.
|
|
118
|
+
|
|
119
|
+
ADR-034 is the same instinct in a hook, and the second time the defect was a promise the code
|
|
120
|
+
already made in writing. `src/hooks/gates.mjs` defines a staged gate as one that runs over the
|
|
121
|
+
staged files only, precisely so that "whole-repo formatting from a pre-commit hook" cannot
|
|
122
|
+
"rewrite unstaged work the author did not ask to touch" — and the dispatcher then ran
|
|
123
|
+
`git add` on every staged path. A file staged with `git add -p` came back with its unstaged
|
|
124
|
+
hunk replaced, not merely committed, and the gate said nothing. The same loop also fed
|
|
125
|
+
staged deletions to `git add`, whose failure was swallowed, so a commit holding a deletion and
|
|
126
|
+
a formattable file recorded unformatted bytes silently. Both are the shape this estate's rules
|
|
127
|
+
keep warning about: a control that appears to hold, reports pass, and does not.
|
|
128
|
+
|
|
129
|
+
ADR-035 is the same thing measured across a whole command surface instead of one control.
|
|
130
|
+
Six commands disagreed about what `--yes`, `--dry-run` and `--json` mean: `upgrade` read
|
|
131
|
+
`--yes` and ignored `--dry-run`, `skills install` read neither and printed "nothing written"
|
|
132
|
+
on the path that wrote, and `doctor --json` and `hooks status --json` both reported `ok: true`
|
|
133
|
+
while the same commands in a terminal exited non-zero. Two of those are the shape above
|
|
134
|
+
again — a check that appears to hold and reports pass. The difference is where the fix goes:
|
|
135
|
+
one function decides whether a run may write, and one table asserts the JSON contract for
|
|
136
|
+
every command at once, because a contract checked one command at a time is checked by
|
|
137
|
+
whoever remembers.
|
|
138
|
+
|
|
139
|
+
ADR-036 is the deferral that record made, settled. ADR-035 left `sync` writing by default
|
|
140
|
+
because the change belonged to a slice that depended on it, and wrote the reason down
|
|
141
|
+
twice rather than quietly half-applying it. Measured afterwards, `sync` was the only
|
|
142
|
+
writer left that rewrote a file a bare invocation had no permission to touch — and it is
|
|
143
|
+
the command a caller reaches for most, because it is the one that regenerates the text in
|
|
144
|
+
front of them. The second half is the half that would have been easy to skip: the note
|
|
145
|
+
inside every generated region and the comment in every manifest both tell the reader to
|
|
146
|
+
run that command, and both ship into repositories that have no other copy of the
|
|
147
|
+
instruction. A flag that changes what a command does without changing what the tool says
|
|
148
|
+
about it leaves a permanently-false instruction in every repository in the estate, which is
|
|
149
|
+
the same failure as a check that reports pass and does not hold — one layer up.
|
|
150
|
+
|
|
151
|
+
ADR-037 is the same failure inside the projection primitive, and the first record here whose
|
|
152
|
+
finding arrived with a line number and turned out to be two branches over. Review 0002's S29
|
|
153
|
+
pointed at the code that appends a region to a document that has none; that branch was
|
|
154
|
+
already correct. The blank line was missing in the code that *replaces* one, so the defect
|
|
155
|
+
only appeared once a repository already had a region — which is every repository that has
|
|
156
|
+
been adopted. The list case is worth reading for what it says about the failure: with no
|
|
157
|
+
separating gap, a Markdown formatter reads the whole region as a continuation of the last
|
|
158
|
+
list item and indents every line of it, so the file comes back changed in a way no amount
|
|
159
|
+
of careful body comparison would notice. The second half is the part that made the first
|
|
160
|
+
worth doing: a body that matched behind a wrong seam was reported `unchanged`, so `check`
|
|
161
|
+
found nothing to report and `sync` had nothing to repair, and the repository was red on its
|
|
162
|
+
own formatter with no remedy on offer. Fixing the seam without that half would have shipped
|
|
163
|
+
a fix that worked once, for the one repository whose rules happened to change next.
|
|
164
|
+
|
|
165
|
+
ADR-038 is the same failure once more, and the smallest instance of it so far: a decision
|
|
166
|
+
record that named the right rule and attributed it to the wrong command. ADR-017 ruled that
|
|
167
|
+
a gate is declared only if the repo can run it, and wrote *"`init` filters the stack table's
|
|
168
|
+
proposals"* — so for as long as `init` was the only writer of a manifest, the belief and the
|
|
169
|
+
code agreed. `skills install` also writes one, for a repo that has none, and it did not
|
|
170
|
+
probe. The result was four gates in a repo with no scripts and no formatter, `check` red on
|
|
171
|
+
all four, and a pre-commit hook that blocked the first commit it was installed in front of.
|
|
172
|
+
Nothing about the rule was wrong; only the sentence that carried it. That is what the
|
|
173
|
+
`Affects:` line on a record is for, and this record amends one in place of editing it —
|
|
174
|
+
because a reader who finds the record and trusts it has to be able to trust the whole of it.
|
|
175
|
+
|
|
176
|
+
ADR-039 is the same shape turned on the tool's own controls, and it is the only one of these
|
|
177
|
+
whose subject is a gate that was enforcing a gate. This repository's workflow carries a step
|
|
178
|
+
named "the gate copy in this repo is current", commented as the place the drift would be
|
|
179
|
+
caught, running `simt-dev ci`. With a hand edit in that copy, `ci` printed the drift and
|
|
180
|
+
exited 0 — so the step read the thing it was written to catch, said it out loud, and passed.
|
|
181
|
+
Every other record here is about a check that reports pass and does not; this one is about
|
|
182
|
+
the check on the checks. The same slice found two smaller instances of the same habit in the
|
|
183
|
+
same command: `--json --dry-run` printed no document at all, because the mode guard was in
|
|
184
|
+
the wrong place, and a refusal in a repository that adopted simt-dev printed a paragraph
|
|
185
|
+
saying it was refused directly above `ok: true` and exited 0.
|
|
186
|
+
|
|
187
|
+
ADR-040 is the only one here that says a previous record's *premise* was wrong, and it is worth
|
|
188
|
+
reading for the shape rather than the command. Review 0002 opened Wave 0 with "`sync` is the only
|
|
189
|
+
writer that writes by default" and named five siblings that "all preview first". Four were. The
|
|
190
|
+
fifth, `hooks install`, read `flags.yes` and `flags.dryRun` directly, imported no write decision
|
|
191
|
+
at all, and wrote the shims and repointed `core.hooksPath` whenever neither flag said otherwise —
|
|
192
|
+
in the ordinary case, where ADR-018's takeover consent never fired. Three strings that ship into
|
|
193
|
+
adopting repositories assumed the preview, one of them inside the shim being installed, and 586
|
|
194
|
+
tests passed. What makes it a rule rather than a fix is that the same commit had to amend a
|
|
195
|
+
comment written the day before, which called `hooks install` a previewing writer; a comment that
|
|
196
|
+
enumerates which commands follow a rule is a claim about code its file does not own, and the next
|
|
197
|
+
reader has no cheap way to check it. So the writers are a set, the set is checked by a table, and
|
|
198
|
+
the table asserts the exit code as well as the bytes — because a command that *refuses* also
|
|
199
|
+
changes nothing, and "wrote nothing" is the one claim a refusal satisfies for free.
|
|
200
|
+
|
|
201
|
+
ADR-041 is the one that closes the gap those other records kept falling into. Every test in this
|
|
202
|
+
repository imported from `../src`, which is right for a unit and blind to the artefact: S01 shipped
|
|
203
|
+
a package whose `files` list and whose runtime reads disagreed, with the whole suite green, because
|
|
204
|
+
no test was reading the tarball. The fix is not a better assertion but a different subject — pack
|
|
205
|
+
the working tree, install it with `--offline`, and drive `node_modules/.bin/simt-dev`. `--offline`
|
|
206
|
+
is the load-bearing word, and it is a constraint rather than a setting: the package has no
|
|
207
|
+
dependencies to install, so adding a build-time or test-time tool starts costing a network on a
|
|
208
|
+
cold runner, and a suite that fails there gets skipped, and a skipped test is indistinguishable
|
|
209
|
+
from a passing one. The same constraint is why one thing that test cannot assert is recorded as
|
|
210
|
+
open instead of worked around: there is no formatter to run over the output, so S29's second
|
|
211
|
+
acceptance half stays pending and the seam it would have caught is asserted structurally in
|
|
212
|
+
`markers.test.mjs` instead.
|
|
213
|
+
|
|
214
|
+
ADR-042 is the smallest instance of the same habit: a declared value nobody ever executes. 13 of the
|
|
215
|
+
registry's 65 path globs began with a double star, which compiled to an invalid regex that a `catch`
|
|
216
|
+
turned into `false`, so the heaviest signal the resolver has was silently dropped for every skill
|
|
217
|
+
that used one. The lint rule that keeps the registry honest asked only whether `paths` was *present* —
|
|
218
|
+
the same question, in the same file, that `checkStack` had already answered for stack tags by
|
|
219
|
+
rejecting a tag the detector cannot emit. A glob that matches nothing is that failure under another
|
|
220
|
+
field name, so `graph lint` now requires each declared glob to match a path built from itself. Its
|
|
221
|
+
third ruling is the part worth carrying forward: the registry-wide test reads the same generator the
|
|
222
|
+
lint rule validates, so narrowing the generator narrows the test in step, and the test passed with
|
|
223
|
+
the generator sabotaged. A check that shares its subject with the thing it checks has to have its own
|
|
224
|
+
assertion about that subject.
|
|
225
|
+
|
|
226
|
+
ADR-043 is the same failure in the other half of the resolver, and the word that connects them is
|
|
227
|
+
*declared*. A hand-authored field is checked for being present and well-formed, which is a question
|
|
228
|
+
about the file, while the resolver asks whether the value can reach anything — and a value nobody
|
|
229
|
+
executed costs nothing to write. Thirteen globs matched nothing (ADR-042); thirteen keywords could
|
|
230
|
+
never be produced by a task, including `durable object`, declared on three skills and dead on all
|
|
231
|
+
three. Two filters were responsible: a tokenizer that discarded every word of length two, which in
|
|
232
|
+
this domain is where `d1`, `r2`, `kv` and `ci` live, and a keyword comparison that asked whether one
|
|
233
|
+
task token equalled the declaration, which no multi-word keyword can ever satisfy. Both were filters
|
|
234
|
+
on precision applied to a registry whose values were never reachable in the first place — the same
|
|
235
|
+
inversion, in the same file, one field over. It also records what the fix costs, because it does
|
|
236
|
+
cost something: six two-letter stopwords now reach the summary signal, while `the` already selected
|
|
237
|
+
eleven skills before the change. The honest reading is that S04's threshold was too low all along,
|
|
238
|
+
and hiding that by putting the floor back to 3 would make `d1` disappear again.
|
|
239
|
+
|
|
240
|
+
ADR-044 is what came after the two above, and it is the first of the three that is about the shape of
|
|
241
|
+
an argument rather than about a broken value. The resolver asked two questions of a skill — *is this
|
|
242
|
+
compatible with my repo* and *is this about my task* — and scored them into one number. The gate had
|
|
243
|
+
already answered the first, so a skill matching three stack tags reached 45 points having said nothing
|
|
244
|
+
about the task, and any score above zero selected. Meanwhile a shared word of English was worth 8 and
|
|
245
|
+
cleared the bar, so a brief for "fix a typo in the README" filled a 20 KB budget with launch galaxies.
|
|
246
|
+
The rule it lands on is one sentence: compatibility is a precondition, not evidence, and a
|
|
247
|
+
precondition must not be counted twice. It also carries the first gold corpus, because plan 0002 has
|
|
248
|
+
claimed an `eval` corpus since before this file existed and every ranking change until now was argued
|
|
249
|
+
from examples. The corpus immediately earned its keep by failing a case where the resolver was right and
|
|
250
|
+
the author was wrong — two skills written into a rejection list both declare the `contract`
|
|
251
|
+
capability, and a hand-authored gold set is an opinion written down. Its fourth mutation is the one to
|
|
252
|
+
remember: reverting the third of three commands to its own older copy of the rule passed all 621 tests,
|
|
253
|
+
because nothing checked the set. ADR-035 said a promise about a set of commands holds only while
|
|
254
|
+
something checks the set; that sentence was written about seven writers and turned out to be about
|
|
255
|
+
three readers too.
|
|
256
|
+
|
|
257
|
+
ADR-046 is the third of these records to be settled by running the command against the estate
|
|
258
|
+
rather than by reading the code, and the first to overturn an earlier ruling on a fact about a
|
|
259
|
+
vendor. ADR-021 decided that a Cursor reader could not be reached through `AGENTS.md` — so a repo on
|
|
260
|
+
`opencode,cursor` was given the same three rules twice, once folded and once in `.mdc`, across five
|
|
261
|
+
repositories and 28 KB. Cursor's documentation says plainly that it reads `AGENTS.md`. The claim was
|
|
262
|
+
false when written, and it was false in a way simt-dev's own surface table already contradicted: the
|
|
263
|
+
`readers` line on the opencode row names Cursor as reading `AGENTS.md` natively. A decision that
|
|
264
|
+
depends on a fact should be checked against the file that records facts, and here that file was one
|
|
265
|
+
row away.
|
|
266
|
+
|
|
267
|
+
Its more useful lesson is the one that cost the most. The first version of the change made the
|
|
268
|
+
estate go *quiet*: `staleRuleFiles` fell from 17 to 0 and every one of those files was still on
|
|
269
|
+
disk, still being read twice. Then the fix for that scanned the wrong set and reported nothing at
|
|
270
|
+
all, because a file becomes orphaned exactly when a repo drops the surface that wrote it. Both were
|
|
271
|
+
found by running `compare` over sixteen repositories, not by reading the code. The flip side is worse:
|
|
272
|
+
three of six mutations passed all 627 tests, including the one that would have made a gate nothing
|
|
273
|
+
can satisfy — a file simt-dev will never write again and may never delete. A feature whose failure
|
|
274
|
+
mode is "the tool silently stops looking at something it wrote" needs a negative control, because
|
|
275
|
+
the broken version returns an empty list and passes every positive test.
|
|
276
|
+
|
|
277
|
+
ADR-047 is the same rule applied to what a profile *selects on*, and it refines ADR-013 ruling 1
|
|
278
|
+
rather than overturning it. ADR-013 promised genericity from emitted facts and repo-local
|
|
279
|
+
profiles, and said detection emits facts and profiles select on facts. But the shipped
|
|
280
|
+
`simt-applies` lists were keyed on language tags — `typescript`, `gradle`, `react` — which name
|
|
281
|
+
what a repo is *written in*, not what it *runs on*. So any TypeScript library received Cloudflare
|
|
282
|
+
Workers rules (D1, Durable Objects, wrangler), a Spring backend on Gradle received Compose and
|
|
283
|
+
Wear OS rules, a React Native app received web CSS rules, and a Poetry project received `uv`
|
|
284
|
+
rules. The defect was invisible in the usual way: the selection was deterministic, every test
|
|
285
|
+
passed, and the profile simply landed in the wrong ecosystem. The fix is data plus `has-*`
|
|
286
|
+
facts, exactly as ADR-013 requires — no engine change — and the one piece that needed a mechanism
|
|
287
|
+
was `has-android-manifest`, a nested path, which is the glob walk S07's dependency closed.
|
|
288
|
+
|
|
289
|
+
ADR-048 is the first record here to withdraw a value rather than a mechanism, and it
|
|
290
|
+
found it by reading a format's specification instead of its own source. The shared
|
|
291
|
+
`.editorconfig` shipped `[prettier] singleQuote = true` under a comment explaining why
|
|
292
|
+
that section was the right home for the estate's one formatting disagreement. An
|
|
293
|
+
editorconfig section header is a glob over paths, so `[prettier]` matched a file named
|
|
294
|
+
`prettier`; and `singleQuote` is not one of the nine keys the specification defines, nor
|
|
295
|
+
one of the six properties Prettier maps. The specification says what an undefined key
|
|
296
|
+
does — cores accept and report it, plugins ignore it — which is the worst possible
|
|
297
|
+
outcome: a line that parses, in a file that was current, enforcing nothing. Nothing in
|
|
298
|
+
this tool could have seen it, because the gate compares bytes and the bytes agreed with
|
|
299
|
+
the shipped body. The spelling that does work is `quote_type`, and the survey says not to
|
|
300
|
+
use it: three of the five repositories running Prettier already state `singleQuote` and
|
|
301
|
+
two do not, so a shared value reformats every source file in those two on the day they
|
|
302
|
+
adopt the tool. The lesson is the narrowest one in this file — a baseline earns its place
|
|
303
|
+
by naming a key a reader honours, and the ability to hold a comment says nothing about
|
|
304
|
+
whether anything reads what is inside.
|
|
305
|
+
|
|
306
|
+
ADR-049 is the same failure one level down, and it is the reason this index exists: a
|
|
307
|
+
citation has to be resolvable, or it is decoration. `rules why ADR-023` printed the status
|
|
308
|
+
`ruling 2 superseded by` — a sentence with no object, because the status is a paragraph and
|
|
309
|
+
the parser read one line of it — and then printed ruling 2 as a ruling in force, which is
|
|
310
|
+
the reversal the module's own header names as its failure mode. Making the status a
|
|
311
|
+
paragraph is half the fix and the other half is a trap: the obvious pattern for
|
|
312
|
+
whole-record supersession then matches *ruling 2 superseded by ADR-024*, and five rulings in
|
|
313
|
+
that record that the status line itself says stand would have been dropped by the field
|
|
314
|
+
that exists to stop exactly that. Underneath both halves is the thing neither the finding
|
|
315
|
+
nor the fix mentioned — the rulings are numbered in five different ways across the records
|
|
316
|
+
and the extractor read one, so ADR-045 declared six rulings and printed one, and ADR-013
|
|
317
|
+
printed the literal text `### 2.` to an agent. A supersession names a ruling by number, and
|
|
318
|
+
a number that cannot be located cannot be reported as no longer in force. The record's
|
|
319
|
+
ninth ruling is the mutation that earned its keep: dropping the ordinal from `**One.**`
|
|
320
|
+
passes every test in the suite, because no record in the estate has both a word-numbered
|
|
321
|
+
ruling and a partial supersession to catch it — the failure ADR-042 recorded, reproduced
|
|
322
|
+
inside the fix for it.
|
|
323
|
+
|
|
324
|
+
ADR-050 is the same habit in a command rather than a reader, and its finding named one line
|
|
325
|
+
of code where the defect was two questions. `upgrade` decided a repository had no region by
|
|
326
|
+
asking whether its `AGENTS.md` was empty, which a hand-written one never is — so a version
|
|
327
|
+
bump wrote nine kilobytes of rules into a repository that had never asked for any. But the
|
|
328
|
+
obvious repair, asking whether every *region* is absent, reproduces it exactly: the estate
|
|
329
|
+
builds this state by adopting and then replacing `AGENTS.md` by hand, which leaves the
|
|
330
|
+
baselines' regions in place, and a guard that sees two regions concludes the repository is
|
|
331
|
+
adopted. The question was never about all the files. The second half is a trap of the same
|
|
332
|
+
shape: ADR-037 made a correct body behind a wrong seam report as `updated` so that `sync`
|
|
333
|
+
would repair it, which gave one field two meanings, and the command whose entire job is to
|
|
334
|
+
say *why* a repository is behind was reporting a formatting repair as a change of policy.
|
|
335
|
+
The reader is told to diff rules that did not move. Both are the same mistake: a field read
|
|
336
|
+
for the thing it was not.
|
|
337
|
+
|
|
338
|
+
ADR-051 is the same mistake about a *file*, and it is worth reading for where the belief
|
|
339
|
+
came from. `CLAUDE.md` was classified as a convenience and a misaimed pointer made the gate
|
|
340
|
+
go green, on the reasoning that "the editors that matter already read `AGENTS.md`" — which
|
|
341
|
+
this repository's own pointer table refutes with a quotation from Claude Code's
|
|
342
|
+
documentation: *"Claude reads CLAUDE.md by default and AGENTS.md only as a fallback."* A
|
|
343
|
+
pointer is not a second copy of a file, it is the route to one, and the old test named
|
|
344
|
+
DeepSeek's loader rendering a drifted copy beside its twin. That observation is true and it
|
|
345
|
+
is about duplication; a symlink aimed elsewhere is not duplication, it is a reader looking
|
|
346
|
+
at the wrong file. The belief was written down in a test, so the finding it produced could
|
|
347
|
+
not see it. The second half was worse and quieter: a directory at `CLAUDE.md` — which is
|
|
348
|
+
where Claude Code keeps per-project instructions — was classified as *absent*, so `check`
|
|
349
|
+
said nothing and `sync` crashed with `EEXIST` after writing every other target, naming
|
|
350
|
+
`simt-dev doctor`, which reports nothing about it either. The note that would have said so
|
|
351
|
+
printed `undefined bytes`, and `undefined` in a report reads as nothing at a glance.
|
|
352
|
+
|
|
353
|
+
ADR-052 is the same habit in the resolver, and it is the first record here where the *fix*
|
|
354
|
+
was the second copy of the logic rather than the first. The budget loop existed in `skills
|
|
355
|
+
brief` and in the MCP tool, and both tested `entry.via.size > 0` and inlined a dependency
|
|
356
|
+
whatever it weighed — so `--skill parallel-feature-team --budget 2000` resolved **13,503
|
|
357
|
+
bytes against a 2,000-byte budget**, with nothing in the output saying so. The comment gave
|
|
358
|
+
the reason: *"a skill that is useless without its dependency is worse than a large context,
|
|
359
|
+
because it silently misleads."* The premise is right and the conclusion does not follow: a
|
|
360
|
+
missing dependency is fixed by a line in the output, and an unbounded budget is not a fix
|
|
361
|
+
for anything. The copies had also drifted — one tested `deferred` first and never inlined
|
|
362
|
+
one, the other tested it second and would inline 384 KB — so the same question had two
|
|
363
|
+
answers and neither could see the other. ADR-035's sentence about a promise about a *set*
|
|
364
|
+
of readers holds only while something checks the set; here the check is a test that drives
|
|
365
|
+
both surfaces and compares them, because two copies asserted separately drift and two
|
|
366
|
+
copies asserted against each other do not.
|
|
367
|
+
|
|
368
|
+
ADR-054 is the same habit found in the stamp itself, and it is worth reading for the order
|
|
369
|
+
the two defects arrived in. ADR-032 ruled that the digest covers `rules/` and `graph/` and
|
|
370
|
+
deliberately excludes `src/`, on reasoning that still holds: a refactor that projects
|
|
371
|
+
byte-identical bytes must not move a consumer's stamp. But the exclusion was drawn around a
|
|
372
|
+
*directory*, and the shared `.editorconfig` and `.gitattributes` blocks are not in one — they
|
|
373
|
+
are two string constants in `src/projection/baselines.mjs`, and every adopting repository
|
|
374
|
+
receives them. By ADR-015's own rule a source is identified by its content rather than by the
|
|
375
|
+
file it lives in, so those bodies were excluded content, not excluded code. What exposed it
|
|
376
|
+
was an unrelated slice: deleting seven inert lines from a shipped baseline was the first
|
|
377
|
+
change here that moved bytes out of `src/` without moving the digest. `check` caught it,
|
|
378
|
+
because the gate compares bytes. `upgrade` did not, and printed a pin that was unchanged
|
|
379
|
+
beside a restamped region, and then named the cause as *the rules changed* — two false
|
|
380
|
+
sentences, one caused by the other. With the stamp silent about a baseline, the only cause
|
|
381
|
+
left to report was the one least likely to be true, and the reader is sent to diff `rules/`
|
|
382
|
+
when the change is in `.editorconfig`. So the fix is two halves that have to land together:
|
|
383
|
+
the digest now covers the rendered baseline bodies and names them by the file they become,
|
|
384
|
+
and `upgrade` reads its cause off the projected body with the two kinds of content counted
|
|
385
|
+
apart. The second half is the one worth keeping, because it is what makes a stamp that
|
|
386
|
+
cannot see a change stop being able to invent one.
|
|
387
|
+
|
|
388
|
+
ADR-055 is the same rule applied to the record index, and it is worth reading for a finding
|
|
389
|
+
that grew while it was being fixed. The review filed this as "two records out of
|
|
390
|
+
forty-eight, pre-existing". Re-measuring the corpus said five — three of them written after
|
|
391
|
+
that measurement, including two in this cycle, by the same convention each time: put the
|
|
392
|
+
clause in `Status:`, because `Status:` is the line the tool reads. It is the one line
|
|
393
|
+
guaranteed to travel, into repositories that own none of this package's files, so the path
|
|
394
|
+
arrives there resolving against nothing. `Affects:` is the field for it and is deliberately
|
|
395
|
+
not printed; five records had one available and used the other. What makes the record worth
|
|
396
|
+
keeping is ruling four and its cost: the ruling covers `Status:` and deliberately not the
|
|
397
|
+
record's own argument, because `docs/plans/` means this package's plan in one record and the
|
|
398
|
+
reader's own plans in another, and four records use that exact string for both — so a
|
|
399
|
+
redaction there would corrupt them to protect six. Six prose leaks therefore remain, and a
|
|
400
|
+
test asserts the count and the ids rather than leaving them as an unmeasured shrug. The
|
|
401
|
+
docstring that used to claim the whole output "never contains a path" is withdrawn: it
|
|
402
|
+
asserted a property the function never had, which is the same defect as a citation that
|
|
403
|
+
cannot be resolved — a sentence that reads like evidence and carries none.
|
|
404
|
+
|
|
405
|
+
## The rule these records enforce
|
|
406
|
+
|
|
407
|
+
Every shared rule has exactly **one** canonical home. Every other location is either a
|
|
408
|
+
generated region or a thin pointer. Repeated policy text, duplicated source-of-truth, verbose
|
|
409
|
+
prose and redundant examples are **review blockers**, not style nits.
|
|
410
|
+
|
|
411
|
+
This ruling came out of the estate's own review agent, which already carries it as a checklist
|
|
412
|
+
item — in exactly one file, aimed at exactly the duplication the audit found.
|
|
413
|
+
|
|
414
|
+
ADR-017, ADR-018 and the one before them share a cause worth naming. Each was found by
|
|
415
|
+
running the tool the way a consumer runs it — against a real repository, doing the thing a
|
|
416
|
+
person would do — and not by a unit test. A test exercises the function; the defect lived in
|
|
417
|
+
what the function was asked, in a location it never looked, or on a surface the JSON never
|
|
418
|
+
carried. Two of the three were also cases of the same question being answered in two places:
|
|
419
|
+
ADR-018's guard could not see the default hooks directory because it looked only at a config
|
|
420
|
+
key, and `hooks status` carried its own copy of the read logic the shared module already had.
|
|
421
|
+
|
|
422
|
+
A fourth joins them. ADR-019 is the same shape one layer over: `simt init` dropped all
|
|
423
|
+
five gates on `repo-05` and told the reader to add a script to a `package.json` the
|
|
424
|
+
repository will never have. The facts were right, the remedy was not followable, and once
|
|
425
|
+
the repository declared what it does have, the dispatcher, `check` and `hooks status`
|
|
426
|
+
gave three different answers about one manifest. Every one of these shipped green because
|
|
427
|
+
the verification stopped at the tool's own gate.
|
|
428
|
+
|
|
429
|
+
A fifth joins them, and it is the first about *where* the rules land rather than what they
|
|
430
|
+
say. ADR-020 found that the tool's own conflict scanner read `.github/instructions/` while
|
|
431
|
+
the projector wrote nothing there, so three of the four editors that read this repository's
|
|
432
|
+
rules never received the always-on process rules at all — Cursor did. The fix was not a
|
|
433
|
+
missing branch but a missing table: every editor is now a named row carrying the document it
|
|
434
|
+
was verified against, and the test suite fails if that row emits a frontmatter key the vendor
|
|
435
|
+
does not document. The failure it would have caught is quiet in both directions — an editor
|
|
436
|
+
that ignores an unknown key, and an editor that discards a rule whose `trigger` it does not
|
|
437
|
+
recognise, which is what Antigravity documents.
|
|
438
|
+
|
|
439
|
+
A sixth is about a key rather than a byte, and it came from a question asked about this
|
|
440
|
+
repository's own rule files rather than from a command anyone ran. A canonical `.mdc`
|
|
441
|
+
frontmatter block holds Cursor's keys and simt-dev's in one list, and `alwaysApply: true`
|
|
442
|
+
sat four lines away from `applies: always` with nothing to say which reader owned which.
|
|
443
|
+
ADR-022 gives every key this tool reads a `simt-` prefix, leaves the vendors' spellings
|
|
444
|
+
alone, and makes an unrecognised key a reported defect — because the mistake it looks like
|
|
445
|
+
is the one ADR-016 already shipped: a rule that reaches no repository because its
|
|
446
|
+
selection key was missing.
|
|
447
|
+
|
|
448
|
+
A seventh is about the gate itself. Plan 1 promised a reusable workflow referenced by
|
|
449
|
+
every consumer, and nothing in this repository had a `.github/` directory. A `uses:`
|
|
450
|
+
line satisfies the promise by pointing every reader at bytes they do not have — the exact
|
|
451
|
+
defect the rule at the top of this file exists to prevent — so ADR-023 copies the gate
|
|
452
|
+
into `.github/workflows/` instead, writes it once, never rewrites it, and skips it
|
|
453
|
+
entirely for a repo that does not run GitHub Actions. The record also states what the
|
|
454
|
+
choice costs: a template improvement does not reach a repo that already has the file, so
|
|
455
|
+
that limitation is written down rather than left for someone to rediscover as a bug.
|
|
456
|
+
|
|
457
|
+
An eighth narrows it, and the reason it was needed is the shape of ADR-023's own cost.
|
|
458
|
+
Write-once is right for a repo whose file may contain their deployment job, and wrong for
|
|
459
|
+
the one repository that owns the template: a gate file that is a frozen copy of the gate is
|
|
460
|
+
a second gate nobody maintains, and ADR-023 ruling 4 means nothing would ever report the
|
|
461
|
+
gap. ADR-024 gives the exception exactly one boundary — a path, not a flag, because a flag
|
|
462
|
+
is a spelling for the wrong answer — and refuses `simt-dev ci` everywhere else. Both copies
|
|
463
|
+
say which world they are in, in their own header, because the reader of a diff in this
|
|
464
|
+
repository should not have to guess who changed their CI.
|
|
465
|
+
|
|
466
|
+
A ninth is about a file rather than a byte. The plan's tooling row asks for eight
|
|
467
|
+
baselines and the same plan, eight sections earlier, already said how they should work:
|
|
468
|
+
a generated region, so local content is never clobbered. Six of the eight are JSON or bare
|
|
469
|
+
strings and cannot hold a region, which is not a reason to ship them anyway — it is the
|
|
470
|
+
answer. `.editorconfig` can, so it is the baseline, and it goes in first so the repo's own
|
|
471
|
+
settings keep winning underneath it. `end_of_line` is left out on purpose: it is a claim
|
|
472
|
+
about line endings, which is `.gitattributes`' claim to make, and a repository that wanted
|
|
473
|
+
CRLF could only opt out by deleting the indentation settings with it.
|
|
474
|
+
|
|
475
|
+
A tenth supplies the counterexample the ninth predicted, and it arrives as a file that was
|
|
476
|
+
already owed: no repository in the estate had a Dependabot config, and the plan named that
|
|
477
|
+
as missing before any of this existed. The ninth said a config whose grammar cannot hold a
|
|
478
|
+
comment is ruled out as a baseline, and left its consequences promising that the next such
|
|
479
|
+
file would need a different mechanism. This is that mechanism, and it is not a new idea —
|
|
480
|
+
it is the gate's own ownership rule, unchanged, pointed at a file whose `updates:` is a YAML
|
|
481
|
+
list and therefore has nowhere to put a region. The lesson is narrow and worth stating
|
|
482
|
+
plainly: partial ownership was never the reason the gate file is write-once, and a rule read
|
|
483
|
+
as depending on mergeability will predict the wrong answer here. What detection buys is
|
|
484
|
+
different — an entry per manifest the repo actually has, so the config never names an
|
|
485
|
+
ecosystem that would sit there opening no pull requests.
|
|
486
|
+
|
|
487
|
+
An eleventh goes back to a deferral and finds it was one line too broad. The ninth refused
|
|
488
|
+
`.gitattributes` because every line that earns its place in a shared region changes what git
|
|
489
|
+
does to the working tree, and concluded the region would be "either dangerous or empty" —
|
|
490
|
+
which is true of every attribute worth sharing and false of the file. Thirteen of sixteen
|
|
491
|
+
repositories had none at all; the three that did agreed about almost nothing. What survives
|
|
492
|
+
is one line, and its safety is a single word: `text=auto` detects rather than instructs, so
|
|
493
|
+
`* text eol=lf` — the usual way a repository quietly corrupts its own binaries — is the form
|
|
494
|
+
the shared region refuses to use. Line endings were left unruled in the ninth because a
|
|
495
|
+
repository wanting CRLF could only opt out by deleting the region; here it opts out with one
|
|
496
|
+
line beneath it, which is what last-match-wins is for.
|
|
497
|
+
|
|
498
|
+
A twelfth turns out to be about a limit rather than a mechanism, and the limit is the
|
|
499
|
+
record. Row 5 named two tsconfig problems and they look like one: seven strictness flags
|
|
500
|
+
missing from a repository, and one option spelled `Bundler` in three repos and `bundler` in
|
|
501
|
+
a fourth. `tsconfig.base.json` fixes the first. It cannot touch the second, and no amount of
|
|
502
|
+
thinking about it will make it able to — a child overrides every key its parent sets, so a
|
|
503
|
+
base file is a floor for what is *missing* and is inert for what is *spelled differently*.
|
|
504
|
+
Writing that limit down is what turned the second problem into a check, which is the only
|
|
505
|
+
form of it a reader can act on. It also decided what the floor may contain: `noEmit` is
|
|
506
|
+
inheritable and two of these repositories bundle elsewhere, so shipping it would have
|
|
507
|
+
silenced the emit in a repository nobody had looked at.
|
|
508
|
+
|
|
509
|
+
A thirteenth is the first one that finds the plan wrong. Row 5 offered to resolve "three
|
|
510
|
+
disagreeing `.nvmrc` files", and reading them showed each one agrees perfectly with its own
|
|
511
|
+
`engines`; what differs is three repositories choosing different node majors, which is a
|
|
512
|
+
decision each has a reason for. There was no defect to resolve, and the useful half of the
|
|
513
|
+
work was ruling out the check that would have reported it — with a test, so completeness
|
|
514
|
+
does not put it back. What is left is one real defect, and it is the first shipped baseline
|
|
515
|
+
with no correct value to write: the npm version is not in `package-lock.json`, and a node
|
|
516
|
+
floor is as many answers as the team has reasons for. So this one ships no file at all. It
|
|
517
|
+
reports, and every remedy is a command run where the answer exists, which is the only shape
|
|
518
|
+
available when the tool can name the field and the manager but not the version.
|
|
519
|
+
|
|
520
|
+
A fourteenth ships nothing at all, and that is the point of it. The first six baselines each
|
|
521
|
+
hit a mechanism question — a region or a whole file, a floor or a check, a value to write or
|
|
522
|
+
no value to derive — and each answer was waiting for somebody to find out what the format
|
|
523
|
+
permits. The two items still open in the plan have no such question, because nobody has
|
|
524
|
+
decided whether they should exist: commitlint is named by the plan and by no rule file and
|
|
525
|
+
by no `package.json` in the estate, and the ESLint "block" cannot describe two of the four
|
|
526
|
+
configurations because they have no TypeScript in them at all. Writing that down in the rows
|
|
527
|
+
that asked for them costs less than shipping either one would, and it is the difference
|
|
528
|
+
between a row that says "unfinished" and one that says "undecided". It also corrected two
|
|
529
|
+
counts the plan got wrong, which is only possible because somebody read the estate rather
|
|
530
|
+
than the plan.
|