Reframed 2026-07-31 by the author, per operator direction, before any implementation. The original body treated this as a duplication defect — the parity plane root restated 33 times — and prescribed Compose interpolation. That prescription was right about the mechanism and wrong about the defect: projecting one value would have entrenched a transient migration name in one authoritative place instead of 33 sloppy ones. Better hygiene, identical debt. The original framing is preserved at the end under Superseded framing so the correction stays auditable.
Context
parity names a migration state, not a thing. It meant "the containerized stack running in parity with the native local Agent OS" — a condition that existed only while both stacks coexisted during the #16167 cutover. That cutover has happened. The name now describes a relationship to something that no longer runs, and a future reader has no way to recover what it was parity with.
Operator framing, 2026-07-31: "parity was a transient migration state, which name was never supposed to stay inside substrate, since it has no meaning for future us."
The duplication makes it worse but is not the defect. The defect is that a temporary condition became a durable identifier.
Scope, measured and deliberately narrow (git grep, excluding the generated resources/content/** mirror, 2026-07-31):
| token |
occurrences |
files |
.neo-ai-data-parity |
52 |
9 |
integration-parity |
21 |
8 |
NEO_PARITY* / PARITY_ROOT |
24 |
5 |
neo-local-parity |
18 |
3 |
parity-plane |
4 |
1 |
parity also has a legitimate, ongoing meaning in this repo — equivalence between two artifacts — and that sense is explicitly OUT of scope. config-leaf-parity.json, configTemplateParityProof.spec.mjs, GuideToolParity.spec.mjs, and list_pull_requests field parity all use the word correctly and permanently. A naive grep -il parity returns 1034 tracked files; that number is meaningless here and must not be used to size this work. Only the plane-identity family above is in scope.
Live latest-open sweep: latest 20 open issues read 2026-07-31T13:56:32Z; no equivalent. A2A claim sweep at the same time: no competing claim. #16206 (ai/deploy composable layering) is the nearest neighbour and does not cover naming.
The Problem
A name that encodes a transition cannot survive the transition. Three costs, in increasing severity:
- It is unreadable forward. "Parity with what?" has no answer available to a future maintainer from the substrate alone. The referent — a native host stack that the cutover deleted — is gone. The name is a dangling pointer in prose.
- It misdescribes what the profile now is. The profile is a second, isolated, disposable plane that runs beside the canonical one under its own Compose project, plane id, and relocated root. That is a durable and useful thing. Nothing about it is parity; it is isolation.
- The duplication makes the debt expensive rather than free to fix. Because the root literal is hand-copied 33 times in
docker-compose.dev.yml alone, renaming today is a 33-line edit with no mechanical check that the edits agree. This is why the two problems are one ticket: the naming debt is the defect, and the duplication is what makes it costly.
And the sharpest instance of (3): --expected-plane-data-root "/app/.neo-ai-data-parity" exists to prove the served plane root (ADR 0019 §10.6, "resolvable is not observable"). Its expected value is a third hand-copy of the thing under test, so the desired-vs-observed comparison has two hand-maintained columns — the shape §10.6 names as passing trivially and detecting nothing.
The Architectural Reality
ai/deploy/docker-compose.dev.yml — x-plane-env: &plane-env is a YAML map consumed via merge keys. YAML anchors alias whole nodes, not string prefixes, so *root/sqlite/... does not parse. That is why the file's existing anchor mechanism was not applied to the root; a real constraint, not an oversight.
- The file's own header states the principle it violates: "ONE VALUE, PROJECTED — … divergence is not prevented here, it is inexpressible." True of
plane-id; false of the plane root.
- ADR 0019 §10.3 / §10.7 —
plane.id is opaque and "never derives a filesystem path or port." So the root cannot be derived from the plane id, and any rename needs its own declared value. Verified before proposing, because deriving from the id is the obvious-looking move that the ADR forbids.
- ADR 0019 §10.5 requires per-member placement and forbids implicit cascade: "relocation is per-member placement work … never an implicit cascade." Any remedy must keep a per-member line.
- The canonical profile already uses
${VAR:-default} for this class of problem (${NEO_HOST_BACKUP_ROOT:-${HOME}/.neo-ai/backups}, #16201). The idiom is established in the sibling file.
- ADR 0019 §10.7's matrix row is titled "Dev parity" and carries the name in decision authority, so the ADR moves with the rename.
The Fix
One rename, projected through one value. The two halves are inseparable: the projection is what makes the rename a one-line operation now and forever after.
- Choose a durable name describing what the plane IS. Recommendation:
dev — /app/.neo-ai-data-dev, project neo-local-dev, volume dev-plane, test dir integration-dev. It is accurate today, survives the next migration, and needs no referent. Alternatives considered: sandbox (accurate on disposability, but implies untrusted rather than non-canonical); isolated (accurate but describes a property every non-canonical plane shares, so it does not distinguish); keeping parity (rejected — it is the defect).
- Introduce one interpolated root with a default preserving the new value, and project it into all 27 env bindings, 3 mount targets, and 3 healthcheck args in
docker-compose.dev.yml.
- Rename the plane-identity family across the measured set: compose files, project name, volume,
integration-parity test config and its CI job, ParityPlaneVolumeScoping.spec.mjs, the latency-pair diagnostics, and ADR 0019 §10.7's row.
- Add a static guard asserting the file contains at most one literal spelling of the plane root, so a future hand-copy fails rather than merely being ugly.
Verified empirically before proposing (Compose on the canonical machine, 2026-07-31): interpolation resolves inside a merge-keyed x- extension field, and an override moves every member together:
default: NEO_AI_DB_PATH: /app/.neo-ai-data-parity/sqlite/memory-core-graph.sqlite
PARITY_ROOT=…-teamB: NEO_AI_DB_PATH: /app/.neo-ai-data-teamB/sqlite/memory-core-graph.sqlite
This is not the implicit cascade §10.5 forbids: every member keeps its own line, and a member that must not move simply does not consume the variable — which is how NEO_TENANT_REPO_MIRROR_ROOT's pinning stays expressible. Only the shared prefix literal is deduplicated.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| plane root name |
ai/deploy/docker-compose.dev.yml |
one interpolated root carrying a durable name, projected into every member binding |
absent env resolves the new default; no per-deployment action required |
parity→dev profile comments |
docker compose config render shows one root spelling across all members |
| plane-identity family |
compose files, volume, project, test config, CI job |
renamed off the migration term in one coordinated change |
none — a half-rename leaves two names for one plane, worse than either |
ADR 0019 §10.7 row |
git grep for the retired token returns only the generated mirror |
equivalence-parity surfaces |
config-leaf-parity.json, GuideToolParity.spec.mjs, configTemplateParityProof.spec.mjs |
unchanged — different, legitimate meaning |
none |
— |
those files appear in no diff |
| healthcheck expected root |
ADR 0019 §10.6 |
consumes the same value as the binding it proves |
absent env resolves the same default |
— |
render shows arg and NEO_PLANE_DATA_ROOT equal under default and override |
| literal-count guard |
this ticket |
static spec asserts at most one literal spelling of the plane root |
none — a second literal is the defect |
— |
red-proof: reintroducing a second literal fails the spec |
Decision Record impact
amends ADR 0019 §10.7 — the per-profile placement matrix names this profile "Dev parity" and carries its root literal, so the row changes with the rename. §10.3's opacity rule is not challenged: the root gets its own declared value precisely because the plane id may not derive one. §10.5's per-member placement is preserved by construction.
Acceptance Criteria
Out of Scope
parity as equivalence — config-leaf-parity.json, GuideToolParity.spec.mjs, configTemplateParityProof.spec.mjs, list_pull_requests field parity. Correct, permanent usage.
- The canonical, parity-CI-successor, and local-agent-os placement decisions — only names and the projection change; no plane moves, no member set changes, no port band changes.
ai/deploy composable layering — #16206 owns that.
- Migrating existing dev-plane volume data. The plane is disposable by design; a renamed volume is a fresh volume, and that must be stated in the profile comments rather than silently absorbed.
Avoided Traps
- Deduplicating without renaming. The original prescription. It would have made the transient name more authoritative by giving it a single canonical home — the debt entrenched rather than repaid.
- Renaming without deduplicating. A 33-line hand edit with no mechanical check that the edits agree, leaving the next rename exactly as expensive and the healthcheck still asserting against a hand-copy.
- Deriving the root from
plane.id. The obvious-looking move, and ADR 0019 §10.3/§10.7 explicitly forbid it — the id is opaque and never derives a path or port.
- Sizing this from
grep -il parity. Returns 1034 tracked files by conflating the equivalence sense and the generated mirror. Using it would inflate the work by an order of magnitude and put legitimate files at risk of a careless rename.
- Treating it as cosmetic. The healthcheck consequence is why it is not: a desired-vs-observed check with two hand-copied columns degrades to desired-vs-desired.
- A plain YAML anchor. Anchors alias nodes, not prefixes; recorded so the next reader does not re-derive it.
Related
- #16206 —
ai/deploy composable layering; nearest neighbour, natural merge target if its owner prefers one change over two
- #16201 / PR #16221 — established the
${VAR:-default} idiom for a plane-adjacent path in the canonical profile
- #16167 — the cutover that ended the parity condition this name records
- ADR 0019 §10.3 (opaque id), §10.5 (per-member placement), §10.6 (observed identity), §10.7 (profile matrix)
Superseded framing (original body, 2026-07-31)
Filed as "Dev parity profile restates its plane root 33 times" — a duplication defect with Compose interpolation as the remedy. The counts and the empirical interpolation proof carry forward unchanged; the framing does not. Recorded rather than deleted because the correction is the useful part: a fix that removes duplication of a wrong name makes the wrong name canonical. Deduplication is a multiplier on whatever it deduplicates, so the name has to be right first.
Origin Session ID: 59fd6dbb-fc01-482b-a778-bed01f16e699
Retrieval Hint: parity transient migration state name retired from substrate plane root dev rename compose interpolation one value projected
Context
paritynames a migration state, not a thing. It meant "the containerized stack running in parity with the native local Agent OS" — a condition that existed only while both stacks coexisted during the #16167 cutover. That cutover has happened. The name now describes a relationship to something that no longer runs, and a future reader has no way to recover what it was parity with.Operator framing, 2026-07-31: "parity was a transient migration state, which name was never supposed to stay inside substrate, since it has no meaning for future us."
The duplication makes it worse but is not the defect. The defect is that a temporary condition became a durable identifier.
Scope, measured and deliberately narrow (
git grep, excluding the generatedresources/content/**mirror, 2026-07-31):.neo-ai-data-parityintegration-parityNEO_PARITY*/PARITY_ROOTneo-local-parityparity-planeparityalso has a legitimate, ongoing meaning in this repo — equivalence between two artifacts — and that sense is explicitly OUT of scope.config-leaf-parity.json,configTemplateParityProof.spec.mjs,GuideToolParity.spec.mjs, andlist_pull_requestsfield parity all use the word correctly and permanently. A naivegrep -il parityreturns 1034 tracked files; that number is meaningless here and must not be used to size this work. Only the plane-identity family above is in scope.Live latest-open sweep: latest 20 open issues read 2026-07-31T13:56:32Z; no equivalent. A2A claim sweep at the same time: no competing claim. #16206 (
ai/deploycomposable layering) is the nearest neighbour and does not cover naming.The Problem
A name that encodes a transition cannot survive the transition. Three costs, in increasing severity:
docker-compose.dev.ymlalone, renaming today is a 33-line edit with no mechanical check that the edits agree. This is why the two problems are one ticket: the naming debt is the defect, and the duplication is what makes it costly.And the sharpest instance of (3):
--expected-plane-data-root "/app/.neo-ai-data-parity"exists to prove the served plane root (ADR 0019 §10.6, "resolvable is not observable"). Its expected value is a third hand-copy of the thing under test, so the desired-vs-observed comparison has two hand-maintained columns — the shape §10.6 names as passing trivially and detecting nothing.The Architectural Reality
ai/deploy/docker-compose.dev.yml—x-plane-env: &plane-envis a YAML map consumed via merge keys. YAML anchors alias whole nodes, not string prefixes, so*root/sqlite/...does not parse. That is why the file's existing anchor mechanism was not applied to the root; a real constraint, not an oversight.plane-id; false of the plane root.plane.idis opaque and "never derives a filesystem path or port." So the root cannot be derived from the plane id, and any rename needs its own declared value. Verified before proposing, because deriving from the id is the obvious-looking move that the ADR forbids.${VAR:-default}for this class of problem (${NEO_HOST_BACKUP_ROOT:-${HOME}/.neo-ai/backups}, #16201). The idiom is established in the sibling file.The Fix
One rename, projected through one value. The two halves are inseparable: the projection is what makes the rename a one-line operation now and forever after.
dev—/app/.neo-ai-data-dev, projectneo-local-dev, volumedev-plane, test dirintegration-dev. It is accurate today, survives the next migration, and needs no referent. Alternatives considered:sandbox(accurate on disposability, but implies untrusted rather than non-canonical);isolated(accurate but describes a property every non-canonical plane shares, so it does not distinguish); keepingparity(rejected — it is the defect).docker-compose.dev.yml.integration-paritytest config and its CI job,ParityPlaneVolumeScoping.spec.mjs, the latency-pair diagnostics, and ADR 0019 §10.7's row.Verified empirically before proposing (Compose on the canonical machine, 2026-07-31): interpolation resolves inside a merge-keyed
x-extension field, and an override moves every member together:This is not the implicit cascade §10.5 forbids: every member keeps its own line, and a member that must not move simply does not consume the variable — which is how
NEO_TENANT_REPO_MIRROR_ROOT's pinning stays expressible. Only the shared prefix literal is deduplicated.Contract Ledger Matrix
ai/deploy/docker-compose.dev.ymldocker compose configrender shows one root spelling across all membersgit grepfor the retired token returns only the generated mirrorparitysurfacesconfig-leaf-parity.json,GuideToolParity.spec.mjs,configTemplateParityProof.spec.mjsNEO_PLANE_DATA_ROOTequal under default and overrideDecision Record impact
amends ADR 0019§10.7 — the per-profile placement matrix names this profile "Dev parity" and carries its root literal, so the row changes with the rename. §10.3's opacity rule is not challenged: the root gets its own declared value precisely because the plane id may not derive one. §10.5's per-member placement is preserved by construction.Acceptance Criteria
git grepfor the retired plane-identity tokens returns zero hits outsideresources/content/**, and the equivalence-parityfiles appear in no diff. Both halves asserted — a rename that also renames the legitimate sense is a failure, not an over-delivery.docker compose -f docker-compose.dev.yml configrenders one root spelling across every member binding, mount target, and healthcheck arg.assertPlaneCoherenceandassertPlaneMemberCoherencepass unchanged.Out of Scope
parityas equivalence —config-leaf-parity.json,GuideToolParity.spec.mjs,configTemplateParityProof.spec.mjs,list_pull_requestsfield parity. Correct, permanent usage.ai/deploycomposable layering — #16206 owns that.Avoided Traps
plane.id. The obvious-looking move, and ADR 0019 §10.3/§10.7 explicitly forbid it — the id is opaque and never derives a path or port.grep -il parity. Returns 1034 tracked files by conflating the equivalence sense and the generated mirror. Using it would inflate the work by an order of magnitude and put legitimate files at risk of a careless rename.Related
ai/deploycomposable layering; nearest neighbour, natural merge target if its owner prefers one change over two${VAR:-default}idiom for a plane-adjacent path in the canonical profileSuperseded framing (original body, 2026-07-31)
Filed as "Dev parity profile restates its plane root 33 times" — a duplication defect with Compose interpolation as the remedy. The counts and the empirical interpolation proof carry forward unchanged; the framing does not. Recorded rather than deleted because the correction is the useful part: a fix that removes duplication of a wrong name makes the wrong name canonical. Deduplication is a multiplier on whatever it deduplicates, so the name has to be right first.
Origin Session ID:
59fd6dbb-fc01-482b-a778-bed01f16e699Retrieval Hint:
parity transient migration state name retired from substrate plane root dev rename compose interpolation one value projected