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 #79Payments

cloud-post-code: streaming payments scenario factory (#03)

Problem: 03-payments-streaming-x402 — Streaming pay-per-second payments with mid-stream cancellation

Author

cloud-post-code avatar

@cloud-post-code

github profile →
Status
In review
Opened on
Jul 7
Branch
hackathon/cloud-post-code-streaming-payments

Description

The pitch.

## Problem

**Problem:** `03-payments-streaming-x402` — Streaming pay-per-second payments with mid-stream cancellation
**Layer:** payments
**Difficulty:** easy

`nest run streaming_payments` crashed with `KeyError: "No scenario factory registered for 'streaming_payments'"`. The plugin, scenario YAML, and three adversarial validators existed but were unrunnable — the scenario factory was missing.

## What I built

### `packages/nest-core/nest_core/scenarios_builtin/streaming_payments.py` (new)

- **`_TickRelayBuyer`** — billing gated on seller ack. Buyer sends `tick_req`; seller replies `tick_ack`; buyer reads balance delta (`balance_before - balance_after`) from the plugin, emits `payment_debited`/`payment_credited` as JSON self-messages, then schedules the next tick via `ctx.schedule(1.0, ...)` so ticks land at distinct virtual times. Even-indexed buyers cancel mid-stream (at `rounds//2`) to exercise genuine early `close_stream`.

- **`StreamingSellerAgent`** — replies `tick_ack` on each `tick_req`.

- **`streaming_payments_factory`** — shared-ledger pattern from marketplace factory.

Domain events are encoded as JSON self-messages (`ctx.send(ctx.agent_id, json.dumps(...))`) — the engine records them as `kind:send` with `ts`, `agent`, and `corr` attribution. Validators parse them via `_streaming_audit_events()`, identical in structure to `_empic_audit_events()`. No `ctx.emit()` — simulator is unmodified.

### `packages/nest-core/nest_core/validators.py`

- Added `_streaming_audit_events()` helper — parses `type:streaming_audit` self-messages from engine-attributed `kind:send` records.
- Rewrote all four streaming validators to consume audit events rather than bare top-level dicts.
- Added **`validate_streaming_no_debit_without_ack`** (adversarial): every `payment_debited` audit entry must be preceded by a `kind:receive` `tick_ack` from the engine. A buggy driver that bills without delivery produces a debit with no ack — FAIL. Registered for `streaming_payments` and `streaming_payments_partition`.

### `packages/nest-core/nest_core/scenarios.py`

Registered `streaming_payments` and `streaming_payments_partition` in `_try_load_builtin`.

## How to run it

```bash
uv run nest run streaming_payments

uv run python -c "
from pathlib import Path
from nest_core.validators import validate_trace
for r in validate_trace(Path('traces/streaming_payments.jsonl'), 'streaming_payments'):
    print(('PASS' if r.passed else 'FAIL'), r.name, '-', r.detail)
"

uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run pytest -v
```

## Result

Seeds 1, 2, 42, 1337 — all pass:

```
PASS streaming_conservation          - conservation verified: 300 total flow
PASS streaming_no_drain_after_close  - verified 5 streams, no drain-after-close
PASS streaming_no_debit_without_ack  - every payment_debited preceded by a tick_ack
PASS streaming_no_overbill_on_partition - verified 5 streams across 13 partition edges, no over-bill
```

Partition scenario (full buyer/seller isolation): all four PASS, conservation shows `0 total flow` — correct, no acks delivered, no billing.

1007 passed, 0 type errors.

## Validator discrimination

- **Conservation** — amounts derive from `balance_before - balance_after tick_stream()`, not a config constant. A plugin that moves the wrong amount breaks conservation.
- **No drain after close** — ticks land at `ctx.time = 1, 2, 3...` (via `ctx.schedule`), so `debit_tick > close_tick` is non-trivial.
- **No debit without ack** — reads `kind:receive` engine events for `tick_ack`. A driver that bills without waiting for delivery has no matching ack → FAIL. Unit tests include a negative control that verifies this.
- **No overbill on partition** — billing stops when acks stop arriving; partition drops the ack, so no debit is recorded.

## Fixes from review (Skyrider3 rounds 1 and 2)

1. Seed-bank failures (seeds 1, 2, 1337) — replaced pre-scheduled tick blast with request/ack chain.
2. No adversarial validator — 

…

Try it

Open PR on GitHubView diff

Checkout locally

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