Skip to content

fix: write session traces in headless serve mode#886

Merged
anandgupta42 merged 8 commits into
mainfrom
fix/serve-session-traces
Jun 8, 2026
Merged

fix: write session traces in headless serve mode#886
anandgupta42 merged 8 commits into
mainfrom
fix/serve-session-traces

Conversation

@ralphstodomingo
Copy link
Copy Markdown
Contributor

@ralphstodomingo ralphstodomingo commented Jun 4, 2026

Problem

Sessions run through the headless server (altimate serve) never write trace files. The VS Code "Altimate Code" chat panel drives all its sessions this way — it spawns altimate serve --port <n> and POSTs to /session/{id}/prompt_async — so chat sessions produce no ses_*.json under ~/.local/share/altimate-code/traces/, while terminal sessions (TUI, run) do.

Root cause

Session tracing is implemented at the client-entrypoint layer, not in the session engine:

  • cli/cmd/run.ts builds exporters and calls startTrace()/endTrace() itself
  • cli/cmd/tui/worker.ts subscribes to the event stream and feeds per-session traces inline
  • cli/cmd/serve.ts had no trace wiring at all

The only hook in the shared session pipeline is Tracer.active?.logSpan(...) (session/prompt.ts), and Tracer.active is only ever set by the two terminal entrypoints — in serve mode it is permanently null, so every span silently no-ops.

Fix

  • altimate/observability/trace-consumer.ts (new) — extracts the TUI worker's inline event-stream→trace logic into a shared TraceConsumer: config loading (tracing.*), per-session trace map, MAX_TRACES eviction, event feeding (message.updated, message.part.updated, session.updated, session.status), reset()/flush(). Also exports subscribeTraceConsumer() — a standalone in-process /event subscription mirroring the worker's loop, for hosts that don't have one.
  • cli/cmd/tui/worker.ts — rewired to the shared consumer; behavior unchanged (same events, same eviction, same shutdown flush).
  • cli/cmd/serve.ts — starts subscribeTraceConsumer({ directory: process.cwd() }) after Server.listen(), so serve sessions write the same trace files as the terminal.
  • Grace-window finalization — real serve sessions emit trailing events after session.status: idle (the user message is re-published with its generated summary once title generation completes). Finalizing inline on idle let the straggler re-create an empty trace whose finalization overwrote the rich <sessionId>.json. The consumer now schedules finalization 2s after idle; any event for the session during the window pushes the deadline back and is absorbed into the live trace (which also lands the generated title in trace metadata).

Known limitation / future improvement

The 2s finalize grace window is a heuristic. There is no bus signal for "no more events for session X" — session.status: idle is the engine's done-signal, but title generation completes after idle and re-publishes the user message. If that LLM call ever takes longer than the window, the straggler would still re-create an empty trace and overwrite the rich file. A deterministic guard is possible without engine changes: remember finalized sessions' known message IDs and only re-create a trace for events referencing an unseen message ID (a genuine new turn), dropping known-ID re-publishes. Deferred to keep this PR minimal.

Verification

  • bun typecheck clean; full tracing suite (107 tests) passes unchanged
  • test/altimate/trace-consumer.test.ts (7 tests): full event sequence → completed trace file with generation/tool spans + metadata, interleaved sessions, post-idle straggler doesn't clobber the trace, malformed events never throw, tracing.enabled=false writes nothing, flush()/reset() semantics
  • HTTP E2E (driving serve exactly like the chat panel): on main the traces dir stays empty; on this branch ses_*.json is written
  • Full UI E2E in docker code-server: extension volume-mounted into codercom/code-server, fixed binary at /usr/local/bin/altimate, chat panel opened, agentic message sent (glob tool call + 2 generations). Resulting trace: 42KB, completed, spans session/system-prompt/generation/tool(glob)/generation, auto-generated title in metadata, 62.8k tokens.
Chat panel (agentic turn in docker code-server) Trace saved locally in the same container
chat panel traces dir

Left: the chat session that was driven through the panel (glob tool call, model listing, 3*7). Right: a verification command run afterwards in the same container's integrated terminal — ls of the traces directory proving the chat session's ses_*.json exists locally, plus a node one-liner printing the trace's contents to show it's complete, not an empty shell:

ls -la ~/.local/share/altimate-code/traces/
node -e 'const t=require(process.argv[1]);console.log(`title: ${t.metadata.title}\nmodel: ${t.metadata.model}  status: ${t.summary.status}\nspans: ${t.spans.map(s=>s.kind).join(", ")}\ntoolCalls: ${t.summary.totalToolCalls}  generations: ${t.summary.totalGenerations}  tokens: ${t.summary.totalTokens}`)' \
  ~/.local/share/altimate-code/traces/ses_16a421cd3ffe2Ir02BuQhdh524.json
# → title: dbt models listing and 3*7 calculation
# → model: altimate-backend/altimate-default  status: completed
# → spans: session, span, generation, tool, generation
# → toolCalls: 1  generations: 2  tokens: 62855

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • In-process session tracing that writes per-session traces to a local traces directory and a stoppable background subscription for serve-mode with reconnect/backoff and graceful shutdown.
  • Improvements

    • Centralized trace handling across CLI/TUI with lazy config loading, bounded in-memory trace caching, and safer lifecycle semantics to avoid premature finalization.
  • Bug Fixes

    • Prevents cleared or evicted traces from being resurrected after stream resets/rehydration races.
  • Tests

    • Expanded tests for persistence, rehydration, lifecycle finalization, reconnection, and malformed-event resilience.

