Consumer conformance record

As a Singular integrator, I can replay the current conformance interface, distinguish partial evidence from completed coverage, and trace older observations to the candidate that produced them. The connected naming release journey is documented separately in recovery and permanent retirement.

Permanent contract and preserved consumer evidence

The first milestone's fixed contract admits registration (insertActive) and permanent termination (updateTerminal). All seven edge values remain encodable in request datums and fold redeemers, but witnessTerminal, insertAbsent, updateActive, deleteAbsent and deleteActive fail at the permanent state validator, including independently constructed requests and mixed batches. Application payload updates leave the registry leaf unchanged; they are not the excluded updateActive registry transition.

The seven-edge interface and older receipts below describe the preserved broader consumer contract. They do not establish current permanent-contract successes. Its generic consumer expectations remain recorded, and the new permanent requirement remains uncovered without a bounded live consumer comparison receipt. The candidate evidence separates compiled refusal tests, connected CLI execution and their limits. Defined redeemers, missing CLI commands and setup failures are not evidence of behavioral refusal.

The contract and its binding

The rows answer to the consumer contract source-bound to lambdasistemi/cardano-keri@14a64a4681d3e429fab5877062b5c476c2a4bfe2: the registry design rulings (eleven operator rulings and fourteen registry theorems) and docs/user/consumer-checklist.md. cardano-keri consumes the generic registry — Insert, Update, Delete — not the naming application, whose restriction refuses Delete and reuse by design. No naming evidence can establish any row here, and none is claimed.

