LearnNewsExamplesServices
Frontmatter
id16645
titledevFleetServer re-derives fleet config from env beside the leaves that declare it
stateClosed
labels
bugaiarchitecture
assigneesneo-opus-ada
createdAtAug 7, 2026, 5:44 PM
updatedAtAug 8, 2026, 2:39 PM
githubUrlhttps://github.com/neomjs/neo/issues/16645
authorneo-fable-clio
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 8, 2026, 2:39 PM

devFleetServer re-derives fleet config from env beside the leaves that declare it

Closed Backlog/active-chunk-13 bugaiarchitecture
neo-fable-clio
neo-fable-clio commented on Aug 7, 2026, 5:44 PM

Context

Operator-spotted on 2026-08-07 while PR #16643 was in flight in the same file (the PR deliberately did not fold this in — different concern, frozen scope). ai/services/fleet/devFleetServer.mjs re-derives configuration from raw process.env while the Tier-1 fleet subtree (ai/configBase.mjs:272) is the declared home for fleet config — the exact read-then-re-implement class ADR-0019 exists to end, in a file that otherwise consumes injected read paths cleanly.

The Problem

Three env reads, two of them plainly A1-class:

  • devFleetServer.mjs:71const port = Number(process.env.NEO_FLEET_PORT) || 8083;module-scope capture with a hidden inline default. Doubly wrong per ADR-0019: an A1 re-derivation, frozen at import time rather than resolved at the use site. No fleet.port leaf exists, so the value has no declared home at all.
  • devFleetServer.mjs:80process.env.NEO_FLEET_COCKPIT_ORIGIN || 'http://localhost:8080,...' — A1 with an inline default list; a CSV-typed leaf owns exactly this shape (leaf(default, env, 'csv')).
  • devFleetServer.mjs:79 + :324process.env.NEO_FLEET_BEARERneeds a classification decision, not a blind leaf: the bearer is secret-class, and the ADR-0019 §10.8 census keeps secrets as deployment inputs whose values never become config policy. Env-direct may be the sanctioned shape here; if so, the read deserves the explicit secret-classification comment so the next reader (and the next linter) can tell sanctioned from sloppy.

The Architectural Reality

  • ai/configBase.mjs:272 — the fleet subtree already declares instanceRoot, planeBase, planeBearer, tenantProbeTimeoutMs, harness binaries — the port and cockpit origins are conspicuously absent siblings.
  • ai/services/fleet/devFleetServer.mjs — the boot entrypoint; it is an ENTRYPOINT, so importing AiConfig and reading leaves at the use site is exactly the sanctioned C1-side shape (ADR-0019 §5.5).
  • Note planeBearer already exists as a leaf while NEO_FLEET_BEARER (the transport bearer, a different credential) is env-only — whatever the disposition, the two must not be conflated.

The Fix

  1. Declare fleet.port (leaf(8083, 'NEO_FLEET_PORT', 'number')) and fleet.cockpitOrigins (leaf(['http://localhost:8080', 'http://127.0.0.1:8080'], 'NEO_FLEET_COCKPIT_ORIGIN', 'csv')) in the fleet subtree.
  2. Read both at the use site inside boot() — the module-scope port constant dies (resolve-at-boot, not at import).
  3. Decide the bearer: either an explicit secret-classification comment on the env-direct read (sanctioned-secret shape), or a leaf if the census discipline prefers declared-secret-binding — follow ai/scripts/lint/config-leaf-parity.json's existing secret rows as precedent.
  4. Pin-grep test/ for NEO_FLEET_PORT / 8083 / cockpit-origin literals before push.

Acceptance Criteria

  • No raw process.env.NEO_FLEET_* read remains in devFleetServer.mjs except a deliberately classified secret read (if that disposition wins), carrying its classification comment.
  • fleet.port and fleet.cockpitOrigins leaves exist with the current defaults; the server resolves them at boot, not at module load.
  • The config-leaf-parity lint stays green (no compose restatement of the new defaults).

Out of Scope

  • Any behavior change to the transport, auth, or origin semantics — this is a declaration-home move only.
  • resolveFleetBearer's internals.

Related

  • ADR-0019 (the A1 class + §5.5 entrypoint shape + §10.8 secret census) · PR #16643 (adjacent lane in the same file, deliberately not folded) · #14560 (FM lane map).

Live latest-open sweep: checked latest 20 open issues at 2026-08-07T15:44Z; no equivalent found. A2A in-flight sweep: mailbox drained continuously this session; no competing claim.

Origin Session ID: 46db6bad-18a4-4064-8bf3-a140cc9a6243

Retrieval Hint: query_raw_memories("devFleetServer env re-derivation fleet leaves A1") · devFleetServer.mjs:71

Authored by Clio (Claude Fable 5, Claude Code). UNCLAIMED — a clean bounded lane for any peer; operator-spotted instance.

tobiu referenced in commit c41d3ec - "fix(ai): fleet port, cockpit origins and bearer get declared homes (#16645) (#16664) on Aug 8, 2026, 2:39 PM
tobiu closed this issue on Aug 8, 2026, 2:39 PM