Who owns a registry fold

A contributor changing how the registry folds pending requests — which UTxO it spends, what an edge owes, how the transaction is assembled — wants to change one module and have the review ask one question. Before the extraction, all of that lived in one 890-line module; a change to a custody refund and a change to a slot computation were reviewed in the same file with no boundary between them. Now the fold path is three focused owners behind the same public facade, and Singular.Registry.FoldSelection owns which pending requests a fold takes, and this page tells a contributor which owner their change belongs to, what the flow through them is, and what must never change while it moves.

The story this serves

As a registry caller, I submit requests and fold them through the existing Singular.Registry.TxBuilder.Update import; I want the same request order, the same token effects, the same custody refunds and the same destinations as before, so my callers and my conformance rows see nothing move. A contributor refactoring the fold's internals owes me that. The focused suite pins what its rows actually assert: the order duties accumulate in for the request list a caller supplies; the active witness burn's net -1, with the one input that holds it consumed and no output carrying it; which holder input a burn selects and which custody input a custody spend selects — exact policy, key and quantity; the per-owner grouping of deposit returns and a deletion's approval riding inside its deposit output; and the fold's empty signer set, both in the duties it accumulates and in the required-signer field of the transaction the public entry actually builds.

What no focused row asserts: the query's input-order sorting (the rows hand registryDuties already-ordered lists, and the built-body row folds one request); the quantities of every mint except the active burn; a custody refund's output address and amount; a delivering edge's destination; and an approval returned by a delivering or inadmissible edge. The fresh-blueprint Criterion3Spec tests the accepted-edge portion of that gap. It books two real requests and presents their real outputs in descending TxIn order to one public fold, then runs connected stages for all seven admissible edges. Its comparisons derive expected mints, approval assets, holder inputs, destinations, custody refunds and signers from observed pre-state and the accepted Lean row. They inspect the unsigned body and the landed destination, refund and state outputs through the real provider. Each of the seven named effect comparisons has a one-field mutant required to fail in the same run. The local submitted devnet run passed 23 examples with no failures. Its receipts record the two booked request TxIns, the real and reversed provider orders, canonical TxIn order, fold transaction IDs and roots across all connected stages. The terminal witness leaves the root unchanged, as Lean requires; the other six edges change it. On ae4fe9a, the exact-head final gate, independent receipt audit and pushed-head CI completed. Those receipts bind that commit. This finite representative devnet case does not cover arbitrary request sets, inadmissible-edge refusals, the delivering approval's ADA minimum, or proof correctness beyond live cage acceptance.

One owner per fold concern

flowchart TD
    Callers[Callers — tests, commands, journeys, conformance] -->|import the six public names| U[Singular.Registry.TxBuilder.Update — facade and orchestration]
    Callers -->|which pending requests a fold takes| S[Singular.Registry.FoldSelection — selection]
    U -->|queries, proofs, state, slot, context completion| C[Update.Context]
    U -->|what the requests owe| D[Update.Duties]
    U -->|evaluation adapter and the one story language program| B[Update.Build]
    D -->|reads the context a caller hands in| C
    B -->|reads the duties the fold decided| D
    C -->|edges| IE[TxBuilder.Internal.Edges]
    C -->|identity, datums| II[TxBuilder.Internal.Identity]
    C -->|lookup| IL[TxBuilder.Internal.Lookup]
    D -->|the edge-to-mint decisions it shares with the cage| IE
    D -->|addresses, policy pins, datums| II
    D -->|constructs attached mints, spends, redeemers| CF[TxBuilder.ConnectedFold]
    B -->|input-to-reference conversion| II
    B -->|reads the attached action types| CF
    U -->|builds the request script| II
    U -->|reads the custody-spend accessor| CF

