Follow-up idea surfaced during review of PR #11130, which implements #11017 as a single primary-dev-sync Orchestrator lane. The current PR is intentionally scoped to one resolved primary checkout so it can land the safe auto-pull + KB cascade primitive first.
The operator pointed out that the live swarm topology uses three separate repo clones in three folders, one per active agent family. If the goal is to keep dev fresh for the whole local swarm, a single primary checkout is only the first step. The next step is an explicit configuration surface for the repo roots that should participate in dev freshness.
This is deliberately out of scope for PR #11130. It should be a follow-up ticket so the first primitive can merge without broadening its risk profile.
The Problem
#11017 and PR #11130 close the stale-primary gap for the daemon's resolved checkout, but they do not address independent peer clones. git worktree list --porcelain can resolve a primary checkout from a worktree, yet it cannot discover unrelated sibling clones. The current service therefore has no authoritative way to know that other agent roots exist.
Blind auto-discovery would be risky: sibling-folder scans and matching origin URLs can pick up stale test clones, forks, or unrelated local experiments. Blind synchronization would be worse: an agent clone might be on a feature branch with local work, and the orchestrator must not switch branches or rewrite that working tree.
The desired surface is explicit operator configuration: "these are the repo roots that may be kept fresh," with safe per-root behavior.
ai/daemons/services/PrimaryRepoSyncService.mjs resolves one primary root and runs the sync ladder.
ai/daemons/Orchestrator.mjs exposes NEO_ORCHESTRATOR_PRIMARY_DEV_SYNC_INTERVAL_MS and NEO_ORCHESTRATOR_PRIMARY_DEV_SYNC_ENABLED.
learn/agentos/DeploymentCookbook.md documents those two env vars.
#11017 explicitly lists external clones as out of scope: daemons running from external clones are not addressed by the single primary-root resolver.
Adjacent but non-duplicate tickets:
#10435 solved canonical-root overrides for bootstrapWorktree.mjs data symlinking in independent clones. That is clone bootstrap/data-linking, not periodic dev synchronization.
#10822 includes a future config doctor for clone-aware config drift. That is diagnosis/migration guidance, not Orchestrator-owned dev freshness.
The Fix
Extend the #11130 primitive from one implicit primary root to an explicit target-root list while preserving existing behavior when no list is configured.
Proposed MVP surface:
Add an operator config env var such as NEO_ORCHESTRATOR_DEV_SYNC_ROOTS, containing a JSON array of absolute repo root paths.
When unset, keep the exact #11130 behavior: resolve one primary root and sync only that root.
When set, iterate the configured roots and apply the same safety ladder per root.
For each root:
verify it is a Neo repo clone;
run git fetch origin dev --quiet;
if the current branch is dev, apply the existing dirty-state gate, metadata-only resolution, fast-forward pull, and outcome recording;
if the current branch is not dev, do not checkout/switch branches; record a safe fetched-only / not-dev-branch result.
Keep npm run ai:sync-kb single-root by default. Run the KB cascade from the orchestrator's owning/root checkout after at least one successful dev update, not once per configured clone.
Naming should be reviewed during implementation. If the service remains single-root-first internally, PrimaryRepoSyncService can keep the name and gain resolveTargetRoots(). If multi-root becomes the dominant behavior, rename to a less misleading service name in the implementation PR.
Still runs once from the orchestrator-owned/root checkout after successful dev updates
Manual npm run ai:sync-kb remains escape hatch
DeploymentCookbook + PR body
Unit test proves one KB invocation for multi-root cycle
Health outcome details
Existing HealthService task outcome envelope
Aggregate per-root result details without losing root-specific failure reasons
Failed root does not prevent other configured roots from being attempted unless config parsing fails
JSDoc
Unit test verifies partial success aggregate
Acceptance Criteria
AC1: Root-list config added with no tracked machine-specific paths. The operator supplies paths via env/config, not committed source.
AC2: Unset config preserves the PR #11130 single-primary behavior exactly.
AC3: Config parser validates malformed JSON and non-array values with an operator-visible skipped/failed outcome.
AC4: Each configured root is verified as a git repo with origin/dev before sync logic runs.
AC5: Roots currently on dev use the existing dirty-state gate, metadata-only resolution, fast-forward pull, and outcome recording.
AC6: Roots not on dev are not switched. They may fetch origin/dev, but working-tree fast-forward is skipped and recorded.
AC7: A failure in one configured root does not prevent later roots from being inspected unless the root-list config itself is invalid.
AC8: KB sync cascade remains single-root by default and does not run once per configured clone.
AC9: Documentation updates learn/agentos/DeploymentCookbook.md with the new config surface and explicitly warns against committing local absolute paths.
Auto-discovering sibling clones by filesystem scan or matching origin URLs.
Switching configured roots to dev automatically.
Running independent KB sync cascades for every clone.
Moving Codex/Gemini/Claude harnesses to worktree parity.
Solving clone config drift; #10822 covers the config-doctor direction.
Avoided Traps
Auto-discovery by sibling folder scan: rejected because the operator, not a heuristic, knows which clones are active swarm roots.
Branch switching: rejected because a configured clone may be in the middle of feature work.
Per-clone KB sync by default: rejected because it risks duplicated work and datastore contention. The first follow-up should keep the KB cascade single-root.
Bundling into PR #11130: rejected because PR #11130 is already a coherent single-root primitive with all CI green; broadening it now increases review risk.
Context
Follow-up idea surfaced during review of PR #11130, which implements #11017 as a single
primary-dev-syncOrchestrator lane. The current PR is intentionally scoped to one resolved primary checkout so it can land the safe auto-pull + KB cascade primitive first.The operator pointed out that the live swarm topology uses three separate repo clones in three folders, one per active agent family. If the goal is to keep
devfresh for the whole local swarm, a single primary checkout is only the first step. The next step is an explicit configuration surface for the repo roots that should participate in dev freshness.This is deliberately out of scope for PR #11130. It should be a follow-up ticket so the first primitive can merge without broadening its risk profile.
The Problem
#11017 and PR #11130 close the stale-primary gap for the daemon's resolved checkout, but they do not address independent peer clones.
git worktree list --porcelaincan resolve a primary checkout from a worktree, yet it cannot discover unrelated sibling clones. The current service therefore has no authoritative way to know that other agent roots exist.Blind auto-discovery would be risky: sibling-folder scans and matching
originURLs can pick up stale test clones, forks, or unrelated local experiments. Blind synchronization would be worse: an agent clone might be on a feature branch with local work, and the orchestrator must not switch branches or rewrite that working tree.The desired surface is explicit operator configuration: "these are the repo roots that may be kept fresh," with safe per-root behavior.
The Architectural Reality
Current PR #11130 surfaces:
ai/daemons/services/PrimaryRepoSyncService.mjsresolves one primary root and runs the sync ladder.ai/daemons/Orchestrator.mjsexposesNEO_ORCHESTRATOR_PRIMARY_DEV_SYNC_INTERVAL_MSandNEO_ORCHESTRATOR_PRIMARY_DEV_SYNC_ENABLED.learn/agentos/DeploymentCookbook.mddocuments those two env vars.Adjacent but non-duplicate tickets:
bootstrapWorktree.mjsdata symlinking in independent clones. That is clone bootstrap/data-linking, not periodic dev synchronization.The Fix
Extend the #11130 primitive from one implicit primary root to an explicit target-root list while preserving existing behavior when no list is configured.
Proposed MVP surface:
NEO_ORCHESTRATOR_DEV_SYNC_ROOTS, containing a JSON array of absolute repo root paths.git fetch origin dev --quiet;dev, apply the existing dirty-state gate, metadata-only resolution, fast-forward pull, and outcome recording;dev, do not checkout/switch branches; record a safefetched-only/not-dev-branchresult.npm run ai:sync-kbsingle-root by default. Run the KB cascade from the orchestrator's owning/root checkout after at least one successful dev update, not once per configured clone.Naming should be reviewed during implementation. If the service remains single-root-first internally,
PrimaryRepoSyncServicecan keep the name and gainresolveTargetRoots(). If multi-root becomes the dominant behavior, rename to a less misleading service name in the implementation PR.Contract Ledger Matrix
NEO_ORCHESTRATOR_DEV_SYNC_ROOTSlearn/agentos/DeploymentCookbook.mddev; never checkout/switch branchesnpm run ai:sync-kbremains escape hatchAcceptance Criteria
origin/devbefore sync logic runs.devuse the existing dirty-state gate, metadata-only resolution, fast-forward pull, and outcome recording.devare not switched. They may fetchorigin/dev, but working-tree fast-forward is skipped and recorded.learn/agentos/DeploymentCookbook.mdwith the new config surface and explicitly warns against committing local absolute paths.Out of Scope
originURLs.devautomatically.Avoided Traps
Related
bootstrapWorktreeindependent-clone canonical root).Origin Session ID:
20a824b0-29d1-4082-ae12-87705ec69c3fHandoff Retrieval Hint:
query_raw_memories(query="PR #11130 primary-dev-sync multiple repo clones configured roots operator path list KB cascade single-root")