---
id: 11834
title: 'Sub 2: Orchestrator test-fixture + invariant coverage for new config substrate'
state: CLOSED
labels:
  - enhancement
  - ai
  - testing
assignees:
  - neo-opus-ada
createdAt: '2026-05-23T11:21:54Z'
updatedAt: '2026-06-07T17:14:48Z'
githubUrl: 'https://github.com/neomjs/neo/issues/11834'
author: neo-opus-ada
commentsCount: 0
parentIssue: 11831
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-25T05:49:31Z'
---
# Sub 2: Orchestrator test-fixture + invariant coverage for new config substrate

## Parent Epic

#11831 — **Sub 2 of 6 (parallel after Sub 1).**

## Scope

Refactor `Orchestrator.spec.mjs` test-fixture (`createTestOrchestrator()` helper) for the new config substrate landed in Sub 1. Add invariant tests that catch regression of removed anti-patterns.

## Acceptance Criteria

1. **Refactored `createTestOrchestrator()`** — no longer uses `Neo.create(Orchestrator, {summarySweepIntervalMs: X, ...})` (won't reach the lazy-getter properties). Uses AiConfig.data mutation + env var with try/finally restore + direct instance-field override.
2. **Config precedence tests** prove: env var overrides AiConfig; gitignored `ai/config.mjs` overrides template; service override via instance field works; singleton leak prevention between tests (afterEach restore of mutated AiConfig.data).
3. **Parent-config-propagation tests** prove: mutating `orchestrator.taskDefinitions` propagates to `processSupervisorService.taskDefinitions` via `afterSetTaskDefinitions` hook (NOT via `start()` context replay).
4. **Source-level invariants** prove anti-patterns are removed:
   - No `_`-suffix configs in `Orchestrator.mjs` without corresponding hook (grep test)
   - No `configure()` method (grep test)
   - No `DEFAULT_X_INTERVAL_MS` exports in `TaskDefinitions.mjs` (grep test)
   - No `parseInterval` / `parseEnabledFlag` call sites in `Orchestrator.mjs` (grep test)
   - No `processSupervisorService.set({...this...})` block in `start()` (grep test)

## Files (touched)

- `test/playwright/unit/ai/daemons/Orchestrator.spec.mjs` (refactor `createTestOrchestrator()` + add invariant tests)

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

## Timeline

- 2026-05-23T11:21:55Z @neo-opus-ada added the `enhancement` label
- 2026-05-23T11:21:55Z @neo-opus-ada assigned to @neo-opus-ada
- 2026-05-23T11:21:55Z @neo-opus-ada added the `ai` label
- 2026-05-23T11:21:55Z @neo-opus-ada added the `testing` label
- 2026-05-23T11:23:07Z @neo-opus-ada added parent issue #11831
- 2026-05-23T13:03:13Z @neo-opus-ada cross-referenced by PR #11842
- 2026-05-25T01:10:00Z @neo-opus-ada cross-referenced by PR #11945
- 2026-05-25T05:49:31Z @tobiu referenced in commit `193b5f5` - "test(orchestrator): add Sub-1 invariant + config-precedence + propagation coverage (#11834) (#11945)

Sub 2 of Epic #11831 — locks the contract Sub 1 (#11833) established by adding the regression-detection layer that prevents the removed anti-patterns from re-entering.

New spec file Orchestrator.invariants.spec.mjs (16/16 pass) covering 3 AC clusters:

**AC2 — Config-precedence (6 tests)**:
- Env var overrides AiConfig.data for interval values
- AiConfig.data is consulted when env var is absent
- Env var overrides AiConfig.data for boolean enable flags (localOnly)
- AiConfig.localOnly.X=null falls through to deployment-profile default (local enables, cloud disables)
- AiConfig.data mutations are isolated per test via beforeEach/afterEach restore (substrate-leak prevention)

**AC3 — Parent-prop propagation via afterSetX hooks (5 tests, witness-property style)**:
- taskDefinitions → processSupervisorService
- dataDir → processSupervisorService + maintenanceBackpressureService
- healthService → both (witness via marker property)
- taskStateService → both (witness via marker)
- spawnFn → processSupervisorService (witness via marker on function)
- heavyMaintenanceLeasePath → maintenanceBackpressureService

Propagation uses witness-property assertions rather than reference equality because child reactive setters may wrap inputs via ClassSystemUtil.beforeSetInstance — the Sub-1 contract is value content flows through afterSetX, not reference identity.

**AC4 — Source-level invariants (5 tests, comment/string-stripped)**:
- Orchestrator.mjs has no `configure()` shadow-resolver method
- TaskDefinitions.mjs has no `DEFAULT_*_INTERVAL_MS` exports
- Orchestrator.mjs has no `parseInterval` / `parseEnabledFlag` call sites
- Orchestrator.mjs has no `processSupervisorService.set({...this...})` context-replay block
- Orchestrator.mjs has no `_`-suffix reactive config slot without a corresponding `beforeSet*` or `afterSet*` hook

Inline stripCommentsAndStrings helper handles block comments, line comments, and string literals so source-grep doesn't false-positive against JSDoc that explicitly names what the class doesn't do (lines 124-125 of Orchestrator.mjs document the absences).

cloudOnly handling is defensive — save+restore only if present on origin/dev. The cloudOnly collection lands on dev when #11940 merges; until then, this spec runs cleanly without depending on it.

AC1 (refactored createTestOrchestrator) is the helper Sub 1 (#11833) already shipped; Sub 2 layers the invariant-test coverage on top. Total touched-surface: 232/232 orchestrator-tree unit tests pass.

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-25T05:49:31Z @tobiu closed this issue
- 2026-06-06T21:08:07Z @neo-gpt cross-referenced by #11831

