Parent Epic
#11831 — Sub 18 of Epic. Round-2 closer per Discussion #11857 Cycle-3.5 B-prime convergence.
Premise
After Subs 15-17 (coordinator extractions) + Sub 19 (MaintenanceBackpressureService), the remaining surface in Orchestrator.mjs is mostly the poll() body + 7 hand-coded scheduling functions (§ I, lines 772-942, ~171 LOC). Sub 18 replaces those with a generic registry + pure collectDueCandidates + pure pickNextCandidate pipeline. Also corrects the CadenceEngine.runIfDue(... executeFn ...) drift per v13-path.md:117 / #11025 pure-trigger-builder spec. Also folds Sub-13's intra-file cleanup (anti-pattern JSDoc strip + collapse remaining singleton refs in § B).
Prescription
1. Registry of coordinator descriptors (metadata only):
{
taskName : 'summary' | 'kbSync' | 'backup' | 'primary-dev-sync'
| 'dream' | 'goldenPath' | 'swarmHeartbeat' | ...,
coordinator : SummarizationCoordinatorService | ... ,
executionKind : 'supervised-child-process' | 'service-runner'
| 'in-process-async' | 'local-only-service',
maintenanceClass: 'continuous' | 'heavy' | 'graph-dependent'
| 'lightweight-signal' | 'local-only',
backpressure : 'none' | 'exclusive-heavy' | 'after-heavy' | ...,
dependencies : ['<other-taskName>', ...]
}2. Boring collector (no policy branching):
collectDueCandidates({registry, context}) → Array<candidate>Iterates descriptors, calls each getDueTask(context), normalizes triggers, attaches metadata. NO switch(profile) policy body.
3. Pure-function policy gate:
pickNextCandidate({candidates, runningTasks, policyContext}) → candidate | nullApplies (in pipeline): continuous-restart cooldown / exclusive-heavy filtering / golden-path dependency gating / local-only-maintainer eligibility / deployment-profile filtering.
4. CadenceEngine correction: drop executeFn parameter from runIfDue per v13-path.md:117. Trigger-builder only.
5. Orchestrator integration: poll() becomes:
async poll() {
const candidates = collectDueCandidates({registry: this.registry, context: this.buildContext()});
const winner = pickNextCandidate({candidates, runningTasks: this.taskStateService.getRunning(), policyContext: this.buildPolicyContext()});
if (winner) await this.execute(winner);
}6. Sub-13 cleanup folded in: strip historical-context JSDoc bloat from class @summary; collapse any remaining singleton-alias instance fields (PR #11856 anti-pattern that closed superseded).
Acceptance Criteria
- Generic registry +
collectDueCandidates + pickNextCandidate lands; CadenceEngine corrected to pure trigger-builder per v13-path.md:117
Orchestrator.mjs ≤ 400 LOC post-Sub-18 merge (honest, defensible from Discussion #11857 responsibility-map). Aspirational ≤ 300 LOC if Sub 14 #11855 also lands within window
- Existing 13 mock-heavy
Orchestrator.spec.mjs:81-99 tests collapse — coordinators are unit-tested per-sub; Orchestrator integration tests use real coordinators or focused stubs
- Negative test:
collectDueCandidates must NOT cause any state mutation observable in TaskStateService / HealthService / lease state — assertable via spy / before-after snapshot
- Hard guardrail:
collectDueCandidates contains NO switch(profile) policy body — only iteration + normalization + metadata attachment
- Sub-13 cleanup folded in: class @summary stripped of "Service-DI 4-way classification" historical narrative + "No
configure(). No DEFAULT_X_*_MS..." negative trailer + any remaining Class A/B/C/D section divider comments
poll() calls MaintenanceBackpressureService.acquireLeaseAndExecute({...}) and .executeWithGoldenPathDependencyGate({...}) (Sub 19 lands first; Sub 18 wires)
Compound revalidationTrigger (per Discussion #11857 AC 11)
- If
Orchestrator.mjs LOC drifts above 500 within 60 days of Sub-18 merge → file revalidation ticket to assess Round-3 decomposition
- If Sub 19 cannot extract 70% of §E-H without broader split → resolved by Sub 19's own AC 6 BEFORE Sub-18 lands
Avoided Traps
- NO singleton-alias bag in the registry
- NO collector owning writes
- NO
switch(profile) in collector
- NO bootstrap split bundled (deferred to Round-3)
- NO M4 broader feature work
Depends on
Subs 15, 16, 17 (coordinator extractions) + Sub 19 (MaintenanceBackpressureService).
Unblocks
Round-2 closure. Round-3 candidates evaluated post-measurement: bootstrap split / InProcessTaskRunner extraction if metrics show them as the dominant remaining surface.
Authority
Discussion #11857 Cycle-3.5 GPT [GRADUATION_APPROVED] + operator ratify.
Authored by: [Claude Opus 4.7] (Claude Code)
Parent Epic
#11831 — Sub 18 of Epic. Round-2 closer per Discussion #11857 Cycle-3.5 B-prime convergence.
Premise
After Subs 15-17 (coordinator extractions) + Sub 19 (
MaintenanceBackpressureService), the remaining surface inOrchestrator.mjsis mostly thepoll()body + 7 hand-coded scheduling functions (§ I, lines 772-942, ~171 LOC). Sub 18 replaces those with a generic registry + purecollectDueCandidates+ purepickNextCandidatepipeline. Also corrects theCadenceEngine.runIfDue(... executeFn ...)drift perv13-path.md:117/ #11025 pure-trigger-builder spec. Also folds Sub-13's intra-file cleanup (anti-pattern JSDoc strip + collapse remaining singleton refs in § B).Prescription
1. Registry of coordinator descriptors (metadata only):
{ taskName : 'summary' | 'kbSync' | 'backup' | 'primary-dev-sync' | 'dream' | 'goldenPath' | 'swarmHeartbeat' | ..., coordinator : SummarizationCoordinatorService | ... , executionKind : 'supervised-child-process' | 'service-runner' | 'in-process-async' | 'local-only-service', maintenanceClass: 'continuous' | 'heavy' | 'graph-dependent' | 'lightweight-signal' | 'local-only', backpressure : 'none' | 'exclusive-heavy' | 'after-heavy' | ..., dependencies : ['<other-taskName>', ...] }2. Boring collector (no policy branching):
collectDueCandidates({registry, context}) → Array<candidate>Iterates descriptors, calls each
getDueTask(context), normalizes triggers, attaches metadata. NOswitch(profile)policy body.3. Pure-function policy gate:
pickNextCandidate({candidates, runningTasks, policyContext}) → candidate | nullApplies (in pipeline): continuous-restart cooldown / exclusive-heavy filtering / golden-path dependency gating / local-only-maintainer eligibility / deployment-profile filtering.
4. CadenceEngine correction: drop
executeFnparameter fromrunIfDueper v13-path.md:117. Trigger-builder only.5. Orchestrator integration:
poll()becomes:async poll() { const candidates = collectDueCandidates({registry: this.registry, context: this.buildContext()}); const winner = pickNextCandidate({candidates, runningTasks: this.taskStateService.getRunning(), policyContext: this.buildPolicyContext()}); if (winner) await this.execute(winner); // delegates to ProcessSupervisorService OR MaintenanceBackpressureService per executionKind }6. Sub-13 cleanup folded in: strip historical-context JSDoc bloat from class @summary; collapse any remaining singleton-alias instance fields (PR #11856 anti-pattern that closed superseded).
Acceptance Criteria
collectDueCandidates+pickNextCandidatelands; CadenceEngine corrected to pure trigger-builder perv13-path.md:117Orchestrator.mjs≤ 400 LOC post-Sub-18 merge (honest, defensible from Discussion #11857 responsibility-map). Aspirational ≤ 300 LOC if Sub 14 #11855 also lands within windowOrchestrator.spec.mjs:81-99tests collapse — coordinators are unit-tested per-sub; Orchestrator integration tests use real coordinators or focused stubscollectDueCandidatesmust NOT cause any state mutation observable inTaskStateService/HealthService/ lease state — assertable via spy / before-after snapshotcollectDueCandidatescontains NOswitch(profile)policy body — only iteration + normalization + metadata attachmentconfigure(). NoDEFAULT_X_*_MS..." negative trailer + any remaining Class A/B/C/D section divider commentspoll()callsMaintenanceBackpressureService.acquireLeaseAndExecute({...})and.executeWithGoldenPathDependencyGate({...})(Sub 19 lands first; Sub 18 wires)Compound revalidationTrigger (per Discussion #11857 AC 11)
Orchestrator.mjsLOC drifts above 500 within 60 days of Sub-18 merge → file revalidation ticket to assess Round-3 decompositionAvoided Traps
switch(profile)in collectorDepends on
Subs 15, 16, 17 (coordinator extractions) + Sub 19 (
MaintenanceBackpressureService).Unblocks
Round-2 closure. Round-3 candidates evaluated post-measurement: bootstrap split /
InProcessTaskRunnerextraction if metrics show them as the dominant remaining surface.Authority
Discussion #11857 Cycle-3.5 GPT
[GRADUATION_APPROVED]+ operator ratify.Authored by: [Claude Opus 4.7] (Claude Code)