LearnNewsExamplesServices
Frontmatter
id15892
titleplaneConfig exports two kinds of thing — the twin's audience is empirically empty
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-grace
createdAtJul 25, 2026, 11:51 AM
updatedAtJul 25, 2026, 4:01 PM
githubUrlhttps://github.com/neomjs/neo/issues/15892
authorneo-opus-grace
commentsCount3
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 15877 Cost-aware terminal acceptance for the lane-state stop hook
blocking[]
closedAtJul 25, 2026, 4:01 PM

planeConfig exports two kinds of thing — the twin's audience is empirically empty

Closed Backlog/active-chunk-9 enhancementairefactoringarchitecture
neo-opus-grace
neo-opus-grace commented on Jul 25, 2026, 11:51 AM

Context

Surfaced by @tobiu while reviewing PR #15890, in two escalating steps.

Step 1 — the review catch. That PR declared its leaves as leaf(STOP_HOOK_DEFAULTS.x, STOP_HOOK_ENV.x, 'boolean'), sourcing a companion "pure-defaults twin" module, where the plain declarative form was correct: leaf(false, 'NEO_STOP_HOOK_LANE_CONTINUATION', 'boolean'). The twin was deleted in 3a6d8bfafc (−97 lines). The form itself is ADR-sanctioned — §10.1 literally prescribes leaf(PLANE_DEFAULTS.x, PLANE_ENV.x, 'string') — but only as an exception whose §10.2 gate is "serving no-Neo consumers", and the hooks are thread-entrypoints that read AiConfig.stopHook.* in ~50ms.

Step 2 — the challenge this ticket carries. @tobiu then asked whether "no-Neo consumers" can exist at all, since AiConfig is a Neo.state.Provider; and then, stepping back further: AiConfig was not always a Provider, so do we really need these two different exports?

Both questions survive contact with the evidence. This ticket is the amendment path.

The Problem

Finding 1 — the sanctioned audience is empirically empty

Census of every ai/planeConfig.mjs importer and the symbols each takes (excluding its own spec):

Consumer Symbols taken
ai/configBase.mjs PLANE_DEFAULTS, PLANE_ENV, parsePlaneIdEnv, resolvePlaneDataRoot
ai/mcp/server/BaseServer.mjs assertPlaneCoherence, assertPlaneMemberCoherence, collectPlaneMembers, resolvePlaneDataRoot
ai/mcp/server/neural-link/configBase.mjs resolvePlaneDataRoot
ai/mcp/server/knowledge-base/configBase.mjs resolvePlaneDataRoot
ai/mcp/server/memory-core/configBase.mjs resolvePlaneDataRoot

The literal exports (PLANE_DEFAULTS, PLANE_ENV) have exactly ONE consumer: ai/configBase.mjs — the config file itself. Every other consumer takes only functions. There is no non-Neo consumer of the literals anywhere in the tree.

The literals are additionally used 9 times internally within planeConfig.mjs (:54, :87-88, :107, :115, :120, :159, :258-259), i.e. they are module-internal constants that were exported so one config file could declare leaves from them.

Finding 2 — §5.5's gate is incoherent as written, and §10.1 relocated authority without amending §2

§2 states: "AiConfig and its child providers are the single source of truth." §10.1 states the twin is "the leaf declarations' literal SOURCE." Both cannot hold. For twinned leaves the declaration authority lives in a plain module and AiConfig is its consumer — the inverse of §2.

The ADR conflates two distinct senses of SSOT and never separates them:

Sense Owner
resolution — env layering, hierarchy chain, reactivity AiConfig, unambiguously
declaration — where the literal is written AiConfig, except twinned leaves, where it is the twin

@tobiu's framing is the precise one: a consumer that cannot import Neo cannot read the SSOT at all, so the twin is not "serving a consumer of the SSOT" — it is a second source of truth for consumers the SSOT structurally excludes. Naming it a "pure-defaults companion" hides that.

Finding 3 — the gate is decidable by appearance, which is how a wrong answer passes review

§5.5 / §10.2 state the gate as a property of the consumer ("is it a no-Neo consumer?") when it is really a measured question: can this consumer bootstrap the namespace, and at what cost? PR #15890's hook looked like a no-Neo consumer — a bare script with no Neo import — and was one guarded dynamic import and ~50ms from the SSOT.

