Skip to content

fix(server): replay all un-applied events during projection bootstrap - #7538

Merged
t3dotgg merged 3 commits into
pingdotgg:mainfrom
krutftw:fix/projection-bootstrap-unbounded-catchup
Aug 27, 2026
Merged

fix(server): replay all un-applied events during projection bootstrap#7538
t3dotgg merged 3 commits into
pingdotgg:mainfrom
krutftw:fix/projection-bootstrap-unbounded-catchup

Conversation

@krutftw

@krutftw krutftw commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

What Changed

apps/server/src/orchestration/Layers/ProjectionPipeline.ts, bootstrapProjector: pass Number.MAX_SAFE_INTEGER as the limit to eventStore.readFromSequence(lastAppliedSequence, …) so projection catch-up on startup replays every un-applied event instead of the default first 1 000. One argument plus a three-line comment; no other behaviour change. The store still reads in READ_PAGE_SIZE (500) pages, so memory stays bounded — this is exactly how the store's own readAll() is defined.

Why

Fixes #7537. readFromSequence's limit defaults to DEFAULT_READ_FROM_SEQUENCE_LIMIT = 1_000, and bootstrap did not pass one. If more than 1 000 events are un-applied at startup (events appended while the server was down — offline t3 project CLI writes, external writers, repeated crash-before-project), only the first 1 000 are projected, projection_state advances to last_applied + 1000, and the next live event's runProjectorForEvent upserts last_applied_sequence to the new max — so the gap is never replayed and those projects/threads/messages are permanently missing from the projections (and from invariants such as requireActiveProjectWorkspaceRootAbsent).

Verified by reading the shipped bundle (0.0.34-nightly.20260819.1132) and main. No existing test encodes the cap (OrchestrationEngine.test.ts stubs readFromSequence: () => Stream.empty, which accepts any arguments); I did not add a new test for the >1 000 case because I could not run the suite from this environment — happy to add one if you want it.

UI Changes

None.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes (n/a)
  • I included a video for animation/interaction changes (n/a)

Authorship disclosure: investigated and prepared by Claude (Fable 5) running in Claude Code on the author's machine, at the author's request.


Note

Medium Risk
Changes orchestration projection bootstrap replay semantics and can increase startup work when large backlogs exist, but scope is limited to catch-up reads and aligns with readAll behavior.

Overview
Fixes projection bootstrap silently stopping after 1,000 un-applied events when readFromSequence used its default limit. bootstrapProjector now passes Number.MAX_SAFE_INTEGER as the read limit so startup catch-up replays the full backlog; paging still uses the event store’s page size, so memory stays bounded.

Adds an integration test that appends 1,001 events behind a lagging projects projector and asserts projection_state advances to the latest sequence after bootstrap.

Reviewed by Cursor Bugbot for commit e84a70e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix projection bootstrap to replay backlog beyond event store default limit

Passes Number.MAX_SAFE_INTEGER as the explicit limit to eventStore.readFromSequence inside the bootstrapProjector helper in ProjectionPipeline.ts, so bootstrap processes the full backlog instead of stopping at the event store's default page size. Adds a test in ProjectionPipeline.test.ts that appends 1,001 project.created events and confirms the projects projector catches up to the last sequence. Risk: reading with no practical limit could load a very large event stream into memory during bootstrap for projectors far behind the head sequence.

📊 Macroscope summarized e84a70e. 1 file reviewed, 1 issue evaluated, 1 issue filtered, 0 comments posted

🗂️ Filtered Issues

apps/server/src/orchestration/Layers/ProjectionPipeline.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 1730: bootstrapProjector passes an effectively unbounded limit to a paginated query with no high-water-mark snapshot. readFromSequence issues another WHERE sequence > cursor LIMIT 500 query whenever a page is nonempty, so an external writer that keeps appending enough events to keep pages nonempty makes the stream never terminate. Since engine construction awaits projectionPipeline.bootstrap before starting its command worker, this can indefinitely prevent server startup; capture the maximum sequence before replay (or otherwise bound the replay) instead of following a moving tail. [ Already posted ]

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e62b12a9-7e5d-464c-b70f-81600247bab6

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

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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Aug 19, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — Projection bootstrap now replays an effectively unbounded event stream before the orchestration server starts, rather than stopping at 1,000 events. Paging limits memory, but concurrent appends could prolong startup indefinitely, so the runtime availability impact should receive human review.

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

@macroscopeapp
macroscopeapp Bot dismissed their stale review August 27, 2026 07:34

