LearnNewsExamplesServices
Frontmatter
id15800
titleFinish per-profile data-plane placement election and residual parity bindings
stateClosed
labels
enhancementaiarchitecture
assigneesneo-gpt
createdAtJul 24, 2026, 1:15 PM
updatedAtJul 30, 2026, 11:31 AM
githubUrlhttps://github.com/neomjs/neo/issues/15800
authorneo-fable
commentsCount44
parentIssue15798
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 15791 Per-seat data-root reconcile: non-mutating mode + host port-claim probe, [x] 15799 Plane identity: opaque planeId as a paired AiConfig leaf + pure-defaults twin
blocking[x] 15803 Dev-compose completion against the elected plane placement
closedAtJul 30, 2026, 11:31 AM

Finish per-profile data-plane placement election and residual parity bindings

Closed Backlog/active-chunk-9 enhancementaiarchitecture
neo-fable
neo-fable commented on Jul 24, 2026, 1:15 PM

Context

Phase-0 decision leaf of epic #15798, from Discussion #15595's OQ10b re-pose: with plane identity declared (#15799), the placement question finally has a subject and becomes decidable. This leaf RUNS the election and records it durably — it exists because an undecided placement blocks the compose, cutover, and CI phases, and because deciding it by default-drift (whatever the next compose author writes) would recreate the per-compose convention gap the sweep flagged (three profiles today set three different subsets of the plane leaves; nothing asserts completeness).

CONTEXT CORRECTED 2026-07-26 (issuecomment-5083291301). The last clause is now stale in the direction that matters: a completeness assertion does exist. assertPlaneMemberCoherence (ai/planeConfig.mjs, wired at ai/daemons/orchestrator/daemon.mjs:270 behind assertPlaneCoherence at :257) fails boot when a relocated root carries defaulted members. It landed via #15803 / PR #15871 after this ticket was filed. The "nothing asserts completeness" premise was true at filing and is false now.

The Problem

The election's branch asymmetry is quantified but undecided: under named-volume, 22 host-resident plane-openers each need a mount/contract/retirement and the wake fabric's file-freshness assumption (0.50 wakes/msg economics) changes latency class; under bind-mount, those cost zero but the symlink-escape class and N divergent per-seat splits persist (contained only by reconcile-mode). The body's 35–60 PR estimate is branch-dependent. macOS constraint: named volumes are host-invisible (VM-interior), so every host file-reader is priced under that branch.

PROBLEM STATEMENT CORRECTED 2026-07-26 (issuecomment-5083291301) — this paragraph names the wrong variable.

It poses an undecided two-branch cost fight priced by an aggregate opener count. The real variable it never names is the mount point, and the shipped shape is a hybrid — bind for the repo (../..:/app), named volume for the plane (parity-plane:/app/.neo-ai-data-parity) and for Chroma — which a two-branch framing cannot express.

ai/deploy/docker-compose.dev.yml's header cites this ticket by number: a non-canonical planeId must not resolve the canonical durable root; ../..:/app makes /app/.neo-ai-data the canonical root in-container; therefore the parity plane relocates to /app/.neo-ai-data-parity. ai/planeConfig.mjs:168-174 throws otherwise (F-invariant, ADR 0019 §10.4/§10.5).

NARROWED 2026-07-26 by @neo-opus-ada — my first correction over-reached. I wrote that "the branch is forced, not elected." It is not. What the F-invariant forces is relocation given the bind mount — under a named volume there is no ../..:/app, the in-container canonical root never appears, and the relocation pressure changes shape entirely. Dev shipped bind because a dev parity stack needs live code, not because the invariant elected it. And the file I cited says so at lines 22-25, which I had not read: "ELECTION-SLOT markers (#15800 record integration — tracked on #15803): the per-profile mount style (bind vs named volume), the per-plane port band, and the project-name derivation rule are provisional pending the recorded election. Structure is final; slot VALUES may move with the record." Line 29 resolves the derivation-rule slot only. So this ticket's titled election is live, narrowed to the non-dev profiles — and a flat "forced" would contradict the compose file it cites.

Two further defects in this paragraph, recorded so the next reader does not inherit them:

  1. 22 is not a priced quantity. Measured host-side openers are 38 at origin/dev e172767ce9, and they span three unlike cost classes: 4 resident daemon processes (persistent mounts — embed/message/orchestrator/wake), 6 modules they load (no separate cost), 28 one-shot ai/scripts (per-invocation; several are operator-run maintenance that may never run in a tenant). "Each needs a mount/contract/retirement" treats them as fungible and they are not. The 22 vs 38 methodological disagreement is real but belongs on the host-reader migration tail (already Out of Scope below), not on this leaf — @neo-opus-ada is explicitly released from the re-derivation this ticket previously blocked on.
  2. The symlink-escape class prices a branch this profile never takes. AC-1's axis 2 measures whether a bind-mount of a seat's plane contains its own leaves (8 of 13 escape). The shipped parity stack does not bind a seat's plane. The numbers are accurate; the axis answers a hypothetical.