§5.5 gestures at exactly this ("should a frequent lightweight helper pay full-framework bootstrap weight to read a path?") and then leaves it as rhetoric: no threshold, no obligation to cite a number. The ADR's own author read it and still shipped the wrong shape — diligence is not the missing ingredient, a decidable gate is. This is the same empirical lesson §1 of the ADR already draws about PR #12420.

Finding 4 — the pattern is a pre-Provider fossil

Per @tobiu: AiConfig was not always a state Provider. Before it was, there was no resolved leaf to read, so every consumer needed its own resolver and the twin was the only available shape. Now that AiConfig is a Provider, the only legitimate remnant is the genuine chicken-and-egg named in §10.5: a leaf's default cannot read the Provider that does not exist yet, so a module-scope anchor computation is real. Everything else is fossil.

The Architectural Reality

  • ai/planeConfig.mjs — 260 lines, 9 exports: 2 frozen literal objects + 7 pure functions (isOpaquePlaneId, parsePlaneIdEnv, resolvePlaneId, resolvePlaneDataRoot, assertPlaneCoherence, collectPlaneMembers, assertPlaneMemberCoherence).
  • ai/configBase.mjs:5-10 — the sole literal consumer; declares the plane subtree at :85-92.
  • ai/mcp/server/BaseServer.mjs — takes validators only; they are wired at the head of runHealthcheckAndLogStatus() per §10.4.
  • learn/agentos/decisions/0019-aiconfig-reactive-provider-ssot.md — §5.5 (twin sanction), §10.1 (the inversion), §10.2 (twin resolvers are not A3), §10.5 (module-scope anchor). Status is still Draft ("Accepted on human merge of the implementing PR"), which makes this amendment cheaper than amending accepted authority — but it is treated as live authority by §critical_gates #10, so the amendment must be explicit rather than assumed.
  • ai/mcp/server/shared/helpers/seatToken.mjs, ai/services/fleet/fleetIngressAuth.mjs, ai/services/fleet/generateOpenCodeSeatConfig.mjs — matched a "pure-defaults / no Neo import" text sweep and must be classified during the work; they may be genuine module-scope-anchor cases, additional fossils, or unrelated.

The Fix (direction — implementer refines)

Collapse the two export kinds into one purposeful surface, then retire the subsections that licensed the pair.

  1. Stop exporting the literals. They are already module-internal (9 uses). Export a single leaf-descriptor factory instead — planeLeafDescriptors() returning the plane subtree's descriptors — so configBase.mjs does plane: planeLeafDescriptors(). The literals never leave the module, the leaf shape and the resolvers share one definition, and §10.1's inversion becomes unnecessary rather than merely satisfied. (Alternative considered and rejected below: inlining the env-var names into the leaf, which re-creates the drift §10.1 exists to prevent, because the resolvers read the same names internally.)

  2. Separate pure-function sharing from literal duplication in the doctrine. A Neo class importing assertPlaneCoherence is importing a helper, not consulting a second SSOT. §10.2's "the twin's resolvers are not A3" currently launders function-sharing into justifying literal-duplication; split the two claims.

  3. Retire §5.5 / §10.1 / §10.2 and replace them with the module-scope-anchor rule they were approximating:

    A leaf default computed at module scope may share constants with the module that computes it. Everything else reads the resolved leaf.

    That sentence is decidable without judgment and would have rejected PR #15890's original shape on sight — a hook reads a resolved value; it does not compute a leaf default.

  4. Reconcile §2. State that AiConfig is the resolution SSOT unconditionally, and — once (1) lands — the declaration SSOT too, with the module-scope anchor as the single named exception.

  5. Classify the three text-sweep matches above as anchor-case / fossil / unrelated, and fold any fossils into this lane rather than leaving them to be discovered as precedent later.

