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.
- package/CHANGELOG.md +103 -0
- package/LICENSE +202 -0
- package/README.md +92 -2
- package/bin/simt-dev.mjs +28 -0
- package/docs/decisions/ADR-001-push-requires-approval.md +42 -0
- package/docs/decisions/ADR-002-refactor-blast-radius.md +45 -0
- package/docs/decisions/ADR-003-test-breadth.md +46 -0
- package/docs/decisions/ADR-004-hook-owns-format.md +45 -0
- package/docs/decisions/ADR-005-file-length.md +42 -0
- package/docs/decisions/ADR-006-coverage-bar.md +59 -0
- package/docs/decisions/ADR-007-section-skeleton.md +54 -0
- package/docs/decisions/ADR-008-verdict-vocabulary.md +59 -0
- package/docs/decisions/ADR-009-skill-layout.md +48 -0
- package/docs/decisions/ADR-010-runner-invocation.md +56 -0
- package/docs/decisions/ADR-011-windows-support.md +52 -0
- package/docs/decisions/ADR-012-drop-windows-support.md +59 -0
- package/docs/decisions/ADR-013-genericity-by-facts-and-local-profiles.md +114 -0
- package/docs/decisions/ADR-014-workspace-is-configuration.md +211 -0
- package/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +117 -0
- package/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +180 -0
- package/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +89 -0
- package/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +173 -0
- package/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +110 -0
- package/docs/decisions/ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md +100 -0
- package/docs/decisions/ADR-021-one-surface-per-repo.md +94 -0
- package/docs/decisions/ADR-022-a-key-simt-dev-reads-is-prefixed.md +98 -0
- package/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +106 -0
- package/docs/decisions/ADR-024-simt-dev-overwrites-its-own-gate-file.md +111 -0
- package/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +153 -0
- package/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +117 -0
- package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +130 -0
- package/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +131 -0
- package/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +120 -0
- package/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +114 -0
- package/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +129 -0
- package/docs/decisions/ADR-032-provenance-is-a-content-digest-not-a-commit.md +141 -0
- package/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +82 -0
- package/docs/decisions/ADR-034-a-staged-tool-refuses-a-partially-staged-file.md +91 -0
- package/docs/decisions/ADR-035-one-write-decision-and-one-json-document.md +119 -0
- package/docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md +103 -0
- package/docs/decisions/ADR-037-unchanged-means-writing-would-not-change-a-byte.md +100 -0
- package/docs/decisions/ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md +95 -0
- package/docs/decisions/ADR-039-a-ci-step-runs-a-form-that-can-fail.md +101 -0
- package/docs/decisions/ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md +158 -0
- package/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +92 -0
- package/docs/decisions/ADR-042-a-declared-glob-must-match-a-path-built-from-it.md +94 -0
- package/docs/decisions/ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md +90 -0
- package/docs/decisions/ADR-044-compatibility-is-not-evidence.md +116 -0
- package/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +119 -0
- package/docs/decisions/ADR-046-one-rule-one-copy.md +140 -0
- package/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +141 -0
- package/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +150 -0
- package/docs/decisions/ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md +153 -0
- package/docs/decisions/ADR-050-a-byte-count-is-not-a-region-and-updated-is-not-a-change-of-rules.md +102 -0
- package/docs/decisions/ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md +134 -0
- package/docs/decisions/ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md +101 -0
- package/docs/decisions/ADR-053-the-node-floor-is-22-20-0.md +49 -0
- package/docs/decisions/ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md +100 -0
- package/docs/decisions/ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md +98 -0
- package/docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md +160 -0
- package/docs/decisions/README.md +528 -0
- package/graph/skills.graph.json +1622 -0
- package/package.json +42 -3
- package/rules/core.md +76 -0
- package/rules/mdc/intake-questions.mdc +50 -0
- package/rules/mdc/preview-postflight.mdc +61 -0
- package/rules/mdc/slice-commit.mdc +52 -0
- package/rules/mdc/test-gate.mdc +54 -0
- package/rules/stacks/android.md +63 -0
- package/rules/stacks/cloudflare-workers.md +65 -0
- package/rules/stacks/connectiq.md +62 -0
- package/rules/stacks/container.md +31 -0
- package/rules/stacks/jvm.md +54 -0
- package/rules/stacks/node.md +45 -0
- package/rules/stacks/python.md +48 -0
- package/rules/stacks/react-native.md +58 -0
- package/rules/stacks/react-web.md +61 -0
- package/rules/stacks/typescript.md +21 -0
- package/rules/stacks/uv.md +32 -0
- package/src/argv.mjs +116 -0
- package/src/ci/dependabot.mjs +268 -0
- package/src/ci/workflow.mjs +360 -0
- package/src/commands/ci.mjs +285 -0
- package/src/commands/compare.mjs +195 -0
- package/src/commands/graph.mjs +73 -0
- package/src/commands/help.mjs +108 -0
- package/src/commands/hooks.mjs +457 -0
- package/src/commands/index.mjs +241 -0
- package/src/commands/init.mjs +566 -0
- package/src/commands/mcp.mjs +63 -0
- package/src/commands/rules.mjs +374 -0
- package/src/commands/skills.mjs +647 -0
- package/src/commands/skillsinstall.mjs +515 -0
- package/src/commands/sync.mjs +331 -0
- package/src/commands/upgrade.mjs +202 -0
- package/src/compare/discover.mjs +212 -0
- package/src/compare/hooksstate.mjs +159 -0
- package/src/compare/row.mjs +299 -0
- package/src/decisions/index.mjs +512 -0
- package/src/graph/load.mjs +541 -0
- package/src/hooks/capability.mjs +129 -0
- package/src/hooks/dispatcher.mjs +268 -0
- package/src/hooks/gates.mjs +144 -0
- package/src/hooks/resolve.mjs +194 -0
- package/src/hooks/shim.mjs +140 -0
- package/src/hooks/where.mjs +118 -0
- package/src/locate.mjs +75 -0
- package/src/mcp/server.mjs +167 -0
- package/src/mcp/tools.mjs +410 -0
- package/src/name.mjs +95 -0
- package/src/output/index.mjs +96 -0
- package/src/projection/baselines.mjs +209 -0
- package/src/projection/conflicts.mjs +205 -0
- package/src/projection/manifest.mjs +478 -0
- package/src/projection/markers.mjs +349 -0
- package/src/projection/mdc.mjs +327 -0
- package/src/projection/project.mjs +504 -0
- package/src/projection/rules.mjs +640 -0
- package/src/projection/surfaces.mjs +398 -0
- package/src/projection/targets.mjs +221 -0
- package/src/resolve/fingerprint.mjs +553 -0
- package/src/resolve/near.mjs +61 -0
- package/src/resolve/resolve.mjs +457 -0
- package/src/resolve/source.mjs +105 -0
- package/src/toolchain.mjs +202 -0
- package/src/tsconfig.mjs +314 -0
- package/src/upgrade/plan.mjs +203 -0
- 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.
|