Dismissing prior approval to re-evaluate c777f44

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Effect service conventions: the service definition, imports, layer, and error modeling in this change are consistent with the conventions. One change-discipline gap: this is a backend behavior change to projection bootstrap with no accompanying focused test.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/orchestration/Layers/ProjectionPipeline.ts
krutftw and others added 2 commits August 27, 2026 00:37
bootstrapProjector called eventStore.readFromSequence(lastApplied) without a limit,
so the default 1 000-event cap applied and any further backlog was never projected
(and became permanently skipped once the engine appended its next event).
Pass Number.MAX_SAFE_INTEGER; the store still pages in 500-row chunks.

Fixes pingdotgg#7537
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 27, 2026
@t3dotgg
t3dotgg force-pushed the fix/projection-bootstrap-unbounded-catchup branch from c777f44 to e84a70e Compare August 27, 2026 07:41
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 27, 2026 07:41

Dismissing prior approval to re-evaluate e84a70e

@t3dotgg t3dotgg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

Approved. makeOrchestrationEngine waits for projectionPipeline.bootstrap before it starts the command worker, so the supported server cannot append during bootstrap. The event store still reads 500 rows per page.

@t3dotgg
t3dotgg merged commit a6797b3 into pingdotgg:main Aug 27, 2026
21 checks passed
frankdavidcorona pushed a commit to frankdavidcorona/t3code that referenced this pull request Aug 27, 2026
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 29, 2026
## What's Changed
* fix(grok): improve skills, plans, usage, and turn reliability by @t3dotgg in pingdotgg/t3code#8358
* fix(server): recover stale Codex approval callbacks by @luckyPipewrench in pingdotgg/t3code#5195
* test(server): remove duplicate missing worktree test by @t3-code[bot] in pingdotgg/t3code#8252
* fix(server): replay all un-applied events during projection bootstrap by @krutftw in pingdotgg/t3code#7538
* test: remove low-signal test files by @t3-code[bot] in pingdotgg/t3code#8397
* test: prune trivial error and layout tests by @t3-code[bot] in pingdotgg/t3code#8400
* Fix Android adaptive launcher icon by @colonelpanic8 in pingdotgg/t3code#4332
* feat(web): split provider settings into list and editor by @t3dotgg in pingdotgg/t3code#8380
* fix(codex): accept Codex 0.150 account plans by @gsimone in pingdotgg/t3code#8447
* fix(tooling): allow ignored-only staged changes by @juliusmarminge in pingdotgg/t3code#8468
* fix(mobile): keep iOS home header stable by @juliusmarminge in pingdotgg/t3code#8467
* fix(web): stop showing red x summaries for ordinary tool failures by @t3dotgg in pingdotgg/t3code#8395
* fix(mobile): refine Git action toast glass styling by @juliusmarminge in pingdotgg/t3code#8399
* fix(desktop): allow preview automation in agent-created threads by @t3dotgg in pingdotgg/t3code#8483
* test(web): remove redundant cache key test by @t3-code[bot] in pingdotgg/t3code#8484
* fix(release): move nightly schedule to minute 38 by @t3dotgg in pingdotgg/t3code#8509
* fix(web): stabilize the provider settings editor by @t3dotgg in pingdotgg/t3code#8472
* fix(web): open GitHub pull requests in browser when loading fails by @t3dotgg in pingdotgg/t3code#8507
* fix(codex): show sub-agent models by @t3dotgg in pingdotgg/t3code#8502
* feat(analytics): report connected client platforms by @t3dotgg in pingdotgg/t3code#8481
* feat(server): accept PDF, ZIP, and other file uploads up to 50MB by @t3dotgg in pingdotgg/t3code#8235
* feat(web): toggle a thread's pin from the keyboard by @ipanasenko in pingdotgg/t3code#8440
* fix(web): add back button to project settings by @StiensWout in pingdotgg/t3code#8168
* refactor(mobile): compile semantic themes for Uniwind by @juliusmarminge in pingdotgg/t3code#7327
* fix(desktop): Cache Runtime locally on WSL Filesystem, dramatically improving launch times  by @ikifar2012 in pingdotgg/t3code#5769
* fix(mobile): show OpenCode model sources in picker by @juliusmarminge in pingdotgg/t3code#8573
* fix(clients): honor project default models in new threads by @anirudhsama in pingdotgg/t3code#6011
* fix(mobile): show file actions on Android by @none23 in pingdotgg/t3code#8215
* fix(connect): explain DPoP connection failures by @extoci in pingdotgg/t3code#8351
* feat(web): make the sidebar project filter a searchable combobox by @SunkenInTime in pingdotgg/t3code#5931
* fix(server): a draft can retry its first send after a failed bootstrap by @shivamhwp in pingdotgg/t3code#8226
* fix(desktop): stop hidden previews draining battery by @Bil0000 in pingdotgg/t3code#8567
* fix(desktop): oauth popups open from the browser preview by @walid-baharwal in pingdotgg/t3code#8435
* fix(web): keep long task drawers usable on small screens by @shivamhwp in pingdotgg/t3code#8313
* fix(opencode): handle child approvals, stops, and model catalogs by @t3dotgg in pingdotgg/t3code#8480
* fix: make thread auto-settling opt-in by @shivamhwp in pingdotgg/t3code#8321
* fix(web): stop session activity timing test from blocking releases by @t3dotgg in pingdotgg/t3code#8585
* fix(mobile): show composer menus when starting a task by @juliusmarminge in pingdotgg/t3code#8587
* fix(web): show the configured stash shortcut by @UtkarshUsername in pingdotgg/t3code#8437
* feat(web): add toggleable confirmation before unpinning a thread by @UtkarshUsername in pingdotgg/t3code#7313
* fix: restore automatic thread settling defaults by @t3dotgg in pingdotgg/t3code#8596
* fix(mobile): restore composer glass and rounded shadows by @juliusmarminge in pingdotgg/t3code#8597

