Skip to content

fix(fresh-agent): freshclaude/freshopencode/freshcodex parity audit#476

Merged
danshapiro merged 4 commits into
mainfrom
feat/fresh-agent-parity-audit
Jun 24, 2026
Merged

fix(fresh-agent): freshclaude/freshopencode/freshcodex parity audit#476
danshapiro merged 4 commits into
mainfrom
feat/fresh-agent-parity-audit

Conversation

@danshapiro

Copy link
Copy Markdown
Owner

Summary

Four fresh-agent parity bugs identified and fixed with TDD + load-bearing validation:

  1. OPENCODE_CMD env parity (serve-manager.ts): ??|| for empty-string handling (matching CODEX_CMD/platform.ts conventions); this.env assigned before this.command so the fallback reads the correct env. Tests isolated from host process.env with env:{}.

  2. malformed_session_status false-positive (opencode/adapter.ts): absent status (idle sessions are absent from the status map) treated as idle rather than logging a false-positive malformed warning.

  3. "is not tracked" materialization race (runtime-manager.ts): subscribe now uses requireOrRecoverSession instead of requireSession, recovering untracked ses_* ids via attach instead of throwing — preventing client-facing errors when subscribe arrives before the runtime manager registers the materialized real id.

  4. freshcodex transcript auto-render (codex/adapter.ts): wired onTurnCompleted on the CodexRuntimePort type to emit an idle sdk.session.snapshot on turn completion, so transcripts auto-render without manual pane refresh — matching freshclaude/freshopencode behavior.

Verification

  • 151 tests pass across 4 affected test files
  • Typecheck + lint clean (0 errors)
  • Load-bearing validation on all 4 bugs (1 falsified test-isolation assumption → fixed, 1 recommended pinning test → added, 2 confirmed sound via real binary contract tests)
  • Fresheyes independent review: PASSED (iteration 1, 2 minor non-blocking notes addressed)

Bug #5 (deferred)

Right-click reopen-as opencode for terminal-mode opencode sessions — explicitly deferred per user instruction. Root cause documented (terminal sessionRef not populated eagerly; reducer defers association to busy→idle).

codex added 4 commits June 23, 2026 03:40
…hopencode

Drive each coding agent and compare UI surfaces + nondestructive commands;
fix the four real shortcomings surfaced by the audit (TDD, red-green):

1. freshopencode serve-manager ignored the OPENCODE_CMD env var, breaking
   parity with CLAUDE_CMD (sdk-bridge) and CODEX_CMD (codex runtime), which
   both honor their *_CMD var in terminal AND fresh-agent modes. The serve
   manager now resolves command = options.command ?? env.OPENCODE_CMD ?? 'opencode'.

2. freshopencode logged a false-positive 'malformed_session_status' warning on
   every attach/resume of an idle session: the opencode /session/status map
   only reports active (busy/retry) sessions, so an idle session is absent
   (undefined). reconcileStatus now treats a missing entry as idle (matching
   the serve manager's onceIdle) and reserves the warning for present-but-
   malformed entries.

3. freshopencode leaked a 'Fresh-agent session ... is not tracked' error
   banner to the transcript during placeholder->real-id materialization: the
   adapter emits freshAgent.session.materialized mid-send (before the runtime
   manager registers the real id), so the ws-handler's subscribe hit the strict
   requireSession and threw. subscribe now uses requireOrRecoverSession so the
   freshopencode attach-recovery path covers the materialization transition
   (claude/codex fall through unchanged via canRecoverFreshOpenCode).

4. freshcodex transcript did not auto-render after a turn completed: the codex
   adapter fire-and-forgets send and its subscribe only wired
   onThreadLifecycle, whose thread_status_changed(idle) fires BEFORE the
   completed turn is committed to the app-server's thread history, leaving the
   client with an empty transcript until a manual Refresh. subscribe now also
   wires the runtime's onTurnCompleted (already public, used by terminal-mode
   codex) to emit a snapshot-invalidating idle event after the turn is
   committed, mirroring freshopencode's post-idle emit.
- OpenCode serve manager: OPENCODE_CMD ?? → || for empty-string parity
  with other env-var reads; isolate test makeManager() from host process.env
  (env:{}) so the 'opencode' default assertion holds when a developer has
  OPENCODE_CMD set in their shell (3 construction sites).
- OpenCode adapter: reword onceIdle-equivalence comment to avoid overstating
  semantics; clarify the absent-as-idle rationale.
- OpenCode adapter: add pinning test for concurrent attach-during-send
  idempotency — asserts serveManager.subscribe is called exactly once for
  the real id, guarding against a refactor that swaps remember() and
  emitMaterialized() ordering in materializeOrSend.
Fresheyes review noted the concurrent-attach-during-send test claims to
guard against a remember()/emitMaterialized() ordering swap, but attach
runs after promptAsync (both have already executed). The test still
meaningfully pins concurrent attach idempotency (exactly one serve
subscription) — fix the comment to match what it actually verifies.
…completion)

Merged PR #475's server-authoritative turn-completion pipeline with this
branch's transcript auto-render fix. The onTurnCompleted handler now
serves both purposes:
- Emits an idle sdk.session.snapshot on every turn completion (including
  interrupts) so the client re-fetches the committed transcript.
- Emits a sdk.turn.complete edge only for positive completions
  (params.turn.status === 'completed') with monotonic-at dedup, for the
  green/sound pipeline.

Took main's version of codex-adapter.test.ts (which includes the full
sdk.turn.complete test suite) and re-added the transcript auto-render test.
@danshapiro danshapiro merged commit 12967f9 into main Jun 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants