docs(openapi): publish the anchor-attempts request body, and stop the pattern from shipping a regex flag - #9778
Conversation
… pattern from shipping a regex flag
POST /v1/decision-ledger/anchor-attempts exists to be called by a submitter this
repo deliberately does not ship -- ledger-anchor-bittensor.ts's own header says
so: "a small process on the operator's own node infrastructure ... never in this
repo". Its only reference is the OpenAPI document, which registered responses
and no body at all, leaving the entire contract discoverable only by reading the
TypeScript that rejects it.
The schema lives beside parseBittensorAnchorReport rather than in the spec file,
because the two are one contract described twice and must be edited together.
The validator stays the runtime authority: each of its arms returns a NAMED
rejection identifying the exact field it refused, which is what makes a submitter
bug diagnosable from the 400 body alone, and a generic zod issue list would be a
downgrade for precisely the caller this endpoint is built for. A cross-check
suite runs 25 payloads through BOTH and asserts they agree on accept/reject, so
neither can drift without failing CI -- including the ones that matter most:
status/backendRef and status/error coupling, the u16 netuid ceiling, and blank
-after-trim hotkeys and errors.
Two fixes fell out of actually generating the document:
- The hash regexes carried an `i` FLAG. JSON Schema has no flags concept, so
they serialized as `^[0-9a-f]{64}$/i` -- a pattern requiring a literal "/i"
suffix, which would reject every valid hash in any generated client.
Case-insensitivity is now spelled into the character class, which is
behaviourally identical for hex and safe to publish.
- SpecEntry in internal-and-public-route-specs.ts declared only `params` under
`request`, so an entry there could not describe its own body even though
registerRouteSpec already knew how to render one (define-route.ts's
RouteSpecOptions has carried `body` since #9705). Widened to match the seam
it feeds.
Closes #9770
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-29 08:35:52 UTC
Review summary Nits — 6 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionPartially addressed 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
Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy · Diff highlights exactly what changed. Scroll preview
A short scroll-through clip (desktop) — click either thumbnail to open the full animation. Evidence for scroll-linked behavior a single screenshot can't show. 🟩 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.
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | b9b9041 | Commit Preview URL Branch Preview URL |
Jul 29 2026, 08:15 AM |
Bundle ReportChanges will increase total bundle size by 5.27kB (0.07%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: loopover-uiAssets Changed:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9778 +/- ##
==========================================
- Coverage 90.30% 89.48% -0.82%
==========================================
Files 912 912
Lines 113554 113556 +2
Branches 26954 26954
==========================================
- Hits 102543 101615 -928
- Misses 9682 10851 +1169
+ Partials 1329 1090 -239
Flags with carried forward coverage won't be shown. Click here to find out more.
|



Summary
POST /v1/decision-ledger/anchor-attemptsexists to be called by a submitter this repo deliberately does not ship —ledger-anchor-bittensor.ts's own header says so: "a small process on the operator's own node infrastructure … never in this repo". Its only reference is the OpenAPI document, which registered responses and no body at all, leaving the entire contract discoverable only by reading the TypeScript that rejects it.Closes #9770
Where the schema lives, and why the validator stays
Beside
parseBittensorAnchorReport, not in the spec file — the two are one contract described twice and must be edited together.The validator remains the runtime authority. It is not replaced. Each of its arms returns a named rejection identifying the exact field it refused, which is what makes a submitter bug diagnosable from the 400 body alone; a generic zod issue list would be a downgrade for precisely the off-repo caller this endpoint is built for.
They're held together by a cross-check suite: 25 payloads through both, asserting they agree on accept/reject. Neither can drift without failing CI. It covers the cases most likely to diverge —
status/backendRefandstatus/errorcoupling, the u16 netuid ceiling, non-integerseq, and blank-after-trimhotkey/error.Two bugs that fell out of actually generating the document
1. The hash regexes carried an
iflag. JSON Schema has no flags concept, so they serialized as:A generated client compiling that would require a literal
/isuffix and reject every valid hash. Case-insensitivity is now spelled into the character class ([0-9a-fA-F]) — behaviourally identical for hex, safe to publish. The published patterns are now^[0-9a-fA-F]{64}$and^0x[0-9a-fA-F]{64}$, and there's a cross-check case pinning that uppercase hashes still parse.2.
SpecEntrycouldn't describe a body. It declared onlyparamsunderrequest, even thoughregisterRouteSpecalready readsoptions.request?.bodyandRouteSpecOptionshas carriedbodysince #9705. The rendering path existed and was unreachable from this file. Widened to match the seam it feeds.Validation
ledger-anchor-bittensor.tsledger-anchor-bittensor+openapi-security-parity+define-routetypecheck·ui:openapi:check·contract:api-schemas:check·git diff --checkVerified against the regenerated artifact rather than assumed —
required: ["signed","status"],statusenum["ok","failed"], netuid bounds0–65535, and both hash patterns flag-free.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #123) — a linked open issue is required for every contributor PR.Safety
UI Evidencesection below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository.No runtime behaviour changes beyond the regex widening, which accepts exactly the same inputs the
iflag did. Auth is untouched: the route staysauth: "orb"(theLOOPOVER_LEDGER_ANCHOR_REPORT_TOKENingest bearer, which fails closed when unset), andopenapi-security-paritystill passes. The published schema describes a submitter's payload — a signed checkpoint, a status, and public on-chain coordinates. Thehotkeyfield is documented explicitly as an ss58 account, a public on-chain identity and never key material, so nobody reads the field name and infers a secret belongs there. No UI output, hence no UI Evidence table.Notes
The 422 rejection reasons #9770 also asked for (
unknown_key,bad_signature,row_not_found,row_hash_mismatch) were already documented upstream, including therow_not_foundnote explaining it is by design when the receiver does not hold the ledger — the constraint recorded on #9719. Left as-is.