feat(proof): public per-repo proof summary, endpoint and README badge (#9569) - #9608
feat(proof): public per-repo proof summary, endpoint and README badge (#9569)#9608JSONbored wants to merge 3 commits into
Conversation
…#9569) The shareable, unauthenticated twin of the in-app trust panel. One composition serves both, so the public page and #9193's panel cannot disagree about a figure -- which is the property the page exists to demonstrate. THE PRIVACY BOUNDARY IS STRUCTURAL. Every field is built by NAMING it, never by filtering a wider object. A blocklist has to anticipate every field a future upstream type might grow and silently leaks the one it did not; an allowlisted shape cannot leak a field nobody wrote down. Tested by feeding hostile records carrying hotkey/wallet/reward/trust-score/private- rank and asserting none of it reaches the serialized page -- while the named fields do, so the test proves allowlisting rather than an empty object. NEVER A BARE SCALAR. Any accuracy figure carries its coverage and a Wilson interval; below a 20-decision floor there is no rate at all, only an explicit insufficient_data state that still publishes the count. A perfect record over 19 decisions must not render as 100%. Wilson rather than Wald because a gate metric lives near p->1, exactly where Wald claims impossible certainty. HONEST BOUNDARY STATES. An empty ledger is `empty`, not `verified` -- different claims. A failed read is `unavailable`, not `broken`, which would accuse the operator of tampering. A FAILED anchor attempt is not an anchor: the public attempt log is where failures are legible, and presenting one here would claim corroboration that does not exist. The verification-contract boundary statement travels IN the payload, so a screenshot or embed cannot shed it the way a footer caption can. The badge reports the LEDGER's state rather than an accuracy percentage: a badge is a one-glance claim, and an accuracy number without the interval that makes it honest does not fit in one. Disabled and errored both render a neutral SVG -- a broken image in a README is worse than an honest "unavailable". DECISION (requirement 6), recorded beside the code that implements it: the page is opt-OUT per repo, default ON once the operator's fleet-wide flag (default OFF) is on. Every figure is already publicly fetchable through the ledger-verify / anchors / decision-record endpoints, so gating a page over it would add friction without privacy. The per-repo switch still exists because a page is a different artifact from an API -- discoverable, linkable, and it markets a repo's numbers whether or not the maintainer wants that. A repo can opt out but cannot opt IN when the operator has not, which keeps the fleet switch a real switch. Found and fixed while testing: `DB.prepare()` throws SYNCHRONOUSLY on a driver-level failure, so the `.catch()` chain never ran and a D1 outage would have 503'd the whole public page instead of degrading. Each section is now a real try/catch, which is the difference between the fail-safe-per-section contract being documented and being true. Backend half of #9569; the /proof/:owner/:repo UI route renders this payload and lands separately.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
loopover-ui | 26f90ca | Jul 29 2026, 04:31 AM |
|
Important 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏳ LoopOver is waiting…LoopOver has seen this pull request and is waiting on CI checks to finish before reviewing it. This comment will update once the review runs. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed · 🟨 Waiting |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Bundle ReportChanges will increase total bundle size by 3.84kB (0.05%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: loopover-uiAssets Changed:
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #9608 +/- ##
==========================================
- Coverage 90.24% 89.14% -1.10%
==========================================
Files 900 902 +2
Lines 112914 112332 -582
Branches 26776 26709 -67
==========================================
- Hits 101896 100141 -1755
- Misses 9687 11101 +1414
+ Partials 1331 1090 -241
Flags with carried forward coverage won't be shown. Click here to find out more.
|
…d to honor (#9569) Review caught the real defect: both handlers called isProofPageEnabledForRepo(c.env) with no second argument, so the ProofPageRepoOverride documented at length in proof-summary.ts and in the PR body was never loaded or passed. Every repo was effectively opt-out-less once the fleet flag was on -- a gate that is described, typed, and unit-tested as a pure function, but never reachable from the surface it governs. That is the registered-but-unreachable class, and the long comment made it worse rather than better by making it look done. - Adds a real `publicProof:` focus-manifest block (engine parser + toJson + loader snapshot), mirroring `publicStats:`/`ops:`. Precedence is deliberately the opposite of those two: read from the TARGET repo's manifest rather than the operator's self-repo, because the thing being opted out of is that repo's own page. - loadProofPageRepoOverride resolves it, degrading a failed manifest load to "no override" -- a broken manifest never takes a page DOWN, which is the failure direction worth accepting here and is now stated in the doc comment rather than left implicit. - Both routes load the override BEFORE anything else, so a repo that turned its page off does not have its decision records queried to build a summary that will be discarded. - Documents the block in .loopover.yml.example, including the precedence and the opt-out default. Tests that would have caught it: a repo opting out in its manifest now gets 404 from BOTH routes with the fleet flag on, while a different repo in the same fleet still serves 200 (the opt-out is per repo, not a kill switch); explicit opt-in and no-block-at-all both serve; and the resolver is covered across absent/explicit/failing loads.
What
The shareable, unauthenticated twin of the in-app trust panel. One composition serves both, so the public page and #9193's panel can't disagree about a figure — which is precisely the property this page exists to demonstrate.
The privacy boundary is structural, not a filter
Every field is built by naming it, never by spreading a wider object. A blocklist has to anticipate every field a future upstream type might grow, and silently leaks the one it didn't; an allowlisted shape can't leak a field nobody wrote down. Tested by feeding hostile records carrying
hotkey/walletAddress/rewardTao/trustScore/privateRankand asserting none of it reaches the serialized page — while the named fields do come through, so the test proves allowlisting rather than an empty object.Never a bare scalar
Any accuracy figure carries its coverage and a Wilson interval. Below a 20-decision floor there is no rate at all — an explicit
insufficient_datastate that still publishes the count, because "we have 7 decisions, too few to claim a rate" is more honest than hiding both. A perfect record over 19 decisions must not render as 100%. Wilson rather than Wald because a gate metric lives near p→1, exactly where Wald claims impossible certainty.Honest boundary states
An empty ledger is
empty, notverified— different claims. A failed read isunavailable, notbroken, which would accuse the operator of tampering. A failed anchor attempt is not an anchor: the public attempt log is where failures are legible, and presenting one here would claim corroboration that doesn't exist. The verification-contract boundary statement travels in the payload, so a screenshot or embed can't shed it the way a footer caption can.The badge reports the ledger's state, not an accuracy percentage — a badge is a one-glance claim, and an accuracy number without the interval that makes it honest doesn't fit in one. Disabled and errored both render a neutral SVG, because a broken image in a README is worse than an honest "unavailable".
The opt-out decision (requirement 6)
Recorded in the module beside the code that implements it, not only here:
Opt-out per repo, default ON once the operator's fleet-wide flag (default OFF) is on. Every figure is already publicly fetchable through
/v1/public/decision-ledger/verify,/…/anchorsand/…/decision-records/…— gating a page over data anyone can already curl adds friction without privacy, and makes a verification story look less confident than it is. The per-repo switch still exists because a page is a genuinely different artifact from an API: discoverable, linkable, indexable, and it markets a repo's numbers whether or not the maintainer wants that. A repo can opt out but cannot opt in when the operator hasn't, which keeps the fleet switch a real switch.A real bug the tests caught
DB.prepare()throws synchronously on a driver-level failure, so my.catch()chain never ran — a D1 outage would have 503'd the entire public page instead of degrading section by section. Each section is now a realtry/catch. That's the difference between the fail-safe-per-section contract being documented and being true, and there's a test that breaksprepareoutright plus one for a driver returning noresultsarray.Tests (19, 100% of branches on both new modules)
Accuracy with coverage+interval and the floor in both directions; failed-attempt-is-not-an-anchor and newest-wins regardless of list order; all four ledger states including the unknown-position break; the privacy-boundary regression; sample bounding and the in-payload caveat; every badge message and color including the neutral not-yet-decided case; the flag's truthy parsing and the full opt-out matrix; end-to-end route 404-while-off / 200-when-on with cache headers; per-section degradation; and a real recorded anchor flowing through to
anchored.Auth exemptions and OpenAPI operations are added in this PR alongside the routes, per the #9120 lesson.
Backend half of #9569 — the
/proof/:owner/:repoUI route renders this payload and lands separately, so I've left the issue open.