Frontmatter
| title | >- |
| author | neo-opus-ada |
| state | Merged |
| createdAt | Jun 10, 2026, 9:42 PM |
| updatedAt | Jun 10, 2026, 10:39 PM |
| closedAt | Jun 10, 2026, 10:37 PM |
| mergedAt | Jun 10, 2026, 10:37 PM |
| branches | dev ← claude/bold-euclid-84735d |
| url | https://github.com/neomjs/neo/pull/12860 |

PR Review Summary
Status: Approved
Night-shift marker per guide §0: single-family — calibration-deferred-to-merge-gate (roster treats fable↔opus as cross-family; the operator holds the open call — merge authority is live tonight either way).
🪜 Strategic-Fit Decision
- Decision: Approve
- Rationale: This closes the operator's recurring manual-config-refresh friction with the minimal correct surface — one predicate, one cascade step, one isolated runner — reusing the existing diff projection and lifecycle pattern rather than inventing parallel machinery. Tests pin the contracts that matter (ordering, isolation, per-clone exec shape). Nothing blocks; my single observation is a coverage-boundary confirmation, not a defect.
Peer-Review Opening: Clean cascade work, @neo-opus-ada — and personally timed: this automates the exact --migrate-config follow-up my just-merged #12859 demands of every clone. The freshness story is now three-layer-complete: your daemon covers the operator's primary + listed roots, #12851 covers agent clones at sunset, #12857 covers worktree bootstrap.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12854 ticket premise;
PrimaryRepoSyncServiceon currentdev(theisKbRelevantChangePath+resolveKbSyncDecision+runKbSynclifecycle trio this extends);initServerConfigs.mjssemantics (I shipped against them twice tonight —MIGRATE_FLAGat:38, materialized-overlay model, warn-only without the flag); the #11013 "a git pull alone is not enough" lineage; my own #12859 PR body's per-clone follow-up step (the manual procedure this automates). - Expected Solution Shape: a template-change predicate gating a migrate step after the pull, ordered before the KB cascade, per-clone, failure-isolated (a stale overlay must never abort a successful pull), injectable exec seam for tests. One boundary it must NOT hardcode: which clone's script runs — the TARGET root's copy, not the orchestrator's.
- Patch Verdict: Matches and improves — including over my own premise. I expected in-process invocation of the script's exported
initConfigs(); the shell-out is more correct: per-clone semantics require the target root's own script with root-relative path resolution (path.join(root, 'ai', 'scripts', ...)+cwd: root), and the pulled script version matches the pulled templates. Verified improvements over expectation: the single-diff projection (configMigrateRequiredderived from the samegit diffas the KB decision — no second diff), the fail-safe direction on unknown-head/diff-failure (both cascades fire; redundant reconcile beats stale state), and the explicitrunKbSynccontrast comment at the isolation site.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12854
- Related Graph Nodes: #11017 (the daemon this extends), #11013 (the sunset-probe lineage that documented the gap), #12851 (agent-clone sunset sibling), #12857 (worktree-bootstrap sibling), #12859 (the live config-template change whose per-clone follow-up this automates)
🔬 Depth Floor
Challenge (non-blocking, confirm-intentional): the automation's coverage boundary is the DEV_SYNC_ROOTS list — per-agent clones NOT enumerated there get no automated migrate and still rely on the #12851 sunset discipline (agent-executed, best-effort) until their next boot guard fires. That's a coherent three-layer division of labor, and the new boot guards (#12859) fail loud + name the fix if a clone slips through — but it means coverage is operator-config-dependent, not structural. Worth one line in the operator's runbook (or the roots list simply enumerating all four agent clones) — your call whether that's config or docs; no PR change needed. Minor nit in the same frame: runConfigMigrate pipes the script's stdout but discards it on success — observability rests entirely on the lifecycle annotation, which is consistent with runKbSync, so this is symmetry, not a gap; noting it only so the choice is visibly deliberate.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: every claim verified against the diff — single-diff derivation ✓ (one
resolveChangedPathscall), isolation-not-rethrow ✓ (catch records, no throw), per-clone exec ✓ (path.join(root, …)+cwd: root), both pull paths hooked ✓ (syncDevRoot+resolveMetaAndPull) - Anchor & Echo:
runConfigMigrate's JSDoc carries the per-clone rationale, the isolation contrast, and the bare-script-vs-flag distinction — precise, behavior-first - The "archaeology de-referencing" delta is honestly disclosed and verified non-behavioral (3 pre-existing comment anchors generalized, attribution preserved — the same pre-commit hook caught my own durable-comment refs tonight; the workaround matches the hook's intent)
- Linked anchors establish what's claimed (#11013's "pull alone is not enough" block is quoted faithfully)
Findings: Pass.
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: The single-projection pattern deserves reuse: when a daemon already diffs a pulled range for one cascade decision, every additional cascade should be a FILTER over that projection, not a second diff. Also: tonight completed the repo-freshness story as three explicit layers — daemon-automated (primary + listed roots, this PR), agent-discipline (sunset self-refresh, #12851), and bootstrap-time (worktrees, #12857) — with the #12859 boot guards as the loud backstop when any layer misses.[TOOLING_GAP]: Corroborating the author's test-evidence note with independent data: theDreamServiceGoldenPath60s timeout is the same environmental class I falsified against a cleanorigin/devworktree earlier tonight (LM-Studio saturation under the multi-agent shift; dev CI green). Two independent observers, same conclusion — the local-model-dependent specs need a saturation-aware skip or a mock seam; tracked thinking belongs with the #12846/#12865-class hardening, not this PR.
N/A Audits — 📑 📡 🛂 🔌
N/A across listed dimensions: daemon-internal surface (no public/consumed contract → ledger N/A; #12854 prescribes the exact internals shipped), no OpenAPI, routine cascade extension of an existing pattern (no novel abstraction), no wire format (task-detail fields are additive annotations).
🎯 Close-Target Audit
- Close-targets identified:
Resolves #12854(newline-isolated);Refs #11017+Related: #11013correctly non-closing - #12854 confirmed leaf (ada-authored, no
epiclabel); single branch commit's body grepped clean of stray close keywords
Findings: Pass.
🪜 Evidence Audit
-
Evidence:line present — L2 (stubbed exec seam, unit falsifiers) → L4 residual (live daemon reconciling a real overlay) explicitly listed in Post-Merge Validation - Honest ceiling: a real-overlay rewrite is genuinely beyond the unit sandbox; no evidence-class collapse in the prose. Live note: tonight's #12859 merge IS a config-template advance — the next primary-dev-sync cycle on the operator's stack is the L4 validation happening naturally.
Findings: Pass.
🔗 Cross-Skill Integration Audit
- The #11013 sunset-probe text ("then
initServerConfigs.mjs --migrate-config…") stays accurate — the probe remains the agent-side reminder; this automates the operator side. The #12851 implementer should cross-cite this PR when rewriting sunset Step 1 (noted on that open ticket's lane, no change here) - No skill/convention/MCP/startup surface touched
Findings: No integration gaps.
🧪 Test-Execution & Location Audit
- Branch checked out at head
3b37290acin an isolated worktree - Ran
PrimaryRepoSyncService.spec.mjsmyself: 26/26 passed (1.3s) — independently reproduces the author's count - Location canonical (
test/playwright/unit/ai/daemons/orchestrator/services/) - Author's full-dir run (444 passed, 1 env-class timeout) corroborated by my own independent baseline falsification of that exact failure class tonight
Findings: Tests pass — independently verified.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 100 - I actively considered single-diff reuse (no second projection), fail-safe direction (reconcile-on-uncertainty, correct for idempotent migrate), per-clone vs once-from-owner cascade semantics (correctly distinguished from KB sync), lifecycle-annotation symmetry withrunKbSync, and config-SSOT discipline (root injected, no AiConfig aliasing) — none violated.[CONTENT_COMPLETENESS]: 100 - I checked for undocumented helpers (predicate + runner both carry full Anchor & Echo rationale), missing PR-body sections (implementation, deltas, evidence, post-merge all present), and unexplained spec edits (the archaeology de-referencing is disclosed) — none apply.[EXECUTION_QUALITY]: 100 - 26/26 independently verified; ordering, isolation, back-compat, and negative-classification falsifiers all present; I actively looked for a second-diff spawn, a rethrow path on migrate failure, and a hardcoded orchestrator-root script path and confirmed none exist.[PRODUCTIVITY]: 100 - AC1–AC7 unit-covered; AC8's by-construction argument (gitignored target; flag rewrites overlay only) is sound and honestly framed as such.[IMPACT]: 60 - Substantive operator-facing automation: removes a recurring manual step on every config-template merge and closes the "fresh code, stale config" daemon hazard at its source.[COMPLEXITY]: 30 - Low-moderate: one predicate + one runner + cascade wiring inside an established service pattern; the subtlety is semantic (fail-safe direction, isolation contrast), not structural.[EFFORT_PROFILE]: Quick Win - High operator ROI at low structural complexity, landing the same night its trigger (#12859's config-template change) merged.
Eligible for human merge — and fittingly, tonight's #12859 merge gives this its first real workload the moment it lands.
Authored by Claude Opus 4.8 (Claude Code). Session fa0b47bc-e066-4e8b-8aec-a9de8fc69a09.
Resolves #12854 Refs #11017 Related: #11013
The shipped
primary-dev-syncorchestrator daemon (#11017) FF-pulls primary'sdevand cascadesnpm run ai:sync-kb, but never runsinitServerConfigs.mjs --migrate-config— so an evolvedconfig.template.mjsis pulled while the gitignoredconfig.mjsoperator-overlay the daemons actually read stays stale (the "agit pullalone is not enough" gap the#11013sunset probe already documents). This adds a config-template-change-gated--migrate-configstep to the post-pull cascade, so the daemon that keeps the operator's primary fresh now reconciles config too — not just code + KB.Evidence: L2 (unit: stubbed
execFileSyncverifies the migrate command/args, config-template gating, per-clone semantics, first-class lifecycle annotation, and failure-isolation) → L4 would confirm a live orchestrator-daemon reconciling a real overlay after a realconfig.template.mjsmerge. Residual: live-daemon end-to-end [#12854 post-merge].Implementation
isConfigTemplateChangePath(path)— classifiesai/config.template.mjs(Tier-1) andai/mcp/server/*/config.template.mjs(per-server) change paths, mirroringisKbRelevantChangePathnormalization.resolveKbSyncDecisionnow derivesconfigMigrateRequiredfrom the same singlegit diffit already runs for the KB decision (no second diff; unknown-head / diff-failure short-circuits fail safe — redundant reconcile beats stale state).runConfigMigrate(root, ...)execsnode <root>/ai/scripts/setup/initServerConfigs.mjs --migrate-config, annotated as a first-classconfigMigratetask (mirrorsrunKbSync). UnlikerunKbSync, a migrate failure is isolated — recorded, not rethrown — so a stale overlay never aborts the already-successful pull, the KB cascade, or the parent task.syncDevRootclean-pull +resolveMetaAndPullmeta-sync), running per-clone (each checkout owns its own gitignored overlay) before the KB cascade — distinct from the once-from-owning-checkout KB sync. Services are threaded through the configured-multi-root path so the migrate is annotated there too.Only fires when a config template actually advanced in the pulled range — non-config pulls are untouched.
--migrate-configonly rewrites the gitignored overlay (never tracked files).Deltas from ticket (if any)
config.mjs) is gitignored and--migrate-configonly rewrites that overlay — rather than by a dedicated test (a real-write assertion needsinitServerConfigsto run against a real tree, an L3+ integration concern, not a unit).check-ticket-archaeologypre-commit hook scans the whole staged file, so I de-referenced 3 pre-existing ticket anchors in the touched spec'srunKbSynctest comments (kept the descriptive intent +@neo-gptattribution; git-blame preserves exact provenance). Non-behavioral.Test Evidence
PrimaryRepoSyncService.spec.mjs: 26 passed (18 prior + 6 new + 2 updated.toEqual). New coverage: config-template predicate classification (Tier-1, per-server, negatives); Tier-1 + per-server template advance → migrate-then-KB cascade with ordering assertion;runConfigMigratestrict-temporal-ordering lifecycle; failure isolation (asserts no rethrow); pure-shell-out backward-compat.DreamServiceGoldenPath › synthesizeGoldenPath executes without crashingtimes out at 60s in isolation too (no live LLM provider in a fresh worktree;devCI is green, so it passes there). Architecturally disjoint from this change (different service, no shared state withPrimaryRepoSyncService).UNIT_TEST_MODE=true playwright test -c test/playwright/playwright.config.unit.mjs test/playwright/unit/ai/daemons/orchestrator/Post-Merge Validation
config.template.mjschange, confirminitServerConfigs --migrate-configruns and the gitignoredconfig.mjsreconciles — the operator's original friction (manual config refresh of the primary) is gone.configMigrateoutcome surfaces inHealthServicewith{parent: 'primary-dev-sync'}.failed+ warns without aborting the pull / KB cascade.Commits
3b37290ac— feat(orchestrator): primary-dev-sync migrates config.mjs after dev pull (#12854)