instar 1.3.1213 → 1.3.1215

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 (30) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +33 -0
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/tailscaleStatusParser.d.ts +27 -2
  5. package/dist/core/tailscaleStatusParser.d.ts.map +1 -1
  6. package/dist/core/tailscaleStatusParser.js +30 -4
  7. package/dist/core/tailscaleStatusParser.js.map +1 -1
  8. package/dist/data/standards-guard-index.json +1 -1
  9. package/dist/data/standards-guard-index.meta.json +2 -2
  10. package/dist/data/standards-registry.meta.json +1 -1
  11. package/dist/monitoring/RopeHealthMonitor.d.ts +13 -0
  12. package/dist/monitoring/RopeHealthMonitor.d.ts.map +1 -1
  13. package/dist/monitoring/RopeHealthMonitor.js +25 -2
  14. package/dist/monitoring/RopeHealthMonitor.js.map +1 -1
  15. package/dist/server/routes.d.ts.map +1 -1
  16. package/dist/server/routes.js +1 -0
  17. package/dist/server/routes.js.map +1 -1
  18. package/dist/testing/selfActionRegistry.d.ts.map +1 -1
  19. package/dist/testing/selfActionRegistry.js +38 -0
  20. package/dist/testing/selfActionRegistry.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/data/builtin-manifest.json +46 -46
  23. package/src/data/standards-guard-index.json +1 -1
  24. package/src/data/standards-guard-index.meta.json +2 -2
  25. package/src/data/standards-registry.meta.json +1 -1
  26. package/upgrades/1.3.1214.md +24 -0
  27. package/upgrades/1.3.1215.md +39 -0
  28. package/upgrades/side-effects/rope-key-expiry-mesh-scope.md +85 -0
  29. package/upgrades/side-effects/w29-ledger-selfaction-model.md +39 -0
  30. package/upgrades/w29-ledger-selfaction-model.eli16.md +7 -0
