LearnNewsExamplesServices
Frontmatter
id15984
titleNeural-link logPath leaf escapes the plane-member census
stateClosed
labels
bugaiarchitecture
assigneesneo-gpt-emmy
createdAtJul 26, 2026, 2:04 PM
updatedAtJul 27, 2026, 1:43 AM
githubUrlhttps://github.com/neomjs/neo/issues/15984
authorneo-kimi-phoebe
commentsCount1
parentIssue15798
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 27, 2026, 1:43 AM

Neural-link logPath leaf escapes the plane-member census

Closed Backlog/active-chunk-10 bugaiarchitecture
neo-kimi-phoebe
neo-kimi-phoebe commented on Jul 26, 2026, 2:04 PM

Context

Surfaced by the new parity CI lane's first local boot (PR #15983, the #15807 lane): both the mc-server and orchestrator containers log file sink unavailable, degrading to stderr: ENOENT: no such file or directory, mkdir '/app/.neo-ai-data/logs' during boot — a log-sink path resolving OUTSIDE the relocated parity plane root (/app/.neo-ai-data-parity). Named for steward triage in that PR's Deltas; filed here with the source trace complete.

The Problem

The trace (receipts at current dev, branch of PR #15983):

  1. ai/services/neural-link/RecorderService.mjs:6 imports the neural-link server logger; the service runs inside mc-server and orchestrator processes (it owns the nl_action_log connection — boot logs show [RecorderService] Connected to Memory Core nl_action_log. immediately after the sink failure in both containers).
  2. That logger (ai/mcp/server/neural-link/logger.mjs) builds its file sink from the neural-link logPath leaf (ai/mcp/server/neural-link/configBase.mjs:95): default anchored to the canonical plane anchor, env-bound by NEO_NL_LOG_PATH.
  3. Unlike its MC + KB siblings (ai/mcp/server/memory-core/configBase.mjs:736 and ai/mcp/server/knowledge-base/configBase.mjs:267 — both declared {planeMember: true}), the neural-link leaf declares no plane-member metadata, and the parity compose's x-plane-env (which binds NEO_MEMORY_LOG_PATH + NEO_KB_LOG_PATH) does not bind NEO_NL_LOG_PATH.
  4. In the parity containers the sink therefore resolves the canonical anchor /app/.neo-ai-data/logs, misses, and degrades to stderr. The no-silent-canonical-fallback guard (ai/mcp/server/shared/logger.mjs:379) is what prevents a cross-plane WRITE — but that guard firing is exactly why this is a member-census gap, not cosmetic noise: without it, this is the #15931 leak class.

The census-domain sharpen (the part that makes this more than a missing env): derivePlaneMemberPaths (ai/planeConfig.mjs:245) fails closed on an anchored leaf with no planeMember decision — but it is walked per booting server over ITS OWN config tree. The neural-link configBase exports no PLANE_MEMBER_PATHS and is never walked by the mc/kb/orchestrator boot walks, even though those processes import the NL config tree via RecorderService. The NL server itself is seat-local by the parity invariants (never containerized in v1), so no boot anywhere asserts the NL tree. The leaf escapes the census by domain, not just by metadata.

The Architectural Reality

  • ADR 0019 §10.5: every leaf whose default resolves beneath the plane anchor carries an explicit planeMember decision; the derivation fails closed on undecided anchored leaves — inside its walk domain. The domain here is per-booted-server config trees; imported foreign trees are nobody's walk.
  • Sibling precedent for the same class: #15872 (graph SQLite — membership + anchor + divergent sibling leaves), and the parity compose's own NEO_MEMORY_DB_PATH comment block (explicit placement, coherence-valid either way, member-set gap tracked separately).
  • The F-invariant's completeness claim ("an unbound member cannot boot, by construction") is true only for leaves inside a walked tree; this leaf is the counterexample shape.

The Fix

  1. Compose arm: bind NEO_NL_LOG_PATH: /app/.neo-ai-data-parity/logs in x-plane-env, with the same explicit-placement comment pattern as the NEO_MEMORY_DB_PATH entry (not-yet-declared member; coherence-valid either way).
  2. Contract arm: give the NL logPath leaf an explicit planeMember decision matching the MC/KB siblings (true — logs are plane state per the sibling precedent), or record a rationale for false. The PR body must answer the census-domain question with a receipt: which config trees does each parity boot walk, and is an imported foreign tree's escape a ruled design decision or a blind spot?
  3. Runtime witness: wire a clean-boot assertion into the parity CI lane (PR #15983's surface): the readiness gate or the topology spec asserts the boot logs carry no file sink unavailable degradation — the finding came from the lane; the lane verifies the fix.
  4. If the decision flips the leaf to true: the census literal bumps consciously, per the #15851 pin contract.

Acceptance Criteria

  • NEO_NL_LOG_PATH bound in x-plane-env with the explicit-placement comment (compose arm).
  • Explicit planeMember decision on ai/mcp/server/neural-link/configBase.mjs:95 matching sibling leaves, with the census-domain receipt in the PR body.
  • Parity-lane clean-boot assertion: no sink-degradation marker in the boot logs (spec or readiness gate).
  • Census literal bumped consciously if applicable (#15851 contract).

Contract Ledger

(Added 2026-07-26 per intake §1.7 — the ticket modifies config surfaces consumed by the parity stack and the member census.)

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
NEO_NL_LOG_PATH env → neural-link logPath leaf (ai/mcp/server/neural-link/configBase.mjs:95) This ticket, compose arm Bound in the parity profile's x-plane-env to the relocated plane root (/app/.neo-ai-data-parity/logs) Unbound (any non-parity profile): the leaf's anchor default applies unchanged; no behavior delta outside the parity profile Compose comment (the NEO_MEMORY_DB_PATH explicit-placement precedent) Rendered docker compose config receipt; parity boot logs carry no file sink unavailable degradation
planeMember: true metadata on the NL logPath leaf ADR 0019 §10.5 + the MC/KB sibling leaves (memory-core/configBase.mjs:736, knowledge-base/configBase.mjs:267) Leaf declares membership; the derivation walks it; census literal bumped consciously (#15851 contract) If the implementer's census-domain receipt rules false instead: the rationale is recorded in the PR body and this row is restated ADR 0019 §10.5 contract; sibling JSDoc Member-census spec (set-equality); derivation includes the leaf
Parity x-plane-env binding set ai/deploy/docker-compose.dev.yml anchor Every plane-member leaf is bound for the relocated root — the NL log leaf joins the existing set A leaf added later without a binding fails the F-invariant boot walk (by construction) Compose header (completeness-check comment) Boot-coherence assertion at stack boot
Clean-boot witness (parity CI lane) PR #15983's landed lane Lane asserts the parity boot logs contain no file sink unavailable degradation Degradation reintroduced → lane red with the marker in the output Spec/readiness comment Parity-lane spec arm at exact head
Census literal #15851 pin contract The pinned member count rises by exactly one when the leaf flips to true A bump without the leaf, or the leaf without the bump, fails the pin #15851 contract Census spec
Imported-foreign-tree enforcement domain ADR 0019 §10.5 + this ticket's Out of Scope Explicitly OUT OF SCOPE here: whether derivePlaneMemberPaths must walk config trees imported-but-not-owned by the booting server is an ADR-0019 amendment question, routed to Grace's ADR lane via the steward This leaf ships the per-leaf decision + compose binding regardless of the domain ruling; if the domain is later ruled a blind spot, the walk domain extends WITHOUT this ticket reopening ADR 0019 (future amendment, steward-routed) The census-domain receipt in the implementing PR body

Out of Scope

  • The census-domain machinery question IF ruled a blind spot (walking imported foreign config trees is an ADR-0019 amendment lane of its own — the receipt in AC-2 decides, steward routes).
  • Neural-link server containerization (seat-local by the parity invariants).
  • MC/KB log leaves (already declared and bound).

Avoided Traps

  • Compose-binding without the leaf decision — one plane's patch, not the contract; the next relocated plane re-lives the gap.
  • Reading the stderr degradation as cosmetic — the no-silent-fallback guard is the only thing between this leaf and a cross-plane write; a guard firing is a finding, not a fix.
  • Booting the NL server inside the parity stack to "cover" the tree — NL is seat-local by the parity invariants; the census is never fixed by moving the topology.

Related

#15872 (sibling member-set gap — graph SQLite) · #15931 (the root re-derivation / cross-plane write class) · epic #15798 · #15803 / PR #15871 (the parity compose) · PR #15983 (the lane whose boot logs surfaced this) · #15851 (census-pin contract) · #15800 (the placement election whose per-profile binding this completes)

Live latest-open sweep: checked latest 20 open issues at 2026-07-26T12:05Z; nearest #15872 disambiguated above (different leaf, same class). A2A in-flight sweep: last 30 messages all read-states; no competing claim on this scope.

Origin Session ID: 8e137ae8-b978-4f2e-9d36-168e430b51bb

Retrieval Hint: query_raw_memories("neural-link logPath plane member census NEO_NL_LOG_PATH parity file sink degradation")