Session tracing was implemented only at the terminal entrypoints — the TUI
worker and `run` construct the tracer and feed it events, but `serve` had no
trace wiring at all. Sessions driven over HTTP (e.g. the VS Code chat panel,
which spawns `altimate serve` and POSTs to `/session/{id}/prompt_async`)
never wrote `ses_*.json` files to `~/.local/share/altimate-code/traces/`:
the only hook in the shared session pipeline is `Tracer.active?.logSpan`,
and `Tracer.active` was never set in serve mode.

- Extract the TUI worker's inline event-stream→trace logic into a shared
  `TraceConsumer` (`altimate/observability/trace-consumer.ts`): config
  loading, per-session trace map, MAX_TRACES eviction, event feeding,
  reset/flush
- Rewire `cli/cmd/tui/worker.ts` to use the shared consumer (behavior
  unchanged)
- Add `subscribeTraceConsumer()` — an in-process `/event` subscription
  mirroring the worker's loop — and start it in `ServeCommand`, so serve
  sessions produce the same trace files as the terminal
- Tests: full event-sequence → completed trace file, interleaved sessions,
  malformed events, disabled config, flush/reset semantics

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ralphstodomingo ralphstodomingo self-assigned this Jun 4, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e0d8f51a-ea2d-4807-ac18-3823d4190a59

📥 Commits

Reviewing files that changed from the base of the PR and between a701fdf and c09a486.

📒 Files selected for processing (2)
  • packages/opencode/src/altimate/observability/trace-consumer.ts
  • packages/opencode/src/cli/cmd/serve.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/opencode/src/cli/cmd/serve.ts
  • packages/opencode/src/altimate/observability/trace-consumer.ts

📝 Walkthrough

Walkthrough

A shared TraceConsumer centralizes session trace lifecycle, event routing, disk rehydration/eviction, and span logging; TUI delegates tracing to it and serve subscribes the in-process event stream. Tests and invariants are updated to validate rehydration, eviction, non-finalization-on-idle, and robustness.

Changes

TraceConsumer and integration

Layer / File(s) Summary
TraceConsumer class, state, and lifecycle controls
packages/opencode/src/altimate/observability/trace-consumer.ts
TraceConsumer initializes per-session trace caches, user message tracking, stream-generation guard, and config loading. getOrCreateTrace() creates or rehydrates traces and evicts old sessions. reset() increments generation and clears state; flush() ends all traces and awaits completion.
TraceConsumer event processing and span logging
packages/opencode/src/altimate/observability/trace-consumer.ts
handleEvent() routes bus events to per-session traces with best-effort error handling. Logs user messages as prompt spans, enriches assistant messages, processes step/tool call events, filters synthetic/ignored parts, and updates session titles without finalizing on idle.
In-process SDK subscription and event routing
packages/opencode/src/altimate/observability/trace-consumer.ts
subscribeTraceConsumer() creates a TraceConsumer, sets up auth-aware fetchFn, subscribes to the in-process SDK event stream, routes events to the consumer, and returns a stop() method that aborts subscription and flushes traces.
Serve command tracing subscription
packages/opencode/src/cli/cmd/serve.ts
Serve command imports subscribeTraceConsumer and subscribes the trace consumer with the current working directory during handler execution, enabling trace files for headless/HTTP-driven sessions.
TUI worker delegation to TraceConsumer
packages/opencode/src/cli/cmd/tui/worker.ts
TUI worker refactors to use TraceConsumer instead of inline trace state: imports TraceConsumer, constructs instance, calls reset() on new event stream, invokes loadConfig() before event loop, routes each event via handleEvent(), and calls flush() on shutdown.
TraceConsumer behavior test suite
packages/opencode/test/altimate/trace-consumer.test.ts
Comprehensive Bun test suite validates incremental snapshots (serve path), finalization on session.deleted/flush(), idle-state non-finalization, cache-miss rehydration, multi-session separation, malformed-event robustness, disabled-consumer behavior, reset() clearing, and subscribeTraceConsumer reconnection/drain.
Regression and invariant test updates
packages/opencode/test/altimate/tracing-followups.test.ts, packages/opencode/test/cli/tui/worker-trace-clearing.test.ts, packages/opencode/test/upstream/bridge-merge-invariants.test.ts
Updated tests verify behavior moved to TraceConsumer: tracing-followups confirms streamGeneration guard placement, worker-trace-clearing shifts assertions to consumer and verifies both files avoid destructive idle handlers, bridge-merge invariants expand consumer event-type literals.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI/Main
  participant Consumer as TraceConsumer
  participant SDK as SDK_EventStream
  participant Trace as Trace
  CLI->>Consumer: subscribeTraceConsumer(directory)
  Consumer->>SDK: subscribe(fetchFn)
  SDK->>Consumer: stream event
  Consumer->>Consumer: handleEvent(event)
  Consumer->>Trace: getOrCreateTrace(sessionID) / append span
  Trace->>Trace: record span / persist
  CLI->>Consumer: stop()
  Consumer->>Trace: flush() and await endTrace()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • AltimateAI/altimate-code#895: The main PR builds directly on the Trace capabilities (rehydrateFromFile, setPrompt, logUserMessage) added in #895 to implement the shared TraceConsumer.
  • AltimateAI/altimate-code#905: Both PRs implement the same trace-rehydration race fix using a monotonic streamGeneration guard.

Suggested labels

contributor

Poem

