feat(ledger): scheduled checkpoint anchoring, tying Rekor and git-commit together - #9413
Conversation
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
7f4eda6 to
1c7a6b8
Compare
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-27 17:26:43 UTC
Review summary Nits — 6 non-blocking
Concerns raised — review before merging
📋 Copy for AI agents — paste into your coding agentDecision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Decision record
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
7079aca to
c0a3874
Compare
…mit together (#9274) Sixth and final scheduled-side sub-issue of #9267. Enqueued every ~2min cron tick unconditionally (added to IMMEDIATE_SCHEDULED_JOB_TYPES alongside the regate sweep) rather than gated to isHourly: the seq-threshold trigger must fire independent of the hourly clock per the issue's own requirement, and the job's own decideLedgerAnchorSchedule is what actually decides whether a given tick does anything — a cheap two-query no-op the overwhelming majority of ticks. decideLedgerAnchorSchedule is pure: anchors on the hourly tick when the tip changed since the last attempt (never-anchored counts as "changed"), anchors immediately once the seq delta reaches 256 regardless of the clock (treating a never-anchored ledger as starting from seq 0, so a big first burst doesn't wait for the next hourly tick), and skips an unchanged tip either way. Both backends run independently per checkpoint. The safety boundary is structural, not conventional: submitToRekor and submitToGitAnchor never reject on their own, but the REAL git path wraps installation-token minting around submitToGitAnchor (a genuine IO call that can throw, unlike anything inside submitToGitAnchor itself) — so the orchestrator catches each backend call centrally and records a status:'failed' row on an unexpected rejection, rather than trusting every future caller to uphold "never rejects" by convention. Also found and removed real dead code: the OLD unversioned {seq, rowHash, at} stub buildLedgerAnchorPayload, still sitting in decision-record.ts alongside the new signed/versioned one #9270 built to supersede it in ledger-anchor.ts. Never removed when #9270 landed — same function name, two implementations, the old one dead and untested by anything except its own now-removed tests. Added loadDecisionLedgerTip (decision-record.ts) and loadLastLedgerAnchorAttempt (ledger-anchor-persistence.ts) as the two reads the scheduler needs — the latter deliberately keyed on the last ATTEMPT regardless of status, not the last success, so a backend stuck failing doesn't get hammered against the same stale checkpoint forever while newer tips go unanchored. Updates ~10 existing exact-array assertions in index.test.ts and one in selfhost-queue-common.test.ts to include the new job type — real, necessary churn from enqueuing unconditionally on every tick, not routed around.
c0a3874 to
505ffba
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9413 +/- ##
==========================================
- Coverage 89.52% 88.63% -0.90%
==========================================
Files 842 843 +1
Lines 109835 109880 +45
Branches 26159 26173 +14
==========================================
- Hits 98333 97387 -946
- Misses 10239 11521 +1282
+ Partials 1263 972 -291
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Summary
Sixth and final scheduled-side sub-issue of #9267 (external ledger anchoring), stacked on #9272 (#9397 — #9273's own PR #9403 has already merged into this base). Ties #9272 (Rekor) and #9273 (git-commit) together into a periodic job.
Enqueued every ~2min cron tick unconditionally (added
anchor-decision-ledgertoIMMEDIATE_SCHEDULED_JOB_TYPES, alongside the existing regate sweep) rather than gated toisHourly: the seq-threshold trigger must fire independent of the hourly clock per the issue's own requirement, and the job's owndecideLedgerAnchorScheduledecides whether a given tick actually does anything — a cheap two-query no-op the overwhelming majority of ticks.decideLedgerAnchorScheduleis pure: anchors on the hourly tick when the tip changed since the last attempt (never-anchored counts as "changed"), anchors immediately once the seq delta reaches 256 regardless of the clock (treating a never-anchored ledger as starting from seq 0, so a big first burst doesn't wait for the next hourly tick), skips an unchanged tip either way.Both backends run independently per checkpoint. The "never rejects" safety boundary is enforced structurally, not by convention:
submitToRekor/submitToGitAnchornever reject on their own, but the real git path wraps installation-token minting aroundsubmitToGitAnchor— a genuine IO call that can throw, unlike anything insidesubmitToGitAnchoritself — so the orchestrator catches each backend call centrally and records astatus: 'failed'row on an unexpected rejection, rather than trusting every future caller to uphold the invariant.Also found and removed real dead code: an old, unversioned
{seq, rowHash, at}stubbuildLedgerAnchorPayloadwas still sitting indecision-record.ts, alongside the new signed/versioned one #9270 built specifically to supersede it inledger-anchor.ts. Same function name, two implementations — never cleaned up when #9270 landed. Removed the dead stub and its now-orphaned tests.Closes #9274.
Test plan
npx vitest run test/unit/ledger-anchor-scheduler.test.ts— 21/21, 100% statement/branch/function/line coveragesubmitRekorpath exercised (global-fetch stub), not just the injected fakesubmitGit(simulating a token-mint failure) proven to land as a recordedstatus: 'failed'row, not an unhandled rejectionnpx vitest run test/unit/ledger-anchor-job-dispatch.test.ts— 2/2: the real dispatch wiring, including a real installation-token mint and the realsubmitToGitAnchorGET→PUT call sequence against a stubbed Contents APInpx vitest run test/unit/decision-record.test.ts test/unit/ledger-anchor-persistence.test.ts— newloadDecisionLedgerTip/loadLastLedgerAnchorAttemptcovered directly in their own filesnpx vitest run test/unit/index.test.ts test/unit/selfhost-queue-common.test.ts— all 10+ affected exact-array assertions updated and passing, confirming the new job type's presence/position/cadence across every tick shape (:00,:14,:30,06:00, broker-only)npx tsc --noEmitclean ·npm run db:schema-drift:check/db:migrations:checkclean ·npx tsx scripts/check-import-specifiers.tsclean