Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
278 commits
Select commit Hold shift + click to select a range
4777399
fix: offer preview-tier self-service sources on /sources/add
tnunamak Aug 18, 2026
87a355d
fix: keep the real message when a blob upload fails
tnunamak Aug 18, 2026
6585777
fix: never show the owner a read failure on /sources
tnunamak Aug 18, 2026
7d089a3
fix: say when coverage is unknown because the collector is out of date
tnunamak Aug 18, 2026
ec11a01
fix: stop showing read failures across the rest of the console
tnunamak Aug 18, 2026
275ab48
fix: keep the reason code when a session failure is redacted
tnunamak Aug 18, 2026
ba91356
docs: commit six design notes that were living only on one disk
tnunamak Aug 18, 2026
a38ceed
fix(usaa): recover served credit-card gaps, and capture what the rest…
tnunamak Aug 18, 2026
55d8b80
fix: stop ChatGPT demanding a login it never needed, and say why a ru…
tnunamak Aug 18, 2026
35f9bb9
fix: fail the build when a package imports something it never declared
tnunamak Aug 18, 2026
488ae63
fix(reddit): stop missing an OTP prompt that renders a beat too late
tnunamak Aug 18, 2026
e0622e9
fix: stop asking the owner to reconnect connections he deleted
tnunamak Aug 18, 2026
b40cba6
docs: the wider failure class is the one nothing reports
tnunamak Aug 18, 2026
2a90dad
docs: a third variant -- reported correctly, in a field nobody read
tnunamak Aug 18, 2026
aa8019f
fix: index spine_events by instance for every event type, not just te…
tnunamak Aug 18, 2026
56f0284
feat: a finished import says so instead of reading as unmeasured
tnunamak Aug 18, 2026
77544c6
fix: three stale expectations, one of them hiding a real time-scope trap
tnunamak Aug 18, 2026
428898c
fix: tell the owner what happened to his data, not whose code is broken
tnunamak Aug 18, 2026
0987bd1
feat(venmo): list at preview so the owner can actually add it
tnunamak Aug 18, 2026
c65ce3f
fix: stop destroying browser sessions on every restart
tnunamak Aug 18, 2026
d93e4e4
fix: let a zero-history row escape a verdict it can never disprove
tnunamak Aug 18, 2026
501c280
fix(reddit): look more than once after the owner finishes a manual login
tnunamak Aug 18, 2026
13cd8e4
fix: one stale gap row should not read as an unreadable exporter
tnunamak Aug 18, 2026
4aeb308
docs: a finished import has no way to prove what it ingested
tnunamak Aug 18, 2026
1229396
fix: let a connector declare which tokens survive redaction
tnunamak Aug 18, 2026
9a1511e
feat: let a stream prove its remaining gaps are impossible, not merel…
tnunamak Aug 18, 2026
07d0cf7
fix(venmo): fail loudly when the page never reached venmo.com
tnunamak Aug 19, 2026
2815b1e
fix: let a terminal gap be reopened when its reason has been fixed
tnunamak Aug 19, 2026
7b105ee
fix: let the sources list see the proof the detail view already computes
tnunamak Aug 19, 2026
a9e8585
fix(gmail): keep the messages continuation on the same page as its fact
tnunamak Aug 19, 2026
fc42630
fix: stop calling a proven-accounted source uncollectable
tnunamak Aug 19, 2026
fcc867d
fix: index the canonical count the repair sweep reads every pass
tnunamak Aug 19, 2026
5274dbd
fix: make a touch tap on a remote page actually click
tnunamak Aug 19, 2026
0aa3da4
fix: accept the declared reason tokens the caller already passes
tnunamak Aug 19, 2026
81a40fd
test: prove the requeue reason refusal happens before any database se…
tnunamak Aug 19, 2026
126be97
fix(reddit): bound the session probe so a silent server can't wedge a…
tnunamak Aug 19, 2026
40548a8
fix(venmo): probe the API the collector uses, not a page route that e…
tnunamak Aug 19, 2026
816bba8
fix(google-maps): declare the import strategy the streams actually use
tnunamak Aug 19, 2026
fcf6dde
fix(heb): stop demanding a code from a page that only mentions one
tnunamak Aug 19, 2026
ae45d4d
fix(chase): require a real code input before asking the owner for a code
tnunamak Aug 19, 2026
bf9587a
fix(venmo): a disabled code box is not evidence a code was sent
tnunamak Aug 19, 2026
e9e46e5
fix(usaa,chase): never click send on a code the page cannot prove it …
tnunamak Aug 19, 2026
2618707
fix: make a maintainer action unrepresentable in a grant-scoped verdict
tnunamak Aug 19, 2026
9f2abb6
fix: stop telling the owner we hold zero of their data
tnunamak Aug 19, 2026
0c4ed3c
fix(venmo): accept the host Venmo actually lands on after a successfu…
tnunamak Aug 19, 2026
2fcbb68
fix(apple-contacts): stop reporting a zero the run never measured
tnunamak Aug 19, 2026
eba9b56
fix(gmail): reopen the UID band no cursor was ever going to walk
tnunamak Aug 19, 2026
6af4257
feat: detect the three ways a cursor silently excludes data
tnunamak Aug 19, 2026
4bea6e2
fix(reddit): establish the origin instead of guessing which host answers
tnunamak Aug 19, 2026
4843e46
fix(venmo): type into the field the login page actually ships
tnunamak Aug 19, 2026
7c83dd6
fix(gmail): tie the backfill ceiling to the forward floor so the band…
tnunamak Aug 19, 2026
2fbfb62
fix(apple-contacts): keep the members iCloud enumerates but does not …
tnunamak Aug 19, 2026
4c34e7d
fix: retry the ingest 503 the server has been asking us to retry
tnunamak Aug 19, 2026
355896a
fix(collector): resolve the tsx binary instead of trusting PATH
tnunamak Aug 19, 2026
74582ac
fix(signal): regenerate the first-party connector registry
tnunamak Aug 17, 2026
9b19cc1
fix(gmail): record why an attachment failed, after 117 silent attempts
tnunamak Aug 19, 2026
3eeffd8
feat: give a failed run a log line carrying its own run id
tnunamak Aug 18, 2026
c931038
feat: show the owner the connectors he cannot currently see
tnunamak Aug 18, 2026
1ed79d2
test(health): lock in the terminal_facts_historical generation fence
tnunamak Aug 19, 2026
e5ad7a2
fix(health): stop treating in-image-browser runs as remote_surface un…
tnunamak Aug 18, 2026
7743ae9
fix(health): guarantee at least one dirty-backlog repair per maintena…
tnunamak Aug 18, 2026
9475f0d
fix: rotate the dirty cursor by last attempted id, not last fetched
tnunamak Aug 18, 2026
616d706
fix: give sweep queries a real per-unit bound, and name the deadline …
tnunamak Aug 18, 2026
82cd757
feat: alert when the maintenance sweep stops making durable progress
tnunamak Aug 18, 2026
40138d1
fix: a cancelled sweep query must not condemn the evidence it was rea…
tnunamak Aug 18, 2026
ed73dc0
fix: one slow discovery query must not abort the whole batch
tnunamak Aug 18, 2026
23b16ff
fix: delete the fleet-wide record count the sweep no longer needed
tnunamak Aug 18, 2026
b676b68
test: realign assertions with shipped read-model and fold contract
tnunamak Aug 19, 2026
d8165d2
fix(sources): hide pure recovered historical fragments from owner Sou…
tnunamak Aug 15, 2026
2c86036
fix(sources): bind sources_visibility into the connector-summary curs…
tnunamak Aug 15, 2026
ab8856c
fix(sources): preserve legacy summary cursors
tnunamak Aug 15, 2026
5704a24
feat(connectors): add reversible connector-instance grouping for acco…
tnunamak Aug 16, 2026
1f5f927
feat(connectors): wire connector-instance canonicalization into every…
tnunamak Aug 16, 2026
87d91c8
fix(conformance): sanction the migration script's operator-supplied m…
tnunamak Aug 16, 2026
a747120
fix(connectors): refuse to group a fragment whose own status is 'active'
tnunamak Aug 16, 2026
c0c6dfc
fix(connectors): allow paused connections to resume via credential edit
tnunamak Aug 16, 2026
c713643
fix(mcp-server): narrow the two undefined index reads the contract ch…
tnunamak Aug 19, 2026
cfe7f51
test(ri): stop shipping credential-shaped literals GitHub blocks the …
tnunamak Aug 20, 2026
3ba8652
chore(connectors): reattach the apple_contacts await allowlist to its…
tnunamak Aug 20, 2026
031aa67
feat(gmail): bind the mailbox count the SELECT already returned
tnunamak Aug 20, 2026
e178c16
feat(chatgpt): reconcile the conversation graph instead of its own me…
tnunamak Aug 20, 2026
2ca7550
fix(amazon): stop asserting zero items for an order we never opened
tnunamak Aug 20, 2026
f0e20f9
fix(reddit): stop halting the action-ordered listings on the first ol…
tnunamak Aug 20, 2026
ced6ab7
fix(steam): read the totals Steam already sends
tnunamak Aug 20, 2026
77a4d78
fix(whatsapp): stop counting dropped media as covered
tnunamak Aug 20, 2026
f0f3255
feat(slack): use slackdump's own end-of-pagination marker as the anchor
tnunamak Aug 20, 2026
5bc3612
fix(groupme): read the message count where GroupMe actually puts it
tnunamak Aug 20, 2026
ffe4a5a
fix(signal): make a re-link backfill visible to the forward-only cursor
tnunamak Aug 20, 2026
25c6ceb
fix(claude_code): gate the markdown streams on content, and fail clos…
tnunamak Aug 20, 2026
d497a9a
fix(codex): fail closed on an unreadable skills directory, like its s…
tnunamak Aug 20, 2026
679259e
fix(notion): tally covered per record instead of forcing it equal to …
tnunamak Aug 20, 2026
e1b92b3
fix(google_maps): count the artifact elements that never became records
tnunamak Aug 20, 2026
8017837
docs(github): record why no provider total is bound here
tnunamak Aug 20, 2026
7760b7a
feat(usaa): check the transactions against the balance USAA printed
tnunamak Aug 20, 2026
855e6f1
fix(apple-contacts): see the groups iCloud stores as their own vCards
tnunamak Aug 20, 2026
f32dbfe
fix(chase,heb): stop a stale cursor pinning the export, and check HEB…
tnunamak Aug 20, 2026
39531b3
fix(connectors): repoint four signal/conformance imports at the vendo…
tnunamak Aug 20, 2026
d0b5763
fix(connectors): realign the noAwaitInLoops allowlist with the rebase…
tnunamak Aug 20, 2026
cfafa99
test: repair two fixtures this branch's own changes invalidated
tnunamak Aug 20, 2026
0cd898a
fix: say when coverage is unknown because the collector is out of date
tnunamak Aug 18, 2026
1e8cc04
Merge remote-tracking branch 'origin/main' into combined
tnunamak Aug 20, 2026
83fca78
fix(console): derive enrollment-form connector check from the bundle …
tnunamak Aug 20, 2026
ee204f1
fix(connectors): satisfy the repo biome pass on branch-touched files
tnunamak Aug 20, 2026
5a9bce3
chore(docs): regenerate the stream evidence inventory
tnunamak Aug 20, 2026
7d46440
fix(connectors): give this branch's new gap evidence vetted owner copy
tnunamak Aug 20, 2026
23d8f5c
fix(sweep): scope the recovery-continuation throttle to its own contr…
tnunamak Aug 20, 2026
5434d29
fix(connectors): drop the now-stale timeline_points coverage entry
tnunamak Aug 20, 2026
2051d75
fix(ri): stop two guards misreading this branch's own improvements
tnunamak Aug 20, 2026
c322aa4
test(ri): use spotify as the development-tier UAT allowlist fixture
tnunamak Aug 20, 2026
d8bec3c
chore(docs): regenerate the stream evidence inventory for timeline_po…
tnunamak Aug 20, 2026
6ad0248
fix: give 429 and 500 back to the layers that own them
tnunamak Aug 20, 2026
39f3cad
fix: move two connector facts out of the RI and into the manifests th…
tnunamak Aug 20, 2026
2e85e1d
chore: defer the cursor-provenance audit to its own change
tnunamak Aug 21, 2026
ba99867
fix(heb): recognize the block page Imperva serves without an iframe
tnunamak Aug 20, 2026
a1d3709
fix(health): a file import has no credentials to verify
tnunamak Aug 21, 2026
dbd0f0e
docs: the import coverage receipt does not need building
tnunamak Aug 21, 2026
1b2ef70
fix(device-ingest): let a fully durable batch settle past its deadline
tnunamak Aug 21, 2026
602dbde
test(accounting): register the two new device-ingest Postgres skips
tnunamak Aug 21, 2026
3f44947
fix(refresh-policy): derive refresh mode from declared facts
tnunamak Aug 21, 2026
7115c8d
fix(health): a generation-fenced projection needs a run, not a wait
tnunamak Aug 21, 2026
87e9398
test: retire the "manual but background-safe" connector category
tnunamak Aug 21, 2026
c27e73a
fix(slack): separate never-in-scope channels from unproven history
tnunamak Aug 21, 2026
4f26544
fix(groupme): report messages the provider counts but will not serve
tnunamak Aug 21, 2026
b64b902
test(accounting): track the new skip baseline in the inventory gate
tnunamak Aug 21, 2026
3a83ae4
chore(connectors): realign sequential-await allowlist after the walk …
tnunamak Aug 21, 2026
1609ec7
test(neko): pin Amazon and Reddit to the managed surface, not a mode …
tnunamak Aug 21, 2026
c3aff8e
fix(gmail): stop the delta pass blanking collected message envelopes
tnunamak Aug 21, 2026
f101b3c
feat(repair): restore Gmail envelopes the delta pass blanked
tnunamak Aug 21, 2026
d7960cf
chore(connectors): realign the gmail sequential-await allowlist
tnunamak Aug 21, 2026
14d5c7b
fix(venmo): stop typing the password into the step-one sign-in form
tnunamak Aug 21, 2026
1bf3f6c
fix(gmail): size attachments per part, and withhold unproven coverage
tnunamak Aug 21, 2026
cfe7380
fix(gmail): stop message_bodies claiming coverage it cannot prove
tnunamak Aug 21, 2026
efc601b
fix(heb): treat H-E-B's own "No past orders" page as a proven-empty r…
tnunamak Aug 21, 2026
cb094e6
fix(slack): stop reactions/message_attachments claiming coverage they…
tnunamak Aug 21, 2026
aa669ba
test(connectors): fail the fleet when a state_stream child claims DET…
tnunamak Aug 21, 2026
5f84e71
fix(ri): name the missing import directory instead of failing silently
tnunamak Aug 21, 2026
3e22910
feat(ri): make connection pause and resume real owner actions
tnunamak Aug 21, 2026
d062450
fix(test): hoist the resume hook out of the spread-ternary
tnunamak Aug 21, 2026
663d6ef
fix(ri): survive the vendored undici parser assertion
tnunamak Aug 21, 2026
9e41bf9
fix(ri): drain RS state responses the runtime discards
tnunamak Aug 21, 2026
96dbe82
fix(ri): name the runs abandoned while awaiting owner input
tnunamak Aug 21, 2026
ea3f588
test(ri): induce the real undici fault, not a synthesized one
tnunamak Aug 21, 2026
48e26c9
fix(apple_contacts): honor the full-refresh bypass so coverage can be…
tnunamak Aug 21, 2026
cbabd90
feat(runs): let the owner ask for a full re-enumeration to prove cove…
tnunamak Aug 21, 2026
bfcb265
feat(device-exporters): let an owner bind a new device to an existing…
tnunamak Aug 21, 2026
9f9d08b
fix(land): reconcile full_refresh with the landed pause-resume tests
tnunamak Aug 21, 2026
f945f28
fix(controller): persist controller identity so a successor can adjud…
tnunamak Aug 21, 2026
d984e4a
feat(repair): adjudicate runs stranded under dead controller ids
tnunamak Aug 21, 2026
51ef868
refactor(controller): stop recording interrupted runs as failures
tnunamak Aug 21, 2026
2ddcca1
perf(shutdown): drop the connector drain from the SIGTERM path
tnunamak Aug 21, 2026
2bd11b7
fix(manual-upload): fence in-flight artifacts by owner epoch, not a 1…
tnunamak Aug 21, 2026
f2705c6
docs(openspec): specify owner-epoch adjudication of interrupted runs
tnunamak Aug 21, 2026
612b843
fix(land): reconcile the restart-orphan test with the awaiting-intera…
tnunamak Aug 21, 2026
bf19efd
fix(test): attribute the Postgres-gated repair skips
tnunamak Aug 21, 2026
b330506
fix(test): type the Postgres-gated repair skip against the real overload
tnunamak Aug 21, 2026
fca8e68
fix(health): invalidate terminal facts on declaration change, not any…
tnunamak Aug 21, 2026
6d62344
fix(health): derive projection remediation from the reason, not the c…
tnunamak Aug 21, 2026
fd05925
fix(backup): classify controller_identity as backup_required
tnunamak Aug 21, 2026
39d1970
fix(gmail): stop emitting records after DONE
tnunamak Aug 21, 2026
ca748a4
fix(test): hold the scheduler conformance harness to the no-failure c…
tnunamak Aug 21, 2026
a8f688e
fix(privacy): port main's email genericization onto this branch
tnunamak Aug 21, 2026
fc5a3c9
fix(privacy): purge remaining real addresses and gate against reintro…
tnunamak Aug 21, 2026
6a8adc1
fix(test): make the restart-reconciliation conformance scenario exerc…
tnunamak Aug 21, 2026
c05042d
fix(health): stop grouping every spine event to read 13 numbers
tnunamak Aug 21, 2026
223f251
feat(canary): add pre-registered deploy verification harness
tnunamak Aug 21, 2026
fea7629
spec(checkpoint): frame the claim contract as a qualification standard
tnunamak Aug 21, 2026
1f950ce
spec(run-lifecycle): own run state as a fenced state machine
tnunamak Aug 21, 2026
abedb64
fix(slack): advance message cursors on emission, not iteration
tnunamak Aug 21, 2026
4881dbc
fix(heb): tell the owner what H-E-B is actually showing
tnunamak Aug 21, 2026
ea3e4e5
fix(slack): finish the archive enumeration instead of resuming it for…
tnunamak Aug 21, 2026
1ec7e6a
fix(credentials): make stored credentials the only sign-in path
tnunamak Aug 21, 2026
6c56c6d
fix(canary): reconcile the deploy harness with the zero-knowledge guard
tnunamak Aug 21, 2026
3728701
fix(auto-login): return explicitly when no credential field carries a…
tnunamak Aug 21, 2026
07c5b43
fix(auto-login): settle firstNonEmpty so both gates pass
tnunamak Aug 21, 2026
a4b2ba5
fix(groupme): stop reading an ambiguous empty page as a proven walk
tnunamak Aug 21, 2026
aeee923
fix(canary): derive the OTP refusal set from manifests instead of har…
tnunamak Aug 21, 2026
4fc6639
fix(auto-login): settle firstNonEmpty and realign the chase allowlist
tnunamak Aug 21, 2026
1c3da58
fix(groupme): stop reading an ambiguous empty page as a proven walk
tnunamak Aug 21, 2026
1b4703a
fix(canary): match an OTP connector however its separators are spelled
tnunamak Aug 21, 2026
501361e
perf(records): index the keyset pagination both the page and backfill…
tnunamak Aug 21, 2026
814b4d1
perf(postgres): give bulk ingest its own connection lane and a hard c…
tnunamak Aug 21, 2026
a149ca1
fix(test): prove sweep fairness by ordering, not by outrunning the clock
tnunamak Aug 21, 2026
3ba2a61
fix(test): make the schedule-route refresh assertion discriminating
tnunamak Aug 22, 2026
55d5e2a
fix(read-model): stop a non-measuring run from erasing a coverage proof
tnunamak Aug 22, 2026
ea1ca49
fix(test-accounting): declare the two new Postgres coverage-fold skips
tnunamak Aug 22, 2026
6ef209d
fix(test): track the fold version by constant, not by copied literal
tnunamak Aug 22, 2026
1ebd36c
fix(heb): refuse to prove an account empty after it collected orders
tnunamak Aug 22, 2026
e60ce2f
fix(amazon): refuse to prove a year empty after it collected orders
tnunamak Aug 22, 2026
9d233a9
feat(integrity): detect records stranded by a deleted connection at boot
tnunamak Aug 22, 2026
f0049ca
fix(usaa): walk through the ATM-deposit offer to reach the export page
tnunamak Aug 22, 2026
dbd944f
feat(ri): add a sources-report CLI that renders what /sources renders
tnunamak Aug 22, 2026
f5ce393
build(docker): stamp OCI provenance labels onto the core image
tnunamak Aug 22, 2026
1e2d813
fix(usaa): close a statement gap once its PDF comes back
tnunamak Aug 22, 2026
ffbb94f
fix(slack): collect archived channels, and make the scope setting rea…
tnunamak Aug 22, 2026
8609f37
fix(health): stop a server restart reading as a connector failure
tnunamak Aug 22, 2026
0a74c82
chore(connectors): realign the usaa sequential-await allowlist
tnunamak Aug 22, 2026
fcc1f67
docs: record the local-collector coverage architecture
tnunamak Aug 22, 2026
0785345
fix(health): keep freshness measurable when a local backlog is terminal
tnunamak Aug 22, 2026
3cf21fd
fix(scheduler): stop a restart from delaying the next run a full inte…
tnunamak Aug 22, 2026
4007ab4
fix(groupme): retract the unproven "provider contradicted itself" claim
tnunamak Aug 22, 2026
36e8100
docs(restart): propose resumable checkpoints, record the drain verdict
tnunamak Aug 22, 2026
b269e82
fix(stream): normalize touch button so a tap presses a real button
tnunamak Aug 22, 2026
f50e1d6
fix(console): stop claiming a browser login completed without evidence
tnunamak Aug 22, 2026
b87a1ba
feat(sources): fuse state, freshness, and activity into one honest st…
tnunamak Aug 22, 2026
3bfebf0
fix(gaps): stop honoring a too_large proof the item's own size refutes
tnunamak Aug 22, 2026
5ff7e32
docs: record the USAA and Gmail coverage root causes
tnunamak Aug 22, 2026
81c3c53
fix(test): refuse test runs against non-provisioned Postgres databases
tnunamak Aug 22, 2026
aefd5ce
chore(test-accounting): account for the new guard proof's skip baseline
tnunamak Aug 22, 2026
fe358e3
fix(test): keep the test-database sentinel out of the public schema
tnunamak Aug 22, 2026
656c556
fix(repair): read space-separated CLI flag values instead of substitu…
tnunamak Aug 22, 2026
c84f747
fix(test): stamp the sentinel in withTemporaryPostgresDatabase too
tnunamak Aug 22, 2026
d0c4533
docs: record the applied D4 repair and the CLI parsing defect
tnunamak Aug 22, 2026
40412f1
fix(console): say what is wrong in words an owner understands
tnunamak Aug 22, 2026
803099a
feat(sources): show archived sources instead of hiding them
tnunamak Aug 22, 2026
8bb49d7
fix(ri): let sources-report say "Archived" instead of "Paused"
tnunamak Aug 22, 2026
5b88b52
fix(runtime): retry a network-level fetch failure on ingest and state…
tnunamak Aug 22, 2026
64ba27b
feat(sources): surface a never-succeeded Venmo setup as its own row
tnunamak Aug 22, 2026
6150c10
fix(chase): close a statement gap once its PDF comes back
tnunamak Aug 22, 2026
e3f261c
fix(console,cli): reconcile the axis-vocabulary merge with owner-appr…
tnunamak Aug 22, 2026
7bcf33e
merge: bring in Archived and setup_failed VerdictLabel additions
tnunamak Aug 22, 2026
5c7c3eb
fix(sources): record why a setup shell was revoked, not just that it was
tnunamak Aug 22, 2026
b2fda94
Merge commit '7bcf33e98' into fix/bseries-coverage-verdicts-0822
tnunamak Aug 22, 2026
6789377
fix(coverage): protect a measured boundary from a checkpoint-only re-…
tnunamak Aug 22, 2026
fd1a45d
fix(sources): stop promising a future measurement a finished import c…
tnunamak Aug 22, 2026
9496dbe
fix(console): widen RefVerdictPill.label to match the server's Verdic…
tnunamak Aug 22, 2026
fbb1369
Merge branch 'fix/label-vocab-reconcile-v2-0822' into deploy/drain38-…
tnunamak Aug 22, 2026
95ea185
Merge branch 'fix/quiet-setup-expiry-0822' into deploy/drain38-prod-0822
tnunamak Aug 22, 2026
0e7484f
Merge branch 'fix/chase-statement-gap-close-0822' into deploy/drain38…
tnunamak Aug 22, 2026
cfecdfd
Merge branch 'fix/googlemaps-import-coverage-0822' into deploy/drain3…
tnunamak Aug 22, 2026
60ba596
Merge branch 'fix/restart-resilience-0822' into deploy/drain38-prod-0822
tnunamak Aug 22, 2026
36d00a1
Merge branch 'fix/local-collector-coverage-0822' into deploy/drain38-…
tnunamak Aug 22, 2026
628c4b6
Merge branch 'fix/bseries-coverage-verdicts-0822' into deploy/drain38…
tnunamak Aug 22, 2026
703d46e
Merge branch 'fix/slack-archived-channels-0822' into deploy/drain38-p…
tnunamak Aug 22, 2026
bf46f8b
Merge branch 'fix/test-db-isolation-guard-0822' into deploy/drain38-p…
tnunamak Aug 22, 2026
7e2dd66
Merge branch 'fix/test-db-isolation-guard-0822' into deploy/drain38-p…
tnunamak Aug 22, 2026
24131dd
fix(health): stop heartbeats from erasing dead-letter detail on local…
tnunamak Aug 22, 2026
74b924a
fix(controller-boot): project boot-epoch provenance into run_history.…
tnunamak Aug 22, 2026
b70e580
fix(amazon): report list-page cards that never parse into an order
tnunamak Aug 22, 2026
c36fa7a
Merge branch 'fix/honest-terminal-reason-0822' into fix/pr166-consoli…
tnunamak Aug 22, 2026
0a8e7ba
Merge branch 'fix/amazon-cancelled-bisect-0822' into fix/pr166-consol…
tnunamak Aug 22, 2026
5b325cd
Merge branch 'fix/deadletter-and-signal-freshness-0822' into fix/pr16…
tnunamak Aug 22, 2026
c43232c
Merge branch 'fix/ledger-sweep-0822' into fix/pr166-consolidated-0822
tnunamak Aug 22, 2026
8871e88
Merge branch 'fix/groupme-claim-verification-0822' into fix/pr166-con…
tnunamak Aug 22, 2026
f4fee82
Merge branch 'fix/usaa-gmail-coverage-0822' into fix/pr166-consolidat…
tnunamak Aug 22, 2026
d1d1d96
Merge branch 'fix/browser-stream-ux-0822' into fix/pr166-consolidated…
tnunamak Aug 22, 2026
4acc0ff
fix(scheduler): stop fabricating records_emitted/known_gaps on run_ti…
tnunamak Aug 22, 2026
66999ff
fix(pr166-consolidated): repair five merge-fallout defects surfaced b…
tnunamak Aug 22, 2026
2c44e57
feat(runtime): let a controller restart keep a provably safe checkpoint
tnunamak Aug 22, 2026
a749b1e
merge: bring in run-honesty fix (stop fabricating records_emitted/kno…
tnunamak Aug 22, 2026
feffd80
merge: bring in resumable-checkpoint fix (keep provably safe checkpoi…
tnunamak Aug 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,27 @@ FROM browsers AS core-browser

ARG PDPP_REFERENCE_REVISION=unknown

# Image provenance. Without these the deployed artifact cannot say what source
# it was built from, and identifying production means md5-diffing files against
# candidate worktrees. Sampling files that way is actively misleading: a file
# unchanged between two commits matches BOTH, so a sample that happens to miss
# the changed files "confirms" the wrong commit. Labels remove the guesswork.
#
# PDPP_BUILD_DIRTY must be set from `git status --porcelain` at build time. A
# silently-dirty build tree is how bad images shipped before, so an unclean
# tree is recorded in the artifact rather than left to memory.
ARG PDPP_BUILD_REVISION=unknown
ARG PDPP_BUILD_SOURCE=unknown
ARG PDPP_BUILD_CREATED=unknown
ARG PDPP_BUILD_DIRTY=unknown
ARG PDPP_BUILD_COMPOSITION=unknown

LABEL org.opencontainers.image.revision="${PDPP_BUILD_REVISION}" \
org.opencontainers.image.source="${PDPP_BUILD_SOURCE}" \
org.opencontainers.image.created="${PDPP_BUILD_CREATED}" \
pdpp.build.dirty="${PDPP_BUILD_DIRTY}" \
pdpp.build.composition="${PDPP_BUILD_COMPOSITION}"

# PDPP_LOCAL_TRANSFORMER_SUPERVISOR_RESTART_CONTRACT is baked in (unlike the
# root docker-compose.yml `reference` service, which sets it explicitly in
# compose env) because this image stage is deployed exclusively through
Expand Down
62 changes: 53 additions & 9 deletions apps/console/src/app/(console)/audit/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,60 @@
// Copyright The PDP-Connect Contributors
// SPDX-License-Identifier: Apache-2.0

import { SegmentError } from "../components/segment-error.tsx";
import { useEffect } from "react";
import { createRetryCounter, nextRetryDelayMs } from "../components/read-resilient-retry.ts";
import { ListLoadingSkeleton } from "../components/route-loading.tsx";

/**
* Audit-segment error boundary (App Router convention) — SLVP bar: Stripe,
* Linear, Vercel, and Plaid never tell an owner "we hit a transient read
* interruption, retrying." The page renders, or it quietly shows last-known
* state. The owner never learns the backend hiccuped.
*
* Root cause of the throw this boundary catches (`Error: The destination
* stream closed early`): the read itself is fine — React's Flight/RSC
* streaming writer reacting to the HTTP response closing before the stream
* finished flushing. It is a client-transport race below the data layer, not
* a backend outage — see `sources/error.tsx` for the full original writeup.
*
* `/audit` has no client-cached last-known-read marker, so this boundary
* shows the plain skeleton with no staleness caption rather than fabricate a
* timestamp.
*
* Self-contained on purpose: a `"use client"` boundary must not import
* server-only modules, since the dashboard shell transitively pulls in
* `lib/owner-token.ts` (`server-only`).
*/

/**
* Consecutive-failure counter, held at MODULE scope rather than component
* state — see `read-resilient-retry.ts` for why a `useState` counter would
* silently reset every catch and never actually back off.
*/
const retryCounter = createRetryCounter();

export default function AuditError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
useEffect(() => {
// Logged for operator diagnostics only — never surfaced to the owner.
console.error(error);
}, [error]);

useEffect(() => {
// Unbounded, capped backoff: every mount (i.e. every failed attempt)
// schedules the next retry at a delay that grows with the module-scoped
// counter. There is deliberately no ceiling on the counter itself — a
// persistent failure degrades to a slow quiet heartbeat, never a dead end.
const delay = nextRetryDelayMs(retryCounter.attempts);
const id = setTimeout(() => {
retryCounter.attempts += 1;
reset();
}, delay);
return () => clearTimeout(id);
}, [reset]);

export default function AuditError(props: { error: Error & { digest?: string }; reset: () => void }) {
return (
<SegmentError
{...props}
backHref="/audit"
backLabel="Back to audit"
description="The audit view ran into an error while reading from your reference deployment. Reading failed; nothing changed. Try again, or check your reference deployment status."
title="Couldn't load your audit log"
/>
<div data-testid="audit-read-recovering">
<ListLoadingSkeleton label="audit events" rows={8} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright The PDP-Connect Contributors
// SPDX-License-Identifier: Apache-2.0

/**
* Read-resilience acceptance invariants for the audit segment, mirroring
* `sources/read-resilience.invariants.test.ts`. See that file and
* `syncs/read-resilience.invariants.test.ts` for the full standard this
* pattern enforces; this file pins the same properties for `/audit`.
*/

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import { fileURLToPath } from "node:url";

const HERE = fileURLToPath(new URL(".", import.meta.url));
const ERROR_FILE = `${HERE}error.tsx`;

const BLOCK_COMMENT_RE = /\/\*[\s\S]*?\*\//g;

/**
* Strip `/* ... *‍/` block comments before checking for retired owner-facing
* copy. The boundary's doc comment legitimately QUOTES the retired phrases
* (to explain what this pattern replaces and why) — that is documentation,
* not rendered JSX text, so it must not trip the ban.
*/
function withoutBlockComments(src: string): string {
return src.replace(BLOCK_COMMENT_RE, "");
}

const RETIRED_COULDNT_RE = /Couldn't/;
const RETIRED_ERROR_HEADING_RE = /Read error/;
const RETIRED_TRY_AGAIN_RE = /Try again/;
const RETIRED_INTERRUPTION_COPY_RE = /transient read interruption/i;
const RETIRED_READ_FAILURE_FRAMING_RE = /read failure/i;
const RETIRED_BACK_LINK_RE = /Back to audit/;
const RETIRED_SEGMENT_ERROR_IMPORT_RE = /from\s+["']\.\.\/components\/segment-error\.tsx["']/;

const USES_LOADING_SKELETON_RE = /<ListLoadingSkeleton\b/;
const IMPORTS_LOADING_SKELETON_RE = /from\s+["']\.\.\/components\/route-loading\.tsx["']/;
const RECOVERING_TESTID_RE = /data-testid="audit-read-recovering"/;
const SERVER_ONLY_IMPORT_RE = /^import[\s\S]*?from\s+["'][^"']*(owner-token|server-only|data-source|ref-client)/m;
const CALLS_RESET_RE = /reset\(\)/;
const UNBOUNDED_RETRY_SCHEDULES_NEXT_RE = /setTimeout\([\s\S]*reset\(\)/;
const MODULE_SCOPE_COUNTER_RE = /const retryCounter = createRetryCounter\(\)/;
const NO_REACT_STATE_COUNTER_RE = /useState\(/;
const IMPORTS_SHARED_RETRY_RE = /from\s+["']\.\.\/components\/read-resilient-retry\.ts["']/;
const NO_TERMINAL_GIVE_UP_FLAG_RE = /gaveUp|autoRetried|maxAttemptsReached/;

test("the boundary source contains none of the retired owner-facing failure copy outside doc comments", async () => {
const rawSrc = await readFile(ERROR_FILE, "utf8");
const src = withoutBlockComments(rawSrc);
assert.doesNotMatch(src, RETIRED_COULDNT_RE);
assert.doesNotMatch(src, RETIRED_ERROR_HEADING_RE);
assert.doesNotMatch(src, RETIRED_TRY_AGAIN_RE);
assert.doesNotMatch(src, RETIRED_INTERRUPTION_COPY_RE);
assert.doesNotMatch(src, RETIRED_READ_FAILURE_FRAMING_RE);
assert.doesNotMatch(src, RETIRED_BACK_LINK_RE);
assert.doesNotMatch(rawSrc, RETIRED_SEGMENT_ERROR_IMPORT_RE);
});

test("the boundary renders the same loading skeleton the route's loading.tsx uses, not a bespoke banner", async () => {
const src = await readFile(ERROR_FILE, "utf8");
assert.match(src, IMPORTS_LOADING_SKELETON_RE);
assert.match(src, USES_LOADING_SKELETON_RE);
assert.match(src, RECOVERING_TESTID_RE);
// loading.tsx uses ListLoadingSkeleton label="audit events" rows={8}.
assert.match(src, /ListLoadingSkeleton label="audit events" rows=\{8\}/);
});

test("the boundary retries unbounded on a capped backoff held at module scope, with no manual-retry terminal state", async () => {
const src = await readFile(ERROR_FILE, "utf8");
assert.match(src, CALLS_RESET_RE);
assert.match(src, UNBOUNDED_RETRY_SCHEDULES_NEXT_RE);
assert.match(src, IMPORTS_SHARED_RETRY_RE);
assert.match(src, MODULE_SCOPE_COUNTER_RE);
assert.doesNotMatch(
src,
NO_REACT_STATE_COUNTER_RE,
"the retry counter must live at module scope, not React state, or backoff never grows across remounts"
);
assert.doesNotMatch(src, NO_TERMINAL_GIVE_UP_FLAG_RE, "no gated give-up state — retry must be unbounded");
});

test("the boundary is self-contained: no server-only import", async () => {
const src = await readFile(ERROR_FILE, "utf8");
assert.doesNotMatch(src, SERVER_ONLY_IMPORT_RE);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright The PDP-Connect Contributors
// SPDX-License-Identifier: Apache-2.0

/**
* Behavioral tests for the shared read-resilient-boundary backoff primitive.
* See `read-resilient-retry.ts` for why the retry counter this module
* supports must be created once per segment boundary at module scope.
*/

import assert from "node:assert/strict";
import test from "node:test";
import {
createRetryCounter,
nextRetryDelayMs,
RETRY_BASE_DELAY_MS,
RETRY_MAX_DELAY_MS,
} from "./read-resilient-retry.ts";

test("nextRetryDelayMs starts at the base delay for the first attempt", () => {
assert.equal(nextRetryDelayMs(0), RETRY_BASE_DELAY_MS);
});

test("nextRetryDelayMs doubles per attempt until the cap", () => {
assert.equal(nextRetryDelayMs(1), RETRY_BASE_DELAY_MS * 2);
assert.equal(nextRetryDelayMs(2), RETRY_BASE_DELAY_MS * 4);
assert.equal(nextRetryDelayMs(3), RETRY_BASE_DELAY_MS * 8);
});

test("nextRetryDelayMs is capped at RETRY_MAX_DELAY_MS and never exceeds it, however large the attempt", () => {
const atCap = nextRetryDelayMs(10);
const wayPastCap = nextRetryDelayMs(1000);
assert.equal(atCap, RETRY_MAX_DELAY_MS);
assert.equal(wayPastCap, RETRY_MAX_DELAY_MS);
});

test("createRetryCounter returns an independent counter each call — no shared state between boundaries", () => {
const a = createRetryCounter();
const b = createRetryCounter();
a.attempts = 5;
assert.equal(b.attempts, 0, "mutating one boundary's counter must not affect another's");
});
52 changes: 52 additions & 0 deletions apps/console/src/app/(console)/components/read-resilient-retry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright The PDP-Connect Contributors
// SPDX-License-Identifier: Apache-2.0

/**
* Shared unbounded-capped-backoff retry primitive for segment error
* boundaries (App Router `error.tsx` convention).
*
* SLVP bar (Stripe, Linear, Vercel, Plaid): none of those products tell a
* user "couldn't load X" with a Try again button because a stream hiccuped.
* When a segment's server read throws `Error: The destination stream closed
* early` — React's RSC streaming writer reacting to the HTTP response closing
* while the Flight stream was still being written, not a failed data read —
* the boundary must retry quietly and indefinitely rather than parking on a
* failure card. See `sources/error.tsx` for the fully-annotated original of
* this pattern; this module factors ONLY the backoff arithmetic, which is
* byte-identical across every segment that adopts it. Each segment's own
* skeleton and copy stay in that segment's `error.tsx` — factoring those out
* too would hide the per-route visual contract behind a generic wrapper.
*
* The retry counter MUST live at module scope, never in React state: Next.js
* remounts the error boundary fresh on every catch (a new error instance
* re-enters the boundary), so a `useState` counter would silently reset to 0
* on every failure and the backoff would never grow past its base delay. A
* `RetryCounter` created by `createRetryCounter()` at each `error.tsx`
* module's top level (NOT inside this shared module, and NOT shared between
* routes) gives each segment boundary its own independent counter with
* exactly the right lifetime: "how many times has this boundary caught in a
* row since the page was last freshly loaded."
*/

/** First retry is near-immediate — long enough to dodge a tight synchronous loop. */
export const RETRY_BASE_DELAY_MS = 300;
/** Backoff ceiling: keep retrying at a calm, bounded cadence forever rather than escalating without limit. */
export const RETRY_MAX_DELAY_MS = 15_000;

/**
* A single segment boundary's consecutive-failure counter. Create exactly one
* of these per `error.tsx` module (at module scope, not inside the component)
* and reuse it across every catch that module handles.
*/
export type RetryCounter = { attempts: number };

/** Create a fresh, independent module-scoped retry counter for one segment boundary. */
export function createRetryCounter(): RetryCounter {
return { attempts: 0 };
}

/** Capped exponential backoff. Never returns a delay the owner would perceive as "given up". */
export function nextRetryDelayMs(attempt: number): number {
const scaled = RETRY_BASE_DELAY_MS * 2 ** attempt;
return Math.min(scaled, RETRY_MAX_DELAY_MS);
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,26 @@ function SourceAcquisitionPaths({ paths }: { paths: readonly ConnectorAcquisitio
);
}

/**
* Development-tier method line, resolved before the `not_available_here`
* short-circuit below. `sourceSetupAvailability` reports `not_available_here`
* for every Development entry regardless of whether it renders an add
* action (see `sourceSetupAction`'s scaffold check), so that check alone
* would print "No proven setup path is available" directly above a real
* "Add account" button for a self-testable entry -- contradicting the
* action right next to it.
*/
function developmentMethodLine(entry: ConnectorCatalogEntry): string {
if (entry.isKnownScaffold) {
return "Not implemented yet: this connector cannot collect data.";
}
return "Setup path implemented, not yet proven against a live account.";
}

function sourceMethodLine(entry: ConnectorCatalogEntry, existingSourceCount: number): string {
if (entry.publicTier === "development") {
return developmentMethodLine(entry);
}
if (sourceSetupAvailability(entry) === "not_available_here") {
return "No proven setup path is available in this dashboard.";
}
Expand Down Expand Up @@ -352,6 +371,41 @@ function ExperimentalSetupSummary({
);
}

/**
* Development-tier connectors are registered and shipped, but this dashboard
* does not offer them in the main list or the Preview disclosure above --
* either no live run has proven the setup path yet (real, self-testable), or
* the connector is a scaffold with no real collection code yet (never gets
* an add action). Collapsed by default, same precedent as Preview, one tier
* more cautious: an owner running their own instance can see everything that
* exists and tell "not proven yet" apart from "not built yet", instead of a
* connector silently vanishing between "shipped" and "visible".
*/
function DevelopmentSetupSummary({
entries,
existingSourcesByConnector,
}: {
entries: readonly ConnectorCatalogEntry[];
existingSourcesByConnector?: Readonly<Record<string, readonly ExistingSourceSetupLink[]>>;
}) {
if (entries.length === 0) {
return null;
}
return (
<details className="rounded-sm border border-border/80 border-dashed bg-muted/10 p-3" data-testid="development-setup-summary">
<summary className="pdpp-caption cursor-pointer text-muted-foreground">Development ({entries.length})</summary>
<div className="mt-3 grid gap-3">
<p className="pdpp-caption text-muted-foreground">
These connectors are registered on this instance but not yet offered above. Some have a real, implemented
setup path with no live-account run yet -- test them with non-critical data. Others are scaffolds with no
collection code yet and have no add action here.
</p>
<SourceSetupCardList entries={entries} existingSourcesByConnector={existingSourcesByConnector} />
</div>
</details>
);
}

function SourceSetupCardList({
entries,
existingSourcesByConnector,
Expand Down Expand Up @@ -393,7 +447,16 @@ export function SourceSetupCatalog({
const filtered = filterSourceCatalog(catalog, query);
const available = filtered.filter((entry) => entry.publicTier === "supported" && isRunnableAddOffer(entry));
const experimental = filtered.filter((entry) => entry.publicTier === "preview" && isRunnableAddOffer(entry));
// Every Development-tier entry belongs here -- real-but-unproven and known
// scaffolds alike. Visibility is the point: an owner running this instance
// must be able to tell a scaffold apart from a connector nobody has tested
// yet, not have either one silently omitted. `SourceSetupCard` itself
// already withholds the add action for a scaffold (`sourceSetupAction`
// returns null), so listing every Development entry here cannot render a
// dead-end "Add" button.
const development = filtered.filter((entry) => entry.publicTier === "development");
const actionable = [...available, ...experimental];
const anyMatch = actionable.length > 0 || development.length > 0;
return (
<Section description="Add sources this dashboard can set up now." title="Add data">
<form action={action} className="mb-4 grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto]">
Expand All @@ -405,7 +468,7 @@ export function SourceSetupCatalog({
Search
</IcButton>
</form>
{actionable.length > 0 ? (
{anyMatch ? (
<div className="grid gap-5">
{available.length > 0 ? (
<SourceSetupCardList entries={available} existingSourcesByConnector={existingSourcesByConnector} />
Expand All @@ -416,6 +479,7 @@ export function SourceSetupCatalog({
)}

<ExperimentalSetupSummary entries={experimental} existingSourcesByConnector={existingSourcesByConnector} />
<DevelopmentSetupSummary entries={development} existingSourcesByConnector={existingSourcesByConnector} />
</div>
) : (
<p className="pdpp-caption rounded-md border border-border/80 border-dashed p-4 text-muted-foreground">
Expand Down
Loading