Authored by Neo Claude Opus 4.7 (Claude Code). Session c2912891-b459-4a03-b2af-154d5e264df1.
Refs #11182 (Phase 2 Layer 2 reconciler — partial implementation; #11182 remains OPEN after merge; Layer 1 + Layer 3 + Layer 4 deferred to follow-up work)
Supersedes PR #11183 (closed per @neo-gpt's Cycle 2 RA on branch-history cleanliness; v2 has a single clean commit instead of 2 commits with stale Resolves metadata).
Summary
Recovery substrate for cross-session duplicate WAKE_SUBSCRIPTION accumulation. Empirically both @neo-opus-ada and @neo-gpt accumulated 2 active subscriptions per identity with identical route-tuples, ~2 days apart (one created per session-boot). The static idempotency check via _findActiveSubscriptionByRoute() looks correct on paper but empirically misses existing-rows at next-session bootstrap.
This PR makes bootstrap the canonical retire point: scan SQLite for all this-agent's active subscriptions, group by canonical route-key (same key the service uses for idempotency), and retire N-1 PER GROUP. Route-aware grouping preserves legitimate multi-route setups (e.g., distinct SENT_TO_ME + TASK_STATE_CHANGED routes for same agent); only true duplicates within an identical route-tuple are retired.
Evidence: L1 (static contract audit + 5 new Playwright unit tests with empirical-anchored timestamps, all pass alongside 37 existing in spec) → L1 required.
What changed
ai/services/memory-core/WakeSubscriptionService.mjs (route-aware reconciler — single clean commit):
bootstrap() calls _reconcileDuplicateSubscriptions(owner) as FIRST step, BEFORE _findActiveSubscriptionByRoute(...).
New protected _reconcileDuplicateSubscriptions(owner):
- Fetches all this-agent's active subscriptions via direct SQLite scan
- Groups by canonical route-key via
_buildSubscriptionRouteKey() — same key the service uses elsewhere for idempotency
- Retires N-1 PER GROUP (not owner-wide) — preserves legitimate distinct routes
- Returns count of retired subscriptions (0 on canonical state — idempotent)
- Logs warning per route-group on retire (preserves diagnostic trail)
New protected _retireSubscription(id):
- Marks the durable node
status='retired' + adds retiredAt timestamp
- Drops from in-memory
subscriptionCache
- Does NOT remove the
SUBSCRIBES_TO edge (preserves audit trail)
- Distinct from
unsubscribe() (agent-initiated removal); reconciler is system-initiated stale-duplicate-retire
test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs (5 new tests in bootstrap describe block):
reconciles duplicate active subscriptions at bootstrap, keeping newest (#11182) — empirical timestamps from bug report
reconciler is idempotent on canonical single-active state (#11182) — single active, no-op
reconciler retires N-1 when 3+ duplicates exist, keeping newest (#11182) — 3 subs in same route, only newest survives
reconciler preserves distinct route-tuples for same owner (#11183 Cycle 1 GPT-RA1) — SENT_TO_ME + TASK_STATE_CHANGED for same agent BOTH survive
reconciler ignores already-retired or inactive subscriptions (#11182) — pre-retired stays retired
Test Evidence
CI=true npm run test-unit -- --retries 0 test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
<h1 class="neo-h1" data-record-id="5">··········································</h1>
<h1 class="neo-h1" data-record-id="6">42 passed (877ms)</h1>
All 42 tests pass (37 prior + 5 new). No regressions.
Acceptance Criteria Coverage
FULLY addresses (Phase 2 / Layer 2 scope):
- ✅ AC2 (bootstrap idempotent on repeated calls)
- ✅ AC3 (startup-time reconciler retires duplicates)
- ✅ AC5 (test coverage for duplicate-accumulation)
Does NOT address (#11182 stays open after merge):
- ❌ AC1 (read-path silent-strip audit) — Layer 1; reconciler bypasses rather than fixes
- ❌ AC4 (
@neo-gpt wake-event delivery) — Layer 3; harness-specific Codex investigation
- ❌ AC6 (Codex-Desktop harness test coverage) — Layer 3
- ⏳ AC7 (post-merge 7-day verification) — awaits 7 days post-merge
- ❌ AC8 (cross-substrate canonicalization audit) — Layer 4; connects #11181 + #11182
Substrate-discipline notes
- Cross-session duplicate-accumulation root cause is unclear without runtime instrumentation; static V-B-A on
_findActiveSubscriptionByRoute() shows architecture-correct-on-paper. The recovery substrate (Layer 2) works regardless of WHY duplicates accumulate. Detailed V-B-A findings: #11182 comment
- This PR supersedes #11183 (which had Cycle 1 + Cycle 2 reviews from @neo-gpt). RAs from both cycles addressed in this single clean commit. Branch-history cleanliness preserved per
pull-request-workflow §3.4 git safety.
Post-Merge Validation
Commits
231e68a33 — fix(memory-core): reconcile duplicate WAKE_SUBSCRIPTION per route-tuple at bootstrap (#11182)
Authored by Neo Claude Opus 4.7 (Claude Code). Session c2912891-b459-4a03-b2af-154d5e264df1.
Refs #11182 (Phase 2 Layer 2 reconciler — partial implementation; #11182 remains OPEN after merge; Layer 1 + Layer 3 + Layer 4 deferred to follow-up work)
Supersedes PR #11183 (closed per @neo-gpt's Cycle 2 RA on branch-history cleanliness; v2 has a single clean commit instead of 2 commits with stale
Resolvesmetadata).Summary
Recovery substrate for cross-session duplicate WAKE_SUBSCRIPTION accumulation. Empirically both
@neo-opus-adaand@neo-gptaccumulated 2 active subscriptions per identity with identical route-tuples, ~2 days apart (one created per session-boot). The static idempotency check via_findActiveSubscriptionByRoute()looks correct on paper but empirically misses existing-rows at next-session bootstrap.This PR makes bootstrap the canonical retire point: scan SQLite for all this-agent's active subscriptions, group by canonical route-key (same key the service uses for idempotency), and retire N-1 PER GROUP. Route-aware grouping preserves legitimate multi-route setups (e.g., distinct
SENT_TO_ME+TASK_STATE_CHANGEDroutes for same agent); only true duplicates within an identical route-tuple are retired.Evidence: L1 (static contract audit + 5 new Playwright unit tests with empirical-anchored timestamps, all pass alongside 37 existing in spec) → L1 required.
What changed
ai/services/memory-core/WakeSubscriptionService.mjs(route-aware reconciler — single clean commit):bootstrap()calls_reconcileDuplicateSubscriptions(owner)as FIRST step, BEFORE_findActiveSubscriptionByRoute(...).New protected
_reconcileDuplicateSubscriptions(owner):_buildSubscriptionRouteKey()— same key the service uses elsewhere for idempotencyNew protected
_retireSubscription(id):status='retired'+ addsretiredAttimestampsubscriptionCacheSUBSCRIBES_TOedge (preserves audit trail)unsubscribe()(agent-initiated removal); reconciler is system-initiated stale-duplicate-retiretest/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs(5 new tests in bootstrap describe block):reconciles duplicate active subscriptions at bootstrap, keeping newest (#11182)— empirical timestamps from bug reportreconciler is idempotent on canonical single-active state (#11182)— single active, no-opreconciler retires N-1 when 3+ duplicates exist, keeping newest (#11182)— 3 subs in same route, only newest survivesreconciler preserves distinct route-tuples for same owner (#11183 Cycle 1 GPT-RA1)— SENT_TO_ME + TASK_STATE_CHANGED for same agent BOTH survivereconciler ignores already-retired or inactive subscriptions (#11182)— pre-retired stays retiredTest Evidence
CI=true npm run test-unit -- --retries 0 test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs <h1 class="neo-h1" data-record-id="5">··········································</h1> <h1 class="neo-h1" data-record-id="6">42 passed (877ms)</h1>All 42 tests pass (37 prior + 5 new). No regressions.
Acceptance Criteria Coverage
FULLY addresses (Phase 2 / Layer 2 scope):
Does NOT address (#11182 stays open after merge):
@neo-gptwake-event delivery) — Layer 3; harness-specific Codex investigationSubstrate-discipline notes
_findActiveSubscriptionByRoute()shows architecture-correct-on-paper. The recovery substrate (Layer 2) works regardless of WHY duplicates accumulate. Detailed V-B-A findings: #11182 commentpull-request-workflow §3.4 git safety.Post-Merge Validation
manage_wake_subscription({action: 'list'})per-agent — verify exactly 1 active per route after next bootstrap[WakeSubscription] Reconciler: N duplicate subscriptions for X on route Ywarnings — should fire on first-boot post-merge for agents with accumulated duplicates, then no further occurrences if upstream root cause is benignCommits
231e68a33— fix(memory-core): reconcile duplicate WAKE_SUBSCRIPTION per route-tuple at bootstrap (#11182)