The facade holds no algorithm. It runs the fold's sequence — query of the chosen requests, proofs, state preparation, context completion, duties, upper slot, program, build — and every decision in that sequence belongs to exactly one of the three owners below it. Which pending requests reach that sequence belongs to Singular.Registry.FoldSelection alone: it judges every pending request on its own, by seven checks in order, and returns each as settled or excluded with a reason, up to a capacity bound of nine requests per fold, an estimate under preprod's execution budget of 14 million memory units, bound by trie-proof verification, not measured on a real network (measuring it belongs to #530). The graph is acyclic and one-way between the fold owners: Context knows nothing of duties or assembly, Duties reads Context but no assembly, Build reads Duties but no facade. Below the fold owners, both Duties and Build also read the older Internal owners directly — the duty derivation shares its edge-to-mint table with the cage through Internal.Edges — and both the facade and the build owner read ConnectedFold for the attached action types.

Module Responsible for
Singular.Registry.FoldSelection Which pending requests a fold takes: judging every pending request alone, by seven checks in order, returning each as settled or excluded with a reason, up to nine requests per fold, an estimate not measured on a real network.
Singular.Registry.TxBuilder.Update The six public exports and the two fold entry points; the sequence of preparation, decision and assembly. Nothing else.
Singular.Registry.TxBuilder.Update.Context The RegistryContext a caller hands in and its empty value; finding the state, request and fee UTxOs; the ordered speculative proofs through the trie; the new state output and the cage script; the validity upper slot; completing a partly empty context from the provider.
Singular.Registry.TxBuilder.Update.Duties RegistryDuties and registryDuties: the one derivation of what a fold's requests owe — mints under the three token policies, destinations, custody spends, burn sources, deposit and approval returns, and the (empty) signer set.
Singular.Registry.TxBuilder.Update.Build NoCtx, the evaluation adapter over the provider, and the one transaction story language program: spends, mints, outputs, signatures, attached or referenced scripts, collateral and validity.

The flow a fold runs

sequenceDiagram
    participant Caller
    participant Sel as FoldSelection
    participant Facade as Update facade
    participant Ctx as Update.Context
    participant Dut as Update.Duties
    participant Bld as Update.Build
    Caller->>Sel: selectFold — judges each pending request alone, seven checks, settled or excluded with reason, up to nine, an estimate not measured on a real network
    Sel-->>Caller: chosen requests
    Caller->>Facade: updateTokenSelected cfg prov tm tid addr ctx chosen
    Facade->>Ctx: queryContext — state, chosen requests (sorted by input), fee, params
    Facade->>Ctx: computeProofs — speculative trie walk in request order
    Facade->>Ctx: prepareState — new state output, cage script
    Facade->>Ctx: completeContext — cage/holder inventories, cage script default
    Facade->>Dut: registryDuties — what the chosen requests owe
    Facade->>Ctx: computeUpperSlot — earliest request deadline
    Facade->>Bld: mkEvalTx + buildProgram — the one story language program
    Bld-->>Facade: assembled transaction
    Facade-->>Caller: the built ConwayTx

What must not change

The extraction is a representation change, not a new registry rule. The Lean model remains the authority: the fold's mint quantities, owner deposits, custody refunds and approval returns are what Singular.obligations says they are, and no fold requires a signer (Singular.Statements.fold_requires_no_signer). What the focused tree executes: the duties list in the order of the requests it was given — not sorted, reversed or swept from the inventory — with outputs, mints and burn sources each read back from what the builder produced; the active burn's net -1 with its held input and no carrier output; the exact holder and custody input selections; the per-owner grouping of deposit returns and a deletion's approval inside its deposit output; and the built body's empty required-signer set through the public entry. The new fresh-blueprint end-to-end case directly compares the previously unpinned accepted-edge effects on finite connected stages, with in-run mutants and real post-state observations. Its two-request ordering witness controls the provider's enumeration of real outputs; it does not claim every possible input order. Refused edges and the delivering approval's ADA minimum remain outside these comparisons. A contributor who needs any pinned behavior to change is not refactoring; that is a behavior change and belongs to the design flow, not to this structure.

Independent evidence boundaries

Two neighbors deliberately do NOT share this code. The cage computes its own duties from the requests it consumes (dutiesOf, dutyOk); a builder that agreed with the validator by construction would not catch a disagreement, so registryDuties stays a separate derivation. And the Conformance suite's state is computed from run receipts — its rows say what the chain did, not what this builder intended, and this extraction touches none of it.

Where common changes land

  • A request gains or changes what it carries: Update.Context finds and decodes it (the datum shape itself lives in Singular.Registry.Types).
  • Which pending requests a fold takes — the seven checks, the reasons, the capacity bound of nine, an estimate not measured on a real network: Singular.Registry.FoldSelection, and only there.
  • An edge's obligations change — what it mints, where it delivers, what custody it spends or returns: Update.Duties, and only there.
  • The transaction's shape changes — a new input kind, reference scripts, collateral or validity rules: Update.Build.
  • What an omitted context field defaults to, or which queries the fold runs: Update.Context.
  • The public surface and the fold's sequence: the facade, which must stay a coordinator.

Decisions this extraction recorded

Decision Chosen Considered and not taken
How to split the fold Three owners matching preparation, decision and assembly, behind the unchanged facade. Finer owners per edge or per policy: more indirection, same guarantee.
How callers keep working The facade re-exports its six names byte-for-byte; children are implementation modules of the same library. Rewriting callers to the children: churn across tests, commands and conformance with no behavioral benefit.
Where the context completion lives Update.Context, as completeContext, moved verbatim from the facade's inline block — queries are preparation, and the facade only sequences them. Leaving the queries in the facade: the owner graph would say Context owns queries while the facade ran them.
The pre-existing ConnectedFold copies Left untouched: it is the permissionless journey fold with its own documented lineage — its own header says its proof, upper-slot and refund rules are transcribed from the repair runner, pre-dating this extraction — so this slice neither creates nor unifies parallel logic there. Unifying them: a different ticket's design question — the two builders serve different flows with different rules, and the parallel logic is recorded as a standing limit on "no duplicate algorithm" within this extraction's fence.

Source

The facade and owners, linked on this site to the generated API reference and readable in the repository at the revision you are viewing:

The wider builder map these owners sit in is Builder and blueprint modules; the check commands and their extents are in Checking off-chain code.