Vote for your favorite SkillMD. The submission with the most likes wins the $1,000 Audience Choice Award for the NandaHack x HCLTech hackathon. Voting is open through September 25.Vote now →
in reviewhumanPR #93Payments

hackathon/superheroin888: delivery-gated streaming payments with mid-stream cancellation (problem 03)

Problem 03 — streaming pay-per-second payments with mid-stream cancellation (payments layer). One problem, one layer.

Author

superheroin888 avatar

@superheroin888

github profile →
Status
In review
Opened on
Jul 8
Branch
hackathon/superheroin888-streaming-payments

Description

The pitch.

## What this is

**Problem 03 — streaming pay-per-second payments with mid-stream cancellation** (`payments` layer). One problem, one layer.

The tree already contained a `streaming` plugin, three validators, and two scenario YAMLs — but they were scaffolding, not a solution:

- `nest run scenarios/streaming_payments.yaml` **crashed**: no `streaming_payments` scenario factory existed.
- The plugin hardcoded `opened_at_tick=0` ("will be set by context"), stamped `closed_at_tick=0` on close, had zero partition awareness, and named the wrong payer on a payee-side close.
- All three validators checked event shapes (`payment_debited`, `event_type: stream_opened`) that the trace layer **never emits** — they passed vacuously on any trace, including an empty one.

## What ships

**Delivery-gated billing** as the core design: the payer records each delivered work unit (`record_delivery`) and only then drains one tick (`tick_stream`). A unit the payee never delivered — dropped message, network partition, or a closed stream — is never billed. This is the per-request metering shape of x402-style HTTP payments on the simulator's logical clock.

- **Plugin** (`("payments", "streaming")`, drop-in for the stock `Payments` protocol): `open_stream`/`close_stream` per the brief, close idempotent and final at any tick (unused remainder never spent), `pay()` literally implemented as a one-tick stream so one-shot and streaming share every invariant, refund only on settled refs. `verify_payment` returns `STREAMING` for a half-drained stream — drained ticks are final and irrevocable, so the settlement is in progress, not `PENDING` (documented in the docstring, as the brief asks). Deterministic: ticks come from the caller; no wall clock, no RNG.
- **Scenario factory** (was missing): 5 buyer/seller pairs, rolling streams, per-unit ack → per-unit debit, mid-stream cancellation on every third stream, cancel deadlines so the run makes progress under drop. Both existing YAMLs now run and pass.
- **Validators rewritten against the real trace format and made non-vacuous** (they FAIL on a trace with no streaming lifecycle — a plugin that quietly pre-pays cannot pass):
  1. *conservation* — settled total == sum of per-unit debits, never past `max_total`;
  2. *no-drain-after-close* — trace-order check, plus no unit billed twice;
  3. *no-overbill-on-partition* — every debit must cite a previously **delivered** ack (`receive` event), which subsumes both random drop and full partition.

## Adversarial discrimination

- Under `payments: streaming`: all three validators PASS (5% drop run: 97 streams, 297 debits, every settled receipt equals its debit sum).
- Under `payments: prepaid_credits` on the identical scenario: all three FAIL ("no streaming lifecycle observed").
- Under a full buyer/seller partition: 96 streams open, **zero** credits move, validators pass — not billing for undelivered work is the correct behavior.
- 8 synthetic attack traces (drain-after-close, double-bill, over-bill without ack, time-travel billing, receipt mismatch, past-cap billing, missing lifecycle) each caught by the right validator.

## Tests

26 plugin unit tests · 6 hypothesis properties (conservation under random op sequences — adapted from the escrow suite as the brief suggests — cap enforcement, close finality) · 6 end-to-end scenario tests (discrimination, byte-identical replay at seed 42, multi-seed, partition ⇒ zero flow) · 6 validator smoke tests.

## Reproduce

From a clone of this PR branch:

```bash
gh pr checkout 93   # or: git fetch origin pull/93/head && git checkout FETCH_HEAD
make ci-local       # the repo's Definition of Done: uv sync, ruff check,
                    # ruff format --check, pyright, pytest -v
```

Expected: `ci-local: all 5 checks passed. Safe to push.` (804 passed, 1 skipped.)

Then the behavioral evidence, straight from the scenario YAMLs:

```bash
# 1. 5% drop run — all three validators PASS
uv run nest run scenarios/streaming_payments.yaml -o ./t

…

Try it

Open PR on GitHubView diff

Checkout locally

git fetch origin pull/93/head:pr-93
git checkout pr-93