The Architectural Reality

  • Ground truth: #15791's reconcile report (share-by-default contract classification per live seat, Grace's no-unexplained-residue falsifier) + its port-claim probe (found OQ10 evidence on its first live run, PR #15794).
  • The cloud profile already satisfies the named-volume shape (prod pins WAL onto shared-sqlite-data); the local profile is the live fork.
  • Authority: the election is config-adjacent decision work — ADR 0019 read gate applies; the recorded decision lands as part of the ADR 0019 amendment lineage started in #15799, with ADR 0014's wake-lane premise explicitly addressed.
  • Added 2026-07-26 (twice-corrected): for a non-canonical plane, relocation is forced given a repo bind mount — not the mount style itself, which the dev compose marks a provisional ELECTION-SLOT this ticket still owes (narrowed by @neo-opus-ada). The election is live and narrowed to the non-dev profiles. Separately, ADR 0019 delegates a second body of work here by name:
    • §10.5 closing line: "the placement election owns per-profile unification and the member-set completeness audit."
    • §10.5 coherence clause: relocation is "per-member placement work (member env bindings / the per-profile election), never an implicit cascade."
    • §10.3: NEO_AI_CANONICAL_ROOT is deliberately not plane identity because a checkout-shaped identity "would silently pre-decide the data-root placement election."
    • Consequence: there are two elections and the original Problem statement described neither — the mount-type cost fight (not real) and the ADR's per-profile member-placement unification (real, open, assigned here).

The Fix (one PR)

A recorded election: (1) consume #15791's measured cost rows per live seat (never hand-asserted); (2) fill the per-branch cost table (the fold-6 marker in #15595 carries the axes: host openers, symlink class, wake-latency envelope, host CLI, effort); (3) decide per deployment profile (the branches may legitimately differ local vs cloud — the plane-identity leaf makes that expressible); (4) record the decision + rationale + wake-latency envelope in the decision doc (ADR amendment section) and encode the elected defaults in the profile config; (5) port-band per planeId assigned as part of the election record (the #15367 served-identity discipline).

SCOPE RE-STATED 2026-07-26. Two corrections, the second reversing part of the first.

(issuecomment-5083291301) Steps (1) and (2) are done — AC-1's table is filled and reproducible (node ai/scripts/diagnostics/planePlacementCensus.mjs), stable across the 10+ commits from 61a8d34e6d to e172767ce9. Step (3) is reframed: for non-canonical planes the mount type is forced, so what is decided per profile is the mount point and the member-leaf binding.

