instar 1.3.705 → 1.3.706

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.
Files changed (37) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +16 -0
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  5. package/dist/core/PostUpdateMigrator.js +20 -0
  6. package/dist/core/PostUpdateMigrator.js.map +1 -1
  7. package/dist/core/types.d.ts +6 -0
  8. package/dist/core/types.d.ts.map +1 -1
  9. package/dist/core/types.js.map +1 -1
  10. package/dist/monitoring/guardAcceptedFallbacks.d.ts +48 -0
  11. package/dist/monitoring/guardAcceptedFallbacks.d.ts.map +1 -0
  12. package/dist/monitoring/guardAcceptedFallbacks.js +106 -0
  13. package/dist/monitoring/guardAcceptedFallbacks.js.map +1 -0
  14. package/dist/monitoring/guardManifest.d.ts +25 -0
  15. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  16. package/dist/monitoring/guardManifest.js +48 -0
  17. package/dist/monitoring/guardManifest.js.map +1 -1
  18. package/dist/monitoring/guardPostureView.d.ts +23 -0
  19. package/dist/monitoring/guardPostureView.d.ts.map +1 -1
  20. package/dist/monitoring/guardPostureView.js +66 -0
  21. package/dist/monitoring/guardPostureView.js.map +1 -1
  22. package/dist/monitoring/probes/GuardPostureProbe.d.ts +13 -2
  23. package/dist/monitoring/probes/GuardPostureProbe.d.ts.map +1 -1
  24. package/dist/monitoring/probes/GuardPostureProbe.js +143 -40
  25. package/dist/monitoring/probes/GuardPostureProbe.js.map +1 -1
  26. package/dist/scaffold/templates.d.ts.map +1 -1
  27. package/dist/scaffold/templates.js +1 -0
  28. package/dist/scaffold/templates.js.map +1 -1
  29. package/dist/server/routes.d.ts.map +1 -1
  30. package/dist/server/routes.js +46 -1
  31. package/dist/server/routes.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/data/builtin-manifest.json +63 -63
  34. package/src/data/state-coherence-registry.json +18 -0
  35. package/src/scaffold/templates.ts +1 -0
  36. package/upgrades/1.3.706.md +80 -0
  37. package/upgrades/side-effects/g3-dark-but-load-bearing-guards.md +143 -0
