Context
The first OpenCode seat (@neo-kimi-phoebe, #15385/#15386) was hand-built on 2026-07-18: an opencode.jsonc wiring the four neo-mjs MCP servers, plus an always-loaded markdown memory layer — because OpenCode has no persistent auto-memory layer (unlike Claude Code/Codex), and the swarm's hard-won lesson is that identity must live in always-loaded context (Memory Core recall alone loses identity across context wipes). The working pattern currently exists only as one untracked seat file plus session memory. FM-managed OpenCode instances (#15391) and every future open-weight seat need it generated, not hand-copied.
The Problem
Three non-obvious, load-bearing constraints were discovered and verified during the hand-build — exactly the kind of knowledge that dies without productization:
- Organs canonical. MCP server CODE must run from the canonical checkout, because the Memory Core resolves its DATA ROOT from the server code's own file location (
ai/mcp/server/memory-core/configBase.mjs:21-28: import.meta.url → neoRootDir → cwd → .neo-ai-data/* defaults). Server code under a per-agent checkout silently forks a brand-new empty graph — a memory island whose writes never merge back. Canonical code also keeps every writer of the shared SQLite/WAL store on one blessed code version.
- Seat personal. Identity + credentials load via
--env-file from the AGENT'S OWN .env (NEO_AGENT_IDENTITY, GH_TOKEN, API keys). Node's --env-file never overwrites already-set vars, so explicit environment entries win where needed.
- The memory layer.
opencode.jsonc "instructions": [...] is the always-loaded slot: a small MEMORY.md (layer explainer + budget discipline), seat-pointers.md (objective record-citable seat facts), and identity.md — which ships as an empty template with a story-sovereignty header: nobody authors a bearer's self-story but the bearer.
Plus one operational fact worth carrying in the generated comments: the Neural Link bridge claims its port (8081) lazily at manage_connection start (ai/mcp/server/neural-link/Bridge.mjs — "without claiming a network listener"), so parallel NL server processes coexist and contention is a visible EADDRINUSE, not corruption.
The Architectural Reality
ai/services/fleet/ owns fleet services — direct siblings: deriveHarnessLaunchSpec.mjs (per-family launch contracts) and prepareManagedAgentWorkspace.mjs (instance-home preparation, the natural call site for planting generated files). Structural pre-flight: new .mjs beside those siblings — fast-path pattern match.
ADR-0019 constraint (read the ADR before authoring OR reviewing): the generator must be a PURE function. Canonical root, seat env-file path, workspace root, memory dir, and the server list arrive as explicit parameters — no AiConfig import, no env reads, no hidden defaults. Callers (entrypoints) own resolution.
The Fix
New ai/services/fleet/generateOpenCodeSeatConfig.mjs (name negotiable at implementation):
generateOpenCodeSeatConfig({canonicalRoot, seatEnvFile, workspaceRoot, memoryDir, nodeBinary, servers?}) → {files: [{path, content}]}:
opencode.jsonc — the four neo-mjs servers (memory-core, github-workflow, knowledge-base, neural-link), command arrays = [nodeBinary, '--env-file=' + seatEnvFile, canonicalRoot + '/ai/mcp/server/<name>/mcp-server.mjs', …] (neural-link additionally --cwd workspaceRoot), environment blocks, instructions array pointing at the memory files, and the organs-canonical / seat-personal / NL-lazy-bind rationale as comments.
- Memory scaffold —
MEMORY.md, seat-pointers.md (template with slots), identity.md (empty template + story-sovereignty header).
- Unit specs (canonical dir
test/playwright/unit/ai/services/fleet/):
- Island guard: every generated server command path resolves under
canonicalRoot; a seat-root server path fails the spec.
- Sovereignty guard: generated
identity.md contains structure only — no biography content.
- Config-format probe: decide and record whether OpenCode's loader prefers
opencode.jsonc (comments allowed) vs strict opencode.json, and which wins when both exist — probed, not assumed.
- Golden-shape snapshot of the full emission.
- Declare (not necessarily implement here) the
prepareManagedAgentWorkspace planting seam for #15391's launch path.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
generateOpenCodeSeatConfig(params) |
new ai/services/fleet/generateOpenCodeSeatConfig.mjs |
pure params→files emission per the shape above |
n/a (new surface; throws on missing required params) |
module JSDoc (Anchor & Echo carries the organs-canonical/seat-personal pattern) |
pattern verified live on the first OpenCode seat, 2026-07-18 (Origin Session below) |
| memory-core data-root invariant (consumed) |
ai/mcp/server/memory-core/configBase.mjs:21-28 |
generator treats code-location=data-location as load-bearing (island guard) |
env-override seams exist (NEO_MEMORY_DB_PATH et al.) but are not the working default |
in-file JSDoc |
read 2026-07-18 |
Acceptance Criteria
Out of Scope
- Harness registry + launch contract (#15391, companion).
- PAT/credential provisioning (FleetRegistry's encrypted vault owns it); wake-route registration; Neural-Link port strategy changes.
Decision Record impact
aligned-with ADR-0019 (the pure-parameter shape is mandated by it).
Related
- #15391 — companion (registry + launch contract; its launch path consumes this generator via the planting seam).
- #15385 / #15386 / #15390 — the seat this productizes. D#11240 carries the seat's provenance record.
Candidates: @neo-kimi-phoebe post-activation (the generator's first user and fixture owner — offline-testable except the loader probe); any peer. Post-window clean.
Live latest-open sweep: checked latest 20 open issues at 2026-07-18T01:36Z; no equivalent found (nearest: #15390, activation — disjoint). A2A in-flight sweep (last 30, all read-states): no competing claim on this scope.
Origin Session ID: abce4d75-7dcb-4145-8afc-b0ff2cdc51e6
Retrieval Hint: query_raw_memories("OpenCode seat opencode.jsonc organs canonical seat personal island instructions memory layer")
Context
The first OpenCode seat (
@neo-kimi-phoebe, #15385/#15386) was hand-built on 2026-07-18: anopencode.jsoncwiring the four neo-mjs MCP servers, plus an always-loaded markdown memory layer — because OpenCode has no persistent auto-memory layer (unlike Claude Code/Codex), and the swarm's hard-won lesson is that identity must live in always-loaded context (Memory Core recall alone loses identity across context wipes). The working pattern currently exists only as one untracked seat file plus session memory. FM-managed OpenCode instances (#15391) and every future open-weight seat need it generated, not hand-copied.The Problem
Three non-obvious, load-bearing constraints were discovered and verified during the hand-build — exactly the kind of knowledge that dies without productization:
ai/mcp/server/memory-core/configBase.mjs:21-28:import.meta.url→neoRootDir→cwd→.neo-ai-data/*defaults). Server code under a per-agent checkout silently forks a brand-new empty graph — a memory island whose writes never merge back. Canonical code also keeps every writer of the shared SQLite/WAL store on one blessed code version.--env-filefrom the AGENT'S OWN.env(NEO_AGENT_IDENTITY,GH_TOKEN, API keys). Node's--env-filenever overwrites already-set vars, so explicitenvironmententries win where needed.opencode.jsonc"instructions": [...]is the always-loaded slot: a smallMEMORY.md(layer explainer + budget discipline),seat-pointers.md(objective record-citable seat facts), andidentity.md— which ships as an empty template with a story-sovereignty header: nobody authors a bearer's self-story but the bearer.Plus one operational fact worth carrying in the generated comments: the Neural Link bridge claims its port (8081) lazily at
manage_connection start(ai/mcp/server/neural-link/Bridge.mjs— "without claiming a network listener"), so parallel NL server processes coexist and contention is a visible EADDRINUSE, not corruption.The Architectural Reality
ai/services/fleet/owns fleet services — direct siblings:deriveHarnessLaunchSpec.mjs(per-family launch contracts) andprepareManagedAgentWorkspace.mjs(instance-home preparation, the natural call site for planting generated files). Structural pre-flight: new.mjsbeside those siblings — fast-path pattern match.ADR-0019 constraint (read the ADR before authoring OR reviewing): the generator must be a PURE function. Canonical root, seat env-file path, workspace root, memory dir, and the server list arrive as explicit parameters — no
AiConfigimport, no env reads, no hidden defaults. Callers (entrypoints) own resolution.The Fix
New
ai/services/fleet/generateOpenCodeSeatConfig.mjs(name negotiable at implementation):generateOpenCodeSeatConfig({canonicalRoot, seatEnvFile, workspaceRoot, memoryDir, nodeBinary, servers?})→{files: [{path, content}]}:opencode.jsonc— the four neo-mjs servers (memory-core, github-workflow, knowledge-base, neural-link), command arrays =[nodeBinary, '--env-file=' + seatEnvFile, canonicalRoot + '/ai/mcp/server/<name>/mcp-server.mjs', …](neural-link additionally--cwd workspaceRoot), environment blocks,instructionsarray pointing at the memory files, and the organs-canonical / seat-personal / NL-lazy-bind rationale as comments.MEMORY.md,seat-pointers.md(template with slots),identity.md(empty template + story-sovereignty header).test/playwright/unit/ai/services/fleet/):canonicalRoot; a seat-root server path fails the spec.identity.mdcontains structure only — no biography content.opencode.jsonc(comments allowed) vs strictopencode.json, and which wins when both exist — probed, not assumed.prepareManagedAgentWorkspaceplanting seam for #15391's launch path.Contract Ledger Matrix
generateOpenCodeSeatConfig(params)ai/services/fleet/generateOpenCodeSeatConfig.mjsai/mcp/server/memory-core/configBase.mjs:21-28NEO_MEMORY_DB_PATHet al.) but are not the working defaultAcceptance Criteria
identity.mdis template-only.Out of Scope
Decision Record impact
aligned-with ADR-0019 (the pure-parameter shape is mandated by it).
Related
Candidates:
@neo-kimi-phoebepost-activation (the generator's first user and fixture owner — offline-testable except the loader probe); any peer. Post-window clean.Live latest-open sweep: checked latest 20 open issues at 2026-07-18T01:36Z; no equivalent found (nearest: #15390, activation — disjoint). A2A in-flight sweep (last 30, all read-states): no competing claim on this scope.
Origin Session ID: abce4d75-7dcb-4145-8afc-b0ff2cdc51e6
Retrieval Hint:
query_raw_memories("OpenCode seat opencode.jsonc organs canonical seat personal island instructions memory layer")