LearnNewsExamplesServices
Frontmatter
id11864
titleSub 20: Harmonize existing Coordinator services to scheduling/.mjs pure-function pattern\_ \_
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-ada
createdAtMay 23, 2026, 10:39 PM
updatedAtMay 25, 2026, 7:26 AM
githubUrlhttps://github.com/neomjs/neo/issues/11864
authorneo-opus-ada
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 25, 2026, 7:26 AM

Sub 20: Harmonize existing Coordinator services to scheduling/.mjs pure-function pattern

Closed v13.0.0/archive-v13-0-0-chunk-13 enhancementairefactoringarchitecture
neo-opus-ada
neo-opus-ada commented on May 23, 2026, 10:39 PM

Parent Epic

#11831 — Sub 20 of Epic. Consistency follow-up after Sub 15 (#11858 / PR #11863) established the scheduling/<task>.mjs pure-function pattern. Without this Sub, the existing 3 services remain class-instances while Subs 15/16/17 land as pure functions — Sub 18 (#11862) wire-up would need to handle two consumer shapes.

Premise

Sub 15 (PR #11863) discovered through 6 iterations that the orchestrator-internal due-trigger projections should be pure functions in scheduling/<task>.mjs, NOT class-based services. Three existing services match the wrong-shape precedent I copied initially:

  • ai/daemons/orchestrator/services/SummarizationCoordinatorService.mjs — class extends Base + singleton: true + Neo.setupClass, but the class has no reactive configs, no lifecycle, no Neo class-system features beyond namespace registration. Just wraps buildSummaryTrigger pure function + getDueTask adapter
  • ai/daemons/orchestrator/services/BackupCoordinatorService.mjs — same anti-pattern
  • ai/daemons/orchestrator/services/PrimaryRepoSyncService.mjs — partially: getDueTask is the projection (should move to scheduling/); runTask may be genuinely service-shaped (state + execution) and stay in services/ — verify per-file

Operator's KISS framing: complexity belongs where it's earned. These services use zero Neo class-system features and should drop the machinery.

Prescription

For each of the 3:

  1. SummarizationCoordinatorService.mjs → scheduling/summary.mjs:

    • Move buildSummaryTrigger (pure function — genuinely earned, has 2 sources + handover-priority branching) + getDueTask({db, state, now, summarySweepIntervalMs, getUnreadSunsetHandoversFn, markNodesAsReadFn, log}) to scheduling/summary.mjs
    • Drop class + singleton + Neo.setupClass
    • Move spec to test/.../scheduling/summary.spec.mjs
    • Delete services/SummarizationCoordinatorService.mjs
  2. BackupCoordinatorService.mjs → scheduling/backup.mjs:

    • Same shape — move getDueTask to pure function
    • Move spec, delete services/ file
  3. PrimaryRepoSyncService.mjs split (verify shape first):

    • If runTask has genuine state/execution responsibilities, keep services/PrimaryRepoSyncService.mjs for just runTask; move getDueTask to scheduling/primaryDevSync.mjs
    • If runTask is also a thin wrapper, fold both into scheduling/primaryDevSync.mjs and delete services/ file

Acceptance Criteria

  1. ai/daemons/orchestrator/scheduling/summary.mjs + backup.mjs + primaryDevSync.mjs ship as pure-function modules
  2. Each spec moves to test/.../scheduling/<task>.spec.mjs with Coordinator/Service naming dropped
  3. Orchestrator's imports update: import {getDueTask as summaryGetDueTask} from './scheduling/summary.mjs' etc. (currently the orchestrator uses the singleton-method-call shape SummarizationCoordinatorService.getDueTask({...}))
  4. Delete services/SummarizationCoordinatorService.mjs + services/BackupCoordinatorService.mjs (and services/PrimaryRepoSyncService.mjs if fully migrated; trim if only runTask remains)
  5. All existing unit tests for the migrated services pass at the new locations + new shapes
  6. No JSDoc decay-prone references (no line numbers, no section labels, no neighboring-file method names — per Sub 15 lesson)

Avoided Traps

  • DON'T just rename without dropping the class machinery. The point is removing extends Base + singleton: true + Neo.setupClass, not just relocating
  • DON'T preserve Neo.gatekeep registration "for tooling discoverability" — pure-function modules don't need namespace registration; consumers import directly
  • DON'T fold all 3 into one file. Per-task files preserve parallel-reviewability + match the precedent Sub 15 established
  • DON'T add JSDoc cross-references that decay (per Sub 15 final lesson)

Depends on

Epic #11831. Sub 15 #11858 (precedent — merged via PR #11863).

Unblocks

Sub 18 #11862 — Sub 18 can now consume ALL coordinators via the same import shape (import {getDueTask as X} from './scheduling/X.mjs') instead of needing branched logic for class-instances vs pure functions.

Authority

Operator KISS framing in #11863 merge feedback: complexity belongs where it's earned; the 3 existing services use zero Neo class-system features and should drop the machinery for consistency with Sub 15's pure-function pattern.

Authored by: [Claude Opus 4.7] (Claude Code)

tobiu referenced in commit 7210fba - "feat(agentos): batch — extract goldenPath + harmonize summary/backup/primaryDevSync to scheduling/ (#11860 #11864) (#11866) on May 23, 2026, 11:04 PM
tobiu changed title from Sub 20: Harmonize existing Coordinator services to scheduling/<task>.mjs pure-function pattern to **Sub 20: Harmonize existing Coordinator services to scheduling/.mjs pure-function pattern   ** on May 24, 2026, 2:32 PM
tobiu closed this issue on May 25, 2026, 7:26 AM
tobiu referenced in commit 9836060 - "refactor(agentos): harmonize 3 Coordinator services to scheduling/.mjs (#11864) (#11941) on May 25, 2026, 7:26 AM