instar 1.3.663 → 1.3.664
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/core/CoherenceGate.d.ts +30 -0
- package/dist/core/CoherenceGate.d.ts.map +1 -1
- package/dist/core/CoherenceGate.js +81 -9
- package/dist/core/CoherenceGate.js.map +1 -1
- package/dist/core/CoherenceReviewer.d.ts +21 -0
- package/dist/core/CoherenceReviewer.d.ts.map +1 -1
- package/dist/core/CoherenceReviewer.js +52 -5
- package/dist/core/CoherenceReviewer.js.map +1 -1
- package/dist/core/SendGateway.d.ts.map +1 -1
- package/dist/core/SendGateway.js +21 -2
- package/dist/core/SendGateway.js.map +1 -1
- package/dist/core/reviewers/escalation-resolution.d.ts.map +1 -1
- package/dist/core/reviewers/escalation-resolution.js +8 -0
- package/dist/core/reviewers/escalation-resolution.js.map +1 -1
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.664.md +34 -0
- package/upgrades/side-effects/reviewer-fail-closed-on-abstain.md +70 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The CMT-1794 fail-open fix (v1) — the systemic "No Silent Degradation" gap the convergent audit found. The coherence reviewers that screen outbound messages (leaked credentials/PII to the wrong recipient, org-constraint violations, fabricated claims, hallucinated URLs) were SILENTLY PASSING the message whenever their own LLM call errored, timed out, or returned unparseable output: the base `CoherenceReviewer.review()` resolved with `pass:true` and `CoherenceGate` counted that errored reviewer as a GENUINE PASS (it only abstain-counted a promise rejection, and `review()` catches internally). So on any LLM blip, the highest-stakes outbound checks were skipped and the message went out unchecked.
|
|
9
|
+
|
|
10
|
+
v1 (the substance):
|
|
11
|
+
- **Abstain, not silent pass.** `CoherenceReviewer` (and the `escalation-resolution` reviewer's `review()` OVERRIDE — a gap the new ratchet caught) now tag an errored/timeout/unparseable result as `abstained:true` with a STRUCTURED cause (typed-timeout `.code`, never a string-match of the error text). `CoherenceGate` counts an abstain as an abstain (excluded from the pass/block tallies + `passCount`), not a genuine pass.
|
|
12
|
+
- **High-criticality abstain fails CLOSED external.** A floor reviewer (`information-leakage`, `value-alignment`, `claim-provenance`, `url-validity`) that abstains on an EXTERNAL channel routes into the gate's EXISTING `highCritTimeout` fail-closed path (reuse, not a new tier — the convergence caught that the draft's parallel `critical` tier was a no-op). Internal channels stay fail-open-with-report (Decision D — blocking the agent's own control loop on a blip is worse). The floor is a hardcoded, multi-machine-uniform set that config can RAISE but not silently downgrade.
|
|
13
|
+
- **Provider-swap before abstain (§8).** Reviewer calls are now `gating:true`, so the router swaps harness/account before a reviewer abstains — a single-provider blip keeps the review alive; fail-closed engages only on a true multi-provider outage.
|
|
14
|
+
- **Coverage fix (Dec B).** `information-leakage` was skipped for the DEFAULT `primary-user` recipient, so the headline protection never ran on the common path; it now stays enabled for primary-user on EXTERNAL channels (a message to the operator can still leak a third party's PII), keyed on the resolved external flag.
|
|
15
|
+
- **SendGateway Stage-4** thrown-gate-error catch now fails CLOSED on external (was swallow-and-send).
|
|
16
|
+
- **Operator kill-switch** `responseReview.failClosedOnCriticalAbstain` (default true) reverts the fail-closed behavior live, no deploy.
|
|
17
|
+
- **CI ratchet** (`tests/unit/reviewer-fail-closed-ratchet.test.ts`) drives every reviewer subclass through a forced LLM error and fails the build if any returns a verdict without the abstain tag — so a future reviewer/override can't silently reintroduce the fail-open.
|
|
18
|
+
|
|
19
|
+
v2 refinements are tracked (CMT-1801), all bounded-not-blocking: Dec C short-message critical-only coverage (narrowed by PEL catching credential patterns deterministically), the breaker/hold optimization (the amplification it optimizes is already bounded by the spawn-cap + maxRetries), the kill-switch's true-live server wiring (v1 uses snapshot + safe default), and the integration/e2e tier.
|
|
20
|
+
|
|
21
|
+
## What to Tell Your User
|
|
22
|
+
|
|
23
|
+
If your agent ever sent an outbound message during an LLM hiccup that should have been screened (for a leaked credential, a third party's PII, an org-constraint violation, a fabricated claim/link) — that gap is closed. When one of those checks can't run because its own LLM call errors out, the agent now HOLDS the message for a moment (and retries) instead of sending it unchecked — but only for messages going to the outside world, and only on a genuine outage (a one-off blip just swaps to a backup and keeps going). Messages to you directly aren't held (that would risk freezing the agent). If a sustained outage ever made this too cautious, there's a one-flip off-switch that takes effect with no restart.
|
|
24
|
+
|
|
25
|
+
## Summary of New Capabilities
|
|
26
|
+
|
|
27
|
+
- `responseReview.failClosedOnCriticalAbstain` (config, default true) — operator kill-switch: revert the new fail-closed-on-critical-reviewer-abstain behavior live, without a deploy. Mirrors `messaging.toneGate.failClosedOnExhaustion`.
|
|
28
|
+
|
|
29
|
+
## Evidence
|
|
30
|
+
|
|
31
|
+
- `tests/unit/reviewer-fail-closed-on-abstain.test.ts` (3) — external+all-abstain → fail-closed (the exact bug, previously `pass:true`); internal → pass-with-report; abstain ≠ clean pass.
|
|
32
|
+
- `tests/unit/reviewer-fail-closed-ratchet.test.ts` (10) — every reviewer abstains on a forced LLM error; the review()-override set is locked.
|
|
33
|
+
- 164 tests green across CoherenceReviewer + CoherenceGate + MessageSentinel + spawn-cap + the new files; `npm run build` + `tsc --noEmit` clean.
|
|
34
|
+
- Driven by the converged + approved spec (2-round spec-converge + codex cross-model); the convergence caught a no-op tier and a false rides-retry premise before any code.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Side-Effects Review — Coherence reviewers fail CLOSED on abstain (CMT-1794, v1)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `reviewer-fail-closed-on-abstain`
|
|
4
|
+
**Date:** `2026-06-25`
|
|
5
|
+
**Author:** `Instar Agent (echo)`
|
|
6
|
+
**Second-pass reviewer:** `the spec's 2-round convergence (6 internal reviewers + codex cross-model) IS the multi-angle review; see docs/specs/reports/reviewer-fail-closed-on-abstain-convergence.md`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Implements the converged+approved `reviewer-fail-closed-on-abstain` spec (the CMT-1794 fail-open fix), v1 = the substance. The audit found that when a coherence reviewer's LLM call errors/times-out/returns-unparseable, the base `CoherenceReviewer.review()` resolved with a permissive `pass:true` and `CoherenceGate` counted it as a GENUINE PASS (it only abstain-counted a promise REJECTION; review() catches internally) — so on an LLM blip the highest-stakes outbound checks (leak/constraint/provenance/url) silently passed. Files: `src/core/CoherenceReviewer.ts` (tag abstains with a structured cause), `src/core/CoherenceGate.ts` (count abstains as abstains → route to the existing `highCritTimeout` floor path + the kill-switch + Dec B coverage), `src/core/reviewers/escalation-resolution.ts` (its `review()` OVERRIDE tags abstains too — a gap the §9 ratchet caught), `src/core/types.ts` (the kill-switch config field), + 2 test files (13 tests). `src/core/SendGateway.ts` Stage-4 catch now fails closed on external.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `CoherenceGate` reviewer abstain handling — **modify** — an errored/unparseable reviewer is now an ABSTAIN (not a counted pass); a high-criticality abstain on an external channel fails the turn CLOSED via the EXISTING `highCritTimeout` path (reuse, not a new tier).
|
|
15
|
+
- `CoherenceReviewer.review()` / `parseResponse()` / escalation-resolution override — **modify** — tag abstains (host-set, injection-proof).
|
|
16
|
+
- `SendGateway` Stage-4 catch — **modify** — external fails closed (held) instead of swallow-and-send.
|
|
17
|
+
- `responseReview.failClosedOnCriticalAbstain` kill-switch — **add** — live-readable revert.
|
|
18
|
+
|
|
19
|
+
## 1. Over-block
|
|
20
|
+
|
|
21
|
+
A reviewer that genuinely PASSES still passes — only a reviewer that COULD NOT FORM AN OPINION (errored) now abstains. The over-block surface is: during a real multi-provider LLM outage, external outbound needing a critical review is HELD (not lost — it rides the existing retry path). That is the safe direction the standard mandates, it is operator-revertable (the kill-switch), and the critical set is narrow (4 floor reviewers). §8 ensures a single-provider blip SWAPS (review stays alive) rather than abstaining, so a held outbound requires a true outage, not a transient flake.
|
|
22
|
+
|
|
23
|
+
## 2. Under-block
|
|
24
|
+
|
|
25
|
+
Bounded coverage deferred to v2 (tracked CMT-1801): short URL-free external messages still skip the gate (Dec C) — but PEL catches credential PATTERNS deterministically regardless of length, so the residual is a short non-pattern PII string; the breaker/hold optimization (§3) is deferred, but the amplification it optimizes is ALREADY bounded by the host spawn-cap + `maxRetries` (3 turns), so v1 is bounded, not unbounded. MessageSentinel inbound stays fail-open-except-capacity by deliberate decision (it leaks nothing on fail-open; the emergency-stop fast-path runs first).
|
|
26
|
+
|
|
27
|
+
## 3. Level-of-abstraction fit
|
|
28
|
+
|
|
29
|
+
Correct — Signal-vs-Authority: the abstain is a HOST-set signal (trusted catch/parse code, never model output), the gate is the authority. The fix REUSES the gate's existing `'high'` criticality + `highCritTimeout` machinery (the convergence's headline correction: the draft's parallel `critical` tier was a no-op the code never read).
|
|
30
|
+
|
|
31
|
+
## 4. Signal vs authority compliance
|
|
32
|
+
|
|
33
|
+
- [x] No — produces a SIGNAL (the abstain tag) consumed by the existing smart gate; removes a silent fail-open. The structured `abstainCause` is derived from typed error classes (typed-timeout `.code`), NOT a string-match of the error text — the very standard this work enforces, applied to itself.
|
|
34
|
+
|
|
35
|
+
## 5. Interactions
|
|
36
|
+
|
|
37
|
+
- **Shadowing:** none — the abstain branch is the unified rejected-or-abstain-tagged path; capacity-shed (`capacityUnavailable`) keeps precedence over abstain over genuine pass/block (explicit tri-state).
|
|
38
|
+
- **Double-fire:** none — one abstain per reviewer per evaluation.
|
|
39
|
+
- **Kill-switch scope:** governs ONLY the NEW abstain-tag-driven `highCritTimeout`; a promise REJECTION keeps its pre-existing unconditional fail-closed (no behavior change there).
|
|
40
|
+
- **passCount:** an abstain no longer inflates `passCount` (else a degraded reviewer reads as healthy during an outage).
|
|
41
|
+
|
|
42
|
+
## 6. External surfaces
|
|
43
|
+
|
|
44
|
+
- A new config key `responseReview.failClosedOnCriticalAbstain` (default true; live-readable via the optional `liveConfig` getter — v1 falls back to snapshot/safe-default, true no-restart wiring is CMT-1801). No new HTTP route. The held disposition rides the existing retry/feedback path. No operator-facing action added (the held/escalation rides the existing attention/DegradationReporter surface).
|
|
45
|
+
|
|
46
|
+
## 6b. Operator-surface quality
|
|
47
|
+
|
|
48
|
+
No operator surface touched (no dashboard/approval/grant file). Not applicable.
|
|
49
|
+
|
|
50
|
+
## 7. Multi-machine posture
|
|
51
|
+
|
|
52
|
+
**replicated/uniform by construction** — the floor set + abstain logic + ratchet are compiled from source (identical across machines); the `reviewerCriticality` config can only RAISE (the hardcoded floor holds on every machine regardless of config), so the leak/constraint protection cannot silently diverge. Held drafts are machine-local by design (Decision E). No user-facing notice, no cross-topic durable state, no generated URL.
|
|
53
|
+
|
|
54
|
+
## 8. Rollback cost
|
|
55
|
+
|
|
56
|
+
Pure code change + one additive config key. Back-out = revert the commit (reviewers return to the prior fail-open) OR flip `failClosedOnCriticalAbstain:false` (live, no deploy) to revert just the fail-closed behavior. No data migration, no agent-state repair.
|
|
57
|
+
|
|
58
|
+
## Conclusion
|
|
59
|
+
|
|
60
|
+
v1 ships the SUBSTANCE of CMT-1794: no reviewer silently fails open (the §9 ratchet locks it across every subclass + caught the escalation-resolution override gap), a high-criticality abstain fails closed external (proven by a dedicated test), the leak-review coverage bypass (Dec B) is closed, the provider-swap (§8) keeps reviews alive on a single-provider blip, and a live kill-switch reverts it. The v2 refinements (Dec C, breaker/hold, kill-switch live-wiring, integration/e2e) are bounded-not-blocking and tracked under CMT-1801.
|
|
61
|
+
|
|
62
|
+
## Second-pass review
|
|
63
|
+
|
|
64
|
+
The spec's 2-round convergence (6 internal reviewers across security/adversarial/scalability/integration/decision-completeness/lessons + a codex GPT-tier cross-model pass + the conformance gate) IS the multi-angle review for this change — it reshaped the design (caught the no-op tier + the false rides-retry premise) before any code. See the convergence report. The implementation was verified against that converged design with 164 passing tests across every touched area.
|
|
65
|
+
|
|
66
|
+
## Evidence pointers
|
|
67
|
+
|
|
68
|
+
- `tests/unit/reviewer-fail-closed-on-abstain.test.ts` (3): external+all-abstain→fail-closed (the exact bug, was pass:true); internal→pass-with-report (Decision D); abstain≠clean-pass.
|
|
69
|
+
- `tests/unit/reviewer-fail-closed-ratchet.test.ts` (10): every reviewer subclass abstains on a forced LLM error (never a silent pass); the review()-override set is the known {information-leakage (delegates to super), escalation-resolution (tags in its own catch)} — a NEW override trips the ratchet.
|
|
70
|
+
- Regression: CoherenceReviewer (47) + CoherenceGate (27) + MessageSentinel + spawn-cap-fail-closed-gates + all the above = 164 tests green. `npm run build` + `tsc --noEmit` clean.
|