test(app-blocks): pin every fee reversal to its own procedure input id

The reversal deletes a row and issues a refund, and all three observing
procedures have a snapshot or projection in scope carrying a different
workflow id, so a site keyed on the wrong one refunds the wrong viewer.
Mutation-checked: keying cancelAppWorkflow on canceledWorkflow.workflowId
fails "EVERY procedure that cancels a workflow also reverses the fee" with
`expected ... to contain 'workflowId: input.workflowId'`.

terminalStatus is deliberately not pinned to one expression — the poll and
cancelWorkflow read snapshot.status, cancelAppWorkflow reads its projection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacxDev
2026-09-18 17:48:04 -05:00
parent 6bbae946ff
commit 29ee5a8a42
@@ -485,6 +485,18 @@ describe('author fee — the viewer-charge seam', () => {
new Set(['pollWorkflow', 'cancelWorkflow', 'cancelAppWorkflow'])
);
expect(reversalOwners).toHaveLength(3);
// 🔴 EVERY reversal is keyed on the procedure's OWN input id. The reversal
// deletes a row and issues a refund, so a site keyed on some other id in
// scope would refund the wrong viewer — and all three procedures have a
// `snapshot`/`workflow` object in scope carrying a different workflow id.
// (`terminalStatus` is deliberately NOT pinned to one expression: the poll
// and `cancelWorkflow` read `snapshot.status`, `cancelAppWorkflow` reads its
// projection's.)
for (const site of callSites(source, 'reverseBlockAuthorFee({')) {
expect(site).toContain('workflowId: input.workflowId');
expect(site).toMatch(/terminalStatus: \w+\.status,/);
}
});
it('🔴 every reversal is guarded on TERMINAL-ness AND on not-succeeded', () => {