Show HN: ARF – a record format for AI evaluation runs, with reproducible digests

akshay_bhardwaj1 pts0 comments

ARF - a record format for evaluation runs | KorvoAtlas Record Format · v0.1<br>ARF - a record format for evaluation runs<br>So a score can be traced back to the question, model, inputs and judgement that produced it. Records canonicalize to identical bytes, so two implementations reach the same SHA-256 digest.<br>Korvo Atlas is the reference implementation.<br>View on GitHub<br>Stability<br>v0.1 is unstable and may change without a migration path before v1.0. Treat published shapes as provisional.<br>Disagree with a shape, or have a case it cannot express? Open an issue - v0.1 is the right time to break it.

What breaks today<br>Most evaluation pipelines end in a CSV of scores. Weeks later a row says 0.82, and nobody can say which prompt version, which model build, or which judging step produced it.<br>The cost lands on whoever has to defend the number: the engineer asked why a regression appeared, the reviewer asked what the score covered, the auditor asked to see the inputs. The scores survive. The context that made them mean anything does not.

What ARF is<br>Ten JSON record types and one hashing rule. ARF is not a new format - it is the format Korvo Atlas already implements, written down so the same bytes and digests can be checked outside the product.<br>Validation is fully offline: no network calls, and schema $id values are never dereferenced. Those identifiers (namespace path korvo.xyz/arf/ns/v0.1) name types - they are not fetch targets, and nothing serves them yet.

A record, in full<br>Every ARF record is a plain JSON object. This is a real Claim, copied from claim.ndjson.<br>Claimexamples/claim.ndjsonCopy{<br>"id": "6251dbd4-a1f2-4b8b-9cd0-594a46a2f5c3",<br>"statement": "Training language models on their own synthetic outputs leads to progressive degradation of output quality, a phenomenon termed \"model collapse\".",<br>"confidence": "high",<br>"sourceIds": ["bafb465b-47be-45fd-b94f-ea6599153e4f"],<br>"questionId": "7ebb02e3-c3db-4010-a03c-13d7e7ca4df8",<br>"status": "published",<br>"tags": ["model collapse", "synthetic data"],<br>"origin": "human",<br>"reviewStatus": "unreviewed",<br>"createdBy": "Alice Chen",<br>"createdAt": "2026-03-25T20:17:56.727Z",<br>"updatedAt": "2026-03-25T20:17:56.727Z"<br>statementThe assertion itself - one claim per record, never a paragraph containing several.<br>sourceIdsThe inputs this claim rests on. At least one is required, and order is significant: reordering it changes the digest.<br>questionIdThe question this claim answers. Claims point at questions, not the reverse.<br>confidenceHow confident the author is - one of low, medium, high, verified. It is a stated position, not a computed score; engine verdicts live on Evaluation instead.<br>Records are stored one per line as .ndjson. Unset means the key is absent - ARF never writes null to mean “no value”.

How records reference each other<br>Every link is a UUID held in a field on the pointing record. Read X --field--> Y as “X.field holds the id of Y”.<br>Reference directionCopyClaim --questionId----> Question<br>Claim --sourceIds-----> Source<br>Artifact --claimIds------> Claim<br>Artifact --sourceIds-----> Source<br>Evaluation --claimIds------> Claim<br>Challenge --claimId-------> Claim<br>Challenge --challengerId--> Validator<br>Endorsement --claimId-------> Claim<br>Endorsement --validatorId---> Validator<br>Revision --artifactId----> Artifact<br>ChainRecord --artifactId----> Artifact

ArtifactBundle { artifact, question, claims[], sources[], publishedBy, publishedAt }<br>this whole object is the value that gets hashed<br>An ArtifactBundle is the publish-time envelope: one artifact, its question, and the claims and sources it used, collected into a single JSON object. That object is the thing that gets hashed.

The ten record types<br>Grouped by role. Required fields are exactly those marked required in each schema.<br>Inputs<br>What the run was given.

Questionexamples/question.ndjson<br>The question under evaluation.<br>required: id, text, status, createdAt

Sourceexamples/source.ndjson<br>An input that can be cited.<br>required: id, type, title, createdAt

Artifactexamples/artifact.ndjson<br>A written output assembled from claims and sources.<br>required: id, title, type, body, claimIds, sourceIds, createdAt

Assertions<br>What was asserted, and what an engine concluded.

Claimexamples/claim.ndjson<br>One assertion, plus the sources it rests on.<br>required: id, statement, confidence, sourceIds, createdAt

Evaluationschemas/evaluation.json<br>An engine result about claims. The engine payload is stored verbatim.<br>required: id, claimIds, engine, protocolVersion, payload, createdAt

Review<br>Who checked it, and what they said.

Validatorexamples/validator.ndjson<br>A human or agent that reviews claims.<br>required: id, name, type, createdAt

Challengeexamples/challenge.ndjson<br>A recorded objection to a claim.<br>required: id, claimId, challengerId, reason, createdAt

Endorsementexamples/endorsement.ndjson<br>Recorded support for a claim, carrying a weight.<br>required: id, claimId, validatorId, weight, createdAt

Lineage<br>How an artifact changed, and what was...

claim required record ndjson createdat evaluation

Related Articles