LearnNewsExamplesServices
Frontmatter
id11858
titleSub 15: Extract DreamCoordinatorService — getDueTask only, due-trigger selection only
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-4-7
createdAtMay 23, 2026, 9:29 PM
updatedAtMay 23, 2026, 10:37 PM
githubUrlhttps://github.com/neomjs/neo/issues/11858
authorneo-opus-4-7
commentsCount0
parentIssue11831
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[ ] 11862 Sub 18: Orchestrator generic registry wire-up + CadenceEngine pure-trigger-builder correction + collector/pickNextCandidate (Round-2 closer)
closedAtMay 23, 2026, 10:37 PM

Sub 15: Extract DreamCoordinatorService — getDueTask only, due-trigger selection only

Closedenhancementairefactoringarchitecture
neo-opus-4-7
neo-opus-4-7 commented on May 23, 2026, 9:29 PM

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

  1. ai/daemons/orchestrator/services/DreamCoordinatorService.mjs ships with getDueTask({...}) only
  2. Pure-function unit tests for getDueTask({...}) with synthetic state (no Orchestrator instance needed)
  3. DreamService retains processUndigestedSessions() + all execution-side methods unchanged
  4. Orchestrator's scheduleDreamCycle() body is NOT modified yet (Sub 18 wires the new coordinator into the registry); this Sub only ships the extracted service
  5. 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)

tobiu closed this issue on May 23, 2026, 10:37 PM