simt-dev 0.0.0-stage → 0.1.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 (128) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/LICENSE +202 -0
  3. package/README.md +92 -2
  4. package/bin/simt-dev.mjs +28 -0
  5. package/docs/decisions/ADR-001-push-requires-approval.md +42 -0
  6. package/docs/decisions/ADR-002-refactor-blast-radius.md +45 -0
  7. package/docs/decisions/ADR-003-test-breadth.md +46 -0
  8. package/docs/decisions/ADR-004-hook-owns-format.md +45 -0
  9. package/docs/decisions/ADR-005-file-length.md +42 -0
  10. package/docs/decisions/ADR-006-coverage-bar.md +59 -0
  11. package/docs/decisions/ADR-007-section-skeleton.md +54 -0
  12. package/docs/decisions/ADR-008-verdict-vocabulary.md +59 -0
  13. package/docs/decisions/ADR-009-skill-layout.md +48 -0
  14. package/docs/decisions/ADR-010-runner-invocation.md +56 -0
  15. package/docs/decisions/ADR-011-windows-support.md +52 -0
  16. package/docs/decisions/ADR-012-drop-windows-support.md +59 -0
  17. package/docs/decisions/ADR-013-genericity-by-facts-and-local-profiles.md +114 -0
  18. package/docs/decisions/ADR-014-workspace-is-configuration.md +211 -0
  19. package/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +117 -0
  20. package/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +180 -0
  21. package/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +89 -0
  22. package/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +173 -0
  23. package/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +110 -0
  24. package/docs/decisions/ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md +100 -0
  25. package/docs/decisions/ADR-021-one-surface-per-repo.md +94 -0
  26. package/docs/decisions/ADR-022-a-key-simt-dev-reads-is-prefixed.md +98 -0
  27. package/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +106 -0
  28. package/docs/decisions/ADR-024-simt-dev-overwrites-its-own-gate-file.md +111 -0
  29. package/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +153 -0
  30. package/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +117 -0
  31. package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +130 -0
  32. package/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +131 -0
  33. package/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +120 -0
  34. package/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +114 -0
  35. package/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +129 -0
  36. package/docs/decisions/ADR-032-provenance-is-a-content-digest-not-a-commit.md +141 -0
  37. package/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +82 -0
  38. package/docs/decisions/ADR-034-a-staged-tool-refuses-a-partially-staged-file.md +91 -0
  39. package/docs/decisions/ADR-035-one-write-decision-and-one-json-document.md +119 -0
  40. package/docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md +103 -0
  41. package/docs/decisions/ADR-037-unchanged-means-writing-would-not-change-a-byte.md +100 -0
  42. package/docs/decisions/ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md +95 -0
  43. package/docs/decisions/ADR-039-a-ci-step-runs-a-form-that-can-fail.md +101 -0
  44. package/docs/decisions/ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md +158 -0
  45. package/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +92 -0
  46. package/docs/decisions/ADR-042-a-declared-glob-must-match-a-path-built-from-it.md +94 -0
  47. package/docs/decisions/ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md +90 -0
  48. package/docs/decisions/ADR-044-compatibility-is-not-evidence.md +116 -0
  49. package/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +119 -0
  50. package/docs/decisions/ADR-046-one-rule-one-copy.md +140 -0
  51. package/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +141 -0
  52. package/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +150 -0
  53. package/docs/decisions/ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md +153 -0
  54. package/docs/decisions/ADR-050-a-byte-count-is-not-a-region-and-updated-is-not-a-change-of-rules.md +102 -0
  55. package/docs/decisions/ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md +134 -0
  56. package/docs/decisions/ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md +101 -0
  57. package/docs/decisions/ADR-053-the-node-floor-is-22-20-0.md +49 -0
  58. package/docs/decisions/ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md +100 -0
  59. package/docs/decisions/ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md +98 -0
  60. package/docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md +160 -0
  61. package/docs/decisions/README.md +528 -0
  62. package/graph/skills.graph.json +1622 -0
  63. package/package.json +42 -3
  64. package/rules/core.md +76 -0
  65. package/rules/mdc/intake-questions.mdc +50 -0
  66. package/rules/mdc/preview-postflight.mdc +61 -0
  67. package/rules/mdc/slice-commit.mdc +52 -0
  68. package/rules/mdc/test-gate.mdc +54 -0
  69. package/rules/stacks/android.md +63 -0
  70. package/rules/stacks/cloudflare-workers.md +65 -0
  71. package/rules/stacks/connectiq.md +62 -0
  72. package/rules/stacks/container.md +31 -0
  73. package/rules/stacks/jvm.md +54 -0
  74. package/rules/stacks/node.md +45 -0
  75. package/rules/stacks/python.md +48 -0
  76. package/rules/stacks/react-native.md +58 -0
  77. package/rules/stacks/react-web.md +61 -0
  78. package/rules/stacks/typescript.md +21 -0
  79. package/rules/stacks/uv.md +32 -0
  80. package/src/argv.mjs +116 -0
  81. package/src/ci/dependabot.mjs +268 -0
  82. package/src/ci/workflow.mjs +360 -0
  83. package/src/commands/ci.mjs +285 -0
  84. package/src/commands/compare.mjs +195 -0
  85. package/src/commands/graph.mjs +73 -0
  86. package/src/commands/help.mjs +108 -0
  87. package/src/commands/hooks.mjs +457 -0
  88. package/src/commands/index.mjs +241 -0
  89. package/src/commands/init.mjs +566 -0
  90. package/src/commands/mcp.mjs +63 -0
  91. package/src/commands/rules.mjs +374 -0
  92. package/src/commands/skills.mjs +647 -0
  93. package/src/commands/skillsinstall.mjs +515 -0
  94. package/src/commands/sync.mjs +331 -0
  95. package/src/commands/upgrade.mjs +202 -0
  96. package/src/compare/discover.mjs +212 -0
  97. package/src/compare/hooksstate.mjs +159 -0
  98. package/src/compare/row.mjs +299 -0
  99. package/src/decisions/index.mjs +512 -0
  100. package/src/graph/load.mjs +541 -0
  101. package/src/hooks/capability.mjs +129 -0
  102. package/src/hooks/dispatcher.mjs +268 -0
  103. package/src/hooks/gates.mjs +144 -0
  104. package/src/hooks/resolve.mjs +194 -0
  105. package/src/hooks/shim.mjs +140 -0
  106. package/src/hooks/where.mjs +118 -0
  107. package/src/locate.mjs +75 -0
  108. package/src/mcp/server.mjs +167 -0
  109. package/src/mcp/tools.mjs +410 -0
  110. package/src/name.mjs +95 -0
  111. package/src/output/index.mjs +96 -0
  112. package/src/projection/baselines.mjs +209 -0
  113. package/src/projection/conflicts.mjs +205 -0
  114. package/src/projection/manifest.mjs +478 -0
  115. package/src/projection/markers.mjs +349 -0
  116. package/src/projection/mdc.mjs +327 -0
  117. package/src/projection/project.mjs +504 -0
  118. package/src/projection/rules.mjs +640 -0
  119. package/src/projection/surfaces.mjs +398 -0
  120. package/src/projection/targets.mjs +221 -0
  121. package/src/resolve/fingerprint.mjs +553 -0
  122. package/src/resolve/near.mjs +61 -0
  123. package/src/resolve/resolve.mjs +457 -0
  124. package/src/resolve/source.mjs +105 -0
  125. package/src/toolchain.mjs +202 -0
  126. package/src/tsconfig.mjs +314 -0
  127. package/src/upgrade/plan.mjs +203 -0
  128. package/src/workspace.mjs +289 -0
