instar 1.3.639 → 1.3.640
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/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +35 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/IntelligenceRouter.d.ts +9 -0
- package/dist/core/IntelligenceRouter.d.ts.map +1 -1
- package/dist/core/IntelligenceRouter.js +17 -4
- package/dist/core/IntelligenceRouter.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +7 -1
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/monitoring/DegradationReporter.d.ts +42 -0
- package/dist/monitoring/DegradationReporter.d.ts.map +1 -1
- package/dist/monitoring/DegradationReporter.js +117 -1
- package/dist/monitoring/DegradationReporter.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +3 -3
- package/upgrades/1.3.640.md +46 -0
- package/upgrades/side-effects/resilient-degradation-ladder-increment-2.md +97 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Side-Effects Review — Resilient Degradation Ladder Increment 2 (never-silent tracking)
|
|
2
|
+
|
|
3
|
+
**Slug:** `resilient-degradation-ladder-increment-2` · **Tier:** 2 (spec-driven; converged +
|
|
4
|
+
operator-approved). **Spec:** `docs/specs/resilient-degradation-ladder.md` §4, D6.
|
|
5
|
+
|
|
6
|
+
## Summary of the change
|
|
7
|
+
|
|
8
|
+
The never-silent half of the operator's principle ("never silently remain degraded indefinitely"),
|
|
9
|
+
dark/dev-gated:
|
|
10
|
+
- `DegradationReporter` gains an open-degradation lifecycle (§4): `openDegradation` /
|
|
11
|
+
`resolveDegradation` / `sweepOpenDegradations` / `configureNeverSilent`, keyed on
|
|
12
|
+
`(component, framework)`, bounded by MAX_OPEN, O(1) per open/resolve.
|
|
13
|
+
- `IntelligenceRouter` gains `onHeuristicFallthrough` (fired at each NON-gating throw — the caller
|
|
14
|
+
will use its heuristic) + `onResolved` (fired at each successful real-LLM answer) hooks.
|
|
15
|
+
- Server wires the hooks to the reporter + calls `configureNeverSilent` (dev-gated via
|
|
16
|
+
`resolveDevAgentGate`) + starts a 60s `unref`'d sweep timer.
|
|
17
|
+
- `DEV_GATED_FEATURES` registration (`degradationLadderNeverSilent`).
|
|
18
|
+
|
|
19
|
+
## Decision-point inventory
|
|
20
|
+
|
|
21
|
+
Frozen in spec §5 D6: escalateMs 15m, TTL 30m, MAX_OPEN 500, key (component, framework),
|
|
22
|
+
liveness-gated (≥1 retry to escalate; TTL-close otherwise), deduped per episode.
|
|
23
|
+
|
|
24
|
+
## 1. Over-block / false positive
|
|
25
|
+
|
|
26
|
+
The big false-positive class (round-1): a run-once / idle component that degrades once and never
|
|
27
|
+
calls again. Closed by the LIVENESS gate — a degradation with 0 retries since open AUTO-CLOSES at
|
|
28
|
+
the TTL instead of escalating (tested). Only a degradation that genuinely RE-attempted and still
|
|
29
|
+
fell to heuristic (≥1 retry) escalates. Escalation is deduped per episode (re-escalates only after
|
|
30
|
+
another full window — tested).
|
|
31
|
+
|
|
32
|
+
## 2. Under-block
|
|
33
|
+
|
|
34
|
+
Tracking only opens on a NON-gating heuristic fallthrough (where the heuristic actually runs); a
|
|
35
|
+
gating fail-closed is NOT tracked (it's the safe outcome, not a heuristic — tested:
|
|
36
|
+
onHeuristicFallthrough does NOT fire for a gating call). No-op when disabled.
|
|
37
|
+
|
|
38
|
+
## 4. Signal vs authority
|
|
39
|
+
|
|
40
|
+
The escalation is an attention SIGNAL (a deduped fixed-template Telegram line), never a gate. It
|
|
41
|
+
takes no destructive action. The auto-resolve is observational (a successful call clears the open
|
|
42
|
+
state). Consistent with Signal-vs-Authority.
|
|
43
|
+
|
|
44
|
+
## 5. Interactions — THE WEDGE-CRITICAL SECTION
|
|
45
|
+
|
|
46
|
+
§4 extends the exact `DegradationReporter` subsystem that caused the 2026-06-21 event-loop wedge
|
|
47
|
+
(`gateHealthAlert`→`toneGate.review`→router→`report`→`reportEvent`→recursion + growing-array
|
|
48
|
+
JSON.stringify). This increment is designed to NOT repeat it:
|
|
49
|
+
- The sweep NEVER calls `report()`/`reportEvent()`/`gateHealthAlert` — it surfaces an attention item
|
|
50
|
+
via `telegramSender` DIRECTLY (no toneGate, no events-array growth, no recursion). Tested: the
|
|
51
|
+
escalation path sends via the fake telegramSender, never through report.
|
|
52
|
+
- The open map is BOUNDED (MAX_OPEN; oldest evicted — tested) — no unbounded growth.
|
|
53
|
+
- open/resolve are O(1) Map mutations — no full-map serialize per event.
|
|
54
|
+
- The existing `_gatingHealthAlert` reentrancy guard + `MAX_EVENTS` cap are untouched.
|
|
55
|
+
|
|
56
|
+
## 6. External surfaces
|
|
57
|
+
|
|
58
|
+
No new route. The only new egress is the deduped escalation line (the operator's explicit want).
|
|
59
|
+
The new config (`intelligence.degradationLadder.neverSilent`) shipped in Increment 1's types.
|
|
60
|
+
|
|
61
|
+
## 6b. Operator-surface quality
|
|
62
|
+
|
|
63
|
+
N/A — no dashboard/approval surface. `openDegradationCount()` is a read-only observability getter.
|
|
64
|
+
|
|
65
|
+
## Framework generality
|
|
66
|
+
|
|
67
|
+
Framework-agnostic — the hooks key on the resolved framework; works for whichever framework the
|
|
68
|
+
component routes to.
|
|
69
|
+
|
|
70
|
+
## 7. Multi-machine posture
|
|
71
|
+
|
|
72
|
+
Machine-local: each machine's reporter tracks its own degradations + its own sweep timer. No
|
|
73
|
+
replicated state. (A sustained per-machine degradation could double-alert on a 2-machine setup for
|
|
74
|
+
one provider outage — accepted as a minor local-dedup limitation, noted in the spec §6.)
|
|
75
|
+
|
|
76
|
+
## 8. Rollback cost
|
|
77
|
+
|
|
78
|
+
Trivial: dark on the fleet (`configureNeverSilent({enabled:false})` when not dev/configured) — the
|
|
79
|
+
lifecycle methods are all `if (!enabled) return` no-ops and no sweep timer is started. The router
|
|
80
|
+
hooks are no-ops downstream when disabled. Revert = remove an unused-on-fleet code path.
|
|
81
|
+
|
|
82
|
+
## Evidence pointers
|
|
83
|
+
|
|
84
|
+
- `tests/unit/degradation-never-silent.test.ts` (6): open→resolve duration; run-once TTL-auto-close
|
|
85
|
+
(NO escalation); stuck (≥1 retry) escalates once past the window + deduped + re-escalates after a
|
|
86
|
+
new window; (component,framework) keying (no cross-resolution); bounded (MAX_OPEN evict);
|
|
87
|
+
disabled=no-op.
|
|
88
|
+
- `tests/unit/degradation-ladder.test.ts` (+3 hook cases): onResolved on success; onHeuristicFallthrough
|
|
89
|
+
on a non-gating exhaustion; onHeuristicFallthrough does NOT fire for a gating call.
|
|
90
|
+
- The `DEV_GATED_FEATURES` both-sides wiring test confirms `degradationLadderNeverSilent` resolves
|
|
91
|
+
live-on-dev / dark-on-fleet. Full `npm run lint` (incl. lint-dev-agent-dark-gate) + `tsc` green.
|
|
92
|
+
|
|
93
|
+
## Conclusion
|
|
94
|
+
|
|
95
|
+
Delivers the never-silent guarantee — a heuristic fallback can no longer silently persist
|
|
96
|
+
indefinitely: it auto-resolves on recovery and escalates if genuinely stuck — built carefully to NOT
|
|
97
|
+
repeat the wedge it extends. Dark/dev-gated, no-op when off. Ship.
|