Parent Epic
#11831 — Sub 15 of Epic. First of the 3 coordinator extractions in Discussion #11857 Cycle-3.5 B-prime convergence.
Premise
Per v13-path.md:117 D3.1 boundary: "per-task coordinators decide 'what work is due'; Orchestrator wires; ProcessSupervisor executes." DreamService currently has its scheduling logic interleaved in Orchestrator's scheduleDreamCycle() (~30 LOC inside the 942-LOC monolith). Pre-existing M4-shape coordinators (SummarizationCoordinatorService, BackupCoordinatorService, PrimaryRepoSyncService) own their due-trigger selection via getDueTask({...}). DreamService should match.
Prescription
Create ai/daemons/orchestrator/services/DreamCoordinatorService.mjs with a single public surface:
getDueTask({state, now, dreamIntervalMs, ...}) → trigger | null
Matches SummarizationCoordinatorService.getDueTask({db, state, now, summarySweepIntervalMs, ...}) precedent shape (see ai/daemons/orchestrator/services/SummarizationCoordinatorService.mjs).
Strict boundary:
- Coordinator owns due-trigger selection ONLY
DreamService.processUndigestedSessions() (and other execution-side methods) stay in DreamService — coordinator does NOT absorb them
- No state writes / no
markStarted/markCompleted/recordTaskOutcome calls — those stay with Orchestrator + ProcessSupervisorService
Acceptance Criteria
ai/daemons/orchestrator/services/DreamCoordinatorService.mjs ships with getDueTask({...}) only
- Pure-function unit tests for
getDueTask({...}) with synthetic state (no Orchestrator instance needed)
DreamService retains processUndigestedSessions() + all execution-side methods unchanged
- Orchestrator's
scheduleDreamCycle() body is NOT modified yet (Sub 18 wires the new coordinator into the registry); this Sub only ships the extracted service
- No new env vars / config keys / behavior changes — pure extraction
Avoided Traps
- Coordinator MUST NOT absorb
DreamService processing methods
- No reactive-config-aliased "wrapper" around
DreamService (Sub-1 anti-pattern at new layer)
- No premature integration into Orchestrator (Sub 18 owns the wire-up)
- Stays poorer than a mini-framework — just a single
getDueTask({...}) pure function
Depends on
Epic #11831 (parent).
Unblocks
Sub 18 (wire-up needs all 3 coordinators registered).
Authority
Discussion #11857 Cycle-3.5 GPT [GRADUATION_APPROVED] (DC_kwDODSospM4BA-...); operator architectural ratify in-session.
Authored by: [Claude Opus 4.7] (Claude Code)
Parent Epic
#11831 — Sub 15 of Epic. First of the 3 coordinator extractions in Discussion #11857 Cycle-3.5 B-prime convergence.
Premise
Per
v13-path.md:117D3.1 boundary: "per-task coordinators decide 'what work is due'; Orchestrator wires; ProcessSupervisor executes."DreamServicecurrently has its scheduling logic interleaved in Orchestrator'sscheduleDreamCycle()(~30 LOC inside the 942-LOC monolith). Pre-existing M4-shape coordinators (SummarizationCoordinatorService,BackupCoordinatorService,PrimaryRepoSyncService) own their due-trigger selection viagetDueTask({...}). DreamService should match.Prescription
Create
ai/daemons/orchestrator/services/DreamCoordinatorService.mjswith a single public surface:getDueTask({state, now, dreamIntervalMs, ...}) → trigger | nullMatches
SummarizationCoordinatorService.getDueTask({db, state, now, summarySweepIntervalMs, ...})precedent shape (seeai/daemons/orchestrator/services/SummarizationCoordinatorService.mjs).Strict boundary:
DreamService.processUndigestedSessions()(and other execution-side methods) stay inDreamService— coordinator does NOT absorb themmarkStarted/markCompleted/recordTaskOutcomecalls — those stay with Orchestrator + ProcessSupervisorServiceAcceptance Criteria
ai/daemons/orchestrator/services/DreamCoordinatorService.mjsships withgetDueTask({...})onlygetDueTask({...})with synthetic state (no Orchestrator instance needed)DreamServiceretainsprocessUndigestedSessions()+ all execution-side methods unchangedscheduleDreamCycle()body is NOT modified yet (Sub 18 wires the new coordinator into the registry); this Sub only ships the extracted serviceAvoided Traps
DreamServiceprocessing methodsDreamService(Sub-1 anti-pattern at new layer)getDueTask({...})pure functionDepends on
Epic #11831 (parent).
Unblocks
Sub 18 (wire-up needs all 3 coordinators registered).
Authority
Discussion #11857 Cycle-3.5 GPT
[GRADUATION_APPROVED](DC_kwDODSospM4BA-...); operator architectural ratify in-session.Authored by: [Claude Opus 4.7] (Claude Code)