Skip to content

Harden ADE against disk-full and interrupted-write incidents#797

Merged
arul28 merged 29 commits into
mainfrom
ade/start-skill-work-remebr-guidance-edba0f69
Jul 13, 2026
Merged

Harden ADE against disk-full and interrupted-write incidents#797
arul28 merged 29 commits into
mainfrom
ade/start-skill-work-remebr-guidance-edba0f69

Conversation

@arul28

@arul28 arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Incident response + product hardening for the July 12 disk-full outage, where a full disk during a kvDb table rebuild bricked every subsequent launch and two chats silently lost their Codex threads. This PR makes a full disk an ordinary operating condition: canonical data is never silently lost, ADE recovers automatically when safe, and a nontechnical user gets a guided, jargon-free recovery path.

Durability

  • Table rebuilds are transactional (BEGIN IMMEDIATE, row-count-verified, index-preserving); a startup recovery pass classifies staging tables stranded by older builds (drop empty/partial, complete interrupted renames, never drop ambiguous data) and runs before migrate(). Also stops the previously unprotected rebuild of automation_ingress_events that ran on every startup.
  • Chat metadata writes are atomic with one .lkg previous generation; provider-pointer changes are journaled to a bounded (64 KiB) ledger; truncated transcript tails are healed instead of corrupting the next append; pre-migration DB backups are headroom-checked, size-verified, and singular.

Continuity

  • A provider resume failure can no longer silently replace a chat's thread: failures are classified (missing thread vs optional-MCP vs transient, with a rollout-file existence probe), replacing a pointer requires an explicit token, and the chat shows a recovery card — Retry original thread / Recover from ADE history (bounded ≤24 KiB capsule, marked reconstructed) / Start a new chat (linked). Startup reconciliation restores lost pointers from the ledger, SQLite resume commands, or transcript events.

Recovery UX

  • Typed recovery errors (AdeRecoveryErrorCode) survive IPC; the brain writes bounded machine/project last-failure reports, backs off on crash loops, dedupes repeated failure logs, and copytruncates launchd logs at 10 MiB.
  • Project-open failures mount a full-screen plain-language recovery surface with one-click Repair (space check → stop service → validate DB → finish interrupted saves → restart → verify RPC → chat reconciliation report). Repair never deletes a file. The primary-socket safety guard is unchanged — only its user-facing mapping improved.

Storage

  • ADE-scoped disk-pressure monitor (normal/warning/critical/exhausted, rise-fast fall-slow, fail-open): quiet top-right indicator; at exhausted, new chat turns / ADE-launched CLI sessions / managed process starts are refused with calm copy; running work is never touched.
  • Settings → Storage dashboard: disk gauge, plain categories with safety badges, per-lane itemization of archived and deleted-lane leftovers, preview-confirmed cleanup with server-side protection of canonical data; scanner is symlink-safe and covers os.tmpdir ade-* staging.
  • Old inactive chat/terminal history gzip-compresses with verify-before-delete, transparent reads (search included), and reinflate-on-append.
  • CLI parity: ade storage snapshot/compress, last-failure line in ade brain status, storage line in ade doctor.

Tests & docs

  • Shared fault-injection utility + an end-to-end incident replay (SQLITE_FULL mid-rebuild, ENOSPC mid-write, restart self-heal, pointer reconciliation, repair flow, symlink containment), per-statement migration fault injection, real SQLITE_FULL via PRAGMA max_page_count.
  • New docs/features/storage-and-recovery/README.md (contracts + all retention bounds) plus updates to chat, terminals, search, settings, remote-runtime docs and ARCHITECTURE.

Validation

  • Desktop: 8/8 vitest shards green under Node 22 except one pre-existing failure (syncHostService.test.ts "streams terminal snapshots…" — reproduces identically at the branch base f9344115 on this machine; unrelated to this diff).
  • ade-cli: 84 files / 1,725 tests green. Typecheck, lint, and builds green for both apps.
  • Note: remoteRuntime.offlineRpc.integration.test.ts is a dormant pre-existing test that no vitest project ran before this branch; the new integration-main project is deliberately scoped so it stays dormant (see comment in vitest.workspace.ts).

🤖 Generated with Claude Code

ADE   Open in ADE  ·  ade/start-skill-work-remebr-guidance-edba0f69 branch  ·  PR #797

Summary by CodeRabbit

  • New Features
    • Added a Storage settings experience with disk-pressure indicator, storage snapshots, cleanup previews, safe removals, and history compression.
    • Added ade storage / ade disk CLI commands for snapshot, compress, and maintenance actions (including text output).
    • Added Project Recovery takeover flow (diagnose/repair) and chat continuity recovery UI.
  • Improvements
    • Transparently read and index gzip-compressed chat/terminal transcripts.
    • Expanded disk-pressure protections across readiness checks and runtime operations.
  • Tests
    • Added unit/integration coverage for storage, recovery, compression, disk-pressure behavior, and failure deduplication/backoff.

Greptile Summary

This PR hardens ADE storage, recovery, and continuity paths against disk-full and interrupted-write failures. The main changes are:

  • Transactional database rebuild recovery and safer startup handling.
  • Atomic chat metadata writes and thread-pointer reconciliation.
  • Guided project recovery UI and typed recovery errors.
  • Disk-pressure monitoring, storage dashboard, safe cleanup, and history compression.
  • CLI storage commands plus updated tests and documentation.

Confidence Score: 5/5

This looks safe to merge.

No blocking issues found in the changed code.

T-Rex T-Rex Logs

What T-Rex did

  • The initial disk-full-incident integration test run completed with Vitest reporting 5 tests passed, but the wrapper exited with code 2 due to a /bin/sh: 1: Bad substitution.
  • A clean retry was performed with the exact command, working directory, and timestamps, and the Vitest run completed with exit code 0.
  • The clean run summary reported Test Files 1 passed (1), Tests 5 passed (5), duration 5.69s.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
apps/desktop/src/main/services/storage/storageInsightsService.ts Cleanup confirmations now carry preview identities and recheck them before removal.
apps/desktop/src/main/main.ts Desktop runtime wiring now includes storage services and documents the daemon-backed fallback behavior.
apps/ade-cli/src/bootstrap.ts The daemon runtime now wires disk pressure and storage insights for headless projects.

Reviews (8): Last reviewed commit: "Compress at warning level and decompress..." | Re-trigger Greptile

arul28 and others added 16 commits July 12, 2026 12:13
A disk-full during the legacy schema retrofit's create/copy/drop/rename
of automation_ingress_events stranded its staging table and bricked
every later launch ("table already exists"). Root causes fixed:

- Shared rebuildTableInTransaction helper (BEGIN IMMEDIATE, row-count
  validation, index recreation, rollback on failure) now backs both
  schema retrofits and the runtime_processes rebuild.
- retrofitLegacyPrimaryKeyNotNullSchema skips CRR-excluded tables: they
  never become CRRs and keep their unique indexes, which also stops the
  unprotected full rebuild it performed on every startup.
- recoverInterruptedTableRebuilds runs before migrate() and classifies
  stranded __ade_crr_repair_/__ade_fk_repair_ staging tables (drop empty
  or partial staging, complete interrupted renames, never drop ambiguous
  nonempty staging; ambiguous tables are skipped by the retrofits so
  startup stays alive).
- writeMigrationBackupIfNeeded checks free-space headroom, copies to a
  temp file, verifies size, and renames — no partial backups.
- classifySqliteOpenError exposes typed codes (disk_full, db_integrity,
  insufficient_headroom, migration_*) for the recovery flow.

Covers the exact incident state in tests, plus a real SQLITE_FULL via
PRAGMA max_page_count, per-statement fault injection, and interrupted
drop-then-rename on a migrate()-owned table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
persistChatState wrote the only copy of every provider thread pointer
with an in-place writeFileSync; a torn write during the disk-full
incident destroyed chat continuity. Changes:

- New durableFile module: writeFileAtomic (same-dir temp + rename +
  optional fsync), writeJsonWithPrevious (exactly one .lkg previous
  generation), readJsonWithRecovery, and safe abandoned-temp cleanup.
- Chat metadata now writes atomically with shape validation; reads fall
  back to the .lkg copy and log the recovery. Session-creation transcript
  headers and Linear context files write atomically too.
- Bounded per-project continuity ledger (thread-pointers.jsonl, 64 KiB
  newest-wins compaction) records every provider pointer change with the
  previous value; fingerprints are seeded on session load so restarts do
  not spam entries. Pointer changes fsync metadata and flush transcripts.
- Transcript appends heal a truncated tail line (missing trailing
  newline) before the first append per path, so an interrupted write can
  no longer corrupt the next event; user messages flush immediately
  instead of waiting out the 100ms debounce.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bounding

During the disk-full incident the brain crash-looped under launchd with
no backoff, unbounded logs, and no record Desktop could read; project
open surfaced a raw internal socket error with only a Dismiss button.

- Shared AdeRecoveryErrorCode + AdeLastFailureReport types; kvDb open
  errors map onto them.
- lastFailureStore writes an atomic, bounded (current + one previous)
  failure report to the machine and project .ade/runtime dirs; the brain
  records classified startup/db failures, applies a short backoff after
  repeated identical failures, and clears the report on healthy startup.
