instar 1.3.1021 → 1.3.1022
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/package.json +1 -1
- package/scripts/check-rule3-coverage.cjs +16 -3
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.1022.md +42 -0
- package/upgrades/rule3-registry-lookup-provider-paths.eli16.md +36 -0
- package/upgrades/side-effects/rule3-registry-lookup-provider-paths.md +57 -0
package/package.json
CHANGED
|
@@ -149,9 +149,22 @@ function readRegistry() {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
function isInRegistry(filepath, registryContent) {
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
// The registry's Location column is SECTION-relative, not uniformly relative
|
|
153
|
+
// to src/. Most sections write paths relative to src/, but the provider
|
|
154
|
+
// substrate section ("Provider substrate (`src/providers/`)") writes them
|
|
155
|
+
// relative to src/providers/ — 21 of its 23 rows, measured on main.
|
|
156
|
+
//
|
|
157
|
+
// Stripping only `src/` therefore looked for `providers/adapters/X.ts` while
|
|
158
|
+
// the row said `adapters/X.ts`, so no provider file could ever match. A file
|
|
159
|
+
// listed in three registry rows was still refused for a "missing registry
|
|
160
|
+
// entry", which told the author to add a row that already existed.
|
|
161
|
+
if (registryContent.includes(filepath.replace(/^src\//, ''))) return true;
|
|
162
|
+
// Only widen for genuine provider paths, so no other file gains a match it
|
|
163
|
+
// would not otherwise have had.
|
|
164
|
+
if (filepath.startsWith('src/providers/')) {
|
|
165
|
+
return registryContent.includes(filepath.replace(/^src\/providers\//, ''));
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
155
168
|
}
|
|
156
169
|
|
|
157
170
|
function hasMatchingCanary(stagedFiles, filepath) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-28T09:26:52.565Z",
|
|
5
|
+
"instarVersion": "1.3.1022",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The Rule 3 pre-commit gate's registry lookup could never match a file under `src/providers/`.
|
|
9
|
+
|
|
10
|
+
`isInRegistry()` stripped only `src/`, so it searched the state-detector registry for
|
|
11
|
+
`providers/adapters/foo/Bar.ts` — while the registry's provider section writes its Location column
|
|
12
|
+
relative to `src/providers/`, i.e. `adapters/foo/Bar.ts`. 21 of that section's 23 rows use that
|
|
13
|
+
form. The strings never met.
|
|
14
|
+
|
|
15
|
+
A registered file was therefore still refused, with a message instructing the author to add a
|
|
16
|
+
registry entry that already existed — in one case, one that appears in three separate rows.
|
|
17
|
+
|
|
18
|
+
The lookup now also tries the `src/providers/`-relative form, guarded by
|
|
19
|
+
`filepath.startsWith('src/providers/')` so no other file gains a match it did not already have.
|
|
20
|
+
|
|
21
|
+
## What to Tell Your User
|
|
22
|
+
|
|
23
|
+
Nothing. This is a contributor-facing pre-commit script — not shipped, not executed at runtime, no
|
|
24
|
+
user-visible behaviour.
|
|
25
|
+
|
|
26
|
+
## Summary of New Capabilities
|
|
27
|
+
|
|
28
|
+
None. A registry entry still does not exempt a file on its own: `inRegistry && (hasRationale ||
|
|
29
|
+
hasCanary)` is unchanged. This only stops the gate from ignoring a registry row that genuinely
|
|
30
|
+
exists.
|
|
31
|
+
|
|
32
|
+
## Evidence
|
|
33
|
+
|
|
34
|
+
- Red → green, observed rather than predicted: the provider case failed and the `src/`-relative
|
|
35
|
+
control passed before the fix (1 failed / 23 passed); 24/24 after. The control is what isolates
|
|
36
|
+
the cause to the path form.
|
|
37
|
+
- **Why the branch had no coverage:** `tests/unit/scripts/check-rule3-coverage.test.ts` copied the
|
|
38
|
+
script into its tmp repo but executed the *original*. Since the script resolves its registry from
|
|
39
|
+
`__dirname/../specs/`, it read the real repo's registry while reading staged files from the tmp
|
|
40
|
+
repo — so every registry fixture a test wrote was silently ignored. Fixed by running the copy;
|
|
41
|
+
all 22 pre-existing tests pass unchanged under the corrected harness.
|
|
42
|
+
- Side-effects review: `upgrades/side-effects/rule3-registry-lookup-provider-paths.md`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ELI16 — the gate told authors to add a registry row that was already there
|
|
2
|
+
|
|
3
|
+
There is a registry listing every place Instar reads state from something it doesn't control.
|
|
4
|
+
A pre-commit gate uses it: if your file is in the registry *and* carries a rationale comment,
|
|
5
|
+
the gate lets it through.
|
|
6
|
+
|
|
7
|
+
For files under `src/providers/`, that lookup could never succeed.
|
|
8
|
+
|
|
9
|
+
**Why.** The gate turned `src/providers/adapters/foo/Bar.ts` into
|
|
10
|
+
`providers/adapters/foo/Bar.ts` and searched the registry for that string. But the registry's
|
|
11
|
+
provider section writes its paths relative to `src/providers/`, so the row actually says
|
|
12
|
+
`adapters/foo/Bar.ts`. The two never matched — 21 of the 23 provider rows are written that way.
|
|
13
|
+
|
|
14
|
+
The result: a file listed in the registry — one of them appears in **three** rows — was still
|
|
15
|
+
refused, with a message telling the author to add a registry entry. Adding the row again would
|
|
16
|
+
not have helped. That is the worst kind of gate message: it is confidently wrong about what
|
|
17
|
+
would fix it.
|
|
18
|
+
|
|
19
|
+
**Why nobody noticed.** The test file for this gate builds a throwaway repo, copies the script
|
|
20
|
+
into it, writes a registry fixture — and then runs the *original* script instead of the copy.
|
|
21
|
+
The script finds its registry relative to its own folder, so it read the real repo's registry
|
|
22
|
+
while reading the staged files from the throwaway one. Every registry fixture a test wrote was
|
|
23
|
+
quietly ignored, so this whole branch had no working test coverage. That is fixed here too, by
|
|
24
|
+
running the copy.
|
|
25
|
+
|
|
26
|
+
**How I know the fix works rather than believing it.** I wrote two tests: the provider case, and
|
|
27
|
+
a control using the ordinary path form that should already have worked. Before the fix the
|
|
28
|
+
provider one failed and the control passed. After, both pass, and the 22 tests that were already
|
|
29
|
+
there still pass.
|
|
30
|
+
|
|
31
|
+
Worth saying: the control failed on my first attempt too, which meant my explanation was wrong.
|
|
32
|
+
Chasing that disagreement is what turned up the harness defect. A control test that fails is
|
|
33
|
+
telling you something.
|
|
34
|
+
|
|
35
|
+
**Scope.** A registry entry still does not exempt a file on its own — a rationale or a canary is
|
|
36
|
+
still required. This only stops the gate from ignoring a registry row that genuinely exists.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Side-effects review — Rule 3 gate registry lookup (provider paths)
|
|
2
|
+
|
|
3
|
+
**Change:** `isInRegistry()` in `scripts/check-rule3-coverage.cjs` gains a second, narrowly-scoped
|
|
4
|
+
path form; `tests/unit/scripts/check-rule3-coverage.test.ts` runs the copied script instead of the
|
|
5
|
+
original and gains 2 tests + a registry-fixture helper.
|
|
6
|
+
|
|
7
|
+
## Direction of effect — the only question that matters for a gate
|
|
8
|
+
|
|
9
|
+
This makes the gate **strictly more permissive**, so the risk to weigh is a false ACCEPT, never a
|
|
10
|
+
false refusal.
|
|
11
|
+
|
|
12
|
+
| Surface | Effect |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Non-provider files | **None.** The widened form is behind `filepath.startsWith('src/providers/')`, so no other file can gain a match it did not already have. |
|
|
15
|
+
| Provider files, registered, with rationale | Now correctly accepted (was refused). **This is the fix.** |
|
|
16
|
+
| Provider files, registered, no rationale/canary | Still refused — `inRegistry && (hasRationale \|\| hasCanary)` is unchanged. |
|
|
17
|
+
| Provider files, unregistered | Still refused. Containment against a row that does not exist still fails. |
|
|
18
|
+
| Runtime / shipped product | **None.** A pre-commit script; not in `src/`, not bundled, not executed by the server. |
|
|
19
|
+
|
|
20
|
+
## The false-accept surface, stated plainly
|
|
21
|
+
|
|
22
|
+
Containment is a substring test, so a short row path could in principle match an unintended file.
|
|
23
|
+
That property is **pre-existing** and unchanged in kind — this only adds a second candidate string
|
|
24
|
+
for paths already under `src/providers/`. A file would need to be under `src/providers/` *and* have
|
|
25
|
+
its `src/providers/`-relative path appear verbatim in the registry, which is precisely the
|
|
26
|
+
condition the fix exists to honour.
|
|
27
|
+
|
|
28
|
+
I considered resolving each row against its section heading instead, which would remove the
|
|
29
|
+
guesswork entirely. It is the better long-term shape and a larger change; it is not done here.
|
|
30
|
+
|
|
31
|
+
## Test-harness change — read this as a coverage change, not a cosmetic one
|
|
32
|
+
|
|
33
|
+
`runCheck` now executes the copy inside the tmp repo. The script resolves the registry from
|
|
34
|
+
`__dirname/../specs/`, so running the original read the **real** repo's registry while reading
|
|
35
|
+
staged files from the tmp repo.
|
|
36
|
+
|
|
37
|
+
Consequence: every registry fixture written by a test was silently ignored, and the
|
|
38
|
+
"already in the registry" branch had **no reachable coverage at all**. `beforeEach` had always
|
|
39
|
+
copied the script in — the copy was simply never run.
|
|
40
|
+
|
|
41
|
+
All 22 pre-existing tests pass unchanged under the corrected harness, which is the evidence that
|
|
42
|
+
this repaired coverage rather than altering what those tests assert.
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
- **Red → green, observed:** before the fix, the provider test failed and the `src/`-relative
|
|
47
|
+
control passed (1 failed / 23 passed). After, 24/24.
|
|
48
|
+
- The control is the load-bearing part: it fails if the cause is anything other than the provider
|
|
49
|
+
path form.
|
|
50
|
+
- **A correction this produced:** on my first attempt the control failed *too*, which meant my
|
|
51
|
+
explanation was wrong — a registry entry alone never exempted anything. Following that
|
|
52
|
+
disagreement is what surfaced the harness defect. The earlier claim that 26 files on `main` were
|
|
53
|
+
affected is withdrawn: all 26 lack a rationale and would be refused regardless.
|
|
54
|
+
|
|
55
|
+
## Rollback
|
|
56
|
+
|
|
57
|
+
`git revert`. Restores the unreachable lookup; no state, no migration, no config.
|