instar 1.3.730 → 1.3.732
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 +161 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +26 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceGate.d.ts +119 -0
- package/dist/core/CoherenceGate.d.ts.map +1 -1
- package/dist/core/CoherenceGate.js +342 -19
- package/dist/core/CoherenceGate.js.map +1 -1
- package/dist/core/CoherenceReviewer.d.ts +30 -0
- package/dist/core/CoherenceReviewer.d.ts.map +1 -1
- package/dist/core/CoherenceReviewer.js +14 -0
- package/dist/core/CoherenceReviewer.js.map +1 -1
- package/dist/core/ConversationRegistry.d.ts +260 -0
- package/dist/core/ConversationRegistry.d.ts.map +1 -0
- package/dist/core/ConversationRegistry.js +1151 -0
- package/dist/core/ConversationRegistry.js.map +1 -0
- package/dist/core/FileClassifier.d.ts.map +1 -1
- package/dist/core/FileClassifier.js +7 -0
- package/dist/core/FileClassifier.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +13 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +105 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/ResponseReviewDecisionLog.d.ts +45 -0
- package/dist/core/ResponseReviewDecisionLog.d.ts.map +1 -0
- package/dist/core/ResponseReviewDecisionLog.js +85 -0
- package/dist/core/ResponseReviewDecisionLog.js.map +1 -0
- package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
- package/dist/core/WriteDomainRegistry.js +21 -0
- package/dist/core/WriteDomainRegistry.js.map +1 -1
- package/dist/core/conversationContextWiring.d.ts +51 -0
- package/dist/core/conversationContextWiring.d.ts.map +1 -0
- package/dist/core/conversationContextWiring.js +78 -0
- package/dist/core/conversationContextWiring.js.map +1 -0
- package/dist/core/conversationIdentity.d.ts +112 -0
- package/dist/core/conversationIdentity.d.ts.map +1 -0
- package/dist/core/conversationIdentity.js +141 -0
- package/dist/core/conversationIdentity.js.map +1 -0
- package/dist/core/deliverToConversation.d.ts +62 -0
- package/dist/core/deliverToConversation.d.ts.map +1 -0
- package/dist/core/deliverToConversation.js +85 -0
- package/dist/core/deliverToConversation.js.map +1 -0
- 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/reviewers/conversational-tone.d.ts.map +1 -1
- package/dist/core/reviewers/conversational-tone.js +15 -1
- package/dist/core/reviewers/conversational-tone.js.map +1 -1
- package/dist/core/types.d.ts +76 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/untrustedConversationContext.d.ts +77 -0
- package/dist/core/untrustedConversationContext.d.ts.map +1 -0
- package/dist/core/untrustedConversationContext.js +134 -0
- package/dist/core/untrustedConversationContext.js.map +1 -0
- package/dist/monitoring/ReviewCanaryBattery.d.ts +160 -0
- package/dist/monitoring/ReviewCanaryBattery.d.ts.map +1 -0
- package/dist/monitoring/ReviewCanaryBattery.js +416 -0
- package/dist/monitoring/ReviewCanaryBattery.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +15 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +7 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +13 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +15 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +10 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +114 -1
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates/jobs/instar/review-canary-battery.md +31 -0
- package/src/scaffold/templates.ts +15 -0
- package/upgrades/1.3.731.md +97 -0
- package/upgrades/1.3.732.md +107 -0
- package/upgrades/side-effects/context-aware-outbound-review.md +62 -0
- package/upgrades/side-effects/durable-conversation-identity-increment1.md +89 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The response-review pipeline's watch-mode data vetoed the planned enforcement
|
|
9
|
+
flip: of 12 reviewed real messages, 2 would have been WRONGLY blocked — both
|
|
10
|
+
technical content the operator had EXPLICITLY asked for (one was the worktree
|
|
11
|
+
keep/delete list the user requested). The reviewer judges each message in
|
|
12
|
+
isolation by construction: its own prompt allows "code the user explicitly
|
|
13
|
+
asked to see," but `ReviewContext` carries NO conversation — the carve-out was
|
|
14
|
+
prose with no input.
|
|
15
|
+
|
|
16
|
+
Per `docs/specs/context-aware-outbound-review.md` (review-converged round 4,
|
|
17
|
+
approved), the opted-in reviewer now receives a bounded, untrusted-data-
|
|
18
|
+
enveloped slice of recent conversation, and the flip evidence becomes durable
|
|
19
|
+
and two-sided-measured:
|
|
20
|
+
|
|
21
|
+
- **Context channel (§D1/§D2):** an injected synchronous provider
|
|
22
|
+
(TopicMemory rows + the topic's verified-operator binding →
|
|
23
|
+
`buildConversationContext`), rendered by ONE shared envelope
|
|
24
|
+
(`untrustedConversationContext.ts` — per-call random boundary, JSON-encoded
|
|
25
|
+
scrubbed bodies, the structural `ask-license mode` line, the four-clause
|
|
26
|
+
carve-out contract as ONE ATOMIC block). Budget-clamped (6 msgs / 500 chars /
|
|
27
|
+
4000 total).
|
|
28
|
+
- **Principal binding (§D4, Know Your Principal):** role + `verifiedOperator`
|
|
29
|
+
tags + the window's ask-license mode are computed at the WIRING layer from
|
|
30
|
+
AUTHENTICATED uids only; any uid-less user row in an unbound window degrades
|
|
31
|
+
AWAY from licensing (fail-closed).
|
|
32
|
+
- **Structural scoping (§D3):** only `conversational-tone` (v1), only for
|
|
33
|
+
primary-user recipients, receives an augmented ctx copy; every other
|
|
34
|
+
reviewer structurally cannot render conversation. The carve-out is ONE-WAY
|
|
35
|
+
(can only move a would-block toward PASS), content-class-bounded (never
|
|
36
|
+
credentials/PII), and never touches the deterministic PEL layer.
|
|
37
|
+
- **Total containment (§D5):** the HTTP seam above the pipeline fails OPEN on
|
|
38
|
+
a crash, so every new context path is individually contained — any failure
|
|
39
|
+
degrades to today's exact (stricter) behavior.
|
|
40
|
+
- **Durable flip evidence (§D8):** `logs/response-review-decisions.jsonl` —
|
|
41
|
+
one size-rotated row per evaluated turn (outcome, llmVerdict, violations,
|
|
42
|
+
contextMeta, 200 scrubbed chars; never conversation bodies), surviving the
|
|
43
|
+
restarts that erased the in-memory history.
|
|
44
|
+
- **The one-way property is MEASURED (§D9.4/4b):** soak-only counterfactual
|
|
45
|
+
re-reviews of qualifying would-blocks (shared `pairId`), plus a daily
|
|
46
|
+
adversarial `ReviewCanaryBattery` (PEL-missable secret/PII fixtures with
|
|
47
|
+
covering asks, seeded into reserved NEGATIVE topic ids, baseline +
|
|
48
|
+
with-context arms, reviewer-level assertions, finally-cleanup) behind the
|
|
49
|
+
Bearer-gated `POST /review/canary-battery/run` + the off-by-default
|
|
50
|
+
`review-canary-battery` job. Every run outcome — including refusals —
|
|
51
|
+
writes a `batterySummary` row; a silent skip is impossible.
|
|
52
|
+
- Ships dev-gated dark (`responseReview.conversationalContext` — `enabled`
|
|
53
|
+
OMITTED → resolveDevAgentGate; LIVE on dev agents, DARK on the fleet).
|
|
54
|
+
Kill-switch `enabled: false` applies at the NEXT evaluate via the new
|
|
55
|
+
liveConfig wiring — no restart. The enforcement flip
|
|
56
|
+
(`observeOnly: false`) remains the operator's manual action, gated on the
|
|
57
|
+
§D9 clean-day criteria.
|
|
58
|
+
|
|
59
|
+
## What to Tell Your User
|
|
60
|
+
|
|
61
|
+
<!-- audience: agent-only, maturity: experimental -->
|
|
62
|
+
- **My message reviewer can now see what you asked for (experimental, dark):
|
|
63
|
+
** the safety reviewer that checks my replies used to judge each message in
|
|
64
|
+
isolation — so when you explicitly asked me for a technical list, it wanted
|
|
65
|
+
to block my answer. On development agents it now reads the last few
|
|
66
|
+
messages of our conversation (wrapped as quoted data, never instructions),
|
|
67
|
+
so "you asked for this" finally counts. It can only ever RESCUE a message
|
|
68
|
+
from a wrong block — it can never become a new reason to block, and it never
|
|
69
|
+
licenses secrets. Blocking stays off until the operator flips it after a
|
|
70
|
+
measured clean day.
|
|
71
|
+
|
|
72
|
+
## Summary of New Capabilities
|
|
73
|
+
|
|
74
|
+
| Capability | How to Use |
|
|
75
|
+
|-----------|-----------|
|
|
76
|
+
| Context-aware reviewer carve-out (dev-gated dark) | `responseReview.conversationalContext` in `.instar/config.json` (enabled omitted → live on dev agents, dark on fleet; `enabled:false` = live kill-switch, no restart) |
|
|
77
|
+
| Durable would-block decision log | `logs/response-review-decisions.jsonl` (one row per reviewed turn; `contextMeta` on `GET /review/history` entries too) |
|
|
78
|
+
| Daily anti-laundering canary battery | `POST /review/canary-battery/run` (Bearer; 503 while dark) + the off-by-default `review-canary-battery` job |
|
|
79
|
+
| Canary tag plumbing (test route only) | `POST /review/test` accepts `canary` + `fixtureId`; `/review/evaluate` ignores them (a real turn cannot self-tag) |
|
|
80
|
+
|
|
81
|
+
## Evidence
|
|
82
|
+
|
|
83
|
+
- Veto data grounded live (2026-07-02 watch mode): 2/12 real messages would
|
|
84
|
+
have been wrongly blocked — both operator-requested technical content; both
|
|
85
|
+
are now permanent §D9.2 regression fixtures AND the battery's pass-side
|
|
86
|
+
controls.
|
|
87
|
+
- Spec converged round 4 (0 CRITICAL / 0 MAJOR; internal panel + both external
|
|
88
|
+
cross-model doors verified the fold table 5/5), approved under standing
|
|
89
|
+
Session-A preapproval (topic 29836), tag commit dfdb7b21a.
|
|
90
|
+
- All three test tiers shipped and green: unit (57 new tests across 5 files —
|
|
91
|
+
every §5 boundary both sides, incl. the throw fixtures at fetch/tag/render
|
|
92
|
+
time, the uid-less fail-closed rule, the counterfactual + canary-tag
|
|
93
|
+
exclusion, the battery outcome table), integration (8 — real auth
|
|
94
|
+
middleware + real TopicMemory SQLite through the full HTTP pipeline), e2e
|
|
95
|
+
lifecycle (6 — production init path with the REAL LiveConfig + dev-gate
|
|
96
|
+
funnel + TopicOperatorStore: dev boot alive, on-disk kill-switch applies
|
|
97
|
+
without restart, fleet boot byte-identical + battery 503).
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Per `docs/specs/durable-conversation-identity.md` (review-converged round 11 —
|
|
9
|
+
0 CRITICAL / 0 MAJOR — approved under the standing Session-A preapproval,
|
|
10
|
+
topic 29836), the Phase-1 structural refactor's first increment lands: a
|
|
11
|
+
durable, channel-agnostic identity for conversations.
|
|
12
|
+
|
|
13
|
+
- **`ConversationRegistry` (§3):** the sparse join table between canonical key
|
|
14
|
+
(`slack:<team>:<channel>[:<thread>]`), structured tuple, and a stable minted
|
|
15
|
+
NEGATIVE id. Telegram positive ids pass through unregistered forever
|
|
16
|
+
(back-compat by construction). The mint candidate is the legacy hash value
|
|
17
|
+
(zero-loss adoption + mixed-fleet skew convergence); the registry is the
|
|
18
|
+
collision authority — probe DOWN through ONE shared displacement
|
|
19
|
+
implementation, bounded at `MAX_PROBE_DISTANCE = 64`.
|
|
20
|
+
- **The WAL journal (§3.4):** `conversation-registry.jsonl` at the stateDir
|
|
21
|
+
ROOT (the one glob shape the deployed backup resolver expands — R3-C4).
|
|
22
|
+
Probed/durable-binding mints fsync ONE line BEFORE the id returns; the O(N)
|
|
23
|
+
snapshot is batched with a size-adaptive interval (the CommitmentTracker
|
|
24
|
+
freeze shape is structurally avoided). Torn-tail discard; non-tail
|
|
25
|
+
corruption fails CLOSED (quarantine-aside + attention + a durability
|
|
26
|
+
incident); unknown ops (a rollback across a newer version) are
|
|
27
|
+
skipped-and-preserved with snapshot-flush SUSPENSION until re-upgrade;
|
|
28
|
+
single global monotonic `seq` across rotations and restarts.
|
|
29
|
+
- **Eager mint at Slack inbound (§6.3):** the dispatch mints (get-or-create)
|
|
30
|
+
the resolved routing key's conversation id on every inbound and carries it
|
|
31
|
+
as `conversationId` in message metadata + the session bootstrap context —
|
|
32
|
+
the identity durable state can finally bind to. Fail-toward-delivery: a
|
|
33
|
+
mint degradation never blocks a message.
|
|
34
|
+
- **Safety rails:** per-channel mint-rate breaker (speculative drops re-mint
|
|
35
|
+
free; the durable-binding carve-out has its OWN cap + typed
|
|
36
|
+
`conversation-registration-capacity` refusal); the D1 runtime kill-switch
|
|
37
|
+
(`conversationIdentity.recording.enabled: false` → legacy-identical
|
|
38
|
+
in-memory hashing, no redeploy; minted-id durable binds refused typed);
|
|
39
|
+
§3.1 workspace pinning with per-machine `multi-workspace-unsupported`
|
|
40
|
+
refusal and `_`→teamId in-place upgrade (local authenticated source only).
|
|
41
|
+
- **The `deliverToConversation` funnel SKELETON (§5):** typed, non-exceptional
|
|
42
|
+
§5.1 results (dark/dryRun return the SAME `not-delivered` shape as
|
|
43
|
+
unresolvable + a would-deliver audit line — NEVER success-shaped); local-
|
|
44
|
+
origin-only resolution (KYP); system-channel suppression inside the funnel.
|
|
45
|
+
ZERO consumers migrate in this increment; the E1 idempotency guard, P17
|
|
46
|
+
budgets, and the deterministic gate-exempt arm land with their §6.1
|
|
47
|
+
increments before any consumer rides it.
|
|
48
|
+
- **Read-only observability (§8):** `GET /conversations`,
|
|
49
|
+
`/conversations/:id`, `/conversations/resolve`, `/conversations/health`
|
|
50
|
+
(labels escaped on render; no write routes exist by design).
|
|
51
|
+
- **Migration parity (§6.2/§9):** backup manifest gains BOTH
|
|
52
|
+
`state/conversation-registry.json` AND the `conversation-registry.jsonl*`
|
|
53
|
+
glob; config defaults arrive via the add-missing merge (`recording.enabled:
|
|
54
|
+
true`; `followThrough.dryRun: true` with `enabled` OMITTED — the
|
|
55
|
+
developmentAgent gate resolves it); a dev-gate strip migration removes a
|
|
56
|
+
default-shaped `followThrough.enabled: false`; the CLAUDE.md capability
|
|
57
|
+
section reaches new agents (template) AND existing agents (migrator +
|
|
58
|
+
Codex/Gemini shadow markers).
|
|
59
|
+
|
|
60
|
+
Behavior-identical for existing flows: recording is additive, delivery is
|
|
61
|
+
dark, the three legacy hash copies keep computing the same values the registry
|
|
62
|
+
records (the §4 callsite consolidation is the next increment; a grep ratchet
|
|
63
|
+
pins the copy set so a fourth is a CI failure).
|
|
64
|
+
|
|
65
|
+
## What to Tell Your User
|
|
66
|
+
|
|
67
|
+
<!-- audience: agent-only, maturity: experimental -->
|
|
68
|
+
- **Slack conversations now have a durable identity (foundation only, no
|
|
69
|
+
visible change yet):** every Slack channel or thread I talk in is written
|
|
70
|
+
into a permanent address book with a stable number the moment a message
|
|
71
|
+
arrives, so upcoming increments can make promises/reminders/notices in
|
|
72
|
+
Slack survive restarts the way they do on Telegram. A negative topic id in
|
|
73
|
+
my state is one of these minted conversation ids — I can look it up in the
|
|
74
|
+
address book — not an error. Delivery to Slack through this identity stays
|
|
75
|
+
switched off (dry-run first on development agents).
|
|
76
|
+
|
|
77
|
+
## Summary of New Capabilities
|
|
78
|
+
|
|
79
|
+
| Capability | How to Use |
|
|
80
|
+
|-----------|-----------|
|
|
81
|
+
| Conversation inventory | `GET /conversations` (Bearer; `?platform=slack`, `?limit=N`) |
|
|
82
|
+
| Resolve an id | `GET /conversations/:id` (positive → Telegram pass-through; unknown negative → honest 404) |
|
|
83
|
+
| Forward lookup (mints nothing) | `GET /conversations/resolve?key=…` or `?sessionKey=…` |
|
|
84
|
+
| Health + growth/suspension tripwires | `GET /conversations/health` (the Tier-3 alive target) |
|
|
85
|
+
| Recording kill-switch (D1) | `conversationIdentity.recording.enabled: false` (live-read, no restart) |
|
|
86
|
+
| Delivery gate (dark) | `conversationIdentity.followThrough` (dev-gated; `dryRun: true` first) |
|
|
87
|
+
|
|
88
|
+
## Evidence
|
|
89
|
+
|
|
90
|
+
- Spec converged round 11 (0 CRITICAL / 0 MAJOR; internal multi-lens panel +
|
|
91
|
+
two external cross-model doors per round), approved under the standing
|
|
92
|
+
Session-A operator preapproval (topic 29836); tag commit aa5086eb8.
|
|
93
|
+
- All three test tiers shipped and green: unit (79 tests across 5 files —
|
|
94
|
+
golden parity with the legacy hash, frozen schema-v1 constants, the crafted
|
|
95
|
+
in-charset collision pair probing to distinct ids in either order, WAL
|
|
96
|
+
crash-durability without a snapshot flush, torn-tail/corruption/unknown-op
|
|
97
|
+
replay rules incl. snapshot suspension, rotation-spanning replay with one
|
|
98
|
+
global seq, breaker both budgets, recording kill-switch both arms,
|
|
99
|
+
workspace pinning, adoption gating, funnel §5.1 typed contract, the
|
|
100
|
+
mint-idiom grep ratchet), integration (7 — real authMiddleware + real
|
|
101
|
+
registry through the full HTTP pipeline incl. the label-escape pin and the
|
|
102
|
+
read-only resolve), migration-parity unit (7 — defaults never materialize
|
|
103
|
+
`followThrough.enabled`, the #1001 strip, the backup glob expanding through
|
|
104
|
+
the REAL BackupManager with every expanded file in the snapshot), e2e
|
|
105
|
+
lifecycle (4 — production init path answers `/conversations/health` 200,
|
|
106
|
+
the mint→restart→same-id cycle via journal replay, Telegram pass-through
|
|
107
|
+
sparseness).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Side-Effects Review — Context-Aware Outbound Review (S2 context-aware reviewers)
|
|
2
|
+
|
|
3
|
+
**Spec:** docs/specs/context-aware-outbound-review.md (converged r4, approved under standing Session-A preapproval, topic 29836; tag commit dfdb7b21a). **Parent:** S2 enforcement-readiness track — the enforcement flip of `responseReview.observeOnly` is gated on this spec's §D9 criteria and stays a manual operator action.
|
|
4
|
+
**Ships DARK on the fleet, LIVE on development agents** via `responseReview.conversationalContext` (`enabled` OMITTED from defaults → `resolveDevAgentGate` at the WIRING layer; explicit false force-darks, explicit true is the fleet flip).
|
|
5
|
+
**Files:** src/core/untrustedConversationContext.ts (new), src/core/conversationContextWiring.ts (new), src/core/ResponseReviewDecisionLog.ts (new), src/monitoring/ReviewCanaryBattery.ts (new), src/core/CoherenceGate.ts, src/core/CoherenceReviewer.ts, src/core/reviewers/conversational-tone.ts, src/core/types.ts, src/server/routes.ts, src/server/AgentServer.ts, src/server/CapabilityIndex.ts, src/commands/server.ts, src/scaffold/templates.ts, src/core/PostUpdateMigrator.ts, src/scaffold/templates/jobs/instar/review-canary-battery.md (new, ships OFF)
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
|
|
9
|
+
1. **untrustedConversationContext.ts (new):** the ONE shared untrusted-data envelope (§D2) — `clampConversation` (§D6 budget clamps: 6 msgs / 500 chars / 4000 total, oldest dropped first) + `renderUntrustedConversation` (per-call random `CTX_BOUNDARY`, JSON-encoded bodies, credential scrub per body, role labels + `USER(verified-operator)` principal tag, the structural `ask-license mode` line, and the §D3 four-clause prompt contract rendered as ONE ATOMIC block). Never throws; per-message scrub failure drops that message; any render failure drops the whole section.
|
|
10
|
+
2. **conversationContextWiring.ts (new):** `buildConversationContext(rows, operator)` — the wiring-layer principal computation (§D4): role from `fromUser`, `verifiedOperator` tags from authenticated-uid-vs-binding match, and the window `askLicenseMode` (bound → `verified-operator`; unbound → `single-sender` ONLY when every user row carries the same single authenticated uid; ANY uid-less user row or 2+ uids → `weak-corroboration-only`, fail-closed per R3-M2/R4-L1).
|
|
11
|
+
3. **ResponseReviewDecisionLog.ts (new):** the durable §D8 flip-evidence JSONL (`logs/response-review-decisions.jsonl`) — append-only, size-rotated (10MB → single `.1` archive via SafeFsExecutor), every write failure swallowed (telemetry never gates delivery).
|
|
12
|
+
4. **CoherenceGate.ts:** optional `conversationContextProvider` + widened `liveConfig` getter (now carries the wiring-RESOLVED `conversationalContext` block; ABSENT getter ⇒ DARK even against an enabled snapshot — round-2 L4 precedence); acquisition once per `_evaluate` inside its own try/catch, only for primary-user recipients with a numeric topicId; augmented SHALLOW-COPY fan-out (base ctx never carries conversation — §D3 structural availability); `EvaluateRequest.telemetry` (test-route-only canary tags) + `EvaluateResponse._contextMeta`; §D8 rows written at the `logAudit` seam for EVERY outcome (textHead = 200 scrubbed chars; canary/fixtureId stamped by the writer); §D9.4 counterfactual re-review (observeOnly-only, fire-and-forget, one context-stripped re-review per opted-in violating reviewer, `counterfactual:true` + shared `pairId`); `appendDecisionRow` exposes the single writer to the battery; in-memory audit entries gain `contextMeta` (§6). `DynamicReviewer` honors the `'recent-conversation'` contextRequirements opt-in key.
|
|
13
|
+
5. **conversational-tone.ts:** renders the atomic block ONLY when the gate handed it the augmented ctx; absent fields ⇒ prompt BYTE-IDENTICAL to feature-dark (the pre-existing static exception stands).
|
|
14
|
+
6. **routes.ts:** `/review/test` accepts `canary` + `fixtureId` (forwarded as `telemetry`; response gains `contextMeta`); `POST /review/evaluate` does NOT read them (a real turn cannot self-tag — boundary 13B, pinned by test); new Bearer-gated `POST /review/canary-battery/run` (503 while dark).
|
|
15
|
+
7. **ReviewCanaryBattery.ts (new):** the §D9.4b(a) daily battery driver — refuse-or-(pre-clean → seed reserved NEGATIVE topic ids with per-run-unique messageIds + uid-carrying user rows → replay both arms via the Bearer `/review/test` route → reviewer-level PEL-unmaskable assertions → finally-cleanup) → a `batterySummary` row on EVERY outcome including refusals.
|
|
16
|
+
8. **server.ts wiring:** the `resolveConversationalContext` LIVE closure (LiveConfig + `resolveDevAgentGate` — the kill-switch applies at the NEXT evaluate, no restart), the provider over real TopicMemory + `_agentServerRef` TopicOperatorStore, and the battery construction (localhost Bearer self-call to `/review/test`).
|
|
17
|
+
9. **Job template `review-canary-battery.md` (new):** ships `enabled: false` everywhere; operator enables it only on the soaking dev agent (spec §4.5 — no fleet migration carries it on).
|
|
18
|
+
10. **templates.ts + PostUpdateMigrator.ts:** `CONTEXT_AWARE_REVIEW_CLAUDEMD_SECTION` in `generateClaudeMd` (new agents) + an idempotent content-sniffed `migrateClaudeMd` append (existing agents), carrying the house dark-feature honesty phrasing (round-1 m5: `/review/history` 501s on most installs).
|
|
19
|
+
|
|
20
|
+
## Blast radius
|
|
21
|
+
|
|
22
|
+
- **Fleet default: byte-identical.** `enabled` omitted + not a dev agent ⇒ the resolver returns false ⇒ no acquisition, no section, prompts byte-identical (pinned by unit boundary 3/4 and the e2e fleet boot). The battery route 503s; the job ships OFF.
|
|
23
|
+
- **Total containment (round-1 M6, load-bearing):** the HTTP seam above the pipeline fails OPEN on a crash, so EVERY new context path — fetch, tagging, clamp, render, meta, D8 write, counterfactual — is individually contained; any failure degrades to "no section" (the STRICTER current posture), never to a throw escaping `_evaluate`. Pinned by throw fixtures at fetch/tag/render time.
|
|
24
|
+
- **Structural exposure bound:** conversation fields ride ONLY the augmented copies handed to the resolved opt-in set (`conversational-tone` alone in v1) for primary-user recipients; `information-leakage` and every other reviewer receive a base ctx that cannot render them (round-1 M1 + round-2 m2). Widening requires a spec revision, never config alone.
|
|
25
|
+
- **One-way risk direction:** context can only move a would-block toward PASS (measured by the counterfactual pairs + the canary battery, both soak-only); PEL is untouched and runs before all reviewers.
|
|
26
|
+
|
|
27
|
+
## Risk + mitigation
|
|
28
|
+
|
|
29
|
+
- **Risk:** the context channel launders a credential/PII paste past the reviewer. **Mitigation:** the §D3.3 bounded-scope clause + the daily adversarial canary battery (PEL-missable fixtures, baseline + with-context arms, reviewer-level assertions) — a laundering event FAILS the soak and resets the §D9 clock.
|
|
30
|
+
- **Risk:** an attacker-influenceable context body carries instructions. **Mitigation:** the proven §Design-4 envelope (random boundary + JSON-encoded bodies + corroborating-only preamble), boundary-7 injection tests.
|
|
31
|
+
- **Risk:** uid-less legacy rows fake a single-sender license. **Mitigation:** R3-M2 fail-closed rule (any uid-less user row in an unbound window ⇒ weak-corroboration-only), pinned both sides in boundary 6.
|
|
32
|
+
- **Risk:** the D8 log grows unbounded or leaks conversation. **Mitigation:** size rotation; only `contextMeta` + 200 scrubbed chars persist — bodies never (at-rest honesty note carried in the spec + docs).
|
|
33
|
+
- **Risk:** the battery strands fixture rows in the production TopicMemory. **Mitigation:** reserved NEGATIVE topic ids (collision structurally impossible), pre-clean + finally-cleanup + per-run-unique messageIds + seed-count assertion (R4-m4), all pinned by unit tests.
|
|
34
|
+
|
|
35
|
+
## Migration parity
|
|
36
|
+
|
|
37
|
+
- **Config:** NO `migrateConfig` entry on purpose — every key is optional with in-code defaults and the dev-gate convention REQUIRES `enabled` absent (a migration writing `enabled:false` would recreate the PR #1001 bug). Documented on the `ConversationalContextConfig` type.
|
|
38
|
+
- **CLAUDE.md:** section ships in `generateClaudeMd` + content-sniffed idempotent `migrateClaudeMd` append.
|
|
39
|
+
- **Hook scripts:** none changed — `response-review.js` already sends topicId + transcriptPath and is always-overwritten anyway.
|
|
40
|
+
- **Job:** new template installs through the normal built-in job path (`installBuiltinJobs` refresh), `enabled:false` everywhere.
|
|
41
|
+
|
|
42
|
+
## Rollback
|
|
43
|
+
|
|
44
|
+
- `responseReview.conversationalContext.enabled: false` — context off, reviewers byte-identical to pre-spec behavior, applied at the NEXT evaluate through the liveConfig wiring (no restart; pinned by boundary 12 + the e2e on-disk flip test).
|
|
45
|
+
- The D8 JSONL is additive telemetry; deleting it loses history, breaks nothing. `observeOnly` remains the orthogonal enforcement lever.
|
|
46
|
+
|
|
47
|
+
## Tests
|
|
48
|
+
|
|
49
|
+
- **Tier 1 (unit, 71 tests):** `untrusted-conversation-context.test.ts` (12 — envelope/clamps/scrub/atomicity, boundaries 7-9), `conversation-context-wiring.test.ts` (8 — boundary 6 both sides incl. the uid-less fail-closed rule), `coherence-gate-conversation-context.test.ts` (12 — boundaries 1-5, 12, 14 + the §D9.2 veto-day regression fixtures pinned both directions), `response-review-decision-log.test.ts` (9 — boundary 10 + boundary 11 counterfactual both sides, incl. canary-tag exclusion), `review-canary-battery.test.ts` (16 — boundary 13: outcome table, refuse conditions, seed-then-cleanup contract, failed-outranks-inconclusive).
|
|
50
|
+
- **Tier 2 (integration, 8 tests):** full HTTP pipeline over the real AgentServer auth middleware + real TopicMemory SQLite — enveloped ask in the captured prompt, byte-identical no-context path, provider-throw → 200 never fail-open, `/review/history` carries contextMeta and never bodies, canary tag plumbing test-route-only, battery route Bearer-gated + live/dark.
|
|
51
|
+
- **Tier 3 (e2e, 6 tests):** production init path with the REAL LiveConfig + resolveDevAgentGate + TopicMemory + TopicOperatorStore — dev boot ALIVE (context section + verified-operator principal tag from a real binding + battery 200), on-disk kill-switch flip applies with no restart, fleet boot byte-identical + 503 (Maturation Path dark side), Bearer gating.
|
|
52
|
+
- Suite health: tsc clean; `npm run lint` clean; `check-repo-invariants` green; `no-silent-fallbacks` green (every new catch tagged with a real justification); docs-coverage class ratchet back at floor with the new architecture page.
|
|
53
|
+
|
|
54
|
+
## Post-rebase conformance addendum (2026-07-03, PR #1343)
|
|
55
|
+
|
|
56
|
+
Rebasing onto post-#1341/#1342 main put this build under two guards that did not exist at its commit ceremony; conforming produced two additional side effects:
|
|
57
|
+
|
|
58
|
+
1. **Write-domain classification (src/core/WriteDomainRegistry.ts):** `POST /review/canary-battery/run` is now classified `machine-local` with an `ephemeral-rebuildable` story (standby-write-reconciliation §3.5 ratchet). Runtime delta: under the (dev-gated, dry-run) WriteAdmission layer the route resolves to a domain instead of null — machine-local ⇒ admit everywhere, which matches reality (the battery exercises THIS machine's review pipeline; fixtures are finally-cleaned; the D8 log is per-machine soak evidence). NOT added to the TODO-classify baseline (the ratchet forbids growth).
|
|
59
|
+
2. **FileClassifier sync exclusions (src/core/FileClassifier.ts):** `.instar/topic-memory.db` and `logs/response-review-decisions.jsonl` are now `git-sync-excluded` — the I9 second-axis file-level arm, same pattern as the wave-1 evolution/attention entries. Behavior delta: if any writer ever queued these paths for git-sync auto-commit, they are now skipped with a DegradationReporter breadcrumb instead of swept into a commit. A live SQLite binary and a per-machine JSONL must never ride git-sync (the round-2 S1 lesson); no current code path queues either, so the practical fleet delta is zero.
|
|
60
|
+
3. **Job template header (src/scaffold/templates/jobs/instar/review-canary-battery.md):** the trigger curl now resolves `AGENT_ID` and sends `X-Instar-AgentId`, conforming to the template-agent-id-header standard (bearer-only localhost calls are deprecated). No behavior change beyond the header; the job still ships `enabled: false`.
|
|
61
|
+
|
|
62
|
+
Rollback for this addendum rides the parent levers: the registry entry and exclusions are inert data rows (removing them restores the prior null-classification / sync-eligible state); the template header is cosmetic to auth (the server accepts bearer-only with a deprecation log).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Side-Effects Review — Durable Conversation Identity, Increment 1 (registry + crash-proof journal + eager mint)
|
|
2
|
+
|
|
3
|
+
**Spec:** docs/specs/durable-conversation-identity.md (CONVERGED round 11 — 0 CRITICAL / 0 MAJOR; approved under the standing Session-A operator preapproval, topic 29836). **Parent:** Structure beats Willpower — durable identity must be a registry, not a convention three copies of a hash function remember.
|
|
4
|
+
**Behavior-identical for existing flows.** The foundation RECORDS identity (always-on, kill-switchable); DELIVERY stays dark-gated: `conversationIdentity.followThrough` omits `enabled` (the developmentAgent gate resolves it — live-on-dev, dark-fleet) with `dryRun: true` FIRST, exactly as spec §9 prescribes.
|
|
5
|
+
**Files:** src/core/conversationIdentity.ts (new), src/core/ConversationRegistry.ts (new), src/core/deliverToConversation.ts (new), src/server/routes.ts (read-only `GET /conversations*`), src/commands/server.ts (registry construction + adoption pass + §6.3 eager mint), src/core/types.ts + src/config/ConfigDefaults.ts (`conversationIdentity` block), src/core/devGatedFeatures.ts (followThrough registration), src/core/PostUpdateMigrator.ts (backup manifest + config defaults + CLAUDE.md), src/scaffold/templates.ts (Agent Awareness).
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
|
|
9
|
+
1. **conversationIdentity.ts (new):** the SINGLE hash + identity surface (§4). The frozen 32-bit sum-shift hash, the mint candidate `-(abs(hash)+1)` (golden-parity with `slackRoutingKeySyntheticId`), the v1 structured tuple `(platform, channelId, threadTs?)`, canonical-key/tupleKey forms, the frozen schema-v1 constants (`MAX_PROBE_DISTANCE=64`, probe DOWN, `HLC_ABS_MIN/MAX`), shape clamps, and `walkDisplacement` — the ONE shared displacement implementation §3.3 and the future §3.5.1 merge both call.
|
|
10
|
+
2. **ConversationRegistry.ts (new):** the sparse join table (canonical key ⇄ tuple ⇄ minted negative id). Synchronous in-memory assignment (probe included; returned == will-persist), the §3.4 WAL journal at the stateDir ROOT (`conversation-registry.jsonl` — the one glob shape the deployed `BackupManager.expandGlob` expands, R3-C4): probed/durable-binding mints append+fsync ONE line BEFORE the id returns; speculative non-probed mints ride the batched snapshot only. Torn-tail truncate-discard; NON-tail corruption fails CLOSED (quarantine-aside + attention + durability incident — R7-minor-3); UNKNOWN-op skip-and-preserve with snapshot-flush SUSPENSION (R8-minor-2/R9-M1/R10-M1) surfaced on health (R11-low-1); single global monotonic `seq` across rotations + restarts (R3-M14); rotation 8 MB/50k lines with fully-superseded-only pruning behind a 7-day retention floor; size-adaptive batched snapshot (2 s base → 60 s max; off-loop write past 20k entries/2 MB); snapshot completeness for bind-pins / ambiguous-sends / send-intents (R4-M2/R6-M1 — the op enum + replay ship NOW so later increments' records and rollbacks across them replay correctly; their WRITERS land with §6.1 steps 2+). Mint-rate breaker (per-channel windows, speculative drop-to-nowhere + durable-binding carve-out with its OWN cap → typed `conversation-registration-capacity`, adversarial-B), the D1 `recording.enabled` kill-switch (live-read; degraded = in-memory candidate + collision-checked read B6; minted-id durable binds refused typed), §3.1 workspace pinning (config pin authoritative; local-observed self-corroborating; per-machine `multi-workspace-unsupported` refusal; `_`→teamId in-place upgrade by the LOCAL authenticated adapter only), §6.2 authorized-traffic-gated adoption pass, and the §8 health surface.
|
|
11
|
+
3. **deliverToConversation.ts (new):** the §5 funnel SKELETON — typed, non-exceptional §5.1 results (dark/dryRun return the SAME `not-delivered` shape as unresolvable, plus a would-deliver audit line; NEVER success-shaped), `id>0` → today's Telegram path, `id<0` → local-origin-only resolution (KYP), system-channel suppression inside the funnel (§4), in-thread Slack delivery when live. ZERO consumers migrate in this increment; the E1 guard, P17 budgets, `deterministicKind` arm, and permanent-error classification land WITH their §6.1 increments (2/3/5) before any consumer rides the funnel.
|
|
12
|
+
4. **Read-only routes (§8):** `GET /conversations`, `/conversations/health` (the e2e alive target), `/conversations/resolve`, `/conversations/:id` — Bearer-gated, labels HTML-escaped on render (B3), no write routes (mint happens only at internal chokepoints — §7).
|
|
13
|
+
5. **§6.3 eager mint at Slack inbound:** the dispatch mints (get-or-create) the resolved routing key's conversation id on every inbound and carries it as the pinned `conversationId` field in message metadata + the session bootstrap context. Wrapped fail-toward-delivery: a mint degradation never blocks a message.
|
|
14
|
+
6. **Config/rollout (§9):** `conversationIdentity.recording.enabled: true` default (existence-checked in migrateConfig, NEVER materialized false — the #1001 mechanism); `followThrough.enabled` OMITTED + registered in DEV_GATED_FEATURES; `followThrough.dryRun: true`. PostUpdateMigrator adds the backup-manifest entries (`state/conversation-registry.json` + the top-level glob `conversation-registry.jsonl*`) and the CLAUDE.md Capabilities entry; `generateClaudeMd` carries it for new agents.
|
|
15
|
+
|
|
16
|
+
## Blast radius
|
|
17
|
+
|
|
18
|
+
- **Recording is additive observability.** No existing store changes shape; no store version bump; the registry file is inert data to old code (verified: zero old-code reads of `state/conversation-registry.json`). Rollback-by-revert for the code; `recording.enabled:false` is the runtime kill-switch that restores byte-identical legacy hashing without a redeploy.
|
|
19
|
+
- **Delivery cannot fire on the fleet.** The funnel has zero callers in this increment AND the `id<0` arm is dev-gated + dryRun-first; on a dev agent it returns typed non-deliveries with audit lines until a deliberate `dryRun:false` flip.
|
|
20
|
+
- **The eager mint adds one in-memory map hit per Slack inbound** (post-first-mint) plus, for a NEW conversation, one O(1) journal append (fsync only when probed). The O(N) snapshot is batched off the hot path — the CommitmentTracker freeze shape is structurally avoided (§3.4).
|
|
21
|
+
- **Hash copies are NOT rewired in this increment.** The three legacy copies keep computing the same values the registry records (value-identical by golden parity); the §4 callsite consolidation is the next increment. The mint-idiom grep ratchet pins the current copy set so a FOURTH copy is a CI failure.
|
|
22
|
+
|
|
23
|
+
## Risk + mitigation
|
|
24
|
+
|
|
25
|
+
- **Risk:** registry failure costs a message. **Mitigation:** every mint path is typed-degrading (unparseable key / breaker drop / recording-off / probe overflow → "no durable id" or a collision-checked read); the inbound dispatch wraps the mint in try/catch. Identity never costs a message (§3.6) — pinned by unit tests.
|
|
26
|
+
- **Risk:** a crash loses a probed/thread-level id a durable consumer bound to. **Mitigation:** the WAL rule — fsynced journal line BEFORE the id returns; replay restores it (pinned by the crash tests, no snapshot flush needed).
|
|
27
|
+
- **Risk:** journal corruption silently loses committed records. **Mitigation:** non-tail corruption HALTS replay into quarantine-aside + ONE attention item + a durability-incident record (the §3.7 SQLite-migration tripwire input) — never skip-and-continue.
|
|
28
|
+
- **Risk:** a rollback across a future op-enum extension trips the corruption machinery or composes wrongly. **Mitigation:** unknown-op skip-and-preserve + snapshot-flush suspension (the pre-skew snapshot stays put; prune keys on the static pre-skew watermark) — pinned by the suspension unit test.
|
|
29
|
+
- **Risk:** a mint flood grows the store unboundedly (auto-join, thread storms). **Mitigation:** the per-channel mint-rate breaker (speculative drops re-mint for free; durable carve-out has its own cap + typed refusal), the adoption pass's authorized-traffic gate (security-B8), and the 80%-of-ceiling health tripwire.
|
|
30
|
+
|
|
31
|
+
## Migration parity
|
|
32
|
+
|
|
33
|
+
- migrateConfig: adds `conversationIdentity.recording.enabled: true` ONLY when absent; NEVER writes `followThrough.enabled` or a literal `false` anywhere in the block (pinned by a unit test — the #1001 mechanism).
|
|
34
|
+
- Backup manifest: `state/conversation-registry.json` + top-level glob `conversation-registry.jsonl*` join `config.backup.includeFiles` via the existing idempotent set-union migrator (stateDir-relative; the Tier-2 test drives the REAL BackupManager and asserts the glob's expanded set lands in the snapshot — R3-C4).
|
|
35
|
+
- CLAUDE.md: `GET /conversations*` Capabilities entry via `migrateClaudeMd()` (existing agents) AND `generateClaudeMd()` (new agents) — P3 + P5 both.
|
|
36
|
+
|
|
37
|
+
## Rollback
|
|
38
|
+
|
|
39
|
+
- Runtime: `conversationIdentity.recording.enabled: false` (kill-switch — legacy-identical degradation); `followThrough` stays dark on the fleet by default. Full revert: delete the three new modules + the route block + the dispatch mint lines + the config/migrator entries; `state/conversation-registry.json` + `conversation-registry.jsonl*` are inert to old code. A later RE-enable needs no special path — the idempotent boot adoption pass + journal replay compose over whatever is on disk (R8-low-1).
|
|
40
|
+
|
|
41
|
+
## Tests
|
|
42
|
+
|
|
43
|
+
- `tests/unit/conversation-identity.test.ts` — golden parity with the legacy hash (channel + thread), frozen schema-v1 constants, tuple/key forms + shape clamps, the shared displacement walk + its MAX_PROBE_DISTANCE bound.
|
|
44
|
+
- `tests/unit/conversation-registry.test.ts` — mint idempotency (incl. across re-open), the crafted in-charset collision pair probing to DISTINCT ids in either order, WAL crash-durability (probed + durable-binding survive with NO snapshot flush; speculative writes no synchronous journal line), torn-tail discard, non-tail-corruption fail-closed, unknown-op suspension, rotation-spanning replay with one global seq, idempotent replay, snapshot completeness (bind-pins/ambiguous-sends/send-intents), alias one-hop + the boot assignment-beats-alias invariant, breaker (speculative drop + re-mint, durable carve-out + typed capacity refusal), recording kill-switch both arms, workspace pinning (self-corroboration, multi-workspace refusal, config-pin authority, `_`→teamId in-place upgrade), adoption-pass gating + idempotency, resolution surfaces, health shape.
|
|
45
|
+
- `tests/unit/deliver-to-conversation.test.ts` — §5.1 typed contract (dark/dry/unresolvable/replicated-only/system-channel/no-adapter/transport-error all non-exceptional, never success-shaped; dryRun audit line), in-thread delivery, Telegram passthrough.
|
|
46
|
+
- `tests/unit/conversation-identity-mint-idiom-ratchet.test.ts` — the §10 wiring-integrity grep ratchet (a fourth mint-idiom copy is a CI failure).
|
|
47
|
+
- Integration (`tests/integration/conversation-registry-routes.test.ts`) — the `GET /conversations*` HTTP pipeline (auth, 404 semantics, label escaping, resolve), migrateConfig never-materializes assertions, the backup-manifest-through-real-BackupManager test.
|
|
48
|
+
- E2E (`tests/e2e/conversation-registry-lifecycle.test.ts`) — Phase-1 "feature is alive": production-init wiring answers `GET /conversations/health` 200 (not 503), adoption pass state present, inbound→mint→resolve cycle.
|
|
49
|
+
|
|
50
|
+
## Agent awareness
|
|
51
|
+
|
|
52
|
+
- A "Durable Conversation Identity" Capabilities entry (GET /conversations*, the health surface, what a negative topicId means) ships in `generateClaudeMd` + an idempotent content-sniffed `migrateClaudeMd` section. <!-- tracked: durable-conversation-identity -->
|
|
53
|
+
|
|
54
|
+
## Part-2 landing note (wiring)
|
|
55
|
+
|
|
56
|
+
Part 1 landed the three core modules + Tier-1 tests. Part 2 (this commit)
|
|
57
|
+
lands the wiring: the read-only `GET /conversations*` routes (labels escaped —
|
|
58
|
+
the only Phase-1 render surface), the AgentServer plumbing (bootstrap instance
|
|
59
|
+
takes precedence; a read-only fallback keeps the health surface alive on any
|
|
60
|
+
init path — it can never become a second journal writer because eager mint
|
|
61
|
+
uses the bootstrap instance only), the server-bootstrap construction (live
|
|
62
|
+
kill-switch reads, late-bound workspace source, attention wiring), the §6.3
|
|
63
|
+
eager mint + bootstrap-context carry, the §6.2 adoption pass (session-registry
|
|
64
|
+
membership as the authorized-traffic record), config types/defaults +
|
|
65
|
+
DEV_GATED_FEATURES registration, the PostUpdateMigrator additions (backup
|
|
66
|
+
manifest, dev-gate strip, CLAUDE.md section + shadow markers), the scaffold
|
|
67
|
+
template section, and the Tier-2/Tier-3 tests + docs-coverage artifacts
|
|
68
|
+
(release fragment + architecture page).
|
|
69
|
+
|
|
70
|
+
## Part-3 landing note (surface classification + test parity)
|
|
71
|
+
|
|
72
|
+
CapabilityIndex gains the `/conversations` prefix classification (agent-facing
|
|
73
|
+
capability — surfaced in /capabilities discovery, not INTERNAL_PREFIXES), and
|
|
74
|
+
the backup-manifest parity test learns the two new manifest entries (the
|
|
75
|
+
predictable behavior-change-breaks-old-tests category, swept before push).
|
|
76
|
+
|
|
77
|
+
## Part-4 landing note (release-fragment polish)
|
|
78
|
+
|
|
79
|
+
The release fragment's "What to Tell Your User" line drops inline code
|
|
80
|
+
formatting (the pre-push gate's plain-conversational rule).
|
|
81
|
+
|
|
82
|
+
## Part-5 landing note (fail-safe tagging)
|
|
83
|
+
|
|
84
|
+
Every intentional fail-safe catch this increment introduced carries an
|
|
85
|
+
explicit `@silent-fallback-ok` tag with its safe-direction justification
|
|
86
|
+
(registry: fsync best-effort / prune-retains / workspace-degrades-to-
|
|
87
|
+
placeholder / shutdown teardown; wiring: fail-toward-delivery on boot-load,
|
|
88
|
+
eager mint, adoption pass, attention raise) — the no-silent-fallbacks ratchet
|
|
89
|
+
stays at its 491 baseline with zero untagged swallows from this PR.
|