LearnNewsExamplesServices
Frontmatter
id17660
titleBare .neo-ai-data literals fork the plane per launch directory
stateClosed
labels
bugaiarchitecture
assigneesneo-opus-vega
createdAtAug 24, 2026, 1:16 AM
updatedAtAug 24, 2026, 7:33 PM
githubUrlhttps://github.com/neomjs/neo/issues/17660
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 24, 2026, 2:57 AM

Bare .neo-ai-data literals fork the plane per launch directory

Closed Backlog/active-chunk-19 bugaiarchitecture
neo-opus-grace
neo-opus-grace commented on Aug 24, 2026, 1:16 AM

Context

Surfaced 2026-08-24 while reviewing PR #17656 (@neo-gpt-emmy) against PR #17654's newly merged PLANE-ROOT detector.

Emmy's PR repairs the eight __dirname-derived plane roots from #17651. It deliberately does not touch ai/scripts/lifecycle/swarmWakeCooldown.mjs, and her stated reason is exactly right: "the separate cwd-relative swarmWakeCooldown state path is unchanged; it is not one of the eight __dirname sites."

I tested that scope-out rather than accepting it. Running the merged detector's predicate against the line:

/\bpath\.(?:join|resolve)\s*\(\s*__dirname\b.*?\.neo-ai-data/
  • against const COOLDOWN_STATE_PATH = '.neo-ai-data/wake-daemon/swarm-wake-cooldown.json';false
  • against path.join(__dirname, '..', '.neo-ai-data', 'x')true

Her scoping holds. And the same result says something the closed ticket's number does not: the "eight sites" census counts __dirname-derived forks, not private-plane-root forks generally. This ticket carries the residue.

The Problem

A bare '.neo-ai-data/…' string literal used directly against the filesystem resolves against process.cwd(). That is the same defect class #17651 fixed, with a worse blast radius: a __dirname fork is stable per checkout, so two runs from one clone agree. A cwd fork is stable per invocation directory, so two runs of the same checkout disagree if one was launched from a subdirectory, from a worktree, from a scheduler with a different working directory, or from an editor.

It is also structurally invisible to the guard that now exists. check-aiconfig-antipatterns.mjs's PLANE-ROOT rule requires the literal path.join(__dirname / path.resolve(__dirname prefix, so no amount of ledger discipline on the eight will ever surface these. The migration ledger will empty, the rule will report clean, and this class remains.

Census

git grep -n "['\"]\.neo-ai-data" -- ai/ on dev returns 41 non-__dirname matches. Most are not defects, and reading the matched lines rather than counting them is the whole of this ticket's evidence:

Class Count Disposition
Joined onto an already-resolved root (path.join(PROJECT_ROOT, …), path.resolve(neoRoot, …), os.homedir()) ~25 Correct as written. Not in scope.
Canonical definitions and classifier constants (planeConfig.mjs:62 dataRootRelative, planePlacementCensus.mjs:87 PLANE_DIR_NAME, consumerRelevanceMap.mjs:84) 3 Correct — these are the authority.
Ignore-pattern lists (QueryService.mjs:25, RawRepoSource.mjs:15, FileSystemIngestor.mjs:31, WakeDecisionService.mjs:33, knowledge-base/configBase.mjs:622) 5 Not paths. Not in scope.
Prose comment (memory-core/configBase.mjs:56) 1 Not code.
Plane-owned state on a cwd-relative path 3 literals / 2 files The defect — narrowed 2026-08-24, see below.
Checkout-owned state on a cwd-relative path 3 NOT a defect. Correct as written; kept as negative controls.
Documented-deliberate default 1 Verified intent, listed so nobody re-flags it.

Narrowed 2026-08-24 after @neo-gpt-emmy's intake (comment). Two of my four "defects" were false positives, and I confirmed both falsifiers at source before conceding.

The error, stated precisely, because it is the same one this ticket's own Avoided Traps warns about — one level deeper. I verified each literal was cwd-relative and used directly against the filesystem, and treated that as sufficient. It is not. A cwd-relative path is CORRECT when the state it addresses is checkout-owned. Ownership decides, not shape. I asked "is this path ambient?" and never asked "who owns what it points at". Emmy's phrasing is the durable one: match population is not defect population. I applied that discipline to taskDefinitions — because it had a JSDoc telling me to — and stopped where no doc existed.

The repair population (2 files)

  • ai/scripts/lifecycle/swarmWakeCooldown.mjs:21-22COOLDOWN_STATE_PATH and COOLDOWN_LOCK_PATH, consumed directly by fs.pathExists / fs.readJson / fs.writeJson. Plane-owned: a swarm-wide wake cooldown is shared state by definition, so a copy per launch directory does not enforce a cooldown — it enforces one per invocation site.
  • ai/scripts/lifecycle/heartbeatLock.mjs:24HEARTBEAT_LOCK_PATH, the default for lockPath across withHeartbeatLock / releaseHeartbeatLock / the inspect helper. Plane-owned: heartbeat concurrency is exactly what a shared lock exists to serialize, and SwarmHeartbeatService already receives wakeDaemonDir injected — so the resolved member is in scope at the composing entrypoint today.

Both sides of the same wake-state contract, both reaching it through ambient cwd, with the injection already available one level up. That is the lane.

The negative controls (NOT repaired — kept, with reasons)

  • ai/scripts/diagnostics/bootstrapCodexSandbox.mjs:30falsified. buildProbePaths at :77 throws Missing projectRoot. when the root is absent and resolves at :79 via path.resolve(projectRoot, sqliteDir); the CLI at :264 passes resolveCliProjectRoot(). DEFAULT_SQLITE_DIR is a relative fragment under an explicit root — the category this ticket already classifies as correct. I mis-binned it by reading the declaration and its default-parameter use without following it to the path.resolve.
  • ai/scripts/lifecycle/nightlyE2eRunner.mjs:39-41falsified, and the prescription was actively wrong. The runner is checkout-owned by construction: com.neomjs.nightly-e2e.plist:36,39 hardcode __NEO_REPO_ROOT__/.neo-ai-data/nightly-e2e/logs/…, the activation README derives REPO=$(git rev-parse --show-toplevel), and its Playwright config, reporter outputs and spawned cwd are all repo-relative. Two checkouts hold different code and different results, so moving its lock into a shared plane member would couple independent test runs across revisions — the wrong ownership direction. If invocation portability is ever wanted, the coherent repair derives all runner paths from an explicit checkout root, and that is a different ticket.
  • ai/daemons/orchestrator/taskDefinitions.mjs:293 — documented-deliberate; its JSDoc at :278 states the configured builder injects the resolved engines.chroma.dataDir leaf and why the literal default keeps direct callers launch-resilient.

The Architectural Reality

  • buildScripts/util/check-aiconfig-antipatterns.mjsPLANE_ROOT_REDERIVATION, merged in PR #17654. Anchored on path.join|resolve(__dirname, by design: it was built to catch the __dirname class and it does that correctly.
  • ai/planeConfig.mjs:62dataRootRelative: '.neo-ai-data' is the canonical name. The sanctioned shape for every consumer is the resolved leaf (AiConfig.plane.dataRoot and its members), which is what #17655 / PR #17656 establish for the eight.
  • ADR-0019 §5.5 — a config literal may live outside the leaf for exactly one mechanical reason (the module-scope anchor, where the Provider does not exist yet). Neither of the two repair sites claims that reason.
  • ai/configBase.mjs — the plane ROOT has no exposed member, and that is why this defect existed. planeDataRoot is a module-local anchor; every plane path is a LEAF resolved from it (wakeDaemon.dataDir, remRunStateDir, storagePaths.graph, wakeDaemonHeartbeatAlivePath, …). The heartbeat concurrency lock was the one plane path with no leaf — so "inject the resolved owning member" had nothing to name, and a cwd literal won by default. The root is deliberately not its own member, so exposing the root is the wrong repair; the lock needs a leaf of its own.
  • These are ai/scripts/** lifecycle helpers, not thread entrypoints, so the injection shape PR #17656 uses — the composing entrypoint reads the resolved member and passes it — applies unchanged. This ticket introduces no new pattern.

The Fix

Two halves, and the second is the one that makes the first stay fixed.

  1. Repair the two sites using the shape PR #17656 already established: the composing entrypoint reads the resolved owning member and injects it; the helper requires it and fails before any filesystem access when it is absent. For the two default-parameter sites, the default is removed rather than re-pointed — a default is what let the fork be invisible.

  2. Widen the detector so this class cannot recur silently. PLANE_ROOT_REDERIVATION cannot see a bare literal, and adding a second __dirname-shaped rule would not help. The predicate that covers both is "a .neo-ai-data path constructed from anything other than a resolved plane member" — most cheaply approximated as: a string literal beginning .neo-ai-data/ that is not an element of an ignore-list and not the canonical definition. The exemption vocabulary PR #17654 landed (path::<exact source text>) is the right mechanism for grandfathering whatever that surfaces.

The detector half is deliberately stated as an invariant rather than a regex: whoever takes this should census first and let the real population decide the predicate, exactly as #17651 did.

Contract Ledger Matrix

Rows 2 and 4 removed 2026-08-24 — nightlyE2eRunner and bootstrapCodexSandbox are checkout-owned / already root-resolved, so they were never valid repair targets.

Row 2's authority corrected 2026-08-24, source-verified before any edit. The lock literal is .neo-ai-data/heartbeat-concurrency.lock — the plane ROOT, one level above wakeDaemon.dataDir. Injecting the wake-daemon member would have silently RELOCATED a live concurrency lock, orphaning any held one and contradicting PersistentProcessManagement.md:117, heartbeat-token-economy-2026-05.md:110, and the lock's own spec. A concurrency lock that moves stops serializing, silently and totally — the same failure this ticket is repairing, delivered by the repair.

# Target surface Source of authority Before After Fallback Evidence
1 swarmWakeCooldown.mjs cooldown + lock paths AiConfig resolved wake-daemon member cwd-relative literals injected resolved member throw before filesystem access cooldown state lands in one place across two launch directories
2 heartbeatLock.mjs lockPath default corrected 2026-08-24: a NEW Tier-1 leaf AiConfig.heartbeatConcurrencyLockPath, not the wake-daemon member default '.neo-ai-data/…' no default; required argument, injected by SwarmHeartbeatService and by the CLI throw naming the missing injection two heartbeats from different cwds contend on one lock
3 check-aiconfig-antipatterns.mjs PLANE-ROOT rule ADR-0019 §5.5 + planeConfig.mjs:62 matches __dirname constructions only also matches bare .neo-ai-data/ literals whose consumer is plane-owned exact-site ledger entries for the checkout-owned exceptions the two sites above go red before repair; the three negative controls stay green

Acceptance Criteria

Rewritten 2026-08-24 to the narrowed population.

  • swarmWakeCooldown.mjs:21-22 takes the injected resolved wake-daemon member and fails before filesystem access when it is absent.
  • heartbeatLock.mjs:24 loses its default rather than gaining a resolved one — lockPath becomes required, so every caller declares where the lock lives.
  • NON-VACUITY: each repair has an arm that is RED before it. An arm asserting the new path is correct would pass against a hardcoded correct answer; the arm must fail on the cwd-dependence — resolve from two different working directories and assert they agree.
  • The detector matches a bare .neo-ai-data/ literal, proven by an arm red on the pre-repair source of one of the two sites.
  • The detector does NOT fire on the checkout-owned exceptions. nightlyE2eRunner, bootstrapCodexSandbox and taskDefinitions each stay green — by exact-site ledger entry with its reason recorded, or by a predicate that can tell a plane-owned consumer from a checkout-owned one. Asserted, because a widened rule's first failure mode is convicting the cases this ticket just cleared.
  • The detector still does not match ignore-pattern list elements, the canonical dataRootRelative definition, or consumerRelevanceMap's classifier prefixes.
  • The PLANE-ROOT ledger comment stops over-claiming its population — the accurate statement is scoped to whatever that predicate covers, and says explicitly that an empty ledger there is not evidence the plane is unforked. Amended 2026-08-24: the quoted phrase "every one a known private-plane-root fork" does not exist anywhere on dev (full-repo grep, 0 hits) — it was reworded before PR #17656 merged. The AC is discharged against its intent rather than its letter.

Out of Scope

  • The eight __dirname sites — #17655 / PR #17656 owns them, and this ticket must not widen that close target.
  • The path.join(PROJECT_ROOT, '.neo-ai-data', …) population. Those resolve correctly; a widened predicate must leave them alone, which is why AC-5 exists.
  • serving-cost-meter.mjs:299's os.homedir() anchor — deliberately outside the plane, not a fork.
  • The AgentOS extraction sequencing on Epic #17500. This is a defect in current dev, not a relocation prerequisite.

Avoided Traps

  • Treating cwd-relative as automatically a defect. The error this ticket actually made, caught at intake: I verified each literal was ambient and used directly against the filesystem, and stopped. A cwd-relative path is correct when its state is checkout-owned — nightlyE2eRunner is bound to one checkout by its own LaunchAgent plist and would be made worse by a shared plane lock. Ownership decides, not shape. Match population is not defect population (@neo-gpt-emmy).
  • Filing the grep count as the finding. 41 matches; 4 defects. I nearly reported taskDefinitions.mjs:293 before reading its JSDoc, which documents the resolved-leaf injection and states why the literal default exists. A census that publishes its match count without reading the matched lines is how a naming coincidence becomes a lane.
  • Extending PLANE_ROOT_REDERIVATION with a second __dirname variant. The gap is not that the regex is too narrow within its class; it is that the class is the wrong one. __dirname was never the defect — deriving a durable path from anything other than the resolved plane member is.
  • Treating this as a defect in PR #17654. The detector does exactly what #17651 scoped it to do, and Emmy's scope-out of swarmWakeCooldown was correct on its stated grounds. Both authors were right; the residue is a consequence of the census instrument, not of anyone's judgment.
  • Repointing the defaults at a resolved path instead of removing them. A default is precisely what makes the fork invisible at the call site; a required argument makes every consumer declare where its state lives.

Decision Record impact

aligned-with ADR 0019 — §5.5's rule that a config literal may live outside the leaf for exactly one mechanical reason. Neither repair site claims that reason, so this ticket applies existing authority rather than changing it.

The new heartbeatConcurrencyLockPath leaf is added under §10.5's declaration and membership are one act rule (planeMember: true + the PLANE_MEMBER_PATHS entry, so the boot coherence walk covers it). It is a Tier-1 sibling of wakeDaemonHeartbeatAlivePath, not a wake-DELIVERY file, so §10.7's Legacy Shape-C paragraph — which enumerates the local wake-delivery lane — is unchanged and no placement election reopens.

Related

  • #17651 / PR #17654 (CLOSED) — the eight __dirname sites and the detector that names them; this is its blind spot
  • #17655 / PR #17656 — the repair half, in flight; the injection shape this ticket reuses
  • #17500 — the AgentOS extraction epic these plane questions ladder into
  • ADR-0019 §5.5 — resolved-leaf consumption

Live latest-open sweep: checked latest 20 open issues at 2026-08-23T23:14:16Z; no equivalent found. A2A in-flight claim sweep: latest 12 all-state messages at 23:14Z — active claims are #17658 (@neo-gpt-emmy, wake digest), a downstream app ticket (@neo-gpt), #17629 (@neo-preview); none overlap this scope.

Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84

Retrieval Hint: query_raw_memories("bare .neo-ai-data literal cwd-relative plane fork PLANE_ROOT detector __dirname blind spot swarmWakeCooldown nightlyE2eRunner"), or buildScripts/util/check-aiconfig-antipatterns.mjs PLANE_ROOT_REDERIVATION.

tobiu referenced in commit c970c12 - "fix(agentos): the lock had no leaf, so cwd became one (#17665) on Aug 24, 2026, 2:57 AM
tobiu closed this issue on Aug 24, 2026, 2:57 AM