simt-dev 0.1.1 → 0.2.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 +36 -0
- package/README.md +31 -4
- package/docs/decisions/ADR-001-push-requires-approval.md +5 -5
- package/docs/decisions/ADR-002-refactor-blast-radius.md +1 -1
- package/docs/decisions/ADR-003-test-breadth.md +4 -4
- package/docs/decisions/ADR-004-hook-owns-format.md +5 -5
- package/docs/decisions/ADR-005-file-length.md +5 -5
- package/docs/decisions/ADR-006-coverage-bar.md +3 -3
- package/docs/decisions/ADR-007-section-skeleton.md +5 -5
- package/docs/decisions/ADR-008-verdict-vocabulary.md +1 -1
- package/docs/decisions/ADR-009-skill-layout.md +1 -1
- package/docs/decisions/ADR-010-runner-invocation.md +1 -1
- package/docs/decisions/ADR-012-drop-windows-support.md +2 -2
- package/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +2 -2
- package/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +13 -13
- package/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +2 -2
- package/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +9 -9
- package/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +7 -7
- package/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +2 -2
- package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +5 -5
- package/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +2 -2
- package/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +1 -1
- package/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +2 -2
- package/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +3 -3
- package/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +4 -4
- package/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +2 -2
- package/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +3 -3
- package/docs/decisions/ADR-046-one-rule-one-copy.md +5 -5
- package/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +4 -4
- package/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +5 -5
- package/docs/decisions/ADR-057-an-identity-is-per-developer-and-a-repository-does-not-own-it.md +115 -0
- package/docs/decisions/ADR-058-one-tool-three-channels-and-the-order-is-forced.md +148 -0
- package/docs/decisions/README.md +3 -1
- package/package.json +1 -1
- package/src/commands/hooks.mjs +10 -10
- package/src/commands/init.mjs +67 -1
- package/src/commands/skillsinstall.mjs +1 -1
- package/src/compare/discover.mjs +1 -1
- package/src/compare/row.mjs +2 -2
- package/src/decisions/index.mjs +30 -6
- package/src/graph/load.mjs +1 -1
- package/src/hooks/capability.mjs +1 -1
- package/src/hooks/dispatcher.mjs +1 -1
- package/src/hooks/gates.mjs +1 -1
- package/src/hooks/where.mjs +1 -1
- package/src/identity.mjs +218 -0
- package/src/projection/baselines.mjs +1 -1
- package/src/projection/conflicts.mjs +1 -1
- package/src/projection/manifest.mjs +1 -1
- package/src/projection/markers.mjs +3 -3
- package/src/projection/mdc.mjs +2 -2
- package/src/projection/surfaces.mjs +2 -2
- package/src/resolve/fingerprint.mjs +3 -3
- package/src/resolve/source.mjs +1 -1
- package/src/tsconfig.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,41 @@ All notable changes to this package are recorded here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.2.0] — 2026-10-11
|
|
8
|
+
|
|
9
|
+
### Publishable from PyPI and Homebrew as well as npm
|
|
10
|
+
|
|
11
|
+
simt-dev is a *repository* tool, and the people who adopt a repository are not
|
|
12
|
+
all Node developers. Somebody running `simt init` inside a repository with no
|
|
13
|
+
`package.json` has no reason to install a package manager, and a Homebrew user
|
|
14
|
+
has no reason to install Node to install a tool that needs Node.
|
|
15
|
+
|
|
16
|
+
So it now publishes to three channels, from one version number:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm i -D simt-dev # a tarball of .mjs files
|
|
20
|
+
pip install simt-dev # a wheel carrying those same bytes
|
|
21
|
+
brew install QuazarMedia/tap/simt-dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The wheel is not a wrapper around `npm install`. It carries the tool's own
|
|
25
|
+
source, and `scripts/verify-wheel.mjs` builds both artefacts and hashes every
|
|
26
|
+
file in both — a release fails when a single byte differs between them. That
|
|
27
|
+
check exists because the obvious defect in this arrangement is the wheel and the
|
|
28
|
+
tarball quietly shipping different code.
|
|
29
|
+
|
|
30
|
+
`simt` is the one command, on every channel. Where node comes from is
|
|
31
|
+
`SIMT_DEV_NODE`, then the bundled node from `pip install "simt-dev[node]"`,
|
|
32
|
+
then `node` on `PATH` — and if none of those resolve you get the remedy rather
|
|
33
|
+
than a placeholder path. There are no required dependencies on any channel.
|
|
34
|
+
*(ADR-058)*
|
|
35
|
+
|
|
36
|
+
The channels are published in a fixed order rather than in parallel, and the
|
|
37
|
+
reason is not taste: the Homebrew formula downloads the npm tarball at install
|
|
38
|
+
time, so a formula naming a version still sitting in npm's staging area
|
|
39
|
+
installs a 404. Both jobs fail with that stated, and are run again after
|
|
40
|
+
`npm stage approve`.
|
|
41
|
+
|
|
7
42
|
## [0.1.1] — 2026-10-11
|
|
8
43
|
|
|
9
44
|
### A foreign hook on an event this repo does not gate is chained
|
|
@@ -126,5 +161,6 @@ never ran `simt hooks install` again will find nothing until the dependency is r
|
|
|
126
161
|
and `simt hooks install --yes` is re-run. That is a real upgrade step and it is called out
|
|
127
162
|
here rather than discovered.
|
|
128
163
|
|
|
164
|
+
[0.2.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.0
|
|
129
165
|
[0.1.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.1.1
|
|
130
166
|
[0.1.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -19,19 +19,46 @@ that refuses a commit which breaks a gate that repo can genuinely run.
|
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
22
|
-
Requires **Node >= 22.20.0
|
|
22
|
+
Requires **Node >= 22.20.0** (the PyPI install can supply it, see below).
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
25
|
# pinned, per repository - what CI and a teammate both get
|
|
26
|
-
npm i -D simt-dev@0.
|
|
26
|
+
npm i -D simt-dev@0.2.0
|
|
27
27
|
|
|
28
28
|
# or globally, if you would rather not add a dependency
|
|
29
|
-
npm i -g simt-dev@0.
|
|
29
|
+
npm i -g simt-dev@0.2.0
|
|
30
30
|
|
|
31
31
|
# or with no install at all, for a one-off
|
|
32
|
-
npx -y simt-dev@0.
|
|
32
|
+
npx -y simt-dev@0.2.0 check
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
### From PyPI
|
|
36
|
+
|
|
37
|
+
The same tool, from the same bytes. The wheel carries the exact contents the npm
|
|
38
|
+
tarball carries — the wheel is not a wrapper around `npm install`, so nothing is
|
|
39
|
+
fetched when you install it.
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
pip install simt-dev
|
|
43
|
+
|
|
44
|
+
# and if this machine has no node at all
|
|
45
|
+
pip install "simt-dev[node]"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`simt` is the command on both channels. Where node comes from is resolved in
|
|
49
|
+
this order, and the first one that answers wins: `SIMT_DEV_NODE`, the bundled
|
|
50
|
+
node from the `node` extra, then `node` on your `PATH`. If none of them resolve,
|
|
51
|
+
you get a message naming the remedy rather than a placeholder node path.
|
|
52
|
+
|
|
53
|
+
### From Homebrew
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
brew install QuazarMedia/homebrew-tap/simt-dev
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The formula declares its own `node` dependency, so `brew install` gives you a
|
|
60
|
+
working command on a machine that has never had Node.
|
|
61
|
+
|
|
35
62
|
Pin the version. The tool writes a content digest into the files it generates, and an
|
|
36
63
|
unpinned upgrade changes them under you.
|
|
37
64
|
|
|
@@ -8,14 +8,14 @@ The repos state a commit/push policy. They disagree on whether an agent may push
|
|
|
8
8
|
|
|
9
9
|
- A repo outside this estate: "always create a focused Git commit… skip the commit only
|
|
10
10
|
when the user explicitly asks not to commit." Silent on push.
|
|
11
|
-
- `
|
|
12
|
-
- `
|
|
11
|
+
- `repo-07/AGENTS.md` DoD #8: "**Committed and pushed** with a conventional message."
|
|
12
|
+
- `repo-14` and `repo-11`: "create a non-interactive Conventional Commit. Report
|
|
13
13
|
the commit SHA; **do not push**."
|
|
14
|
-
- `
|
|
14
|
+
- `repo-04`, `repo-13`: silent.
|
|
15
15
|
|
|
16
16
|
The Social pair justify it: a push to the production branch triggers Cloudflare Workers Builds,
|
|
17
17
|
so "an approved push is therefore a production deployment action, not only source publication."
|
|
18
|
-
`
|
|
18
|
+
`repo-07` has no such coupling, and its rule asks for an autonomous push.
|
|
19
19
|
|
|
20
20
|
## Ruling
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ Commit locally, report the SHA, **push only on explicit user approval**.
|
|
|
24
24
|
When a repo's push triggers a deployment, a migration, or any remote mutation, say so before
|
|
25
25
|
asking for approval — do not treat approval for the push as approval for the deploy.
|
|
26
26
|
|
|
27
|
-
`
|
|
27
|
+
`repo-07` DoD #8 changes to "committed with a conventional message; pushed on approval."
|
|
28
28
|
This is the single outlier, and the 4:1 majority is also the safe direction.
|
|
29
29
|
|
|
30
30
|
## Consequences
|
|
@@ -6,7 +6,7 @@ Status: **accepted** · 2026-10-05 · affects all repos
|
|
|
6
6
|
|
|
7
7
|
Two repos state opposite philosophies, and both are reasonable in isolation.
|
|
8
8
|
|
|
9
|
-
- `
|
|
9
|
+
- `repo-14` / `repo-11`: "When a second real use or a near-duplicate already
|
|
10
10
|
exists, refactor the existing code to extract the shared part… A third copy of the same
|
|
11
11
|
logic is a defect, and so is an abstraction invented for a single caller."
|
|
12
12
|
- A repo outside this estate: "Match existing style, naming, and test layout. **Do not
|
|
@@ -4,13 +4,13 @@ Status: **accepted** · 2026-10-05 · affects all repos
|
|
|
4
4
|
|
|
5
5
|
## Context
|
|
6
6
|
|
|
7
|
-
- `
|
|
7
|
+
- `repo-14` / `repo-11`: "Focused tests are the default gate, and full suites are
|
|
8
8
|
the exception… **absent a named trigger, running a full suite is a defect, not diligence.**"
|
|
9
9
|
- A repo outside this estate: `fail_under = 100` plus a full-suite script as the pre-push
|
|
10
10
|
default, and a contributor loop that says "run the full gate locally (lint + tests +
|
|
11
11
|
coverage)."
|
|
12
|
-
- `
|
|
13
|
-
- `
|
|
12
|
+
- `repo-07`: reframes the axis entirely as a layer-conditional 5a–5m matrix.
|
|
13
|
+
- `repo-13`: no policy — it just lists `npm run test:unit && npm run test:e2e`.
|
|
14
14
|
|
|
15
15
|
The Social pair make a full suite a sin. media presents it as unremarkable. The hard-gate
|
|
16
16
|
repo makes it mandatory. These cannot all be org rules as stated.
|
|
@@ -34,7 +34,7 @@ adopted as canonical.
|
|
|
34
34
|
## Consequences
|
|
35
35
|
|
|
36
36
|
- Each repo's `simt-dev.json` declares which of the six triggers are active for it. A repo
|
|
37
|
-
with a declared numeric gate activates 4 and 6;
|
|
37
|
+
with a declared numeric gate activates 4 and 6; one repository activate 1, 2 and 3.
|
|
38
38
|
- A reviewer can ask "which trigger?" and get a number, not an opinion.
|
|
39
39
|
- media gains an explicit policy instead of an implicit one that contradicts its siblings.
|
|
40
40
|
|
|
@@ -4,16 +4,16 @@ Status: **accepted** · 2026-10-05 · affects all repos
|
|
|
4
4
|
|
|
5
5
|
## Context
|
|
6
6
|
|
|
7
|
-
- `
|
|
7
|
+
- `repo-11`: "lint and format are enforced by the pre-commit hook — **do not run them
|
|
8
8
|
manually.**"
|
|
9
|
-
- `
|
|
9
|
+
- `repo-14`: "the local pre-push hook republishes opportunistically when a token happens to
|
|
10
10
|
be present." Never says the hook is the gate; instructs agents to run static checks directly.
|
|
11
11
|
- A repo outside this estate: tells agents to run its linter and `git diff --check` by hand,
|
|
12
12
|
and its contributor guide repeats it.
|
|
13
|
-
- `
|
|
13
|
+
- `repo-04`, and 11 repos with no hooks at all: silent.
|
|
14
14
|
|
|
15
15
|
The audit also found the estate's hook reality: 4 incompatible conventions, and
|
|
16
|
-
`
|
|
16
|
+
`repo-05/hooks/pre-push` is **0 bytes** while the real git-lfs pre-push sits unused in
|
|
17
17
|
`scripts/hooks/`.
|
|
18
18
|
|
|
19
19
|
## Ruling
|
|
@@ -33,7 +33,7 @@ skip.
|
|
|
33
33
|
## Consequences
|
|
34
34
|
|
|
35
35
|
- The shared hook dispatcher from plan 0001 lands in every repo that has one, which also
|
|
36
|
-
delivers the dead `
|
|
36
|
+
delivers the dead `repo-05/hooks/pre-push` fix.
|
|
37
37
|
- `simt-dev doctor` reports hook installation state, so "the hook will cover it" is a
|
|
38
38
|
verifiable claim rather than an assumption.
|
|
39
39
|
- The 11 hook-less repos get a pre-commit on adoption, which ends the silent case entirely.
|
|
@@ -4,9 +4,9 @@ Status: **accepted** · 2026-10-05 · affects all repos
|
|
|
4
4
|
|
|
5
5
|
## Context
|
|
6
6
|
|
|
7
|
-
- `
|
|
7
|
+
- `repo-14` / `repo-11`: "**Split any file once it exceeds 1000 lines**; extract
|
|
8
8
|
along the nearest cohesive boundary instead of growing the file further."
|
|
9
|
-
- `
|
|
9
|
+
- `repo-07`: "**File length: 400 lines is a smell threshold, not a hard gate.**" Generated
|
|
10
10
|
`.mc` files are exempt, with a named exception for a 408-line template.
|
|
11
11
|
- Several repos: no file-length rule stated at all.
|
|
12
12
|
|
|
@@ -27,10 +27,10 @@ Per-repo overrides are permitted via `limits.fileLines` in `simt-dev.json`.
|
|
|
27
27
|
|
|
28
28
|
## Consequences
|
|
29
29
|
|
|
30
|
-
-
|
|
30
|
+
- one repository' hard 1000 gate and garmin's soft 400 both survive unchanged in force.
|
|
31
31
|
- The generated-file exemption that garmin had to state in prose becomes a declared property,
|
|
32
32
|
so the hook can honour it without hardcoding `.mc`.
|
|
33
|
-
- `
|
|
33
|
+
- `repo-14`'s pre-commit file-size check (currently inlined as 1000 lines of shell, and
|
|
34
34
|
duplicating `scripts/check-file-sizes.ts`) becomes one implementation in the shared
|
|
35
35
|
dispatcher.
|
|
36
36
|
|
|
@@ -38,5 +38,5 @@ Per-repo overrides are permitted via `limits.fileLines` in `simt-dev.json`.
|
|
|
38
38
|
|
|
39
39
|
- **One number org-wide.** Either it is too tight for legitimately long declarative files or
|
|
40
40
|
too loose to prompt a human at 400.
|
|
41
|
-
- **No rule.**
|
|
41
|
+
- **No rule.** one repository' pre-commit check exists precisely because a rule was missing,
|
|
42
42
|
and it is now implemented twice.
|
|
@@ -9,9 +9,9 @@ The coverage bar is the one concept that is mutually incompatible *in kind* acro
|
|
|
9
9
|
| Repo | Bar |
|
|
10
10
|
|---|---|
|
|
11
11
|
| Repos with a declared numeric bar (outside this estate) | Quantitative. `fail_under = 100`, branch coverage, "a behavioural coverage gate, not a line-count scoreboard" |
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
12
|
+
| `repo-07` | Quantitative, but a different quantity. Memory: 64 KB app / 24 KB Glance against a 32 KB ceiling, plus 12 named lint scripts |
|
|
13
|
+
| `repo-14`, `repo-11`, `repo-13` | Qualitative. No number anywhere. Evidence discipline: named commands, 100 % pass, no unjustified skips, behavioural test delta |
|
|
14
|
+
| `repo-04` | Neither. Three verification commands. |
|
|
15
15
|
|
|
16
16
|
You cannot average these. 100 % line coverage is unreachable for Kotlin/Compose UI code and for
|
|
17
17
|
Monkey C memory budgets; a memory ceiling is not a coverage proxy; "no number" is not a weaker
|
|
@@ -8,9 +8,9 @@ Six `AGENTS.md` files use the same conceptual sections under three different hea
|
|
|
8
8
|
|
|
9
9
|
| File | Authority | Invariants |
|
|
10
10
|
|---|---|---|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
11
|
+
| `repo-04`, `repo-13` | "Authority and Scope" | "Non-Negotiable Invariants" |
|
|
12
|
+
| `repo-07`, `repo-14` | "Authority and scope" | "Non-negotiable invariants" |
|
|
13
|
+
| `repo-11` | "Authority and **ownership**" | "Non-negotiable invariants" |
|
|
14
14
|
|
|
15
15
|
At least one repo outside this estate uses a numbered 12-part layout with no equivalent
|
|
16
16
|
sections at all.
|
|
@@ -43,12 +43,12 @@ knows immediately what breaking it costs and who may change it.
|
|
|
43
43
|
|
|
44
44
|
- Cross-repo reading becomes possible: section N means the same class of thing everywhere.
|
|
45
45
|
- A future grep for "what are the invariants" returns a comparable list across all 16 repos.
|
|
46
|
-
- "Ownership" is kept in the heading because `
|
|
46
|
+
- "Ownership" is kept in the heading because `repo-11` earned it by being the only
|
|
47
47
|
file that enumerates ownership explicitly.
|
|
48
48
|
|
|
49
49
|
## Rejected
|
|
50
50
|
|
|
51
|
-
- **Standardise on Title Case** (`
|
|
51
|
+
- **Standardise on Title Case** (`repo-04`'s variant). Capitalisation in headings is noise;
|
|
52
52
|
three repos already use sentence case.
|
|
53
53
|
- **Normalise a differently-shaped layout immediately.** A mechanical conversion of 12
|
|
54
54
|
numbered parts would produce a worse document than a mapping.
|
|
@@ -6,7 +6,7 @@ Status: **accepted** · 2026-10-05 · affects all repos
|
|
|
6
6
|
|
|
7
7
|
The estate has two incompatible verdict vocabularies.
|
|
8
8
|
|
|
9
|
-
`.cursor/rules/test-gate.mdc` — used by all three
|
|
9
|
+
`.cursor/rules/test-gate.mdc` — used by all three one repository — allows one outcome:
|
|
10
10
|
|
|
11
11
|
> Return `BLOCK` when: any test fails, is skipped without pre-existing justification, or is
|
|
12
12
|
> unrun; the executable diff has no test delta without explicit justification; the coordinator
|
|
@@ -23,7 +23,7 @@ to come from the graph registry instead — which is why `paths` lives in
|
|
|
23
23
|
- A skill is a directory `<name>/` containing `SKILL.md`.
|
|
24
24
|
- `name` and `description` are **mandatory** in frontmatter.
|
|
25
25
|
- `argument-hint` is recommended for any skill invoked by hand, and is optional otherwise —
|
|
26
|
-
|
|
26
|
+
repo-05's 7 skills omit it legitimately, since they are task-shaped rather than
|
|
27
27
|
user-invoked.
|
|
28
28
|
- Body size and reference-file count are declared in `graph/skills.graph.json`, not in
|
|
29
29
|
frontmatter, so the registry stays the single source of truth for resolution metadata.
|
|
@@ -6,7 +6,7 @@ Status: **accepted** · 2026-10-05 · affects all repos
|
|
|
6
6
|
|
|
7
7
|
Four repos independently arrived at the same rule, in two ecosystems, with the same rationale.
|
|
8
8
|
|
|
9
|
-
- `
|
|
9
|
+
- `repo-14` / `repo-11`: "Run tests and quality checks through repository `npm run
|
|
10
10
|
...` scripts… **Do not invoke local package binaries through `npx` or `npm exec`** unless no
|
|
11
11
|
repository script supports the required operation."
|
|
12
12
|
- A Python repo outside this estate, in the same form: "**Do not use `uv run pytest`**; the
|
|
@@ -20,8 +20,8 @@ work:
|
|
|
20
20
|
5. CI must carry a permanent `windows-latest` job.
|
|
21
21
|
|
|
22
22
|
Against that: **no Windows developer appears anywhere in the repos**, and no repo's own
|
|
23
|
-
toolchain is Windows-ready either — `
|
|
24
|
-
`
|
|
23
|
+
toolchain is Windows-ready either — `repo-07` drives the Monkey C toolchain, and
|
|
24
|
+
`repo-05`/`repo-03`/`repo-01` drive Gradle and Android tooling. So the CLI would have been the
|
|
25
25
|
*only* Windows-ready artefact in the estate, serving nobody.
|
|
26
26
|
|
|
27
27
|
The deciding argument is not the cost, it is the shape: obligations 1–3 are correctness
|
|
@@ -9,9 +9,9 @@ Affects: `resolve/source.mjs`, `skills install`, `skills show`, `skills brief`
|
|
|
9
9
|
single-repo use first class. It fixed resolution, not lookup, and the next defect was found
|
|
10
10
|
immediately by the user who asked for the previous one.
|
|
11
11
|
|
|
12
|
-
Someone who cloned **only `
|
|
12
|
+
Someone who cloned **only `repo-14`** has the `repo-14` body of `slice-commit` on disk at
|
|
13
13
|
`.agents/skills/slice-commit`. They have no workspace. The registry entry
|
|
14
|
-
`
|
|
14
|
+
`repo-14/.agents/skills/slice-commit` is satisfied by that file. Both commands refused:
|
|
15
15
|
|
|
16
16
|
```
|
|
17
17
|
$ simt-dev skills install slice-commit
|
|
@@ -15,8 +15,8 @@ Affects: `rules/mdc/*.mdc`, `projection/mdc.mjs`, `projection/project.mjs`, `pro
|
|
|
15
15
|
The estate carried three always-on `.mdc` rule families across three repos, in eight
|
|
16
16
|
files, with no two byte-identical.
|
|
17
17
|
|
|
18
|
-
The worst pair differed by **one byte** (`
|
|
19
|
-
at 1280 against `
|
|
18
|
+
The worst pair differed by **one byte** (`repo-14/.cursor/rules/intake-questions.mdc`
|
|
19
|
+
at 1280 against `repo-11`'s at 1281) while hiding three semantically loaded line
|
|
20
20
|
changes: a plan-tracker path rewritten to point at the other repo, `migrations` added to the
|
|
21
21
|
list of decisions worth asking about, and a requirement silently dropped. No size-, hash-,
|
|
22
22
|
hash-adjacency- or line-count-based duplicate detector could ever have flagged that pair. The
|
|
@@ -26,18 +26,18 @@ difference, it is a semantic loss**, and the smaller copy is the one missing a r
|
|
|
26
26
|
Reading all eight produced three findings that a variant mechanism cannot represent:
|
|
27
27
|
|
|
28
28
|
1. `test-gate`'s fourth `BLOCK` bullet is **complementary across the pair, not alternative**.
|
|
29
|
-
`
|
|
30
|
-
or deploy-order evidence; `
|
|
29
|
+
`repo-14` blocks a consumer that requires an API field with no backward-compatibility
|
|
30
|
+
or deploy-order evidence; `repo-11` blocks a producer that ships without a test
|
|
31
31
|
asserting the new invariant. Each repo's reviewer was told to check only its own half.
|
|
32
32
|
2. `preview-postflight`'s second Requirements bullet is likewise complementary, and looks
|
|
33
|
-
like a conflict because the two files put different rules in the same slot: `
|
|
33
|
+
like a conflict because the two files put different rules in the same slot: `repo-14`
|
|
34
34
|
says set `SOCIAL_API_BASE_URL` in the Cloudflare Workers Builds preview environment;
|
|
35
|
-
`
|
|
35
|
+
`repo-11` says credentials live in the operator keychain only, never in source,
|
|
36
36
|
chat, D1 or logs.
|
|
37
|
-
3. Two deletions left no trace. `
|
|
37
|
+
3. Two deletions left no trace. `repo-11` dropped `repo-14`'s worked example
|
|
38
38
|
(`Partial pass (e.g. 6/8 pass) is a hard blocker`) — the example is what does the work,
|
|
39
39
|
and its removal is invisible in a diff review because the rule's normative force looks
|
|
40
|
-
unchanged. `
|
|
40
|
+
unchanged. `repo-13` dropped the entire `## Allowed Without Account` section, so
|
|
41
41
|
it has no documented set of probes that are legal without credentials.
|
|
42
42
|
|
|
43
43
|
A variant mechanism models "these are the same rule, rendered per stack". None of these three
|
|
@@ -79,7 +79,7 @@ the rule would either hard-code one repo's paths for everyone or push the decisi
|
|
|
79
79
|
prose, which is the drift this mechanism exists to remove.
|
|
80
80
|
|
|
81
81
|
An **empty** value, or an empty default, drops the line it sits on. That is how one canonical
|
|
82
|
-
file reaches `
|
|
82
|
+
file reaches `repo-13` (no plan-tracker note) and `repo-15` (not a Workers repo
|
|
83
83
|
at all) without printing a blank bullet. A placeholder with neither a default nor a
|
|
84
84
|
declaration is a **gate failure**, because a rule shipping a literal `{{key}}` reads as
|
|
85
85
|
finished and is not.
|
|
@@ -88,7 +88,7 @@ finished and is not.
|
|
|
88
88
|
|
|
89
89
|
`preview-postflight` declares `applies: []` and `optIn: true`. It names specific preview
|
|
90
90
|
Workers, `AUTH_PROVIDER_POLICY`, `POSTFLIGHT_PASSWORD_UID` and specific production domains.
|
|
91
|
-
Selecting it on `cloudflare-workers` would hand it to `
|
|
91
|
+
Selecting it on `cloudflare-workers` would hand it to `repo-15` and `repo-08` — Workers
|
|
92
92
|
repos with no preview Workers and no Social identity — which is the same over-match bug that
|
|
93
93
|
`rules/stacks/react-web.md` had when it declared `typescript` and `vitest`.
|
|
94
94
|
|
|
@@ -128,7 +128,7 @@ Silent deletion is the one thing a reviewer cannot audit.
|
|
|
128
128
|
|
|
129
129
|
`RULE_FILES` named only `AGENTS.md`, `CLAUDE.md` and `CONTRIBUTING.md` for the whole life of
|
|
130
130
|
the tool. `simt-dev init` therefore reported **`CONFLICTS none detected`** on
|
|
131
|
-
`
|
|
131
|
+
`repo-11` — the repo holding the worst-drifted content in the estate — because it
|
|
132
132
|
never looked at the `.mdc` files at all.
|
|
133
133
|
|
|
134
134
|
A detector that only knows the filenames it was written for will quietly miss a whole rule
|
|
@@ -144,7 +144,7 @@ the same failure, one level down.
|
|
|
144
144
|
|
|
145
145
|
### 6. An autonomous-push probe must match every phrasing the estate actually used
|
|
146
146
|
|
|
147
|
-
The probe matched `committed and pushed`, which is `
|
|
147
|
+
The probe matched `committed and pushed`, which is `repo-07`'s exact DoD wording. A
|
|
148
148
|
different author wrote `Always commit and pushed`; another would write `commit and push`. The
|
|
149
149
|
first version matched only the first form and reported a real autonomous-push rule as clean.
|
|
150
150
|
|
|
@@ -168,7 +168,7 @@ the estate's real text, not against text written to match the probe.**
|
|
|
168
168
|
|
|
169
169
|
- **A variant mechanism per stack** (one `test-gate.mdc` per repo). This is what produced the
|
|
170
170
|
drift; modelling it as intended behaviour would make the drift permanent and legitimate.
|
|
171
|
-
- **Picking the "best" copy per family and adopting it as canonical.** `
|
|
171
|
+
- **Picking the "best" copy per family and adopting it as canonical.** `repo-14`'s
|
|
172
172
|
`test-gate` is the better copy only by accident; it is missing the producer-side `BLOCK`
|
|
173
173
|
bullet that social-api has. Choosing one per family would have discarded a real rule.
|
|
174
174
|
- **Deleting the 8 files outright** rather than generating over them. A repo with no
|
|
@@ -5,7 +5,7 @@ Affects: `hooks/capability.mjs`, `projection/manifest.mjs`, `commands/init.mjs`,
|
|
|
5
5
|
|
|
6
6
|
## Context
|
|
7
7
|
|
|
8
|
-
The first real adoption wrote a manifest declaring five gates into `
|
|
8
|
+
The first real adoption wrote a manifest declaring five gates into `repo-13`.
|
|
9
9
|
Three of them could not run:
|
|
10
10
|
|
|
11
11
|
| Gate | Kind | Why it could not run |
|
|
@@ -25,7 +25,7 @@ user does after adopting simt-dev is discover that the tool makes committing imp
|
|
|
25
25
|
the only escape any of them will find is `--no-verify`.
|
|
26
26
|
|
|
27
27
|
That is the exact spiral the dispatcher was built to prevent. Step 8's design note says a gate
|
|
28
|
-
that cannot run must be a failure rather than a skip, precisely because `
|
|
28
|
+
that cannot run must be a failure rather than a skip, precisely because `repo-15` removed
|
|
29
29
|
tests from `pre-commit` for being slow and the team's answer was `--no-verify`. Failing closed
|
|
30
30
|
is right; declaring an unrunnable gate is wrong.
|
|
31
31
|
|
|
@@ -6,14 +6,14 @@ Affects: `simt-dev hooks install`, `simt-dev hooks status`, `simt-dev compare`,
|
|
|
6
6
|
|
|
7
7
|
## Context
|
|
8
8
|
|
|
9
|
-
Phase 4 began by piloting `simt-dev init` on `
|
|
9
|
+
Phase 4 began by piloting `simt-dev init` on `repo-15`. The dry run was
|
|
10
10
|
unremarkable. The defect appeared when I read what `hooks install` would actually
|
|
11
11
|
do to that repository, which is something I had not done in any of the earlier
|
|
12
12
|
phases.
|
|
13
13
|
|
|
14
|
-
`
|
|
14
|
+
`repo-15` has a hand-written `.git/hooks/pre-commit` — 4088 bytes, with prose
|
|
15
15
|
explaining why it deliberately does not run tests. `core.hooksPath` is unset.
|
|
16
|
-
`simt-dev hooks status --root ../
|
|
16
|
+
`simt-dev hooks status --root ../repo-15 --json` reported:
|
|
17
17
|
|
|
18
18
|
```json
|
|
19
19
|
{ "hooksPath": null, "installed": [{ "event": "pre-commit", "present": false }],
|
|
@@ -64,7 +64,7 @@ question, which is why fixing the shared one changed nothing visible.
|
|
|
64
64
|
1. **Ask git where hooks are, never infer it from whether config exists.**
|
|
65
65
|
`resolveHooksDir(root)` implements the two rules directly: `core.hooksPath`
|
|
66
66
|
when set (a relative value resolves against the top of the working tree, which
|
|
67
|
-
is what makes `
|
|
67
|
+
is what makes `repo-05`'s `hooks` work), otherwise `<git-common-dir>/hooks`.
|
|
68
68
|
|
|
69
69
|
`git rev-parse --git-path hooks` is the obvious shortcut and is unusable. It
|
|
70
70
|
returns `.git/hooks` in a normal repository, `hooks` in a linked worktree, and
|
|
@@ -103,7 +103,7 @@ question, which is why fixing the shared one changed nothing visible.
|
|
|
103
103
|
7. **A foreign hook for an event this tool does not gate is chained, not stopped.**
|
|
104
104
|
`core.hooksPath` does not scope this tool to the events it cares about and leave
|
|
105
105
|
the rest to git; git reads *every* hook from the named directory and from nowhere
|
|
106
|
-
else. So the moment routing lands, `
|
|
106
|
+
else. So the moment routing lands, `repo-05`'s `post-checkout`, `post-commit`
|
|
107
107
|
and `post-merge` — events this tool gates nothing about — stop running. Nothing
|
|
108
108
|
fails, nothing is logged, and the hook is still on disk looking intact: the
|
|
109
109
|
failure is indistinguishable from the hook having passed. Ruling 3's refusal
|
|
@@ -128,9 +128,9 @@ question, which is why fixing the shared one changed nothing visible.
|
|
|
128
128
|
anything the author sees.
|
|
129
129
|
|
|
130
130
|
"Does this tool gate it" is a fact about the manifest, not about the event vocabulary,
|
|
131
|
-
and reading it the second way cost `
|
|
131
|
+
and reading it the second way cost `repo-05` its `pre-push`. That hook is git-lfs.
|
|
132
132
|
`pre-push` is an event this tool gates *in general*, so a vocabulary check displaced it
|
|
133
|
-
— and because `
|
|
133
|
+
— and because `repo-05` declares no `pre-push` gate there is no shim for that event
|
|
134
134
|
either, so git would have found no `pre-push` hook at all. LFS objects would have
|
|
135
135
|
stopped being uploaded, with nothing to notice it: the same silent failure this ruling
|
|
136
136
|
exists to prevent, reached by fixing the wrong half of it. The test asks the manifest.
|
|
@@ -154,8 +154,8 @@ a list that rots, so one test now asserts the two agree and another asserts ever
|
|
|
154
154
|
row carries `notes` and `foreignHooks` even when empty.
|
|
155
155
|
|
|
156
156
|
Measured across the estate after the fix, six repositories run hooks simt-dev did
|
|
157
|
-
not write — `
|
|
158
|
-
`
|
|
157
|
+
not write — `repo-15` (4088 B), `repo-05` (four hooks), `repo-14` (two),
|
|
158
|
+
`repo-11` (two), `repo-10` (one), and the `.wt-wear-redesign`
|
|
159
159
|
worktree (five, including the `git-lfs` `pre-push`). Every one of them would have
|
|
160
160
|
been silently taken over.
|
|
161
161
|
|
|
@@ -8,21 +8,21 @@ Affects: `simt-dev init`, `simt-dev check`, `simt-dev hooks status`, `simt-dev h
|
|
|
8
8
|
## Context
|
|
9
9
|
|
|
10
10
|
ADR-017 made `simt-dev init` declare only gates a repository can actually run, and it shipped
|
|
11
|
-
green because the first adopter — `
|
|
11
|
+
green because the first adopter — `repo-13` — is a Node repository whose scripts
|
|
12
12
|
and binaries are exactly what the filter understood. It was verified by running that
|
|
13
13
|
repository's own tests.
|
|
14
14
|
|
|
15
|
-
`
|
|
15
|
+
`repo-05`, the third pilot, is a Gradle repository. It has **no `package.json` at all**. The
|
|
16
16
|
dry run dropped all five proposed gates, and the remedy for each one was an instruction the
|
|
17
17
|
reader cannot follow. The output as it stood, before this record was written:
|
|
18
18
|
|
|
19
19
|
```
|
|
20
|
-
$ simt-dev init --root ../
|
|
20
|
+
$ simt-dev init --root ../repo-05
|
|
21
21
|
dropped gate test: no "test" script in package.json
|
|
22
22
|
remedy: add a "test" script to package.json, then re-run: simt-dev init --yes
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Ruling 4 changes both lines. `
|
|
25
|
+
Ruling 4 changes both lines. `repo-05` will never have a `package.json`. "Install prettier as
|
|
26
26
|
a dev dependency" is equally impossible. The filter was right about the facts and wrong about
|
|
27
27
|
what followed from them, and the reader's only honest reaction to that remedy is to stop
|
|
28
28
|
trusting the next one.
|
|
@@ -75,10 +75,10 @@ One manifest. Three answers. The one I would have shipped if I had only run `che
|
|
|
75
75
|
|
|
76
76
|
## Consequences
|
|
77
77
|
|
|
78
|
-
- `
|
|
78
|
+
- `repo-05` declares `i18n-completeness: scripts/check_string_translations.sh` on
|
|
79
79
|
pre-commit — the gate its own hand-written pre-commit hook already runs — and
|
|
80
80
|
`test: ./gradlew check` on pre-push.
|
|
81
|
-
- The `
|
|
81
|
+
- The `repo-13` and `repo-15` manifests are unaffected: they declare no commands.
|
|
82
82
|
- A Node repository that wants to override a gate now can, without abandoning the gate name it
|
|
83
83
|
shares with every other repository. `gates.commands.format` is a supported thing to write.
|
|
84
84
|
- `readCapability` reads one more file per invocation, and it reads it in a `try` that cannot
|
|
@@ -104,7 +104,7 @@ One manifest. Three answers. The one I would have shipped if I had only run `che
|
|
|
104
104
|
|
|
105
105
|
## Evidence
|
|
106
106
|
|
|
107
|
-
`
|
|
107
|
+
`repo-05` is the first adopter with a non-npm runner and the first to declare a command for a
|
|
108
108
|
gate that is not of kind `command`. Every earlier adopter was a Node repository, which is why
|
|
109
109
|
ADR-017's own verification passed. The estate's three runners — npm, Gradle and Python — were
|
|
110
110
|
all in the repository set the whole time; only the pilot order had not reached them.
|
|
@@ -14,7 +14,7 @@ the machine of whoever remembers to run it, and a hook is a developer's machine.
|
|
|
14
14
|
review is somebody else's, and every merge is a machine nobody gated.
|
|
15
15
|
|
|
16
16
|
The plan called this "a reusable workflow", and named
|
|
17
|
-
`
|
|
17
|
+
`the organisation/simt-dev/.github/workflows/sync-check.yml` as a file "referenced by every
|
|
18
18
|
consumer". Nothing in this repository had a `.github/` at all. There were three ways to
|
|
19
19
|
close the gap, and they differ in who the resulting file belongs to:
|
|
20
20
|
|
|
@@ -93,7 +93,7 @@ rules once told readers to edit a source tree they did not have.
|
|
|
93
93
|
|
|
94
94
|
## Rejected
|
|
95
95
|
|
|
96
|
-
- **`uses:
|
|
96
|
+
- **`uses: the organisation/simt-dev/.github/workflows/sync-check.yml@main`.** Rejected under
|
|
97
97
|
ruling 1: it is the plan's own suggestion, and it points every reader at bytes they do
|
|
98
98
|
not have. Worth recording that the plan wrote it, because the next person to read §6
|
|
99
99
|
will find it there.
|
package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md
CHANGED
|
@@ -19,8 +19,8 @@ empty", and deferred it to a record of its own.
|
|
|
19
19
|
|
|
20
20
|
The estate confirms the first half and not the conclusion. Thirteen of sixteen repositories
|
|
21
21
|
have no `.gitattributes` at all. The three that do disagree about almost everything:
|
|
22
|
-
`
|
|
23
|
-
endings plus ConnectIQ binary markers; `
|
|
22
|
+
`repo-05` declares only LFS filters for `.tif` and `.zip`; `repo-07` declares line
|
|
23
|
+
endings plus ConnectIQ binary markers; `repo-11` declares exactly one whitespace
|
|
24
24
|
suppression for hash-locked vendored content. There is no divergence here of the kind a
|
|
25
25
|
baseline fixes by being consistent. There is a file missing.
|
|
26
26
|
|
|
@@ -48,7 +48,7 @@ to be the line ruling 7 sent this file to make.
|
|
|
48
48
|
because nothing in the pattern says those are binary. `text=auto` is a detection: git
|
|
49
49
|
sniffs each path and applies normalisation only to files it concludes are text. This is
|
|
50
50
|
the most common way a repository corrupts its own binaries, and it is invisible until
|
|
51
|
-
someone diffs an image. `
|
|
51
|
+
someone diffs an image. `repo-07` had independently arrived at `text=auto eol=lf` on
|
|
52
52
|
its first line, which is the strongest available evidence that the estate would pick the
|
|
53
53
|
cautious form if asked — this ruling does not ask, it supplies.
|
|
54
54
|
|
|
@@ -96,8 +96,8 @@ what any file is". A future baseline for a per-stack attribute would have to arr
|
|
|
96
96
|
different row with its own record, which is what §3.2's keep-it-local rule asks for anyway.
|
|
97
97
|
|
|
98
98
|
The repositories with an existing `.gitattributes` are the interesting case and they are not
|
|
99
|
-
in conflict: `
|
|
100
|
-
`
|
|
99
|
+
in conflict: `repo-05`'s LFS filters, `repo-07`'s binary markers and
|
|
100
|
+
`repo-11`'s whitespace suppression all stay beneath the region and all keep winning,
|
|
101
101
|
because git takes the last match. No repository loses a line it wrote.
|
|
102
102
|
|
|
103
103
|
This repository adopts neither baseline for itself, and the reason is the same one that keeps
|
|
@@ -10,7 +10,7 @@ a copied byte range. Settles the `tsconfig.base.json` item of §3.1 row 5 and ro
|
|
|
10
10
|
## Context
|
|
11
11
|
|
|
12
12
|
Row 5 promised eight baselines and named two problems among them that are not about a file
|
|
13
|
-
being missing:
|
|
13
|
+
being missing: repo-08's seven absent strictness flags, and `moduleResolution` spelled
|
|
14
14
|
inconsistently across the workspace. ADR-025 ruled `tsconfig.base.json` out of the region
|
|
15
15
|
mechanism because JSON has no comments, and its consequences promised that "the next fix is
|
|
16
16
|
three more JSON files and therefore a different mechanism". ADR-026 supplied that mechanism
|
|
@@ -95,7 +95,7 @@ one an external document uses.
|
|
|
95
95
|
|
|
96
96
|
A TypeScript repository now gets twelve flags it did not have, and one of them — the floor
|
|
97
97
|
arriving through `extends` — is a behaviour change that can surface real type errors in
|
|
98
|
-
`
|
|
98
|
+
`repo-08`, which is the point of the record and also the reason adoption is one commit
|
|
99
99
|
with a `tsc` run rather than a silent upgrade. A base file is a floor, and a repository
|
|
100
100
|
that had not been standing on one is the case where raising it finds something.
|
|
101
101
|
|
|
@@ -104,7 +104,7 @@ nobody has been asked, and it is the record to read next for either.
|
|
|
104
104
|
|
|
105
105
|
- **A shipped `.nvmrc` or an `engines` value in a shared baseline.** Rejected as
|
|
106
106
|
unrulable: the value is per-repository and the tool can only guess it. Shipping `22` would
|
|
107
|
-
break `
|
|
107
|
+
break `repo-14`, which pins 24.
|
|
108
108
|
- **Reporting cross-repository node divergence as a finding.** Rejected by the Context. The
|
|
109
109
|
plan's premise was wrong and the correction is recorded here so it is not re-derived from
|
|
110
110
|
the plan text.
|