🐰 I hopped in code and found a stream,
tiny traces stitched beneath the beam.
From TUI burrows to serve-side plains,
session paths logged like careful lanes.
A carrot for each saved machine.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is comprehensive and well-structured but is missing the required 'PINEAPPLE' keyword at the top that the template mandates for AI-generated contributions. Add 'PINEAPPLE' at the very beginning of the PR description before the 'Summary' section, as required by the repository's template for AI-generated contributions.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: enabling session traces to be written in the headless serve mode, which is the primary objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/serve-session-traces

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@anandgupta42 anandgupta42 requested a review from sahrizvi June 4, 2026 13:31
ralphstodomingo and others added 2 commits June 5, 2026 11:08
Real serve sessions emit trailing events after `session.status: idle` —
the user message is re-published with its generated `summary` once title
generation completes. Finalizing immediately on idle deleted the trace
from the map, and the straggler then lazily re-created an EMPTY trace
for the same sessionID whose finalization overwrote the rich
`<sessionId>.json` file (observed end-to-end in docker code-server:
42KB trace clobbered down to 749 bytes).

Fix: schedule finalization 2s after idle instead of finalizing inline;
any event for the session during the grace window pushes the deadline
back and is absorbed into the live trace. As a bonus the generated
session title now lands in trace metadata. flush()/reset()/eviction
clear pending timers.

Verified: chat session in docker code-server (glob tool call + 2
generations) produces a complete 42KB trace with title, model, tool
span, and token totals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The worker's inline event handling moved into the shared
altimate/observability/trace-consumer.ts — point the cross-module
event-type literal invariant at the new consumer (and cover the full
set it consumes) instead of cli/cmd/tui/worker.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

Resolves the worker.ts conflict from main's trace-data-loss work (#895,
#901-905), which landed in parallel with this branch's trace extraction.

Both lines of work fix the same class of bug (traces losing data / being
clobbered) from different angles:
  - this branch: extract the worker's inline event→trace logic into a shared
    TraceConsumer and wire it into `altimate serve`, so the VS Code chat panel
    (headless serve) writes traces at all (AI-6950).
  - main #895+: stop finalizing on session.status=idle (idle fires every turn,
    not at session end), rehydrate-from-disk on cache-miss re-creation, and a
    streamGeneration guard against concurrent stream teardown.

Resolution: main's superior logic is the source of truth. The shared
TraceConsumer is now a faithful 1:1 port of main's *current* worker logic
(no idle finalize, rehydrateFromFile, streamGeneration guard, logUserMessage /
setPrompt-vs-setTitle split, synthetic/ignored skip). The worker delegates to
it; `serve` subscribes to it. This branch's earlier 2s grace-window heuristic
is dropped entirely — main's rehydrate-on-cache-miss is the deterministic fix
it was a stand-in for.