@@ -0,0 +1,39 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The Tailscale key-expiry tier of `RopeHealthMonitor` now scans only tailnet nodes that back
9
+ a mesh rope (plus self), rather than every node on the tailnet. `parseTailscaleStatus`
10
+ takes an optional mesh-address set and flags each entry `backsMeshRope`; `soonestKeyExpiry`
11
+ takes `meshScopedOnly`; `server.ts` supplies the addresses from the registry's `tailscale`
12
+ endpoints. An unreadable or empty address source falls back to the previous tailnet-wide
13
+ scan, so a failure is never quieter.
14
+
15
+ ## What to Tell Your User
16
+
17
+ Your machines warn you when a Tailscale key is about to expire — but they were checking
18
+ every device on your account, including phones and old laptops that aren't part of the
19
+ mesh. One dead device with a lapsed key made that warning permanent, so it stopped meaning
20
+ anything, and on 2026-08-29 it was read as a live risk and reported as one.
21
+
22
+ The warning now covers only the devices your machines actually reach each other on. Same
23
+ horizon, same wording — it just stops crying wolf about hardware that can't affect
24
+ anything.
25
+
26
+ ## Summary of New Capabilities
27
+
28
+ - Key-expiry warnings are scoped to devices that genuinely carry mesh traffic.
29
+ - This machine's own key always counts, since its expiry would drop every route it has.
30
+ - Every failure path (unreadable registry, empty list) falls back to the old tailnet-wide
31
+ scan — the change can make the check noisier, never quieter.
32
+ - No device names, addresses, or account details leave the parser; addresses go in, a
33
+ boolean comes out.
34
+
35
+ ## Evidence
36
+
37
+ - Unit: `tests/unit/tailscaleStatusParser.test.ts` (+6), `tests/unit/RopeHealthMonitor.test.ts` (+2),
38
+ verified red-then-green against pre-fix behaviour.
39
+ - Side-effects review: `upgrades/side-effects/rope-key-expiry-mesh-scope.md`.
@@ -0,0 +1,85 @@
1
+ # Side-effects review — rope key-expiry mesh scoping
2
+
3
+ **Change:** the Tailscale key-expiry tier of `RopeHealthMonitor` now considers only tailnet
4
+ nodes that back a mesh rope (plus self), instead of every node on the tailnet.
5
+ `parseTailscaleStatus` gained an optional mesh-address set and a per-entry
6
+ `backsMeshRope` flag; `soonestKeyExpiry` gained `opts.meshScopedOnly`; `server.ts` supplies
7
+ the addresses from the registry's `tailscale` endpoints.
8
+
9
+ **Origin (live, 2026-08-29, topic 62395):** a tailnet node offline since 2026-08-13 with a
10
+ lapsed key was the tailnet-wide soonest expiry, so `GET /mesh/rope-health` reported
11
+ `warn: true, inDays: -16.6` continuously. Every real mesh machine's key was valid into
12
+ Dec 2026 / Feb 2027. The permanent warning was not merely noise — it was read as a live
13
+ finding and relayed to the operator as a real risk requiring their login.
14
+
15
+ ## 1. Over-block — what legitimate input does this now reject?
16
+
17
+ A genuine expiry on a tailnet node that backs a mesh rope but whose address is NOT in the
18
+ registry's endpoint list — e.g. a peer reachable over tailscale that has only ever
19
+ advertised `lan`/`cloudflare`. That node's expiry would no longer warn.
20
+
21
+ Bounded by two fallbacks that both fail LOUD: an unreadable address source and an empty
22
+ address set BOTH degrade to the tailnet-wide scan. So the narrowed scope applies only when
23
+ a non-empty mesh address list is genuinely available. Self is unconditionally included.
24
+
25
+ ## 2. Under-block — what does this still miss?
26
+
27
+ It does not detect a rope dropping for any reason other than key expiry (unchanged), and
28
+ it cannot warn about a mesh machine whose tailscale address this machine has never
29
+ recorded. It also does not change the two-week horizon, so a key that lapses inside one
30
+ check interval is still reported late — pre-existing, untouched.
31
+
32
+ ## 3. Level-of-abstraction fit
33
+
34
+ Correct. The parser owns the scrubbed shape and is the only place that may see addresses,
35
+ so the matching belongs there; the monitor owns the policy (`meshScopedOnly`); the wiring
36
+ site owns knowledge of the registry. Putting the filter in the monitor would have required
37
+ addresses to escape the parser, breaking that file's stated content-scrub boundary.
38
+
39
+ ## 4. Signal vs authority compliance
40
+
41
+ Compliant. This is a digest/status signal with no blocking authority — it gates nothing,
42
+ changes no routing, and cannot suppress a rope. It makes an existing signal more accurate.
43
+
44
+ ## 5. Interactions
45
+
46
+ - The `key-expiry-warning` metric fires strictly less often (that is the fix).
47
+ - `composeDigest()` drops the permanent expiry sentence when scoped — the other digest
48
+ sentences (per-peer conditions) are untouched.
49
+ - `TailscaleKeyExpiryEntry` gained a field; one existing unit assertion deep-equalled the
50
+ old shape and was updated. No serialized/persisted format includes this type.
51
+ - No interaction with the U4.3 recovery prober or the resolver — the expiry tier has its
52
+ own cadence and data source.
53
+
54
+ ## 6. External surfaces
55
+
56
+ `GET /mesh/rope-health → keyExpiry.soonest` may now name a later expiry (a mesh node)
57
+ where it previously named an earlier one (an unrelated node). No shape change. The
58
+ content-scrub boundary is preserved: addresses flow INTO the parser and only a boolean
59
+ flows out — no identity, address, or account data is added to any output.
60
+
61
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
62
+
63
+ **Machine-local BY DESIGN.** Each machine runs its own `tailscale status` and reads its own
64
+ registry, so each scopes to the peers it knows. This is correct rather than a limitation: a
65
+ tailnet node backing a rope is a per-observer fact, and the digest is composed per machine.
66
+ No replication path added. A single-machine agent supplies an empty address list and
67
+ therefore keeps exactly today's tailnet-wide behaviour.
68
+
69
+ ## 8. Rollback cost
70
+
71
+ Trivial. Remove the `meshTailscaleAddresses` dep from the `server.ts` construction and the
72
+ monitor reverts to the tailnet-wide scan by an explicitly-tested path. No persisted data,
73
+ no migration, no state repair.
74
+
75
+ ## Tests
76
+
77
+ - Unit `tests/unit/tailscaleStatusParser.test.ts` (+6): flagging, mesh-scoped soonest,
78
+ un-scoped soonest (pre-fix behaviour retained), no-set and empty-set back-compat, self
79
+ always counts, node with no address array is not mesh-backing.
80
+ - Unit `tests/unit/RopeHealthMonitor.test.ts` (+2): the dead-node regression through the
81
+ monitor (scoped vs un-scoped on identical input), and the throwing/empty source falling
82
+ back to tailnet-wide rather than to a silent empty scope. Verified RED against pre-fix
83
+ behaviour and green after.
84
+ - E2E: not applicable — no new route or feature-liveness surface; this narrows an existing
85
+ status field already covered by the monitor's own suite.
@@ -0,0 +1,39 @@
1
+ # Side-Effects Review — window-lifecycle self-action convergence model
2
+
3
+ **Slug:** w29-ledger-selfaction-model · **Date:** 2026-08-29 · **Author:** Codey (W29 Lane C2), commit ritual by Echo/Observer 1 · **Tier:** 1 (test infrastructure, 40 LOC)
4
+
5
+ ## Summary
6
+ Registers the WindowLifecycleObligationLedger's owned 60s notify tick in `src/testing/selfActionRegistry.ts` (a faithful convergence model of its save-before-send dedupe brake) plus the `@self-action-controller` annotation at the real emit site in `src/server/routes.ts`. Closes the ACT-320 closure:gap declared at the W28 merge.
7
+
8
+ ## 1. Over-block
9
+ None — the change blocks nothing; it is a registry entry + comment. The ratchet could over-fail future edits to the real brake; that is its purpose.
10
+
11
+ ## 2. Under-block
12
+ The model covers the dedupe-brake class only (one issue → one emit, restart-surviving). It does not model the tick's other behaviors (evaluation cost, remediation flows) — those are not self-action emits.
13
+
14
+ ## 3. Level-of-abstraction fit
15
+ Matches the established registry pattern (30th entry, same shape as the other 29): modeled trigger→brake→emit under the pinned PressureFixture. No new mechanism.
16
+
17
+ ## 4. Signal vs authority compliance
18
+ No decision authority anywhere — pure test infrastructure. The ratchet is CI signal.
19
+
20
+ ## 5. Interactions
21
+ The forcing lint (`lint-no-unregistered-self-action`) stops flagging this emit (verified); no other check consumes the registry entry. No double-fire risk — the annotation is a comment.
22
+
23
+ ## 6. External surfaces
24
+ None. No runtime behavior, routes, or messaging changes.
25
+
26
+ ## 7. Multi-machine posture
27
+ Not applicable — test-only code; the modeled controller itself is Echo-local by the W28 spec's non-leakage rule.
28
+
29
+ ## 8. Rollback cost
30
+ Revert the commit; the lint returns to report-only flagging of the emit. No data or state involved.
31
+
32
+ ## Fidelity verification (the load-bearing check)
33
+ The model records the issue into durable state BEFORE emitting, mirroring `routes.ts` where `ledger.surfacedIssues.push(...)` + `windowStore.save(...)` precede the async `sendToTopic` (verified at the shipped lines during this review). Ratchet: 141/141 including the new entry's bounded-emission and restart cases.
34
+
35
+ ## Review record
36
+ Lane C2 (codex): 1 review cycle, clean, stop conditions declared before cycle 1, last-material-defect cycle 0 (none found). Observer independently verified fidelity + gates at commit time.
37
+
38
+ ## Conclusion
39
+ Clear to ship.
@@ -0,0 +1,7 @@
1
+ # ELI16 — proving the rulebook engine's messenger can't spam
2
+
3
+ The window-rulebook engine that shipped this week has a background loop that checks, every minute, whether any window duty is being violated — and when it finds a new problem, it sends one notice to the observers' channel. A loop that sends messages on its own is exactly the kind of thing that has caused message floods before, so this repo keeps a registry of every such self-acting loop, each paired with a small simulated model of its braking logic. A test then squeezes every model under sustained worst-case pressure — the problem never goes away, the process restarts mid-stream — and proves the loop settles at a bounded number of actions instead of firing forever.
4
+
5
+ The engine's loop was shipped with its brake in the code (it durably records each issue BEFORE sending, so the same issue can never send twice — even across a restart) but WITHOUT its entry in that registry, which was tracked as a follow-up with a deadline. This change pays that debt: it adds the faithful model of the engine's brake to the registry (the model was checked line-against-line with the real code: record-first, then send), plus the one-line annotation at the real send site that links code and registry. The pressure test now covers it — 141 checks pass, including the new ones proving one problem yields exactly one notice, forever, restart included.
6
+
7
+ Nothing about the engine's behavior changes. This is test infrastructure: the safety net under the engine's messenger, not the messenger itself. If someone later edits the engine's brake and breaks it, the registry test is now the tripwire that catches it before it ships.