Skip to content

fix(native-chat): structured chat failures reach the diagnostic trace instead of vanishing - #24079

Closed
brennanb2025 wants to merge 8 commits into
mainfrom
brennanb2025/structured-session-failure-reports
Closed

brennanb2025 wants to merge 8 commits into
mainfrom
brennanb2025/structured-session-failure-reports

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 63 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1071 $\color{#cf222e}{\Huge{\mathbf{−}}}$​173 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​898
Prod 63 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​527 $\color{#cf222e}{\Huge{\mathbf{−}}}$​250 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​277

What this fixes

When something fails in the background of a structured native chat (the Codex/Claude chat UI), Orca records nothing a person can find. Examples: recording that the agent process exited, renewing the session lease, writing the conversation journal, settling a late message, saving the point where a Claude chat can resume, saving restart-recovery data. The chat may show that something went wrong (#23116, #22999, #23679), but the error itself, its stack and its cause are lost.

This is what kept the Sep 10 P0 ("native Codex chat cannot resume after update") invisible: the write that records a provider's exit kept failing, and no log said so. That P0 itself is fixed on main (#22820, #20509). This PR makes the next failure like it diagnosable: each one now lands in the local diagnostic trace with the step that failed and the chat's session id.

Scope: structured native chat only (behind experimentalStructuredNativeChat, off by default), main process. That covers the Codex/Claude structured session host and journal, the Claude structured-session adapter, and the structured-session create RPC. Not affected: terminal agents, the renderer, the chat UI (nothing visible changes), other subsystems' logging.

Supersedes #20542, which wired the one missing callback but kept the optional-callback shape that caused the loss.

ELI5

The chat's background machinery had two ways to shout "this failed": a phone line nobody ever plugged in, and shouting into a room that doesn't exist in the shipped app. Now every failure writes a note in the app's diagnostic trace instead, and a test stops anyone adding a new silent shout.

What Changed

Before: a background failure in a structured chat went to an optional callback that the only production caller never supplied, or to console.warn, which a packaged Electron main process has nowhere to print. Either way it was gone. After: it becomes one agent-session.failure record in logs/main.trace.ndjson (the rotated, size-capped, redacted trace the diagnostic bundle already collects), with step, sessionId and the error. The user sees no difference in the chat.

  • One reporter: traceAgentSessionError({ step, sessionId, error, detail }) in src/main/observability/agent-session-error-trace.ts writes a failed span through the existing ambient tracer plus a console line (the only record where no trace sink is installed, e.g. the headless server or diagnostics disabled). It never throws.
  • Each step must declare what it may record. One table maps every step (70) to 'full' (message, stack, cause chain, error code) or 'kind' (only the error's class and code, e.g. SqliteError SQLITE_FULL); the step type is derived from it, so a new step can't compile without that choice. The 15 'kind' steps are the restart-recovery and operation-settlement steps that existing privacy tests already keep out of logs: reading the recovery capsule (which holds the user's latest prompt) can throw a JSON.parse error that quotes its input.
  • The optional callback chain is gone. Runtime onError, host onEventSinkError, onBarrierError, onDeliveryError, onNoteFailed and the reporting-only onError deps of the lease renewer, idle sweep, delivery loop, queued drain and lifecycle delivery are removed; each catch site reports with its own step and session id.
  • Callbacks that carry behavior stay, required. The event sink's onFailure still forces the failing session's provider down, exactly as before. Dead-generation settlement returns { ok } | { ok: false, error } and the host stop still throws that error as the cause.
  • About 50 console failure sites migrated, with the caught error attached where it used to be dropped, including Claude's own "exit cursor / resume point was not persisted" (Claude's version of the Sep 10 failure). Two writes that swallowed their failure with no trace at all (a queued draft's hold marker, a delivery-doubt record) now report; they still swallow.
  • A stuck lease is reported once, not every 10 s. Live QA showed that after one short store failure, lease renewal fails every tick for as long as the session stays in that state. The renewer now records a session's failure once until that session renews or the failure changes (the same rule the journal read path already uses).
  • A ratchet test fails if console.warn/console.error or an optional on*Error?/on*Failure?/on*Failed? callback reappears in structured-session production code (agent-session wire/journal, model catalog, runtime structured-agent-session-*, Claude claude-structured-*, RPC structured-agent-session-*). Two behavioral optional callbacks are allowlisted with a comment.

Why

The bug was structural: a failure's destination was optional, so forgetting to wire it was silent, and the fallback (the console) does not exist in a shipped app. Wiring the missing callback (#20542) fixes today's gap but leaves nine optional hops that the next refactor can break the same way. Here reporting doesn't depend on any caller: the trace sink is installed once at startup, the same way the tracer is already used across the main process (one span helper per domain in observability/), and every catch site reports with its own context. It removes plumbing rather than adding it, and the ratchet keeps the class from coming back.

Differences from how comparable session runtimes do this:

  • Destination is the redacted trace file, not a separate text log. Intended: it's the file the diagnostic bundle already collects, it's size-capped (10 MB × 10), and it passes through the existing redactor.
  • 15 steps record only the error's class and code. Intended, for privacy: their errors can quote the user's latest prompt.
  • No general rate limit. Intended: only one timer-driven path was found to repeat indefinitely (lease renewal, every 10 s per stuck session, about 15 MB/day measured live), and it now dedupes per session. Event-driven paths (journal row bookkeeping, status publish) fire per user activity, not on a timer; an idle-sweep settlement failure records twice per 5-minute sweep (accepted).
  • Some failure sites outside structured chat still log only to the console: the orchestration worker session, worktree teardown, and about 27 background git sites (git command failures themselves already reach the trace). Temporary: same bug class, left out to keep this PR to one subsystem; no follow-up ticket filed yet.

Privacy notes. 'full' steps can record local file paths from filesystem errors (OS username, project folder), which the redactor keeps by existing policy. provider-start, operation-agent-start and claude-dispatch-write can carry the provider's stderr tail (up to 8 KB for Claude) or Claude's error-result text; Orca already stores that text locally and shows it behind "Details", and the diagnostic bundle is previewed before any upload. No 'full' step was found to carry prompt, message, draft, journal-row or transcript text.

Linked Issue

N/A (maintainer). Follow-up to the Sep 10 P0 fixed by #22820 and #20509; supersedes #20542.

Visual Proof

There is no UI change; the proof is that the chat looks identical while the trace now records the failure. Live QA on a remote Mac (M4Air), rig Orca with an isolated profile and home, a structured Claude chat. Scenario: send one message, make the session store folder read-only, kill -TERM the Claude process, restore permissions.

Chat after the failure, this PR vs main (identical apart from the clock):

This PR: chat after the store failure and provider exit

main: chat after the store failure and provider exit

Trace after the fix (538b8cc), all with the chat's sessionId; main writes none of these:

Time step error
02:56:37 lease-renewal EACCES
02:56:53 claude-exit-cursor-persist EACCES
02:56:53 provider-exit-lease-release EACCES
02:56:57 lease-renewal agent_session_ownership_unknown

Nothing further in the next 203 s (before the dedupe, 33cd520 wrote this lease-renewal line every 10 s: 42 lines in 6.5 min).

This PR at 538b8cc: chat after the failure

Sending another message after permissions are restored fails the same way on this PR and on main ("Claude couldn't restart"); that is an existing problem, not changed here:

This PR: second message after restore

main: second message after restore

Testing

  • Typecheck (config/tsconfig.node.json, through the machine typecheck gate): clean on every pushed head.

  • Changed-lines quality gate (check-changed-code-quality.mjs) and oxlint on all changed files: clean.

  • Explicit vitest files per round (reporter, ratchet, agent-session wire/journal, runtime structured-*, orca-runtime.test.ts, Claude structured tests that changed, tests/e2e/codex-child-approval-activity-row.unit.test.ts): 2394 passed / 2 skipped at the round-2 head; renewer and reporter suites re-run after each later fix.

  • Privacy: a table-driven test runs every 'kind' step through a real trace file with an error that quotes a prompt-like sentinel and proves the sentinel never lands; the kind list is pinned as a literal list; each was checked to fail by flipping a step to 'full'.

  • Dedupe: tests for repeat, relapse after recovery, changed failure, leave-and-return, a batch probe that always fails, and per-session clearing; each fails when its rule is removed.

  • Live Electron QA on macOS (remote M4Air), as above. Not run live: Windows, Linux, SSH hosts (the trace lands in the execution host's own profile; nothing here is platform-specific), and the journal-write steps (the scenario exercised the record store, not the journal).

  • Not run: the real Claude CLI tests (claude-structured-real-cli*.test.ts), which write into the real ~/.claude.

  • CI on 4a2e408: PR Checks and Mobile Checks green, including static analysis, typecheck, all 5 unit-test shards, relay integration, cross-version wire compatibility and packaging (macOS, Windows).

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Review

Reviewed in rounds by independent reviewers (privacy, behavior preservation, performance/completeness, architecture), a readiness checklist, and live QA. Fixed during review: a CI unit test still calling the event-sink factory without its now-required callback; the privacy choice made mandatory per step (it was an opt-in list); the module renamed away from src/shared/agent-session-failure.ts (the user-facing failure vocabulary); the Claude structured-session and create-RPC console sites brought into scope and under the ratchet; two silently swallowed writes; a duplicate-report fix that turned out to leave Stop/close/quit with no record (reverted); weak privacy tests; the lease-renewal flood found in live QA; type assertions that kept CI's unit job from running. Deferred as nits: the idle-sweep double record, span naming consistency, ratchet evasions via destructuring (const { warn } = console), an optional sessionId on the Claude dispatch input, "unknown" reconciliation outcomes recorded as failures.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Security: no new network sinks; trace content passes the existing redactor, and the privacy table above keeps prompt-quoting errors out. Cross-platform: no platform-specific code. Remote SSH: structured sessions run on the execution host, which writes its own trace; nothing assumes local. Mobile: two RPC methods changed only their log line; the refusal they return is identical. Backwards compatibility: no persisted data, schema or wire change. Performance: nothing added on success paths; one record per failure, with the one timer-driven repeat deduped.

Checklist

  • This PR is small and focused (focused on one subsystem, but 126 files: the callback removal and the ratchet only close the bug class together)
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…e diagnostic trace

Structured chat session code reported background failures (exit recovery,
lease renewal, journal writes, late settlement, restart recovery) through an
optional onError/onEventSinkError sink chain that no installer supplied, and
through console.warn/error calls that reach nothing in a packaged build. Both
dropped every failure, which is why the "native Codex chat cannot resume after
update" settlement failure stayed invisible.

- Add reportAgentSessionFailure (src/main/observability/agent-session-failure.ts):
  a failed `agent-session.failure` span with a closed step vocabulary and the
  session id, written through the ambient tracer to the redacted, rotating
  main.trace.ndjson, plus a console line for dev runs. Never throws. Steps whose
  errors can quote the recovery capsule record the error's kind only.
- Remove the reporting-only sink hops (runtime onError, host onEventSinkError,
  onBarrierError, onDeliveryError, onNoteFailed, and the onError deps of the
  lease renewer, idle sweep, delivery loop, queued drain, lifecycle delivery and
  dead-generation settlement). Each catch site reports its own step.
- Keep behavioral callbacks required: the event sink's onFailure forces the
  session's provider down; dead-generation settlement returns its error so the
  host stop can throw it as the cause.
- Migrate the console failure sites in native-chat agent-session code and
  runtime structured-agent-session files, attaching the error where it was
  dropped.
- Ratchet test fails on console.warn/error or an optional on*Error? sink in
  that scope.

No user-visible behavior change.
…view

- Rename the reporter to observability/agent-session-error-trace (traceAgentSessionError) so it no
  longer shares a name with the user-facing failure vocabulary in shared/agent-session-failure.
- Derive the step union from one step -> 'full' | 'kind' table, so a new step must choose what its
  record may carry; test every 'kind' step through a real trace file.
- Keep error.code in full records; the report's own step and session win over detail keys.
- Record a stop-time dead-generation settlement failure once, where it is swallowed.
- Report the remaining console-only structured session failures (Claude resume point, exit cursor,
  dispatch, history window, option restore; chat create) and two swallowed durable writes; widen
  the ratchet to those files, optional on*Failure/on*Failed sinks and bracket console access.
- Pass the now-required onFailure in the e2e unit test that still omitted it.
… again

- Revert the round-1 move of the dead-generation settlement report: the settlement records its own
  failure, as the cancel, close, tab-close, /clear and quit paths swallow or strip the cause; drop
  the session-close report that only existed to recover it. Pin one record per host-stop failure.
- Error-trace tests: drive the kind-only cases from a pinned step list, and quote the sentinel
  where V8's JSON.parse message actually repeats it. The operation-settlement privacy check reads
  each console argument's text instead of JSON.stringify, which prints an Error as {}.
- Pin the queued-send-failure-hold and dispatch-doubt-record reports.
- Claude dispatch, attachment-read and option-restore records carry the Orca session id.
…ery tick

After one store EACCES at provider exit, lease renewal failed with ownership_unknown every 10 s
for as long as the app ran, and each failure wrote a trace span. The renewer now records a
session's lease-renewal or lease-probe failure once until that session renews or the failure
changes, keyed on the error's code where it has one, and forgets sessions that stop being renewable.
The trace tests narrow parsed trace lines with a guard instead of casting, and the lease-renewer
repeat tests run on a real record store with spies instead of a cast stub. The two pre-existing
runtime-state doubles this branch re-touched carry line-specific SAFETY rationales.
…be and per-session rules

The failure key read an error's name, code and message; a hostile getter would have rejected the
renewal tick, so it falls back to a constant. Tests pin one lease-probe record per session when the
batch probe keeps failing, and that a renewal clears only its own session's recorded failure.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready (head 4a2e408)

The problem. When something failed in the background of a structured native chat (recording that the agent process exited, renewing the session lease, writing the journal, saving a Claude resume point), Orca recorded nothing a person could find. Failures went either to an optional callback that the only production caller never supplied, or to console.warn, which a packaged Electron main process has nowhere to print. This is why the Sep 10 "native Codex chat cannot resume after update" P0 stayed invisible.

What changes for the user. Nothing visible in the chat. Each such failure now leaves one agent-session.failure record, with the failing step and the chat's session id, in the local diagnostic trace (logs/main.trace.ndjson), which the diagnostic bundle already collects.

Fixed during review:

  • A CI unit test still called the event-sink factory without its now-required callback.
  • The privacy choice (record the full error, or only its class and code) was an opt-in list, so a new step would default to full text. Now every step must declare it, and the step type is derived from that table.
  • The reporter module shared a name with src/shared/agent-session-failure.ts, the user-facing failure vocabulary, and 8 files import both. It is now agent-session-error-trace.ts.
  • Claude's own "exit cursor / resume point was not persisted" sites and the create RPC were still console-only. They now report, and the ratchet covers them.
  • Two writes that swallowed their failure with no trace now report. Behavior is unchanged.
  • A fix for one duplicate record left Stop, closing the chat and quit with no record at all. It was reverted.
  • Two privacy tests could never fail. Both are fixed and checked by flipping a step to full text.
  • Live QA found that, after one short store failure, lease renewal wrote a failure record every 10 s indefinitely (about 15 MB/day per stuck session). It now records a session's failure once until the session renews or the failure changes. That was verified live.
  • Type assertions failed CI's static analysis, which had kept the CI unit-test job from running on every earlier head.

Deferred (nits):

  • A settlement failure during an idle sweep is recorded twice per 5-minute sweep.
  • The span's name and kind attribute don't match the domain's existing agentSession.create span.
  • A few ratchet evasions are still possible, e.g. const { warn } = console.
  • The Claude dispatch input's sessionId is optional.
  • The designed "can't tell yet" outcomes of restart reconciliation are recorded as failures.
  • The orchestration worker session, worktree teardown and background git sites are still console-only. That is a follow-up, with no ticket filed yet.

Verified:

  • The typecheck is clean on every pushed head.
  • The changed-lines quality gate and oxlint are clean.
  • The targeted test files pass (2394 tests at the round-2 head; the renewer and reporter suites were re-run after each later fix).
  • CI on 4a2e408 is green, including all 5 unit-test shards.
  • Live Electron QA on a remote Mac, comparing a rig build of this PR with main:
    • The PR writes claude-exit-cursor-persist, provider-exit-lease-release and lease-renewal records with the right session id. Main writes none.
    • The chat looks identical on both builds.
    • After the fix, the stuck lease produced one record in 207 s instead of one every 10 s.

Not verified:

  • The real Claude CLI tests, which write into the real ~/.claude.
  • Live runs on Windows, Linux or SSH hosts.
  • The journal-write steps live: the QA scenario broke the record store, not the journal.
  • Live QA ran at 538b8cc; the last head, 4a2e408, only adds renewer tests and makes the failure key unable to throw.

Found in QA, not caused by this PR: after a short store failure at provider exit, the next message fails with "Claude couldn't restart", even after permissions are restored. Main behaves identically. The trace this PR adds now shows the likely cause: the lease stays in agent_session_ownership_unknown with nothing re-checking it. This needs its own fix.

@brennanb2025
brennanb2025 marked this pull request as ready for review September 30, 2026 10:13
@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Structured chat error handling now routes to diagnostic trace.

The PR appears safe to merge, with a non-blocking restart-recovery diagnostic correlation gap.

Findings

  1. P2 Restart failures lack identifiers ▶

Summary

The PR routes structured native-chat background failures into the diagnostic trace, replaces optional reporting callbacks, and adds privacy and regression tests. One restart-recovery reporting path still omits identifiers needed to correlate its failures with an action or chat.

Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Structured chat background operation] --> B{Failure}
  B --> C[Step-specific reporter]
  C --> D[Redacted diagnostic trace]
  C --> E[Console fallback]
  B --> F[Existing refusal or recovery behavior]
Loading

Reviews (1) · Last reviewed commit: "fix(native-chat): a lease failure key ne..."

.catch(() => {
console.warn('[structured-agent-session] restart offer completion failed')
})
.catch((error: unknown) => traceAgentSessionError({ step: 'restart-offer-complete', error }))

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 Restart failures lack identifiers When a restart action spans multiple chats, a failure to complete an offer or record a failed resume is traced without the operation ID or affected session IDs, even though both are available here. The same applies to the rollback report below. This does not block recovery, but it makes the diagnostic harder to connect to the action or chats that need attention.

Knowledge Base Used: Agent workspaces and sessions

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@pullfrog pullfrog Bot left a comment

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.

✅ No new issues found.

Reviewed changes — reviewed the full 126-file diff at 4a2e408, focused on the new reporter, the callback removals, and the lease-renewer dedupe.

  • New failure reporter — traceAgentSessionError in src/main/observability/agent-session-error-trace.ts writes one failed agent-session.failure span plus a console.warn, never throws, and gates per step between 'full' and 'kind'.
  • Step table — AGENT_SESSION_ERROR_STEPS (70 entries, 15 'kind') is closed, so a new catch site cannot compile without choosing what it may record.
  • Callback removal — optional on*Error?/on*Failure?/on*Failed? sinks are gone from host/journal/lease/idle/delivery/queued/runtime; every catch reports its own step. Behavioral callbacks (onFailure force-close, onOpened) are retained and now required.
  • Dead-generation settlement — returns { ok: true } | { ok: false; error }; the host-lifetime caller rethrows with cause: settled.error.
  • Lease-renewal dedupe — a stuck lease reports once per session until it renews, leaves the renewable set, or the failure key changes; the key uses the error code, not its per-attempt temp path.
  • Ratchet + privacy tests — a repo scan fails on new console.warn/console.error or optional error sinks in the structured-session trees; a table-driven test proves every 'kind' step drops a prompt-quoting SyntaxError.

Two claims were checked against the source and hold: no 'full' step's error can carry prompt/draft/journal-row/transcript text (the one content-quoting JSON.parse, the recovery capsule, sits behind the 'kind' steps), and no removed callback carried a non-reporting side effect (the provider force-close on sink failure is preserved).

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 0c63a634-87d3-485d-8726-6e23db22e32b

📥 Commits

Reviewing files that changed from the base of the PR and between cef66fb and 4a2e408.

📒 Files selected for processing (126)
  • src/main/claude/claude-api-retry-row.test.ts
  • src/main/claude/claude-context-usage-restart.test.ts
  • src/main/claude/claude-context-usage-unloaded-turn.test.ts
  • src/main/claude/claude-stream-json-connection.test.ts
  • src/main/claude/claude-structured-command-dispatch.ts
  • src/main/claude/claude-structured-dispatch-attachment-rejection.test.ts
  • src/main/claude/claude-structured-dispatch-content.ts
  • src/main/claude/claude-structured-dispatch.test.ts
  • src/main/claude/claude-structured-dispatch.ts
  • src/main/claude/claude-structured-effort-reporting.test.ts
  • src/main/claude/claude-structured-history-window.ts
  • src/main/claude/claude-structured-journal-translation-background-tasks.test.ts
  • src/main/claude/claude-structured-journal-translation.test.ts
  • src/main/claude/claude-structured-model-preflight.test.ts
  • src/main/claude/claude-structured-options-context-window.test.ts
  • src/main/claude/claude-structured-options.test.ts
  • src/main/claude/claude-structured-options.ts
  • src/main/claude/claude-structured-provider-fallback.test.ts
  • src/main/claude/claude-structured-resume-point.test.ts
  • src/main/claude/claude-structured-resume-point.ts
  • src/main/claude/claude-structured-session-acquisition.ts
  • src/main/claude/claude-structured-session-adapter.ts
  • src/main/claude/claude-structured-session-exit-lifecycle.ts
  • src/main/claude/claude-structured-session-reading-control.test.ts
  • src/main/claude/claude-structured-session-startup.ts
  • src/main/claude/claude-structured-turn-resume-point.test.ts
  • src/main/claude/claude-subagent-resume-after-restart.test.ts
  • src/main/claude/claude-subagent-roster.test.ts
  • src/main/codex/codex-structured-journal-goal-resume.test.ts
  • src/main/codex/codex-structured-journal-goal-revision.test.ts
  • src/main/codex/codex-structured-journal-subagent-readers.test.ts
  • src/main/codex/codex-structured-journal-translation-settlement.test.ts
  • src/main/codex/codex-structured-journal-translation-turn-lifecycle.test.ts
  • src/main/codex/codex-structured-journal-translation.test.ts
  • src/main/codex/codex-structured-journal-turn-settles-once-journal.test.ts
  • src/main/native-chat/agent-session-journal/journal-database.ts
  • src/main/native-chat/agent-session-journal/journal-open-failure.ts
  • src/main/native-chat/agent-session-journal/journal-per-session-import.test.ts
  • src/main/native-chat/agent-session-journal/journal-per-session-import.ts
  • src/main/native-chat/agent-session-journal/journal-per-session-source.ts
  • src/main/native-chat/agent-session-journal/journal-queued-messages.ts
  • src/main/native-chat/agent-session-journal/journal-restart-reconciliation.ts
  • src/main/native-chat/agent-session-journal/journal-row-writer.ts
  • src/main/native-chat/agent-session-journal/queued-message-bookkeeping-failure.test.ts
  • src/main/native-chat/agent-session-journal/queued-message-store.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-agent-start.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-flow.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-claude-root-exit.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-conversation-close.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-conversation-lifetime.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-conversation-open.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-conversations.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-conversations.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-crash-turn-end.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-dead-generation-settlement.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-dead-generation-settlement.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-delivery-loop.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-event-recovery.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-event-sink-queue.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-event-sink.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-event-sink.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-eviction.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-eviction.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-delivery.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-lifetime.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-runtime-state.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-runtime-state.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-tabs.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-teardown.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-teardown.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-types.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-idle-sweep.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-idle-sweep.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-late-settlement.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-lease-renewer.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-lease-renewer.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-operation-settlement.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-operation-settlement.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-operation-start-refusal.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-pre-spawn-first-answer.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-provider-started.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-messages.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-messages.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-mutations.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-pause.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-stop.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-wiring.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-recovered-turn-clock.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-resolved-append.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-cap.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-continuation.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-continuation.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-failure-ledger.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-offer-records.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-offer-withdrawal.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-ownership.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-resume-host.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-resume-wiring.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-teardown.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restore-without-import.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-send-preparation.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-send-preparation.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-status-feed.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-status-feed.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-subagent-recency.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-surface-lifetime.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-turns.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-unexpected-exit.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-unexpected-exit.ts
  • src/main/native-chat/agent-session-wire/structured-rewind-recovery.ts
  • src/main/observability/agent-session-error-trace-ratchet.test.ts
  • src/main/observability/agent-session-error-trace.test.ts
  • src/main/observability/agent-session-error-trace.ts
  • src/main/runtime/rpc/methods/structured-agent-session-at-rest.test.ts
  • src/main/runtime/rpc/methods/structured-agent-session-create.ts
  • src/main/runtime/rpc/methods/structured-agent-session-precommit-refusal.ts
  • src/main/runtime/structured-agent-session-close.ts
  • src/main/runtime/structured-agent-session-host-refusal.test.ts
  • src/main/runtime/structured-agent-session-journal-open.ts
  • src/main/runtime/structured-agent-session-lifecycle-delivery.test.ts
  • src/main/runtime/structured-agent-session-lifecycle-delivery.ts
  • src/main/runtime/structured-agent-session-runtime.ts
  • src/main/runtime/structured-agent-session-tab-retirement.ts
  • src/main/runtime/structured-claude-pending-rewind.test.ts
  • tests/e2e/codex-child-approval-activity-row.unit.test.ts
💤 Files with no reviewable changes (4)
  • src/main/native-chat/agent-session-wire/structured-agent-session-restore-without-import.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-types.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-conversation-lifetime.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-resume-wiring.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The pull request adds traceAgentSessionError and routes many Claude, journal, session-wire, and runtime failures through it with step and session details. It changes the deferred event-sink failure callback from optional onError to required onFailure. Dead-generation settlement now returns a discriminated result with an error on failure. Tests update their callback fixtures and verify the new reporting and settlement behavior.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 4a2e4

The change improves structured-chat diagnostics without an established regression in chat behavior or recovery. It appears mergeable after normal checks; platform-specific live validation remains limited.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 4a2e4

The change improves failure diagnosis, but some newly reported errors also reach process output without redaction. Protected diagnostic files and the opt-in feature limit exposure. Who can access or collect process output remains uncertain.

Retained concerns

  • Medium · security · inferred: Full-detail failures now have an unconditional console output path outside trace redaction. If newly reported failures contain confidential text and process output is retained or collected, that data can reach readers without the diagnostic-file controls. Actual sensitive content and collector exposure remain unverified; this is not a verified disclosure finding.
Security review details

Security Blast Radius

  • inferred — The demonstrated new data surface is session-associated failure information reaching diagnostic storage and process output under the host process's identity. The concern does not establish cross-tenant access, privilege gain, or automatic remote export; broader console-collector exposure is unknown.

Security Findings and Attack Paths

  • inferred — A conditional disclosure path exists from confidential content in a full-detail failure to the original Error passed to console.warn, then to anyone able to read collected process output. The canonical Security section contains no retained findings, and the reviewed evidence does not demonstrate confidential payloads or an attacker-accessible collector.

Trust Boundaries and Controls

  • observed — The normal trace path redacts records before persistence, uses private directory and file modes, and rotates bounded files. Bundle collection applies server-mode redaction, a default 30-minute lookback, and a 4 MiB ceiling; upload validates protocol and destination host.
  • observed — Disabling diagnostics leaves the trace sink unset, but the reporter still attempts console output. That branch therefore does not inherit trace sanitization. The source describes console output as absent in packaged desktop main processes and available in development or headless runs.

Resilience and Maintainability Implications

  • observed — Sink recovery deduplicates concurrent recovery per session, serializes provider shutdown, catches asynchronous recovery rejection, and clears the recovery marker afterward. Queue callback-before-cleanup ordering predates this PR; the evidence does not establish a newly introduced cleanup failure.

Hardening Proposals

  • proposed — Apply equivalent secret redaction to console output while preserving kind-only reporting, and define access and retention expectations wherever headless process output is collected.
🚥 Pre-merge checks | ✅ 4 | ❓ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ❓ Inconclusive Docstring coverage is 54.05% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 50 files. (72 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: structured native-chat failures now reach the diagnostic trace.
Description check ✅ Passed The description is complete and covers the required explanation, changes, rationale, scope, linked-issue status, visual proof, testing, review, compatibility notes, and checklist. The unchecked checkl…
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 54.05% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 50 files. (72 skipped: 72 over the file limit.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

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.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Superseded by #24312, which landed the same fix with a required logger dependency (plus repeat suppression and the headless host's trace). The parts of this PR that #24312 lists as follow-ups move to a small PR on top of that logger: the remaining console-only structured-chat failure sites (chat journal, Claude structured session, runtime), a ratchet that keeps console-only failure logging and optional error callbacks out of that code, and session ids on the restart-offer failure logs (Greptile's P2 here).

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.

1 participant