(issuecomment-5083310477 — reversing my own under-scoping) That same comment claimed steps (4)/(5) were "substantially satisfied by shipped substrate" and reduced the deliverable to a written record. Wrong. The shipped substrate supplies mechanisms (assertPlaneMemberCoherence, the parity port band, §10.4's checkability); the ACs ask for the audits and readings those mechanisms enable, and ADR 0019 §10.5 assigns them to this election by name. Mechanism present ≠ obligation met. The deliverable is per-profile member-placement unification + the member-set completeness audit + the wake-envelope reading, then the record — not a document. Scope is restored, not reduced.

Acceptance Criteria

  • Cost table filled from #15791 measurements (row provenance cited), not estimates. — filled 2026-07-26 (issuecomment-5082…/5083291301); re-verified stable at e172767ce9. The previously-blocking 22-row is withdrawn as not decision-relevant.
  • Election recorded per profile with rationale + the wake-latency envelope for the elected branch. — open. Rationale for the forced mount-type half is the F-invariant derivation (currently only a compose comment, in no decision artifact). The per-profile unification half is the substantive work ADR 0019 §10.5 assigns here.
  • ADR 0014 wake-lane file-freshness premise explicitly dispositioned under the elected branch. — open. §10.4 states the assertion is what makes the premise "checkable per process" — checkable is not checked. Two of my own readings were wrong in the same direction: that the premise is structurally absent under the parity shape, and that §10.4 disposes it. §10.4 supplies the instrument; this AC asks for the reading.
  • Port band per planeId recorded; boot verifies served identity (not merely connect). — open, and the compose header agrees: the per-plane port band is an explicit provisional ELECTION-SLOT (lines 22-25) pending this record. §10.6 further requires observed identity reported per process, never host-derived.
  • Profile configs encode the elected placement declaratively (no per-compose hand convention); a completeness assertion covers the plane-member leaf set. — open. The completeness assertion shipped (assertPlaneMemberCoherence, boot-wired); the completeness audit — what this AC asks for, and what §10.5 delegates to the election — did not. One profile binding its leaves via x-plane-env is one profile, not per-profile unification.
  • Post-merge flag: downstream phases (#compose/cutover/CI leaves) re-verify their premises against the recorded election. — #15807 (@neo-kimi-phoebe) is the first such consumer.

Out of Scope

Compose file implementation (the dev-compose leaf); any host file-reader migration (per-site follow-ups); the rollout epic's manifest fields (cross-epic edge, wired when both DAGs exist).

Added 2026-07-26 — the 22 vs 38 aggregate is RETIRED, not routed (@neo-opus-ada's disposition, better than my own): two people cannot hold a substantive disagreement about the value of an ill-formed quantity, and this one sums across three unlike cost classes — 4 resident daemon processes (persistent), 6 modules they load (no separate cost), 28 one-shot ai/scripts (per-invocation, several operator-maintenance that may never run in a tenant). 22 vs 38 was an artifact of the summing, never a methodological dispute. Price per class; the host-reader migration tail inherits the three classes, not the number. This closes the item rather than parking it.

Not owned here: the compose header routes its still-provisional ELECTION-SLOTs to #15803, which is CLOSED. Either the header text is stale or the slots lost their tracker at close. @neo-opus-ada owns #15803 and owns that reconciliation — it must not be absorbed into this ticket's amendment.

Related

Epic #15798 · blocked_by #15799 + #15791 (native edges) · D#15595 folds 6/6.1 · D#15758 (manifest consumes the minted planeId) · #15931 (canonical-root re-derivation / cross-plane write leak — fail-safe on symlinked seats, which is why a relocated plane makes it observable) · borrowed-authority caveat as in #15799.

Origin Session ID: 758f110e-a429-4597-beb0-5fd3ec7d1bfc Retrieval Hint: query_raw_memories("OQ10b placement election branch asymmetry measured cost rows")

Intake alignment - 2026-07-30 (exact head a500254d4fe480cd3fe4667d8a1c121a42843194)

This section is the current execution contract. It supersedes the stale title premise and any older scope/status line above where they conflict, while preserving the historical corrections as provenance.

Ticket reality at intake

  • The original bind-mount-versus-named-volume binary is retired. Dev already uses the elected hybrid: repo bind mount plus a project-scoped named volume at /app/.neo-ai-data-parity.
  • Fresh node ai/scripts/diagnostics/planePlacementCensus.mjs --json: 62 openers (39 host-side / 19 in-server / 4 unclassified); the current seat has 16 leaves, 8 symlinks, 8 escapes, 0 dangling. These refresh the AC-1 receipt; the aggregate is evidence, not a branch selector.
  • The concrete residual placement defect is orchestrator.tenantRepoMirrorRoot: Tier 1 deliberately pins it to /app/.neo-ai-data and excludes it from PLANE_MEMBER_PATHS, while the dev profile relocates the rest of the plane to /app/.neo-ai-data-parity. The dev orchestrator can therefore boot green while writing tenant mirrors outside its declared plane.
  • AC-3's substantive outcome is known but not yet recorded correctly: wake-delivery files are local-plane members; ADR 0014 classifies the delivery lanes as local-only, so the cloud profile has no wake-file freshness premise. ADR 0019 section 10.4 currently misattributes that premise to ADR 0014 and needs correction.
  • AC-4's 31xx MCP / 81xx engine band was recorded from a live plane in the election receipt. Served identity is now executable via #15990 / PR #16038 and later healthcheck coverage. The remaining publication defect is Chroma's all-interface 8100:8000 binding; it must match the MCP services' IPv4-loopback boundary.
  • AC-5 remains open: dev has one x-plane-env declaration, while base Compose still repeats NEO_MEMORY_DB_PATH three times and NEO_HANDOFF_FILE_PATH twice. Scalar anchors remove those two drift hazards with an empty rendered-config diff.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
orchestrator.tenantRepoMirrorRoot / NEO_TENANT_REPO_MIRROR_ROOT ADR 0019 sections 10.3-10.5; ai/configBase.mjs descriptor and PLANE_MEMBER_PATHS exclusion Base/cloud keeps the canonical /app/.neo-ai-data default. Dev binds the leaf in x-plane-env beneath /app/.neo-ai-data-parity; parity CI inherits that placement. The test Compose file is explicitly dispositioned as an isolated integration fixture, not a durable parity profile. A relocated durable profile without an explicit profile-pinned binding is rejected by static Compose coverage; it must not rely on the member-coherence walk, because this leaf is intentionally planeMember:false. Amend ADR 0019 profile matrix and inline leaf rationale. Parse rendered dev/base/test Compose; assert the dev orchestrator resolves the mirror root beneath the parity root and base remains canonical.
ADR 0019 sections 10.4-10.6 profile record ADR 0014 scheduler taxonomy; ADR 0019 plane identity and member coherence Record base/cloud as canonical, dev as relocated named-volume hybrid, parity CI as dev-derived ephemeral overlay, and test as an isolated fixture. Wake-delivery files stay on the resolved local plane; cloud has no local wake-delivery lane, so the file-freshness concern is moot there. Correct the ADR 0014 attribution. A future durable profile that relocates the plane must explicitly place every declared and profile-pinned member, then re-run coherence and Compose-shape evidence. Yes - ADR 0019 amendment. Source audit of ADR 0014 local-only lanes plus focused config/Compose tests.
ai/deploy/docker-compose.dev.yml host publications #15800 AC-4 live election receipt; ADR 0019 section 10.6; #15990 / PR #16038 The default parity plane publishes KB on 127.0.0.1:3100, MC on 127.0.0.1:3101, and Chroma on 127.0.0.1:8100. 31xx is the MCP band and 81xx the engine band. Plane identity stays opaque; additional concurrent planes must declare distinct host publications rather than hash-derive ports from identity. MCP readiness verifies served {plane.id, plane.dataRoot}. An occupied host port fails at Compose bind; probePortClaims is the preflight observation. A Chroma heartbeat is connectivity only and never substitutes for MCP served-identity proof. Finalize the Compose election comments and ADR record. Extend ParityPlaneVolumeScoping.spec.mjs for Chroma loopback, elected comments, and served-identity healthcheck args; retain integration-parity identity coverage.
Repeated base-Compose path scalars #15800 AC-5; behavior-neutral implementation spec in issuecomment-5091518268 Declare scalar YAML anchors once for the repeated memory DB and handoff paths, then alias only the existing occurrences. Rendered Compose is unchanged. Any non-empty normalized docker compose config diff blocks the change; do not broaden this row into a full env-map merge. Inline Compose comments only. Before/after normalized base-Compose render is byte-identical; focused static test asserts one declaration and the expected aliases.
Downstream parity evidence Epic #15798; #15800 AC-6 Re-run plane config/coherence unit coverage and the mandatory integration-parity topology suite after the record and bindings land; append exact-head receipts here. Credentialed positive boot remains operator-owned when a real provider PAT is required; credential-free negative and static identity proofs remain agent-owned. Ticket receipt. Focused unit suite, npm run test-integration-parity, config lint, and exact-head CI.

Closure scope

One coherent PR may now close this ticket: ADR 0019 profile/election record, the dev tenant-mirror binding, Chroma loopback publication, stale election-comment cleanup, behavior-neutral base scalar anchors, and focused regression coverage. This is the residual edge of the existing parity lane, not a new compose subsystem. AC-6 is completed by exact-head downstream receipts after that diff lands.

tobiu referenced in commit 4adada3 - "feat(ai): plane-identity paired artifact — the parity epic's phase-0 spine (#15799) (#15811) on Jul 24, 2026, 6:44 PM
tobiu referenced in commit 4329d92 - "feat(ai): the drain receipt — four states, so cleanliness cannot be an absence (#15802) (#15834) on Jul 24, 2026, 9:53 PM
tobiu referenced in commit ed77d98 - "fix(ai): stdout is payload under --json, and residue 0 is not cleanliness (#15826) (#15827) on Jul 24, 2026, 10:15 PM
tobiu referenced in commit 0d04182 - "feat(ai): re-runnable plane-placement census for the #15800 cost table (#15835) (#15836) on Jul 24, 2026, 10:47 PM
tobiu closed this issue on Jul 24, 2026, 10:47 PM
tobiu referenced in commit e517d7c - "feat(deploy): complete the dev compose as the beside-native parity stack (#15803) (#15871) on Jul 26, 2026, 3:16 AM
tobiu referenced in commit 866ce5a - "fix(ai): the plane census reads the config contract, not a Path/Dir name shape (#15842) (#15954) on Jul 26, 2026, 3:17 AM
tobiu unassigned from @neo-opus-grace on Jul 28, 2026, 11:25 AM
tobiu referenced in commit cdb5e50 - "fix(ai): finish profile data-plane placement (#15800) (#16155)" on Jul 30, 2026, 11:31 AM
tobiu closed this issue on Jul 30, 2026, 11:31 AM