feat(e2e): readable economy run + live consuming morphisms#191
Merged
Conversation
Follow-up to the green riverdale-economy economy (PR #190): the run was unreadable — the background keepalive + per-send DL1 lines printed live (50+ lines over 20 min) while the flow output was buffered and dumped only at the end, and every step just said "OK" with no view of the economy moving. Harness (shared by all lanes, back-compatible): - keepalive: gate the first-accepted + per-10 heartbeat behind E2E_VERBOSE; stop() prints one line ("[keepalive] silent · N sent, M failed"). - sendDataTransaction: fix describeUpdate (it read message.value on the UNWRAPPED generator output → always "update"; now reads the envelope key + inner fiberId/assetId/name); gate the healthy 3/3 per-send line behind E2E_VERBOSE. Divergence/failure lines always print. - FlowLogger: live (write-through) mode; flush() is a no-op when live. - Concurrency: an INTERACTIVE single-example run (TTY) defaults to 1 so its flows stream live. The isTTY gate keeps CI identical — tictactoe is a single-example lane with 3 parallel flows that must NOT be serialized. Visibility (riverdale-economy): - new poll-only step actions `phase` (section banner) and `economy` (reads the ML0 checkpoint once and prints a per-party table: state + a few stateData fields + held assets, with deltas vs the previous snapshot). - assertState/assertAsset now summarize what they observed (state+seq / holder+amount) instead of a bare OK. - example.ts: 13 phase banners + 12 economy snapshots across P0..P12 (all functional/assert steps unchanged); friendly assetAsset labels. tsc-clean. phase/economy are additive early-continue no-ops; verbose-gating defaults to quiet, so all lanes get a quieter, more readable run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R5TUSJPD8FCtJagf7siXgt
Consuming-morphism confirmation in the runner's applyMorphism path (the two modes a source-seq-advance confirm can't observe): - FRACTIONALIZE removes the source + writes one shard per shardId → confirm via the FIRST output shard's existence (confirmPath points at it). - BURN/DECOMPOSE remove the source record → confirm via the source's ABSENCE (it existed pre-send, so exists→404 means the morphism committed); a small resubmit-aware absence poll, never re-burning a gone asset. Non-consuming morphisms (STAKE/Transfer/Wrap) keep the source-seq-advance confirm. example.ts P12: STAKE was already live; now FRACTIONALIZE (mint 900 RVD into carol's wallet → 3×300 shards, asserted) and BURN (mint 200 into frank's wallet → burn → the economy snapshot shows it disappear) run live too. All three approved wallet morphisms are now exercised end-to-end. Economy snapshots: color the deltas — yellow for a changed value / departed instance (`taxesPaid 0→50`, `RVD-tax 50→0`), green for a newly-acquired instance (`+RVD-loan×10000`) — so the eye lands on what moved. ANSI codes are only in the value columns, so label alignment is unaffected. tsc-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R5TUSJPD8FCtJagf7siXgt
Cut the riverdale-economy wall-clock by running the INDEPENDENT setup steps
concurrently. The causal economic chain (lend→buy→repay→tax) stays serial —
only steps with no inter-dependency are batched.
Runner: a `parallel: true` step flag. The per-step body is extracted into a
`processStep(step, i, sw, sl, slog)` closure; a maximal run of consecutive
`parallel` steps executes via Promise.allSettled, each writing to its OWN
buffered FlowLogger that flushes in step order (concurrent output never
interleaves). The SEQUENTIAL path is byte-for-byte unchanged — the else
branch calls processStep with the flow's live logger + the same
try/catch → {passed:false, failedStep:i+1} — so no other lane is affected
(no existing example sets `parallel`). 12 step-level `continue`s became
`return`s; tsc-clean guarantees none dangle.
example.ts FLOW 1 — 3 batches (12 steps):
- P0: the 2 createAssetPolicy + retailer.machine@1.0.0 (distinct registry
names, no shared lineage). Same-package later versions (retailer@2.0.0,
fed@1.0.0/2.0.0) stay SEQUENTIAL: a lineage is monotonic append-only
(VersionLineage.publish → NonMonotonic), so racing 1.0.0/2.0.0 could land
2.0.0 first and reject 1.0.0 (the N3 negative). Causal, not parallel.
- P1: all six fiber creates (each mints its own `as` alias — distinct
session.fibers key, no shared-mutable race).
- P2: the three RVD-leg mints into the consumer fiber (distinct assetIds).
GOODS + RVD_LOAN mints stay sequential (split off by their assertAsset).
Concurrency-safe: every batched step writes a distinct registry name / `as`
alias / assetId, so no shared key is touched by two concurrent steps.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5TUSJPD8FCtJagf7siXgt
scasplte2
approved these changes
Jun 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #190 (the now-merged riverdale-economy economy): make the e2e run readable and show the economy moving.
Why
A real run was unreadable — the background keepalive (
[keepalive] N sent, every 3s) and per-send[dl1] update → 3/3 ✓lines printed live (50+ lines over a 20-min run) while the flow output was buffered and dumped only at the end. You watched plumbing spam, then a wall of✓ OKwith no view of the economy actually moving.Harness (shared by all lanes — back-compatible)
E2E_VERBOSE;stop()prints one line.describeUpdate(it readmessage.valueon the unwrapped generator output → always "update"; now reads the envelope key + innerfiberId/assetId/name); the healthy 3/3 per-send line is gated behindE2E_VERBOSE. Divergence/failure lines always print.flush()is a no-op when live.isTTYgate is load-bearing — it keeps CI identical (tictactoe is a single-example lane with 3 parallel flows that must not be serialized; CI has no TTY).Visibility (riverdale-economy)
phase(section banner) andeconomy(reads the ML0 checkpoint once and prints a per-party table: state + a few stateData fields + held assets, with deltas vs the previous snapshot, e.g.inventory 1000→500,RVD-loan 10000→0).assertState/assertAssetnow summarize what they observed (retailer → received (seq 1),GOODS → Fiber(retailer) ×500) instead of a bareOK.example.ts: 13phasebanners + 12economysnapshots across P0..P12 — all functional/assert steps unchanged.Sample
tsc-clean.phase/economyare additive early-continue no-ops (other examples unaffected); verbose-gating defaults to quiet, so every lane gets a quieter, more readable run. Runnpm test -- --only riverdale-economy(in a TTY) to watch it live.🤖 Generated with Claude Code