instar 1.3.746 → 1.3.747
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 +41 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +18 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/durableSecretScrub.d.ts +129 -0
- package/dist/core/durableSecretScrub.d.ts.map +1 -0
- package/dist/core/durableSecretScrub.js +220 -0
- package/dist/core/durableSecretScrub.js.map +1 -0
- package/dist/core/types.d.ts +18 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/data/durableOutputChokepoints.d.ts +84 -0
- package/dist/data/durableOutputChokepoints.d.ts.map +1 -0
- package/dist/data/durableOutputChokepoints.js +145 -0
- package/dist/data/durableOutputChokepoints.js.map +1 -0
- package/dist/messaging/SessionSummarySentinel.d.ts +28 -0
- package/dist/messaging/SessionSummarySentinel.d.ts.map +1 -1
- package/dist/messaging/SessionSummarySentinel.js +25 -1
- package/dist/messaging/SessionSummarySentinel.js.map +1 -1
- package/dist/monitoring/DurableOutputScrubber.d.ts +123 -0
- package/dist/monitoring/DurableOutputScrubber.d.ts.map +1 -0
- package/dist/monitoring/DurableOutputScrubber.js +179 -0
- package/dist/monitoring/DurableOutputScrubber.js.map +1 -0
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +23 -0
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/src/data/durableOutputChokepoints.ts +192 -0
- package/upgrades/1.3.747.md +61 -0
- package/upgrades/side-effects/durable-output-hygiene-standard.md +229 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Side-Effects Review — Durable-Output Hygiene Standard (Layer B scrub, defect class 4)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `durable-output-hygiene-standard`
|
|
4
|
+
**Date:** `2026-07-03`
|
|
5
|
+
**Author:** `echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `not required (tier 2; dark-first, dryRun-defaulted content transform — no allow/deny gate surface)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Implements the converged + approved spec `docs/specs/durable-output-hygiene-standard.md`
|
|
11
|
+
(INSTAR-Bench v2 defect class 4: a session-digest writer reproduced a live access token
|
|
12
|
+
verbatim into stored memory). Ships the Layer-B deterministic security floor: a shared
|
|
13
|
+
credential-pattern module (`src/core/durableSecretScrub.ts` — `scrubForStore` /
|
|
14
|
+
`scrubStructured`, metadata-returning, fail-safe-toward-redaction, size-bounded, linear
|
|
15
|
+
patterns only) wrapped by the config-gated `src/monitoring/DurableOutputScrubber.ts`
|
|
16
|
+
(enabled via the developmentAgent dark-gate, dryRun defaults TRUE, FeatureMetricsLedger
|
|
17
|
+
counters under `durable-output-scrub`, per-store poisoning alarm), wired at the FIRST
|
|
18
|
+
chokepoint — `src/messaging/SessionSummarySentinel.ts` `saveSummary` (fields task/
|
|
19
|
+
blockers/files/topics; mandatory `redactionNote` provenance on any altered summary).
|
|
20
|
+
The auditable chokepoint inventory (`src/data/durableOutputChokepoints.ts`) + a
|
|
21
|
+
shrink-only ratchet test pin every other known store as `pending`/`exempt` with owners.
|
|
22
|
+
Config: `monitoring.durableOutputScrub` in ConfigDefaults (enabled OMITTED — dev-gate;
|
|
23
|
+
`dryRun: true`; `perStore: {}`); registered in `DEV_GATED_FEATURES` and in
|
|
24
|
+
`guardManifest` (configPath + dryRunConfigPath, event-driven, no tick). Server boot
|
|
25
|
+
wiring in `src/commands/server.ts` (scrubber construction, metrics sink, poisoning →
|
|
26
|
+
one deduped attention item per store). Types in `src/core/types.ts`. Tests: unit ×3
|
|
27
|
+
(floor, scrubber, chokepoint ratchet) + integration ×1 (SessionSummarySentinel write
|
|
28
|
+
path), 32 tests.
|
|
29
|
+
|
|
30
|
+
## Decision-point inventory
|
|
31
|
+
|
|
32
|
+
- `SessionSummarySentinel.saveSummary → scrubForStore` — add — a content TRANSFORM at
|
|
33
|
+
the persistence write (never a block: a scrubbed write still lands). Strict no-op
|
|
34
|
+
when the scrubber is absent or not engaged (dark), original-text passthrough in
|
|
35
|
+
dryRun.
|
|
36
|
+
- `DurableOutputScrubber.scrub / scrubRecord` — add — the gating layer: enabled
|
|
37
|
+
(dev-gate) × dryRun (default TRUE) decide whether the computed redaction is applied
|
|
38
|
+
or only counted. Fail-safe: dryRun is `config.dryRun !== false`, so a missing flag
|
|
39
|
+
can never silently enable real redaction.
|
|
40
|
+
- `durableSecretScrub.scrubForStore` — add — pure deterministic floor; on scrub
|
|
41
|
+
exception or oversize input the FIELD is withheld under a typed marker
|
|
42
|
+
(`[REDACTED:scrub-error]` / `[REDACTED:oversize]`) — raw bytes never land because
|
|
43
|
+
the scrub broke, and the event is counted (never fail-open, never silent loss).
|
|
44
|
+
- `Poisoning alarm (onPoisoningSuspected → createAttentionItem)` — add — signal-only
|
|
45
|
+
escalation, one deduped attention item per store (stable id
|
|
46
|
+
`durable-output-poisoning:<store>`); the AttentionTopicGuard flood ceiling backstops.
|
|
47
|
+
- `Chokepoint ratchet (tests/unit/durable-output-chokepoint-ratchet.test.ts)` — add —
|
|
48
|
+
CI-side: statuses may only graduate pending→wired, never regress; a new durable
|
|
49
|
+
persistence path must classify itself in the inventory.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 1. Over-block
|
|
54
|
+
|
|
55
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
56
|
+
|
|
57
|
+
Nothing is rejected — the scrub is a content transform, never a block; every write
|
|
58
|
+
still lands. The over-ACTION risk is a FALSE-POSITIVE redaction destroying a
|
|
59
|
+
legitimate span (the known FP suspect: the JWT-shaped pattern on dotted identifiers).
|
|
60
|
+
That risk is exactly why dryRun defaults TRUE and the dryRun:false flip is the
|
|
61
|
+
operator's endpoint decision on the measured soak packet (spec Frontloaded Decision
|
|
62
|
+
#4: ≥25 reviewed would-redact events with zero FPs, or ≥14 days AND ≥10 reviewed).
|
|
63
|
+
While dark/dryRun (the shipped state) the worst case is a spurious would-redact
|
|
64
|
+
counter — no content is altered.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 2. Under-block
|
|
69
|
+
|
|
70
|
+
**What failure modes does this still miss?**
|
|
71
|
+
|
|
72
|
+
- The floor catches KNOWN TOKEN SHAPES ONLY (stated normatively in the spec/standard
|
|
73
|
+
text): encoded, split, or paraphrased secrets pass the regex floor — Layer A (the
|
|
74
|
+
prompt clause, a separate rollout step) is the only layer that can catch those, and
|
|
75
|
+
it is best-effort.
|
|
76
|
+
- Only the session-summary chokepoint is WIRED in this change; the other inventoried
|
|
77
|
+
stores (self-knowledge facts, KB synthesizer, correction distiller, cartographer
|
|
78
|
+
summaries, learnings + their replicated-receive paths) are `pending` with owners,
|
|
79
|
+
pinned shrink-only by the ratchet so they cannot silently stay unwired.
|
|
80
|
+
- A secret that only PARTIALLY matches a pattern leaves a residual fragment
|
|
81
|
+
(span-replacement, documented + accepted in the spec; mitigated by Layer A + the
|
|
82
|
+
planted-secret bench axis).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. Level-of-abstraction fit
|
|
87
|
+
|
|
88
|
+
Right layer: a deterministic, cheap, pattern-based DETECTOR-plus-floor at the
|
|
89
|
+
persistence write — deliberately NOT an LLM gate (LLM compliance is probabilistic; the
|
|
90
|
+
floor is not). It does not re-implement an existing primitive: the ≥3 diverged inline
|
|
91
|
+
scrub copies (`autonomousHeartbeatScrub`, PolicyEnforcementLayer patterns,
|
|
92
|
+
`SecretRedactor.BUILTIN_PATTERNS`) are the drift problem the shared module exists to
|
|
93
|
+
close; they migrate onto it per the spec's rollout step 0 (tracked in the inventory,
|
|
94
|
+
not silently duplicated). It FEEDS existing surfaces (FeatureMetricsLedger, attention
|
|
95
|
+
queue, /guards via guardManifest) instead of growing parallel ones.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 4. Signal vs authority compliance
|
|
100
|
+
|
|
101
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
102
|
+
|
|
103
|
+
- [x] Yes — but sanctioned: this is a detector holding MUTATION authority over durable
|
|
104
|
+
content, classified per the spec (§Decision points touched) as a **sanctioned
|
|
105
|
+
deterministic safety floor** (the fork-bomb spawn-cap precedent), carrying the
|
|
106
|
+
bypass-carries-its-own-cap preconditions: dark-first, dryRun-defaulted soak with
|
|
107
|
+
measured FP rate, MANDATORY provenance marker on every alteration (no silent
|
|
108
|
+
mutation), typed markers + structured kind/offset/length metadata, per-store opt-out
|
|
109
|
+
(`perStore` in config + `exempt` in the inventory), operator-gated enforce flip.
|
|
110
|
+
- It has NO block/allow surface — a write is never refused, only (eventually,
|
|
111
|
+
post-operator-flip) span-redacted with a visible marker.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 5. Interactions
|
|
116
|
+
|
|
117
|
+
- **Shadowing:** the scrub runs INSIDE `saveSummary` on the fresh-generate path only;
|
|
118
|
+
the staleness re-save path deliberately re-persists the already-scrubbed summary
|
|
119
|
+
(re-scrubbing would recompute an empty provenance and drop `redactionNote`). It does
|
|
120
|
+
not run before/after any existing allow/deny check — none exists on this write.
|
|
121
|
+
- **Double-fire:** the existing telemetry scrubs (heartbeat scrub, PolicyEnforcementLayer)
|
|
122
|
+
cover DIFFERENT surfaces (outbound messages, logs); no store is scrubbed twice. The
|
|
123
|
+
poisoning alarm dedupes per store id; the AttentionTopicGuard is the flood backstop.
|
|
124
|
+
- **Races:** the scrubber is constructed once at boot and is stateless per call except
|
|
125
|
+
the in-memory poisoning window (single process, no shared file state) — no new
|
|
126
|
+
cross-process state.
|
|
127
|
+
- **Feedback loops:** metrics/alarm consumers never feed back into the scrub decision;
|
|
128
|
+
a metrics-sink or alarm-callback throw is swallowed (tagged @silent-fallback-ok) so
|
|
129
|
+
observability can never break the persist path.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 6. External surfaces
|
|
134
|
+
|
|
135
|
+
- Other agents / install base: none while dark (fleet resolves the dev-gate to OFF;
|
|
136
|
+
behavior byte-identical to today). On a dev agent, dryRun TRUE means stored bytes
|
|
137
|
+
are still unchanged — only metrics rows + (if a burst) one attention item appear.
|
|
138
|
+
- External systems: none — pure local regex, no LLM call, no spawn-cap slot, no
|
|
139
|
+
egress, no third-party spend.
|
|
140
|
+
- Persistent state: adds the optional `redactionNote` field on persisted
|
|
141
|
+
SessionSummary records (additive JSON field, old readers unaffected) — and only
|
|
142
|
+
post-operator-flip.
|
|
143
|
+
- **Operator surface (Mobile-Complete Operator Actions):** no new operator-facing
|
|
144
|
+
ACTION in this change. The eventual dryRun:false flip is a config decision made on
|
|
145
|
+
an attention-queue decision packet (readable from the phone); no PIN route or form
|
|
146
|
+
is added here.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
151
|
+
|
|
152
|
+
No operator surface — not applicable (no dashboard renderer/approval page/grant form
|
|
153
|
+
touched).
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
158
|
+
|
|
159
|
+
**Declared: machine-local execution over git/code-replicated config, with the
|
|
160
|
+
replicated-store RECEIVE paths named as their own chokepoints.** The scrub runs where
|
|
161
|
+
the write runs; the inventory + config replicate via code. The spec (§3) names the
|
|
162
|
+
real multi-machine hazard — a peer running with the scrub dark writes an unscrubbed
|
|
163
|
+
secret that replication delivers into a scrub-enabled machine's store, bypassing
|
|
164
|
+
writer-side chokepoints — and the inventory therefore lists each replicated store's
|
|
165
|
+
receive path as its own chokepoint (`receivePath`, scrub-on-receive on the LOCAL
|
|
166
|
+
materialized copy only, never what re-replicates; identity fingerprints computed with
|
|
167
|
+
redaction markers normalized out; redaction metadata merge-inert). Those receive-path
|
|
168
|
+
wirings are `pending` entries pinned by the ratchet. No user-facing notices (one-voice
|
|
169
|
+
n/a — the poisoning alarm rides the existing attention queue, which is pool-coalesced
|
|
170
|
+
via P17); no URLs generated; the only durable state is the store content itself, which
|
|
171
|
+
already has an owner machine.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 8. Rollback cost
|
|
176
|
+
|
|
177
|
+
Pure code + config change, shipped dark. Rollback = revert the commit and ship a
|
|
178
|
+
patch. No data migration: while dark/dryRun NO stored bytes differ; post-flip, altered
|
|
179
|
+
records carry `redactionNote` + typed markers (the original span is destroyed BY
|
|
180
|
+
DESIGN — that irreversibility is why the flip is operator-gated, and it is a property
|
|
181
|
+
of the feature, not of rolling it back). No agent state repair; no user-visible
|
|
182
|
+
regression during a rollback window.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Conclusion
|
|
187
|
+
|
|
188
|
+
The review confirmed the shipped state is inert on the fleet (dev-gate OFF), metrics-
|
|
189
|
+
only on the dev agent (dryRun TRUE, fail-safe default `!== false`), and that every
|
|
190
|
+
failure path fails toward redaction/withholding rather than leaking (scrub-error /
|
|
191
|
+
oversize withhold the field under a typed marker; metrics/alarm throws never break the
|
|
192
|
+
write). One design point was tightened during the finish pass: the scrub was
|
|
193
|
+
registered in `guardManifest` with `dryRunConfigPath` so /guards, the boot tripwire,
|
|
194
|
+
and the dark-but-load-bearing classifier cover the posture from day one. Clear to
|
|
195
|
+
ship dark; the enforce flip remains the operator's decision on the soak packet.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Second-pass review (if required)
|
|
200
|
+
|
|
201
|
+
**Reviewer:** not required
|
|
202
|
+
**Independent read of the artifact: not required** — tier 2, no allow/deny gate
|
|
203
|
+
surface, dark-first + dryRun-defaulted; the operator-gated flip is the human check
|
|
204
|
+
on the only destructive transition.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Evidence pointers
|
|
209
|
+
|
|
210
|
+
- Bounded test runs (2026-07-03): `tests/unit/durableSecretScrub.test.ts` (12),
|
|
211
|
+
`tests/unit/DurableOutputScrubber.test.ts` (10),
|
|
212
|
+
`tests/unit/durable-output-chokepoint-ratchet.test.ts` (6),
|
|
213
|
+
`tests/integration/session-summary-durable-scrub.test.ts` (4) — all pass.
|
|
214
|
+
- Ratchet/lint verification: `tests/unit/lint-dev-agent-dark-gate.test.ts` (24, golden
|
|
215
|
+
map regenerated via `attributeEnabledFalsePaths` over the rebased ConfigDefaults),
|
|
216
|
+
`tests/unit/no-silent-fallbacks.test.ts` (back at baseline),
|
|
217
|
+
`tests/unit/lint-guard-manifest.test.ts` (14),
|
|
218
|
+
`tests/unit/feature-delivery-completeness.test.ts` + `tests/unit/CapabilityIndex.test.ts`
|
|
219
|
+
(125), guard-posture-view (52) — all pass. `tsc --noEmit` clean;
|
|
220
|
+
`scripts/docs-coverage.mjs` floors green.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Class-Closure Declaration (display-only mirror)
|
|
225
|
+
|
|
226
|
+
No agent-authored-artifact defect fixed in THIS commit — not applicable. (This change
|
|
227
|
+
BUILDS the class-4 guard machinery per the spec; the defect-class closure itself is
|
|
228
|
+
graded by the Standards Enforcement Coverage audit over the chokepoint inventory once
|
|
229
|
+
the registry text ships, which remains operator-gated.)
|