Split out of morph#362, which recorded the problem but could not fix this half.
What is wrong
UndoTransaction{ledgerId, journalId} reverses a journal entry, and no action
in the ledger rung's wire surface returns a journalId. StoreTransaction
returns GetLedgerResult — the accounts list — and so does GetLedger. There
is no GetJournal or ListTransactions. So a client can only ever call
UndoTransaction with an id it guessed.
Verified by enumerating the wire surface rather than by inspection: JournalId
appears in exactly one DTO, examples/ledger/dto/transaction_dto.hpp:112, which
is UndoTransaction's own input. No result type carries one.
This matters because undo is in the rung's own "What to implement" (step 6) and
its journal-derived-undo story is one of the subsystems the rung exists to
exercise. The in-process tests get the id from the database; the client-side
half is unreachable.
Why it is a separate ticket
morph#362 closed by documenting both unreachable actions in
examples/ledger/README.md, which is what a client author needed and what that
issue's acceptance asked for. Actually making UndoTransaction drivable is a
different change, and it cannot be done inside examples/ alone — see below.
The constraint that makes this cross-lane
Whichever direction is chosen, two files under scripts/ must change, and they
are outside the examples/ lane:
scripts/scenario/scenarios/ledger/undo-needs-a-journal-id-nothing-hands-out.scenario
asserts expect ok field @body !~ "journalId" — it pins the current
absence and would fail the moment a result carries one.
scripts/scenario/coverage_allowlist.json exempts UndoTransaction from
workflow coverage on the stated grounds that it cannot be driven.
Both are the record of the current contract, so both are load-bearing rather
than incidental.
The two directions
- Make it drivable — a
GetJournal/ListTransactions action, or a receipt
carrying the journalId on StoreTransaction's result. This is the option
the rung's own step 6 implies, and it is the larger change: it adds wire
surface, which needs a DTO, a schema, scenario coverage and a spec sentence.
- Record undo as administrative — state in the rung's documentation that
UndoTransaction is an operator action driven out of band, like
RunReportJob is a service-principal action. Cheaper, and consistent with
how the sibling unreachable action was resolved, but it closes off a
capability the rung says it demonstrates.
morph#362's README section says the question is open and cites that issue; this
one now carries it.
Verification status: reproduced
The JournalId enumeration above, run over the rung's DTO headers. The
!~ "journalId" assertion read from the scenario file on master. Both
recorded by the N ladder lane while closing morph#362.
Not verified: whether any consumer outside the rung depends on
StoreTransaction's result shape, which direction 1 would change. The rung's
own GUI and tests are the only known consumers, but that was not exhaustively
checked.
What would close this
A decision, recorded in examples/ledger/README.md, plus whichever follows:
- If drivable: an out-of-process client can complete store → read id → undo,
demonstrated by a scenario that asserts the reversal, and the
undo-needs-a-journal-id-nothing-hands-out.scenario and
coverage_allowlist.json entries are retired rather than left asserting a
contract that no longer holds.
- If administrative: the README says so in the same terms it uses for
RunReportJob, and the allowlist entry's stated reason is updated to name the
decision rather than the absence.
Not closable by adding a GetJournal action while leaving the scenario
asserting journalId is absent — the two would contradict, and the scenario
would fail.
Split out of morph#362, which recorded the problem but could not fix this half.
What is wrong
UndoTransaction{ledgerId, journalId}reverses a journal entry, and no actionin the ledger rung's wire surface returns a
journalId.StoreTransactionreturns
GetLedgerResult— the accounts list — and so doesGetLedger. Thereis no
GetJournalorListTransactions. So a client can only ever callUndoTransactionwith an id it guessed.Verified by enumerating the wire surface rather than by inspection:
JournalIdappears in exactly one DTO,
examples/ledger/dto/transaction_dto.hpp:112, whichis
UndoTransaction's own input. No result type carries one.This matters because undo is in the rung's own "What to implement" (step 6) and
its journal-derived-undo story is one of the subsystems the rung exists to
exercise. The in-process tests get the id from the database; the client-side
half is unreachable.
Why it is a separate ticket
morph#362 closed by documenting both unreachable actions in
examples/ledger/README.md, which is what a client author needed and what thatissue's acceptance asked for. Actually making
UndoTransactiondrivable is adifferent change, and it cannot be done inside
examples/alone — see below.The constraint that makes this cross-lane
Whichever direction is chosen, two files under
scripts/must change, and theyare outside the
examples/lane:scripts/scenario/scenarios/ledger/undo-needs-a-journal-id-nothing-hands-out.scenarioasserts
expect ok field @body !~ "journalId"— it pins the currentabsence and would fail the moment a result carries one.
scripts/scenario/coverage_allowlist.jsonexemptsUndoTransactionfromworkflow coverage on the stated grounds that it cannot be driven.
Both are the record of the current contract, so both are load-bearing rather
than incidental.
The two directions
GetJournal/ListTransactionsaction, or a receiptcarrying the
journalIdonStoreTransaction's result. This is the optionthe rung's own step 6 implies, and it is the larger change: it adds wire
surface, which needs a DTO, a schema, scenario coverage and a spec sentence.
UndoTransactionis an operator action driven out of band, likeRunReportJobis a service-principal action. Cheaper, and consistent withhow the sibling unreachable action was resolved, but it closes off a
capability the rung says it demonstrates.
morph#362's README section says the question is open and cites that issue; this
one now carries it.
Verification status: reproduced
The
JournalIdenumeration above, run over the rung's DTO headers. The!~ "journalId"assertion read from the scenario file onmaster. Bothrecorded by the
N ladderlane while closing morph#362.Not verified: whether any consumer outside the rung depends on
StoreTransaction's result shape, which direction 1 would change. The rung'sown GUI and tests are the only known consumers, but that was not exhaustively
checked.
What would close this
A decision, recorded in
examples/ledger/README.md, plus whichever follows:demonstrated by a scenario that asserts the reversal, and the
undo-needs-a-journal-id-nothing-hands-out.scenarioandcoverage_allowlist.jsonentries are retired rather than left asserting acontract that no longer holds.
RunReportJob, and the allowlist entry's stated reason is updated to name thedecision rather than the absence.
Not closable by adding a
GetJournalaction while leaving the scenarioasserting
journalIdis absent — the two would contradict, and the scenariowould fail.