The canonical identity rows (#69) carry the correction epic 16 executed: cardano-keri's ruling is that the registry provides identity uniqueness, but a permissionless ledger cannot prohibit a rival registry — a consistent initialization from a second seed passes every frozen check and the node accepts it (offchain/naming-correspondence.md, "What t50 settled"). Canonical identity is therefore a derivation the consumer authenticates, not a refusal the chain performs: the canonical registry token's name is SHA-256 of the canonical seed's outRef, that seed can never be spent twice, and a rival from another seed can never carry that name.

The denominator

conformance/rows.json carries 47 rows, 46 owned: one permanent contract requirement, five registry identity requirements, twenty-four registry operation requirements, eight serialization requirements, five consumer resolution requirements and three execution-limit requirements, plus the checkpoint and treasury policy (bonds, poison, the juvenility window W, the signature threshold) recorded as out of scope — it belongs to cardano-keri's checkpoint machine and treasury, explicitly never claimed by Singular. list prints the owned denominator separately so the boundary stays visible. Superseded and held rows stay in the denominator and visible to completeness accounting: relabelling a row pays no coverage and shrinks no board.

rows.json never carries executed: the declared field is the coverage plan and the parser rejects the string. A row prints as executed only when a run receipt for it exists and matches the current base (--receipts DIR or CONFORMANCE_RECEIPTS).

The registry-mode contract change

This is a contract change, declared here rather than discovered by a consumer through a failing decode. Anything that reads Singular's datums or builds its redeemers changes with it.

The state datum: six fields become eight

# before after
0 root root
1 maxFee maxFee
2 processTime processTime
3 retractTime retractTime
4 repPolicy applicationPolicy
5 consumerPin activePolicy
6 — absentPolicy
7 — terminalPolicy

consumerPin is deleted with the pinned consumer script it named; the mandatory withdrawal that script required is deleted with it. repPolicy is renamed activePolicy and keeps its meaning — the policy that mints the token standing for a live registration. Three pins are new: the application policy that certifies requests, and the absent and terminal token policies.

A decoder that reads field 4 as the representative policy now reads the application policy, and a decoder that reads six fields refuses an eight-field datum. Both are the intended consequence, and both are exercised: the state round-trip row round-trips all four derived pins through a boot datum, and its armed control demands the old six-field encoding and fails.

None of the four is a literal. Each is derived, for the registry identity a boot creates, from the two partitions' script-identity.json: the application policy is the naming application script's applied hash, and the three token policies are the one parametrised witness script applied at kinds 0, 1 and 2.

The operation: a fourth constructor

index before after
0 Insert value Insert value
1 Delete value Delete value
2 Update old new Update old new
3 — Read value

The three published indices do not move. A read leaves its key exactly where it is, and on chain only the terminal codec byte is admitted for it.

The request: a destination, then an edge index

Request gained one field, appended last: destination, a pair of the binary address bytes the minted token must land at and the inline datum that output must carry, as an Option: Some carries the datum itself, None means a datum-less output. The request carries the datum rather than its hash (#419), so anyone can build the fold from the chain alone; an approval still binds the datum by its BLAKE2b-256 hash, empty for none. A request that mints nothing names no destination.

Issue #183 then re-cut the request itself. Field for field, old wire beside new:

field old wire final wire (#183)
0 requestToken requestToken
1 requestOwner requestOwner
2 requestKey requestKey
3 requestValue: Operation — Insert v / Delete v / Update old new / Read v edge: Int — the seven-edge table index, 0-6
4 tip: Int — must equal state.tip deposit: Int — must equal the request output's lovelace less state.tip
5 submitted_at submitted_at
6 destination destination

The constructor index does not move: RequestDatum is 0 before and after, and the four unchanged fields keep their positions. What changes is what fields 3 and 4 mean.

The seven edges, with the trie move each names and the value bytes that used to carry it:

edge name trie move old wire
0 insertAbsent insert 0x00 Insert 0x00
1 insertActive insert 0x01 Insert 0x01
2 updateActive update 0x00→0x01 Update 0x00 0x01
3 updateTerminal update 0x01→0x02 Update 0x01 0x02
4 deleteAbsent delete 0x00 Delete 0x00
5 deleteActive delete 0x01 Delete 0x01
6 witnessTerminal read 0x02 Read 0x02

What a consumer has to change. Write the edge index where the operation payload used to go, and the deposit — the lovelace the request output holds, less the state's tip — where the tip used to go. An edge names its own leaf bytes, so a consumer no longer chooses them and can no longer get them wrong.

Refusals that go away, because no request can express them. leaf-codec (a value byte outside the three-byte codec), insert-terminal, tip-mismatch, update-to-absent, update-absent-to-terminal and update-active-to-active are all faults of value bytes that the edge wire does not carry.

Refusals that remain, and the two that are new. A read whose key does not bind 0x02 is refused read-absent, read-non-terminal or key-unknown, and a tree-changing edge whose key binds 0x02 is refused edge-from-terminal — all read off the trie rather than off the request. New with this wire: edge-inadmissible for a tag outside 0-6, and deposit-mismatch for a deposit that is not the holding less the tip.

The cage datum: a third constructor

index before after
0 RequestDatum RequestDatum
1 StateDatum StateDatum
2 — AbsentCustody { refund }

The absent custody key is the identity of the output's sole non-ADA asset, which must have quantity one under the registry's pinned absent policy. It is not duplicated in the datum. The retired two-field constructor-2 wire is therefore incompatible and must not decode as AbsentCustody.

Redeemers that are gone

The naming application's spend redeemer becomes Maintain, Retire, Recover; Fold and Cancel are removed with the pending-claim lifecycle they served. Its mint redeemer becomes the single Approve { edge, key, owner, destination }; WithdrawApproval and InsertApproval are removed. The representative policy is retired in favour of the one parametrised witness policy, instantiated three times.

What a reader should do

Read tokens, never the root. The presence of an active witness under the active policy, at the asset name equal to the registry key, is a live registration; the presence of a terminal witness is a name that is over. The asset name is the key itself — there is no hashed representative name any more.

Current registry-mode integration

The released interface follows the eight-field registry-mode state contract above. The historical reports below retain their original transaction identities, measurements and limitations; they are not current execution claims. Coverage requires fresh receipts bound to the source and blueprint being replayed. A build or a green check of expected debt grants no coverage.

Surface Current contract and remaining limit
State codec, blueprint-encoding-round-trip/submitted-datum-byte-round-trip/state-fields-chain-round-trip The eight fields are root, maxFee, processTime, retractTime, applicationPolicy, activePolicy, absentPolicy and terminalPolicy. The state round-trip row boots two cages and compares all eight fields submitted versus chain-observed, including the four derived pins; its armed control demands the old six-field encoding and fails. Historical owner, stake_script and consumer-pin receipts do not establish this codec.
Blueprint encodings, blueprint-encoding-round-trip Every encoder/decoder pair in the registry types is compared against the compiled blueprint's own declared schema, constructor index and field order — including the appended read operation, the appended request destination and the appended custody datum. The retired consumer encoder is gone with its script.
Parameters, script-parameter-application State and staking declare zero parameters. Request retains two ordered parameters, statePolicyId and cageTokenName, and order discrimination. The witness policy declares two, kind and registry. The former state allowlist is absent.
Address derivation, applied-validator-identity State uses the production derivation compared with the actual chain address. The same comparison rejects an erroneous extra application with every other input fixed. Its receipt records addresses, arity, decisions and the off-chain identity venue; it is not a phase-2 rejection. Request retains applied-versus-unapplied address discrimination.
Generic observations, empty-fold/request-value-and-refund-routing Both require observe-and-report and remain held-q002; surplus-fold-actions has no counterpart in Singular's model and is unmet by ruling (lambdasistemi/singular#345). Current refusals need structural phase/script attribution and an accepting control; a missing named failure branch remains an explicit limit. An observation does not fulfill the consumer requirement.

Historical execution reports

Historical scope: every report from here through “Historical limits” describes the earlier source revisions named in its provenance. Present-tense wording in those retained reports belongs to those revisions. The current contract above supersedes their owner fields, state arity, accepting-path expectations and pending-E17 descriptions. Do not use their measurements or receipts as evidence for the current candidate.

Devnet sessions run in order, each on an isolated node with its own published world: the generic registry rows first — the four issue-#63 rows plus the issue-#70 rows below — then the canonical identity rows, then the serialization boundary rows. Two further checks, blueprint-encoding-round-trip and script-parameter-application, never start a node at all: they compare Haskell values against the compiled blueprint read at run time, so a reader totalling "rows executed on a real devnet" counts the thirteen generic, five canonical-identity and five serialization rows below — never the local checks. The counts are kept visibly separate for exactly that reason.

The generic registry rows

Against a real devnet, one cage, one key, in order:

row outcome evidence
update-existing-key generic Update v1→v2 accept inclusion proof for v2 implies the chain-read root; forged-value proof does not (control)
delete-existing-key generic Delete accept exclusion proof verifies against the chain-read root (empty trie); pre-delete proof bound to the deleted value does not (control)
reinsert-deleted-key re-Insert v3 accept inclusion proof for v3 implies the chain-read root; forged-value proof does not (control)
insert-occupied-key Insert on occupied key refuse hand-built fold submitted; node refuses in phase 2, attributed to the state script (874e476d…, CekError); fresh cage accepts a valid insert (control)

insert-occupied-key's refusal is a node verdict on a submitted transaction, the li-refusals bar: the fold is hand-built (the library builder cannot emit a transaction whose scripts do not evaluate) and calibrated against the library builder on every valid fold — same inputs, state output, refund destinations and Modify proofs — so the refused shape differs from a library fold only in the operation under test. The eval-time refusal the builder reports first is kept as a log line, never as the verdict. Each receipt records where its refusal was observed (venue: node-submit for every row here), and refusal reasons are trimmed to their attribution (failure class, script hash, machine error) — receipts stay under a run-enforced 16KB bound, because a receipt nobody can open is weak evidence.

Had the chain accepted the occupied insert, that would be a finding reported with the accepted transaction — never relabelled as a refusal.

Model-compared registration and retirement candidate (#229)

A local clean candidate at 9b42458ffc1fab6bf9b7cf88ef44884e24333b8b ran the registration and retirement chapters on a Cardano devnet. Its retained receipts carry five and seven model-and-chain step records respectively. The executable driver supplies the model outcome and all nine declared observations for each accepted step; the reader-facing book renders the story programs from those receipts. This is candidate evidence, not merge, release or independent acceptance.

User-visible condition Recorded result
Two distinct active registrations accepted and compared after each fold
Repeating an occupied key refused by the state script, beside accepted registrations
Redirecting active-token delivery refused by the state script; the same request without redirection was accepted
Retiring a token from the immediately preceding registration accepted, with burn and terminal leaf compared
Retiring an Absent key or a never-registered key each refused by the state script beside an accepted retirement control

The node did not supply a Plutus trace name for the refused folds. The receipts retain the script hash and the empty trace rather than inferring a branch name. An accepting control establishes that the builder can fold a valid request in the same run.

The former two-request batch allocation check is no longer a current conformance claim. The driver evaluates one request per transaction, so the specified per-kind-and-key batch mint rule has no executable consumer in these chapters. The old batch evidence remains historical to its own revision. The former receipt-field appendix examples were removed with their schema; new appendix checks validate the generic step records.

The wire these rows and the archive verb speak

Everything on this page, and the release archive's packaged insert-active command, runs on the final request encoding: the edge index at field 3 and the deposit at field 4, as set out above.

The re-cut carried its own enforcing check rather than being asserted. For exactly one commit the cage compiled BOTH encodings, and every other-edge fixture was folded under each of them and required to give the same verdict with the same refusal reason, receipted per fixture. The commit after it removed the old codec. That pair — the receipt at the dual-codec commit, the removal at its successor — is the evidence a one-commit rewrite cannot produce, and it is what stands behind the side-by-side table above.

The issue-70 generic rows

The table preserves the eleven requirements and their historical observations. The current generic session runs ten rows, followed by its session receipt; the retraction-window requirement now runs separately as a three-step story compared with the model. The remaining bespoke refusal rows attribute the refusal to the script that failed and retain their accepting controls.

row outcome evidence
Retract outside phase 2 refuse before and after; accept inside The dedicated story submits three owner-signed retractions with finite validity bounds. Both outside-window attempts must be refused by the request script and by the model for not-phase2; the same first request retracted inside its window is accepted by both. The second request is booked alongside the first in the same registry, with the same owner and edge, and shares its accepting control. The receipt records each comparison.
reject-before-deadline-consumer-requirement a folder must not reject a request during its processing window accept — unmet-by-ruling (historically refuse) the consumer requires that a folder must not reject a request during its processing window (cardano-keri R9_reject_needs_rejectable, 0e638fad, RegistryGoals.lean blob d38e81c0); Singular's model lets a folder reject a request with no timing admission, and since #320 the chain accepts that reject. By operator ruling 2026-10-01 the requirement is kept unmet: the row is recorded unmet-by-ruling and never counted as passing, and alignment is cardano-keri#468; the same reject refunding the owner one lovelace short is refused in-run first, attributed to the state script (control). Historically the node refused the reject at script validation, attributed to the state script (ce7615f6…), and the same request rejected by the library after its retraction window was accepted in-run
fold-against-superseded-root fold with stale proofs refuse — unmet-by-ruling stale proofs against a superseded root refused, attributed to the state script; the same shape folded against the live root is accepted in-run (control) — the refusal is the staleness, not the shape. The chain refuses; Lean has no counterpart to compare with. By operator ruling 2026-10-02 ("Narrow #287") the row is recorded unmet-by-ruling, never a pass; follow-up lambdasistemi/singular#346
empty-fold empty fold accept — held-q002 the chain accepts a fold carrying no actions (tx b670c28e…); with one live request waiting, empty actions are refused in-run (control) — the acceptance is specific to the empty fold
surplus-fold-actions surplus action refuse — unmet-by-ruling two actions over one request are refused, attributed to the state script (traced reason surplus-actions); one action fewer than there are requests is refused too (missing-action), and an exact one-to-one fold is accepted in-run (control). The chain refuses; Lean has no counterpart to compare with. By operator ruling 2026-10-02 ("Narrow #287") the row is recorded unmet-by-ruling, never a pass; follow-up lambdasistemi/singular#345. Historically, before the cage repair, the surplus was accepted (tx facebfe6…)
historical-owner-change owner change via Modify accept — defect evidence the chain accepted a Modify changing the state owner to 0xab…ab, signed by the previous owner (tx 78e49eea…); resolved-by-ruling: the registry has no owner role at all, so the transfer is a privilege that must not exist — retained as defect evidence of the outstanding owner gate
historical-non-owner-sweep sweep by a non-owner refuse — superseded claim the sweep is refused, attributed to the request script (7f32c3e7…); the same sweep signed as the owner is accepted in-run (control). SUPERSEDED: it asserted registry-owner authority, which does not exist — observation preserved, conformance claim withdrawn
request-value-and-refund-routing crossed refunds accept — held-q002 bonds of 5 and 3 ada refunded crossed (4 and 2 ada, aggregate exactly the validator's ceiling), accepted (tx 566ddc80…); refunds totalling below the aggregate floor are refused in-run (control) — the range is real, and it is still not the requirement
historical-permissionless-fold permissionless fold accept after the #79 repair the fold with NO owner signer is accepted (tx 4142f7d6…, mem 717070 / cpu 231585673 / size 11442); the same fold WITH the owner signer is accepted in-run (control) — see F-002 below
retired-stake-hook-with-withdrawal / retired-stake-hook-without-withdrawal stake_script hook retired nothing runs them: the registry interface (registry mode) has no stake_script hook and the model has no withdrawal. History: the pinned staking credential could not register (MissingScriptWitnessesUTXOW without the witness, cert-purpose CekError with it), so the hook never reached a ledger

The retraction story gives the registry thirty seconds for processing and thirty more for retraction. Its waits follow the submission times read from the booked requests and those registry durations. The model reads the witness from each built transaction; the ledger refusal is attributed by the applied request script hash, without claiming an observable live refusal name. Open validity intervals remain a named gap: the model represents finite bounds only, while the Aiken tests establish the open-interval not-phase2 refusal.

Four dispositions, never to be mistaken for one another.

Unmet by ruling (unmet-by-ruling; reject-before-deadline-consumer-requirement, fold-against-superseded-root, surplus-fold-actions, wrong-redeemer-constructor-index): executed, and kept unmet by an operator ruling. The one verdict carries two meanings, and every row states which. reject-before-deadline-consumer-requirement: the registry deliberately does not do what the consumer requirement states, kept unmet by operator ruling 2026-10-01. The consumer requires that a folder must not reject a request during its processing window (cardano-keri R9_reject_needs_rejectable, 0e638fad, blob d38e81c0); Singular's model lets a folder reject a request with no timing admission, and the chain accepts that reject (#320). Alignment is cardano-keri#468. fold-against-superseded-root, surplus-fold-actions and wrong-redeemer-constructor-index: the chain refuses, Lean has no counterpart to compare with, kept unmet by operator ruling 2026-10-02 ("Narrow #287"); follow-ups: fold-against-superseded-root lambdasistemi/singular#346, surplus-fold-actions lambdasistemi/singular#345, wrong-redeemer-constructor-index lambdasistemi/singular#347. The session ends non-zero while any row is unmet; it is never a pass. Held (held-q002; empty-fold, request-value-and-refund-routing): executed, with the consumer requirements still unmet. empty-fold recorded a conflict with the pre-revision Singular model; rejecting empty batches is now approved. request-value-and-refund-routing requires the operation-specific value-routing repair. The rows move only after the required implementation and execution evidence is accepted. Resolved-by-ruling (historical-owner-change): a ruling settled the row's question; the observation is retained as defect evidence of the outstanding owner gate — never a pass, never an owner-semantics claim. Superseded (historical-owner-signed-sweep, historical-non-owner-sweep, historical-registry-termination): the expectation asserted authority, a sweep or a termination that does not exist at this commit; observations are preserved, claims are withdrawn, and no execution credit attaches. Retired (retired-stake-hook-with-withdrawal, retired-stake-hook-without-withdrawal): the registry interface has no stake_script hook, so nothing runs them.

Evidence provenance. The receipts cited in this section are the generic session's ship run, taken fresh at clean tip 1d98d51 after the receipt-overwrite repair (blueprint state:ce7615f6… request:8970c286…, cardano-node 10.7.0, every receipt dirty: false, every held row carrying its acceptance with transaction id and measurements). historical-permissionless-fold's first post-repair execution was at clean tip 748c4a9 (txid fe54d3a6…); its ship-run repetition at 1d98d51 is 4142f7d6…. The overwritten pre-repair receipts are retained only as evidence of that defect.

Held rows (operator question (Q-002), story 2). empty-fold and request-value-and-refund-routing are executed holds, never passes; surplus-fold-actions, held when this history was written, is now unmet by ruling (above). Only empty-fold was established as a direct conflict between the two model results. In the pinned pre-revision contract, Singular's Lean permits the empty fold (foldItems, | s, [] => .ok — Model.lean 188–189) and pairs each request with exactly one action in its FoldItem, so a surplus tail is unmodelled; State carries no owner field and Action.fold routes no refunds. Singular.Statements.fold_iff (Model.lean 250–256) makes its five conjuncts sufficient — native spend, fold items, net-mint match, representative-mint and application-mint witnesses — and no owner is among them: folding is permissionless. The consumer's theorems require the empty batch refused (R8_empty_fold_refused) and the 1:1 accounting. Its fold value routing is not R11_contribute_value — that states the deposit amount, and R11_retract_value states bond+tip on retract; fold routing lives in the consumer's Registry.processBody, Registry.stepFn and the Cage.delegated_is_registry equality, and it is operation-specific: register/revive lock the deposit into a checkpoint, goDormant/goConvicted/convict refund to the recorded owner, rejection returns the operation bond — guarded by rejectable and userPostable, not on demand — and the fold tip goes to the folder. A blanket exact-refund repair would preserve the wrong value mode and prevent checkpoint funding. Upstream #100/#101 are referenced proposals, not a delivered partition fix: #101 is open, and Singular owns the required local repair. R5_plugin_pinned needs a distinction this page previously collapsed. It is about the plugin, not a registry owner: the bound Registry.Sys carries plugin, and the consumer's fold checks pl = s.plugin. So registry-owner privileges and fields are superseded — the operator ruled Singular has no registry-owner role, and there is no owner to pin — but the application/plugin identity correspondence is not waived by that. The required application and native-witness bindings and the policy bindings remain required, and must be established through the actual ownerless representation; this is not a reason to reintroduce stake_script or to assert its old mechanism is the required one. The remaining requirements stay unmet; the receipts carry verdict held-q002 and the session ends non-zero while anything is held — a hold can never read as a pass, and a verdict moves only by execution. historical-owner-change is resolved-by-ruling, not a fourth unresolved hold: its receipt carries resolved-by-ruling, its acceptance stays defect evidence, and it is outside the held-set.

A repair this slice had to make to keep that sentence true: a refused control used to submit through the same path as a refusal ROW and wrote its refusal under the row's id, so empty-fold/surplus-fold-actions/request-value-and-refund-routing's held receipts were being replaced by their controls' refusals in every earlier receipts directory — receipts asserting the opposite of the executed finding (historical-owner-change's survived only because its control was retired, which is what isolated the cause). The control now writes nothing: its outcome is run-log evidence under its own identity, it cannot silently pass (an accepted control fails the run as a FINDING; a refusal that does not attribute fails the run naming the mismatch), and the receipt policy is unit-tested to discriminate — a control never overwrites a held receipt, a refusal row always writes, a non-attributing refusal writes nothing. The overwritten historical receipts in /tmp/t70-receipts* are kept untouched as evidence of the defect, never relabelled as fresh proof; the receipts cited by this page are the ones taken fresh at the clean tip after the repair.

historical-permissionless-fold and F-002, both halves by execution. An independent audit found that the runner itself always supplied the state owner's signature, so no fold row in the suite could have observed the owner gate; historical-permissionless-fold was built as the exact regression property. Executed against the pre-#79 candidate it was REFUSED, attributed to the state script, and recorded diverges-from-lean (receipt retained as history). After epic 17's #79 repair it was executed again: the fold with no owner signer is accepted (agrees-with-model, txid 4142f7d6… at the ship run, fe54d3a6… at the first post-repair execution, blueprint state:ce7615f6… request:8970c286… both times), and the same fold with the owner signer is accepted in-run. Both acceptances are the post-repair evidence, not a weakened test: the two transactions differ in the signer set alone, so the acceptance is attributable to the removed gate. The expectation was always accept; what moved is the candidate, and the verdict moved by execution only — never by editing a row or flipping a stored receipt.

The owner-dependency extent. Before #79, every fold-based row flowed through validateOwnership: update-existing-key–insert-occupied-key, reject-before-deadline-consumer-requirement–retired-stake-hook-without-withdrawal, historical-non-owner-sweep and request-value-and-refund-routing depended on the state owner's signature, while retract-outside-window's retract never did (it depends only on the request owner). #79 removed the gate from Modify folding, and the 2026-09-12 operator ruling settles more: the registry has no owner role whatsoever — not latent, not transferable, not gating End or migration. state.ak's End and validateMigration still call validateOwnership; those calls are outstanding conformance defects owned by epic 17, and historical-owner-change's accepted owner change is their executed evidence.

Superseded rows, observations preserved. historical-owner-change is resolved-by-ruling: the owner-pinning question has no premise left to be pending on, the acceptance is defect evidence, and the retired control (previous-owner End refused, new-owner accepted) tested behaviour that must not exist — it was never landed. historical-non-owner-sweep's refusal and its owner-signed control stay in the record with the conformance claim withdrawn: a gate that tests authority that does not exist discriminates nothing Singular owes. historical-owner-signed-sweep and historical-registry-termination are superseded the same way: the registry has no sweep and no termination, the end-to-end examples they cited were removed on 2026-09-12 (f3a68b1b), nothing runs either row, and list prints both uncovered. retired-stake-hook-with-withdrawal/retired-stake-hook-without-withdrawal are retired: the registry interface has no stake_script hook, so nothing runs them. The imported partition's State.stake_script hook supplies registry-owner authority by another name, which is exactly why epic 17 removes the field; the pinned staking credential cannot register anyway (the cause above), so the hook never reached a ledger. Preserved history stays unchanged, and changing applicability earns no execution credit.

execution-units-and-transaction-size for this session is written from the accepting folds of update-existing-key, delete-existing-key and reinsert-deleted-key, each read off the step that folded it in its row's receipt; the other rows' worst cases live in their own receipts.

The canonical identity rows

A designation split publishes the canonical seed; canonical-seed-identity boots the canonical registry from it; rival-seed-authentication initializes a rival from a second seed through the same bootstrap path; a second split publishes the rival seed, so no boot can ever consume the wrong UTxO as its funder.

row outcome evidence
canonical-seed-identity derived name recomputed and matched accept the state UTxO carries exactly the SHA-256 of the published seed's outRef, quantity 1; a fabricated outRef's derivation does not match (control)
rival-seed-authentication rival registry from a second seed rival accepted on chain; authentication rejects the node accepts the rival's bootstrap tx; both token names read back from the chain differ (each SHA-256 of its own seed's outRef); the canonical registry is unaffected — same UTxO, value and datum bytes as the canonical-seed-identity snapshot; the derived name accepts the canonical and rejects the rival
policy-address-only-authentication-control policy+address-only authenticator control must fail the run the weak authenticator accepts the rival read back from the chain while the full authenticator rejects the same value — the name is the only discriminator; armed (naive-authenticator), the run fails naming the accepted rival
applied-validator-identity applied-address derivation accept the manifest pins unapplied d42860fa97… with 1 declared parameter; applied in Haskell to ce7615f6ba4…; the derived address equals the address the chain reports and the library's; the unapplied address does not pass (control; armed run fails)
tokenless-output-authentication forged output at the canonical address authentication rejects; no script ran a forged output carrying the canonical address and datum but no token is accepted by the ledger with no script executed — no witness, no redeemer, no mint, empty node evaluation — and the same detector fires on the boot tx (control); authentication rejects it on the missing token

rival-seed-authentication asserts an acceptance: the ledger takes the rival, and the consumer's authentication is what excludes it. The receipt records the ledger's verdict (accepted, with the rival's txid and measurements); the rejection is asserted on chain-read assets and named in the run — never relabelled as a refusal, never skipped because the outcome reads oddly. The acceptance is the finding, and it is the accepted design. policy-address-only-authentication-control is what makes rival-seed-authentication worth anything: a check that cannot fail proves nothing, so the weak authenticator's acceptance of the rival is itself executed and required.

The serialization boundary rows (devnet session)

One fresh cage per row (two where a row needs two phases), each cage booted, exercised and — except where the row says otherwise — closed on the same isolated node, in canonical order:

row outcome evidence
submitted-datum-byte-round-trip datum bytes submitted and read back accept boot StateDatum plus one request RequestDatum, byte-compared submitted Datum versus chain-observed Datum for both; units from the boot minting, size the larger of the two transactions; corrupted comparison fails the run (control)
update-redeemer-constructor-witnesses every UpdateRedeemer constructor executed accept one executing witness per constructor — End 0, Contribute 1, Modify 2, Retract 3, Sweep 4 — each read back from the redeemer of a submitted transaction the validator executed (the Modify fold carries Modify and Contribute together); four witness transactions named; a skipped witness fails the run (control)
wrong-redeemer-constructor-index redeemer at a wrong constructor index refuse — unmet-by-ruling valid fold retargeted to Constr 5 keeping its fields (same CBOR size, so fee and collateral stay sufficient and any refusal attributes to the script); node refuses in phase 2 with CekError, attributed to both cage scripts (state+request, ledger order, unstable — the tamper breaks fold consistency the request script also checks); fresh cage accepts a valid fold (control); impossible marker fails the run (control). The chain refuses; Lean has no counterpart to compare with. By operator ruling 2026-10-02 ("Narrow #287") the row is recorded unmet-by-ruling, never a pass; follow-up lambdasistemi/singular#347
request-and-mint-constructor-witnesses RequestAction and MintRedeemer coverage accept, with one recorded gap Update, Rejected (phase-3 reject), Minting (boot), Burning (end) each executed and read back from its redeemer; Migrating is unreachable on the imported partition (previousPolicies=[], so has(previousPolicies, oldPolicy) fails at state.ak validateMigration FR1) and is recorded as a gap with that reason in gap-request-and-mint-constructor-witnesses-Migrating.txt — never a pass, never omitted; a skipped witness fails the run (control)
state-fields-chain-round-trip OnChainTokenState six fields round trip accept, schema pending two boots, stake_script None and Some (staking hash), all six fields byte-identical submitted versus chain-observed; corrupted comparison fails the run (control). The six fields include the owner field epic 17 is removing: the schema repair is pending, this expectation is superseded at that repair, and the row will be re-executed against the repaired blueprint

The serialization checks that need no node

No transaction, no node, no execution units — hence mem/cpu zero and no transactions named. What is measured is stated per row, and these rows never appear in a devnet-executed count.

row outcome evidence
blueprint-encoding-round-trip Haskell encodings against the blueprint schema accept (blueprint-check), schema pending all thirteen ToData types round-trip and each constructor index and field order matches the compiled blueprint's declared schema read at run time (REGISTRY_BLUEPRINT), including field-title order; no type needed a gap; Constr 99 validates against nothing and index 99 demanded for End fails the run (control); txSize is the blueprint file size in bytes, 92048. The checked schema is the six-field owner-bearing state: superseded at epic 17's pending repair, re-executed against the repaired blueprint
script-parameter-application parameter application derived in Haskell accept (param-check) parameter counts and encodings published from the blueprint — state 1 (previousPolicies), request 2 (statePolicyId, cageTokenName, in source order), staking 0 — unapplied hashes match the pinned blueprint hashes, the applied state hash is 874e476d…, non-empty allowlists and swapped request params discriminate; 2 demanded for state fails the run (control); txSize is the largest applied script size in bytes, 7805

proof-step-constructor-witnesses: unmarked and escalated

proof-step-constructor-witnesses carries no receipt and prints uncovered: Branch and Leaf are witnessed on chain across many accepted folds, but no accepted fold has ever carried a Fork, and two independently ground lone-Fork absence proofs — each verified by the mts Haskell stack against its own root — are refused by the compiled validator with a bare CekError at build-time evaluation. The full bundle (keys, trie shape, both verdicts, the refusal bytes, what could not be determined, and the bounded consumer consequence) is filed as a user story escalation; the offline oracle (find-fork-keys, check-fork-exclusion) and shape probes (show-nibbles, show-d-proof, show-all-proofs) reproduce every offline claim. This is a finding held open, not a gap and not a pass.

Measurements

Ship run: base b3f4b5a, clean tree, cardano-node 10.7.0. The issue-#70 generic session's ship run: base 1d98d51, clean tree, cardano-node 10.7.0 — every generic receipt below names that base with dirty: false, and the held rows carry their acceptances with transaction ids and measurements because their controls write none.

Serialization ship run: base b2201c3, clean tree, cardano-node 10.7.0 — every receipt below names that base with dirty: false. Each invocation observes the tree before creating its output, and ships from a fresh output directory, so no run ever measures the previous run's receipts. The defect this guards against reported dirty: true on clean trees; its direction is conservative — it can block a valid ship, it can never let a dirty tree pass as clean — and no merged receipt ever claimed clean while dirty.

Maxima queried from the running node, never hardcoded: maxTxExUnits 140000000 mem / 10000000000 cpu, maxTxSize 16384 bytes.

fold mem (headroom) cpu (headroom) size (headroom)
update-existing-key Update 649162 (139350838) 217096102 (9782903898) 11451 (4933)
delete-existing-key Delete 638564 (139361436) 213717823 (9786282177) 11451 (4933)
reinsert-deleted-key re-Insert 616554 (139383446) 200156393 (9799843607) 11451 (4933)
empty-fold empty fold (held) 273449 (139726551) 87149465 (9912850535) 8388 (7996)
surplus-fold-actions surplus action (held) 905068 (139094932) 295812293 (9704187707) 11555 (4829)
historical-owner-change owner change (defect) 628372 (139371628) 207905393 (9792094607) 11403 (4981)
request-value-and-refund-routing crossed refunds (held) 1122369 (138877631) 376904236 (9623095764) 11613 (4771)
historical-permissionless-fold permissionless fold 717070 (139282930) 231585673 (9768414327) 11442 (4942)
canonical-seed-identity canonical boot 143440 (139856560) 46848478 (9953151522) 8502 (7882)
rival-seed-authentication rival boot 143440 (139856560) 46848478 (9953151522) 8502 (7882)
tokenless-output-authentication forged payment 0 (140000000) 0 (10000000000) 333 (16051)
submitted-datum-byte-round-trip datum round trip 143440 (139856560) 46848478 (9953151522) 8466 (7918)
update-redeemer-constructor-witnesses five witnesses 629296 (139370704) 204228993 (9795771007) 11423 (4961)
request-and-mint-constructor-witnesses four witnesses 649502 (139350498) 217245659 (9782754341) 11423 (4961)
state-fields-chain-round-trip state None+Some 147634 (139852366) 49473955 (9950526045) 8497 (7887)

Execution units stay under 3.8% of the maxima for every row (worst: request-value-and-refund-routing's crossed-refunds fold at 3.77% cpu, still ~26× headroom; tokenless-output-authentication reports zeros honestly: no script purpose exists to evaluate). Serialized size is the tight dimension at ~71% of maxTxSize for folds and ~52% for boots; larger batches (fold-batch-size-boundary) may press against it first. The canonical session's worst case is recorded in its execution-units-and-transaction-size receipt; the registry session's execution-units-and-transaction-size reads the worst accepting folds of update-existing-key, delete-existing-key and reinsert-deleted-key off their receipts' steps, and its other rows' worst cases live in their own receipts; the serialization rows carry theirs in the row receipts, and no serialization execution-units-and-transaction-size is claimed yet. Refusal reasons keep every failing script hash in ledger order — a tampered fold can fail two scripts, and trimming volume never trims identities — under the same run-enforced 16KB bound.

Historical limits

  • Programs with end-to-end references: insert-key, retract-inside-window and reject-after-window run as programs in the registry session, their state computed from those receipts; the epic-16 CageSpec examples they cite remain supporting references. historical-owner-signed-sweep and historical-registry-termination cite none: the examples they cited were removed on 2026-09-12 (f3a68b1b), nothing runs either row, and both are superseded by the ownerless-registry ruling (no sweep, no termination) — history earns no conformance credit.
  • Executed, unmet by ruling, not a pass: reject-before-deadline-consumer-requirement records the early rejection the consumer forbids and the chain accepts, kept unmet by operator ruling 2026-10-01 (unmet-by-ruling). fold-against-superseded-root, surplus-fold-actions and wrong-redeemer-constructor-index: the chain refuses, Lean has no counterpart to compare with, kept unmet by operator ruling 2026-10-02 ("Narrow #287") (lambdasistemi/singular#346, lambdasistemi/singular#345, lambdasistemi/singular#347).
  • Executed holds, not passes: empty-fold and request-value-and-refund-routing remain held by execution. empty-fold records the pre-revision empty-fold conflict with R8_empty_fold_refused; request-value-and-refund-routing violates the consumer's required operation-specific fold value routing (Registry.processBody, Registry.stepFn, Cage.delegated_is_registry). R11_contribute_value states the deposit amount and is not that fold requirement. Upstream #100/#101 are referenced proposals, not a delivered partition fix: #101 is open, and Singular owns the required local repair. R5_plugin_pinned concerns the plugin, not a registry owner. The consumer requirements stay unmet; a held row is never a pass, and the verdicts move only by execution. historical-owner-change's owner change is retained as defect evidence of the outstanding owner gate, resolved-by-ruling, with its defect owned by epic 17.
  • Superseded, never pending: historical-owner-signed-sweep, historical-non-owner-sweep and historical-registry-termination asserted registry-owner authority, a sweep or a termination that does not exist — observations preserved, claims withdrawn, no execution credit for changed applicability. retired-stake-hook-with-withdrawal/retired-stake-hook-without-withdrawal are retired: the registry interface has no stake_script hook, so nothing runs them.
  • Uncovered: proof-step-constructor-witnesses, the five consumer-resolution requirements and fold-batch-size-boundary–observed-toolchain-and-environment print uncovered (as do retired-stake-hook-with-withdrawal/retired-stake-hook-without-withdrawal, retired, and historical-owner-signed-sweep/historical-registry-termination, superseded — no receipts). proof-step-constructor-witnesses is not merely uncovered: its Fork finding is filed for a user story and the row stays unmarked until that story resolves — an unmarked row with a finding, never a gap and never a pass. request-and-mint-constructor-witnesses's Migrating gap is of the recorded kind, with its validator-read reason beside the receipts.
  • Schema pending (epic 17): blueprint-encoding-round-trip, submitted-datum-byte-round-trip and state-fields-chain-round-trip describe the six-field owner-bearing OnChainTokenState. The ownerless schema repair is pending; their expectations are superseded at that repair, their receipts stay valid as history, and the rows will be re-executed with fresh receipts against the repaired blueprint — coverage is not inherited across a wire-format change. Epic 17's antecedent ownerless repair exists locally (f3a68b1) but is not accepted and not merged; nothing here cherry-picks it, and the affected runtime checks follow the definitive schema artifacts when the explicit handoff arrives — integration stays serialized behind it.
  • Green expected-debt CI grants no conformance credit: the workflow's generic-rows step asserts declared debt — exact receipt set, exact verdict per receipt, held set exactly empty-fold request-value-and-refund-routing, unmet set exactly reject-before-deadline-consumer-requirement fold-against-superseded-root surplus-fold-actions, nothing failing — and nothing more. It pays neither the held rows nor a full execution-units-and-transaction-size, and strict completion and release stay RED on that debt.
  • Out of scope: checkpoint-and-treasury-policy (cardano-keri), the naming rows (epic 16 demonstration, not consumer evidence), LR/LT rows (epic 17).
  • The registry-identity rows authenticate the canonical registry as the consumer derives it from the published seed. They do not evidence the serialization boundary (serialization), the constructor coverage (update-redeemer-constructor-witnesses–request-and-mint-constructor-witnesses) or the batch limits (fold-batch-size-boundary).
  • The measurements above are devnet folds of single requests. They say nothing about batch limits (fold-batch-size-boundary) or any other environment (observed-toolchain-and-environment records the observed one below).

Current replay commands

Run from the repository or extracted source root with Nix available. Use a fresh directory outside the tracked tree for each family. The generic session runs ten rows and may also emit its execution-units-and-transaction-size measurement receipt; the serialization session runs five devnet rows and two local checks. Superseded rows remain listed and are not runtime commands.

nix run ./conformance#conformance -- list
blueprint="$(nix build --quiet --no-link --print-out-paths ./onchain#plutus-blueprint)"
receipts="$(mktemp -d /tmp/singular-conformance.XXXXXX)"

# The finite retraction window has its own three-step model-compared receipt.
REGISTRY_BLUEPRINT="$blueprint" nix run ./conformance#conformance -- run retract-outside-window --receipts-dir "$receipts/retraction-window"

# Expected exit 1: exact held set empty-fold, request-value-and-refund-routing; unmet set reject-before-deadline-consumer-requirement, fold-against-superseded-root, surplus-fold-actions; no failed rows.
REGISTRY_BLUEPRINT="$blueprint" nix run ./conformance#conformance -- run update-existing-key delete-existing-key reinsert-deleted-key insert-occupied-key reject-before-deadline-consumer-requirement fold-against-superseded-root empty-fold surplus-fold-actions request-value-and-refund-routing register-active-key --receipts-dir "$receipts/generic"

# Expected exit 0 after all five identity rows and their controls.
REGISTRY_BLUEPRINT="$blueprint" nix run ./conformance#conformance -- run canonical-seed-identity rival-seed-authentication policy-address-only-authentication-control applied-validator-identity tokenless-output-authentication --receipts-dir "$receipts/identity"

# Expected exit 1: update-redeemer-constructor-witnesses and request-and-mint-constructor-witnesses are partial; wrong-redeemer-constructor-index is unmet by ruling (#347); the other four agree.
REGISTRY_BLUEPRINT="$blueprint" nix run ./conformance#conformance -- run blueprint-encoding-round-trip submitted-datum-byte-round-trip update-redeemer-constructor-witnesses wrong-redeemer-constructor-index request-and-mint-constructor-witnesses script-parameter-application state-fields-chain-round-trip --receipts-dir "$receipts/serialization"

nix run ./conformance#conformance -- list --receipts "$receipts/generic"
nix run ./conformance#conformance -- list --receipts "$receipts/identity"
nix run ./conformance#conformance -- list --receipts "$receipts/serialization"

These are expected result contracts, not claims that an arbitrary nonzero exit is acceptable. Check the terminal row counts, exact receipt IDs, source/blueprint binding, per-row verdicts and named residuals. A crash, missing receipt or failed control fails the replay. The CI workflow checks those details against the actual output; partial and held rows remain debt even when that regression check is green.

Armed controls use a separate fresh receipt directory. Their nonzero exit must name the intended failed assertion: the wrong refusal reason, forged value or datum, weak authenticator, corrupted state derivation (legacy flag unapplied-address), wrong constructor index, wrong parameter count or deliberately missing witness. The normal partial exit alone does not prove an armed control fired. See conformance.yml for the executable session and receipt checks.

The Fork oracle and its probes remain available without a node or blueprint; their output is local proof evidence, not proof-step-constructor-witnesses completion:

nix run ./conformance#conformance -- find-fork-keys
nix run ./conformance#conformance -- check-fork-exclusion
nix run ./conformance#conformance -- show-all-proofs

The runner gives each node a unique temporary directory. Receipts are written under the selected output directory and are never inherited as current merely because an older run succeeded.

Historical observed environment

cardano-node 10.7.0, GHC 9.12.3, Aiken compiler v1.1.21 (blueprint hal/mpf 0.0.0), unapplied state script pinned d42860fa972c8a325daae773adc372795c48cf365d0a72b137c749d3 with one declared parameter, applied state script ce7615f6ba4de80dfa9b9c6aef680666472ba4ed7e640ff55aad7c6e, unapplied request script hash 8970c286… (re-pinned from 64d1afbfe585…/874e476d7408…/6b5ce7… by the issue-#79 imported-validator repair; onchain/REPAIR.patch records the change, PROVENANCE.md the authority). The issue-#70 rows were executed against blueprint state:ce7615f6ba4de80dfa9b9c6aef680666472ba4ed7e640ff55aad7c6e request:8970c286…; the pre-#79 executions (historical-permissionless-fold's refusal, the former bespoke retraction and the stale-request/proof refusals) are history at the earlier hashes. Environments other than this devnet shape are explicitly not covered.