instar 1.3.977 → 1.3.979
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/dashboard/glance.js +16 -0
- package/dist/core/GuardPostureStore.d.ts.map +1 -1
- package/dist/core/GuardPostureStore.js +7 -0
- package/dist/core/GuardPostureStore.js.map +1 -1
- package/dist/core/StageTransitionValidator.d.ts +9 -0
- package/dist/core/StageTransitionValidator.d.ts.map +1 -1
- package/dist/core/StageTransitionValidator.js +55 -2
- package/dist/core/StageTransitionValidator.js.map +1 -1
- package/dist/core/StandardsEnforcementAuditor.d.ts +64 -3
- package/dist/core/StandardsEnforcementAuditor.d.ts.map +1 -1
- package/dist/core/StandardsEnforcementAuditor.js +58 -1
- package/dist/core/StandardsEnforcementAuditor.js.map +1 -1
- package/dist/core/types.d.ts +7 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/guardPostureView.d.ts +5 -0
- package/dist/monitoring/guardPostureView.d.ts.map +1 -1
- package/dist/monitoring/guardPostureView.js +11 -2
- package/dist/monitoring/guardPostureView.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +5 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.3.978.md +88 -0
- package/upgrades/1.3.979.md +26 -0
- package/upgrades/eli16/guard-heartbeat-unproven-summary.md +44 -0
- package/upgrades/side-effects/assessment-confidence-unverified.md +125 -0
- package/upgrades/side-effects/ci-green-latest-run.md +119 -0
- package/upgrades/side-effects/guard-heartbeat-unproven-summary.md +137 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Side-Effects Review — assessmentConfidence: my own honesty field was overclaiming
|
|
2
|
+
|
|
3
|
+
## Summary of the change
|
|
4
|
+
|
|
5
|
+
`CoverageSummary.assessmentTrustworthy` (shipped hours earlier in PR #1641) reported **`true` over
|
|
6
|
+
the exact defect it was added to expose.** Verified on the LIVE endpoint after v1.3.975 deployed:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
total: 22 | enforcedRatio: 0.0455 | assessmentTrustworthy: TRUE | canaryOk: true | canaryFailures: 0
|
|
10
|
+
registry.parsed: 22 | articleHeadings: 22 | bytes: 46606 | families: [5 of the 6]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The source registry carries **81** articles. The stale agent-home copy is not malformed — it is a
|
|
14
|
+
self-consistent document that is a quarter of the real one, so it passes every INTERNAL check by
|
|
15
|
+
construction (all headings parse, none dropped, anchors present, count above the floor).
|
|
16
|
+
|
|
17
|
+
`assessmentTrustworthy` was computed as `total > 0 && registry.canaryOk` — i.e. "my internal checks
|
|
18
|
+
passed". It READ as "this assessment is trustworthy". Conflating those is the failure the whole
|
|
19
|
+
instrument was being fixed for.
|
|
20
|
+
|
|
21
|
+
Replaced by a tri-state `assessmentConfidence` (`'verified' | 'unverified' | 'untrustworthy'`) plus
|
|
22
|
+
an always-populated `confidenceReason`. The boolean is retained, deprecated, TRUE only on
|
|
23
|
+
`'verified'`.
|
|
24
|
+
|
|
25
|
+
**Root insight:** nothing INSIDE a 22-rule document says it should have 81. Trustworthiness is not
|
|
26
|
+
obtainable by looking harder at the file — it needs an EXTERNAL expectation. That mechanism is the
|
|
27
|
+
blocked `standards-registry-snapshot-refresh` spec, so `'verified'` is unreachable today, and the
|
|
28
|
+
honest verdict for every passing read is `'unverified'`.
|
|
29
|
+
|
|
30
|
+
Proven against the real live registry (all four states):
|
|
31
|
+
|
|
32
|
+
| input | before | after |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| the live 22-of-81 copy | `trustworthy: true` | `unverified` + reason |
|
|
35
|
+
| the real 81-article registry | `true` | `unverified` — still correct; nothing to check against |
|
|
36
|
+
| a MATCHING expectation | unreachable | `verified` |
|
|
37
|
+
| a MISMATCHED expectation | unreachable | `untrustworthy` |
|
|
38
|
+
|
|
39
|
+
## Decision-point inventory
|
|
40
|
+
|
|
41
|
+
- `computeCoverage` / `GET /conformance/coverage{,/health}` — OBSERVE-ONLY. Gates nothing, blocks
|
|
42
|
+
nothing, no code path branches on the verdict. This changes what is REPORTED, never what anything
|
|
43
|
+
DOES.
|
|
44
|
+
- `deriveAssessmentConfidence` — new, exported, pure. Extracted deliberately: inline, TypeScript's
|
|
45
|
+
flow analysis proved `'verified'` unreachable and REJECTED the comparison deriving the deprecated
|
|
46
|
+
boolean. That rejection is TRUE and is the point; a function keeps the union open for when an
|
|
47
|
+
expectation ships without pretending it is reachable now. (TS caught this, which is worth noting —
|
|
48
|
+
the compiler objected to a claim I could not yet honour.)
|
|
49
|
+
- No gate, hook, sentinel, reaper, scheduler, migration or config surface touched.
|
|
50
|
+
|
|
51
|
+
## 1. Over-block
|
|
52
|
+
|
|
53
|
+
Nothing blocks. The adjacent risk is over-alarming: `assessmentTrustworthy` is now `false`
|
|
54
|
+
EVERYWHERE, including for a complete and correct registry. That is intentional and argued in the
|
|
55
|
+
ELI16: the instrument genuinely cannot verify completeness today, so anything stronger is the same
|
|
56
|
+
overclaim in nicer clothes.
|
|
57
|
+
|
|
58
|
+
The tri-state exists precisely so this is not alarming: `'unverified'` says *I do not know*, which is
|
|
59
|
+
neither reassuring nor a red flag. A consumer reading only the deprecated boolean sees `false` and
|
|
60
|
+
may over-read it — which is why the boolean is deprecated in its doc comment and the reason string is
|
|
61
|
+
always populated.
|
|
62
|
+
|
|
63
|
+
## 2. Under-block
|
|
64
|
+
|
|
65
|
+
No detection is weakened: `'untrustworthy'` still fires on every case the boolean fired on (nothing
|
|
66
|
+
parsed, canary objected), asserted in the updated tests. The change only ADDS a state between "fine"
|
|
67
|
+
and "broken" where none existed.
|
|
68
|
+
|
|
69
|
+
The residual gap, stated plainly: **a coherent stale registry is still reported with real-looking
|
|
70
|
+
figures** (`total: 22, enforcedRatio: 0.0455`). It is now labelled `unverified` with a reason naming
|
|
71
|
+
the possibility of "a coherent older copy", but the instrument cannot say *which* is the case. Only
|
|
72
|
+
the blocked spec closes that. This change makes the uncertainty visible; it does not resolve it, and
|
|
73
|
+
it must not be described as resolving it.
|
|
74
|
+
|
|
75
|
+
## 3. Blast radius
|
|
76
|
+
|
|
77
|
+
One field's semantics in `src/core/StandardsEnforcementAuditor.ts`; the route spreads the summary so
|
|
78
|
+
the new fields flow automatically. Consumers checked with a full-text scan (`grep` silently returned
|
|
79
|
+
nothing for a pattern present twice in this very file — python was used instead, and that tooling
|
|
80
|
+
anomaly is recorded):
|
|
81
|
+
|
|
82
|
+
- `assessmentTrustworthy` appears in exactly three test files (all updated) and nowhere in `src/`
|
|
83
|
+
outside its declaration and derivation. No runtime branch reads it.
|
|
84
|
+
- The response is additive apart from the boolean's meaning narrowing; no field renamed or removed.
|
|
85
|
+
|
|
86
|
+
## 4. Rollback plan
|
|
87
|
+
|
|
88
|
+
Single-commit revert; no state, config, migration or persisted artifact. The audit recomputes per
|
|
89
|
+
request.
|
|
90
|
+
|
|
91
|
+
## 5. Test coverage (all three tiers)
|
|
92
|
+
|
|
93
|
+
- **Unit** (`standards-enforcement-auditor.test.ts`, 16 pass): a coherent-but-stale registry (a real
|
|
94
|
+
whole-family subset of the live constitution) has zero dropped headings and `parsed ===
|
|
95
|
+
articleHeadings` — i.e. passes every internal check — and STILL must not read `verified`; plus
|
|
96
|
+
`deriveAssessmentConfidence`'s four verdicts in both directions, including that a mismatched
|
|
97
|
+
expectation is `untrustworthy` rather than merely `unverified`.
|
|
98
|
+
- **Integration** (`conformance-dev-gate-route.test.ts`): the HTTP body carries
|
|
99
|
+
`assessmentConfidence: 'untrustworthy'` for an empty registry.
|
|
100
|
+
- **E2E** (`standards-coverage-lifecycle.test.ts`): the verdict and its reason survive the production
|
|
101
|
+
initialization path.
|
|
102
|
+
|
|
103
|
+
## 6. A fourth test found encoding the defect
|
|
104
|
+
|
|
105
|
+
`standards-enforcement-auditor.test.ts` asserted `assessmentTrustworthy === true` for the full
|
|
106
|
+
registry — pinning the overclaim as a specification. That is the FOURTH test found this evening
|
|
107
|
+
asserting the behaviour it should have been challenging (the others: the standards parser's
|
|
108
|
+
five-family allowlist, a fixture whose meaning changed with the wall clock, and the
|
|
109
|
+
learning-velocity route's filed-items-count-as-learning fixtures).
|
|
110
|
+
|
|
111
|
+
Four in one day is not a coincidence about those four tests. It is a property of how tests get
|
|
112
|
+
written here: they pin what the code currently does. A green suite therefore attests to
|
|
113
|
+
self-consistency, not correctness — which is the same shape as everything else this project has
|
|
114
|
+
found, applied to our own verification. Recorded as a class; not fixed here, because the fix is a
|
|
115
|
+
standard about how assertions are chosen, not a code change.
|
|
116
|
+
|
|
117
|
+
## 7. How this was found, which is the transferable part
|
|
118
|
+
|
|
119
|
+
Not by a test. By deploying and reading the live surface. The suite was fully green — it tested the
|
|
120
|
+
cases I had thought of, which were exactly the cases my fix handled. The stale-but-coherent case was
|
|
121
|
+
outside my imagination, and no amount of internal testing would have surfaced it.
|
|
122
|
+
|
|
123
|
+
Two hours from shipping to discovery. The generalisable rule: **after shipping an honesty fix, read
|
|
124
|
+
the live surface for the case you were fixing.** If the number you set out to correct still looks
|
|
125
|
+
fine, you have not corrected it.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Side-Effects Review — CI greenness must use the LATEST run per check
|
|
2
|
+
|
|
3
|
+
## Summary of the change
|
|
4
|
+
|
|
5
|
+
`StageTransitionValidator.ciIsGreen` iterated EVERY entry in GitHub's `statusCheckRollup` and refused
|
|
6
|
+
on any non-success conclusion. GitHub returns every run of every check, **including superseded ones**,
|
|
7
|
+
so a check that failed and was then re-run to green appears twice and the failed entry never
|
|
8
|
+
disappears.
|
|
9
|
+
|
|
10
|
+
Verified on the live PR #1641 (merged legitimately at 02:07:44Z):
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
rollup entries: 31 | names appearing more than once: eli16 (2), UX impact declaration (2),
|
|
14
|
+
UX assertions (messaging E2E) (2), UX merge safety (2)
|
|
15
|
+
eli16 COMPLETED FAILURE completedAt 2026-07-26T01:44:01Z
|
|
16
|
+
eli16 COMPLETED SUCCESS completedAt 2026-07-26T01:45:32Z
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Branch protection merged the PR because it evaluates each check's CURRENT state. The tracker refused
|
|
20
|
+
it with `CI_NOT_GREEN` because it evaluated the stale run too — so **no correct merge whose CI had
|
|
21
|
+
ever been red could ever be recorded.**
|
|
22
|
+
|
|
23
|
+
Fix: `latestRunPerCheck` collapses the rollup to the newest run per check name (by `completedAt`,
|
|
24
|
+
falling back to `startedAt`) before `ciIsGreen` evaluates it. `GhPrView.statusCheckRollup`'s type
|
|
25
|
+
gains the `name`/`context`/`startedAt`/`completedAt` fields the dedupe needs; the route's existing
|
|
26
|
+
`gh pr view --json …statusCheckRollup` already returns them, so no query change.
|
|
27
|
+
|
|
28
|
+
## Decision-point inventory
|
|
29
|
+
|
|
30
|
+
- `ciIsGreen` feeds the `building → merged` GATE. This change makes the gate ACCEPT a case it
|
|
31
|
+
previously refused, so it is the direction that needs the most scrutiny — see §1/§2.
|
|
32
|
+
- No other decision point, hook, sentinel, reaper, scheduler, migration or config surface is touched.
|
|
33
|
+
- `latestRunPerCheck` is a pure function over the rollup array.
|
|
34
|
+
|
|
35
|
+
## 1. Over-block
|
|
36
|
+
|
|
37
|
+
Strictly reduced, and that is the intent: a merged PR with a superseded failure is now recordable.
|
|
38
|
+
Nothing that used to pass now fails.
|
|
39
|
+
|
|
40
|
+
The one preserved refusal worth naming: a **latest** run that is still in flight (`status` not
|
|
41
|
+
`COMPLETED`) is still not green, asserted in its own test, so the dedupe cannot be used to skip a
|
|
42
|
+
pending check by pairing it with an older success.
|
|
43
|
+
|
|
44
|
+
## 2. Under-block — the part that matters
|
|
45
|
+
|
|
46
|
+
A dedupe is a potential laundering mechanism, so three properties are enforced and each is tested:
|
|
47
|
+
|
|
48
|
+
1. **Latest is authoritative in BOTH directions.** SUCCESS followed by a later FAILURE is NOT green.
|
|
49
|
+
Without this, "keep the latest" would let anyone bury a red by arranging run order.
|
|
50
|
+
2. **Unorderable runs FAIL CLOSED.** Equal or missing timestamps ⇒ the FAILING run wins. An undatable
|
|
51
|
+
success must never mask a red. (Three variants tested: no timestamps either way round, and equal
|
|
52
|
+
timestamps.)
|
|
53
|
+
3. **Unnamed entries are keyed individually.** Bare status contexts with neither `name` nor `context`
|
|
54
|
+
are never collapsed into each other, so a failing one cannot be replaced by a passing one.
|
|
55
|
+
|
|
56
|
+
Residual, stated: the dedupe trusts GitHub's timestamps. A platform that reported them wrongly could
|
|
57
|
+
mis-order runs — but the fail-closed rule bounds the damage to the unorderable case, and the
|
|
58
|
+
alternative (ignoring the rollup entirely, or trusting merge status alone) is strictly weaker.
|
|
59
|
+
|
|
60
|
+
## 3. Blast radius
|
|
61
|
+
|
|
62
|
+
One function plus one type widening in `src/core/StageTransitionValidator.ts`. `ciIsGreen` is private
|
|
63
|
+
to that module (`grep` for callers: only the `to === 'merged'` branch). The route passes the rollup
|
|
64
|
+
through unchanged. No response shape changes; no new code.
|
|
65
|
+
|
|
66
|
+
## 4. Rollback plan
|
|
67
|
+
|
|
68
|
+
Single-commit revert; no state, config, migration or artifact. Reverting restores the previous
|
|
69
|
+
(over-refusing) behaviour immediately.
|
|
70
|
+
|
|
71
|
+
## 5. Test coverage
|
|
72
|
+
|
|
73
|
+
`tests/unit/StageTransitionValidator.test.ts` (46 pass), five new cases: the live #1641 shape
|
|
74
|
+
(superseded FAILURE + later SUCCESS ⇒ green); the reverse (SUCCESS + later FAILURE ⇒ not green);
|
|
75
|
+
unorderable-runs-fail-closed across three variants; unnamed entries not collapsed; and an in-flight
|
|
76
|
+
latest run still not green.
|
|
77
|
+
|
|
78
|
+
No integration/e2e tier added: this is a pure predicate inside an existing gate whose route path is
|
|
79
|
+
already covered, and the decisive evidence is the end-to-end check below.
|
|
80
|
+
|
|
81
|
+
## 6. End-to-end evidence, gathered BEFORE the fix and after
|
|
82
|
+
|
|
83
|
+
The validator's real logic was run against the two real merged PRs with the route's exact helper
|
|
84
|
+
wiring:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
before: item 1 (PR 1641) -> CI_NOT_GREEN
|
|
88
|
+
item 2 (PR 1644) -> MERGE_BASE_UNVERIFIABLE (stale local ref; resolved by git fetch)
|
|
89
|
+
after: item 1 (PR 1641) -> WOULD SUCCEED
|
|
90
|
+
item 2 (PR 1644) -> WOULD SUCCEED
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Note the second line: the `MERGE_BASE_UNVERIFIABLE` code shipped hours earlier in #1643 did its job
|
|
94
|
+
here on a real case — an unknown local SHA reported as *unverifiable* rather than as a false "not an
|
|
95
|
+
ancestor".
|
|
96
|
+
|
|
97
|
+
## 7. Third instance in one code path, in one evening
|
|
98
|
+
|
|
99
|
+
The completion-recording path has now yielded three independent defects tonight, each the same
|
|
100
|
+
underlying error:
|
|
101
|
+
|
|
102
|
+
| | defect | what it reported |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| #1640 | the `gh` binary was unreachable | a flat refusal |
|
|
105
|
+
| #1643 | a SourceTreeGuard refusal was caught and returned as `false` | "the merge is not on main" |
|
|
106
|
+
| this | a superseded check run read as the check's current state | "CI is not green" |
|
|
107
|
+
|
|
108
|
+
All three presented identically from outside — an opaque refusal — and each masked the next. That is
|
|
109
|
+
worth recording as a property of the path rather than three coincidences: **a code path whose failures
|
|
110
|
+
all render as one indistinguishable refusal will hide its own defects in series.** The general fix is
|
|
111
|
+
the one #1643 started: distinguish "could not determine" from "determined, and the answer is no".
|
|
112
|
+
|
|
113
|
+
## 8. How it was found
|
|
114
|
+
|
|
115
|
+
By refusing to assert. I was about to report that recording would work once the server updated, and
|
|
116
|
+
instead ran the validator's own logic against the real PRs. It returned two different refusals. That
|
|
117
|
+
cost two minutes and avoided discovering both blockers serially, each appearing to be a fresh
|
|
118
|
+
mystery. Habit worth keeping: **before claiming something will work once a dependency lands, execute
|
|
119
|
+
its logic now and read what it returns.**
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Side-Effects Review — Fleet guard-posture completeness
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `guard-heartbeat-unproven-summary`
|
|
4
|
+
**Date:** `2026-07-25`
|
|
5
|
+
**Author:** `Instar Agent (instar-codey)`
|
|
6
|
+
**Second-pass reviewer:** `heartbeat_guard_review`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`src/monitoring/guardPostureView.ts` projects the existing `loadBearingUninspectableKeys` summary into the capacity heartbeat as a full `loadBearingUninspectable` count plus at most 16 sorted keys. `src/core/types.ts` adds the optional wire members, and `src/core/GuardPostureStore.ts` includes them in write-on-change semantic equality. `dashboard/glance.js` displays the received count and keys in the existing per-machine Layer-3 record. No guard row, gap membership, anomaly, attention item, notification, machine-health calculation, or placement decision changes.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
No block/allow or actuation decision point is added or modified.
|
|
15
|
+
|
|
16
|
+
- `buildHeartbeatPostureBlock` — **modify** — adds a bounded read-only projection of an existing summary list.
|
|
17
|
+
- `GuardPostureStore.postureSemanticsEqual` — **modify** — recognizes changes to the new wire fields for persistence; it does not decide posture or liveness.
|
|
18
|
+
- `machineRecordNode` — **modify** — displays received data at Layer 3 only.
|
|
19
|
+
- `GuardPostureProbe` — **pass-through** — deliberately unchanged; it continues to alarm only the pre-existing posture and gap classes.
|
|
20
|
+
- `guardPostureProblems` / `machineNeedsAttention` — **pass-through** — deliberately unchanged; the new field cannot alter fleet health or attention classification.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Over-block
|
|
25
|
+
|
|
26
|
+
No block/allow surface — over-block not applicable. The producer accepts every existing inventory, and older heartbeat payloads without the optional fields remain valid.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. Under-block
|
|
31
|
+
|
|
32
|
+
The 16-key sample can omit individual keys if the load-bearing manifest grows beyond 16, but the full count makes that truncation explicit and the dashboard says how many keys are shown. Today’s manifest contains 13 load-bearing guards, so every current key fits.
|
|
33
|
+
|
|
34
|
+
One adjacent persistence limitation was observed and left unchanged: `GuardPostureStore.postureSemanticsEqual` does not compare the three older `loadBearingGapKeys`, `loadBearingSoakingKeys`, or `loadBearingAcceptedKeys` lists. Live in-memory posture still updates before the equality check, but a keys-only change to one of those older lists may not be written for restart recovery when every compared count stays equal. This PR compares only the two new fields required for its own durable wiring. The adjacent finding is reported to Echo and is neither fixed nor filed here.
|
|
35
|
+
|
|
36
|
+
By instruction, the field name retains the earlier `Uninspectable` wording even though `off-runtime-divergent` is more precisely “inspected but unproven.” The code comment and operator label use “cannot prove protection” / “unproven,” preserving wire compatibility without a behavior-free rename.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 3. Level-of-abstraction fit
|
|
41
|
+
|
|
42
|
+
The projection belongs in `buildHeartbeatPostureBlock`, which already translates the complete local inventory into the compact wire shape. It reuses the canonical list instead of re-deriving posture in the heartbeat or dashboard. The 16-key clamp is a transport bound, not a posture heuristic. Display belongs in the existing machine record because `/pool` is the fleet heartbeat consumer and that record already renders the machine’s guard counts.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 4. Signal vs authority compliance
|
|
47
|
+
|
|
48
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
49
|
+
|
|
50
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
51
|
+
|
|
52
|
+
- [ ] No — this change produces a signal consumed by an existing smart gate.
|
|
53
|
+
- [x] No — this change has no block/allow surface.
|
|
54
|
+
- [ ] Yes — but the logic is a smart gate with full conversational context (LLM-backed with recent history or equivalent).
|
|
55
|
+
- [ ] ⚠️ Yes, with brittle logic — STOP. Reshape the design.
|
|
56
|
+
|
|
57
|
+
The new heartbeat members can only describe already-derived state. Neither the store nor the dashboard can enable, disable, suppress, alarm, notify, place, or authorize anything from them. Existing authorities receive byte-for-byte equivalent inputs because the probe does not read the new members.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 4b. Judgment-point check (Judgment Within Floors standard)
|
|
62
|
+
|
|
63
|
+
No new static heuristic is added at a competing-signals decision point. Membership comes from the existing closed list. The number 16 is an explicit transport ceiling over a deterministic sample, paired with an uncapped count; it does not decide whether protection is healthy.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 5. Interactions
|
|
68
|
+
|
|
69
|
+
- **Shadowing:** none. Posture classification and summary assembly complete before heartbeat projection. The dashboard reads the result after pool ingestion.
|
|
70
|
+
- **Double-fire:** structurally prevented. No new call to `emitAttention`, `createAttentionItem`, notification code, or probe evaluator exists. The peer refusal test presents `errored: 1` together with the new key and obtains exactly one existing acute episode and no load-bearing episode.
|
|
71
|
+
- **Races:** no new concurrent state is introduced. `MachinePoolRegistry` already replaces its in-memory posture on each beat; the store’s existing synchronous write-on-change path receives two more compared fields.
|
|
72
|
+
- **Feedback loops:** none. `/pool` and the Machines glance are read surfaces. The displayed value is never fed into guard evaluation, machine health, routing, placement, or messaging.
|
|
73
|
+
- **Compatibility:** the wire fields are optional for old persisted records and old peers. The renderer falls back to the key-array length when a transitional payload has keys but no count and refuses to display a count smaller than the keys actually present.
|
|
74
|
+
- **Payload:** the new list is sorted and sliced to 16. The full count is one number; there is no unbounded free text.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 6. External surfaces
|
|
79
|
+
|
|
80
|
+
Authenticated `/pool` callers receive two additive optional members inside each machine’s existing `guardPosture` block. The Machines dashboard displays them in the existing machine detail record. No response status, authentication rule, peer URL, external-service call, Telegram/Slack message, attention item, or notification changes.
|
|
81
|
+
|
|
82
|
+
The persisted `guard-posture-peers.json` may contain the two additive fields after an upgraded heartbeat. Old records deserialize because the fields are optional; rollback ignores the extra JSON members.
|
|
83
|
+
|
|
84
|
+
No operator-facing action is added. The surface is diagnostic only: there is no grant, approval, destructive operation, or laptop-only workflow.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
89
|
+
|
|
90
|
+
1. **Leads with the primary action?** There is no action. The new answer appears directly in the already-open machine record beside its other safety-check facts; it is not hidden behind another toggle.
|
|
91
|
+
2. **Zero raw internals as primary content?** The plain label is “Load-bearing protection unproven.” Guard keys are supporting identifiers after the human-readable count, not headline or tile content.
|
|
92
|
+
3. **Destructive actions de-emphasized?** No destructive or constructive control is added.
|
|
93
|
+
4. **Plain language + phone width?** The label is plain language and the existing record value style uses `overflow-wrap: anywhere`, so long guard keys wrap instead of forcing horizontal scroll. The change adds no table or fixed-width element. Unit, integration, and end-to-end DOM tests open the real Layer-3 record and require the count/key text; the existing phone-width record layout and tap targets are unchanged.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
98
|
+
|
|
99
|
+
**Posture: replicated via heartbeat.** Each machine derives its own machine-local guard truth and includes the bounded projection in its normal capacity heartbeat. `MachinePoolRegistry` receives it, `GuardPostureStore` retains the last known value for a dark peer, `GET /pool` returns it, and the Machines glance displays it with that machine’s identity.
|
|
100
|
+
|
|
101
|
+
It emits no user-facing notice, so one-voice gating is not needed. It adds no new store or topic-bound state; it extends the existing per-machine posture record. It generates no URLs. The peer probe deliberately ignores the field, preventing a per-machine multiplication of notices.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 8. Rollback cost
|
|
106
|
+
|
|
107
|
+
Revert the two wire members, producer projection, semantic comparison, dashboard row, and tests, then ship a patch. Existing persisted JSON may retain unknown additive members, which older code ignores. There is no schema migration, database cleanup, agent reset, user notification, or classification repair.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Conclusion
|
|
112
|
+
|
|
113
|
+
The change completes the second read surface without creating a second behavioral system. The heartbeat is explicitly bounded, the count preserves truth under truncation, the fleet reader displays the field, and all existing authority paths remain unchanged. Refusal-first tests pin the errored guard’s exclusion from `loadBearingGapKeys`, inclusion in the new list, exactly one existing anomaly with no notification track, the 16-key ceiling, persistence, real `/pool` exposure, and end-to-end display.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Second-pass review (required: guard-related change)
|
|
118
|
+
|
|
119
|
+
**Reviewer:** `heartbeat_guard_review`
|
|
120
|
+
|
|
121
|
+
**Independent read of the artifact:** Concur with the review: the errored load-bearing guard remains outside `loadBearingGapKeys`; the heartbeat adds only an honest full count plus a deterministic 16-key sorted sample; probe, notification, and machine-health decision paths do not consume the new fields; persistence and the real `/pool` → Machines record path are wired; and the focused unit/integration/e2e suite passes 98/98 with direct refusal assertions for field omission, count dishonesty, cap removal, persistence loss, and display loss. I found no inaccurate artifact claim or adjacent scope drift requiring rework.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Evidence pointers
|
|
126
|
+
|
|
127
|
+
- Before implementation, the focused unit suite failed three assertions: the heartbeat count/list were absent and the machine record omitted the load-bearing-unproven row.
|
|
128
|
+
- Unit: 67/67 across producer, probe refusal, and dashboard drill-down suites.
|
|
129
|
+
- Integration: 26/26 across heartbeat persistence, real `/pool`, and Machines glance wiring.
|
|
130
|
+
- E2E: 5/5 in the shipped jargon-belt Machines lifecycle.
|
|
131
|
+
- Static/build: `npm run lint` and `npm run build` pass without threshold changes.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Class-Closure Declaration (display-only mirror)
|
|
136
|
+
|
|
137
|
+
No agent-authored-artifact defect and no self-triggered controller change — not applicable.
|