fix(fresh-agent): freshclaude/freshopencode/freshcodex parity audit#476
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four fresh-agent parity bugs identified and fixed with TDD + load-bearing validation:
OPENCODE_CMD env parity (
serve-manager.ts):??→||for empty-string handling (matchingCODEX_CMD/platform.tsconventions);this.envassigned beforethis.commandso the fallback reads the correct env. Tests isolated from hostprocess.envwithenv:{}.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."is not tracked" materialization race (
runtime-manager.ts):subscribenow usesrequireOrRecoverSessioninstead ofrequireSession, recovering untrackedses_*ids viaattachinstead of throwing — preventing client-facing errors when subscribe arrives before the runtime manager registers the materialized real id.freshcodex transcript auto-render (
codex/adapter.ts): wiredonTurnCompletedon theCodexRuntimePorttype to emit an idlesdk.session.snapshoton turn completion, so transcripts auto-render without manual pane refresh — matching freshclaude/freshopencode behavior.Verification
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).