The .qual format
Append-only JSONL. One record per line. VCS-native by design.
A .qual file is a UTF-8 encoded file where each line is a complete JSON object representing one record. This is JSONL (JSON Lines).
{"metabox":"1","type":"attestation","subject":"src/parser.rs","issuer":"mailto:alice@example.com","issuer_type":"human","created_at":"2026-02-24T10:00:00Z","id":"a1b2c3d4...","body":{"kind":"concern","ref":"git:3aba500","score":-30,"summary":"Panics on malformed input"}}
{"metabox":"1","type":"attestation","subject":"src/parser.rs","issuer":"mailto:bob@example.com","issuer_type":"human","created_at":"2026-02-24T11:00:00Z","id":"e5f6a7b8...","body":{"kind":"praise","score":40,"summary":"Excellent test coverage"}}
Record types
Every record has a type field that identifies its schema. Qualifier defines three record types:
| Type | Description |
|---|---|
attestation |
A quality signal (the primary type) |
epoch |
A compaction snapshot |
dependency |
A dependency edge between subjects |
When type is omitted, it defaults to "attestation". Unknown types are preserved as opaque pass-through data.
Record envelope
All record types share a common Metabox envelope — a fixed set of fields that answer "who said what about which subject, when", plus a type-specific body object. The record envelope is an instance of the Metabox envelope format.
| Field | Type | Required | Description |
|---|---|---|---|
metabox |
string | yes | Envelope version (always "1") |
type |
string | yes* | Record type identifier. *Defaults to "attestation". |
subject |
string | yes | Qualified name of the target artifact |
issuer |
string | yes | Who or what created this record (URI) |
issuer_type |
string | no | Issuer classification: human, ai, tool, unknown |
created_at |
string | yes | RFC 3339 timestamp |
id |
string | yes | Content-addressed BLAKE3 hash |
body |
object | yes | Type-specific payload |
Attestation schema
Attestations are the primary record type. Envelope fields plus body:
| Field | Type | Required | Description |
|---|---|---|---|
detail |
string | no | Extended description (markdown allowed) |
kind |
enum | yes | Type of attestation (see below) |
ref |
string | no | VCS ref pin (e.g. "git:3aba500"), opaque string |
score |
integer | yes | Signed quality delta, -100..100 |
span |
object | no | Sub-artifact range (line/col addressing) |
suggested_fix |
string | no | Actionable suggestion for improvement |
summary |
string | yes | Human-readable one-liner |
supersedes |
string | no | ID of a prior attestation this replaces |
tags |
string[] | no | Freeform classification tags |
Body fields are in alphabetical order (MCF canonical form).
Attestation kinds
| Kind | Default Score | Meaning |
|---|---|---|
pass |
+20 | Meets a stated quality bar |
fail |
-20 | Does NOT meet a stated quality bar |
blocker |
-50 | Blocking issue, must resolve before release |
concern |
-10 | Non-blocking issue worth tracking |
praise |
+30 | Positive recognition of quality |
suggestion |
-5 | Proposed improvement (often with suggested_fix) |
waiver |
+10 | Acknowledged issue, explicitly accepted |
When --score is omitted from qualifier attest, the CLI uses the default score for the given kind.
Epoch schema
An epoch is a compaction snapshot — a synthetic record that replaces a set of attestations with a single scored record preserving the net score. Envelope fields plus body:
| Field | Type | Required | Description |
|---|---|---|---|
refs |
string[] | yes | IDs of the compacted records |
score |
integer | yes | Net score at compaction time |
span |
object | no | Sub-artifact range |
summary |
string | yes | "Compacted from N records" |
Epoch issuer is always "urn:qualifier:compact".
{
"metabox": "1",
"type": "epoch",
"subject": "src/parser.rs",
"issuer": "urn:qualifier:compact",
"issuer_type": "tool",
"created_at": "2026-02-25T12:00:00Z",
"id": "f9e8d7c6...",
"body": {
"refs": ["a1b2...", "c3d4..."],
"score": 10,
"summary": "Compacted from 12 records"
}
}
Dependency schema
A dependency record declares directed edges from one subject to others. Envelope fields plus body:
| Field | Type | Required | Description |
|---|---|---|---|
depends_on |
string[] | yes | Subject names this subject depends on |
{
"metabox": "1",
"type": "dependency",
"subject": "bin/server",
"issuer": "https://build.example.com",
"created_at": "2026-02-25T10:00:00Z",
"id": "1a2b3c4d...",
"body": { "depends_on": ["lib/auth", "lib/http"] }
}
Dependency records don't carry scores. They feed the propagation engine that computes effective scores.
Supersession
Attestations are immutable. To "update" a signal, write a new attestation with body.supersedes pointing to the prior ID. Only the latest in a chain contributes to scoring.
Content-addressed IDs
Record IDs are BLAKE3 hashes of the Metabox Canonical Form (MCF) — a deterministic JSON serialization with fixed envelope field order, alphabetical body field order, no whitespace, and id set to "" during hashing.
{
"metabox": "1",
"type": "attestation",
"subject": "src/parser.rs",
"issuer": "mailto:alice@example.com",
"created_at": "2026-02-24T10:00:00Z",
"id": "",
"body": {
"kind": "concern",
"score": -30,
"summary": "Panics on malformed input"
}
}
Optional body fields (span, detail, suggested_fix, tags, ref, supersedes) and the optional envelope field issuer_type are omitted from the canonical form when absent — the hash changes only when a field is actually present.
This ensures identical records always produce identical IDs, regardless of implementation language.
Compaction
Append-only files grow. Compaction reclaims space:
qualifier compact src/parser.rs # prune superseded
qualifier compact src/parser.rs --snapshot # collapse to epoch
qualifier compact src/parser.rs --dry-run # preview first
Compaction MUST NOT change the raw score. If it does, the implementation has a bug.
File placement
| Strategy | Example | Tradeoff |
|---|---|---|
| Per-directory | src/.qual |
Clean tree, good merge behavior |
| Per-file | src/parser.rs.qual |
Maximum merge isolation |
| Per-project | .qual at root |
Simplest setup, more contention |
The recommended layout is one .qual file per directory. qualifier attest defaults to this.
Dependency graph
Qualifier consumes a dependency graph as qualifier.graph.jsonl:
{"subject":"bin/server","depends_on":["lib/auth","lib/http","lib/db"]}
{"subject":"lib/auth","depends_on":["lib/crypto"]}
{"subject":"lib/http","depends_on":[]}
The graph MUST be a DAG. Cycles are rejected.