Sub-issue of #9719. Decision-independent.
Problem
POST /v1/decision-ledger/anchor-attempts is, by design, a contract for a submitter that lives outside this repo — src/review/ledger-anchor-bittensor.ts:3-8 says so explicitly ("a small process on the operator's own node infrastructure… never in this repo"). An off-repo implementer's only reference is the OpenAPI document.
That document registers responses only (src/openapi/spec.ts:1948-1961), so apps/loopover-ui/public/openapi.json carries no requestBody. The full shape exists and is strictly validated in parseBittensorAnchorReport (ledger-anchor-bittensor.ts:49-91), but an implementer has to read TypeScript source to discover it:
{
signed: {
payload: { v, ledger, seq: int>0, rowHash: /^[0-9a-f]{64}$/, totalCount: int>0, at: string(<=40) },
keyId: string(<=64),
signature: string(<=512, base64)
},
status: "ok" | "failed",
backendRef: { netuid: int[0,65535], blockNumber: int>0, blockHash: /^0x[0-9a-f]{64}$/, hotkey: string(<=64) }, // when "ok"
error: string(<=500) // when "failed"
}
An endpoint whose entire purpose is to be called by code we do not ship, documented only in the source of the thing calling it, is a contract with no published surface.
Scope
Register the request body on the OpenAPI path, derived from the same validator rather than hand-copied — a second hand-maintained description of these bounds would drift, which is the failure @loopover/contract exists to prevent (#9521).
Also document the four named 422 rejection reasons (unknown_key, bad_signature, row_not_found, row_hash_mismatch). They are deliberately specific so a submitter bug is diagnosable from the response alone, and that is only useful if they are written down.
row_not_found deserves an explicit note, because it is the one an off-repo implementer will hit first and misdiagnose: an ok report is verified against the receiving deployment's own decision_ledger (ledger-anchor-bittensor.ts:113-115), so reporting an anchor for a ledger the receiver does not hold is rejected by design, not by misconfiguration. On api.loopover.ai that ledger is empty — see #9719.
Validation
npm run ui:openapi regenerates the committed artifact; ui:openapi:check enforces it. The existing parser tests in test/unit/ledger-anchor-bittensor.test.ts already pin the bounds, so the schema should be checked against them rather than asserted independently.
Sub-issue of #9719. Decision-independent.
Problem
POST /v1/decision-ledger/anchor-attemptsis, by design, a contract for a submitter that lives outside this repo —src/review/ledger-anchor-bittensor.ts:3-8says so explicitly ("a small process on the operator's own node infrastructure… never in this repo"). An off-repo implementer's only reference is the OpenAPI document.That document registers responses only (
src/openapi/spec.ts:1948-1961), soapps/loopover-ui/public/openapi.jsoncarries norequestBody. The full shape exists and is strictly validated inparseBittensorAnchorReport(ledger-anchor-bittensor.ts:49-91), but an implementer has to read TypeScript source to discover it:An endpoint whose entire purpose is to be called by code we do not ship, documented only in the source of the thing calling it, is a contract with no published surface.
Scope
Register the request body on the OpenAPI path, derived from the same validator rather than hand-copied — a second hand-maintained description of these bounds would drift, which is the failure
@loopover/contractexists to prevent (#9521).Also document the four named 422 rejection reasons (
unknown_key,bad_signature,row_not_found,row_hash_mismatch). They are deliberately specific so a submitter bug is diagnosable from the response alone, and that is only useful if they are written down.row_not_founddeserves an explicit note, because it is the one an off-repo implementer will hit first and misdiagnose: anokreport is verified against the receiving deployment's owndecision_ledger(ledger-anchor-bittensor.ts:113-115), so reporting an anchor for a ledger the receiver does not hold is rejected by design, not by misconfiguration. On api.loopover.ai that ledger is empty — see #9719.Validation
npm run ui:openapiregenerates the committed artifact;ui:openapi:checkenforces it. The existing parser tests intest/unit/ledger-anchor-bittensor.test.tsalready pin the bounds, so the schema should be checked against them rather than asserted independently.