Lesson 8 · Exact-payload approval
From cognokratos/tauros-revenue · docs/course/08-exact-payload-approval.md · pinned revision facbbc927eb4
Part III: Human authority · Course map · Next: Lesson 9
Before this lesson: Lessons 5 (revisions) and 6 (state machines), and the
demo data (mix setup).
Goal
See how a human authorizes one exact, immutable payload, never "whatever the invoice currently contains", and what the UI shows them while they do it.
Concept
approve requires the revision_id and payload_hash the human saw. Inside
one transaction, with the invoice locked, Tauros checks the state, that the
revision is current, that the hash is that revision's, that the stored payload
still reproduces its hash, and that the destination is still active. Only then
is an Approval written, naming that revision and hash.
Code to inspect
lib/tauros/revenue/invoice/changes/decide.ex: the checks, in orderlib/tauros/revenue/approval.ex: append-only; one decision per revision; writable only through an Invoice decision by a human approverlib/tauros_web/live/invoice_live/review.ex: the decision form carries the hash that was on screen
Run it
mix phx.server, sign in asdemo@tauros.local.- Overview shows what needs your attention; open Needs review.
- Pick a proposal. Read the authority bar (proposed by an agent, decided by you).
- Compare "What approving authorizes" with the agent's reasoning.
- Approve it. You land on the invoice: state Approved, lifecycle complete, "Approved by you · revision 1 · fingerprint …" under Human decisions.
- Open another one and Request changes with a reason; see how the invoice now says whose move it is.
mix test test/tauros/revenue/approval_test.exs test/tauros_web/live/invoice_review_live_test.exs
Break it
Approve with a hash that is not the revision's, or a revision that is no longer current:
mix test test/tauros/revenue/approval_test.exs # "a hash that is not the revision's is refused"
and Exercise 4 (stale revision).
Why it fails
Note
Book edition note. At the pinned revision the review page shows "This proposal changed while you were reviewing it…" for
stale_revisionandalready_decidedconflicts. Apayload_mismatch(wrong hash) is still refused, but the page shows the generic "The decision could not be recorded." Seeexplain/1inreview.ex. Also note what is verified: the server checks that the submitted revision is current and the hash is that revision's hash. The revision and hash come back from the page's form and are not separately compared with what the page rendered.
Decide compares: 409 payload_mismatch for the wrong hash, 409
stale_revision for an old revision. The UI turns that into "This proposal
changed while you were reviewing it. Nothing was decided."
What to remember
- The approver states what they approve: revision and hash.
- An approval is a record, bound to one payload, written once.
- The UI makes the authority boundary visible: who proposed, who decides.