Change: CE-6 of the Context Engine (Epic-2049, RFD
pramaan-functions docs/notes/rfd-context-engine.md §5.1 / §3 / §4).
It gives Blake (the registrar) a new capability: from the same document he registers, produce
the L1 document_card.v1 — what the document is
(type, dates, parties, amounts, 3–12 key assertions) — with a byte-verifiable
SourceSpan on every evidential field. Provenance is law: no naked facts.
A card field is only valid if its quote is byte-present in the exact
document_pages block it claims (RFD §4, the anti-hallucination gate the
>20% kill criterion rides on). The model cannot count bytes, so it never invents
char offsets: it names a page + block + a verbatim
quote. Code does the rest — a byte-exact block_text.find(quote):
found → compute char_start/char_end, mint the SourceSpan (RESOLVED) absent → drop the field (REJECTED)
So "the model fabricated a quote" and "the span fails to resolve" are the same event,
measured deterministically, offline, per-PR. str.find is exact — no
whitespace normalization, no case-folding ("a court reads the bytes").
| File | Role |
|---|---|
agents/registrar/context_contracts/ | Vendored CE-1 DocumentCard closure (DocumentCard, SourceSpan, CitedValue, Card*, EnrichmentStamp), byte-faithful to pramaan-functions src/shared/context_contracts/ (commit 5b20208). A CodeZip brain can't import that tree, so we vendor + drift-guard, exactly as Atlas vendors query_matters_server_params.json. Do NOT redefine the shape. |
agents/registrar/anchor.py | Pure plumbing: the page/block-tagged evidence renderer, the byte-exact resolve_quote_span, the SpanGateResult reject-rate metric, the sp1: span-id composite, and measure_token_inflation. |
agents/registrar/card_models.py | The flat generation DTO RegistrarCardOutput (shallow, Bug-1357-safe) and assemble_document_card — the bridge that byte-resolves every quote into the canonical span-carrying card, dropping what won't resolve. |
agents/registrar/prompts/card.py | Blake's card system prompt (PROMPTING.md: teach-by-contract, recursive injection fence, provenance-is-law, self-check-before-emit) + the anchored user-prompt builder. |
agents/registrar/agent.py | _card_agent + extract_document_card — the fail-closed producer that NEVER raises. |
agents/registrar/runtime.py, events.py | A new card mode that emits a document.card effect the broker persists (CE-7 ingest boundary). Off the classify turn. |
agents/registrar/evals/card_golden_corpus.jsonl, evals/card_gate.py | The kill-criterion harness: a golden Indian-legal corpus + the deterministic reject-rate gate and the periodic live measurement. |
The card is a separate pass, not part of the interactive classify turn (RFD §5.1; matches CE-7's separate SQS enrich kick). Two guarantees, both test-asserted:
extract_document_card is fail-closed — any producer error (model stall,
timeout, missing provider, bad output) returns a typed status="error" result,
it never raises.run.completed (never run.failed), so the
enrich kick returns and the registration consumer is never blocked.| AC | Proof |
|---|---|
| card failure never blocks registration apply (test-asserted) | test_registrar_card_isolation.py — extract never raises (provider-down, model explodes); classify never calls the producer; card mode completes on failure, never run.failed. |
| anchored-input token inflation measured | anchor.measure_token_inflation + test_anchored_input_token_inflation_is_measured — flat vs anchored token counts + ratio, serialized for the record. |
| < 20% span rejects on golden corpus (kill criterion) | test_registrar_card_golden_corpus.py over card_golden_corpus.jsonl — well-formed corpus resolves at 0% (far under 20%); fabricated cases ARE rejected (the gate has teeth). Live Blake reject-rate is evals.card_gate.measure_live_reject_rate (periodic, needs a key). |
uv run --package registrar pytest tests -q # 270 passed uv run ruff check agents/registrar/ # All checks passed! python scripts/check_brain_egress.py # brain-egress check: clean python scripts/check_prompt_leakage.py # prompt-leakage check: clean uv run pytest tests/ -q -m unit # 170 passed (governance)
Brain stays config+brain only (pramaan-docs ADR-110 §6): the card producer
holds no DB/DSN, calls no tool, and its only egress is the inference provider. The
context_contracts package is pure (no sqlalchemy/boto3). The broker-side card
persistence (CE-7) and the registration-consumer enrich kick are downstream stories.