Source-grep regression guards that asserted the trace logic lived in worker.ts
are repointed at trace-consumer.ts (worker-trace-clearing, tracing-followups
#902, bridge-merge-invariants), with the guard intent preserved.

Verified: typecheck clean; trace-consumer, worker-trace-clearing, tracing-*,
and bridge-merge-invariants suites all green; host-level `serve` smoke test
writes a rich 42KB trace (session/user-message/generation spans).
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@ralphstodomingo ralphstodomingo marked this pull request as ready for review June 8, 2026 05:12
Copilot AI review requested due to automatic review settings June 8, 2026 05:12
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds trace-file generation for sessions executed via the headless HTTP server (altimate serve) by extracting the TUI worker’s event-stream→trace logic into a shared TraceConsumer, then wiring that consumer into serve mode.

Changes:

  • Introduces a shared TraceConsumer (+ helper subscribeTraceConsumer) to build per-session traces from bus events and write them via configured exporters.
  • Rewires the TUI worker to delegate all tracing/event handling and lifecycle operations (loadConfig, reset, flush) to TraceConsumer.
  • Updates and adds tests to validate the new consumer behavior and to keep invariant checks aligned with the new source of event-literal usage.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/opencode/src/altimate/observability/trace-consumer.ts Adds the shared event-stream consumer and an in-process subscriber for serve mode.
packages/opencode/src/cli/cmd/serve.ts Starts the in-process trace consumer subscription in headless serve mode.
packages/opencode/src/cli/cmd/tui/worker.ts Replaces inline tracing logic with the shared TraceConsumer.
packages/opencode/test/altimate/trace-consumer.test.ts New unit tests for the extracted consumer’s event handling and file output.
packages/opencode/test/altimate/tracing-followups.test.ts Updates source-contract tests to target TraceConsumer instead of the worker.
packages/opencode/test/cli/tui/worker-trace-clearing.test.ts Updates regression tests to validate both worker delegation and consumer behavior.
packages/opencode/test/upstream/bridge-merge-invariants.test.ts Updates event-type literal invariants to point at the new consumer location.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/opencode/src/cli/cmd/serve.ts Outdated
Comment on lines 26 to 36
// altimate_change start — trace: session tracing in headless serve
// Sessions driven over HTTP (e.g. the VS Code chat panel) have no TUI
// worker observing the event stream, so traces were never written in
// serve mode. Subscribe the shared trace consumer to the in-process
// event stream so serve sessions produce the same trace files as the
// terminal entrypoints.
subscribeTraceConsumer({ directory: process.cwd() })
// altimate_change end

await new Promise(() => {})
await server.stop()
Comment on lines +323 to +352
;(async () => {
await consumer.loadConfig()
while (!signal.aborted) {
const events = await Promise.resolve(sdk.event.subscribe({}, { signal })).catch(() => undefined)

if (!events) {
await sleep(250)
continue
}

for await (const event of events.stream) {
await consumer.handleEvent(event)
}

if (!signal.aborted) {
await sleep(250)
}
}
})().catch((error) => {
Log.Default.error("trace event stream error", {
error: error instanceof Error ? error.message : error,
})
})

return {
stop: async () => {
abort.abort()
await consumer.flush()
},
}
Comment on lines +138 to +139
Trace.setActive(trace)
this.sessionTraces.set(sessionID, trace)
Comment on lines +13 to +18
* The per-event logic here is a 1:1 port of the worker's inline handling so
* both front-ends behave identically:
* - traces are NOT finalized on `session.status: idle` — idle fires after
* every turn, not at session end; finalization happens on flush()
* (shutdown) and on MAX_TRACES eviction. Long-lived sessions keep their
* Trace in cache across turns.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/opencode/test/altimate/trace-consumer.test.ts (1)

263-264: ⚡ Quick win

Replace fixed sleep with deterministic completion in the reset test.

setTimeout(100) is timing-sensitive and can flake under slower CI. Prefer polling/assert-retry for file existence/content or exposing an awaitable reset-finalization path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/test/altimate/trace-consumer.test.ts` around lines 263 -
264, The test currently uses a fixed sleep (await new Promise(r => setTimeout(r,
100))) to wait for reset finalization; replace that with a deterministic wait by
either (A) polling/assert-retry until the observable side-effect is present
(e.g., check file existence or expected file content in a retry loop with short
backoff and a timeout) or (B) modify the production/test API to expose an
awaitable reset-finalization Promise and call that (e.g., await
traceConsumer.resetFinalized() or await endTrace()). Update the test around
endTrace/reset finalizes to remove the fixed setTimeout and use the chosen
deterministic completion check so CI cannot flake.
packages/opencode/src/cli/cmd/serve.ts (1)

26-33: 💤 Low value

Consider capturing the subscription for graceful shutdown.

The return value of subscribeTraceConsumer() provides a stop() method that aborts the subscription and flushes traces. Discarding it means flush() is never called when the server terminates, potentially losing final trace state.

This is consistent with the existing pattern (line 36 server.stop() is already unreachable), but capturing the subscription would enable proper cleanup if graceful shutdown handling is added later:

-    subscribeTraceConsumer({ directory: process.cwd() })
+    const tracing = subscribeTraceConsumer({ directory: process.cwd() })

A follow-up could add SIGINT/SIGTERM handlers to call tracing.stop() and server.stop().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/serve.ts` around lines 26 - 33, Capture the
subscription returned by subscribeTraceConsumer({ directory: process.cwd() })
(it exposes a stop() method) into a variable (e.g., tracing) instead of
discarding it, and ensure that tracing.stop() is invoked during shutdown/cleanup
alongside server.stop() so traces are flushed; locate the subscribeTraceConsumer
call and add the variable assignment and a place in your existing shutdown flow
to call tracing.stop() where server.stop() is handled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/test/upstream/bridge-merge-invariants.test.ts`:
- Around line 167-169: The consumer-literal invariant for TraceConsumer is
missing "session.status" so tests won't catch producer/consumer drift; update
the invariant array used by the TraceConsumer test (the literal list defined
near TraceConsumer in bridge-merge-invariants.test.ts) to include
"session.status" alongside "message.updated", "message.part.updated", and
"session.updated" so the test asserts that TraceConsumer handles that event
literal.

---

Nitpick comments:
In `@packages/opencode/src/cli/cmd/serve.ts`:
- Around line 26-33: Capture the subscription returned by
subscribeTraceConsumer({ directory: process.cwd() }) (it exposes a stop()
method) into a variable (e.g., tracing) instead of discarding it, and ensure
that tracing.stop() is invoked during shutdown/cleanup alongside server.stop()
so traces are flushed; locate the subscribeTraceConsumer call and add the
variable assignment and a place in your existing shutdown flow to call
tracing.stop() where server.stop() is handled.

In `@packages/opencode/test/altimate/trace-consumer.test.ts`:
- Around line 263-264: The test currently uses a fixed sleep (await new
Promise(r => setTimeout(r, 100))) to wait for reset finalization; replace that
with a deterministic wait by either (A) polling/assert-retry until the
observable side-effect is present (e.g., check file existence or expected file
content in a retry loop with short backoff and a timeout) or (B) modify the
production/test API to expose an awaitable reset-finalization Promise and call
that (e.g., await traceConsumer.resetFinalized() or await endTrace()). Update
the test around endTrace/reset finalizes to remove the fixed setTimeout and use
the chosen deterministic completion check so CI cannot flake.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d4fd89e9-9c10-41b7-8c0a-ac1930f33804

📥 Commits

Reviewing files that changed from the base of the PR and between 5ffeadb and d965a24.

📒 Files selected for processing (7)
  • packages/opencode/src/altimate/observability/trace-consumer.ts
  • packages/opencode/src/cli/cmd/serve.ts
  • packages/opencode/src/cli/cmd/tui/worker.ts
  • packages/opencode/test/altimate/trace-consumer.test.ts
  • packages/opencode/test/altimate/tracing-followups.test.ts
  • packages/opencode/test/cli/tui/worker-trace-clearing.test.ts
  • packages/opencode/test/upstream/bridge-merge-invariants.test.ts

Comment on lines +17 to +33
import { describe, expect, test, beforeEach, afterEach } from "bun:test"
import fs from "fs/promises"
import path from "path"
import os from "os"
import { TraceConsumer } from "../../src/altimate/observability/trace-consumer"
import { FileExporter, type TraceFile } from "../../src/altimate/observability/tracing"

let tmpDir: string

beforeEach(async () => {
tmpDir = path.join(os.tmpdir(), `trace-consumer-${Date.now()}-${Math.random().toString(36).slice(2)}`)
await fs.mkdir(tmpDir, { recursive: true })
})

afterEach(async () => {
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {})
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use tmpdir() fixture with await using instead of module-level os.tmpdir() lifecycle hooks.

This new test file should follow the repository temp-dir convention to guarantee scoped cleanup and consistency with the tracing test suite.

As per coding guidelines, packages/opencode/test/**/*.test.ts must use tmpdir from fixture/fixture.ts with await using, and based on learnings this is required for new files under packages/opencode/test/altimate/.

Sources: Coding guidelines, Learnings

Comment on lines +167 to +169
file: "altimate/observability/trace-consumer.ts",
types: ["message.updated", "message.part.updated", "session.updated"],
},
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jun 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include session.status in the TraceConsumer consumer-literal invariant list.

TraceConsumer processes session.status events, so this invariant should also assert that literal to prevent silent producer/consumer drift on that path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/test/upstream/bridge-merge-invariants.test.ts` around lines
167 - 169, The consumer-literal invariant for TraceConsumer is missing
"session.status" so tests won't catch producer/consumer drift; update the
invariant array used by the TraceConsumer test (the literal list defined near
TraceConsumer in bridge-merge-invariants.test.ts) to include "session.status"
alongside "message.updated", "message.part.updated", and "session.updated" so
the test asserts that TraceConsumer handles that event literal.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining this one: TraceConsumer does not consume session.status — the only occurrences of that string are in explanatory comments ("DO NOT finalize on session.status=idle"), not a handled event literal. The invariant test matches a quoted "session.status" string, so adding it to the list would fail CI (there is no such literal in the file). The consumed literals (message.updated, message.part.updated, session.updated, session.deleted) are all registered.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anandgupta42, you're right — I was mistaken. session.status appears only in guard-comment prose (e.g. "DO NOT finalize on session.status=idle") and is not an event literal that TraceConsumer handles. Adding it to the invariant array would cause a spurious CI failure. The four actually-consumed literals (message.updated, message.part.updated, session.updated, session.deleted) are already covered. Apologies for the noise.

(ᵔᴥᵔ)


✏️ Learnings added
Learnt from: anandgupta42
Repo: AltimateAI/altimate-code PR: 886
File: packages/opencode/test/upstream/bridge-merge-invariants.test.ts:167-169
Timestamp: 2026-06-08T08:38:51.662Z
Learning: In `packages/opencode/src/altimate/observability/trace-consumer.ts`, the phrase `session.status` appears only in explanatory comments (e.g. "DO NOT finalize on `session.status=idle`") and is NOT a consumed event literal. The actual event literals handled by `TraceConsumer` are: `"message.updated"`, `"message.part.updated"`, `"session.updated"`, and `"session.deleted"`. Do not suggest adding `"session.status"` to invariant lists for this file.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: sahrizvi
Repo: AltimateAI/altimate-code PR: 895
File: packages/opencode/test/altimate/tracing-rehydrate.test.ts:0-0
Timestamp: 2026-06-05T17:51:12.678Z
Learning: In `packages/opencode/test/altimate/` (tracing tests), NEW test files should follow the documented convention and use `await using tmp = await tmpdir()` (imported from `fixture/fixture.ts`) with per-test scoping, not a module-level `os.tmpdir()` + `beforeEach`/`afterEach` pattern. The existing sibling tests (`tracing-display-crash`, `tracing-rename-race`, `tracing-adversarial-*`) predate this convention and still use the manual pattern; migrating them is a separate cleanup PR. Do not suppress `tmpdir()` fixture suggestions for brand-new files added to this directory.

Learnt from: kulvirgit
Repo: AltimateAI/altimate-code PR: 405
File: test/sanity/phases/resilience.sh:214-223
Timestamp: 2026-03-30T20:48:47.930Z
Learning: In `test/sanity/phases/resilience.sh`, the sanity tests run in a Docker container against the npm-installed `altimate-code` binary, not the TypeScript source tree. TypeScript source files (`.ts`) are compiled into the binary and cannot be `require()`-d directly. Module-load checks in this phase should be structured as runtime smoke tests (e.g., invoking the CLI or checking the binary's behavior), not as direct `require('./src/...')` TypeScript imports. Suggesting direct `.ts` module imports for these tests is incorrect.

Learnt from: CR
Repo: AltimateAI/altimate-code PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-05-02T23:15:06.220Z
Learning: Applies to packages/opencode/**/*.test.{ts,tsx} : Migration tests should read the per-folder layout without relying on `_journal.json`

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/altimate/observability/trace-consumer.ts">

<violation number="1" location="packages/opencode/src/altimate/observability/trace-consumer.ts:138">
P1: `Trace.setActive(trace)` sets a single process-global active tracer. In serve mode, multiple sessions can run concurrently (e.g., `POST /session/:id/prompt_async` doesn't await completion), so this global will point at whichever session was most recently created. Any code using `Tracer.active?.logSpan(...)` during an earlier session's execution will attribute spans to the wrong session trace, causing cross-session trace corruption. The active trace should be scoped per async context (e.g., `AsyncLocalStorage`) or serve mode should avoid setting the global active tracer.</violation>

<violation number="2" location="packages/opencode/src/altimate/observability/trace-consumer.ts:249">
P2: `session.updated` title is silently dropped when it arrives before message events, because the handler only looks up cached traces instead of falling back to `getOrCreateTrace`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/opencode/src/altimate/observability/trace-consumer.ts Outdated
if (e.type === "session.updated") {
const info = e.properties?.info as Record<string, any> | undefined
if (info?.id && info?.title) {
const trace = this.sessionTraces.get(info.id)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: session.updated title is silently dropped when it arrives before message events, because the handler only looks up cached traces instead of falling back to getOrCreateTrace.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/altimate/observability/trace-consumer.ts, line 249:

<comment>`session.updated` title is silently dropped when it arrives before message events, because the handler only looks up cached traces instead of falling back to `getOrCreateTrace`.</comment>

<file context>
@@ -0,0 +1,353 @@
+      if (e.type === "session.updated") {
+        const info = e.properties?.info as Record<string, any> | undefined
+        if (info?.id && info?.title) {
+          const trace = this.sessionTraces.get(info.id)
+          if (trace) trace.setTitle(String(info.title))
+        }
</file context>

Comment thread packages/opencode/src/altimate/observability/trace-consumer.ts
Comment thread packages/opencode/src/cli/cmd/serve.ts Outdated
worker.ts is an upstream-shared file; the traceConsumer.reset() call (which
replaced main's inline sessionTraces clear) must be wrapped so the upstream
merge tooling protects it. Caught by script/upstream/analyze.ts --markers
--strict (the Marker Guard CI check).
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

1 similar comment
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@anandgupta42
Copy link
Copy Markdown
Contributor

🤖 Multi-Model Consensus Code Review

Reviewed by 8 participants — Claude + GPT 5.4, Gemini 3.1 Pro, Kimi K2.5, MiniMax M2.7, GLM-5, Qwen 3.6 Plus, MiMo-V2-Pro. Convergence: 1 round, verdict unanimous (6 APPROVE on the synthesized findings + 1 wording-caveat that accepts the severity/verdict). DeepSeek V3.2 excluded (provider error).

Code Review Summary — PR #886 "fix: write session traces in headless serve mode"

Verdict: REQUEST CHANGES
Critical Issues: 0
Major Issues: 4
Minor Issues: 4
Nits: 4

The core change is high quality: the TUI worker's ~130 lines of inline event→trace logic are extracted into a shared, well-tested TraceConsumer class, and altimate serve now subscribes to it so headless (VS Code chat panel) sessions get traces. The extraction is a faithful 1:1 port, the streamGeneration race guard is genuinely well-designed, and the fail-safe discipline (observability never crashes core) is consistent. What blocks merge is the serve lifecycle: the consumer is wired up but never finalized on shutdown, never recovers from a stream error, and the entire serve integration is untested.


Major Issues

1. serve.ts discards the stop() handle — serve traces are never finalized (Bug/Lifecycle) — packages/opencode/src/cli/cmd/serve.ts:32

subscribeTraceConsumer({ directory: process.cwd() })  // { stop } discarded
await new Promise(() => {})                            // blocks forever
await server.stop()                                    // dead code

stop() is the only path that calls consumer.flush()trace.endTrace(), which sets summary.status = "completed", computes the summary, and writes the narrative. With the handle discarded and no SIGINT/SIGTERM/beforeExit handlers, flush() never runs.

Precise impact (verified, not "all traces lost"): incremental snapshot() calls inside startTrace/logToolCall/logText/logUserMessage write span data to disk atomically as events arrive, so span data does persist. What is lost is (a) finalization — files stay un-completed, with no summary/narrative; and (b) a possible tail — the last debounced/pending snapshotPromise may not complete on abrupt kill. Three reviewers rated this CRITICAL on the assumption of total data loss; it is downgraded to MAJOR because snapshots persist — but it still defeats the PR's finalization goal, so it blocks merge. To stay precise: traces are not finalized via this integration as written (no graceful-shutdown path is wired) — it is not that finalization is impossible in principle.

The codebase already has the right pattern: run.ts:799-811 registers SIGINT/SIGTERM/beforeExit handlers that call tracer.flushSync(...). Fix:

const traceSub = subscribeTraceConsumer({ directory: process.cwd() })
const shutdown = async () => { await traceSub.stop(); await server.stop(); process.exit(0) }
process.once("SIGINT", () => void shutdown())
process.once("SIGTERM", () => void shutdown())
await new Promise(() => {})

Note: TraceConsumer only exposes an async flush(), while run.ts deliberately uses flushSync because async work in a signal handler can be cut off before exit. Either await stop() fully before process.exit (works for graceful signals) or add a sync flush path for robustness.

2. stop() aborts the stream before draining it — flush races in-flight events (Bug/Lifecycle) — packages/opencode/src/altimate/observability/trace-consumer.ts:347

stop: async () => { abort.abort(); await consumer.flush() }

stop() aborts the signal and immediately flushes, but the background IIFE loop only checks signal.aborted between iterations — it does not interrupt a handleEvent already in progress, and the loop promise is never awaited. So flush() can run concurrently with an in-flight handleEvent, and any events still buffered in the async iterator are dropped. (GPT, Kimi, and Qwen independently flagged this; elevated to a distinct MAJOR because it is a separate correctness defect from the missing signal handlers in Issue 1, and both must be fixed together.) Fix: capture the loop promise and await it inside stop() before flushing:

const loopPromise = (async () => { /* existing while loop */ })()
return { stop: async () => { abort.abort(); await loopPromise.catch(() => {}); await consumer.flush() } }

3. The event-stream loop dies permanently on a mid-stream error (Bug/Resilience) — packages/opencode/src/altimate/observability/trace-consumer.ts:333

for await (const event of events.stream) { await consumer.handleEvent(event) }

The .catch(() => undefined) only guards subscription setup. If events.stream throws mid-iteration (network disconnect, server hiccup), it propagates to the outer IIFE .catch, breaking out of while (!signal.aborted) for good — tracing is silently dead for the rest of the (long-lived) server's uptime. Fix: wrap the for await in try/catch inside the while so the next iteration reconnects:

try {
  for await (const event of events.stream) { await consumer.handleEvent(event) }
} catch (err) {
  Log.Default.warn("trace event stream disconnected", { error: err instanceof Error ? err.message : String(err) })
}

While here, add exponential backoff to the 250ms reconnect (cap ~10–30s) to avoid hot-looping when the stream is durably down (Kimi/GLM-5/MiMo).

4. The serve integration path is untested (Testing) — packages/opencode/test/altimate/trace-consumer.test.ts
The TraceConsumer class is tested thoroughly, but subscribeTraceConsumer — the function actually wired into serve (the SDK client + fetchFn auth shim, the reconnect loop, stop()flush()) — has zero coverage. And every test calls flush() before reading the file, yet serve never flushes (Issue 1): so no test exercises the real serve path (incremental-snapshot-only, no flush). Add: (a) a subscribeTraceConsumer test that feeds events through a mock stream, calls stop(), and asserts summary.status === "completed"; (b) a stream-failure test asserting the loop reconnects (covers Issue 2); (c) a test reading a trace file after live events without flush, asserting spans are on disk.


Minor Issues

5. Config-load failure silently falls back to default tracing (Bug) — trace-consumer.ts:99-101. On a Config.get() exception the catch leaves enabled = true and exporters = undefined; getOrCreateTrace then uses Trace.create(), which does create a default FileExporter (new Trace([new FileExporter(), ...])). So tracing silently runs against the default dir rather than clearly honoring the configured exporters/dir or clearly disabling. (Whether a configured tracing.enabled: false is also ignored depends on the failure happening before that field is read — true when Config.get() itself throws.) Prefer failing safe (enabled = false) on config error, and log a debug line. (Corrects an inaccurate claim in Claude's initial draft that this produced no traces.)

6. Silent catch {} blocks give no diagnostics (Code Quality) — trace-consumer.ts:147-149, 265-267 (+ loadConfig). The fail-safe contract is correct, but on a headless server a systematic failure (disk full, permissions) fails every event with zero signal. Keep "never throws" but add Log.Default.debug("[tracing] …", { error }).

7. sessionUserMsgIds accumulates without end-of-session cleanup (Performance) — trace-consumer.ts:53, 177-179. Reclaimed only on eviction/reset()/flush() — none of which fire in a serve process serving <100 sessions. Session-scoped state therefore accumulates over the process lifetime (growth proportional to total turns), with no per-session completion cleanup. Tie cleanup to a real session-end signal if the bus exposes one (also helps Issue 1).

8. reset() fire-and-forget test relies on a fixed setTimeout(100) (Testing) — test/altimate/trace-consumer.test.ts:264. Flaky under CI load. Least-invasive fix: poll for the expected trace state/file in the test instead of a fixed delay (avoids changing reset()'s signature). If a richer fix is wanted, make reset() return a Promise that awaits the endTrace calls like flush() does. Aligns with the repo's "no flaky tests" policy.


Nits

9. Unguarded concurrent getOrCreateTrace(sameSID) (Concurrency — latent only) — trace-consumer.ts:104-146. Two interleaved calls for the same session (same generation) could each build a Trace and the second clobbers the first in the map, orphaning it (no endTrace). The reviewed consumption path is a serial for await loop with no concurrent callers, so this is not a demonstrated bug — purely a hardening note (downgraded from MINOR per Qwen/GPT convergence). A pendingCreations map would dedupe if concurrent callers are ever introduced.

10. Trace.setActive(trace) mutates a process-global across concurrent serve sessionstrace-consumer.ts:138. Last-writer-wins on Trace._active. Verified harmless today: Trace.active has zero readers anywhere in the codebase — but it's a latent footgun for multi-session serve; consider not calling it from the shared consumer, or set the map before setActive (MiniMax) so a throw can't leave a cache miss.

11. btoa() crashes on non-Latin1 server passwordstrace-consumer.ts:317. Use Buffer.from(\${username}:${password}`).toString("base64")`. Pre-existing parity with worker.ts, but worth fixing in both.

12. await traceConsumer.handleEvent(event) delays Rpc.emit to the TUIworker.ts:104. handleEvent can do disk I/O (rehydrateFromFile); forwarding waits on it. Pre-existing behavior, noted for awareness.


Positive Observations

  • Genuine de-duplication: ~130 lines of inline worker logic → one shared, tested class; worker becomes a thin delegator with identical behavior.
  • The streamGeneration guard (capture-before-await, re-check-after, same-synchronous-turn insert) is an elegant, correct fix for the reset/rehydrate orphan-writer race — and is locked by a source-shape contract test.
  • Excellent "why" comments (idle≠session-end, rehydrate-vs-clobber, synthetic-text filtering).
  • bridge-merge-invariants.test.ts correctly migrates the event-type literal registration to trace-consumer.ts, keeping the invariant honest rather than bypassing it.
  • Thorough class-level tests: malformed events, disabled consumer, rehydration, interleaved sessions, multi-turn.

Missing Tests (summary)

  • subscribeTraceConsumer start/stop/flush lifecycle, incl. drain-before-flush (Issues 2, 4).
  • Stream-disconnect → reconnect (Issue 3).
  • Trace file content before any flush — the real serve path (Issue 1).
  • Config-load-failure catch path (Issue 5).
  • Runtime race test for the streamGeneration guard (currently source-shape only).

Addresses the multi-model consensus review on PR #886. The `TraceConsumer`
extraction was sound, but the `serve` integration had lifecycle gaps.

`cli/cmd/serve.ts`:
- Capture the `subscribeTraceConsumer` handle and wire `SIGINT`/`SIGTERM`/
  `beforeExit` to `stop()` so traces are finalized on shutdown instead of left
  un-`completed`. Mirrors the existing pattern in `cli/cmd/run.ts`.

`altimate/observability/trace-consumer.ts`:
- `stop()` now drains the event loop (bounded by a 1s timeout) before
  `flush()`, so finalization can't race an in-flight `handleEvent`.
- The `for await` stream loop catches mid-stream errors and reconnects with
  exponential backoff (250ms -> 30s) instead of dying permanently; backoff
  sleeps are abortable so shutdown isn't delayed.
- Finalize and evict per-session state on `session.deleted` (a real
  end-of-session signal) so long-lived serve processes don't accumulate
  `sessionUserMsgIds` / cached traces.
- `loadConfig` failure now fails safe (`enabled = false`) instead of silently
  tracing to the default dir, and logs at debug.
- `getOrCreateTrace` and `handleEvent` catch blocks log at debug (still never
  throw) for headless diagnosability.
- Auth header uses `Buffer.from(...).toString("base64")` (UTF-8 safe) instead
  of `btoa`.
- Added a test seam: `subscribeTraceConsumer` accepts an optional injected
  `consumer` and `subscribe` source (production behaviour unchanged via
  defaults).

Tests:
- New `subscribeTraceConsumer` coverage: `stop()` drains + finalizes; a
  mid-stream throw reconnects rather than killing the loop.
- New `TraceConsumer` coverage: incremental snapshots land on disk BEFORE any
  flush (the real serve path), and `session.deleted` finalizes + releases
  state.
- De-flaked the `reset()` test (poll instead of fixed `setTimeout`).
- Registered the `session.deleted` literal in `bridge-merge-invariants`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@anandgupta42
Copy link
Copy Markdown
Contributor

✅ Review fixes applied — commit a701fdf

Addressed the consensus review in one commit on this branch:

Major

  1. Discarded stop() / no finalizationserve.ts now captures the handle and wires SIGINT/SIGTERM/beforeExittraceSub.stop() (mirrors run.ts), so serve traces are finalized on shutdown.
  2. stop() aborted before drainingstop() now drains the loop (bounded by a 1s timeout) before flush(), so finalization can't race an in-flight handleEvent.
  3. Loop died permanently on mid-stream errorfor await is wrapped in try/catch inside the while, reconnecting with exponential backoff (250ms→30s); backoff sleeps are abortable so shutdown stays fast.
  4. Serve integration untested — added a test seam (injectable consumer/subscribe, production unchanged) + tests: stop() drains & finalizes, and a mid-stream throw reconnects instead of killing the loop. Also added a test that incremental snapshots land on disk before any flush (the real serve path).

Minor
5. Config-load failure — now fails safe (enabled = false) + debug log instead of silently tracing to the default dir.
6. Silent catch {}getOrCreateTrace/handleEvent now log at debug (still never throw).
7. sessionUserMsgIds growth — finalize + evict per-session state on session.deleted (a real end-of-session signal); test added.
8. Flaky setTimeout(100) test — de-flaked to poll.

Nit
11. btoa non-Latin1 crash — switched to Buffer.from(...).toString("base64").

Nits 9 (concurrent getOrCreateTrace — latent, serial loop) and 10 (Trace.setActive — verified zero readers) left as-is by design; #12 is pre-existing.

Verified locally: marker guard ✅, typecheck ✅, trace-consumer / worker-trace-clearing / bridge-merge-invariants / tracing suites all green.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/altimate/observability/trace-consumer.ts">

<violation number="1" location="packages/opencode/src/altimate/observability/trace-consumer.ts:249">
P2: `session.updated` title is silently dropped when it arrives before message events, because the handler only looks up cached traces instead of falling back to `getOrCreateTrace`.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/opencode/src/altimate/observability/trace-consumer.ts Outdated
Comment thread packages/opencode/src/cli/cmd/serve.ts Outdated
@dev-punia-altimate
Copy link
Copy Markdown

❌ Tests — Failures Detected

TypeScript — 15 failure(s)

  • connection_refused [1.00ms]
  • timeout
  • permission_denied
  • parse_error
  • network_error [1.00ms]
  • auth_failure
  • rate_limit
  • internal_error
  • empty_error
  • connection_refused
  • timeout [1.00ms]
  • permission_denied
  • parse_error
  • network_error
  • auth_failure

Next Step

Please address the failing cases above and re-run verification.

cc @ralphstodomingo

Follow-up to the consensus-review fixes, addressing cubic-dev-ai comments:

- serve.ts: SIGINT/SIGTERM shutdown now exits with signal-conventional codes
  (130 / 143) instead of 0, so a signalled stop isn't masked as a successful
  run. beforeExit still exits 0. Matches cli/cmd/run.ts.
- trace-consumer.ts (loadConfig): a config-load error no longer disables
  tracing — it keeps `enabled` true and falls back to Trace.create()'s default
  exporter (the original "must not prevent the host from working" intent), but
  now logs a warning so the fallback isn't silent.
- trace-consumer.ts (getOrCreateTrace): drop the dead `Trace.setActive(trace)`
  call. It sets a process-global active trace that nothing reads and that is a
  footgun in multi-session serve (last-event-wins); per-session routing is via
  the sessionTraces map.

Verified: marker guard, typecheck, and trace-consumer / tracing-followups /
worker-trace-clearing / bridge-merge-invariants suites all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

1 similar comment
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@anandgupta42 anandgupta42 merged commit ed1eecd into main Jun 8, 2026
23 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants