Skip to content

fix(server): compact superseded history in the background - #19435

Merged
juliusmarminge merged 2 commits into
mainfrom
t3/db-compaction-1-background
Oct 16, 2026
Merged

juliusmarminge merged 2 commits into
mainfrom
t3/db-compaction-1-background

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

The server never deleted superseded orchestration history, so databases grow without bound. One maintainer database reached 114 GB, most of it repeated whole-payload updates of the same thread, message, node or PR link.

The server now runs compactEventStore one minute after startup and every six hours after that. Each pass deletes events whose state a newer retained event already carries:

  • Whole-thread events (rename, archive, settle, pin, model changes…): only the newest per thread is kept. Visit and mark-unread events supersede only each other, so a later visit never removes the rename that carries the thread's activity time.
  • Messages, nodes and PR links: the oldest and newest event per entity are kept. Clients add an entity where they first see it, so keeping the first event preserves message order for a client that reconnects from an older cursor.
  • thread.created, thread.deleted, runs and turn items are never compacted.

No replay boundary or snapshot fallback is needed: every deleted row has a newer row for the same key, so any replay from an older cursor still ends in the same state and order.

New databases are created with auto_vacuum = INCREMENTAL, and the server returns freed pages to disk online in small yielding batches. Existing databases get that with t3 doctor (#19436). Compaction pages are 50 rows, because a 500-row page with large payloads blocked the shared connection for ~90 ms.

A retried MCP metadata update whose events were compacted now answers from the current thread instead of failing.

Takes over #19249.

🤖 Generated with Claude Code

Fixes #18559


Devin Review

@juliusmarminge
juliusmarminge added this pull request to stack #19438 October 14, 2026 12:46
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Oct 14, 2026
@juliusmarminge juliusmarminge changed the title t3/db compaction 1 background fix(server): compact superseded history in the background Oct 14, 2026
@github-actions github-actions Bot added the size:L 100-499 changed lines (additions + deletions). label Oct 14, 2026
@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.9 KiB 0 B (0.0%) 29.3 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB 0 B (0.0%) 29.3 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: fa1184d · PR result: e5cbab0 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment thread apps/server/src/persistence/DatabaseStorage.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR not approvable — The PR adds default-on background compaction and SQLite page reclamation, including deletion of durable superseded history and changed replay retention behavior. Its persistence and runtime lifecycle impact is broader than a small isolated fix, even with targeted tests covering replay and reclamation.

No code changes detected at e5cbab0. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Team
  • Run ID: de2a88b8-da3a-489a-b0cb-7d198becaf40
📥 Commits

Reviewing files that changed from the base of the PR and between cd8a1ef and e5cbab0.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 4a66dcd7-e1c4-4a7d-b91c-f79b653d3222
📥 Commits

Reviewing files that changed from the base of the PR and between cf8be3b and 8f1be1c.

📒 Files selected for processing: 3
  • apps/server/src/orchestration-v2/FoundationPersistence.test.ts
  • apps/server/src/persistence/DatabaseStorage.ts
  • apps/server/src/server.ts

Included review availability: This review used your included allowance. 9 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 10 reviews per hour.

📝 Walkthrough

The server now schedules event-store compaction and incremental SQLite page reclamation. Compaction retention rules preserve selected thread and entity events. Metadata updates use the thread shell when dispatch returns no metadata-updated event.

Changes: Event Compaction and Storage Maintenance

Layer / File(s) Summary
Compaction retention rules
apps/server/src/orchestration-v2/ProjectionMaintenance.ts, apps/server/src/orchestration-v2/FoundationPersistence.test.ts, apps/server/src/orchestration-v2/runtimeLayer.test.ts, apps/server/src/ws.test.ts
Compaction handles read-state events separately, retains the oldest and newest keyed entity updates, and removes obsolete pull-request-link updates. Tests cover retained events, projection rebuilding, and replay after compaction.
Scheduled database maintenance
apps/server/src/persistence/Sqlite.ts, apps/server/src/persistence/DatabaseStorage.ts, apps/server/src/persistence/DatabaseStorage.test.ts, apps/server/src/server.ts
New databases enable incremental auto-vacuum. DatabaseStorage schedules event compaction and page reclamation, and the server provides its layer. The storage test checks page reclamation and retained table data.

Thread Metadata Update Fallback

Layer / File(s) Summary
Select the metadata update result
apps/server/src/mcp/ThreadMetadataMcpService.ts
When dispatch returns no metadata-updated event, the update path retrieves the thread shell. It returns an orchestration error if the selected thread is null.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant DatabaseStorage
  participant ProjectionMaintenance
  participant EventStoreV2
  participant Sqlite
  DatabaseStorage->>ProjectionMaintenance: Run scheduled event-store compaction
  ProjectionMaintenance->>EventStoreV2: Scan and compact events
  DatabaseStorage->>Sqlite: Check auto_vacuum and freelist
  DatabaseStorage->>Sqlite: Reclaim pages incrementally
Loading

Suggested reviewers: t3dotgg

Merge Risk: ⚪ Minimal · up to 8f1be

No actionable merge-blocking issue was established in the reviewed changes. The scheduled maintenance and metadata fallback appear ready to merge after normal checks.

Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Description check Warning The description clearly explains the database-growth problem and the compaction, retention, vacuum, and MCP changes. It does not include the required section structure, explicit maintainer approval or… Add the required Problem, Change, Scope and approval, and Verification sections. Include the triaged issue or approval discussion with explicit maintainer approval, and document the focused tests or manual checks performed with their observ…
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: background compaction of superseded server history.
Linked Issues check Passed #18559 requires a production caller for ProjectionMaintenanceV2.compactEventStore. DatabaseStorage.layer resolves ProjectionMaintenanceV2, waits one minute after startup, and repeats compaction …
Out of Scope Changes check Passed The changes remain connected to #18559. The 50-row batches, page reclamation, incremental auto-vacuum setup, replay coverage, and MCP fallback support safe operation after event deletion and address t…
Full details: Description check

Explanation

The description clearly explains the database-growth problem and the compaction, retention, vacuum, and MCP changes. It does not include the required section structure, explicit maintainer approval or scope rationale, or focused verification results.

Resolution

Add the required Problem, Change, Scope and approval, and Verification sections. Include the triaged issue or approval discussion with explicit maintainer approval, and document the focused tests or manual checks performed with their observed results and any unverified items.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

juliusmarminge and others added 2 commits October 15, 2026 16:54
Superseded orchestration events (whole-thread state, message and node
updates) accumulated for the lifetime of a database; one install reached
114 GB. compactEventStore existed but nothing in the running server
called it, and SQLite never returned the freed pages to the filesystem.

The new DatabaseStorage service runs compactEventStore six-hourly after
startup and hands freed pages back every minute with incremental_vacuum,
one page per step so client requests interleave. New databases are
created with auto_vacuum = INCREMENTAL; existing files keep their mode
until an offline VACUUM converts them. thread.pull-request-link-updated
joins the compaction rules, keyed by the link's host/repository/number.

No replay boundary is published. Whole-thread events keep only the newest
per thread. Keyed entities (messages, nodes, PR links) keep both their
oldest and newest event: client reducers append an entity where they
first see it, so a replay from any cursor either carries that first event
or finds the entity already present, and ordering survives. thread.deleted
is never compacted because clients switch on the event type. The scan
uses 50-row pages so discovery on the shared connection stays short.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge force-pushed the t3/db-compaction-1-background branch from cd8a1ef to e5cbab0 Compare October 15, 2026 23:58
@juliusmarminge
juliusmarminge merged commit 5752253 into main Oct 16, 2026
29 checks passed
@juliusmarminge
juliusmarminge deleted the t3/db-compaction-1-background branch October 16, 2026 00:03
@AdEx-Partners-DE

Copy link
Copy Markdown
Contributor

A Windows data point from a large database, for anyone wondering what this does on an existing install. The fork I run has carried this change since 16 Oct. The numbers below are read-only queries on its statev2.sqlite at 16:55 UTC, compared with a measurement at 00:54 UTC the same day, before the build had it.

The compactor has caught up. By its own rule (keep the oldest and newest event per entity, delete the ones in between), almost nothing is left to delete:

events now still deletable
message.updated 32,863 for 32,773 messages 0
node.updated 133,397 for 78,651 nodes 22
thread.pull-request-synced 6 (1,990 were written in 19 hours on 12/13 Oct, see #18559)

The file did not shrink, as #19436 says it won't. 3,274,887,168 bytes before, 3,299,213,312 now. auto_vacuum is 0, so the background reclaim does nothing here. The free list holds 49,789 pages of 4 KB, about 204 MB or 6% of the file. That is the floor of what t3 doctor would give back on this install; a VACUUM may recover more from half-empty pages. I have not run doctor.

What is left is mostly turn items. Event payload is 1,077 MB, and turn-item.updated is 671 MB of it (174,144 events for 110,243 items). All events except the newest per item come to 63,901 events and 139 MB. The comment in compactEventStore says why they stay (slimmed payloads reference their prompts), so this is not a request to delete them; it only shows where the remaining growth is on an install that runs many long agent turns. By table: events 1,605 MB, projection_turn_items 673 MB, projection_nodes 162 MB.

No stall visible, with one gap. The pass one minute after the 16:20 UTC restart (about 16:21:20) left no SQL span over 300 ms between 16:21:05 and 16:24:40, and no server.eventLoop.stall span. That pass probably had little left to do. The first, large pass ran at an earlier restart whose trace has rotated away, and "Compacted orchestration history" goes to the logger only, so I cannot give the deleted count or the duration of that first pass.

Not checked: other installs, and what doctor would actually reclaim here.

Would a span around each compaction pass (deleted rows and duration as attributes) be welcome, so people with large databases can report the first pass from their trace file?

Posted by Claude (Opus 5.5, Claude Code via T3 Code) on behalf of @AdEx-Partners-DE.

aorwall added a commit to aorwall/t3code that referenced this pull request Oct 19, 2026
Merges `pingdotgg/t3code` `main` at `3962ae8586` (96 commits since
`e536bfd33b`) into the fork. One merge commit; the tracker entry in
`docs/fork/upstream-merge-log.md` has the full decision list.

## Conflicts (8)

- **`ChatView.tsx`** (`converged`): upstream pingdotgg#19869 replaced
`useCallback` with plain functions plus `useStableCallback`. I took
upstream's file and re-applied each fork delta in that style: the
sandbox start for scripts, the draft-thread create and host-run path in
`runProjectScript`, Workspace script saves, the sandbox surface, file
retargeting, the announced preview and terminal effects, the
sandbox-commands banner, the PR-surface gate and the thread-fork dialog.
**This file is the one to review closely.**
- **`rightPanelStore.ts`** / **`FilePreviewPanel.tsx`**: the fork's
`retargetFile` sits beside upstream's new `navigateFolder` and
`folderTrail` (pingdotgg#15045).
- **`ci.yml`**: upstream folded the `typecheck` job into `build`
(pingdotgg#19633). The Moatless spec check now follows `build`'s final
`Typecheck` step.
- **`Sidebar.tsx`, `ProjectSettingsPanel.tsx`**: upstream's code plus
the fork's additions.
- **`index.html`**: kept the Moatless title. Added a `branding-shell`
path-policy entry for it and `branding.ts`.
- **`source-control-github/package.json`**: took upstream's new
`generate` script and kept `tsc --noEmit` (the `tsc-rs` tripwire).
- **`pnpm-lock.yaml`**: took upstream's copy, stripped `tsc-rs`, and
re-derived it with a real install.

Merge stats: 738 files landed against 736 in the upstream range. The one
upstream file that didn't land is `index.html`, whose only upstream
change was the title. The three extra landed files are fork docs. The
fork delta is 815 files.

## Contract
- Added `UnsupportedMethodError` to
`orchestration.subscribeCommandOutput` and `server.getDatabaseStorage`.
Both are new upstream methods Moatless doesn't dispatch.
- Moatless now serves 11 more `pullRequests.*` reads (moatless pingdotgg#1161,
pingdotgg#1164, pingdotgg#1169). They share `PullRequestRpcError` with methods it still
refuses, so the union keeps `UnsupportedMethodError`. The 11 are
declared as `pull-requests-*-shared-union` exceptions in
`inventory.json`, which retire together once the rest of the family is
served.

## Also in this PR
`sandboxSurfaces.test.ts` had been red on `main` since `95dcaca54f` made
the `pull-request` surface sandbox-independent without updating the
test. The test and module doc now match the code.

## Verification
Duplicate-adds, tripwires, resolution-check, unsupported-methods,
lockfile, format, lint, typecheck and the production build are all
green.

The full test pass hit its one-hour deadline, so I ran the remaining
packages one at a time. These still fail when run alone, in the same
files the 2026-10-15 merge logged as sandbox failures:
- `shared`: `AgentScope`
- `t3`: `ServerBrowserPage`, `AcpAdapterV2`
- `provider-opencode`: `OpenCodeServerLedger`
- `ssh`: `tunnel.test.ts`

`t3`'s `OrchestratorReplayFixtures` `claude_result_is_error` fails only
because this sandbox sets `CLAUDE_CONFIG_DIR`; it passes with that
unset. Every other package passes.

## Upstream features

### Usable as-is
- Folder breadcrumbs navigate to parent folders (pingdotgg#15045).
- Chat view, composer and sidebar no longer keep old renders alive
(pingdotgg#19869, pingdotgg#19852).
- Thread list cache saves without re-encoding every thread (pingdotgg#19915).
- React Compiler runs natively (pingdotgg#20114).
- Linked pull requests are virtualized (pingdotgg#20107).
- A linked PR can be dragged into the composer as a context chip
(pingdotgg#20102).
- HTML renders show a skeleton and open images in the image viewer
(pingdotgg#19654, pingdotgg#20036).
- Model picker search lists the newest models first (pingdotgg#18494).
- Wrapped split diffs stop jumping while scrolling (pingdotgg#20022).
- Text selection continues under the turn minimap (pingdotgg#16748).
- Sent queued messages say "from queue" (pingdotgg#19849).
- Settings stay compact with many connected machines (pingdotgg#19744).
- Middle-click and a setting to open PR links on GitHub (pingdotgg#19854,
pingdotgg#19719).
- Text generation no longer falls back to the flagship model (pingdotgg#19722).
- Pending context handoff is hidden on threads with no sent message
(pingdotgg#20033).
- Mobile and desktop changes don't affect the fork's web client.
- Not taken: the Beta branding (pingdotgg#20032).

### Unsupported in Moatless / needs implementation
- **Live command output** (pingdotgg#15094):
`orchestration.subscribeCommandOutput`, shown in
`CommandOutputPanel.tsx` behind `capabilities.commandOutputStreaming`.
Covered by the new union entry.
- **Database storage notice and `t3 doctor` reclaim** (pingdotgg#19436, pingdotgg#19437):
`server.getDatabaseStorage`, shown in `DatabaseStorageNotice.tsx` behind
`capabilities.databaseStorage`. Covered by the new union entry.
- **Delete a branch after its PR merges or closes** (pingdotgg#19257): a
`pullRequests.runAction` option. The action is refused here.
- **Device setup through a remote device host only** (pingdotgg#20077): behind
`FEATURES.deviceHub`.

### Backend behavior to consider reproducing in Moatless
- Resumed Claude subagents keep updating their own thread and pick up
the parent's renamed worktree branch (pingdotgg#20061, pingdotgg#20079).
- Steering no longer ends running subagents (pingdotgg#19210).
- Worktrees shared by subagents are cleaned up, and orphaned ones are
reported (pingdotgg#19748, pingdotgg#19749).
- Superseded history is compacted in the background (pingdotgg#19435).
- A run whose provider stream ends without a terminal event fails
instead of hanging (pingdotgg#19131).
- One malformed ACP session update no longer ends the session (pingdotgg#20015).
- A dropped HTTP/2 connection no longer crashes the server (pingdotgg#18378).
- PR refreshes are bounded by tracker reads (pingdotgg#19403).
- Cursor threads in Auto use Cursor's Auto-review (pingdotgg#19712).
- Grok health checks no longer start its updater (pingdotgg#18075).
- Expanding a wide ignored folder no longer classifies every child
(pingdotgg#20062).
- OTLP exports name the machine and build they came from (pingdotgg#19370).

Recorded in `docs/fork/gaps.md` under "Runtime fixes upstream made to
its own server".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---
Moatless task:
https://moatless.soaplabstest.com/tasks/d6f425f2-8234-412e-8189-1c71b309c04a
bompus added a commit to bompus/swarmail that referenced this pull request Oct 19, 2026
…king (#175)

T3 Code's history compaction (pingdotgg/t3code#19435, nightly and preview
builds since Oct 15) deletes events that a newer event of the same thread
supersedes. When the event the lifecycle reader saved as its anchor was one of
them, every later reconcile threw "source history changed" and lifecycle state
stayed frozen until repaired by hand.

A missing anchor now counts as a deletion when its event id appears nowhere in
the table; the id at another sequence, a different event at the saved sequence
or a lower watermark still fail as a rewrite. event_id is unique in T3's event
tables, so the lookup is indexed and runs only when the anchor is missing.

Docs: orchestration_events is the event table for T3 0.1.0 and later; one
non-GitHub link makes pr-sessions exit 1 for the project; Muse and Pi are
listed with what Swarmail does for them today.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: V2 event-store compaction is implemented but never runs, so statev2.sqlite keeps every superseded and imported-legacy event

2 participants