- launchd err/out logs are copytruncate-rotated at 10 MiB and repeated
  identical startup-loop failures are deduped to one line plus a
  periodic count, so a crash loop can no longer grow logs unboundedly.
- The local-runtime pool attaches typed codes (disk_full,
  brain_crash_looping, socket_stale_no_owner, socket_owned_by_other,
  brain_not_installed) to its refusals — guard conditions unchanged —
  and project-open IPC surfaces them; projectTransitionError is now
  { message, code?, detail? } with a shared coded-error parser and a
  plain-language hint plus technical-details fold in the alert.

Sync-host/socket-publication decoupling was evaluated and deferred: DB
failures already retry without exiting, but the socket publishes after
project scope init; restructuring that ownership is follow-up work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ity recovery

After the disk-full crash, Codex resume failures silently called
startFreshCodexThread inside existing chats, overwriting the thread
pointer while the original rollouts were still valid on disk.

- providerResumeClassifier classifies resume failures (thread_missing /
  provider_environment / transient / unknown) with a bounded rollout-
  file existence probe; optional-MCP failures are provider-health
  notices, not continuity loss.
- All three silent fallbacks removed: resume failures fail the turn
  with a classified error, keep the pointer, and thread_missing/unknown
  mark the chat continuityRecovery=required and emit a plain-language
  system_notice (detail.kind "continuity_recovery"). New turns fail
  fast while recovery is required.
- startFreshCodexThread now requires an explicit replace token when a
  pointer exists (provider_thread_replace_blocked otherwise), making
  silent replacement structurally impossible.
- agentChat.recoverContinuity action (IPC + brain RPC allowlist):
  retry_original, recover_from_history (bounded ≤24KiB capsule from the
  durable transcript, thread marked reconstructed and linked to the
  original), start_new_chat (separate linked session; old chat kept).
- Startup reconciliation restores a missing/corrupt pointer from the
  continuity ledger, SQLite resume_command, or transcript thread-id
  events, verifying rollout existence for Codex; Claude stale-session
  handling preserves the pointer in continuityRecovery instead of
  nulling it. Also carries the typed project-open error wrapper in
  registerIpc from the recovery-model change (shared file).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Desktop surface for the continuity-recovery contract: a warm amber
in-transcript card when a chat's original provider thread could not be
resumed, offering Retry original thread, Recover from ADE history (with
an inline confirm and a post-recovery capsule disclosure), and Start a
new chat (deep-links to the new session). Reconstructed threads render
a calm "Rebuilt from ADE history" pill; superseded chats link to their
continuation. Plain-language copy throughout; identifiers live behind a
Details fold. Optional-MCP/transient failures keep their provider-health
treatment and never render as continuity loss.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
projectRecoveryService diagnoses a failed project open without needing
the brain (free space, last-failure reports, socket liveness, service
state, read-only database quick_check) and maps it to typed,
plain-language recovery states. repair() mirrors the incident's manual
recovery as ordered, reported steps: check space -> stop the service
safely -> validate the database -> finish interrupted saves (the
self-healing kvDb open) -> restart -> verify the endpoint and a real
project RPC -> count chats needing continuity attention. It never
deletes any file. ade.recovery.diagnose/repair IPC exposes it; the
project-transition alert gains a Repair ADE action that retries the
open on success.

The shared wiring files in this commit (ipc.ts, registerIpc.ts,
preload.ts, global.d.ts, main.ts) also carry the storage-pressure IPC
additions from the parallel change landing next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
diskPressure monitor (desktop main + brain) samples free space on the
project and ADE-home volumes: normal / warning / critical / exhausted
with rise-fast fall-slow hysteresis, subscriber-only sampling, and
fail-open on statfs errors. Enforcement protects data without killing
work: at exhausted, new structured chat turns fail fast with a
plain-language notice, new ADE-launched CLI sessions are refused, and
new managed process starts are refused; running turns, PTYs, and
processes are never touched, and warning level never blocks anything.
The TopBar gains a storage indicator (sibling of the CPU pressure pill,
30s poll, amber warning / red critical) that deep-links to Settings
storage. No reserve files, no cleanup of non-ADE data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…leanup

storageInsightsService walks ADE-owned roots (lstat only, never following
symlinks, clamped to allowed bases, capped with an explicit truncated
flag) into plain-language categories: chats & terminal history, lanes &
worktrees, build & release staging (including os.tmpdir ade-* dirs and
simulator DerivedData), caches, proof & attachments, recovery backups,
and the project database. Worktree entries join live lane rows so
archived lanes and leftovers from deleted lanes are itemized with
per-lane sizes. Cleanup is preview -> confirm with kind-scoped path
validation; protected data (active worktrees, chat session records,
database, terminal history, secrets) is refused server-side even when
requested explicitly. Registered as the storage action domain on both
desktop and the brain, extending window.ade.storage alongside
getPressure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Typed project-open failures now mount ProjectRecoveryScreen in the
content area instead of a dismissible banner: plain-language diagnosis
(via ade.recovery.diagnose with a code-keyed fallback), a one-click
Repair ADE flow that renders the ordered repair steps and the
post-repair report (database health, chats resumed, chats needing
attention, no files removed), Review storage, Back, and a Show
technical details fold that is the only place raw internals appear.
Successful repairs automatically retry the project open through the
normal transition veil. The old banner remains only for un-coded string
errors; its now-unreachable repair machinery is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Settings gains a Storage section built on the storage insights domain:
a disk gauge with ADE usage vs free space (pressure-tinted), a stacked
category breakdown, and plain-language category cards with safety
badges (Safe to remove / Can be compressed / Review first / Protected).
The lanes card itemizes active, archived, and deleted-lane leftovers
with per-lane sizes and per-row removal; every destructive action runs
preview -> confirm -> itemized result with blocked items explained
calmly, and protected data never gets a destructive control. Archived-
lane removals resolve the real lane id from the lanes list, and cleanup
kinds are inferred from paths with the backend as final gatekeeper. The
compress action lights up when the compression API is present.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ation

historyCompression gzips inactive transcripts (30+ days, never a file
with pending appends): stream-compress to a .partial, fsync, verify by
streamed gunzip comparing length and content hash plus a source
unchanged check, rename to .gz, re-check the session has not resumed,
and only then remove the original — any failure keeps the original and
removes the partial. Chat history reads, pagination, PTY scrollback,
and universal search indexing all read .gz transparently (plain file
wins in the crash window), and a resumed session reinflates atomically
before appending. Sweeps run idle-scheduled (10 min after startup, then
daily, capped per run) on desktop and brain, and skip entirely under
disk pressure; storage.compressNow exposes the on-demand action and
snapshots report compressedBytes. Recovery backups flip to
safe-to-remove only after seven days with a healthy database check and
no fresh open failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The compression API landed after the dashboard was written; drop the
structural cast now that preload typing exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…racts

- Shared faultInjection test utility: Nth-call fs fault injection with an
  ENOSPC default and the canonical PRAGMA max_page_count SQLITE_FULL
  recipe; existing ad-hoc injections in the kvDb, durableFile, and chat
  persistence suites now use it.
- diskFullIncident.integration.test.ts replays the July 2026 incident end
  to end: disk fills mid-rebuild and mid-metadata-write, nothing is
  damaged, the next open self-heals, a lost pointer reconciles from
  redundant state, crash-loop accounting and log bounding behave, the
  repair flow recovers a stranded staging database, pressure enforcement
  gates without jargon, and a symlink farm cannot leak scanning or
  cleanup outside ADE roots. The vitest workspace gains an
  integration-main project since unit projects exclude *.integration.
- New docs/features/storage-and-recovery/README.md (source map, the
  staging-classification table, continuity redundancy, typed failure
  flow, pressure states, storage categories/safety, compression rules,
  and every backup/retention bound in one table) plus updates to the
  chat and remote-runtime feature docs, docs/README.md, and
  ARCHITECTURE.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified findings from the dual-track review, all behavior-preserving
except three specified hardenings:

- The recovery screen mounts only when a typed error carries a project
  rootPath (repair-less takeovers fall back to the banner); the remote
  binding-switch failure now threads its rootPath.
- recoverContinuity gains a per-session in-flight guard; the storage
  indicator poll catches rejections; durableFile's directory fsync
  follows the caller's fsync intent.
- Single sources of truth: the recovery-code union derives from one
  const array with a shared narrower; volume.ts owns free-space reads
  and no-space error detection for the new call sites; the compression
  age and repair/advice disk floors are derived so they cannot
  contradict; shared codedError owns the create/encode/parse wire
  contract; DiskPressure DTO types moved to shared/types; formatBytes
  canonicalized in renderer lib; the thread-pointer ledger extracted to
  its own module; kvDb exports its quick_check and readonly-open
  helpers; the jargon-guard test util owns the banned-term list (bare
  SQL now subsumes SQLite).

Review outcome: no Blocker or High findings; data-safety paths
(transactional rebuilds, verified compression, identity-gated cleanup,
pressure fail-open) independently confirmed sound.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ade storage snapshot/compress typed subcommands (cleanup stays behind
  the explicit actions escape hatch because it is destructive and
  preview-gated); ade brain status prints the machine last-failure line
  when a fresh report exists; ade doctor reports disk headroom with a
  next action at warning/critical.
