instar 1.3.500 → 1.3.502
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 +943 -14
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +51 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +7 -0
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/DeliverMessageHandler.d.ts +13 -1
- package/dist/core/DeliverMessageHandler.d.ts.map +1 -1
- package/dist/core/DeliverMessageHandler.js +7 -0
- package/dist/core/DeliverMessageHandler.js.map +1 -1
- package/dist/core/MachinePoolRegistry.d.ts +3 -0
- package/dist/core/MachinePoolRegistry.d.ts.map +1 -1
- package/dist/core/MachinePoolRegistry.js +1 -0
- package/dist/core/MachinePoolRegistry.js.map +1 -1
- package/dist/core/OwnerSuspectBreaker.d.ts +37 -0
- package/dist/core/OwnerSuspectBreaker.d.ts.map +1 -1
- package/dist/core/OwnerSuspectBreaker.js +58 -0
- package/dist/core/OwnerSuspectBreaker.js.map +1 -1
- package/dist/core/PendingInboundStore.d.ts +310 -0
- package/dist/core/PendingInboundStore.d.ts.map +1 -0
- package/dist/core/PendingInboundStore.js +715 -0
- package/dist/core/PendingInboundStore.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +44 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/QueueDrainLoop.d.ts +247 -0
- package/dist/core/QueueDrainLoop.d.ts.map +1 -0
- package/dist/core/QueueDrainLoop.js +0 -0
- package/dist/core/QueueDrainLoop.js.map +1 -0
- package/dist/core/ReapGuard.d.ts +24 -0
- package/dist/core/ReapGuard.d.ts.map +1 -1
- package/dist/core/ReapGuard.js +62 -0
- package/dist/core/ReapGuard.js.map +1 -1
- package/dist/core/SessionManager.d.ts +32 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +74 -2
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SessionRouter.d.ts +36 -2
- package/dist/core/SessionRouter.d.ts.map +1 -1
- package/dist/core/SessionRouter.js +34 -9
- package/dist/core/SessionRouter.js.map +1 -1
- package/dist/core/WorkEvidence.d.ts +45 -0
- package/dist/core/WorkEvidence.d.ts.map +1 -0
- package/dist/core/WorkEvidence.js +88 -0
- package/dist/core/WorkEvidence.js.map +1 -0
- package/dist/core/componentCategories.d.ts.map +1 -1
- package/dist/core/componentCategories.js +3 -0
- package/dist/core/componentCategories.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +15 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/inboundQueueBootSweep.d.ts +61 -0
- package/dist/core/inboundQueueBootSweep.d.ts.map +1 -0
- package/dist/core/inboundQueueBootSweep.js +207 -0
- package/dist/core/inboundQueueBootSweep.js.map +1 -0
- package/dist/core/inboundQueueConfig.d.ts +78 -0
- package/dist/core/inboundQueueConfig.d.ts.map +1 -0
- package/dist/core/inboundQueueConfig.js +135 -0
- package/dist/core/inboundQueueConfig.js.map +1 -0
- package/dist/core/types.d.ts +73 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/messaging/NotificationBatcher.d.ts +12 -0
- package/dist/messaging/NotificationBatcher.d.ts.map +1 -1
- package/dist/messaging/NotificationBatcher.js +36 -0
- package/dist/messaging/NotificationBatcher.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +4 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +12 -0
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/pending-relay-store.d.ts +67 -8
- package/dist/messaging/pending-relay-store.d.ts.map +1 -1
- package/dist/messaging/pending-relay-store.js +151 -14
- package/dist/messaging/pending-relay-store.js.map +1 -1
- package/dist/messaging/reap-notice-delivery-id.d.ts +37 -0
- package/dist/messaging/reap-notice-delivery-id.d.ts.map +1 -0
- package/dist/messaging/reap-notice-delivery-id.js +52 -0
- package/dist/messaging/reap-notice-delivery-id.js.map +1 -0
- package/dist/monitoring/QuotaManager.d.ts +6 -0
- package/dist/monitoring/QuotaManager.d.ts.map +1 -1
- package/dist/monitoring/QuotaManager.js +11 -0
- package/dist/monitoring/QuotaManager.js.map +1 -1
- package/dist/monitoring/ReapLog.d.ts +39 -4
- package/dist/monitoring/ReapLog.d.ts.map +1 -1
- package/dist/monitoring/ReapLog.js +54 -4
- package/dist/monitoring/ReapLog.js.map +1 -1
- package/dist/monitoring/ReapNoticeDrain.d.ts +113 -0
- package/dist/monitoring/ReapNoticeDrain.d.ts.map +1 -0
- package/dist/monitoring/ReapNoticeDrain.js +231 -0
- package/dist/monitoring/ReapNoticeDrain.js.map +1 -0
- package/dist/monitoring/ReapNotifier.d.ts +89 -18
- package/dist/monitoring/ReapNotifier.d.ts.map +1 -1
- package/dist/monitoring/ReapNotifier.js +329 -22
- package/dist/monitoring/ReapNotifier.js.map +1 -1
- package/dist/monitoring/ResumeQueue.d.ts +223 -0
- package/dist/monitoring/ResumeQueue.d.ts.map +1 -0
- package/dist/monitoring/ResumeQueue.js +600 -0
- package/dist/monitoring/ResumeQueue.js.map +1 -0
- package/dist/monitoring/ResumeQueueDrainer.d.ts +151 -0
- package/dist/monitoring/ResumeQueueDrainer.d.ts.map +1 -0
- package/dist/monitoring/ResumeQueueDrainer.js +420 -0
- package/dist/monitoring/ResumeQueueDrainer.js.map +1 -0
- package/dist/monitoring/SessionMigrator.d.ts +10 -0
- package/dist/monitoring/SessionMigrator.d.ts.map +1 -1
- package/dist/monitoring/SessionMigrator.js +43 -1
- package/dist/monitoring/SessionMigrator.js.map +1 -1
- package/dist/monitoring/SessionReaper.d.ts +1 -0
- package/dist/monitoring/SessionReaper.d.ts.map +1 -1
- package/dist/monitoring/SessionReaper.js +5 -0
- package/dist/monitoring/SessionReaper.js.map +1 -1
- package/dist/monitoring/delivery-failure-sentinel.d.ts.map +1 -1
- package/dist/monitoring/delivery-failure-sentinel.js +7 -3
- package/dist/monitoring/delivery-failure-sentinel.js.map +1 -1
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +27 -0
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +10 -3
- 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 +4 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +12 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +119 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/lint-guard-manifest.js +2 -0
- package/src/data/builtin-manifest.json +66 -66
- package/src/data/state-coherence-registry.json +14 -1
- package/src/scaffold/templates.ts +10 -3
- package/upgrades/1.3.501.md +51 -0
- package/upgrades/1.3.502.md +75 -0
- package/upgrades/side-effects/1.3.492.md +20 -0
- package/upgrades/side-effects/durable-inbound-message-queue.md +213 -0
- package/upgrades/side-effects/reap-notify-resume-queue.md +89 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Implements `docs/specs/reap-notify-per-topic-and-midwork-resume-queue.md` (converged 7 iterations with GPT-5.5 + Gemini 2.5 Pro cross-review, operator-approved 2026-06-12, topic 24662 — "reaped sessions should ALWAYS notify the user in the corresponding topic, and mid-work reaps need a persistent resume mechanism").
|
|
9
|
+
|
|
10
|
+
**Part A — per-topic durable reap notices (default ON):**
|
|
11
|
+
- `ReapNotifier` v2: per-topic coalescing — every topic that lost a session gets ONE notice in THAT topic (plain-English reasons, mid-work tag); the lifeline gets unbound sessions + a cross-topic index. Affected-set tracking is separate from the detail buffer, so storms still produce correct per-topic counts. Rollback: `monitoring.reapNotify.perTopic: false`.
|
|
12
|
+
- Durable delivery: notices are `reap-notify:`-prefixed rows in the shared PendingRelayStore (release holds ride the existing `next_attempt_at` column — zero DDL), drained by a NEW always-on `ReapNoticeDrain` (30s tick, CAS claims, per-pass cap 15, backoff to 8 attempts, terminal escalation into ONE aggregated attention item). Independent of the default-OFF DeliveryFailureSentinel. Rollback: `monitoring.reapNotify.drainEnabled: false`.
|
|
13
|
+
- **Store-level fix for everyone (R1.6):** the pending-relay restore-purge cutoff is now `max(attempted_at, next_attempt_at)` with a 7-day corruption clamp — a message HELD for future release no longer gets eaten at boot (the 2026-06-05 silent-deletion class, fixed for ALL rows, not just reap notices).
|
|
14
|
+
- Outcome records: every notice attempt lands in the reap-log as `type:'notify'` pairs (`enqueued` → `sent`/`send-failed-escalated`/`no-topic`/`enqueue-failed`) — "did the user get told?" is now auditable.
|
|
15
|
+
|
|
16
|
+
**Part B — mid-work tagging + resume queue (ships enabled + dry-run observe-only):**
|
|
17
|
+
- Killers supply work evidence at THEIR decision point (the quota-shed migrator snapshots it BEFORE its Ctrl+C grace round tears the work down); the single kill chokepoint clamps it to an exact enum and stamps `midWork` on the event, the reap-log, and the session record.
|
|
18
|
+
- `ResumeQueue`: durable per-machine queue (fsync persist discipline, single-writer lockfile with same-host stale reclaim + foreign-host loud-disable, corrupt-file sidecar, boot reconciliation from the reap-log). Eligibility is stricter than midWork: ≥1 strong signal (or topic-bound + 2 distinct weak); jobs opt in via `resumeOnReap: true` (default false); operator kills excluded by default.
|
|
19
|
+
- `ResumeQueueDrainer`: revives AT MOST ONE entry per minute, only after 3 consecutive calm pressure ticks + quota headroom + session-cap + no migration in flight. Seven drain-time reality validations re-check the world before any spawn. Failure ladder: 3 attempts with backoff → gave-up; resurrection cap (2 per identity per 24h) kills kill-resume-kill loops; circuit breaker on consecutive failures. ALL give-up classes fold into ONE rolling attention item.
|
|
20
|
+
- Emergency stops reach the queue: stop-all/MessageSentinel stops pause it (TTLs frozen, mutation routes 409); per-topic stops cancel that topic's entries; explicit unpause via `POST /sessions/resume-queue/resume`.
|
|
21
|
+
- New spawn-path capability: sessions can now be spawned with an explicit per-spawn `cwd`, so interrupted worktree work resumes in ITS tree.
|
|
22
|
+
- Tier 1 supervision (observe-only during the soak): each about-to-resume decision gets an audited fast-tier LLM sanity check that never defers.
|
|
23
|
+
|
|
24
|
+
**Foundation fixes pulled in-scope (no deferrals):**
|
|
25
|
+
- `SessionMigrator` no longer records a ReapGuard REFUSAL as "halted" — refused-but-alive sessions are no longer double-respawned after a quota migration.
|
|
26
|
+
- The reap-log normalizer no longer strips `launchLane` on read.
|
|
27
|
+
|
|
28
|
+
**Migration parity:** ConfigDefaults gains `reapNotify.perTopic` + `maxImmediatePerFlush` (resumeQueue keys deliberately code-defaulted for the later fleet flip); new content-sniffed `migrateClaudeMd()` section + framework-shadow marker; `state/resume-queue.json` registered machine-local (excluded from backup/restore).
|
|
29
|
+
|
|
30
|
+
## What to Tell Your User
|
|
31
|
+
|
|
32
|
+
- "When the system shuts down your sessions (low memory, usage limits), each affected conversation now gets its own clear notice — and the notice can't be silently lost anymore, even across restarts."
|
|
33
|
+
- "Coming online in watch-mode: sessions killed mid-work get queued and automatically restarted one at a time once your machine recovers, picking the work back up."
|
|
34
|
+
|
|
35
|
+
## Summary of New Capabilities
|
|
36
|
+
|
|
37
|
+
| Capability | How to Use |
|
|
38
|
+
|-----------|-----------|
|
|
39
|
+
| Per-topic shutdown notices | Automatic — each affected topic is told, in plain English |
|
|
40
|
+
| Durable notice delivery | Automatic — `reap-notify` rows in the pending-relay store, always-on drain |
|
|
41
|
+
| Notice audit trail | `GET /sessions/reap-log` — `type:'notify'` outcome pairs |
|
|
42
|
+
| Mid-work tagging | reap-log + session records carry `midWork` + `workEvidence` |
|
|
43
|
+
| Resume queue state | `GET /sessions/resume-queue` (entries, paused, breaker, lastTickAt) |
|
|
44
|
+
| Queue levers | `POST /sessions/resume-queue/:id/cancel` · `/:id/requeue` · `/resume` · `/drain` |
|
|
45
|
+
| Job opt-in | `resumeOnReap: true` on a job definition |
|
|
46
|
+
|
|
47
|
+
## Evidence
|
|
48
|
+
|
|
49
|
+
- Tier-1: `tests/unit/pending-relay-store.test.ts` (R1.6 + origin scoping + CAS), `reap-log.test.ts`, `work-evidence.test.ts`, `session-migrator.test.ts` (pre-grace evidence + refusal recording), `reap-notifier.test.ts` (v2 matrix + legacy modes), `reap-notice-drain.test.ts`, `resume-queue.test.ts`, `resume-queue-drainer.test.ts`.
|
|
50
|
+
- Tier-2/3: resume-queue route integration + reap→durable-notify lifecycle + feature-alive E2E (same PR).
|
|
51
|
+
- Side-effects artifact: `upgrades/side-effects/reap-notify-resume-queue.md`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Added the Durable Inbound Message Queue + Hold-for-Stability policy (CMT-1118,
|
|
9
|
+
spec `docs/specs/durable-inbound-message-queue.md`, 10-round convergence). When
|
|
10
|
+
the multi-machine router cannot deliver an inbound message right now (its
|
|
11
|
+
conversation is mid-move, or the owning machine is briefly unhealthy), the
|
|
12
|
+
message now goes into a crash-proof on-disk queue (`PendingInboundStore`,
|
|
13
|
+
SQLite `synchronous=FULL`, 0600) instead of being injected into a possibly-stale
|
|
14
|
+
local session or relying on platform resend — and a suspect-but-alive machine
|
|
15
|
+
gets up to 90 seconds to recover before its conversation is moved (fewer
|
|
16
|
+
machine swaps on 5-second blips). The drain engine carries the full reviewed
|
|
17
|
+
safety set: receipt-before-inject at-most-once acting, transactional
|
|
18
|
+
emergency-stop fences, queued-rows-only pause with cumulative caps, tenure
|
|
19
|
+
clamps (acquisition-generation, never the renewal epoch), six boot-validated
|
|
20
|
+
config invariants (violation = queue stays OFF, loudly), P19 brakes with an
|
|
21
|
+
Eternal-Sentinel declared tick, and every loss window reported — never silent.
|
|
22
|
+
New Bearer route `GET /pool/queue` (503 while dark); capacity heartbeats gain
|
|
23
|
+
queue depth/tenure/top-K; guard manifest + dark-gate registrations included.
|
|
24
|
+
**Ships DARK**: `multiMachine.sessionPool.inboundQueue` defaults
|
|
25
|
+
`enabled:false, dryRun:true`; hold-for-stability trails one rollout stage.
|
|
26
|
+
|
|
27
|
+
## What to Tell Your User
|
|
28
|
+
|
|
29
|
+
Nothing changes today — this ships off by default. Once enabled (staged rollout,
|
|
30
|
+
dry-run first): messages that arrive while your agent's conversation is moving
|
|
31
|
+
between machines can no longer be lost or delivered to the wrong place — they
|
|
32
|
+
wait in a small crash-proof queue and deliver in order when the move finishes.
|
|
33
|
+
And a machine having a brief network blip gets ~90 seconds to recover before
|
|
34
|
+
its conversations are moved off it, so you'll see far fewer pointless machine
|
|
35
|
+
swaps. If anything ever can't be delivered, you get one plain notice naming
|
|
36
|
+
what to resend — never silence.
|
|
37
|
+
|
|
38
|
+
## Summary of New Capabilities
|
|
39
|
+
|
|
40
|
+
- `GET /pool/queue` — queue counts (queued/claimed/held/frozen, delivered24h
|
|
41
|
+
which excludes possibly-not-injected), durable counters (incl. dry-run
|
|
42
|
+
`wouldEnqueue`/`wouldHold` promotion evidence, `possiblyNotInjected`,
|
|
43
|
+
`holdBypassedByAttemptsCap`), hold/flap state, tenure. 503 while dark.
|
|
44
|
+
- Capacity heartbeats: `inboundQueue` block (depth, oldest, tenure, top-K) —
|
|
45
|
+
feeds the survivor loss-suspected item + capped session recovery when a
|
|
46
|
+
machine dies holding messages.
|
|
47
|
+
- `DeliverAck` gains the typed `sender-rejected` NACK (receive-side authz
|
|
48
|
+
re-validation of carried sender envelopes).
|
|
49
|
+
- Guard posture: `multiMachine.sessionPool.inboundQueue.enabled` +
|
|
50
|
+
`multiMachine.sessionPool.holdForStability.enabled` rows in `/guards`
|
|
51
|
+
(hold's getter reports the EFFECTIVE state — orphaned config derives
|
|
52
|
+
off-runtime-divergent).
|
|
53
|
+
|
|
54
|
+
## Evidence
|
|
55
|
+
|
|
56
|
+
- Unit: `pending-inbound-store.test.ts` 32/32 (tri-state, bounds + carve-out
|
|
57
|
+
boundary, AUTOINCREMENT across prunes, claim CAS, eviction-skips-claimed,
|
|
58
|
+
payload hygiene + locator retention, conditional receipt fence, tenure
|
|
59
|
+
derivation incl. A→B→A, cumulative pause accounting across restart, handle
|
|
60
|
+
encapsulation, 0600); `queue-drain-loop.test.ts` 35/35 (dispositions incl.
|
|
61
|
+
delivered-unconfirmed, stop-fence both interleavings, pause scope round-9,
|
|
62
|
+
hold budget across episodes, herd cap, maxHeldTotal degrade, maxAttempts
|
|
63
|
+
forced re-place both arms, tenure clamps, wake clamps, Eternal-Sentinel
|
|
64
|
+
episode latch, mirror reconciliation, P19 sustained-failure hour);
|
|
65
|
+
`inbound-queue-config.test.ts` 10/10 (all six invariants, defaults parity
|
|
66
|
+
with ConfigDefaults); `inbound-queue-boot-sweep.test.ts` 13/13 (gate-expiry
|
|
67
|
+
×4 reasons, crash-table rows 1–3, stop-scoped PIS veto, pause-aware
|
|
68
|
+
recovery, quarantine + expiry).
|
|
69
|
+
- Integration: `inbound-queue-route.test.ts` 3/3 (ships-dark 503, feature-alive
|
|
70
|
+
200 over real HTTP + real engine + real SQLite, delivered24h exclusion).
|
|
71
|
+
- E2E: `inbound-queue-lifecycle.test.ts` 2/2 (production-init order: sweep →
|
|
72
|
+
invariants → engine adopts swept store → drain → route truth; and the
|
|
73
|
+
production-default disabled boot: gate-expiry + 503).
|
|
74
|
+
- Lints: `lint-dev-agent-dark-gate` clean (both flags registered);
|
|
75
|
+
`lint-guard-manifest` clean (components listed).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Side-Effects Review — v1.3.492 (version-named pairing)
|
|
2
|
+
|
|
3
|
+
This is the version-named pairing for the v1.3.492 release, completing the
|
|
4
|
+
rename step the release cut is expected to perform alongside the
|
|
5
|
+
`NEXT.md → 1.3.492.md` guide rename (see `scripts/pre-push-gate.js` check 5:
|
|
6
|
+
"the fragment/NEXT.md -> <version>.md rename pairs with an artifact rename").
|
|
7
|
+
The release flow did not perform the artifact half of the pairing, which makes
|
|
8
|
+
check 5 false-positive on every docs-only push from a clean post-release tree.
|
|
9
|
+
|
|
10
|
+
The actual side-effects review for everything shipped in v1.3.492 is:
|
|
11
|
+
|
|
12
|
+
- `upgrades/side-effects/cartographer-sweep-eventloop-safety.md`
|
|
13
|
+
(cited by `upgrades/1.3.492.md` § Evidence — reviewer raised 2 material
|
|
14
|
+
concerns, both fixed before commit)
|
|
15
|
+
|
|
16
|
+
That artifact is left in place under its slug name because the release guide's
|
|
17
|
+
Evidence section cites it by that path. This file exists so the repo state
|
|
18
|
+
matches the gate's documented post-release expectation. The underlying gap
|
|
19
|
+
(release automation never renames artifacts) is logged to the framework-issues
|
|
20
|
+
ledger under dedupKey `pre-push-gate-versioned-artifact-fallback`.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Side-Effects Review — Durable Inbound Message Queue + Hold-for-Stability
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `durable-inbound-message-queue`
|
|
4
|
+
**Date:** `2026-06-12`
|
|
5
|
+
**Author:** `echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `independent reviewer subagent (required — gates dispatch + session lifecycle)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Implements the converged + approved spec `docs/specs/durable-inbound-message-queue.md`
|
|
11
|
+
(CMT-1118, 10 convergence rounds): when the session-pool router cannot deliver an
|
|
12
|
+
inbound message right now (`queued`/`placement-blocked` verdicts that today fall
|
|
13
|
+
through to possibly-wrong-place local dispatch), the message is taken into durable
|
|
14
|
+
SQLite custody (`PendingInboundStore`, `synchronous=FULL`, 0600) and a drain engine
|
|
15
|
+
(`QueueDrainLoop`) delivers it when the blockage clears — with hold-for-stability
|
|
16
|
+
giving a suspect-but-alive owner up to `holdMaxMs` (90s) before its conversation is
|
|
17
|
+
re-placed. Ships DARK (`multiMachine.sessionPool.inboundQueue` enabled:false +
|
|
18
|
+
dryRun:true; hold trails one stage). Files: new `src/core/PendingInboundStore.ts`,
|
|
19
|
+
`QueueDrainLoop.ts`, `inboundQueueConfig.ts`, `inboundQueueBootSweep.ts`; edits to
|
|
20
|
+
`SessionRouter.ts`, `OwnerSuspectBreaker.ts`, `DeliverMessageHandler.ts`,
|
|
21
|
+
`MachinePoolRegistry.ts`, `BackupManager.ts`, `ConfigDefaults.ts`,
|
|
22
|
+
`devGatedFeatures.ts`, `guardManifest.ts`, `types.ts`, `TelegramAdapter.ts`,
|
|
23
|
+
`PostUpdateMigrator.ts`, `templates.ts`, `routes.ts`, `AgentServer.ts`,
|
|
24
|
+
`commands/server.ts`, `scripts/lint-guard-manifest.js`; four new test files
|
|
25
|
+
(unit ×4, integration ×1, e2e ×1).
|
|
26
|
+
|
|
27
|
+
## Decision-point inventory
|
|
28
|
+
|
|
29
|
+
- `SessionRouter.queueMessage` — modify — no-op → tri-state custody taking; router
|
|
30
|
+
sets `acked` only for `queued`/`already-queued` (refused keeps today's un-acked
|
|
31
|
+
fall-through).
|
|
32
|
+
- `Consumption site (server.ts onTopicMessage route consult)` — modify — adds the
|
|
33
|
+
ordering gate, the custody-ack short-circuit, and the custody-aware route-throw
|
|
34
|
+
point read; all three no-op when the engine is null (dark).
|
|
35
|
+
- `OwnerHoldVerdict (new)` — add — hold/failover/deliver at every
|
|
36
|
+
`placeAndClaim('failover')` site; always-`failover` injected when the policy is
|
|
37
|
+
off/dry-run (§4.2 effective-state honesty, /guards getter on the unconditional
|
|
38
|
+
boot path).
|
|
39
|
+
- `DeliverMessageHandler.validateSender` — add — receive-side authz re-validation;
|
|
40
|
+
typed non-retryable `sender-rejected` NACK; only consulted when an envelope is
|
|
41
|
+
present (old peers unaffected).
|
|
42
|
+
- `Emergency-stop custody settle (TelegramAdapter.onSentinelStopCustody)` — add —
|
|
43
|
+
stop transitions queued custody terminal + PIS cleanup; pause is engine-level
|
|
44
|
+
(queued-rows-only freeze) and currently API-driven only (the sentinel 'pause'
|
|
45
|
+
category still Escape-keys the session as before — unchanged behavior).
|
|
46
|
+
- `Boot sweep (runInboundQueueBootSweep)` — add — unconditional boot path, before
|
|
47
|
+
`recoverPendingInjects`; gate-expires custody with named reasons; quarantines a
|
|
48
|
+
corrupt store; vetoes PIS records for operator-stop rows ONLY.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 1. Over-block
|
|
53
|
+
|
|
54
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
55
|
+
|
|
56
|
+
The enqueue path can `refuse` legitimate messages at the caps (`maxPerSession` 50 /
|
|
57
|
+
`maxTotal` 500 / `hardMaxTotal` 1000 / oversize >64KB) — but a refusal is NEVER a
|
|
58
|
+
block: it maps to `acked:false` → today's exact local-dispatch fall-through, so the
|
|
59
|
+
worst case for a legitimate message is the pre-feature behavior (delivery, possibly
|
|
60
|
+
wrong-place, counted in `orderingViolations`). The `sender-rejected` NACK can reject
|
|
61
|
+
a sender deauthorized on the receiving machine but still authorized on the sender's
|
|
62
|
+
— per-machine registry divergence during a deauthorization window; the spec chose
|
|
63
|
+
the conservative side (the receive-side registry wins), the entry terminals
|
|
64
|
+
loss-REPORTED, and an old peer (no envelope) is never affected. The config-seam
|
|
65
|
+
validation refuses to START the queue on a violated invariant — deliberately
|
|
66
|
+
over-blocking the feature itself (OFF = today's behavior) rather than running
|
|
67
|
+
half-configured.
|
|
68
|
+
|
|
69
|
+
## 2. Under-block
|
|
70
|
+
|
|
71
|
+
**What failure modes does this still miss?**
|
|
72
|
+
|
|
73
|
+
The spec enumerates these as named windows: loss windows 1–6 (machine disk death;
|
|
74
|
+
crash instants around the receipt; restore-to-new-machine silent forfeit) and
|
|
75
|
+
duplicate windows 1–5 (PIS at-least-once replay instant; route-throw point-read
|
|
76
|
+
error fail-open; version-skew receipt boundary). None are silent — each is reported
|
|
77
|
+
or explicitly accepted in the spec with bounds. The hold verdict cannot detect a
|
|
78
|
+
hard-crashed machine faster than the heartbeat staleness window (one hold cycle of
|
|
79
|
+
added latency, named in spec §4.2). The §5.1 survivor arm only sees peers whose
|
|
80
|
+
last heartbeat carried depth fields (old/mesh-less peers are honestly unknown).
|
|
81
|
+
|
|
82
|
+
## 3. Level-of-abstraction fit
|
|
83
|
+
|
|
84
|
+
The custody store sits at the router verdict layer — the exact seam where the
|
|
85
|
+
no-op `queueMessage` already existed; it does not duplicate the ingress
|
|
86
|
+
exactly-once ledger (bypassed by design for drained entries — the receipt class is
|
|
87
|
+
the drain's own authority) nor the PendingRelayStore (outbound). The hold policy
|
|
88
|
+
feeds the EXISTING OwnerSuspectBreaker signals rather than adding a parallel
|
|
89
|
+
detector. The boot sweep lives on the unconditional boot path because the gated-off
|
|
90
|
+
states are exactly the ones whose components never construct (round-2/round-4
|
|
91
|
+
findings). No higher-layer gate exists that this should feed instead.
|
|
92
|
+
|
|
93
|
+
## 4. Signal vs authority compliance
|
|
94
|
+
|
|
95
|
+
Every decision is a deterministic policy evaluation over enumerable inputs (route
|
|
96
|
+
outcome enum, heartbeat liveness, breaker state, row state) — no brittle
|
|
97
|
+
pattern-matching holds blocking authority (`docs/signal-vs-authority.md`). The
|
|
98
|
+
fail direction is open-to-today's-behavior everywhere: storage failure → refused →
|
|
99
|
+
fall-through; point-read error → fall-through; invariant violation → queue OFF.
|
|
100
|
+
The flap detector and loss reports are signal-only (attention items, counters).
|
|
101
|
+
The one new blocking-shaped surface — `sender-rejected` — is an authz re-check
|
|
102
|
+
against the durable users registry (existing authority, new enforcement point),
|
|
103
|
+
not a new heuristic. Spec §Supervision documents the Tier-0
|
|
104
|
+
deterministic-evaluator carve-out; the 10-round convergence (including the
|
|
105
|
+
lessons-aware reviewer) audited exactly this question.
|
|
106
|
+
|
|
107
|
+
## 5. Interactions
|
|
108
|
+
|
|
109
|
+
- The ingress exactly-once ledger: custody COMPLETES the ledger row (the spec's
|
|
110
|
+
§2.2 lifecycle); drained entries bypass the ledger gate by design — the
|
|
111
|
+
injection receipt is a DISTINCT class, so no double-fire.
|
|
112
|
+
- PendingInjectStore: receipt-first ordering pinned (round-7); stop deletes PIS
|
|
113
|
+
records; the boot sweep runs BEFORE `recoverPendingInjects` and vetoes
|
|
114
|
+
operator-stop records only — the pause case deliberately does NOT veto.
|
|
115
|
+
- OwnerSuspectBreaker: `onClose` is additive; `recordSuccess` semantics unchanged.
|
|
116
|
+
- The reaper/respawn machinery: the drain reuses `respawnSessionForTopic` /
|
|
117
|
+
`spawnSessionForTopic` WITH the `spawningTopics` guard, so drain and live spawns
|
|
118
|
+
cannot race a duplicate spawn.
|
|
119
|
+
- Telegram dedup/confirmations: drain deliveries suppress the per-message
|
|
120
|
+
"✓ Delivered" (the §3.1 contract) — no confirmation flood.
|
|
121
|
+
- Double-dispatch: the custody-ack short-circuit only fires when the enqueue
|
|
122
|
+
COMMITTED (acked), and the route-throw catch point-reads the store — the
|
|
123
|
+
enumerated residual is a point-read ERROR failing open (bounded duplicate,
|
|
124
|
+
§5-enumerated).
|
|
125
|
+
|
|
126
|
+
## 6. External surfaces
|
|
127
|
+
|
|
128
|
+
- New Bearer-gated route `GET /pool/queue` (503 while dark). No payload content is
|
|
129
|
+
ever served — counts, counters, tenure only.
|
|
130
|
+
- Mesh `deliverMessage` envelope gains optional `senderEnvelope`; `DeliverAck`
|
|
131
|
+
gains `'sender-rejected'`. Old peers: never emit the new ack value, ignore the
|
|
132
|
+
new field — both skew directions named in the spec.
|
|
133
|
+
- Capacity heartbeat gains the `inboundQueue` block (depth/oldest/tenure/topK) —
|
|
134
|
+
additive, absent = unknown on old peers.
|
|
135
|
+
- CLAUDE.md template + `migrateClaudeMd` section (Agent Awareness + Migration
|
|
136
|
+
Parity); ConfigDefaults delivers the dark config blocks to existing agents via
|
|
137
|
+
the existing add-missing recursion (verified by test).
|
|
138
|
+
- The store file is excluded from backups via the unconditional
|
|
139
|
+
`BLOCKED_PATH_PREFIXES` (restoring custody to a new machine would claim
|
|
140
|
+
messages it never held).
|
|
141
|
+
- Timing/runtime dependence: drain behavior depends on heartbeat freshness and the
|
|
142
|
+
breaker's suspect windows — exactly the dependencies the spec's hold-budget and
|
|
143
|
+
clamp bounds were designed around.
|
|
144
|
+
|
|
145
|
+
## 7. Rollback cost
|
|
146
|
+
|
|
147
|
+
Feature is dark by default; nothing changes on deploy until an operator advances
|
|
148
|
+
the rollout. If wrong in production: flip `inboundQueue.enabled:false` (or
|
|
149
|
+
`dryRun:true`) + normal restart — the boot sweep gate-expires any residual custody
|
|
150
|
+
with named, loss-reported reasons (no silent stranding; flag semantics are
|
|
151
|
+
boot-read by design, spec §5.3). No data migration: the SQLite store is created on
|
|
152
|
+
first use and quarantine/prune paths bound its lifetime. The OFF-state is
|
|
153
|
+
byte-for-byte today's behavior except the one-shot residual sweep — verified by
|
|
154
|
+
the gate × behavior matrix tests. Worst-case backout is a hot-fix release deleting
|
|
155
|
+
the wiring; no agent state repair is ever needed (rows are per-machine,
|
|
156
|
+
self-expiring, backup-excluded).
|
|
157
|
+
|
|
158
|
+
## Known deliberate simplifications (named, not hidden)
|
|
159
|
+
|
|
160
|
+
- The §3.1 "extraction" is implemented as a drain-specific tail
|
|
161
|
+
(`_drainLocalDeliver`) built from the same primitives as the live tail rather
|
|
162
|
+
than relocating the live tail's 150 lines into a shared function — the live
|
|
163
|
+
hot path is byte-untouched (zero regression risk), at the cost of two parallel
|
|
164
|
+
tails to keep in sync. The drain tail enumerates every contract divergence in
|
|
165
|
+
its comment block.
|
|
166
|
+
- The sentinel 'pause' category keeps its existing behavior (Escape key); the
|
|
167
|
+
engine's pause/freeze surface (`onPause`/`onResume`) is fully built + tested
|
|
168
|
+
but not yet bound to a user-facing trigger — binding it is a UX decision the
|
|
169
|
+
operator should make with live dry-run data (the spec's §3.6 pause semantics
|
|
170
|
+
are about WHEN frozen rows behave correctly, which is what ships).
|
|
171
|
+
- `custodyDurability` reports `'unknown'` (no fsync probe in v1) — the spec's
|
|
172
|
+
round-8 field shipped as an honest placeholder rather than a guessed value.
|
|
173
|
+
- The §5.1 survivor re-placement uses empty-payload synthetic messages through
|
|
174
|
+
`SessionRouter.forceReplace` (the router path, honoring pins + CAS) — "session
|
|
175
|
+
recovery without message replay", exactly the spec's framing.
|
|
176
|
+
|
|
177
|
+
## Known deliberate simplifications — addendum after second-pass review
|
|
178
|
+
|
|
179
|
+
- Lease-acquisition tenure events: `onLeaseAcquired` is called once at engine
|
|
180
|
+
construction (and `handoffInProgress` is unwired); per-acquisition tenure
|
|
181
|
+
bumps + the §3.5 ordered-handoff enqueue gate are pre-promotion work, bounded
|
|
182
|
+
meanwhile by the cross-boot clamp (`boot_session_id`) and the now-wired REAL
|
|
183
|
+
`holdsLease` gate (a non-holder takes no custody at all, which closes the
|
|
184
|
+
§2.2 exposure the reviewer named). <!-- tracked: CMT-1118 (rollout-criteria: dark→dev-dry-run gate; must land before dev-live promotion) -->
|
|
185
|
+
|
|
186
|
+
## Second-pass review
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
**Independent second-pass review (reviewer subagent, 2026-06-12):**
|
|
191
|
+
|
|
192
|
+
Verified against the implementation before raising concerns: MUST 1/3/4/6/7/9/10/11/12 are real — the conditional receipt is one transaction gated on the row still being `claimed` (`src/core/PendingInboundStore.ts`), the drain tail commits the receipt before the PIS record/inject and re-checks stop after it (`src/commands/server.ts` drain tail), the six config-seam invariants are checked at construction and a violation keeps the queue OFF with one loud error per inequality + an attention item, and the boot sweep runs on the unconditional path before `recoverPendingInjects`. The Q1/Q4 fail-open directions are real in code: storage throw → `refused`, null engine → `'refused'`, point-read error → fall-through. Dark-ship holds: all three consumption-site edits are inside the `_sessionRouter && _sessionPoolStage() !== 'dark'` block and additionally null-guarded on `_inboundQueue`; with the default config the engine is never constructed, `/pool/queue` 503s, and the only OFF-state delta is the named boot sweep (a no-op when no store file exists). `sender-rejected` is a registry re-check (existing authority), consulted only when an envelope is present; the hold verdict is a deterministic policy over enumerated inputs — no new brittle blocking authority. Backup exclusion, dev-gate registration, guard manifest, ConfigDefaults, migrator, and the test files all exist as claimed.
|
|
193
|
+
|
|
194
|
+
**Concern raised: the `no-mesh-identity` gate reason is never produced — a fully-enabled queue on a machine without mesh identity strands custody silently.** [...] produce the named gate-expiry (or fold the identity check into the sweep's gate) before live promotion.
|
|
195
|
+
|
|
196
|
+
**Concern raised: the dry-run rollout stage is structurally invisible — its evidence path is dead code.** [...] Either construct the engine in dry-run mode (it never claims custody by design) or name this in "Known deliberate simplifications".
|
|
197
|
+
|
|
198
|
+
**Concern raised: the lease gate is hardwired open and this simplification is not named.** [...] before live promotion either wire the real serving-lease signal (it exists — `leaseCoordinatorRef`) or add this to "Known deliberate simplifications".
|
|
199
|
+
|
|
200
|
+
**Concern raised (minor, latent): MUST 2's no-throw-after-commit is convention, not structure, at the engine layer.** [...] scoping the try to the `store.enqueue` call alone would make the invariant structural. No code change demanded for this dark ship; fix opportunistically.
|
|
201
|
+
|
|
202
|
+
Disposition: the dark-ship contract, fail-open directions, custody/receipt core, and signal-vs-authority compliance all check out in code. The four concerns are pre-promotion obligations (1–3) and a hardening note (4), not dark-deploy blockers.
|
|
203
|
+
|
|
204
|
+
**Author response (iterated per Phase 5, same session):** all four concerns ADDRESSED in code before commit —
|
|
205
|
+
1. no-mesh-identity: a one-shot 90s orphan-store backstop at the sweep site gate-expires + loss-reports custody whenever the swept store is never adopted by an engine (covers identity-missing AND construction-throw paths) — `server.ts` sweep block.
|
|
206
|
+
2. dry-run visibility: the engine now constructs in dry-run too (`qcfg.enabled` alone gates construction); the §2.4 counters accumulate and `/pool/queue` serves them — the promotion evidence is live.
|
|
207
|
+
3. lease gate: `holdsLease` wired to `leaseCoordinatorRef.holdsLease()` (single-machine no-coordinator defaults true); the remaining per-acquisition tenure-event wiring is the named tracked simplification above.
|
|
208
|
+
4. MUST 2: the refusal-mapping try now covers ONLY the store commit; post-commit bookkeeping throws are logged and can no longer convert a committed enqueue into `refused`.
|
|
209
|
+
|
|
210
|
+
**Post-merge addendum (same session):** marked the queue's three defensive
|
|
211
|
+
catches with the in-brace `@silent-fallback-ok` convention (Eternal-Sentinel
|
|
212
|
+
episode latch; loud-log quarantine failure; normal no-quarantine-dir case) —
|
|
213
|
+
no behavior change; restores no-silent-fallbacks headroom after main moved.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Side-Effects Review — Per-Topic Reap Notification + Mid-Work Resume Queue
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `reap-notify-resume-queue`
|
|
4
|
+
**Date:** `2026-06-12`
|
|
5
|
+
**Author:** `echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `[pending — required: session lifecycle + sentinel surface]`
|
|
7
|
+
|
|
8
|
+
> STATUS: IN-FLIGHT — this artifact accompanies the per-step commits of the
|
|
9
|
+
> build and is completed (all seven questions answered, second-pass appended)
|
|
10
|
+
> before the PR opens. Driven by the converged + approved spec
|
|
11
|
+
> `docs/specs/reap-notify-per-topic-and-midwork-resume-queue.md` (r7).
|
|
12
|
+
|
|
13
|
+
## Summary of the change
|
|
14
|
+
|
|
15
|
+
Implements the reap-notify + resume-queue spec: Part A makes every non-silent
|
|
16
|
+
session reap produce a durable per-topic notice (PendingRelayStore rows with a
|
|
17
|
+
`reap-notify:` PK prefix, drained by a new always-on ReapNoticeDrain; the
|
|
18
|
+
store's restore-purge gains the R1.6 held-row exemption + 7-day corruption
|
|
19
|
+
clamp), and Part B tags mid-work reaps with killer-supplied evidence at the
|
|
20
|
+
terminateSession chokepoint and revives them in order via a durable
|
|
21
|
+
ResumeQueue + gated ResumeQueueDrainer (observe-only Tier 1 LLM check during
|
|
22
|
+
soak). Files: src/messaging/{pending-relay-store,reap-notice-delivery-id}.ts,
|
|
23
|
+
src/monitoring/{delivery-failure-sentinel,ReapNotifier,ReapNoticeDrain,ReapLog,
|
|
24
|
+
ResumeQueue,ResumeQueueDrainer,PressureGauge,SessionMigrator,SessionReaper}.ts,
|
|
25
|
+
src/core/SessionManager.ts, server wiring + routes, ConfigDefaults,
|
|
26
|
+
PostUpdateMigrator, templates, three test tiers.
|
|
27
|
+
|
|
28
|
+
## Decision-point inventory
|
|
29
|
+
|
|
30
|
+
- `PendingRelayStore.purgeStaleClaimable` — modify — restore-purge staleness predicate (R1.6); brittle by design (transport-layer mechanics, not judgment).
|
|
31
|
+
- `PendingRelayStore claim queries (selectClaimable / selectClaimableReapNotices / claimCas)` — modify/add — origin-scoped single-owner contract between two drains; transport-layer mechanics.
|
|
32
|
+
- `ReapNotifier flush` — modify — per-topic grouping + release-tier selection (IMMEDIATE vs SUMMARY vs quiet-hours); deterministic template authoring, no judgment blocking.
|
|
33
|
+
- `ReapNoticeDrain` — add — tier0 deterministic delivery state machine (claim → send → backoff → terminal escalation).
|
|
34
|
+
- `terminateSession evidence clamp` — modify — enum whitelist on killer-supplied evidence (hard-invariant validation, brittle-blocker exemption).
|
|
35
|
+
- `ResumeQueue eligibility classifier` — add — deterministic eligibility rules (strong/weak evidence, job opt-in).
|
|
36
|
+
- `ResumeQueueDrainer gates` — add — deterministic spawn-eligibility checks delegating to EXISTING authorities (PressureGauge, QuotaManager.canSpawnSession, session cap, migration-in-flight); plus observe-only Tier 1 LLM check.
|
|
37
|
+
- `Dequeue hard invariants` — add — UUID/enum/charset/length clamps protecting `claude --resume` argv (brittle-blocker exemption).
|
|
38
|
+
- `Emergency-stop → queue pause` — add — pass-through consumer of the existing MessageSentinel/stop-all authority.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 1. Over-block
|
|
43
|
+
|
|
44
|
+
[IN-FLIGHT — completed at Phase 4 after build.]
|
|
45
|
+
|
|
46
|
+
## 2. Under-block
|
|
47
|
+
|
|
48
|
+
[IN-FLIGHT — completed at Phase 4 after build.]
|
|
49
|
+
|
|
50
|
+
## 3. Level-of-abstraction fit
|
|
51
|
+
|
|
52
|
+
[IN-FLIGHT — completed at Phase 4 after build.]
|
|
53
|
+
|
|
54
|
+
## 4. Signal vs authority compliance
|
|
55
|
+
|
|
56
|
+
[IN-FLIGHT — completed at Phase 4 after build. Phase 1 written check recorded
|
|
57
|
+
in build plan: no new brittle judgment blockers; drainer gates delegate to
|
|
58
|
+
existing authorities; Tier 1 check observe-only; hard invariants under the
|
|
59
|
+
documented exemption.]
|
|
60
|
+
|
|
61
|
+
## 5. Interactions
|
|
62
|
+
|
|
63
|
+
[IN-FLIGHT — completed at Phase 4 after build.]
|
|
64
|
+
|
|
65
|
+
## 6. External surfaces
|
|
66
|
+
|
|
67
|
+
[IN-FLIGHT — completed at Phase 4 after build.]
|
|
68
|
+
|
|
69
|
+
## 7. Rollback cost
|
|
70
|
+
|
|
71
|
+
[IN-FLIGHT — completed at Phase 4 after build. Levers: reapNotify.perTopic=false,
|
|
72
|
+
reapNotify.drainEnabled=false, resumeQueue.enabled=false / dryRun=true; no DDL.]
|
|
73
|
+
|
|
74
|
+
## Build progress notes (per-step, folded into the final review)
|
|
75
|
+
|
|
76
|
+
- Step 1 (relay-store foundation): R1.6 purge fix + origin scoping + CAS claim; DFS spec §3h updated.
|
|
77
|
+
- Step 2 (ReapLog): notify record pairs + midWork/workEvidence through the normalizer; fixed pre-existing launchLane drop-on-read (Rule-1 deviation, noted for SUMMARY).
|
|
78
|
+
- Step 3 (evidence chokepoint): WorkEvidence vocabulary module + terminateSession opts.workEvidence with enum clamp + ReapGuard.workEvidence() observe-only fallback (closure-error → nothing; critical-tier marker) + midWork stamped on event/reap-log/session record.
|
|
79
|
+
- Step 4 (killer stamps): SessionMigrator pre-grace evidence snapshot + halt-refusal recording (refusals ≠ halted, no double-respawn); SessionReaper asserts authoritative-empty evidence on proven-idle reaps; chokepoint fallback excludes active-process under bypassActiveProcessKeep.
|
|
80
|
+
- Step 5 (ReapNotifier v2): per-topic grouping with separate affected-set (cap 500 + overflow), plain-English reason map, IMMEDIATE/SUMMARY release tiers with quiet-hours holds + per-flush cap, durable enqueue with outcome records, loud enqueue-failure fallback, legacy modes preserved (perTopic:false byte-compatible; drainEnabled:false direct-send).
|
|
81
|
+
- Step 6 (ReapNoticeDrain): always-on 30s drain over the reap-notify lane (CAS claim, lease + prior-boot reclaim, per-pass cap 15, backoff to maxAttempts 8, terminal escalation into ONE stable-id attention item, bounded terminal-row cleanup [Rule-2 deviation: added because DFS's retention pass is default-OFF, so the always-on lane needed its own growth bound]); boot wiring with lazy telegram refs; NotificationBatcher gains quietHoursEndAt/nextSummaryReleaseAt (one quiet-hours clock).
|
|
82
|
+
- Step 7 (PressureGauge): the spec's "shared PressureGauge extraction" already exists as HostPressureSampler (one shared definition delegating to SessionReaper.computePressure, parity-tested in host-pressure-sampler.test.ts) — plan deviation Rule 4: reused instead of duplicating. Wired sessionManager.setPressureTierProvider to it with the reaper's configured thresholds.
|
|
83
|
+
- Step 8 (ResumeQueue): durable JSON queue (fsync discipline temp→rename→dir), single-writer lockfile (same-host stale reclaim; foreign-host NEVER probed — loud disable with documented recovery), R2.2 eligibility classifier, stable-key dedupe, resurrection ledger with 24h window + requeue-as-the-one-override, incident-age TTL with pause-freeze + pressure-starved marker, overflow drop-to-aggregate, corrupt-file sidecar, boot reconciliation (starting→failed-attempt + reap-log re-enqueue seam). Dry-run interpretation recorded: entries ARE durably enqueued (soak observable); only spawning + user-facing claims are gated.
|
|
84
|
+
|
|
85
|
+
- Step 9 (ResumeQueueDrainer + spawn cwd): gated one-per-tick drainer (calm-ticks on the shared gauge, quota/cap/migration gates never bypassable, manual drain skips calm-ticks ONLY), 7 reality validations + hard invariants (fail-safe on dep errors), failure ladder + breaker + aggregated give-ups, Tier1 observe-only with 5s deadline + off-lever, dry-run inertness (would-resume once per entry, no attention), R2.11 honest notice + literal-data continuation prompt; spawnInteractiveSession + spawnSessionForTopic gain the explicit per-spawn cwd (L13 named extension).
|
|
86
|
+
- Step 10 (boot wiring + emergency stop + routes): queue+drainer constructed at boot (lock-disable logged, lazy telegram refs, 30s-deferred reap-log boot reconciliation — topic-bound candidates only; job entries rely on cron recurrence since opt-in is not reconstructible from the reap-log); enqueue hook at the sessionReaped listener; emergency-stop wiring (stop-all → pause; per-topic stop + MessageSentinel stop → cancelByTopic + operator-stop record); R2.10 routes with clamps; QuotaManager.isMigrationInFlight added (canSpawnSession permits mid-migration spawns absent pressure — the drainer needs the stricter probe); Session.cwd recorded at spawn; JobDefinition.resumeOnReap; Tier1 check wired via shared LlmQueue with attribution.
|
|
87
|
+
- Step 11 (config + migration parity): ConfigDefaults gains reapNotify.perTopic + maxImmediatePerFlush ONLY (drainEnabled + resumeQueue.* deliberately code-defaulted for the fleet flip — registration-discipline test pins this); resumeQueue classified in DARK_GATE_EXCLUSIONS (cost-bearing); resume-queue state files registered machine-local in the state-coherence registry; generateClaudeMd Reap-Log section updated + NEW content-sniffed migrateClaudeMd section + framework-shadow marker; release fragment with maturity-tagged announcements (Part A stable, Part B preview/watch-mode).
|
|
88
|
+
- Step 12 (test tier): integration lifecycle (reap → durable notify pair, quota-shed end-to-end, P17/P19 burst invariants incl. K-entries-vs-rejecting-spawn-target) + R2.10 routes/emergency-stop integration + Tier-3 feature-alive E2E on the production init path (real AgentServer + ReapLog + PendingRelayStore + ReapNotifier + ReapNoticeDrain + ResumeQueue + ResumeQueueDrainer; cwd round-trip through the L13 spawn-path parameter; wiring integrity for pressure gauge / spawn gate / relay store / LlmQueue tier-1).
|
|
89
|
+
- Step 13 (full-suite triage): ratchet/parity updates for the new code (dark-gate golden map +10 shift re-verified by hand; ResumeQueueDrainer registered in componentCategories; migrator CLAUDE.md section tracked in feature-delivery-completeness; session-reaper exact-match assertions gain workEvidence). Behavior-relevant: THREE lenient-side catches REMOVED so a throwing dep resolves to the drainer's strict side per R2.6 — QuotaManager.isMigrationInFlight no longer swallows probe errors (sole caller is the drainer gate, which blocks on throw), and the server's migrationInFlight/topicOwnerElsewhere dep wrappers no longer catch (drainer gate/validateReality resolve to blocked/invalidated). Every remaining new catch carries an in-brace @silent-fallback-ok justification; no-silent-fallbacks baseline untouched (net −1 vs main). localhost-link-guard-route test made hermetic (own tmp stateDir — '/tmp' shared outbound-dedup.db across runs and suppressed sends).
|