Skip to content

feat(go): ADR-0008 Go runtime line — hot-path, CLI, management parity increments - #3810

Draft
waxiangzi wants to merge 189 commits into
lidge-jun:devfrom
waxiangzi:dev-go
Draft

feat(go): ADR-0008 Go runtime line — hot-path, CLI, management parity increments#3810
waxiangzi wants to merge 189 commits into
lidge-jun:devfrom
waxiangzi:dev-go

Conversation

@waxiangzi

@waxiangzi waxiangzi commented Sep 6, 2026

Copy link
Copy Markdown

Summary

  • Adds the ADR-0008 Go runtime increment line to dev. This is intentionally a cross-history PR: dev-go and dev have no useful merge base, so the broad repository diff includes baseline noise; the substantive increment is the new go/ runtime tree (89 files) plus parity harnesses and CI wiring.
  • Completes native Go ownership for the selected hot paths: Responses relay/repair behavior, provider batch handling, config commands and schema normalization, status, and doctor diagnostics including their recovery transactions. The remaining delegated service/tray/shim commands are intentional migration seams tracked by later cutover work.
  • Adds the fix: guard stale proxy pid files #34 acceptance gate: named Hot-path differential oracles steps on macOS and Windows execute tests/go-hotpath-relay.test.ts and tests/go-hotpath-seam.test.ts. Those tests compare native Go output and exit behavior with the TypeScript oracle.

Verification

  • cd go && go test ./...
  • cd go && go vet ./...
  • cd go && go build -buildvcs=false -o /tmp/ocxver ./cmd/ocx
  • bun test --timeout 180000 tests/go-cli-parity.test.ts (49 pass, 0 fail)
  • Go sidecar parity and hot-path relay differential suites pass locally.
  • CI run requested by this PR supplies the required macOS and Windows Hot-path differential oracles evidence for fix: guard stale proxy pid files #34.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • New Features

    • Added an optional Go sidecar runtime for selected management routes, CLI commands, diagnostics, and response handling.
    • Added Go-native configuration, provider, model, status, doctor, and health capabilities with TypeScript-compatible behavior.
    • Added optional hot-path response relay, SSE streaming, and WebSocket bridge support.
    • Added cross-platform static release artifact builds for Linux, macOS, and Windows.
  • Bug Fixes

    • Improved direct local HTTP support for PUT, PATCH, DELETE, and request bodies.
    • Added safer configuration persistence, validation, locking, recovery, and invalid-file backups.
  • Documentation

    • Documented the incremental Go runtime migration, ownership boundaries, operational guidance, and agent workflows.
  • Tests

    • Added extensive Go/TypeScript parity, security, routing, streaming, CLI, and cross-platform CI coverage.

sean.opencode added 30 commits September 5, 2026 23:03
ADR-0008 records the owner decision to migrate the backend to Go as an
incremental sidecar takeover, ending in a single static Go binary.

- Reconcile the runtime-line policy in AGENTS.md, MAINTAINERS.md, and
  structure/06_docs-and-release.md with ADR-0008.
- Add the first-increment plan (fresh go/ tree, ocx-sidecar, one read-only
  route, differential harness) under devlog/_plan.
- Add Agent skills docs (issue tracker, triage labels, domain) and the
  Agent skills section in AGENTS.md.
… + differential oracle

Implement the first Go sidecar increment per ADR-0008 and
devlog/_plan/260905_go_sidecar_takeover: a fresh in-tree Go module that
serves exactly one read-only management route (GET /api/system/health) with
byte-identical HTTP semantics to the in-process TypeScript handler, plus the
differential oracle proving it.

- go/: fresh module (module github.com/lidge-jun/opencodex/go) building the
  ocx-sidecar binary (CGO_ENABLED=0). The health payload struct field order
  and encoding/json number formatting are part of the byte contract with the
  Bun harness; key order, Content-Type, and compactness mirror jsonResponse.
