Context
Successor to closed PR #10933 (Phase 3 unit-row re-add). The 1-line workflow change (suite: [integration] → suite: [unit, integration] in .github/workflows/test.yml) is mechanical, but the unit suite's pre-existing instability under workers:1 substrate makes re-enabling it premature.
PR #10933 went through 5 commit cycles trying to converge:
7e19cd354 — workflow-line change (initial)
8e6c3fd78 — test-body skip-guards (fired too late vs beforeAll/beforeEach)
746fa8aea — beforeEach + Database describe-scope guards (partial)
42522ec08 — FileSystemIngestor describe-scope guard (1 flake-set swapped for another)
2e23dcaf7 — Path B: removed close+null from FileSystemIngestor.afterAll (root-cause fix at one producer)
5cf8e99ac — Path B extended to Server.spec + GraphService.spec afterAlls
Each push surfaced new failures from the same root-cause class: spec files that close+null the singleton SQLite cascade into init failures across every consumer under workers:1. The c.5cf8e99ac extension broke GraphService.spec's own tests because line 642 inside a test also nulls the singleton, and the afterAll's clear-only no longer compensates.
The Problem
The unit suite has structural state-pollution under workers:1 substrate. Multiple specs (Server.spec, GraphService.spec, WakeSubscriptionService.spec, FileSystemIngestor.spec, plus probably more) intentionally close+null the GraphService singleton for their own test isolation. The SDK's lazy re-init breaks once the singleton is null. Sibling consumers fail.
Skip-guarding consumers is whack-a-mole. Producer-side close-removal cascades into the producer's own tests (because they rely on close+immediate-re-init for fresh state). Neither approach converges.
The Architectural Reality
@neo-gemini-3-1-pro is independently working on TestLifecycleHelper in branch agent/sqlite-hardening (per A2A 2026-05-08 06:51) — a unified cleanup substrate for SQLite/singleton lifecycle that addresses the destroy-without-re-init pattern at the helper layer rather than per-spec. That's the proper scope for the structural fix.
The Fix
Three-stage handoff:
Substrate stabilization (Gemini's lane): complete TestLifecycleHelper on agent/sqlite-hardening — unified spec lifecycle helper that handles destroy+re-init paired correctly under workers:1.
Migrate the close-singleton specs to TestLifecycleHelper (one PR per spec OR bundled): Server.spec, GraphService.spec, WakeSubscriptionService.spec, FileSystemIngestor.spec. Each gets its lifecycle migrated to use the helper's destroy+re-init contract.
Re-add unit matrix row (this ticket's deliverable): once the unit suite is structurally stable, re-file the workflow-line change as a clean single-commit PR. Should be exit-code-0 on first run.
Acceptance Criteria
Out of Scope
- Re-attempting the workflow-line change with skip-guards-only (whack-a-mole approach already proven non-convergent across 5 commits)
- Migrating to
workers: undefined for unit suite (would mask the underlying state-pollution bugs; substrate gate exists for a reason)
- Per-test database instances (architectural change beyond AC scope)
Avoided Traps
- Skip-guard whack-a-mole: rejected because every push surfaces new flake from the same root cause; doesn't converge
- Producer-side close-removal alone: rejected because producer specs rely on close+immediate-re-init for their own test isolation
- Closing PR #10933 without filing successor: rejected because the work needs visible tracking; this ticket IS the tracker
- Reusing PR #10933 branch for v2: rejected per @tobiu's "fragmented intermediate insecure states" framing in
feedback_quality_over_speed; new PR cleaner once substrate stabilizes
Related
- Predecessor PR (closed): #10933 (5 commits of attempted convergence; lessons captured in PR body iteration retro)
- Substrate dependency: @neo-gemini-3-1-pro's
agent/sqlite-hardening branch (TestLifecycleHelper)
- Lane C scaffolding: #10897 (Phase 3 still owed; this ticket completes it)
- Sibling sub-tickets (stay open under #10924): #10934 (FileSystemIngestor), #10935 (TransportService residual), #10936 (KBRecorderService), #10937 (PermissionService AGENT:* pollution), #10938 (Database.spec G6)
- Bucket G epic: #10924 (close ledger amended to include this ticket)
MX Lessons captured (substrate-discipline retro)
PR #10933's iteration cycle produced two saved feedback memories:
feedback_lead_role_decision_thresholds: when operator delegates lead, EXECUTE on options I myself recommended; don't loop back with another A/B/C menu
- (forthcoming)
feedback_substrate_scope_restraint: when iterative skip-guards or producer-fixes don't converge after 2-3 cycles, the substrate work is bigger than the originating PR's scope; choose Drop+Supersede over continued iteration
The 8h cost @tobiu flagged this morning was the empirical anchor for the lead-role memory; this ticket's filing IS the empirical application of the scope-restraint memory.
Origin Session ID: 7e897a0b-33ce-4d6c-b1a9-a1ff93e4e571
Retrieval Hint: query_raw_memories(query="unit matrix row workers 1 TestLifecycleHelper Phase 3 #10897 PR 10933 successor scope-restraint")
Context
Successor to closed PR #10933 (Phase 3 unit-row re-add). The 1-line workflow change (
suite: [integration]→suite: [unit, integration]in.github/workflows/test.yml) is mechanical, but the unit suite's pre-existing instability underworkers:1substrate makes re-enabling it premature.PR #10933 went through 5 commit cycles trying to converge:
7e19cd354— workflow-line change (initial)8e6c3fd78— test-body skip-guards (fired too late vs beforeAll/beforeEach)746fa8aea— beforeEach + Database describe-scope guards (partial)42522ec08— FileSystemIngestor describe-scope guard (1 flake-set swapped for another)2e23dcaf7— Path B: removed close+null from FileSystemIngestor.afterAll (root-cause fix at one producer)5cf8e99ac— Path B extended to Server.spec + GraphService.spec afterAllsEach push surfaced new failures from the same root-cause class: spec files that close+null the singleton SQLite cascade into init failures across every consumer under
workers:1. The c.5cf8e99acextension broke GraphService.spec's own tests because line 642 inside a test also nulls the singleton, and the afterAll's clear-only no longer compensates.The Problem
The unit suite has structural state-pollution under
workers:1substrate. Multiple specs (Server.spec,GraphService.spec,WakeSubscriptionService.spec,FileSystemIngestor.spec, plus probably more) intentionally close+null the GraphService singleton for their own test isolation. The SDK's lazy re-init breaks once the singleton is null. Sibling consumers fail.Skip-guarding consumers is whack-a-mole. Producer-side close-removal cascades into the producer's own tests (because they rely on close+immediate-re-init for fresh state). Neither approach converges.
The Architectural Reality
@neo-gemini-3-1-pro is independently working on
TestLifecycleHelperin branchagent/sqlite-hardening(per A2A 2026-05-08 06:51) — a unified cleanup substrate for SQLite/singleton lifecycle that addresses the destroy-without-re-init pattern at the helper layer rather than per-spec. That's the proper scope for the structural fix.The Fix
Three-stage handoff:
Substrate stabilization (Gemini's lane): complete
TestLifecycleHelperonagent/sqlite-hardening— unified spec lifecycle helper that handles destroy+re-init paired correctly underworkers:1.Migrate the close-singleton specs to TestLifecycleHelper (one PR per spec OR bundled):
Server.spec,GraphService.spec,WakeSubscriptionService.spec,FileSystemIngestor.spec. Each gets its lifecycle migrated to use the helper's destroy+re-init contract.Re-add unit matrix row (this ticket's deliverable): once the unit suite is structurally stable, re-file the workflow-line change as a clean single-commit PR. Should be exit-code-0 on first run.
Acceptance Criteria
TestLifecycleHelperlands via Gemini'sagent/sqlite-hardeningbranchTestLifecycleHelperlifecycleWORKERS=1 npm run test-unit(matches CI substrate)unitGitHub Actions check passes on first runOut of Scope
workers: undefinedfor unit suite (would mask the underlying state-pollution bugs; substrate gate exists for a reason)Avoided Traps
feedback_quality_over_speed; new PR cleaner once substrate stabilizesRelated
agent/sqlite-hardeningbranch (TestLifecycleHelper)MX Lessons captured (substrate-discipline retro)
PR #10933's iteration cycle produced two saved feedback memories:
feedback_lead_role_decision_thresholds: when operator delegates lead, EXECUTE on options I myself recommended; don't loop back with another A/B/C menufeedback_substrate_scope_restraint: when iterative skip-guards or producer-fixes don't converge after 2-3 cycles, the substrate work is bigger than the originating PR's scope; choose Drop+Supersede over continued iterationThe 8h cost @tobiu flagged this morning was the empirical anchor for the lead-role memory; this ticket's filing IS the empirical application of the scope-restraint memory.
Origin Session ID:
7e897a0b-33ce-4d6c-b1a9-a1ff93e4e571Retrieval Hint:
query_raw_memories(query="unit matrix row workers 1 TestLifecycleHelper Phase 3 #10897 PR 10933 successor scope-restraint")