Proposal: reserved reason codes for a refusal
| Status | accepted |
| Author | Claude Code for the maintainer. Written after the change landed (DR-184, task 2.2) |
| Date | 2026-10-05 |
| Issue | the second contract review, 5 October 2026; DR-183 D5 ("Refusal reasons: prose, or codes") |
| Lands in | PR #17, task/3.1-lease-record, merged 2026-10-05, with the record it belongs to; released in 1.2.0 |
Summary
A refusal's reasons are codes in three reserved forms, R<n>, runtime_error:<name> and
vendor:<vendor>:<code>, with the prose in reason, so two implementations compare refusals by
code and not by prose.
Motivation
1.1.0 gave the six refusal conditions of SPEC §5.2 the stable identifiers R1 to R6 for the narrowing
vectors, but a refusal record had nowhere to carry one. The reference journal had never written a
refused line, and a reader comparing two implementations' refusals would have been comparing
sentences.
Design
lease-record definition reasonCode: anyOf of ^R[1-9][0-9]?$,
^runtime_error:[a-z0-9_]{1,64}$ and ^vendor:[a-z0-9-]{1,32}:[A-Za-z0-9_.-]{1,64}$. R7 and above
are reserved for the specification to assign; an issuer must not mint one. runtime_error: names a
refusal the issuer could not avoid (no signing key, an unknown parent, a manifest that does not
validate); vendor: is namespaced by whoever defines it. The prose goes in reason. SPEC §5.2.
Backward compatibility
Additive: a new vocabulary on a new schema; no 1.1 record carried reasons. stability: stable.
Security
A code enters the journal where free text would have, and the prose stays in reason under the
same hygiene as the rest of the record. Nothing new reaches a permanent record.
Alternatives
Prose only: incomparable. A closed enum of R1 to R6: leaves an issuer no honest way to record that it could not find its signing key, which is a fact the journal must hold.
Decision
Accepted, 2026-10-05 (DR-183 D5). Shipped in 1.2.0: schemas/lease-record.schema.json
(reasonCode), SPEC §5.2 and §13, valid fixtures lease-record.refused and
lease-record.refused-runtime-error, invalid fixtures lease-record.reason-outside-reserved-forms
and lease-record.refused-without-reasons. No refused record existed in the reference deployment
on 2026-10-05 (fixtures/evidence/2026-10-05-lease-record.md), so the vocabulary refuses nothing
that was written; at its 1.2.1 pin the harness writes reasons as reserved codes with a prose
reason.