@@ -0,0 +1,80 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ The guard-posture inventory (`GET /guards`) can now express that a DARK feature
9
+ is load-bearing — that a critical path depends on it — instead of treating it as
10
+ an ordinary optional dark feature that sits quiet while the path it should guard
11
+ runs unguarded. This is the guard-layer arm of the ratified "A Dark Feature Guards
12
+ Nothing" standard, and it closes the gap the 2026-07-01 silent-loss postmortem
13
+ named (operator message delivery depended on dark guards, and nothing surfaced it).
14
+
15
+ A guard declared `loadBearing` in the manifest now carries a `criticalPath` label
16
+ on every row, and when it sits silently unguarded it is classified one of three
17
+ ways:
18
+ - `loadBearingGap` — LOUD. A critical path is unguarded. It alerts on its OWN
19
+ attention episode track (`guard-posture-loadbearing:ep-N`), so a long-lived gap
20
+ can never mask an acute load-shed on another guard.
21
+ - `loadBearingSoaking` — a dry-run guard graduating within a bounded, code-constant
22
+ soak window. Surfaced on `/guards` only, no alert; it LAPSES to a loud gap if it
23
+ stalls past the window.
24
+ - `loadBearingAccepted` — an owned, PIN-authenticated operator acceptance is on
25
+ record. Full suppression + a visible accepted-risk row.
26
+
27
+ New route (dashboard-PIN-gated, `owner` + `reason` both required):
28
+ `POST`/`DELETE /guards/:key/accept-fallback`. The initial curated load-bearing set
29
+ is small and conservative: the durable inbound-message queue and the stranded-topic
30
+ sentinel, both on the operator-message-delivery critical path.
31
+
32
+ The classifier stays observe-only — it never gates, blocks, or disables anything,
33
+ and `/guards` is byte-identical for a consumer that ignores the new fields. Rollback
34
+ lever for the alert: `monitoring.guardPostureProbe.alertLoadBearingGaps` (the
35
+ classification stays either way).
36
+
37
+ ## What to Tell Your User
38
+
39
+ I can now tell you when a critical safety system is turned off but something
40
+ important actually depends on it — instead of it quietly staying dark. If that
41
+ happens I will flag it clearly and give you three ways to resolve it: turn the
42
+ guard on, let it finish a short grace period while it is being rolled out, or have
43
+ you formally accept the risk (which I record with your name and reason so it is a
44
+ real decision, not a shrug). Accepting a risk needs your dashboard PIN — I cannot
45
+ do it on your behalf. If you ever ask "why is a critical guard flagged as a gap?",
46
+ that is this feature at work.
47
+
48
+ ## Summary of New Capabilities
49
+
50
+ - `GET /guards` rows gain `loadBearing`, `criticalPath`, `loadBearingGap`,
51
+ `loadBearingSoaking`, `loadBearingAccepted`, and `acceptedFallbackReason`; the
52
+ summary and the capacity heartbeat gain `loadBearingGapKeys`,
53
+ `loadBearingSoakingKeys`, and `loadBearingAcceptedKeys`.
54
+ - `POST /guards/:key/accept-fallback` (dashboard-PIN-gated; `owner` + `reason`
55
+ required) records an owned per-machine acceptance; `DELETE` revokes it and
56
+ reopens the gap.
57
+ - The GuardPostureProbe raises a load-bearing gap on its own bounded, per-episode
58
+ attention topic, separate from the acute load-shed track (no masking).
59
+ - Per-machine by design: accepting a risk on one machine never silences a peer's
60
+ gap; a peer's critical-path label is looked up from the local, fleet-uniform
61
+ manifest.
62
+
63
+ ## Evidence
64
+
65
+ Before: on an agent where a load-bearing feature ships dark (e.g. the inbound-message
66
+ queue with `enabled:false`), `GET /guards` reported it identically to any optional
67
+ dark feature — an `off:dark-default` row with no signal that a critical path depended
68
+ on it, so nothing ever surfaced the exposure (the postmortem's silent-loss shape).
69
+
70
+ After (verified over the real HTTP route + real disk in the Tier-3 e2e): the same
71
+ dark guard's `/guards` row now carries `loadBearing:true`, a `criticalPath` string,
72
+ and `loadBearingGap:true`, and the summary lists it under `loadBearingGapKeys`.
73
+ `POST /guards/:key/accept-fallback` with a valid PIN + owner + reason flips the row to
74
+ `loadBearingAccepted` (gap cleared, reason visible); `DELETE` reopens the gap, and both
75
+ survive a fresh-server "reboot" reading the same durable per-machine record. The
76
+ masking regression drives the REAL `createAttentionItem` funnel and asserts an acute
77
+ off-runtime-divergent guard surfaces while a load-bearing-gap episode has been open —
78
+ and an inert-lever test proves the funnel dedups by item id, so the separate episode
79
+ track (not a healthKey change) is what carries the fix. `npx tsc --noEmit` clean and
80
+ `npm run lint` exit 0.
@@ -0,0 +1,143 @@
1
+ # Side-Effects Review — G3 Dark-but-Load-Bearing Guard Classification
2
+
3
+ **Version / slug:** `g3-dark-but-load-bearing-guards`
4
+ **Date:** `2026-07-01`
5
+ **Author:** Echo (autonomous)
6
+ **Spec:** `docs/specs/g3-dark-but-load-bearing-guards.md` (review-convergence 2026-07-02, 6 iterations, cross-model codex GPT-5.5; approved:true)
7
+ **Parent principle:** A Dark Feature Guards Nothing (STANDARDS-REGISTRY.md, ratified 2026-07-01 PR #1316)
8
+ **Second-pass reviewer:** not-required (Tier 2; observe-only classifier + one PIN-gated suppress route; no new gating/blocking decision, byte-identical `/guards` for a consumer ignoring the new fields)
9
+
10
+ ## Summary of the change
11
+
12
+ The `/guards` inventory could not express "this dark feature is load-bearing" — a
13
+ feature that ships DARK but that a CRITICAL PATH depends on was indistinguishable
14
+ from a genuinely-optional dark feature. It sat quiet while the path it should guard
15
+ ran unguarded (the 2026-07-01 silent-loss postmortem's case study: operator message
16
+ delivery depended on dark guards, and nothing surfaced it).
17
+
18
+ G3 adds an optional `loadBearing`/`criticalPath`/`soakWindowDays`/`declaredLoadBearingAt`
19
+ manifest declaration and classifies a load-bearing guard in a silent-unguarded posture
20
+ into ONE of three states — `loadBearingGap` (loud, its OWN attention channel),
21
+ `loadBearingSoaking` (graduate arm, no attention item), `loadBearingAccepted` (owned,
22
+ PIN-authenticated operator acceptance). `criticalPath` travels on EVERY anomaly of a
23
+ load-bearing guard. Pure classifier, observe-only — it never gates.
24
+
25
+ ### Files added
26
+ - `src/monitoring/guardAcceptedFallbacks.ts` — ALL disk I/O for the per-machine
27
+ operator-accept store (`state/guard-accepted-fallbacks.json`, keyed
28
+ `<machineId>:<guardKey>`). Keeps the classifier PURE: the caller reads/scopes
29
+ once and threads the map in. Missing/corrupt file ⇒ empty map (safe direction).
30
+ - Tests: `tests/unit/monitoring/guard-posture-loadbearing.test.ts`,
31
+ `tests/unit/monitoring/guard-posture-probe-loadbearing.test.ts`,
32
+ `tests/integration/guards-accept-fallback-route.test.ts`,
33
+ `tests/e2e/guards-loadbearing-lifecycle.test.ts`,
34
+ `tests/unit/PostUpdateMigrator-guardLoadBearingSection.test.ts`.
35
+
36
+ ### Files modified
37
+ - `src/monitoring/guardManifest.ts` — 4 optional fields; the two NEW manifest lints
38
+ (`validateGuardManifest`); the curated initial load-bearing set (inboundQueue +
39
+ strandedTopicSentinel, both operator-message-delivery critical-path, 30-day soak
40
+ window declared 2026-07-01).
41
+ - `src/monitoring/guardPostureView.ts` — the SIX new row fields + three-state
42
+ precedence (accepted → soaking-within-window → gap) on a threaded `now`; both
43
+ `deriveGuardRow` and `buildGuardInventory` stay PURE (accept map threaded via
44
+ `DeriveInput`/`opts`); `ROW_FIELD_ALLOWLIST` extended with all six by name; summary
45
+ + heartbeat gain the three key-lists.
46
+ - `src/monitoring/probes/GuardPostureProbe.ts` — the SEPARATE `load-bearing-gap`
47
+ episode track (own `openEpisodeId`/`episodeEmitted`/anomalies + item-id namespace
48
+ `guard-posture-loadbearing:ep-N`); acute close condition tests `currentAcute` (not
49
+ the shared set); `load-bearing-gap` class pushed in both evaluate paths; criticalPath
50
+ on all load-bearing anomalies; `alertLoadBearingGaps` sub-flag (default-on).
51
+ - `src/core/types.ts` — the 3 optional key-list fields on `GuardPostureSummary`.
52
+ - `src/server/routes.ts` — GET /guards threads the accept map + now; the PIN-gated
53
+ `POST`/`DELETE /guards/:key/accept-fallback` route (owner+reason required).
54
+ - `src/commands/server.ts` — all 3 `buildGuardInventory` call sites thread the accept
55
+ map + now (route, probe `getLocalPosture`, heartbeat `selfGuardPosture`); the probe's
56
+ `alertLoadBearingGaps` sub-flag resolved from config (default true).
57
+ - `src/scaffold/templates.ts` + `src/core/PostUpdateMigrator.ts` — Agent Awareness +
58
+ Migration Parity (content-sniffed on `loadBearingGap`).
59
+ - `src/data/state-coherence-registry.json` — the new `guard-accepted-fallbacks`
60
+ store category + a `resolution`-class retention policy.
61
+
62
+ ## Decision-point inventory
63
+
64
+ - **Added — one PIN-gated route** (`POST/DELETE /guards/:key/accept-fallback`): the
65
+ ONLY new decision surface. It SUPPRESSES a safety SIGNAL for a named operator; it
66
+ never allows/blocks an action. Gated by `checkMandatePin` (Know Your Principal — a
67
+ Bearer token cannot accept a safety risk); `owner` AND `reason` both REQUIRED (400
68
+ otherwise); a non-load-bearing key is 404.
69
+ - **Added — classifier flags**: `deriveGuardRow` sets orthogonal FLAGS; the nine
70
+ `GuardEffectiveState` values + the normative precedence table are UNCHANGED. No new
71
+ effective state, so the nine-state contract cannot regress.
72
+ - **Added — probe anomaly class** `load-bearing-gap` on its OWN episode track.
73
+ Observe-only: the probe only ever calls the attention funnel; never gates.
74
+ - No agent-to-user message-flow gate is added.
75
+
76
+ ## Roll-up across the seven review dimensions
77
+
78
+ 1. **Over-block**: none. The classifier adds flags; `/guards` is byte-identical for a
79
+ consumer ignoring the new fields. The accept route only suppresses a signal — it
80
+ loosens nothing and blocks nothing. The alert has a rollback sub-flag
81
+ (`alertLoadBearingGaps:false`) that keeps the classification.
82
+ 2. **Under-block**: none. No gate was loosened. A load-bearing dark guard is now MORE
83
+ visible, not less. Soaking only defers the alert within a bounded, code-constant
84
+ window; a typo'd `declaredLoadBearingAt` falls to the LOUD gap (safe direction).
85
+ 3. **Masking**: the round-3/round-4 fix. `load-bearing-gap` is a designed long-lived
86
+ anomaly; running it on a SEPARATE episode track (its own item-id namespace) means a
87
+ week-open gap can never hold the acute track's episode open. The regression test
88
+ drives the REAL `createAttentionItem` funnel and asserts an acute off-runtime-divergent
89
+ surfaces while the lb episode is open; the inert-lever test proves the funnel dedups
90
+ by ID, so a healthKey-only split would leave masking intact.
91
+ 4. **Multi-machine**: classification is per-machine (an accept on machine A never
92
+ silences a peer's gap — proven by test). Manifest fields are fleet-uniform constants;
93
+ a peer's criticalPath is looked up from the local manifest. The heartbeat carries the
94
+ three key-lists (Array.isArray-guarded for an un-upgraded peer). The 3rd-site fix: the
95
+ heartbeat compute threads the local accept map, so an accepted guard never ships to
96
+ peers as a false gap.
97
+ 5. **Purity / I/O layering**: `deriveGuardRow` AND `buildGuardInventory` gain no `fs`.
98
+ The caller reads the accept file ONCE per inventory build and threads the scoped map;
99
+ the new `guardAcceptedFallbacks.ts` module owns all disk I/O. One accept-file read per
100
+ inventory build, never per guard.
101
+ 6. **Bounded Notification Surface**: each track surfaces as ONE bounded, coalesced,
102
+ per-episode class-level forum topic (two class-level topics max, never per-guard).
103
+ Soaking pushes NO attention item.
104
+ 7. **Migration Parity / Agent Awareness**: the manifest ships as code (reaches existing
105
+ agents automatically); the CLAUDE.md vocabulary + accept route reach existing agents
106
+ via a content-sniffed `migrateClaudeMd` addendum; new agents get it in `generateClaudeMd`.
107
+ Migration idempotency + parity covered by a dedicated test.
108
+
109
+ ## Rollout / rollback
110
+
111
+ Classification + route + separate-track wiring ship ALWAYS-ON as pure observability
112
+ (additive, never gating). The probe ALERT on `load-bearing-gap` rides the existing
113
+ GuardPostureProbe enablement + the `monitoring.guardPostureProbe.alertLoadBearingGaps`
114
+ sub-flag (default-on; soak windows are code constants). Rollback = set the sub-flag
115
+ false; `/guards` keeps the classification. The accept store is a per-machine JSON file;
116
+ DELETE scopes to the operator record only, never the manifest soak constant.
117
+
118
+ ## Test evidence
119
+
120
+ `npx tsc --noEmit` clean; full `npm run lint` exit 0. New tests green: 21 unit
121
+ (classification/lints/allowlist/key-lists/purity), 9 probe-track unit (separate track,
122
+ masking regression against the real funnel, inert-lever guard, soaking-silent,
123
+ criticalPath, heartbeat array-guard, per-machine independence, alert rollback), 7
124
+ integration (PIN gate, owner-required, accept clears gap, DELETE-revoke survives reboot),
125
+ 3 e2e (feature-alive: six fields + accept route mounted). Existing guard/posture suites
126
+ (monitoring 260, guard integration+e2e 51, server+migrator 27, parity+discoverability 170)
127
+ all pass.
128
+
129
+ ## Post-review fixes (2026-07-02)
130
+
131
+ - Rebased onto JKHeadley/main after PR #1317 (silent-loss fix) merged. #1317's
132
+ rejected-enumeration + wiring gate on `routes.ts`/`server.ts`/`types.ts` and G3's
133
+ additive edits compose cleanly (adjacent, non-overlapping edits; tsc + `npm run lint`
134
+ clean post-rebase; the guard/posture suite re-run green).
135
+ - CI's full sharded suite caught two mechanical guard-tracking failures the loaded-box
136
+ local run (exit 144) missed. Neither weakens a guard:
137
+ 1. `no-silent-fallbacks` (492 > baseline 491): `readAcceptedFallbacks`'s
138
+ missing/corrupt-file catch in `guardAcceptedFallbacks.ts` is annotated
139
+ `@silent-fallback-ok` — an empty map is the SAFE direction (no phantom accept can
140
+ suppress a real load-bearing gap). Threshold NOT bumped; count back to 491.
141
+ 2. `feature-delivery-completeness`: the migrator's new `loadBearingGap` content-sniff
142
+ section is registered in `legacyMigratorSections` (migrator-only behavioral
143
+ awareness, also emitted inline by `generateClaudeMd` so a fresh init self-matches).