Skip to content

The `index.json` Rollup

Conformance language (MUST/SHOULD/MAY) follows BCP 14 [RFC2119]/[RFC8174] as defined in 00-overview.md.


Status and grade live on the namespace level (and the selection level), not on a per-schema sidecar file. There is exactly one index.json per namespace and one per selection. It is the rollup: a tree of tool → schema → namespace (provider flow) or member → selection (selection flow), where each node carries its newest grade (resolved via resolveLatest) and a rolled-up status.

This chapter supersedes the former Kanban phase-status contract (see 14-kanban-data-contract.md). The two salvaged rules from that contract — the audit trail and the irreversible veto — are restated normatively in Salvaged Rules: Audit Trail + Irreversible Veto.


Two Natures: Live-Rollup and Frozen lockSnapshot

Section titled “Two Natures: Live-Rollup and Frozen lockSnapshot”

index.json has two distinct parts with different lifecycles. Conflating them is an error.

Everything outside lockSnapshot is a live rollup: it is recomputed on every rebuild. rebuildNamespaceIndex / rebuildSelectionIndex walks the folder, runs resolveLatest on each _gradings/, builds the tree, and writes the file. The live rollup is the only overwritable artifact in the island — it is derived and 100% reproducible from the underlying grading entries and snapshots, which are themselves never overwritten. The rebuild MUST run after every grading write.

Frozen lockSnapshot (written once, preserved)

Section titled “Frozen lockSnapshot (written once, preserved)”

lockSnapshot is written exactly once, at grading start, and is preserved by every subsequent rebuild (the rebuild MUST NOT recompute it). It is a point-in-time pin of the member set. The pre-condition gate (21-pre-conditions.md) reads only the frozen lockSnapshot — otherwise an aggregate would be computed over members whose status drifts mid-run.

lockSnapshot reproduces the former lockfile fields: selectionId, selectionVersion, selectionHash, generatedAt, and per member { schemaId, schemaVersion, schemaHash, gradingStatus, override }.


There are two separate status vocabularies. They MUST NOT be interchanged.

Each primitive node (a tool, a schema, an about, a skill, a member) carries one of five status values:

StatusMeaning
pendingNot yet graded.
blockedCannot be graded right now; carries a reason (e.g. fewer than three tests, no about, API down). Repairable.
gradedA grade exists.
stableFully graded and above threshold; ready to use.
rejectedVeto — terminal and irreversible (see Irreversible veto — terminal status rejected).

graded and stable are node values.

The top-level namespace/selection rollup summarises its nodes with a different vocabulary:

StatusMeaning
operationalRolled-up summary: the namespace/selection is usable.
partialSome nodes graded/stable, others still pending/blocked.
blockedBlocked at the rollup level.
pendingNothing graded yet at the rollup level.
rejectedA veto propagated to the rollup.

operational and partial are rollup values. A node is never operational; a rollup is never graded.


For a selection, the rollup carries a member-resolution manifest — the heart of selection grading. For each member it records schemaId → resolved provider artifact + grade + status. Without this manifest the selection aggregate cannot reproduce its “M of N members PASS” verdict, because the member IDs in selection.json are logical and must be resolved (via resolveLatest) to a concrete graded provider artifact. The manifest makes that resolution explicit and auditable.


Salvaged Rules: Audit Trail + Irreversible Veto

Section titled “Salvaged Rules: Audit Trail + Irreversible Veto”

Audit trail — never delete, newest is current

Section titled “Audit trail — never delete, newest is current”

Grading entries and source snapshots MUST NOT be deleted or overwritten. A re-grading writes a new entry alongside the previous one. The current status of a node is always the newest entry (by timestamp; the rollup uses resolveLatest). Only the live part of index.json is rewritten on rebuild; the underlying entries, snapshots, and the frozen lockSnapshot are preserved.

Irreversible veto — terminal status rejected

Section titled “Irreversible veto — terminal status rejected”

A categorical veto maps to the node status rejected, which is terminal. The index derivation maps an aggregateGrade of REJECTED to status rejected. A rejected node MUST NOT be moved back to any other status by editing or deleting its entry; a veto can only be lifted by a fully new evaluation that writes a new entry, with the original veto entry preserved in the audit trail. The four closed veto triggers are defined in 09-security-and-development.md.


{
"indexVersion": 2,
"namespace": "defillama",
"updatedAt": "2026-05-31T12-30Z",
"status": "partial",
"grade": "B",
"summary": { "schemas": 2, "tools": 12, "toolsStable": 8, "about": "graded", "description": "graded", "skills": 1 },
"about": {
"status": "graded",
"grade": "B",
"ref": "prices/resources/about/_gradings/about-namespace--2026-05-31T11-20-00Z.json"
},
"description": {
"status": "graded",
"grade": "B",
"ref": "_gradings/namespace-description--2026-05-31T11-21-00Z.json"
},
"skills": {
"prices.summarizePrices": {
"status": "graded",
"grade": "B",
"ref": "prices/skills/summarizePrices/_gradings/namespace-skills--2026-05-31T11-23-00Z.json"
}
},
"namespaceAggregate": {
"status": "graded",
"grade": "B",
"ref": "_gradings/tools-aggregate-namespace--2026-05-31T11-22-00Z.json"
},
"schemas": {
"prices": {
"status": "graded",
"grade": "B",
"snapshot": { "file": "prices--2026-05-30T19-44-23Z--93baef35.mjs", "hash": "93baef35" },
"toolsAggregate": { "status": "graded", "grade": "B", "boundTo": "93baef35" },
"tools": {
"getFirstPrice": {
"status": "stable",
"grade": "A",
"ref": "prices/tools/getFirstPrice/_gradings/single-test--2026-05-31T11-05-00Z.json"
}
}
},
"coins": { "status": "pending", "reason": "not yet imported" }
},
"blockers": [
{ "node": "schemas.coins", "reason": "selection member, not imported" }
]
}

A selection index.json is analogous, with a lockSnapshot block and a members resolution manifest in place of schemas.


  • rebuildNamespaceIndex / rebuildSelectionIndex produces the live rollup from the folder.
  • The rebuild preserves the frozen lockSnapshot byte-for-byte.
  • The rebuild runs after every grading write.
  • The former per-namespace summary.json as an entry point is superseded by index.json; only the per-schema summary.json (phase-0 pretest data) remains.