@@ -0,0 +1,98 @@
1
+ # ADR-055 — A status line travels, so it may not name a location; prose is the record's own argument
2
+
3
+ Status: **accepted** · 2026-10-10 · filed as S54 · affects `src/decisions/index.mjs`
4
+
5
+ ## Context
6
+
7
+ `rules why` prints a decision record's `Status:` line into whatever repository the reader
8
+ is standing in, and most of them are repositories that adopted simt-dev and therefore own
9
+ none of this package's files. The `summarise` contract said so itself, in the module that
10
+ produces the output: *"Never contains a path, because a record read inside a consumer repo
11
+ cannot name one."*
12
+
13
+ The `ruled` half honoured that. The `status` half did not, because it is the record's own
14
+ prose: five records wrote `· affects src/resolve/resolve.mjs` into a field the tool parses,
15
+ copies and prints verbatim on every `rules why`, `rules show` and MCP call. The review
16
+ measured this as "two records out of forty-eight, pre-existing". The corpus says **five**,
17
+ and **three of them were written after that measurement** — ADR-053 and ADR-054 in this
18
+ cycle, and ADR-041 days earlier. A finding that grows while it is being fixed is a finding
19
+ whose real cause is a convention rather than a typo, and the convention is what needed
20
+ ruling.
21
+
22
+ The convention was ADR-038's: *"That is what the `Affects:` line on a record is for."* Five
23
+ records had an `Affects:` field available and put the clause in `Status:` instead — because
24
+ `Status:` is the line the tool reads, so putting it there feels like the place that counts.
25
+ It is the opposite: it is the one field guaranteed to travel.
26
+
27
+ ## Ruling
28
+
29
+ **One. A `Status:` line may not name a location.** It is this tool's own metadata — accepted,
30
+ the date, what superseded what, what it affects — and every clause in it is a statement about
31
+ *this package* by construction. There is no benign reading to confuse it with, so the
32
+ predicate needs no judgement: a location resolving at this package's root is redacted to a
33
+ phrase naming the thing rather than the path. The redaction happens once, in `loadDecisions`,
34
+ so the text command, the JSON payload and the MCP surface cannot disagree about it — which is
35
+ the same "one place decides" shape ADR-035 settled for writers.
36
+
37
+ **Two. `Affects:` is where a location belongs, and it stays unprinted.** The loader already
38
+ stops the status paragraph at a sibling field, so moving the clause is a reformat rather
39
+ than a change of ruling, and the five records are corrected in place for the clause only.
40
+
41
+ **Three. The exempt list is what `init` writes, and it is executed.** Dot-entries and three
42
+ named files (`AGENTS.md`, `package.json`, `simt-dev.json`) are excluded from redaction,
43
+ because a repository that adopted simt-dev has them. That is a claim about a set held in a
44
+ comment, and a claim about a set held in a comment is a value nobody runs — so a test adopts
45
+ a repository and asserts that every top-level file the tool writes survives the predicate
46
+ unchanged. A name that drifts on stops being redacted; a name that drifts off starts
47
+ redacting a file the reader does have.
48
+
49
+ **Four. Prose is the record's argument and is left as written.** The `ruled` and `rejected`
50
+ excerpts are a human argument, and they may legitimately quote a location — `docs/plans/`
51
+ means *this package's* plan in one record and *the reader's own plans* in another, and no
52
+ amount of reading the string separates them. Redaction there would have to guess, and a
53
+ guessed redaction makes a correct sentence wrong, which is the worse defect. `redactEstate`
54
+ redacts prose and is not a counter-example: an estate repository name is never the reader's
55
+ business either, whereas a path inside an argument sometimes is.
56
+
57
+ **Five. The cost of ruling four is a number, not a shrug.** Seven records still print a
58
+ location in their prose — six older ones, and this record, which quotes the collision it
59
+ turns on — and a test asserts the count and the ids. A record that stops quoting one moves
60
+ it; a record that starts moves it too. The alternative — leaving it unmeasured — is how "two
61
+ of forty-eight, pre-existing" came to describe ten.
62
+
63
+ **Six. The `summarise` docstring's overclaim is withdrawn.** It asserted a property of prose
64
+ that the function never had and could not have had. An unverifiable promise in a comment is
65
+ the defect ADR-049 is about, one level down: a sentence that reads like evidence and carries
66
+ none. The comment now states the scope it can keep.
67
+
68
+ ## Consequences
69
+
70
+ - **Five records changed, and only their `Status:` line.** No ruling, no context and no
71
+ rejected alternative was touched. ADR-049's supersession machinery is unaffected:
72
+ `partialSupersessions` still reads the raw paragraph, because a ruling number is not a
73
+ location and redacting it would have been the wrong trade.
74
+ - **`rules why ADR-042` no longer prints `src/resolve/resolve.mjs`.** It prints
75
+ `accepted · 2026-10-08 · ruling 1 superseded by ADR-053`, which is the whole of what the
76
+ line is for.
77
+ - **A sixth record with a path in `Status:` is scrubbed rather than blocked.** The records
78
+ are corrected so they stay readable to a maintainer on this repository, where the path is
79
+ the useful part; the loader is hardened so a future one is harmless. The corpus test
80
+ asserts the *unscrubbed* status carries none, which is the stronger statement.
81
+ - **Prose leaks are visible rather than absent.** Seven records — six older and ADR-055
82
+ itself, which quotes the collision it turns on. Anyone who decides that is still too many
83
+ now has the count and the argument against redaction.
84
+
85
+ ## Rejected
86
+
87
+ - **Redact the prose too.** The `docs/plans/` collision is not hypothetical — ADR-014,
88
+ ADR-016, ADR-025 and ADR-030 all use that exact string for both meanings, and `test/leaks.mjs`
89
+ already singles it out as a substitution *default* that names where a repo keeps its own
90
+ plans. Redacting it would corrupt four records to protect six.
91
+ - **Rewrite the ten records to drop the paths.** This repository's rule is that a record is
92
+ never edited in place, and the argument these records make is better with the file named.
93
+ The clause moves to the field designed for it; the sentence does not change.
94
+ - **Stop printing the status line.** It carries the supersession, which is the one thing a
95
+ reader needs in order not to act on a reversed ruling.
96
+ - **Put the redaction in `commands/rules.mjs`.** It would then apply to the text output and
97
+ not to `--json` or MCP, and the three surfaces would disagree about what a record says —
98
+ the copy ADR-052 exists to prevent.
@@ -0,0 +1,160 @@
1
+ # ADR-056 — The command, the package and the project are three names, and only one of them is cheap to move
2
+
3
+ Status: **accepted** · 2026-10-10 · filed as S18 · amends [ADR-023](ADR-023-the-gate-ships-as-a-file-the-repo-owns.md) · affects `src/name.mjs`, `package.json`, `src/projection/manifest.mjs`, `src/projection/markers.mjs`
4
+
5
+ ## Context
6
+
7
+ This tool had four names and no record of which was which. `PACKAGE_NAME` was spelled in
8
+ `locate.mjs`, the manifest name in `manifest.mjs`, the config directory in `workspace.mjs`
9
+ and the gate filename in `workflow.mjs` — four definitions, four places to forget. The
10
+ command itself appeared **276 times across 33 files**, in remedy strings, help columns, shim
11
+ source, MCP tool descriptions and the note every generated region carries.
12
+
13
+ Publishing forced the question S18 had been asking since it was filed: *what is this
14
+ called?* And the answer turned out to be four different answers depending on what you were
15
+ asking about, which is not an answer.
16
+
17
+ The measurements that settled it:
18
+
19
+ - `simt-dev.json` is on disk in **fourteen repositories** that adopted the tool.
20
+ - `<!-- simt-dev:begin -->` is in every projected `AGENTS.md` in every one of them.
21
+ - `.simt-hooks/` and `SIMT_DEV_BIN` are in the `.git` directories of those repositories.
22
+ - `simt-dev-check.yml` is in the `.github/workflows` of each of them.
23
+ - The command name appears in **no file at all** until a reader types it.
24
+
25
+ ## Ruling
26
+
27
+ **One. There are three names, and they are recorded as three.** `src/name.mjs` owns all of
28
+ them and nothing else spells them:
29
+
30
+ - `PROJECT` — the repository, and the artefacts already on somebody else's disk.
31
+ - `CLI` — the command a reader types.
32
+ - `PACKAGE_NAME` — what you install to get the command.
33
+
34
+ They are three constants that happen to be related. Deriving one from another was
35
+ considered and rejected: a derivation that picks between a package and a scope turns a
36
+ one-line rename into a one-line diff whose meaning depends on a comparison nobody reading
37
+ it would think to check.
38
+
39
+ **Two. The command is `simt`; the project and the package keep `simt-dev`.** This is the
40
+ whole ruling, and it rests on the asymmetry in the measurement above: the command appears
41
+ only in text the tool itself writes, and that text migrates on the next write, while the
42
+ other two names sit in files the tool cannot rewrite in place. Spending the rename on the
43
+ cheap name is what made this a rename instead of a fourteen-repository roll-forward.
44
+
45
+ **Three. The manifest follows the command, and its move is a read-both/write-new.** The
46
+ manifest was one of the on-disk names and it is the one that moved: `simt-dev.json` is now
47
+ `simt.json`. Without a fallback, all fourteen adopting repositories would read as "no
48
+ manifest" on their next `check` — and "no manifest" is the answer that offers to re-adopt a
49
+ repository that is already done. So:
50
+
51
+ - **Read** takes `simt.json` if it exists, else `simt-dev.json`. Every read site goes
52
+ through one resolver (`manifestPath`) rather than knowing the fallback itself, because a
53
+ fallback that lives in five places is a fallback that eventually stops living in one of
54
+ them. The fingerprint, which reads the directory listing rather than a path, accepts both
55
+ spellings for the same reason: a fingerprint that misses the manifest silently loses the
56
+ repository's declared stack, which is the one input no amount of marker reading replaces.
57
+ - **Write** goes to `simt.json`, and a lone `simt-dev.json` is moved, not shadowed: two
58
+ manifests in one repository is a state nobody can reason about, and the second would be
59
+ the one that is never read.
60
+ - **The window closes per repository** on its first `init` or manifest write after the
61
+ upgrade. Until then, the old name keeps working everywhere.
62
+
63
+ The fallback is asserted in `test/naming.test.mjs` with both spellings — the new name wins
64
+ when both exist, the old name is still read, and a write moves the old file — because its
65
+ failure mode is silent: the tool would simply stop seeing a repository it has been
66
+ managing.
67
+
68
+ **Four. The region namespace follows the command, and its move is read-both/write-new
69
+ too.** The second on-disk name to move: the region was stamped `simt-dev:begin` /
70
+ `simt-dev:end`, and so was the provenance header inside it (`simt-dev:generated`,
71
+ `simt-dev:sha=`, `simt-dev:sources=`); all of those lines are now `simt:`. A marker cannot
72
+ be renamed the way a manifest can, because a region cannot be moved — it lives inside a
73
+ file the reader owns. So the fallback lives in the marker grammar itself, one read regex
74
+ per dialect:
75
+
76
+ - **Read** accepts either spelling in both dialects (HTML comment and `#` comment),
77
+ because a marker that does not match is simply not a region, and a caller that does not
78
+ accept the old spelling would silently report an adopted repository as unadopted.
79
+ - **Write** emits only the `simt:` spelling, so the first write to each region migrates it
80
+ in place: the body comparison in `inject` finds the header prefix drifted and rewrites
81
+ the region where it stands. The window closes per repository on its first `sync`.
82
+ - **A file holding both spellings is malformed, not merged.** Only a hand edit can produce
83
+ it — the tool writes one region per file and finds it by either spelling. Guessing which
84
+ region wins would silently delete the other, so `parse` reports it and a human deletes
85
+ the old one.
86
+
87
+ The provenance digest covers `rules/`, `graph/` and `baselines/` — not `src/`, where the
88
+ marker strings live (ADR-032, S53) — so the stamp *value* does not move with this rename;
89
+ only the key's prefix does. No repository needs re-pinning, and `upgrade` sees no version
90
+ change: the migration is marker bytes only.
91
+
92
+ The fallback is asserted in `test/markers.test.mjs` — a legacy region is read and its
93
+ stamp recovered, the first write migrates it in place, the migrated region is stable, and
94
+ a mixed file is reported malformed — because its failure mode is the manifest's: the tool
95
+ would simply stop seeing a repository it has been managing.
96
+
97
+ **Five. The remaining on-disk names are locked, and the lock is asserted.**
98
+ `test/naming.test.mjs` pins `simt-dev-check.yml`, `.simt-hooks`, `SIMT_DEV_BIN` and the
99
+ project name as literals, so renaming one is a deliberate edit to a test rather than a
100
+ constant change that looks harmless. A lock nobody can trip is a comment.
101
+
102
+ **Six. The command name lives in one constant and is executed, not agreed.**
103
+ `test/naming.test.mjs` walks `src/` and fails on any command string that *spells* the
104
+ command instead of interpolating it. The pattern matches the real verb list rather than
105
+ "space then a lowercase word", because the looser rule flags the tool naming itself in
106
+ prose — `simt-dev will not remove a file it wrote` and `the simt-dev region is not at the
107
+ top of the file` are the project talking about itself, and the project keeps its name.
108
+ The guard was proven with a negative control: one injected spelled command, one failure
109
+ naming the file and line, reverted.
110
+
111
+ **Seven. The package is unscoped.** `@simt` and `@quazarmedia` were both free on npm; an
112
+ unscoped `simt-dev` was already reserved by this project's own account. An unscoped name
113
+ is the one a reader can guess, and the reserved one was the only spelling already pointing
114
+ at something.
115
+
116
+ ## Consequences
117
+
118
+ - **No repository has to do anything by hand, and the migration announces itself.** Every
119
+ adopted repo keeps its hooks directory and gate. The manifest is read under either name
120
+ and moves to `simt.json` on its first write; the region is read under either namespace
121
+ and migrates to `simt:` on its first `sync`. Until each repository has written once,
122
+ `check` reports the region as drifted — which is the migration making itself visible
123
+ rather than hiding, and it disappears on the `sync` that performs it. The migration is
124
+ real, but it runs itself — one repository per write — instead of running as a
125
+ fourteen-repository roll-forward.
126
+ - **The generated gate file names the command correctly.** `name: simt check`, "If `simt`
127
+ cannot be found the job FAILS", "run `simt ci --yes`". These are shipped bytes into
128
+ every adopting repository, which is why they were checked by rendering them, not by
129
+ reading the source.
130
+ - **One upgrade step is real and is documented.** A shim written before this change looks
131
+ for `simt-dev` on `PATH`. A machine that installed the package and never re-ran
132
+ `hooks install` finds nothing until the dependency is refreshed and `simt hooks install
133
+ --yes` re-runs. That is in the CHANGELOG rather than left to be discovered.
134
+ - **The remaining on-disk names can still move, but only deliberately.** The gate file,
135
+ the hooks directory and the shim's environment variable would each need the shape the
136
+ manifest and the markers got — read-both, write-new, an asserted fallback — and S36's
137
+ estate roll-out to execute it.
138
+
139
+ ## Rejected
140
+
141
+ - **Rename the artefacts too — `masar.json`, `masar-hooks/`, `<!-- masar:begin -->`.**
142
+ This was the shape the slice was originally taken in and it is strictly worse: it turns
143
+ a rename into a fourteen-repository migration, every one of which would see its
144
+ `AGENTS.md` stop being managed on the first `check` after the upgrade. It was rejected
145
+ for what it would have cost, not for being unclear. The manifest and the region markers
146
+ are exceptions to this rejection, and they are exceptions only because rulings three
147
+ and four make their moves safe: a marker that does not match is simply not a region, so
148
+ the fallback lives in the marker grammar itself — one read regex per dialect that
149
+ accepts both spellings — and a file holding two regions is reported malformed rather
150
+ than merged. The hooks directory and the gate remain fully rejected: they have no
151
+ second spelling a reader's file could fall back to.
152
+ - **`@quazarmedia/simt-dev` as the package name.** Free, but already named in the shim
153
+ remedy, the review and the workflow install step. Changing it would have edited all
154
+ three for no availability gain, since the unscoped name was already reserved.
155
+ - **Rename the command by a script that rewrites every literal.** The first attempt at the
156
+ propagation used "space then a lowercase word" and rewrote `user.name=simt-dev tests`
157
+ and a test title — both of which would have passed. A rename that cannot tell a command
158
+ from a git identity in a fixture is a rename that will eventually rewrite a sentence.
159
+ - **Derive `PACKAGE_NAME` from `SCOPE` and `PROJECT`.** Rejected above; the three names
160
+ will not always agree, and the day they disagree the derivation picks for you.