Skip to content

feat(ingest): board reconciler startup sweep + slow ticker + watermark (RIG-2883) - #708

Open
rigel-mintaka wants to merge 2 commits into
mainfrom
compass-forge/rig-2883-t3-reconciler
Open

feat(ingest): board reconciler startup sweep + slow ticker + watermark (RIG-2883)#708
rigel-mintaka wants to merge 2 commits into
mainfrom
compass-forge/rig-2883-t3-reconciler

Conversation

@rigel-mintaka

Copy link
Copy Markdown
Contributor

Adds BoardReconciler (T3): the forge board-ingestion reliability backstop and the cold-start/backfill path, mirroring NotifyReconciler at repo-LIST granularity.

Per sweep it enumerates enabled repos (BoardStore.ListEnabledRepos), conditionally lists each repo's updated-order issues since its stored watermark (updatedLister.ListUpdatedIssues, structurally satisfied by T2's GitHub.ListUpdatedIssues at T5), sinks the rows through the shared Ingester, then advances the durable watermark to max(UpdatedAt) + a fresh ETag AFTER the rows sank (advance-after-sink). A zero/absent watermark lists everything — the cold-start and App-reinstall backfill answer, no separate mechanism.

Run fires one immediate sweep then a sweep per Backstop tick, returning nil on ctx cancel. Per-repo errors are isolated; ErrBudgetExhausted aborts the sweep, resumed next tick.

Poison-row livelock is bounded: rows sink one at a time so a persistently-rejected row is skipped-and-counted while the rest sink and the watermark advances past the healthy rows (dropping the ETag so the next sweep re-lists unconditionally) — the re-walk window MUST NOT grow without bound behind a pinned watermark.

BoardStore, updatedLister, and Ingester are the only seams; the package imports no *store.Store or concrete *forge.GitHub (wired at T5). Tests are synctest-driven for the time-dependent Run/ticker cases.

Refs RIG-2883
Co-authored-by: Matt Wilkinson matt@rigel.build

…k (RIG-2883)

Adds `BoardReconciler` (T3): the forge board-ingestion reliability backstop and the cold-start/backfill path, mirroring `NotifyReconciler` at repo-LIST granularity.

Per sweep it enumerates enabled repos (`BoardStore.ListEnabledRepos`), conditionally lists each repo's updated-order issues since its stored watermark (`updatedLister.ListUpdatedIssues`, structurally satisfied by T2's `GitHub.ListUpdatedIssues` at T5), sinks the rows through the shared `Ingester`, then advances the durable watermark to `max(UpdatedAt)` + a fresh ETag AFTER the rows sank (advance-after-sink). A zero/absent watermark lists everything — the cold-start and App-reinstall backfill answer, no separate mechanism.

`Run` fires one immediate sweep then a sweep per `Backstop` tick, returning nil on ctx cancel. Per-repo errors are isolated; `ErrBudgetExhausted` aborts the sweep, resumed next tick.

Poison-row livelock is bounded: rows sink one at a time so a persistently-rejected row is skipped-and-counted while the rest sink and the watermark advances past the healthy rows (dropping the ETag so the next sweep re-lists unconditionally) — the re-walk window MUST NOT grow without bound behind a pinned watermark.

`BoardStore`, `updatedLister`, and `Ingester` are the only seams; the package imports no `*store.Store` or concrete `*forge.GitHub` (wired at T5). Tests are `synctest`-driven for the time-dependent Run/ticker cases.

Refs RIG-2883
Co-authored-by: Matt Wilkinson <matt@rigel.build>
@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown

RIG-2883

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-forge-rig-2883-t3-re.compass-eng-docs.pages.dev

Deployed from compass-forge/rig-2883-t3-reconciler at 237127e.

…ff note (RIG-2883 T3)

Adds the multi-sweep invariant the single-sweep poison test only implied:
a persistently-failing NEWEST row is retried every sweep, the re-walk
window stays bounded (does not grow sweep over sweep), and the watermark
floor holds at the healthy max. Also documents the deliberate tradeoff
that a transient failure co-batched with a newer healthy row is
dropped-until-next-update, so a future maintainer does not "fix" it back
into the poison-pin livelock the isolation prevents.

Refs RIG-2883
Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka marked this pull request as ready for review August 28, 2026 05:41
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