instar 1.3.1183 → 1.3.1185
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/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/package.json +1 -1
- package/scripts/decision-audit-presence-check.mjs +66 -20
- package/src/data/builtin-manifest.json +2 -2
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/upgrades/{1.3.1183.md → 1.3.1184.md} +40 -0
- package/upgrades/1.3.1185.md +37 -0
- package/upgrades/side-effects/w22-decision-audit-scope-coverage.md +150 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"generatedFrom": "source-tree",
|
|
4
4
|
"registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
|
|
5
|
-
"packageVersion": "1.3.
|
|
5
|
+
"packageVersion": "1.3.1185",
|
|
6
6
|
"guards": [
|
|
7
7
|
{
|
|
8
8
|
"ref": "docs/audits/phase-b/f10-triage.md",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sha256": "
|
|
2
|
+
"sha256": "3eb1bd07851f19c2b7daa4f5bcf5b38a58e611750b8edfbe5f4836b7ee919418",
|
|
3
3
|
"registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
|
|
4
|
-
"packageVersion": "1.3.
|
|
4
|
+
"packageVersion": "1.3.1185"
|
|
5
5
|
}
|
package/package.json
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { execFileSync } from 'node:child_process';
|
|
29
|
+
import { readFileSync } from 'node:fs';
|
|
29
30
|
|
|
30
31
|
// Keep in sync with inScope() in scripts/instar-dev-precommit.js — the gate
|
|
31
32
|
// this check detects bypasses OF. (A drift here only weakens detection, never
|
|
@@ -40,18 +41,43 @@ export function isInScopeFile(file) {
|
|
|
40
41
|
return false;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return
|
|
44
|
+
function isPerEntryDecisionFile(change) {
|
|
45
|
+
return /^\.instar\/instar-dev-decisions\/.+\.json$/.test(change.file);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isLegacyDecisionFile(change) {
|
|
49
|
+
return change.file === '.instar/instar-dev-decisions.jsonl';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function recordScopeFiles(record) {
|
|
53
|
+
const files = record?.scope?.files;
|
|
54
|
+
return Array.isArray(files) ? files.filter((file) => typeof file === 'string') : [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function coverageFiles(records) {
|
|
58
|
+
const covered = new Set();
|
|
59
|
+
for (const record of Array.isArray(records) ? records : []) {
|
|
60
|
+
for (const file of recordScopeFiles(record)) covered.add(file);
|
|
61
|
+
}
|
|
62
|
+
return covered;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readDecisionAuditRecords(changes) {
|
|
66
|
+
const records = [];
|
|
67
|
+
for (const change of changes.filter(isPerEntryDecisionFile)) {
|
|
68
|
+
try {
|
|
69
|
+
records.push(JSON.parse(readFileSync(change.file, 'utf8')));
|
|
70
|
+
} catch {
|
|
71
|
+
records.push({});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return records;
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
/**
|
|
52
78
|
* Pure evaluation — exported for unit tests.
|
|
53
|
-
* @param {{ changes: Array<{status: string, file: string}>, title?: string, authorType?: string }} input
|
|
54
|
-
* @returns {{ ok: boolean, exempt?: string, reason?: string, inScopeFiles?: string[] }}
|
|
79
|
+
* @param {{ changes: Array<{status: string, file: string}>, records?: unknown[], title?: string, authorType?: string }} input
|
|
80
|
+
* @returns {{ ok: boolean, exempt?: string, reason?: string, inScopeFiles?: string[], uncoveredFiles?: string[] }}
|
|
55
81
|
*/
|
|
56
82
|
export function evaluateDecisionAuditPresence(input) {
|
|
57
83
|
const title = String(input?.title ?? '');
|
|
@@ -64,21 +90,39 @@ export function evaluateDecisionAuditPresence(input) {
|
|
|
64
90
|
return { ok: true, reason: 'no in-scope changes — local gate not required' };
|
|
65
91
|
}
|
|
66
92
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
if (changes.some(isLegacyDecisionFile)) {
|
|
94
|
+
return { ok: true, reason: 'legacy decision-audit evidence present', inScopeFiles };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const recordChanges = changes.filter(isPerEntryDecisionFile);
|
|
98
|
+
if (recordChanges.length === 0) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
inScopeFiles,
|
|
102
|
+
reason:
|
|
103
|
+
`This PR changes ${inScopeFiles.length} in-scope file(s) but carries NO decision-audit record — ` +
|
|
104
|
+
`the local instar-dev pre-commit gate did not run for these commits. The usual cause is a build ` +
|
|
105
|
+
`worktree without the husky shim (created with raw 'git worktree add' instead of 'instar worktree ` +
|
|
106
|
+
`create'). Fix: in the worktree run 'npm run prepare' (wires .husky/_), then re-commit so the gate ` +
|
|
107
|
+
`evaluates the change and its audit entry (.instar/instar-dev-decisions/<ts>-<slug>.json) rides the ` +
|
|
108
|
+
`commit. See tasks #81/#80 and PRs #827/#829.`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const covered = coverageFiles(input?.records);
|
|
113
|
+
const uncoveredFiles = inScopeFiles.filter((file) => !covered.has(file));
|
|
114
|
+
if (uncoveredFiles.length === 0) {
|
|
115
|
+
return { ok: true, reason: 'decision-audit scope covers in-scope changes', inScopeFiles };
|
|
70
116
|
}
|
|
71
117
|
|
|
72
118
|
return {
|
|
73
119
|
ok: false,
|
|
74
120
|
inScopeFiles,
|
|
121
|
+
uncoveredFiles,
|
|
75
122
|
reason:
|
|
76
|
-
`This PR changes ${inScopeFiles.length} in-scope file(s) but
|
|
77
|
-
`
|
|
78
|
-
`
|
|
79
|
-
`create'). Fix: in the worktree run 'npm run prepare' (wires .husky/_), then re-commit so the gate ` +
|
|
80
|
-
`evaluates the change and its audit entry (.instar/instar-dev-decisions/<ts>-<slug>.json) rides the ` +
|
|
81
|
-
`commit. See tasks #81/#80 and PRs #827/#829.`,
|
|
123
|
+
`This PR changes ${inScopeFiles.length} in-scope file(s), but its decision-audit record scope ` +
|
|
124
|
+
`does not cover ${uncoveredFiles.length} in-scope file(s). Re-run the local instar-dev ` +
|
|
125
|
+
`pre-commit gate for this change so the decision-audit record declares the changed path(s).`,
|
|
82
126
|
};
|
|
83
127
|
}
|
|
84
128
|
|
|
@@ -114,8 +158,10 @@ if (invokedDirectly) {
|
|
|
114
158
|
console.error(`decision-audit gate: git diff failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
115
159
|
process.exit(2);
|
|
116
160
|
}
|
|
161
|
+
const changes = parseNameStatus(diffOut);
|
|
117
162
|
const res = evaluateDecisionAuditPresence({
|
|
118
|
-
changes
|
|
163
|
+
changes,
|
|
164
|
+
records: readDecisionAuditRecords(changes),
|
|
119
165
|
title: process.env.PR_TITLE,
|
|
120
166
|
authorType: process.env.PR_AUTHOR_TYPE,
|
|
121
167
|
});
|
|
@@ -126,7 +172,7 @@ if (invokedDirectly) {
|
|
|
126
172
|
console.error('decision-audit gate: FAIL');
|
|
127
173
|
console.error(res.reason);
|
|
128
174
|
console.error('');
|
|
129
|
-
console.error('In-scope files
|
|
130
|
-
for (const f of res.inScopeFiles ?? []) console.error(` - ${f}`);
|
|
175
|
+
console.error('In-scope files lacking decision-audit coverage:');
|
|
176
|
+
for (const f of res.uncoveredFiles ?? res.inScopeFiles ?? []) console.error(` - ${f}`);
|
|
131
177
|
process.exit(1);
|
|
132
178
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-08-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-08-21T16:44:51.648Z",
|
|
5
|
+
"instarVersion": "1.3.1185",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"generatedFrom": "source-tree",
|
|
4
4
|
"registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
|
|
5
|
-
"packageVersion": "1.3.
|
|
5
|
+
"packageVersion": "1.3.1185",
|
|
6
6
|
"guards": [
|
|
7
7
|
{
|
|
8
8
|
"ref": "docs/audits/phase-b/f10-triage.md",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sha256": "
|
|
2
|
+
"sha256": "3eb1bd07851f19c2b7daa4f5bcf5b38a58e611750b8edfbe5f4836b7ee919418",
|
|
3
3
|
"registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
|
|
4
|
-
"packageVersion": "1.3.
|
|
4
|
+
"packageVersion": "1.3.1185"
|
|
5
5
|
}
|
|
@@ -5,6 +5,34 @@
|
|
|
5
5
|
|
|
6
6
|
## What Changed
|
|
7
7
|
|
|
8
|
+
The PR-boundary decision-audit gate now verifies that the audit record a pull request carries
|
|
9
|
+
actually **covers** the in-scope files that pull request changed, instead of only verifying that
|
|
10
|
+
some audit record was present.
|
|
11
|
+
|
|
12
|
+
Previously `evaluateDecisionAuditPresence()` asked one question — did any decision-audit record
|
|
13
|
+
change in this PR? — so an unrelated or stale-scoped record satisfied the gate while covering none
|
|
14
|
+
of the changed files. The gate reported "covered" while covering nothing.
|
|
15
|
+
|
|
16
|
+
The evaluator now accepts caller-supplied decision records, unions their readable `scope.files`, and
|
|
17
|
+
requires every in-scope changed path to appear in that union. The pure function still performs no
|
|
18
|
+
filesystem I/O; the CLI reads the per-entry JSON records and passes the parsed content in. A record
|
|
19
|
+
whose `scope.files` is absent, malformed, or unreadable contributes no coverage — the check fails
|
|
20
|
+
closed. Directory strings do not cover their descendants.
|
|
21
|
+
|
|
22
|
+
Unchanged: the bot-author, release-cut and no-in-scope-changes exemptions, and the legacy
|
|
23
|
+
`.instar/instar-dev-decisions.jsonl` transition allowance, which still short-circuits to a pass.
|
|
24
|
+
|
|
25
|
+
The failure output now names the specific uncovered files and the remedy (re-run the local gate so
|
|
26
|
+
the record declares those paths) rather than listing every in-scope file.
|
|
27
|
+
|
|
28
|
+
This is a CI-only gate script. There is no runtime surface, no route, no message, and no
|
|
29
|
+
agent-visible or user-visible behaviour change.
|
|
30
|
+
|
|
31
|
+
**Verdict is review-grade, not proven.** The five-property signature runner that would let a guard
|
|
32
|
+
be called fixed does not exist yet — `scratchpad/phaseB/B0.1-THE-BAR.md` is marked
|
|
33
|
+
`DRAFT-FOR-LANES` and its B0.2 implementation was never built — so nothing here is described as
|
|
34
|
+
fixing, verifying or proving the guard effective.
|
|
35
|
+
|
|
8
36
|
A machine that receives an inbound Telegram message for a conversation it does **not** own no longer
|
|
9
37
|
claims that message in its own exactly-once ingress ledger. It still relays the message to the owning
|
|
10
38
|
machine exactly as before.
|
|
@@ -48,6 +76,18 @@ bookkeeping row is written.
|
|
|
48
76
|
|
|
49
77
|
## Evidence
|
|
50
78
|
|
|
79
|
+
- `npx vitest run tests/unit/decision-audit-presence-check.test.ts` — 14/14 passing, covering both
|
|
80
|
+
sides of the boundary: covering scope passes, stale scope fails, malformed scope fails closed,
|
|
81
|
+
directory strings do not cover descendants, multiple records union, and each existing exemption
|
|
82
|
+
still exempts.
|
|
83
|
+
- Side-effects review: `upgrades/side-effects/w22-decision-audit-scope-coverage.md`, including an
|
|
84
|
+
independent second-pass review that concurred and separately confirmed the diff introduces no new
|
|
85
|
+
schema, config, protocol surface or plumbing.
|
|
86
|
+
- Plain-English overview: `docs/specs/w22-decision-audit-scope-coverage.eli16.md`.
|
|
87
|
+
- The gap was identified by the Window-22 guard survey (`.instar/w22/branch-b-guard-ground-truth.md`),
|
|
88
|
+
which located this guard as one of four that prove less than they claim and rated this
|
|
89
|
+
identification the highest-confidence of the four.
|
|
90
|
+
|
|
51
91
|
- Mechanism established from the code: `src/core/SessionRouter.ts` documents that an inbound for a
|
|
52
92
|
topic owned by another live machine is forwarded to that machine over the mesh — so the non-owner
|
|
53
93
|
is the relay, which is why the claim (and not the delivery) is what needed to change.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
A new manually-started maintenance job was added to this repository's own
|
|
9
|
+
pipeline: it marks ONE already-published version of the package deprecated,
|
|
10
|
+
with a plain-English pointer to the version people should use instead.
|
|
11
|
+
|
|
12
|
+
It exists because a release race can leave an ORPHAN on the registry — a
|
|
13
|
+
version that was published, but whose release commit and tag never landed in
|
|
14
|
+
history. Cleaning one up requires a publish-capable credential, and that
|
|
15
|
+
credential lives only inside this repository's pipeline. This job is the one
|
|
16
|
+
place it can be used for that purpose, and its capability is exactly one write.
|
|
17
|
+
|
|
18
|
+
The job is started by hand only. It never checks out the repository, so no
|
|
19
|
+
repository code runs beside the credential. It can deprecate; it cannot publish
|
|
20
|
+
and it cannot unpublish. The version and the message are supplied when it is
|
|
21
|
+
started, so nothing about the target is baked into the file. A range or a tag
|
|
22
|
+
is refused before the credential is touched, because those would deprecate many
|
|
23
|
+
versions in a single call, and the current recommended version is refused so
|
|
24
|
+
that what everyone installs can never be hit by a typo. Afterwards the result
|
|
25
|
+
is read back from the registry — the command exiting cleanly is not accepted as
|
|
26
|
+
proof on its own.
|
|
27
|
+
|
|
28
|
+
## What to Tell Your User
|
|
29
|
+
|
|
30
|
+
Nothing. This is repository maintenance for the people who publish this
|
|
31
|
+
package; it changes no behaviour on any installed agent and adds no capability
|
|
32
|
+
an agent can reach.
|
|
33
|
+
|
|
34
|
+
## Summary of New Capabilities
|
|
35
|
+
|
|
36
|
+
None for agents or users. One maintenance job for maintainers of this
|
|
37
|
+
repository.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Side-Effects Review — decision-audit gate: scope coverage, not mere presence
|
|
2
|
+
|
|
3
|
+
**Change:** `scripts/decision-audit-presence-check.mjs` (+ its unit test)
|
|
4
|
+
**Tier:** 1 (declared). See "Tier declaration" at the bottom — the gate's advisory signal said 2.
|
|
5
|
+
**Branch:** `w22-b-audit-scope-coverage` · **Base:** `7d4076a53` (JKHeadley/main, v1.3.1182)
|
|
6
|
+
|
|
7
|
+
## Summary of the change
|
|
8
|
+
|
|
9
|
+
The decision-audit presence gate ran on every PR and asked one question: did *some* decision-audit
|
|
10
|
+
record change in this PR? It never asked whether that record's declared scope covered the in-scope
|
|
11
|
+
files the PR actually touched. Any unrelated or stale-scoped record satisfied it. The gate reported
|
|
12
|
+
"covered" while covering nothing — a known false positive, and the exact shape the Window-22 guard
|
|
13
|
+
survey was chartered to find.
|
|
14
|
+
|
|
15
|
+
The evaluator now accepts caller-supplied decision records, unions their readable `scope.files`, and
|
|
16
|
+
requires every in-scope changed path to be covered by that union. The pure function does no
|
|
17
|
+
filesystem I/O; the CLI reads the per-entry JSON records and passes parsed content in.
|
|
18
|
+
|
|
19
|
+
## Decision-point inventory
|
|
20
|
+
|
|
21
|
+
One decision point, already existing: pass/fail of the `decision-audit-gate` CI check on a PR. This
|
|
22
|
+
change does not create a decision point; it corrects the predicate of one that already blocks.
|
|
23
|
+
|
|
24
|
+
## 1. Over-block
|
|
25
|
+
|
|
26
|
+
**What it now rejects that it did not before:** a PR that carries a decision-audit record whose
|
|
27
|
+
`scope.files` does not list every in-scope changed path. In practice this is a PR where the gate ran
|
|
28
|
+
for *some* of the commits but not the one that touched the extra file, or where files were added
|
|
29
|
+
after the last gate run.
|
|
30
|
+
|
|
31
|
+
This is the intended new rejection, but it is a genuine behaviour change for authors: previously the
|
|
32
|
+
presence of any record was enough. The failure message names the uncovered paths and tells the
|
|
33
|
+
author to re-run the local gate so the record declares them. Cost of a false block is "re-run the
|
|
34
|
+
gate and re-commit" — recoverable, local, no data loss.
|
|
35
|
+
|
|
36
|
+
**Deliberate non-tightening:** directory strings do *not* count as covering their descendants. That
|
|
37
|
+
choice makes the gate stricter than a permissive reading. It is recorded here because it is the one
|
|
38
|
+
place a reasonable reviewer might want the opposite; the writer emits concrete staged file paths, so
|
|
39
|
+
descendant-coverage would only matter for hand-written records.
|
|
40
|
+
|
|
41
|
+
## 2. Under-block
|
|
42
|
+
|
|
43
|
+
Still missed, explicitly:
|
|
44
|
+
|
|
45
|
+
- A record whose `scope.files` lists the right paths but whose *content* is boilerplate. Coverage is
|
|
46
|
+
a structural claim, not a quality claim; nothing here reads the reasoning.
|
|
47
|
+
- A PR that changes an in-scope file, reverts it in a later commit, and carries a record scoped to
|
|
48
|
+
the intermediate state. The union is computed over declared paths, not over diff history.
|
|
49
|
+
- Files outside the in-scope predicate (`isInScopeFile`) are unaffected — this change does not widen
|
|
50
|
+
what counts as in-scope, and should not be read as doing so.
|
|
51
|
+
- Bot-authored and release-cut PRs remain exempt, unchanged.
|
|
52
|
+
|
|
53
|
+
## 3. Level-of-abstraction fit
|
|
54
|
+
|
|
55
|
+
Correct layer. The check runs at the PR boundary, which is where the bypass it detects becomes
|
|
56
|
+
visible; the local pre-commit gate is the layer that *produces* the record, and it already does its
|
|
57
|
+
job. Pushing coverage down into the pre-commit writer would not help — the failure mode is commits
|
|
58
|
+
that never reached the writer at all.
|
|
59
|
+
|
|
60
|
+
No smarter gate exists that this should feed instead. The verdict is a set-membership fact, not a
|
|
61
|
+
judgment, so there is no authority for it to inform.
|
|
62
|
+
|
|
63
|
+
## 4. Signal vs authority compliance
|
|
64
|
+
|
|
65
|
+
`docs/signal-vs-authority.md` applies to **judgment** decisions — blocking on what a message *means*
|
|
66
|
+
or what an agent's *intent* appears to be. This is not one. "Do the record's declared paths cover the
|
|
67
|
+
diff's paths?" is set membership over enumerable inputs, with no context required to separate the
|
|
68
|
+
legitimate case from the illegitimate one. It sits in the document's own excluded category:
|
|
69
|
+
structural validation at a boundary.
|
|
70
|
+
|
|
71
|
+
The change also moves the check in the *safe* direction relative to the principle: the check already
|
|
72
|
+
held blocking authority with a brittle predicate; this narrows a false-positive **pass**. It does not
|
|
73
|
+
add brittle authority, and it does not add a new blocker.
|
|
74
|
+
|
|
75
|
+
**Fails closed** on an absent, malformed, or unreadable `scope.files` — that record contributes no
|
|
76
|
+
coverage. Justified by the same document: a guard whose false-pass is cheap to exploit and whose
|
|
77
|
+
false-block costs a re-run should fail closed.
|
|
78
|
+
|
|
79
|
+
## 5. Interactions
|
|
80
|
+
|
|
81
|
+
- **Legacy JSONL transition path** (`.instar/instar-dev-decisions.jsonl`) is checked *before* the
|
|
82
|
+
coverage logic and still short-circuits to pass. In-flight PRs on the old format are unaffected.
|
|
83
|
+
- **The "no record at all" branch** is preserved verbatim, including its long remediation message.
|
|
84
|
+
Only the new "record present but scope does not cover" branch is added.
|
|
85
|
+
- **The local pre-commit gate** is the producer of these records; nothing in this change alters what
|
|
86
|
+
it writes.
|
|
87
|
+
- No double-fire: the gate runs once per PR in one workflow.
|
|
88
|
+
- **Self-referential note:** this PR changes an in-scope file, so it must itself carry a record whose
|
|
89
|
+
scope covers `scripts/decision-audit-presence-check.mjs`. The change is therefore exercised against
|
|
90
|
+
itself at merge time.
|
|
91
|
+
|
|
92
|
+
## 6. External surfaces
|
|
93
|
+
|
|
94
|
+
CI-visible only. The check's failure output changes (it now prints "In-scope files lacking
|
|
95
|
+
decision-audit coverage" and lists uncovered paths rather than all in-scope paths). No route, no
|
|
96
|
+
message, no agent-visible behaviour, no user-visible surface. No timing or runtime-state dependence.
|
|
97
|
+
|
|
98
|
+
## 6b. Operator-surface quality
|
|
99
|
+
|
|
100
|
+
The failure message names the specific uncovered files and the concrete remedy (re-run the local
|
|
101
|
+
gate so the record declares the path). It does not require the reader to know the internals of the
|
|
102
|
+
record format. No operator action is required by this change itself.
|
|
103
|
+
|
|
104
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
105
|
+
|
|
106
|
+
**Machine-local by design, and correctly so:** this runs in GitHub Actions against a PR, not on any
|
|
107
|
+
agent machine. There is no per-machine state, no replication path, and nothing to strand on a topic
|
|
108
|
+
transfer. The decision records it reads are repository content, identical on every checkout.
|
|
109
|
+
|
|
110
|
+
## 8. Rollback cost
|
|
111
|
+
|
|
112
|
+
Single-file revert of `scripts/decision-audit-presence-check.mjs` (plus its test). No migration, no
|
|
113
|
+
persisted state, no agent repair. A PR blocked by the new predicate is unblocked by reverting or by
|
|
114
|
+
adding a covering record. Worst realistic case: a burst of PRs fail the gate until authors re-run
|
|
115
|
+
the local gate — visible immediately, recoverable in minutes.
|
|
116
|
+
|
|
117
|
+
## Conclusion
|
|
118
|
+
|
|
119
|
+
Bounded correction of an existing gate's predicate, in the direction of fewer false passes, with a
|
|
120
|
+
recoverable false-block cost. No new decision point, no new authority, no runtime surface.
|
|
121
|
+
|
|
122
|
+
**Verdict is REVIEW-GRADE, not proven.** The five-property signature test that would let a guard be
|
|
123
|
+
called *fixed* does not exist — `scratchpad/phaseB/B0.1-THE-BAR.md` is marked `DRAFT-FOR-LANES` and
|
|
124
|
+
its B0.2 runner was never built. Nothing in this change may be described as fixing, verifying, or
|
|
125
|
+
proving the guard is effective.
|
|
126
|
+
|
|
127
|
+
## Tier declaration (recorded because the signal disagreed)
|
|
128
|
+
|
|
129
|
+
The gate's advisory printed `suggestedTier=2 (size=2, riskFloor=1, 86 LOC across 1 file(s))` — the
|
|
130
|
+
**risk** floor is 1; **size** raised the suggestion to 2, and the size is mostly added tests.
|
|
131
|
+
Observer-1 ruling ten (2026-08-21, topic 29723) declared this change tier 1, reasoning on the record:
|
|
132
|
+
the higher tier exists to force design convergence before code, and that convergence exists for this
|
|
133
|
+
change in the Window-22 causal map, the charter, and rulings eight through ten — mechanism proven,
|
|
134
|
+
predicted after-state stated, falsification condition stated. The ruling is void if the diff carries
|
|
135
|
+
new plumbing, a schema, config, or a protocol surface; it does not (one script, one test file).
|
|
136
|
+
|
|
137
|
+
This paragraph exists so the disagreement between the signal and the declaration is auditable rather
|
|
138
|
+
than suppressed.
|
|
139
|
+
|
|
140
|
+
## Evidence pointers
|
|
141
|
+
|
|
142
|
+
- 14/14 targeted tests pass: `npx vitest run tests/unit/decision-audit-presence-check.test.ts`
|
|
143
|
+
- Guard survey that identified the gap: `.instar/w22/branch-b-guard-ground-truth.md`
|
|
144
|
+
- Findings ledger entry 36: view `6e25dfa2-f374-409b-9e85-57aa210adfe0`
|
|
145
|
+
|
|
146
|
+
## Second-pass review
|
|
147
|
+
|
|
148
|
+
Concur with the review. I checked the artifact, docs/signal-vs-authority.md, and the actual git diff for the worktree: the diff is limited to scripts/decision-audit-presence-check.mjs and its unit test, and it changes the existing PR gate from per-entry record presence to exact in-scope-path coverage by the union of parsed scope.files. The over-block section is honest: absent, malformed, unreadable, different-file, and directory-only scopes now contribute no coverage and can reject the PR, while legacy JSONL still passes before coverage logic. The signal-vs-authority argument is sound because this is structural boundary validation over enumerable changed paths and declared record paths, not a judgment about message meaning or intent. I found no unmentioned behavioral surface in the diff and no new schema, config, protocol surface, or tier-voiding plumbing beyond local parsing/passing of the already-existing decision-record content.
|
|
149
|
+
|
|
150
|
+
Independent reviewer, 2026-08-21T01:10:23-07:00
|