## New Contributors
* @luckyPipewrench made their first contribution in pingdotgg/t3code#5195
* @krutftw made their first contribution in pingdotgg/t3code#7538
* @colonelpanic8 made their first contribution in pingdotgg/t3code#4332
* @ikifar2012 made their first contribution in pingdotgg/t3code#5769
* @walid-baharwal made their first contribution in pingdotgg/t3code#8435

**Full Changelog**: pingdotgg/t3code@v0.0.35...v0.0.36

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.36
aorwall added a commit to aorwall/t3code that referenced this pull request Aug 29, 2026
Merges 58 upstream commits (`badae6a5c` → `6a9d9f988`, upstream v0.0.34
→ v0.0.36) through the `fork-upstream-merge` skill.

`merge-stats.mjs` reconciles exactly: **431 files landed** against **431
in the upstream range**, no gap to explain. Fork delta is 611 files.

## Conflicts

Three, plus one git resolved silently and wrongly.

- **`Sidebar.tsx`** — upstream replaced the project picker's
`Menu`/`MenuRadioGroup` with a searchable `Combobox` (pingdotgg#5931). Took
upstream's structure and re-applied only the
`FEATURES.projectManagement` gate on "New project", now carrying the `//
Fork:` marker it had been missing. Upstream has since grown the mobile
touch-target span itself, so the Mobile Touch Delta has nothing left to
re-apply here — one convergence, unprompted.
- **`ProviderSettingsPanel.tsx`** — upstream split provider settings
into list and editor (pingdotgg#8380, pingdotgg#8472), moving `ProviderLastChecked` and
the refresh button out of `headerAction` into the list footer. Took
upstream's and re-applied the gate as `!readOnly &&
FEATURES.serverAdministration`.
- **`pnpm-lock.yaml`** — took upstream's and re-ran `vp i`, which
restored the fork's `@t3tools/moatless-api` workspace edge.
- **`packages/contracts/src/orchestration.test.ts`** — auto-merged, no
marker, broken. Both sides appended `OrchestrationMessage` to the same
import list and the same `decodeOrchestrationMessage` const at different
offsets, so git took both. Surfaced as a parse error in lint, typecheck
and test at once. This is the case `merge-stats.mjs`'s
conflict-candidate list exists to catch.

`HostedBrowserFrame.tsx` (fork-only) also needed the `renderingActive`
prop pingdotgg#8567 made required. Upstream suspends a parked webview unless
background audio, PiP or a recording still needs it painted; a frame has
none of those to read and is the app's only copy of the preview page, so
it passes `true` and keeps today's behavior.

## Sweep

Five keyword hits, all false positives:

- `apps/web/src/connection/clientMetadata.ts` + test — reports the
client's OS/browser/device on connect (pingdotgg#8481). Auth-adjacent, but it
rides `ClientPresentation` on the relay and remote-bearer bootstraps;
the fork's primary environment sends none of it.
- `packages/client-runtime/src/relay/errorPresentation.ts` + test,
`connection/errors.test.ts` — explain DPoP failures (pingdotgg#8351). Relay only,
and T3 Connect is decided out.

## Feature classification

**Usable as-is**

- Searchable project-filter combobox in the sidebar (pingdotgg#5931).
- Long task drawers stay usable on small screens (pingdotgg#8313) — directly
relevant, the fork's phone story is `apps/web` in mobile Safari/Chrome.
- Toggleable confirmation before unpinning a thread (pingdotgg#7313); toggle
thread pin from the keyboard.
- Back button in project settings (pingdotgg#8168); the configured stash shortcut
is shown (pingdotgg#8437).
- No more red-x summaries for ordinary tool failures (pingdotgg#8395); PRs open
in the browser when loading fails (pingdotgg#8507).
- Project default models are honored in new threads (pingdotgg#6011).
- Provider settings split into list and editor (pingdotgg#8380, pingdotgg#8472) — landed,
though `/settings/providers` is itself gated behind
`serverAdministration`.

**Unsupported in Moatless / needs implementation**

- **Non-image file attachments** (pingdotgg#8235) — a turn may now carry any file
up to 50MB, advertised as `capabilities.fileAttachments.maxUploadBytes`
and sized by `PROVIDER_SEND_TURN_MAX_FILE_BYTES`. Moatless advertises
neither this nor `attachmentUploads`, so the composer's attach
affordance stays off. Costs nothing today (upstream's own web composer
offers images only; `ChatAttachment` widened just far enough to
typecheck), but it is the second capability key to report when uploads
land. Extends the existing **Attachment uploads** entry in
`docs/fork/gaps.md`.
- **Connected-client platform analytics** (pingdotgg#8481) and **DPoP failure
explanations** (pingdotgg#8351) — relay and T3 Connect surfaces, already decided
out of the fork.

No new WS methods entered the contract in this range, so no new
`UnsupportedMethodError` union entries. `unsupported-methods.mjs`
reports `ADD 0`, `DROP 1` (`scripts.run`), `KEEP 2`. The `scripts.run`
DROP is **not** actioned — it is the documented exception in the gaps
register: that union entry answers for `apps/server`, which still stubs
the method (verified surviving in `ws.ts`), not for Moatless.

**Backend behavior to consider reproducing in Moatless**

- **Replay all un-applied events during projection bootstrap** (pingdotgg#7538) —
a one-line pipeline fix upstream; Moatless runs its own projection and
the same class of bug applies.
- **A draft can retry its first send after a failed bootstrap** (pingdotgg#8226)
— Moatless owns turn start, and a draft wedged by a failed bootstrap is
the same dead end here.
- **Recover stale Codex approval callbacks** (pingdotgg#5195) — Moatless owns the
agent runtime in the sandbox.
- **Stop querying Claude context usage after turns** (pingdotgg#8610) — a cheap
correctness/cost fix in the same place Moatless polls.
- **Server-side accept and size-limit non-image uploads** (pingdotgg#8235), the
other half of the gap above.
- Auto-settling churn (pingdotgg#8321 made it opt-in, pingdotgg#8596 reverted) nets to no
change — noted so the next merge does not re-derive it.

## Verification

`verify.mjs`: `tripwires`, `fmt:check`, `lint`, `typecheck` pass. `test`
reports `@t3tools/web` **flaky, passed alone** — in the full run
`MessagesTimeline.test.tsx` skipped all 34 tests on a 30s module-import
timeout under `ChatMarkdown.tsx`; alone the package is 297 files / 3117
tests green. Load, not the merge. `inventory-check.mjs` clean.

Model: Claude Opus 5, via Claude Code in a Moatless sandbox.

---
Moatless task:
https://moatless.soaplabstest.com/tasks/a0c041bb-1426-4591-9296-6a4b0cfa2eff
sandscooling pushed a commit to sandscooling/t3code that referenced this pull request Aug 29, 2026
Brings in 192 upstream commits through v0.0.36 (053affb).

Most of the 52 conflict hunks were additive: the fork's thread `group`
landing on the same lines as upstream's new `linkedPullRequest`. Both sides
kept.

Real tie-breaks, all resolved for upstream:
- Projector bootstrap paging dropped for upstream pingdotgg#7538, which drains the
  whole stream in one read. BOOTSTRAP_PAGE_SIZE went with it.
- Claude `getContextUsage` dropped for upstream pingdotgg#8610. The fork's plan usage
  meter sat next to it and stays; only the context-usage pull is gone.
- The Windows logs-directory assertion in server.test.ts dropped for
  upstream's path.basename form, which handles both separators anyway.

The fork's thread group migration moves from 042 to 044, above upstream's new
042 and 043. The migration runner skips any id at or below the highest one
already recorded, so a fork migration parked on an id upstream later reuses
masks upstream's silently. Existing databases that recorded 42 as
ProjectionThreadsGroupKey need that row dropped once so upstream's 042 and 043
can run; all three migrations are idempotent.

Upstream also stopped passing the raw activity list into the composer, so the
plan usage meter now takes a derived snapshot prop like the context window
does, computed in ChatView.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

2 participants