- src/server/go-sidecar.ts + go-sidecar-slot.ts: optional supervisor. The TS
  server spawns and supervises the child when OPENCODEX_GO_SIDECAR_BIN names a
  binary; readiness is a stdout handshake line. The core health route consults
  only a core-owned slot (AGENTS.md optional-subsystem pattern); the forwarder
  registers at activation and deregisters on stop or unexpected child exit, so
  a default install spawns nothing and every existing route is byte-identical.
- tests/go-sidecar-parity.test.ts: boots the TS server with and without the
  sidecar and asserts the in-process handler and the Go sidecar agree on
  status, headers, and the normalised body, normalising exactly the declared
  volatile fields (pid, uptime). Skips with a visible reason when  is
  absent; CI installs Go (the new  job plus setup-go on the suite lanes).
- .gitignore / tests/repo-hygiene.test.ts: reconcile the pre-ADR gitignore and
  hygiene guard that treated go/ as a retired, untrackable tree. go/ is tracked
  source again; go/bin build output stays ignored.
- route-registry: annotate the Go-owned health route seam; the declared owner
  stays system-routes so registry reconciliation holds for the default install.
- ci.yml: add the go/** scope to the shared CI allowlist, a dedicated  job
  (build/vet/test + the differential oracle), and setup-go on the shard and
  macOS suite lanes; pin the allowlist sync in tests/ci-workflows.test.ts.
…ld contract

The plan states the sidecar is built static (CGO_ENABLED=0); the parity
harness already sets it for its throwaway binary. Make the dedicated go job
enforce the same flag so the CI artifact and the oracle build identically.
… migrate

Ticket #9 on the fork board asks for an explicit, owner-approved migrate-or-cut
decision for the Compatibility Lab with a cost-vs-value basis, recorded so later
Lab tickets (#19 activation gate + provider slot in Go, #33 routes migration +
differential) can reference it. The owner ratified MIGRATE on 2026-09-06.

The record captures the evidence that framed the choice and why migrate wins:
the Lab is a shipped, GUI-exposed capability whose evidence provider feeds the
synchronous routing assembler through the core-owned provider slot, so cutting
would remove a routing control and change behavior for gated profiles rather
than just retire an experiment. The 2026-07 decoupling campaign left the
activation gate, passive-route linker, provider slot, and shutdown hooks as
first-class seams, so the port is bounded; Lab stays increment 6, independently
gated (spec #6), and cannot block increments 2-5 or the flip. The doc keeps the
cut alternative alive as a reopenable revisit point with fresh adoption
evidence before the Lab batch starts, per spec #6's estimate-cost-against-usage
requirement.
…en forwarding branch

Walk the ADR-0008 critical path #8 -> #10||#11 -> #12 -> #13 -> #14.
Increment 1 already sat on dev-go; this run closed its remaining acceptance
gaps and delivered #14 (2.1), the first increment-2 ticket, per
devlog/_plan/260905_go_sidecar_takeover/020_ownership_plumbing.md.

- route-registry: ManagementRoute is now a discriminated union so the `go`
  ownership marker can only sit on a read route (mutates: false) — the write
  arm refuses it at compile time. The health row flips its typed marker with
  the per-route volatile declaration (pid, uptime); GO_OWNED_MANAGEMENT_ROUTES
  is the derived migrated surface and findGoOwnedManagementRoute the dispatch
  lookup.
- management-api: a single forwarding branch at the head of handleManagementAPI
  serves declared Go-owned routes from the attached ocx-sidecar (response
  relayed verbatim) and falls through to the in-process chain for everything
  else and every supervision state. The bespoke health forwarder consult is
  gone from system-routes.ts, whose in-process handler is now purely the
  fallback and differential oracle.
- go-sidecar-slot / go-sidecar: the core-owned slot generalizes from a
  health-only forwarder to a route forwarder; the supervisor registers it at
  activation exactly as before.
- parity harness: volatile normalisation now reads the route's declared
  go.volatileFields from the registry (single source, no mirrored constant),
  and gains a #11 crash-oracle — kill the sidecar, assert the forwarder
  deregisters and the next health response flips back to the proxy's own pid.
- tests/go-ownership-plumbing.test.ts: registry invariants (writes cannot be
  Go-owned; volatile declared per route; migrated surface pinned) and dispatch
  behaviour under a fake forwarder, with no Go toolchain required.

Verified: bun run typecheck; go build/vet/test under go/; focused suites; full
Bun test suite green except the pre-existing release-version-line failure on
this branch (package.json 2.42.0 equals the released tag while HEAD is not the
tagged commit — present without this diff).
#8's acceptance — builds CGO_ENABLED=0 on every release target — was only
proven on the runner's native platform. The go job now loops the six release
targets (linux/darwin/windows x amd64/arm64) and fails if any combination
does not produce a binary, so a future cgo leak or build-tag mistake surfaces
in CI rather than at release. All six combos build clean today.
…losures

devlog unit 260905_go_sidecar_takeover now documents the critical-path run:
the two increment-1 acceptance gaps that gained machine checks (cross-platform
build gate for #8, crash-fallback oracle for #11) and the increment-2 ticket
#14 implementation (typed read/write ownership, single registry-driven
forwarding branch, per-route volatile declarations).
…ce gate

Re-ran every machine gate backing the delivered increment (go build/vet/test,
six-target CGO_ENABLED=0 cross-compile matrix, differential oracle, ownership
plumbing, registry reconciliation, typecheck) and resolved issues #8-#14 in the
tracker in dependency order, each with tree evidence in the closure comment.

Auditing the newly unblocked frontier (#15-#19) against the actual handlers
found a gate the batch texts do not state: a route is Go-servable byte-identically
only when its body is a pure function of state the sidecar process can see. The
system-memory and windows-replace-retries bodies are TS-process introspection
(no on-disk counterpart); the dashboard-session half of #18 lives in an in-memory
Map; #19 would gate nothing until the Lab routes port. Records the per-route
classification and the recommended order: port #16's pure config-core first,
defer process-derived system routes to the flip with a documented exemption,
specify the principal-relay contract before #18.
…settings read route

First vertical slice of the config read batch (spec #2). The registry's
go.volatileFields marker may now be EMPTY: that declares a strict route whose
body is a pure function of shared state and must be byte-identical with no
normalisation — the strongest oracle contract, not a vacuous one (the previous
non-empty rule only ever made sense for process-value routes).

- go/internal/config: shared Go config reader (OPENCODEX_HOME/config.json,
  json.Number preserves on-disk literals, never rewrites the file). The
  load-bearing artifact #20/#21/#24/#35 all depend on.
- GET /api/shadow-call-settings is now Go-owned: marker flip + sidecar handler
  projecting shadowCallIntercept through the exact TS rules (enabled === true,
  model ?? "", shadowSourceModels trim/non-empty/default gpt-5.6-luna).
  In-process handler remains fallback and oracle.
- Oracle: two strict-parity cases (default + configured body, raw bytes equal;
  relay alters nothing). go-ownership/route-registry/hygiene/ci/cli/explainability
  199 pass; go build/vet/test green; typecheck green.

Issue #16 stays open (1/9 routes); remaining routes and per-route status in
devlog 031.
…JSON (#15/#16/#17)

GET /api/custom-models is the first #17 route: the TS body is
JSON.stringify(config.customModels ?? []), a raw echo of a zod-passthrough
config subsection. Byte parity needs document-order JSON, so the shared Go
config package gains an ordered decoder and a JSON.stringify-compatible
marshaler (file key order, no HTML or U+2028/U+2029 escaping, control-char
shortcuts and lowercase \u00xx below U+0020, number literals verbatim) — the
same substrate the /api/config provider-DTO port will need later.

The route is marked strict (empty volatileFields): the differential oracle
compares raw wire bytes with no normalisation, pinned against Bun for string
escaping.

Also records the per-route state-source decisions for the three read-surface
batches (#15 system reads, #16 config reads, #17 model/provider/catalog
reads): every remaining route carries a defer-to-flip verdict with a code
citation (process state, live catalog/discovery caches, updater jobs, registry
static data, platform probes). Pre-flip feasible residue is now fully migrated;
deferred routes become the Go binary's own state at the flip.

Go gates: build/vet/test green. Bun: 199 pass across the six registry/oracle
suites; typecheck green; full suite 17734 pass with only the pre-existing
release-version-line failure (tree state, not this diff).
Ticket #18: go/internal/managementauth reproduces the management admission
decision of src/server/management-auth.ts: admin-token equality (env/file
resolution, no secret-file mutation), dashboard-session authorization with a
port of managementRequestOrigin (loopback observed origin, non-loopback
api-auth rule, hub public-origin override, WHATWG origin serialisation), all
four process-scoped capability HMAC contracts with their replay stores, and
the exact 401/503 rejection bodies. Substrate: the TS front door still admits
every management request pre-flip; the write batches (#21-#23) and the
authorization gate (#26) consume this when Go answers without that front door.

Ticket #19: go/internal/labactivation reproduces the Lab opt-in gate
(routingProfiles non-empty, or automation enabled on disk with
automation-config.json authoritative over the legacy automation-policy.json),
and go/internal/routing/compatibility reproduces the core-owned
evidence-provider slot (set/resolve/detach-own-registration). The seam
registers only when the gate says the install uses Lab; real Lab content
arrives with #33. A no-Lab user executes no Lab code because no Go package
imports Lab content at all.

Machine proof, not prose: Go unit tests plus two differential oracles that run
the same inputs through the TypeScript side and the Go side. authcheck
subcommand evaluates ordered vector arrays in one Go process (replay stores
persist like the TS module-level maps) and the oracle compares principal or
exact status+body plus the session admission reason: 7 suites, every
principal and rejection path. labcheck answers the gate for fixture dirs Go
reads before the TS loader can repair them: 10 fixtures. Both subcommands are
inert on the live path (the supervisor passes no argument).

Gates: go build/vet/test green; typecheck green; focused suites 187 pass;
full suite 17751 pass with only the pre-existing release-version-line failure
(tree state, not this diff).
…ial harness

The Go sidecar now owns the public POST /v1/responses surface behind the
same optional-subsystem pattern as the management reads: a declared seam
route, an independent OPENCODEX_GO_HOTPATH_SEAM gate (spec #4 story 10),
and a private parent bridge that runs the in-process responses pipeline
for one admitted request. The front door mints a body-bound HMAC claim
over the admission so a client credential never crosses the process
boundary; the sidecar relays the claim verbatim and streams the bridge
response byte-for-byte in frame order.

tests/go-hotpath-seam.test.ts is the streaming differential: two live
servers (in-process oracle vs seam) against the same deterministic
fixture upstream must agree on the ordered SSE frame sequence with an
explicitly declared volatile set (per-request trace header, Date, server
CORS echo; body volatile set empty). go/internal/sidecar/hotpath_test.go
pins seam auth, body bound and byte-for-byte chunked stream relay.
Default installs and seam-off sidecar installs are unchanged.
Parse and re-emit a JSON document the way ECMAScript JSON.stringify does:
object keys in document order, spread-equivalent Set, numbers in V8
shortest-decimal form, no HTML/U+2028/U+2029 string escaping. Number
formatting is pinned by a committed Bun-generated corpus (447 rows).
…repair

The data-plane seam serves relay-safe non-streaming requests for one
key-mode openai-responses provider directly upstream, behind the
OPENCODEX_GO_HOTPATH_RELAY gate (default off). Outbound mirrors the TS
passthrough verbatim (body, path, resolved Authorization); 2xx JSON bodies
get the whole-body field backfill (annotations/id/status) re-serialised only
when changed; non-JSON and non-2xx non-empty bodies relay verbatim with a
valid Retry-After preserved. Everything else keeps the #24 parent bridge.

The repair is pinned to the TypeScript oracle by committed goldens produced
from backfillResponsesFieldsJson; the relay-safe predicate and direct-vs-
bridge seam paths are covered by Go unit tests (dead parent bridge) and by
the UA-differentiated differential harness.
Declares OPENCODEX_GO_HOTPATH_RELAY on the TS side (front door passes it to
the sidecar at spawn) and proves the armed relay answers the non-streaming
matrix byte-identically to the in-process oracle while the fixture upstream
sees the Go http client user agent for admitted requests and the Bun agent
for refused/gate-off ones.
sean.opencode added 29 commits September 8, 2026 12:32
flip storage/agent/grok/integration/lab to Go-owned (issue #48)

# Conflicts:
#	go/internal/ocxcli/cli.go
#	tests/go-cli-parity.test.ts
flip alias/combo/route to Go-owned (issue #49)
flip sync/sync-cache to Go-owned (issue #50)
flip logout/account (partial) to Go-owned (issue #51)
flip disconnect/connect-status to Go-owned (issue #52)
flip codex-shim/ensure/restart lifecycle surfaces + service status read to Go-owned (issue #53)
flip gui/mcode/mmx/zcode to Go-owned; update/v2/claude/opencode stay TypeScript-owned as recorded follow-up (issue #54)
The takeover ledger is now code: deferredSurfaces records every command
surface that still routes through DelegateToTypeScript with its reason and
the ticket that will lift it — 13 WholeCommand top-levels (account, claude,
connect, login, opencode, recover-history, restore, service, setup, tray,
uninstall, update, v2) plus the SubcommandSeams inside Go-owned families
(codex-shim install/uninstall/remove, config non-map verbs, connect
connect/rotate/revoke, logs + observe indexer actions, models/lab runtime
maps, service non-status, storage codex-logs, system codex-cli-update).

deferral_test.go hardens the seam the same way core-lab-boundary guards the
Lab: a top-level command cannot be TypeScript-owned without a registered
reason, a registration cannot outlive its flip, ghost entries fail, and a
new TypeScript-owned verb in the models/lab runtime maps must enter the
ledger. Flipping any registered surface now requires deleting its deferral,
which is the audit trail the final surface-map reconciliation needs.
Code-review follow-ups on the deferral registry (48aec1b):
- TSDeferral gains a Fallback flag so a SubcommandSeam can express a
  family whose default delegates everything outside its Go-native surface
  (config's non-map verbs, codex-shim's non-status verbs). Previously the
  empty-Verbs meaning was ambiguous: map-driven for models/lab, but
  negative-space for config — an inert row no test could fail.
- New TestFamilyFallbackDelegationRegistered exercises that negative
  space: the unknown-verb fallback must resolve to the TypeScript owner
  and each Go-native carve-out (configRuntimeSubcommands + --json/
  --source, codex-shim status) must stay native, so deleting a Fallback
  seam now fails instead of passing silently.
- Drop the redundant service SubcommandSeam (its WholeCommand entry
  already registers the whole family; only the status read is native).
- storage codex-logs reason no longer claims the observe slice has not
  flipped — the observe family routes that verb natively today and only
  the storage-alias path still delegates.
- Fix the stale doc comment naming a test function that does not exist.
Grill-with-docs decisions (owner, 2026-09-08), landed as docs before the v2
slice that depends on them:

- ADR-0009: ADR-0008's '100% differential parity' is structurally unreachable
  for interactive/OS/network/coordinator surfaces (repro evidence in the
  restore/uninstall boundary record). Decide an oracle taxonomy (T1 byte
  parity / T2 masked / T3 golden fixture / T4 platform lane / T5 interactive
  subset) with per-surface explicit acceptance, redefine completion as
  'every oracle-able surface Go-owned + an explicit Bun-dependent list', and
  require deferral reasons to be cross-checked against the TypeScript code
  (the v2 deferral's claimed TOML writer is overstated — writes go through
  the upstream codex features CLI).
- CONTEXT.md: root glossary for the takeover domain (Go-owned/TypeScript-
  owned, surface, family, flip, delegation seam, deferral, oracle taxonomy,
  volatile field, batch, standalone, Bun-dependent surface).
The v2 family keeps its TypeScript owner for the write verbs (on/off/mode/
threads/keep-native-v1/mode-hint) — they edit the upstream Codex config.toml
through the features.ts engine (atomic + rollback + thread-limit migration)
plus the codex features CLI — but the `status` read is now native, the same
carve-out shape connect status / service status use.

- v2_command.go: line-based TOML scanners ported from src/codex/features.ts
  (dedicated [features.multi_agent_v2] table, [features] boolean and inline
  forms, [agents] scalars, v1-child<->v2-total thread translation, basic/
  literal string fields) plus the opencodex config.json reads for
  multiAgentMode / keepNativeChatGptOnV1. Pure stdlib; the oracle boundary
  (shapes where the line scanners agree with the TS reader's Bun.TOML.parse
  preference) is declared in the file and in ADR-0009.
- cli.go: OwnershipFor routes v2 status to GoOwned; bare v2 and the write
  verbs still delegate.
- deferral.go: v2 is now a WholeCommand (bare) plus a Fallback SubcommandSeam
  (every verb except status); deferral_test.go registers the status carve-out.
- v2_command_test.go: 9 golden fixture cases pinned to TS byte output.
- parity: 8 new rows diff TS vs Go over fresh homes (config.json carries
  providers so the TS loadConfig repair notice cannot pollute stdout).
  Full parity 774 pass / 2 pre-existing sync-flows environment failures.
- drop an unused method parameter in the grok parity fixture.
Follow-up to the v2a flip (247c9e4), addressing both review axes before
the slice is called done.

Spec axis (real divergences inside the declared oracle boundary, each found
by byte-diffing real Codex config.toml shapes between the two CLIs):

- codexV2StringField now slices values with a quote-aware scanner
  (scanTomlValueToken) instead of a line regex: a `#` inside a string is
  data, not a comment, so `subagent_developer_instructions = "ping #duty"`
  no longer reads as unset. Basic strings honour backslash escapes and
  multiline delimiters are refused (declared out of scope) rather than
  misread as empty strings.
- Literal strings mirror the TS scanner exactly: they end at the FIRST
  following quote with no `''` folding (`'it''s here'` reads as `it`), and
  the decode path also learned `\UXXXXXXXX` escapes for astral code points.
- Per-key reader shapes in features.ts are NOT uniform, and each Go reader
  now mirrors its own counterpart instead of one shared rule: the `[agents]`
  readers went parse-first (underscore separators visible — max_threads =
  1_000 reads 1000) while getMaxConcurrentThreads and getAgentsMaxDepth
  stayed line-scanner-only (max_depth = 2_000 and
  max_concurrent_threads_per_session = 3_000 read as unset). The dotted
  `[features] multi_agent_v2.enabled = true` form is read.
- A codexV2Sections helper resolves the dedicated/inline table locations
  once instead of in every key lookup; opencodexConfigBoolField reuses the
  BOM-aware readRawTopLevelConfig shared with the client-state surface.
- cli.go's stale v2 comment no longer claims a byte-exact TOML reader/writer
  is a precondition; the oracle boundary comment tracks the mirrored shapes.

Each new shape is pinned twice: as golden expectations in v2_command_test.go
(19 cases, all byte text from the TS CLI) and as fresh-home TS-vs-Go parity
rows (17 pass). go vet, go test ./internal/ocxcli, tsc --noEmit all green.
…56 v2a)

The three v2 status parity fixtures embedded escaped double quotes inside
double-quoted strings. Switch to TypeScript single-quoted literals so the
TOML contents carry clean double quotes, matching the on-disk config.toml
form the parity harness compares against.
`ocx login <provider>` keeps its TypeScript owner at the top level; only a
name in the new Go-side keyLoginProviders table (zai, zhipu-bigmodel-coding)
routes to Go. The Go flow mirrors TypeScript handleKeyLogin byte-for-byte:
namespace-collision preflight (case-insensitive, before any browser/key read),
dashboard banner + open, readline key prompt, {placeholder} baseUrl
resolution, an openai-chat GET {baseUrl}/models probe (8s, 401/403 rejected,
transport/5xx unknown), provider-row merge preserving the modelCosts overlay,
atomic config save without requiring defaultProvider, then a bodyless
attested POST /api/providers/reload that asks the live proxy to adopt the
persisted credential. No running proxy is a silent no-op; a live proxy that
cannot reload prints the TypeScript warning.

The keyless and OAuth tiers are deliberately absent from the table:
opencode-free needs no login (usage error, consistent with keyOptional), and
kiro's import-first OAuth stays TypeScript-owned as a recorded deferral.

Oracle: tests/go-cli-parity.test.ts diffs the Go binary against the TS CLI
for the two non-interactive aborts (namespace collision, empty key) for both
providers — zero network, headless-safe, byte-identical. The network branches
stay covered by Go httptest fixtures.

Deps gains Stdin and OpenURL so tests inject the key read and stub the
browser opener.
Port the features.ts config.toml editing engine and the cmdV2 write
verbs to Go so the whole `ocx v2` family resolves GoOwned: on/off
(feature flip through the upstream `codex features` CLI), mode,
keep-native-v1, threads, mode-hint, and the bare `v2` summary.

- v2_edit.go / v2_transition.go / v2_strings.go: byte mirror of the
  features.ts engine (EOL/comment handling, atomic write, string-aware
  scanners, dedicated/inline table shapes, thread-limit migration with
  rollback, string-field set/clear) with goldens generated by running
  the real features.ts.
- codex_features_go.go: codex runtime resolution (env -> persisted ->
  PATH -> fallback), `codex features` spawn, and the mode-hint
  native-binary probe (script wrappers take the null/allow branch).
- v2_command_write.go: cmdV2 verb shell with byte-exact TS texts. The
  trailing catalog resync delegates to the TS sync engine and returns
  silently on the desired-disabled branch (documented residue: a
  service-refused host prints the sync wrapper line where the TS tail
  is silent; functionally identical refusal).
- cli.go: v2 command table entry + dispatch now GoOwned, OwnershipFor
  gate removed; deferral.go drops the v2 WholeCommand + Fallback-seam
  entries (ledger bijection enforces).
- go-cli-parity.test.ts: "v2 write verbs (issue #56 v2b)" describe runs
  both CLIs against isolated homes with a fake codex features shim on
  PATH and the Codex integration OFF; flip/no-op/threads/mode/
  keep-native/mode-hint/unknown/bare rows are byte-identical (win32
  skipped: no bash-shim oracle).
- Go unit tests: v2_command_write_test.go verbs + ownership, plus the
  engine golden suites already in v2_edit/v2_transition/v2_strings
  tests.

go test ./..., go vet, bun run typecheck, and the focused parity
describe are green.
Port the opencode.ts launcher to Go so the command resolves GoOwned:
fetch the live model catalog, inject the V1/V2 runtime provider blocks
into an inherited OPENCODE_CONFIG_CONTENT (or a minimal new doc), and
spawn the opencode CLI with the admission key in its child env.

- opencode_engine.go: admission-key precedence (env token -> service
  token file -> apiKeys[0].key -> placeholder), order-preserving V1/V2
  block merge, JSONC strip/parse, provider-override file search with
  global-first + git-root walk, and the /api/models fetch with the exact
  TS error texts and an 8s timeout client.
- opencode_command.go: cmdOpencode port reusing the export serializer
  family (no normalize/sort), the two wiring stderr lines, and the
  launcher spawn with exit-code/ENOENT hint passthrough. Detached
  self-start inherits the current env (documented divergence: TS sets
  OCX_SERVICE=1 + hardened OCX_API_TOKEN_FILE; same service-token
  bootstrap, unexercisable by parity).
- opencode_golden_test.go / engine/fetch tests: goldens frozen from the
  real TS serializer at a fixed port; header contract, all five fetch
  error texts, JSONC cases, override walk, and api-key precedence.
- cli.go: opencode table row + dispatch now GoOwned; deferral.go drops
  the WholeCommand entry (ledger bijection enforced by deferral_test).
- go-cli-parity.test.ts: "ocx opencode slice (issue #56)" describe runs
  both CLIs against one live fixture proxy with an env-capture opencode
  shim on PATH — wired lane, argv/exit passthrough, ENOENT hint,
  admission-key precedence, inherited-content merge + invalid-JSON
  error, and the provider-override info line all compare byte-for-byte.
  apiKeys fixtures carry complete metadata because the TS config loader
  warns on degraded entries (recorded residual).

go test ./... and bun run typecheck green; focused parity describe green.
Port the claude.ts launcher so the command resolves GoOwned: the
client-state gates (selected client, service-token presence/ownership),
live-proxy ensure, /api/claude-code context-window fetch, the
byte-faithful auth-mode env assembly, the gateway-model-cache prewrite and
the roster-agent sync, then spawn the claude CLI with exit-code and
ENOENT-hint passthrough.

- claude_command.go + claude_engine.go: runClaude mirrors cmdClaude across
  the connected/local/subscription auth-mode matrix with admission
  precedence (env token -> service file -> apiKeys[0] -> placeholder) and
  the exact TS gate texts; engine env goldens are frozen real TS probe
  output at a fixed port.
- claude_context.go / claude_cache.go / claude_detect.go / claude_agents.go
  / claude_alias.go: the supporting subsystems (context-window fetch,
  gateway cache prewrite, auth-mode detection, roster-agent sync, CLI
  alias resolution) with their unit tests.
- deferral.go / cli.go: claude is GoOwned for every verb except the
  `desktop` and `config` subcommands (SubcommandSeam), which remain TS.
- tests/go-cli-parity.test.ts: the ocx claude slice describe — the four
  gate rows (disabled, not selected, token missing, token changed) and the
  local + connected spawn lanes, diffed byte-for-byte between TS and Go
  through a claude env-dump shim against one shared fixture proxy.
Owner decision from the migration close-out grilling (2026-09-09): the
in-place self-replace surface has no hermetic byte-diff oracle, and a Go
port would re-implement the network/self-replace risk surface for
marginal standalone gain. The deferral ledger and cli.go comment now say
'archived as design, not a flip candidate' with the revisit condition
(a hermetic release-server harness) instead of reading as an open #56
remainder.
Both slices the unit designed landed on dev-go — opencode cd97aad,
claude b7d9696 — and #56 closed completed 2026-09-09, so the design
record moves from _plan to _fin with the terminal outcome.
Six claude files landed in b7d9696 without passing gofmt (whitespace
and comment alignment only); normalize them so gofmt -l is clean.
…ty (issue #44)

The Go takeover ADRs were written against the upstream dev line, but the
implementation lives on the owner's fork dev-go as an independent release
line. 0008's 'TypeScript CLI and server remain the operating surface'
clause no longer holds since the #41 cutover made the Go binary the
release runtime; 0008 now records increment state (#1-#43 closed) and
explicitly defers its 100%-parity clause to 0009. 0009 gains a
fork-line addendum tying its Bun-dependent list to the 10 commands a
standalone Go binary delegates (the 'needs the TypeScript lifecycle
owner' error path).
The grilling session that settled the post-close-out direction: fork dev-go
is an independent release line (mature-then-donate upstream), the milestone
anchor is user-visible capability, the immediate phase is a shipped
installable Go artifact, and the artifact is being dogfooded before release
mechanics are chosen. Records the decision tree with rulings, the verified
facts it rested on (registry 42/10, tickets #1-#43 closed, npm line already
independent at 2.48.0, zero fork->upstream PRs), the same-session
deliveries (ADR-0008/0009 update 3803c2a, first go/bin/ocx build + smoke,
dogfood PATH switch), and the open gap list that gates release.
@Ingwannu

Ingwannu commented Sep 9, 2026

Copy link
Copy Markdown
Owner

There is a policy prerequisite before sponsorship of this cross-history Go runtime PR. Current dev AGENTS.md and MAINTAINERS.md explicitly retire the parallel Go runtime and state that Bun-native TypeScript on dev is the only runtime line; a future native addition should be an incremental module, not another full runtime. The PR description instead proposes the ADR-0008 Go runtime line with an 89-file tree and baseline noise.

I am not applying maintainer-sponsored or approving CI/release wiring for that direction as-is. @lidge-jun would need to explicitly approve reopening this architecture first. Alternatively, split out independently useful TypeScript bug fixes or parity tests as normal small dev-based PRs, without the second-runtime forwarding/release machinery. This is an architecture/scope disposition, not a claim that the Go implementation has been fully reviewed or found defective. Keeping it Draft pending that decision avoids discarding work without owner agreement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants