test: real-chain e2e validation against Paseo Next v2 - #2
Merged
Conversation
Spec for a Paseo Next v2 deploy test that round-trips the on-chain contenthash back to the exact bytes staged, rather than asserting exit 0 — a bad CAR deploys successfully and then serves 404s. Records the findings from the spike that shaped it: the default worker signs with no session and pays 0 PAS, CI needs 'ipfs init' and not just the binary, the contenthash addresses the CAR rather than a browsable directory, and the .paseo.li name URL serves the host shell rather than the deployed content.
Adds e2e/BOOTSTRAP.md (what the e2e suite depends on, how to check it, how to recover it) and e2e/bootstrap.sh (idempotent, check-only-by-default preflight; --fix for local ipfs init; --register gated behind confirmation for the one real deploy that reclaims decentralize-ci.dot). Does not implement the e2e tests themselves.
Deploys a run-unique fixture to decentralize-ci.dot and asserts the deployed manifest's per-file CID and size match the bytes that were staged. Exit 0 is not the assertion: a bad CAR deploys successfully and then serves 404s, so the test round-trips the on-chain contenthash back through bulletin-deploy's public manifest API instead. Also guards the archive shape — the default deploy must contain exactly index.html, with --fallback covered as a separate case. The deploy runs under a throwaway HOME so no login session is visible to the child. Without it, a local run by a signed-in developer would transfer decentralize-ci.dot to their own account and permanently break CI's overwrite path. IPFS_PATH is resolved from the real environment first, since a fresh HOME also hides the IPFS repo. The deploy is spawned asynchronously rather than with spawnSync: an 80-second synchronous block starves vitest's reporter RPC, which surfaced as a 'Timeout calling onTaskUpdate' unhandled error and vitest's own warning that results might be false positives. Kept out of 'npm test' via a separate vitest config, so no PR run can reach the chain. The workflow is workflow_dispatch plus nightly only, never a required check.
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.
Adds an end-to-end suite that deploys to Paseo Next v2 for real and verifies what actually landed on-chain.
Why not just assert exit 0
The README documents the failure mode: a bad CAR deploys successfully and then serves 404s. A test asserting on the exit code passes straight through the bug we most need to catch. So the suite round-trips the on-chain contenthash back to the bytes that were staged:
ipfs add -Q --cid-version=1 --raw-leaves --pin=false.Verified on-chain: <contenthash>.fetchManifestRoundtrip+parseManifest), which polls the gateway within a budget.files["index.html"].cidand.sizematch the staged fixture exactly.It also asserts the archive shape — a default deploy contains exactly index.html — which is a permanent regression guard on the default changed in #1. A second case covers
--fallback.Session isolation (subtle, please don't simplify it away)
The deploy runs under a throwaway
HOME. When a login session exists, bulletin-deploy transfers the registered name to the signed-in account — so a local run by a signed-in developer would movedecentralize-ci.dotto their personal account and permanently break CI's overwrite path.IPFS_PATHis resolved from the real environment before HOME is overridden, because a fresh HOME also hides the IPFS repo.Not a required check
workflow_dispatch+ nightly only. Two finalized chain transactions against a public RPC, ~3 minutes wall time.Unit Testsstays the sole required check;npm testis unchanged and stays hermetic, so no PR run can reach the chain.Dependencies
e2e/BOOTSTRAP.mdis a dependency register and recovery runbook, ande2e/bootstrap.shis a read-only preflight (--fixfor local remediation,--registerexplicit and confirmation-gated). Notable entries:decentralize-ci.dotis owned by bulletin-deploy's shared default worker, and the deploy costs 0 PAS only because that worker holds ProofOfPersonhoodFull — if that ever lapses the suite needs a funded account. CI needsipfs init, not just the binary.Design:
docs-internal/superpowers/specs/2026-08-10-e2e-chain-validation-design.md.Verified
npm run typecheckclean;npm teststill exactly 51 tests, e2e not collected;npm run buildclean.npm run test:e2erun for real: 2 passed, two real Paseo deploys, no unhandled errors.