- Internal docs verified against the final code and completed:
  storage-and-recovery source map reflects the extracted modules
  (threadPointerLedger, volume, shared codedError, shared storage
  types); terminals-and-sessions covers .gz transcript reads, reinflate
  on append, and the pressure gates; search documents transparent
  compressed-history indexing; onboarding-and-settings adds the Storage
  tab; ARCHITECTURE lists the ade.storage.*/ade.recovery.* channel
  families and new runtime services.

Known follow-up (pre-existing, unrelated to this branch): headless
one-shot `ade actions list` shows the reduced chat facade rather than
the brain's full chat action surface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The broad src/main/**/*.integration.test glob resurrected the dormant
remoteRuntime.offlineRpc.integration.test.ts, which predates this
branch, was excluded from every vitest project, and no longer passes.
Scope the project to services/__tests__ and leave the dormant file
exactly as it was before this branch; fixing or deleting it is a
separate decision noted in the config comment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview Jul 13, 2026 12:41am

@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@copilot review but do not make fixes

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR adds disk-pressure monitoring, storage inspection and cleanup, durable project recovery, compressed transcript support, chat continuity recovery, and corresponding CLI, IPC, preload, runtime, and desktop UI integrations. It also adds unit and integration coverage for persistence, storage safety, disk-full recovery, and recovery workflows.

Changes

Storage, recovery, and continuity platform