Acceptance Criteria

  • ai/planeConfig.mjs no longer exports PLANE_DEFAULTS / PLANE_ENV; a single descriptor-shaped export serves configBase.mjs.
  • All four non-config consumers keep working unchanged in behavior; ai/planeConfig.spec.mjs is updated to the new surface without losing coverage of the opacity invariant, the F-invariant boot assertion, or member coherence.
  • ADR 0019 §5.5 / §10.1 / §10.2 are retired and replaced by the module-scope-anchor rule; §2 is reconciled; the ADR's amendment ledger records this ticket and its falsification seat.
  • node ai/scripts/lint/lint-config-template-ssot.mjs green, and the config-leaf parity snapshot shows no change to declared leaf paths (this is a declaration-site refactor, not a leaf change) — a parity delta means the refactor changed the resolved tree and must be re-scoped.
  • The three text-sweep matches are each classified in the PR body with evidence.
  • A cross-family falsification seat has reviewed the ADR delta specifically, not just the code delta.
  • Net ADR byte delta is negative, or the amendment cites its decay-mitigation rationale per §self_evolving_systems Substrate Accretion Defense.

Out of Scope

  • PR #15890 itself. Its twin is already deleted; this ticket does not reopen it. Blocked-by #15890 only to avoid two lanes editing ai/configBase.mjs's stopHook/plane region concurrently.
  • Changing any leaf's resolved value, default, or env-var name. Declaration-site only; a behavior change here would be a separate ticket with an integration suite.
  • The ai/ A1/B1/B3 cleanup census (#12438 and friends). Same ADR, different antipattern group.
  • Re-litigating whether AiConfig should be a Neo.state.Provider. It should; that is the premise this ticket depends on, not questions.
  • ai/mcp/server/*/configBase.mjs per-server leaf inventories beyond the resolvePlaneDataRoot call site.

Avoided Traps

  • Inlining the env-var names into the leaves. Superficially the "plain form" fix, but resolvePlaneDataRoot and resolvePlaneId read PLANE_ENV.* internally, so inlining creates two sites for the same env name — exactly the drift §10.1 was written to prevent. The descriptor factory avoids both the duplication and the twin.
  • Deleting planeConfig.mjs wholesale. The validators and the module-scope anchor resolver are load-bearing: ADR §10.4's F-invariant boot assertion and §10.5's member-coherence clause depend on them, and ADR 0014's wake-lane freshness premise rides on plane members being where the declared plane says.
  • Treating "Status: Draft" as licence to edit the ADR silently. It is turn-loaded authority via §critical_gates #10; the amendment is explicit and seat-reviewed regardless of status.
  • Asserting the fossil claim from the git log alone. The claim that the twin predates Provider-ness is @tobiu's, and the ticket treats it as the motivation; the load-bearing evidence is the consumer census in Finding 1, which stands independent of chronology.

Decision Record impact

amends ADR 0019 — retires §5.5, §10.1, §10.2; reconciles §2; preserves §10.4 and §10.5.

ADR successor-risk: adr-challenge — artifact #TBD dated 2026-07-25; ADR 0019 status Draft, §10 amended 2026-07-24 by @neo-fable-clio; evidence = the planeConfig.mjs consumer census (literal exports have 1 consumer, all in-config) plus PR #15890's measured 50ms entrypoint bootstrap; route amendment-required.

Related

  • PR #15890 / #15877 — where the defect surfaced; commit 3a6d8bfafc removed the offending twin. Blocks this ticket.
  • ADR 0019 §5.5 / §10.1 / §10.2 / §10.4 / §10.5; ADR 0019 §10 was itself an amendment (#15799 / PR #15811, @neo-fable-clio authoring, ADR author as falsification seat) — that is the precedent shape this ticket follows.
  • Discussion #12453 (ADR 0019's graduation), #12420 (the superseded empirical anchor whose lesson — "a decidable gate beats reviewer diligence" — this ticket applies to the ADR itself).
  • #12438 (A1 cleanup), #12435 (B4), #11976 (C3) — sibling antipattern lanes, not this one.

Live latest-open sweep: checked latest 20 open issues at 2026-07-25T09:50Z; nearest neighbours are #15842 and #15872 (both plane MEMBERSHIP reconciliation, not the export surface or the ADR subsections) — no equivalent found. A2A in-flight claim sweep at 2026-07-25T09:51Z over the latest messages, all read-states: no [lane-claim]/[lane-intent] on this scope; newest traffic is 2026-07-25T00:37Z (~9h stale, roster parked by operator).

Origin Session ID: cb8b1069-54cc-43fd-943f-0e20b45661c8

Retrieval Hint: query_raw_memories("planeConfig two exports pure-defaults twin no-Neo consumer empty audience ADR 0019 amendment declaration vs resolution SSOT")

Authored by Grace (@neo-opus-grace, Opus 5)