Layer / File(s) Summary
Shared contracts and durable persistence
apps/desktop/src/shared/types/*, apps/desktop/src/shared/codedError.ts, apps/desktop/src/main/services/state/*
Adds typed storage/recovery models, coded error transport, atomic JSON persistence, SQLite rebuild recovery, and failure-report storage.
Disk pressure and storage services
apps/desktop/src/main/services/storage/*
Adds disk-pressure classification, operation gating, history compression, storage snapshots, cleanup previews, and validated cleanup execution.
Chat durability and continuity recovery
apps/desktop/src/main/services/chat/*, apps/desktop/src/main/services/search/*, apps/desktop/src/main/services/sessions/*
Adds gzip transcript handling, durable pointer ledgers, transcript healing, provider resume classification, and explicit continuity recovery flows.
Project and runtime recovery
apps/desktop/src/main/services/runtime/*, apps/desktop/src/main/services/localRuntime/*
Adds diagnosis and repair workflows, startup backoff, log rotation, socket ownership classification, failure deduplication, and structured failure reporting.
Runtime, actions, IPC, preload, and CLI
apps/desktop/src/main/main.ts, apps/ade-cli/src/*, apps/desktop/src/main/services/adeActions/*, apps/desktop/src/main/services/ipc/*, apps/desktop/src/preload/*
Threads new services through runtime construction and exposes storage, recovery, and continuity operations through actions, IPC, preload APIs, and CLI commands.
Desktop storage and recovery UI
apps/desktop/src/renderer/components/app/*, apps/desktop/src/renderer/components/settings/*, apps/desktop/src/renderer/components/chat/*
Adds project recovery screens, storage settings and cleanup dialogs, disk-pressure indicators, and continuity recovery cards.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • arul28/ADE#80: Both changes touch agent chat transcript generation and related text-event handling.
  • arul28/ADE#792: Both changes modify project-open error formatting and project-transition error UI behavior.

Suggested labels: desktop, docs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main hardening work around disk-full and interrupted-write recovery across ADE.
✨ 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 ade/start-skill-work-remebr-guidance-edba0f69

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.

Semantic resolutions: kept both the pointer-reconciliation block and
main's new seedForkedProviderPointer, converting the latter's raw
metadata write to writeJsonWithPrevious (this branch's atomic-write
contract); combined the disk-pressure send gate with main's
preparedMessage option in the send path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread apps/desktop/src/main/main.ts
Comment thread apps/desktop/src/main/services/storage/storageInsightsService.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/main/services/processes/processService.ts (1)

875-994: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Disk-pressure gate only covers start(); other process-launching entry points bypass it.

Updates start() to gate process startup on diskPressureMonitor?.canPerform("process_start"); when disallowed, it throws an error augmented with a recovery code (typed as AdeRecoveryErrorCode) and the monitor’s message. However restart() (889-895), startStack()/restartStack() (901-921, via runStartSet), startGroup()/restartGroup() (923-949, via runStartGroupParallel), and startAll() (951-954, via runStartSet) all call startById/startByDefinition directly and never consult diskPressureMonitor. Given the entire point of this hardening effort is to stop ADE from spawning new writer processes while the disk is critically full, any of these alternate paths (e.g. a "restart stack" or "start all" action, which is exactly what would run on lane/project open) can still launch processes during disk exhaustion.

Recommend moving the check into the single choke point startByDefinition (or startById) so every caller is protected, rather than duplicating it per public method.

🛡️ Proposed fix: enforce at the shared choke point
+  const assertDiskPressureAllows = (kind: "process_start"): void => {
+    const decision = diskPressureMonitor?.canPerform(kind);
+    if (decision && !decision.allowed) {
+      throw Object.assign(new Error(decision.message), {
+        code: decision.code satisfies AdeRecoveryErrorCode,
+      });
+    }
+  };
+
   const startByDefinition = async (
     laneId: string,
     definition: ProcessDefinition,
     opts: { skipTrust?: boolean; overlay?: LaneOverlayOverrides } = {},
   ): Promise<ProcessRuntime> => {
+    assertDiskPressureAllows("process_start");
     if (!opts.skipTrust) projectConfigService.getExecutableConfig();
     async start(arg: ProcessActionArgs): Promise<ProcessRuntime> {
-      const decision = diskPressureMonitor?.canPerform("process_start");
-      if (decision && !decision.allowed) {
-        throw Object.assign(new Error(decision.message), {
-          code: decision.code satisfies AdeRecoveryErrorCode,
-        });
-      }
       return await startById(arg.laneId, arg.processId);
     },
🤖 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 `@apps/desktop/src/main/services/processes/processService.ts` around lines 875
- 994, Move the disk-pressure validation from the public start method into the
shared process-launch choke point, preferably startByDefinition (or startById if
that is the common path), so every startup route is gated. Preserve the existing
disallowed error behavior, including the monitor message and
AdeRecoveryErrorCode recovery code, and remove the redundant check from start
once the shared guard applies to start, restart, stack, group, and all-start
flows.
🧹 Nitpick comments (9)
apps/desktop/src/test/jargonGuard.ts (1)

5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add word boundaries to avoid false positives on unrelated words.

WAL without \b boundaries will match inside legitimate words like "firewall", "wallet", or "walnut" that could plausibly appear in future desktop copy (e.g. networking/security text), causing spurious test failures unrelated to actual jargon leakage.

🔧 Proposed fix
-export const JARGON_PATTERN = /socket|SQL|launchd|CRR|JSONL|WAL|ENOSPC/i;
+export const JARGON_PATTERN = /\b(socket|SQL|launchd|CRR|JSONL|WAL|ENOSPC)\b/i;
🤖 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 `@apps/desktop/src/test/jargonGuard.ts` at line 5, Add word boundaries around
the WAL alternative in JARGON_PATTERN so it only matches the standalone jargon
term and not substrings within words such as “firewall” or “wallet”; preserve
the existing matching behavior for the other pattern alternatives.
apps/desktop/src/renderer/components/app/ProjectTransitionErrorAlert.tsx (1)

17-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared "deferred to recovery screen" predicate.

This same code && rootPath condition is re-implemented in App.tsx's ProjectTabHost (deciding whether to render ProjectRecoveryScreen). Keeping the ownership boundary in one place (e.g. a shared selector/helper like hasRecoverableProjectError(error)) avoids the two call sites silently drifting out of sync as new codes are added.

♻️ Suggested shared helper
+// apps/desktop/src/renderer/state/appStore.ts
+export function isRecoverableProjectTransitionError(
+  error: ProjectTransitionError | null,
+): error is ProjectTransitionError & { rootPath: string } {
+  return Boolean(error?.code && error.rootPath);
+}
-  if (projectTransitionError.code && projectTransitionError.rootPath) return null;
+  if (isRecoverableProjectTransitionError(projectTransitionError)) return null;
🤖 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 `@apps/desktop/src/renderer/components/app/ProjectTransitionErrorAlert.tsx`
around lines 17 - 19, Extract the `projectTransitionError.code &&
projectTransitionError.rootPath` predicate into a shared helper or selector such
as `hasRecoverableProjectError(error)`, then reuse it in both
`ProjectTransitionErrorAlert` and `ProjectTabHost` when deciding whether
recovery handles the error. Preserve the current null-rendering behavior while
ensuring both call sites use the same ownership rule.
apps/desktop/src/main/services/pty/ptyService.ts (1)

3600-3605: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

LGTM on the gating placement (runs before any spawn/transcript work, only for fresh tracked CLI launches).

Same duplicated Object.assign(new Error(...), { code }) pattern as flagged in processService.ts — worth consolidating into a shared coded-error helper if one exists in apps/desktop/src/shared/codedError.ts.

🤖 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 `@apps/desktop/src/main/services/pty/ptyService.ts` around lines 3600 - 3605,
Consolidate the coded error construction in the fresh tracked CLI launch gate
within the PTY service by reusing the shared helper from codedError.ts, if
available, instead of duplicating Object.assign(new Error(decision.message), {
code: decision.code }). Preserve the existing decision message, error code, and
gating behavior.
apps/desktop/src/main/services/processes/processService.ts (1)

876-881: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared codedError helper here
apps/desktop/src/shared/codedError.ts already exports codedError, so this can replace the inline Object.assign(new Error(...), { code }) in both processService.ts and ptyService.ts for consistency and a stable error shape.

🤖 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 `@apps/desktop/src/main/services/processes/processService.ts` around lines 876
- 881, Replace the inline Object.assign error construction in the process-start
disk-pressure check with the shared codedError helper, passing decision.message
and decision.code. Apply the same change in the corresponding disk-pressure
handling in ptyService.ts, preserving the existing throw behavior and
AdeRecoveryErrorCode typing.
apps/desktop/src/renderer/components/app/StoragePressureIndicator.tsx (1)

63-87: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

role="status" masks button semantics for assistive tech.

The clickable element uses role="status", which overrides the native <button> role. Screen readers will likely announce this as a passive live-region rather than an actionable control, even though aria-label/title describe state — the click affordance ("opens storage settings") is not conveyed to AT users the same way it is to sighted/mouse users.

Consider keeping the native button role and using a visually-hidden aria-live region (or aria-live="polite" wrapper) solely for the state-change announcement, separate from the interactive element's exposed role.

🤖 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 `@apps/desktop/src/renderer/components/app/StoragePressureIndicator.tsx` around
lines 63 - 87, The StoragePressureIndicator button should retain its native
button semantics instead of overriding them with role="status". Remove the role
override from the button and add a separate visually hidden aria-live="polite"
element or wrapper that announces snapshot.state changes, while keeping the
button’s accessible label and storage-settings click behavior unchanged.
apps/desktop/src/main/services/runtime/lastFailureStore.ts (1)

140-148: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

TOCTOU between existsSync and rename in clearLastFailure.

The existence check and the rename are separate syscalls; a concurrent writer (desktop + ade-cli both touch the machine-scoped file) could remove/replace the file in between, though the resulting ENOENT is caught and logged rather than crashing. Low risk given the graceful fallback, but worth noting alongside the atomicity gap above.

🤖 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 `@apps/desktop/src/main/services/runtime/lastFailureStore.ts` around lines 140
- 148, Update clearLastFailure to remove the separate existsSync precheck and
attempt moveCurrentToPrevious directly within its existing try/catch. Preserve
graceful handling by treating a missing file (ENOENT) as a no-op rather than
reporting it, while continuing to report other write failures through
reportWriteFailure.
apps/ade-cli/src/bootstrap.ts (1)

465-467: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pass logger to the CLI disk-pressure monitor. Both desktop call sites construct createDiskPressureMonitor({ roots, logger }), but the CLI runtime omits logger even though it's already available (Line 464). This drops disk-pressure state-transition/warning logs in exactly the headless disk-full scenario this PR hardens.

🔧 Proposed change
   const diskPressureMonitor = createDiskPressureMonitor({
     roots: [projectRoot, resolveMachineAdeLayout().adeDir],
+    logger,
   });

Please confirm createDiskPressureMonitor accepts an optional logger (the desktop usage in main.ts indicates it does).

🤖 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 `@apps/ade-cli/src/bootstrap.ts` around lines 465 - 467, Update the
createDiskPressureMonitor call in bootstrap.ts to include the already available
logger from the surrounding scope alongside roots. Preserve the existing
projectRoot and ADE directory configuration, and rely on the monitor’s optional
logger parameter as used by the desktop call sites.
apps/desktop/src/main/services/sessions/sessionService.ts (1)

1144-1189: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting shared tail-slice/align/strip logic.

The .gz branch and the existing file-handle branch both implement the same "slice tail → optionally align to newline → stringify → strip ANSI" sequence on a Buffer. Functionally correct as written, but a shared helper (Buffer -> string) taking the decompressed/read buffer would avoid future divergence between the two paths.

🤖 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 `@apps/desktop/src/main/services/sessions/sessionService.ts` around lines 1144
- 1189, Extract the duplicated Buffer-to-string processing from the gzip branch
and file-handle branch into a shared helper near the surrounding session service
logic. Have the helper perform tail slicing, optional alignToLineBoundary
newline adjustment, UTF-8 conversion, and conditional stripAnsi handling, then
pass each branch’s Buffer and existing options to it without changing behavior.
apps/desktop/src/main/services/chat/agentChatService.ts (1)

2324-2324: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

checkedTranscriptTails grows unbounded — clear it on session disposal/deletion.

Entries are added in flushQueuedTranscriptWrite (Line 2347) but never removed. finishSession/deleteSession/forceDisposeManagedSession clean up lastPersistedPointerFingerprints, eventHistoryBySession, transcriptHistoryCacheBySession, etc., but leave this Set. On a long-lived desktop process cycling through many chat sessions, the resolved-path strings accumulate indefinitely. Since this healing check only needs to run once per path per process, prune the transcript-path entries when a session's runtime/state is torn down (e.g. alongside the existing candidate-path cleanup in the disposal paths).

Also applies to: 36147-36157

🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.ts` at line 2324, Clear
the path entries tracked by checkedTranscriptTails during session teardown to
prevent unbounded growth. Update the cleanup logic in finishSession,
deleteSession, and forceDisposeManagedSession, alongside the existing
lastPersistedPointerFingerprints, eventHistoryBySession,
transcriptHistoryCacheBySession, and candidate-path cleanup, removing entries
associated with the disposed session’s transcript paths while preserving
process-wide deduplication for active sessions.
🤖 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 `@apps/ade-cli/src/services/runtime/failureLogDeduper.ts`:
- Around line 12-40: Update createFailureLogDeduper so failure entries are keyed
by a stable error code or name rather than dynamically formatted messages,
preventing runSyncHostStartupLoop from creating unbounded signatures in
failures; preserve the existing occurrence counting, summary logging, and clear
behavior.

In `@apps/ade-cli/src/services/runtime/runtimeLogMaintenance.ts`:
- Around line 23-31: Update the rotation logic around the Buffer allocated in
runtime log maintenance to capture the byte count returned by fs.readSync and
write only that read range to ${filePath}.1, rather than the entire allocUnsafe
buffer; preserve the existing keep calculation and file descriptor cleanup.

In `@apps/desktop/src/main/services/chat/chatTranscriptHistoryPager.ts`:
- Around line 71-91: The .gz paging branch repeatedly decompresses the entire
transcript for each request. Update the gzip snapshot handling around
readHistoryFileSync to cache decompressed buffers by transcript path plus
current mtime/size, reuse valid cached snapshots, and evict entries with a
bounded cache policy; preserve the existing paging and parsing behavior for
cached and newly read snapshots.

In `@apps/desktop/src/main/services/runtime/lastFailureStore.ts`:
- Around line 100-134: Preserve the current failure report across interrupted
rotations by updating moveCurrentToPrevious and/or readLastFailure: avoid
leaving a gap while replacing last-failure.json, or make readLastFailure fall
back to previousPath(targetPath) when the primary report is missing. Ensure a
failed replacement still exposes the previous report on the next launch without
changing normal rotation behavior.

In `@apps/desktop/src/main/services/runtime/projectRecoveryService.ts`:
- Around line 510-528: Update the stop_service branch in the project recovery
flow to invoke the dependency’s actual service stop/uninstall operation instead
of installServiceBestEffort(). Preserve the existing waitForSocketState
verification, failure handling, and step reporting so validation proceeds only
after the daemon has been stopped.

In `@apps/desktop/src/main/services/search/searchService.ts`:
- Around line 583-623: Bound the compressed read window in both
session-processing paths, including processTerminalSession, by
MAX_READ_BYTES_PER_PASS instead of setting it to the full fileSize. Keep
compressed data access as a subarray view, preserve plain-file behavior, and
retain the existing self-requeue flow so remaining content is processed in
subsequent passes.

In `@apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx`:
- Around line 25-35: Update AUTO_REPAIR_CODES to include
provider_thread_missing, provider_resume_failed, and
continuity_reconstruction_required so canAutoRepair’s fallback eligibility
matches the automatic-repair promises in FALLBACK_COPY and displays the “Repair
ADE” action for these recovery states.

In `@apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.tsx`:
- Around line 197-198: Update the catch branch in the recovery action to use the
plain-language copy returned by failureCopy("recovery_failed") instead of
exposing error.message or String(error). Preserve the existing error capture
flow while ensuring unexpected transport failures display the same sanitized
message as other recovery failures.

In `@apps/desktop/src/renderer/state/appStore.ts`:
- Around line 1000-1035: Update formatProjectTransitionError so every recognized
recovery code, including provider_thread_missing, provider_resume_failed,
continuity_reconstruction_required, and optional_mcp_failed, receives calm
user-facing recovery text instead of falling back to the raw error. Preserve the
raw fallback for unrecognized or absent codes, and retain the existing detail
behavior where applicable.
- Around line 1913-1919: Update the closeProject error handling around
formatProjectTransitionError so coded close failures include closingProjectRoot
as the rootPath when setting projectTransitionError. Preserve the existing error
object for uncoded failures and use the closingProjectRoot already captured
before the try block.

---

Outside diff comments:
In `@apps/desktop/src/main/services/processes/processService.ts`:
- Around line 875-994: Move the disk-pressure validation from the public start
method into the shared process-launch choke point, preferably startByDefinition
(or startById if that is the common path), so every startup route is gated.
Preserve the existing disallowed error behavior, including the monitor message
and AdeRecoveryErrorCode recovery code, and remove the redundant check from
start once the shared guard applies to start, restart, stack, group, and
all-start flows.

---

Nitpick comments:
In `@apps/ade-cli/src/bootstrap.ts`:
- Around line 465-467: Update the createDiskPressureMonitor call in bootstrap.ts
to include the already available logger from the surrounding scope alongside
roots. Preserve the existing projectRoot and ADE directory configuration, and
rely on the monitor’s optional logger parameter as used by the desktop call
sites.

In `@apps/desktop/src/main/services/chat/agentChatService.ts`:
- Line 2324: Clear the path entries tracked by checkedTranscriptTails during
session teardown to prevent unbounded growth. Update the cleanup logic in
finishSession, deleteSession, and forceDisposeManagedSession, alongside the
existing lastPersistedPointerFingerprints, eventHistoryBySession,
transcriptHistoryCacheBySession, and candidate-path cleanup, removing entries
associated with the disposed session’s transcript paths while preserving
process-wide deduplication for active sessions.

In `@apps/desktop/src/main/services/processes/processService.ts`:
- Around line 876-881: Replace the inline Object.assign error construction in
the process-start disk-pressure check with the shared codedError helper, passing
decision.message and decision.code. Apply the same change in the corresponding
disk-pressure handling in ptyService.ts, preserving the existing throw behavior
and AdeRecoveryErrorCode typing.

In `@apps/desktop/src/main/services/pty/ptyService.ts`:
- Around line 3600-3605: Consolidate the coded error construction in the fresh
tracked CLI launch gate within the PTY service by reusing the shared helper from
codedError.ts, if available, instead of duplicating Object.assign(new
Error(decision.message), { code: decision.code }). Preserve the existing
decision message, error code, and gating behavior.

In `@apps/desktop/src/main/services/runtime/lastFailureStore.ts`:
- Around line 140-148: Update clearLastFailure to remove the separate existsSync
precheck and attempt moveCurrentToPrevious directly within its existing
try/catch. Preserve graceful handling by treating a missing file (ENOENT) as a
no-op rather than reporting it, while continuing to report other write failures
through reportWriteFailure.

In `@apps/desktop/src/main/services/sessions/sessionService.ts`:
- Around line 1144-1189: Extract the duplicated Buffer-to-string processing from
the gzip branch and file-handle branch into a shared helper near the surrounding
session service logic. Have the helper perform tail slicing, optional
alignToLineBoundary newline adjustment, UTF-8 conversion, and conditional
stripAnsi handling, then pass each branch’s Buffer and existing options to it
without changing behavior.

In `@apps/desktop/src/renderer/components/app/ProjectTransitionErrorAlert.tsx`:
- Around line 17-19: Extract the `projectTransitionError.code &&
projectTransitionError.rootPath` predicate into a shared helper or selector such
as `hasRecoverableProjectError(error)`, then reuse it in both
`ProjectTransitionErrorAlert` and `ProjectTabHost` when deciding whether
recovery handles the error. Preserve the current null-rendering behavior while
ensuring both call sites use the same ownership rule.

In `@apps/desktop/src/renderer/components/app/StoragePressureIndicator.tsx`:
- Around line 63-87: The StoragePressureIndicator button should retain its
native button semantics instead of overriding them with role="status". Remove
the role override from the button and add a separate visually hidden
aria-live="polite" element or wrapper that announces snapshot.state changes,
while keeping the button’s accessible label and storage-settings click behavior
unchanged.

In `@apps/desktop/src/test/jargonGuard.ts`:
- Line 5: Add word boundaries around the WAL alternative in JARGON_PATTERN so it
only matches the standalone jargon term and not substrings within words such as
“firewall” or “wallet”; preserve the existing matching behavior for the other
pattern alternatives.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f0399e41-45a6-447b-8358-6f7ca149d22b

📥 Commits

Reviewing files that changed from the base of the PR and between 2a0af1b and 01ec27b.

⛔ Files ignored due to path filters (8)
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/README.md is excluded by !docs/**
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/onboarding-and-settings/README.md is excluded by !docs/**
  • docs/features/remote-runtime/README.md is excluded by !docs/**
  • docs/features/search/README.md is excluded by !docs/**
  • docs/features/storage-and-recovery/README.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
📒 Files selected for processing (86)
  • apps/ade-cli/README.md
  • apps/ade-cli/src/adeRpcServer.test.ts
  • apps/ade-cli/src/bootstrap.ts
  • apps/ade-cli/src/cli.test.ts
  • apps/ade-cli/src/cli.ts
  • apps/ade-cli/src/services/runtime/failureLogDeduper.test.ts
  • apps/ade-cli/src/services/runtime/failureLogDeduper.ts
  • apps/ade-cli/src/services/runtime/lastFailureStore.test.ts
  • apps/ade-cli/src/services/runtime/runtimeLogMaintenance.test.ts
  • apps/ade-cli/src/services/runtime/runtimeLogMaintenance.ts
  • apps/ade-cli/src/services/sync/syncHostStartupLoop.ts
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/__tests__/diskFullIncident.integration.test.ts
  • apps/desktop/src/main/services/adeActions/registry.ts
  • apps/desktop/src/main/services/chat/agentChatService.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/chat/chatTranscriptHistoryPager.test.ts
  • apps/desktop/src/main/services/chat/chatTranscriptHistoryPager.ts
  • apps/desktop/src/main/services/chat/providerResumeClassifier.test.ts
  • apps/desktop/src/main/services/chat/providerResumeClassifier.ts
  • apps/desktop/src/main/services/chat/threadPointerLedger.ts
  • apps/desktop/src/main/services/externalSessions/discoverCodex.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.test.ts
  • apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts
  • apps/desktop/src/main/services/processes/processService.test.ts
  • apps/desktop/src/main/services/processes/processService.ts
  • apps/desktop/src/main/services/projects/projectScaffoldService.ts
  • apps/desktop/src/main/services/pty/ptyService.test.ts
  • apps/desktop/src/main/services/pty/ptyService.ts
  • apps/desktop/src/main/services/runtime/lastFailureStore.ts
  • apps/desktop/src/main/services/runtime/projectRecoveryService.test.ts
  • apps/desktop/src/main/services/runtime/projectRecoveryService.ts
  • apps/desktop/src/main/services/search/searchService.test.ts
  • apps/desktop/src/main/services/search/searchService.ts
  • apps/desktop/src/main/services/sessions/sessionService.test.ts
  • apps/desktop/src/main/services/sessions/sessionService.ts
  • apps/desktop/src/main/services/state/durableFile.test.ts
  • apps/desktop/src/main/services/state/durableFile.ts
  • apps/desktop/src/main/services/state/kvDb.rebuildRecovery.test.ts
  • apps/desktop/src/main/services/state/kvDb.test.ts
  • apps/desktop/src/main/services/state/kvDb.ts
  • apps/desktop/src/main/services/storage/diskPressure.test.ts
  • apps/desktop/src/main/services/storage/diskPressure.ts
  • apps/desktop/src/main/services/storage/historyCompression.test.ts
  • apps/desktop/src/main/services/storage/historyCompression.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.test.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.ts
  • apps/desktop/src/main/services/storage/volume.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/browserMock.ts
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/app/App.workKeepAlive.test.tsx
  • apps/desktop/src/renderer/components/app/AppShell.aiStatus.test.tsx
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.test.tsx
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx
  • apps/desktop/src/renderer/components/app/ProjectTransitionErrorAlert.test.tsx
  • apps/desktop/src/renderer/components/app/ProjectTransitionErrorAlert.tsx
  • apps/desktop/src/renderer/components/app/SettingsPage.tsx
  • apps/desktop/src/renderer/components/app/StoragePressureIndicator.test.tsx
  • apps/desktop/src/renderer/components/app/StoragePressureIndicator.tsx
  • apps/desktop/src/renderer/components/app/TopBar.test.tsx
  • apps/desktop/src/renderer/components/app/TopBar.tsx
  • apps/desktop/src/renderer/components/app/settingsSections.ts
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.tsx
  • apps/desktop/src/renderer/components/projects/PublishToGitHubDialog.tsx
  • apps/desktop/src/renderer/components/settings/StorageSection.test.tsx
  • apps/desktop/src/renderer/components/settings/StorageSection.tsx
  • apps/desktop/src/renderer/components/settings/storage/StorageCleanupDialog.tsx
  • apps/desktop/src/renderer/components/settings/storage/storageView.ts
  • apps/desktop/src/renderer/lib/codedError.ts
  • apps/desktop/src/renderer/lib/format.ts
  • apps/desktop/src/renderer/state/appStore.test.ts
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/shared/codedError.ts
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/types/chat.ts
  • apps/desktop/src/shared/types/index.ts
  • apps/desktop/src/shared/types/recovery.ts
  • apps/desktop/src/shared/types/storage.ts
  • apps/desktop/src/test/faultInjection.ts
  • apps/desktop/src/test/jargonGuard.ts
  • apps/desktop/vitest.workspace.ts

Comment thread apps/ade-cli/src/services/runtime/failureLogDeduper.ts
Comment thread apps/ade-cli/src/services/runtime/runtimeLogMaintenance.ts
Comment thread apps/desktop/src/main/services/chat/chatTranscriptHistoryPager.ts
Comment thread apps/desktop/src/main/services/runtime/lastFailureStore.ts
Comment thread apps/desktop/src/main/services/runtime/projectRecoveryService.ts
Comment thread apps/desktop/src/main/services/search/searchService.ts
Comment thread apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx
Comment thread apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.tsx Outdated
Comment thread apps/desktop/src/renderer/state/appStore.ts
Comment thread apps/desktop/src/renderer/state/appStore.ts
Preview identities lived in service state keyed by path, so a second
preview of the same path replaced the identity an earlier confirmation
depended on — a stale confirm could then delete a generation the user
never previewed. Identities now travel inside the returned preview and
cleanup validates each target against the submitted preview's identity
(missing identity blocks with the re-preview message). Regression test
reproduces the drift: same-size content change between two previews,
stale confirm blocked, fresh confirm succeeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Comment thread apps/desktop/src/main/main.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8892e9227b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/runtime/projectRecoveryService.ts Outdated
Comment thread apps/desktop/src/main/services/chat/agentChatService.ts Outdated
Comment thread apps/desktop/src/main/services/processes/processService.ts
Comment thread apps/ade-cli/src/cli.ts Outdated
Verified each bot finding against the code; ten confirmed and fixed,
one refuted with the intent made explicit:

- Repair flow now genuinely stops the service before exclusive database
  work (new pool uninstallServiceBestEffort spawning serve
  --uninstall-service — desktop keeps the child-process boundary and
  never imports ade-cli service code) and reinstalls afterwards.
- Compressed transcript reads are bounded per pass in search indexing
  and history paging; the failure-log deduper uses stable signatures;
  log rotation writes only initialized bytes; interrupted last-failure
  rotation preserves the primary report.
- Renderer: every recognized recovery code gets calm copy (no raw
  fallback text), coded close failures retain the project root, the
  recovery screen's repair eligibility matches its own copy, and
  unexpected continuity-recovery errors are sanitized behind Details.
- Daemon-backed storage fallback now declares isPathActive explicitly
  with the ownership rationale (brain runs the real compression sweep).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d1adf7981

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/externalSessions/discoverCodex.ts
Comment thread apps/desktop/src/main/services/chat/agentChatService.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4430859d61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/chat/agentChatService.ts Outdated
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
…tic in tests

- The disk-pressure gate is now the first act of every process launch
  path (startById, runStartSet, startByDefinition), so refusal happens
  before any config or trust access — which is also what the incident
  integration test asserts.
- Continuity-recovery tests own a fresh CODEX_HOME per test instead of
  inheriting the machine's real ~/.codex, whose size previously decided
  whether a rollout probe completed (CI: definitive absence; a dev
  machine: inconclusive) and flipped classification. Tests that need
  "local history exists" write an explicit rollout fixture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/main/services/chat/agentChatService.ts (1)

2367-2370: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clear checkedTranscriptTails during session cleanup. checkedTranscriptTails only grows: once a transcript path is flushed, isTranscriptPathActive will treat it as active forever, even after deleteSession removes the file. That breaks the new storage-cleanup guard and leaks memory in a long-running desktop process. Remove the resolved paths from this set alongside lastPersistedPointerFingerprints in deleteSession.

🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.ts` around lines 2367 -
2370, Update deleteSession to remove the session’s resolved transcript paths
from checkedTranscriptTails during cleanup, alongside the existing
lastPersistedPointerFingerprints removal. Ensure deleted transcript paths are no
longer treated as active and the set does not grow indefinitely.
🤖 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 `@apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts`:
- Around line 791-833: Update uninstallServiceBestEffort() to enforce a timeout
for the spawned uninstall child process so its Promise always settles if the
process hangs. On timeout, terminate the child, log
local_runtime.service_uninstall_failed with timeout context, and reject; clear
the timer when the child emits error or close, preserving existing success and
failure handling.
- Around line 791-833: Update uninstallServiceBestEffort() to cache and reuse an
in-flight uninstall promise, mirroring the existing serviceInstallPromise guard
in installServiceBestEffort(). Ensure concurrent callers share one spawned
--uninstall-service process, and clear the cached promise after completion so
later calls can run normally.

In `@apps/desktop/src/main/services/processes/processService.test.ts`:
- Around line 1298-1305: Update service.restart to check disk pressure before
invoking stopEntries, ensuring a disk_full failure from startById does not leave
the existing run exited without a replacement. Preserve the current run when the
replacement cannot start, while retaining normal stop-and-restart behavior for
successful starts; adjust the restart test expectations accordingly.

---

Outside diff comments:
In `@apps/desktop/src/main/services/chat/agentChatService.ts`:
- Around line 2367-2370: Update deleteSession to remove the session’s resolved
transcript paths from checkedTranscriptTails during cleanup, alongside the
existing lastPersistedPointerFingerprints removal. Ensure deleted transcript
paths are no longer treated as active and the set does not grow indefinitely.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d698a639-fc44-47e2-ad21-b70d89802b1f

📥 Commits

Reviewing files that changed from the base of the PR and between 01ec27b and 4430859.

⛔ Files ignored due to path filters (2)
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/onboarding-and-settings/README.md is excluded by !docs/**
📒 Files selected for processing (36)
  • apps/ade-cli/README.md
  • apps/ade-cli/src/cli.test.ts
  • apps/ade-cli/src/cli.ts
  • apps/ade-cli/src/services/runtime/runtimeLogMaintenance.test.ts
  • apps/ade-cli/src/services/runtime/runtimeLogMaintenance.ts
  • apps/ade-cli/src/services/sync/syncHostStartupLoop.test.ts
  • apps/ade-cli/src/services/sync/syncHostStartupLoop.ts
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/__tests__/diskFullIncident.integration.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/chat/chatTranscriptHistoryPager.test.ts
  • apps/desktop/src/main/services/chat/chatTranscriptHistoryPager.ts
  • apps/desktop/src/main/services/chat/providerResumeClassifier.test.ts
  • apps/desktop/src/main/services/chat/providerResumeClassifier.ts
  • apps/desktop/src/main/services/externalSessions/discoverCodex.ts
  • apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts
  • apps/desktop/src/main/services/processes/processService.test.ts
  • apps/desktop/src/main/services/processes/processService.ts
  • apps/desktop/src/main/services/runtime/lastFailureStore.test.ts
  • apps/desktop/src/main/services/runtime/lastFailureStore.ts
  • apps/desktop/src/main/services/runtime/projectRecoveryService.test.ts
  • apps/desktop/src/main/services/runtime/projectRecoveryService.ts
  • apps/desktop/src/main/services/search/searchService.test.ts
  • apps/desktop/src/main/services/search/searchService.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.test.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.ts
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.test.tsx
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.tsx
  • apps/desktop/src/renderer/state/appStore.test.ts
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/shared/types/chat.ts
  • apps/desktop/src/shared/types/storage.ts
🚧 Files skipped from review as they are similar to previous changes (25)
  • apps/ade-cli/README.md
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/shared/types/storage.ts
  • apps/desktop/src/main/services/chat/providerResumeClassifier.ts
  • apps/desktop/src/main/services/processes/processService.ts
  • apps/ade-cli/src/services/sync/syncHostStartupLoop.ts
  • apps/desktop/src/renderer/state/appStore.test.ts
  • apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.tsx
  • apps/desktop/src/shared/types/chat.ts
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/main/services/runtime/projectRecoveryService.test.ts
  • apps/desktop/src/main/services/tests/diskFullIncident.integration.test.ts
  • apps/desktop/src/main/services/externalSessions/discoverCodex.ts
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.test.tsx
  • apps/desktop/src/main/services/chat/providerResumeClassifier.test.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.test.ts
  • apps/desktop/src/main/services/chat/chatTranscriptHistoryPager.ts
  • apps/desktop/src/main/services/runtime/lastFailureStore.ts
  • apps/desktop/src/renderer/components/chat/ChatContinuityRecoveryCard.test.tsx
  • apps/desktop/src/main/services/search/searchService.ts
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.ts
  • apps/ade-cli/src/cli.ts
  • apps/desktop/src/main/services/runtime/projectRecoveryService.ts

Comment thread apps/desktop/src/main/services/processes/processService.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

projectTransitionError: formatProjectTransitionError("opening", error),

P2 Badge Preserve selected root for recovery after picker opens

When project.openRepo() fails after the user chooses a folder, typed recovery errors like disk_full or migration_incomplete are stored here without a rootPath; App only mounts the full recovery flow when both code and rootPath are present. This affects the first-open/file-picker path (unlike switchProjectToPath, which adds the root below), so users get only a dismissible banner and cannot run diagnose/repair for the project they just selected.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/chat/agentChatService.ts Outdated
…ll timeout

- retry_original for Codex now runs runtime setup (ensure/resolve)
  inside the catchable block, so a setup failure re-marks continuity
  recovery instead of leaving the chat with the recovery card gone.
- Open-repository dialog failures encode the selected root (chosen in
  the main process, never seen by the renderer) into the coded error so
  the recovery screen can offer Repair rather than the generic banner;
  the shared codedError wire format carries an optional NUL-delimited
  rootPath, covered by a new round-trip test.
- uninstallServiceBestEffort now has a 20s timeout that kills a hung
  child and rejects, so a stuck login-item removal can't leave the
  repair flow waiting forever.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

A send to an already-active chat was converted to steer() before the
chat_turn disk-pressure check ran, and steer() has no gate of its own —
so at exhausted capacity active sessions kept appending user input and
driving provider work while only idle sends paused. The gate now runs
before the steer route, covering both paths; regression test drives an
active session at exhausted pressure and asserts no steer/turn starts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ef06362d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx Outdated
Comment thread apps/desktop/src/main/services/chat/agentChatService.ts
- ProjectRecoveryScreen's Review storage now clears the transition
  error before navigating; while it was set, ProjectTabHost kept
  rendering the takeover and the route change never revealed Settings.
- The fresh-thread replace guard authorizes a recover-from-history
  retry against the recovery's recorded original thread, not only the
  current pointer, so a crash that left threadId pointing at a
  half-built reconstruction thread (state still "required") no longer
  wedges the chat behind provider_thread_replace_blocked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aafa8869ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/storage/storageInsightsService.ts Outdated
…tree

storage cleanup matched lane worktrees by basename as a fallback, so an
archived lane at /tmp/feature could authorize removing an active lane's
.ade/worktrees/feature and delete live work. laneForPath now matches
only the exact resolved worktree_path, for both snapshot classification
and cleanup authorization. Regression test: a same-named archived lane
cannot classify or delete an active worktree, even with a forged
preview.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1ca795567

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/pty/ptyService.ts Outdated
Comment thread apps/desktop/src/main/services/chat/agentChatService.ts Outdated
- The transcript tail-check marked a path verified before the append
  landed, so an ENOSPC-torn append left the file without a trailing
  newline and every later flush skipped healing forever, corrupting the
  next event. The path is now marked checked only after a successful
  append, so a failed append re-checks and heals on the next flush.
- Resuming a tracked CLI session whose PTY is gone spawns a new
  process; the disk-pressure cli_launch gate now covers it (the
  live-attach case still returns earlier), so exhausted storage no
  longer lets a dead session respawn and append to its transcript.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b780f0007

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/processes/processService.ts
restart/restartStack/restartGroup reached the process-start gate only
after stopEntries had already killed the running process, so exhausted
storage would stop a healthy process and then refuse to launch its
replacement — leaving the user worse off than doing nothing. The gate
now runs at the top of each restart path, before any stop, so a refused
restart leaves the existing process running. Updated the test that
previously encoded the killed-then-unrestartable behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
apps/desktop/src/main/services/chat/agentChatService.ts (2)

2370-2370: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

checkedTranscriptTails is never cleared, permanently poisoning isTranscriptPathActive.

checkedTranscriptTails (module-level Set<string>) gets an entry added on every successful transcript flush (line 2422) but is never removed — not in deleteSession, dispose, finishSession, or forceDisposeAll. Unlike pendingTranscriptWrites, which self-cleans via .delete(normalizedPath) in flushQueuedTranscriptWrite, this Set only grows.

isTranscriptPathActive (line 37140) treats membership in this Set as proof the path is "active" (pendingTranscriptWrites.has(normalized) || checkedTranscriptTails.has(normalized)). Since every transcript path that was ever flushed stays in the Set forever, once a chat session ends and its transcript files are deleted, isTranscriptPathActive will still report true for that path indefinitely. If this function gates the storage cleanup / compressed-inactive-history sweep this PR introduces, it means transcripts for ended sessions can never be recognized as inactive and swept — directly undermining the "compressed inactive history" feature, in addition to an unbounded memory leak over the life of the process.

🐛 Proposed fix — clear the tail-checked marker when a transcript path is torn down
       flushQueuedTranscriptWrite(managed.transcriptPath);
       flushQueuedTranscriptWrite(path.join(chatTranscriptsDir, `${trimmedSessionId}.jsonl`));
+      checkedTranscriptTails.delete(path.resolve(managed.transcriptPath));
+      checkedTranscriptTails.delete(path.resolve(path.join(chatTranscriptsDir, `${trimmedSessionId}.jsonl`)));
       teardownRuntime(managed, "ended_session");
       managedSessions.delete(trimmedSessionId);

Also applies to: 2392-2422, 37138-37149

🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.ts` at line 2370, Clear
the checkedTranscriptTails marker whenever its transcript path is torn down,
including deleteSession, dispose, finishSession, and forceDisposeAll, using the
same normalized path key added during transcript flushing. Update the lifecycle
cleanup around checkedTranscriptTails and isTranscriptPathActive so ended or
deleted sessions no longer remain marked active, while preserving
pendingTranscriptWrites behavior.

30762-30792: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Extend the disk-pressure gate beyond sendMessage().

  • messageSession() still routes queued/auto/wake sends straight into steer(), and runSessionTurn() dispatches executePreparedSendMessage() directly, so critical pressure can still drive provider work through those paths.
  • checkedTranscriptTails is only ever added to, and isTranscriptPathActive() treats it as permanently active; clear that state on teardown or old transcript paths will stay protected from compression forever.
🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.ts` around lines 30762 -
30792, Extend the disk-pressure “chat_turn” gate used by sendMessage() to
messageSession() before queued, auto, or wake sends can reach steer(), and to
runSessionTurn() before executePreparedSendMessage() dispatches provider work.
Preserve existing failure emission and return behavior for denied decisions.
Clear checkedTranscriptTails during session/transcript teardown so
isTranscriptPathActive() does not permanently protect obsolete transcript paths.
apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts (2)

1565-1567: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Sanitize lastFailure.detail before exposing it. lastFailureStore.ts already caps the byte length, but detail is still appended verbatim to the thrown error, so control characters and raw diagnostics can cross the IPC boundary. Strip or normalize it here before appending.

🤖 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 `@apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts`
around lines 1565 - 1567, Sanitize lastFailure.detail in the reportDetail
construction within the local runtime connection pool before appending it to the
thrown error. Normalize or strip control characters and unsafe raw diagnostic
content while preserving the existing capped-length behavior from
lastFailureStore.ts, and keep the fallback unchanged when no detail exists.

1558-1561: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Differentiate probe failures from a stale socket

probeSocketHasOwner() collapses timeouts and error events into false, so transient or permission failures get reported as socket_stale_no_owner. Return a tri-state, or only map definitive missing/refused-socket cases to that recovery code.

🤖 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 `@apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts`
around lines 1558 - 1561, Update the recovery classification around
probeSocketHasOwner so probe timeouts and error events are not converted into
socket_stale_no_owner. Preserve socket_owned_by_other for confirmed ownership,
map only definitive missing or refused-socket results to socket_stale_no_owner,
and propagate or classify indeterminate probe failures separately.
🧹 Nitpick comments (2)
apps/desktop/src/main/services/chat/agentChatService.test.ts (1)

30369-30409: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard the fault-injection restore with try/finally.

renameFault.restore() runs as a bare statement after several expect() calls. If any assertion between injecting the fault (30385) and restoring it (30405) throws, fs.renameSync stays monkey-patched globally for the rest of the file's test run, risking cascading, hard-to-diagnose failures in unrelated tests.

🔧 Proposed fix
-    const renameFault = injectFsFault({
-      op: "renameSync",
-      matchPath: (candidate) => path.resolve(candidate) === path.resolve(metadataPath(session.id)),
-    });
-
-    mockState.emitCodexPayload({
-      jsonrpc: "2.0",
-      method: "thread/deleted",
-      params: { threadId: "thread-resumed" },
-    });
-    await vi.waitFor(() => {
-      expect(logger.warn).toHaveBeenCalledWith(
-        "agent_chat.persist_failed",
-        expect.objectContaining({ sessionId: session.id, lkgUpdated: true }),
-      );
-    });
-    expect(fs.readFileSync(metadataPath(session.id))).toEqual(before);
-    expect(readPersistedChatState(session.id).threadId).toBe("thread-resumed");
-    expect(fs.readdirSync(chatSessionsDir()).filter((name) => name.includes(".tmp-"))).toEqual([]);
-
-    renameFault.restore();
-    await service.updateSession({ sessionId: session.id, title: "Persist after ENOSPC" });
-    expect(readPersistedChatState(session.id).threadId).toBeUndefined();
-    expect(JSON.parse(fs.readFileSync(`${metadataPath(session.id)}.lkg`, "utf8")).threadId).toBe("thread-resumed");
+    const renameFault = injectFsFault({
+      op: "renameSync",
+      matchPath: (candidate) => path.resolve(candidate) === path.resolve(metadataPath(session.id)),
+    });
+    try {
+      mockState.emitCodexPayload({
+        jsonrpc: "2.0",
+        method: "thread/deleted",
+        params: { threadId: "thread-resumed" },
+      });
+      await vi.waitFor(() => {
+        expect(logger.warn).toHaveBeenCalledWith(
+          "agent_chat.persist_failed",
+          expect.objectContaining({ sessionId: session.id, lkgUpdated: true }),
+        );
+      });
+      expect(fs.readFileSync(metadataPath(session.id))).toEqual(before);
+      expect(readPersistedChatState(session.id).threadId).toBe("thread-resumed");
+      expect(fs.readdirSync(chatSessionsDir()).filter((name) => name.includes(".tmp-"))).toEqual([]);
+    } finally {
+      renameFault.restore();
+    }
+    await service.updateSession({ sessionId: session.id, title: "Persist after ENOSPC" });
+    expect(readPersistedChatState(session.id).threadId).toBeUndefined();
+    expect(JSON.parse(fs.readFileSync(`${metadataPath(session.id)}.lkg`, "utf8")).threadId).toBe("thread-resumed");
🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.test.ts` around lines
30369 - 30409, Wrap the assertions and persistence attempt after injecting
renameFault in a try/finally block, and move renameFault.restore() into the
finally clause. Keep the existing checks and follow-up updateSession behavior
unchanged while guaranteeing the fs.renameSync fault is restored even when an
assertion fails.
apps/desktop/src/main/services/chat/agentChatService.ts (1)

7856-7876: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid inflating gzip transcripts twice

  • readTranscriptTailForHistory inflates .gz files into a full buffer up to MAX_TRANSPARENT_HISTORY_BYTES before slicing, so gzip tail reads still pay the full decompression cost.
  • resolveBestTranscriptPathForSessionId inflates the same gzip candidate again just to compute size; thread the decompressed length through parseTranscriptHistoryTail or cache it so each candidate is read once.
🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.ts` around lines 7856 -
7876, The gzip transcript path in readTranscriptTailForHistory currently
inflates the full file, while resolveBestTranscriptPathForSessionId inflates the
same candidate again to determine size. Reuse the decompressed buffer or thread
its length through parseTranscriptHistoryTail and the path-resolution flow so
each gzip candidate is read and decompressed only once, while preserving tail
slicing and offset behavior.
🤖 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 `@apps/desktop/src/main/services/chat/agentChatService.test.ts`:
- Around line 30533-30535: Update the test setup around the per-test CODEX_HOME
assignment to retain the created temporary directory and remove it in that
describe block’s afterEach cleanup. Preserve the existing tmpHomeRoot cleanup
and ensure the CODEX_HOME directory is deleted recursively even when a test
fails.

In `@apps/desktop/src/shared/codedError.ts`:
- Around line 31-34: Update the decoding logic around withRoot and rootPath so
the filesystem path is taken directly from the payload after the delimiter
without trimming; trim only the user-facing raw message. Preserve rootPath bytes
across the IPC boundary and add a regression test covering a path with trailing
whitespace.
- Around line 17-19: Update encodeCodedErrorMessage and the surrounding
surfaceCodedError flow so errors with an existing `${code}:` prefix are
re-encoded whenever meta.rootPath is present, preserving the root path for IPC
repair handling. Update parseCodedErrorMessage to retain the decoded rootPath
exactly without trimming it; continue existing behavior when no rootPath is
supplied.

---

Outside diff comments:
In `@apps/desktop/src/main/services/chat/agentChatService.ts`:
- Line 2370: Clear the checkedTranscriptTails marker whenever its transcript
path is torn down, including deleteSession, dispose, finishSession, and
forceDisposeAll, using the same normalized path key added during transcript
flushing. Update the lifecycle cleanup around checkedTranscriptTails and
isTranscriptPathActive so ended or deleted sessions no longer remain marked
active, while preserving pendingTranscriptWrites behavior.
- Around line 30762-30792: Extend the disk-pressure “chat_turn” gate used by
sendMessage() to messageSession() before queued, auto, or wake sends can reach
steer(), and to runSessionTurn() before executePreparedSendMessage() dispatches
provider work. Preserve existing failure emission and return behavior for denied
decisions. Clear checkedTranscriptTails during session/transcript teardown so
isTranscriptPathActive() does not permanently protect obsolete transcript paths.

In `@apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts`:
- Around line 1565-1567: Sanitize lastFailure.detail in the reportDetail
construction within the local runtime connection pool before appending it to the
thrown error. Normalize or strip control characters and unsafe raw diagnostic
content while preserving the existing capped-length behavior from
lastFailureStore.ts, and keep the fallback unchanged when no detail exists.
- Around line 1558-1561: Update the recovery classification around
probeSocketHasOwner so probe timeouts and error events are not converted into
socket_stale_no_owner. Preserve socket_owned_by_other for confirmed ownership,
map only definitive missing or refused-socket results to socket_stale_no_owner,
and propagate or classify indeterminate probe failures separately.

---

Nitpick comments:
In `@apps/desktop/src/main/services/chat/agentChatService.test.ts`:
- Around line 30369-30409: Wrap the assertions and persistence attempt after
injecting renameFault in a try/finally block, and move renameFault.restore()
into the finally clause. Keep the existing checks and follow-up updateSession
behavior unchanged while guaranteeing the fs.renameSync fault is restored even
when an assertion fails.

In `@apps/desktop/src/main/services/chat/agentChatService.ts`:
- Around line 7856-7876: The gzip transcript path in
readTranscriptTailForHistory currently inflates the full file, while
resolveBestTranscriptPathForSessionId inflates the same candidate again to
determine size. Reuse the decompressed buffer or thread its length through
parseTranscriptHistoryTail and the path-resolution flow so each gzip candidate
is read and decompressed only once, while preserving tail slicing and offset
behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bdbbdf39-2b3f-42ad-afd2-bc893fb92602

📥 Commits

Reviewing files that changed from the base of the PR and between 4430859 and 6b780f0.

📒 Files selected for processing (13)
  • apps/desktop/src/main/services/chat/agentChatService.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts
  • apps/desktop/src/main/services/processes/processService.ts
  • apps/desktop/src/main/services/pty/ptyService.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.test.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.ts
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.test.tsx
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/shared/codedError.test.ts
  • apps/desktop/src/shared/codedError.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/desktop/src/main/services/processes/processService.ts
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.test.tsx
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/renderer/components/app/ProjectRecoveryScreen.tsx
  • apps/desktop/src/main/services/storage/storageInsightsService.test.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/storage/storageInsightsService.ts
  • apps/desktop/src/main/services/pty/ptyService.ts

Comment thread apps/desktop/src/main/services/chat/agentChatService.test.ts Outdated
Comment thread apps/desktop/src/shared/codedError.ts
Comment thread apps/desktop/src/shared/codedError.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf6c6f4ee6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/services/storage/historyCompression.ts Outdated
Comment thread apps/desktop/src/main/services/chat/agentChatService.ts Outdated
- surfaceCodedError re-encodes an already-code-prefixed error when a
  rootPath needs attaching, so an open-repo failure that rethrows a
  pre-prefixed coded error no longer drops the path the recovery screen
  needs.
- parseCodedErrorMessage splits the rootPath off before the wrapper
  strip trims the string, so a valid filesystem path with leading or
  trailing whitespace survives byte-for-byte instead of pointing the
  repair at a nonexistent directory. Regression test covers a spaced
  path.
- The continuity-recovery tests clean up their per-test CODEX_HOME temp
  directory instead of leaking one per run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

…sules

- The idle-compression sweep gated on "not normal", so warning-level
  pressure returned 0 files — refusing to compress exactly when the user
  most wants to reclaim space. It now gates on canPerform("compression")
  (allowed at warning, refused only at critical/exhausted), matching
  compressOne. Test covers a warning-level sweep compressing a file.
- buildContinuityCapsule read transcript bytes by offset and fed them to
  the parser, so a gzipped transcript produced no envelopes and the
  Recover-from-history capsule was empty for exactly the old chats most
  likely to be compressed. It now decompresses .gz transcripts
  transparently before parsing. Test proves the capsule carries the
  original task from a compressed transcript.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@arul28
arul28 merged commit fcd993e into main Jul 13, 2026
33 checks passed
@arul28
arul28 deleted the ade/start-skill-work-remebr-guidance-edba0f69 branch July 13, 2026 00:46

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: defe235b9a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +79 to +82
if (fs.existsSync(filePath)) {
try {
fs.copyFileSync(filePath, `${filePath}.lkg`);
lkgUpdated = true;

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 Badge Preserve the last-known-good file before overwriting it

When the primary JSON is already torn/corrupt but the .lkg is still valid, this copies the bad primary over the only good backup before attempting the new atomic write. If that new write then fails under the disk-full/interrupted-write conditions this code is meant to survive, readJsonWithRecovery() can no longer recover the previous chat/session state because both files are now invalid. Only promote the current primary to .lkg after validating it, or update the backup atomically from the last valid payload.

Useful? React with 👍 / 👎.

Comment on lines +207 to +208
const timer = window.setTimeout(() => {
void switchProjectToPath(rootPath);

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 Badge Handle failed reopen after a successful repair

If switchProjectToPath(rootPath) rejects after repair() reported success (for example the service still cannot open the project), the rejection is ignored and this component stays in the success phase with reopenStartedRef set. The store may receive a new transition error, but the recovery screen remains mounted for the same root and keeps telling the user the project was repaired and reopened, with no retry/failure state. Catch the reopen failure and move the screen back to a failure/idle state or reset the reopen guard.

Useful? React with 👍 / 👎.

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