Wallet signing
diff --git a/apps/web/lib/config.test.ts b/apps/web/lib/config.test.ts
index 2a96cdc..c2346c3 100644
--- a/apps/web/lib/config.test.ts
+++ b/apps/web/lib/config.test.ts
@@ -6,28 +6,57 @@ import {
networkConfigs,
} from "./config.ts";
-test("testnet and mainnet configurations are explicit and isolated", () => {
+test("testnet is the deployed registry network and mainnet stays isolated", () => {
assert.equal(networkConfigs.TESTNET.network, "TESTNET");
assert.match(networkConfigs.TESTNET.rpcUrl, /testnet/);
- assert.equal(networkConfigs.TESTNET.registryAccountId, null);
+ assert.equal(networkConfigs.TESTNET.registryAccountId, "commonsops.testnet");
+ assert.equal(networkConfigs.TESTNET.deployed, true);
assert.equal(networkConfigs.MAINNET.network, "MAINNET");
assert.match(networkConfigs.MAINNET.rpcUrl, /mainnet/);
+ assert.equal(networkConfigs.MAINNET.registryAccountId, null);
+ assert.equal(networkConfigs.MAINNET.deployed, false);
assert.notEqual(networkConfigs.TESTNET.rpcUrl, networkConfigs.MAINNET.rpcUrl);
+ assert.notEqual(
+ networkConfigs.TESTNET.registryAccountId,
+ networkConfigs.MAINNET.registryAccountId,
+ );
assert(Object.isFrozen(networkConfigs));
});
-test("wallet boundary fails closed until connector and deployment exist", async () => {
- const wallet = createWalletBoundary("TESTNET");
- assert.equal(wallet.state.status, "UNAVAILABLE");
- assert.equal(wallet.canRead, false);
- assert.equal(wallet.canSign, false);
- await assert.rejects(wallet.connect(), /WALLET_DISABLED/);
- await assert.rejects(wallet.sign(), /WALLET_DISABLED/);
+test("wallet boundary fails closed on every network even with a deployment", async () => {
+ const testnet = createWalletBoundary("TESTNET");
+ assert.equal(testnet.state.status, "UNAVAILABLE");
+ assert.equal(
+ testnet.state.reason,
+ "No supported wallet connector is configured.",
+ );
+ assert.equal(testnet.canRead, false);
+ assert.equal(testnet.canSign, false);
+ await assert.rejects(testnet.connect(), /WALLET_DISABLED/);
+ await assert.rejects(testnet.sign(), /WALLET_DISABLED/);
+
+ const mainnet = createWalletBoundary("MAINNET");
+ assert.equal(mainnet.state.status, "UNAVAILABLE");
+ assert.equal(
+ mainnet.state.reason,
+ "No mainnet registry deployment is configured.",
+ );
+ await assert.rejects(mainnet.connect(), /WALLET_DISABLED/);
+ await assert.rejects(mainnet.sign(), /WALLET_DISABLED/);
});
-test("read state is unconfigured without a deployed contract", () => {
+test("read state is unconfigured on mainnet and pinned to the deployed testnet registry", () => {
assert.deepEqual(emptyRegistryReadState("TESTNET"), {
network: "TESTNET",
+ contractAccountId: "commonsops.testnet",
+ status: "LOADING",
+ templates: [],
+ cycles: [],
+ paymentStates: {},
+ error: null,
+ });
+ assert.deepEqual(emptyRegistryReadState("MAINNET"), {
+ network: "MAINNET",
contractAccountId: null,
status: "UNCONFIGURED",
templates: [],
diff --git a/apps/web/lib/config.ts b/apps/web/lib/config.ts
index 382c25e..fa7b913 100644
--- a/apps/web/lib/config.ts
+++ b/apps/web/lib/config.ts
@@ -26,14 +26,18 @@ export interface AppNetworkConfig {
readonly deployed: boolean;
}
+// The testnet registry is deployed and the full journey is recorded in
+// docs/testnet-journey.md. This workspace still performs no live RPC: the
+// recorded values are imported as fixture data. Mainnet stays isolated and
+// unconfigured, so its reads and signing keep failing closed.
export const networkConfigs: Readonly
> =
Object.freeze({
TESTNET: Object.freeze({
network: "TESTNET",
rpcUrl: "https://rpc.testnet.near.org",
explorerUrl: "https://testnet.nearblocks.io",
- registryAccountId: null,
- deployed: false,
+ registryAccountId: "commonsops.testnet",
+ deployed: true,
}),
MAINNET: Object.freeze({
network: "MAINNET",
diff --git a/apps/web/lib/fixture-journey.test.ts b/apps/web/lib/fixture-journey.test.ts
index bdad789..3e6b8c9 100644
--- a/apps/web/lib/fixture-journey.test.ts
+++ b/apps/web/lib/fixture-journey.test.ts
@@ -5,12 +5,11 @@ import {
fixtureJourneyConstants,
} from "./fixture-journey.ts";
-const expectedNoDeploymentReason =
- "No reviewed testnet registry deployment is configured.";
-const expectedContributorNoDeploymentReason =
- "No reviewed testnet registry deployment is configured. Wallet connector is not configured.";
+const expectedWalletOnlyReason = "Wallet connector is not configured.";
+const expectedClaimUnavailableReason = `Only an open, live cycle can be claimed. ${expectedWalletOnlyReason}`;
+const expectedExpireUnavailableReason = `Neither the claim nor work deadline has elapsed. ${expectedWalletOnlyReason}`;
-test("derives exact disabled operator fixture intents", async () => {
+test("derives exact disabled operator intents from recorded template 0", async () => {
const journey = await buildFixtureJourney();
assert.deepEqual(
journey.operator.intents.map((intent) => intent.method),
@@ -20,38 +19,38 @@ test("derives exact disabled operator fixture intents", async () => {
const [create, materialize] = journey.operator.intents;
assert(create && materialize);
assert.deepEqual(create, {
- receiverId: "",
+ receiverId: "commonsops.testnet",
method: "create_template",
args: {
input: {
- dao_account_id: "marmaj.sputnikv2.testnet",
- title: "Recurring public documentation maintenance",
+ dao_account_id: "commonsops-dao.sputnikv2.testnet",
+ title: "Public documentation maintenance",
description:
- "Review and update the community's public contributor documentation.",
- start_at_ns: "1793740800000000000",
+ "Recurring maintenance of the CommonsOps public documentation: review and merge community pull requests.",
+ start_at_ns: "1793910183011867400",
interval_ns: "604800000000000",
work_window_ns: "432000000000000",
- claim_duration_ns: "86400000000000",
- amount_yocto: "81900000000000000000000000",
+ claim_duration_ns: "172800000000000",
+ amount_yocto: "500000000000000000000000",
},
},
attachedDepositYocto: "40960000000000000000000",
network: "TESTNET",
signingEnabled: false,
- disabledReason: expectedNoDeploymentReason,
+ disabledReason: expectedWalletOnlyReason,
});
assert.deepEqual(materialize, {
- receiverId: "",
+ receiverId: "commonsops.testnet",
method: "materialize_cycle",
- args: { template_id: "7" },
+ args: { template_id: "0" },
attachedDepositYocto: "163840000000000000000000",
network: "TESTNET",
signingEnabled: false,
- disabledReason: expectedNoDeploymentReason,
+ disabledReason: expectedWalletOnlyReason,
});
});
-test("derives claimed contributor identity, evidence and all four disabled intents", async () => {
+test("derives the recorded claimed cycle identity, evidence and all four disabled intents", async () => {
const journey = await buildFixtureJourney();
const contributor = journey.contributor;
@@ -61,20 +60,32 @@ test("derives claimed contributor identity, evidence and all four disabled inten
contributor.observedBlockTimestampNs,
fixtureJourneyConstants.observedBlockTimestampNs,
);
- assert.equal(contributor.cycle.claim?.claimant_id, "chloe.testnet");
+ assert.deepEqual(contributor.cycle.key, {
+ template_id: "0",
+ period_index: "0",
+ });
+ assert.deepEqual(contributor.view.key, {
+ template_id: "0",
+ period_index: "0",
+ });
+ assert.equal(
+ contributor.cycle.dao_account_id,
+ "commonsops-dao.sputnikv2.testnet",
+ );
+ assert.equal(contributor.cycle.claim?.claimant_id, "commonsops.testnet");
assert.equal(
contributor.cycle.claim?.payout_recipient_id,
contributor.cycle.claim?.claimant_id,
);
- assert.equal(contributor.view.payoutRecipientId, "chloe.testnet");
+ assert.equal(contributor.view.payoutRecipientId, "commonsops.testnet");
assert.deepEqual(contributor.evidencePreview, {
- repository_owner: "example",
- repository_name: "docs",
- pull_request_number: "42",
- url: "https://github.com/example/docs/pull/42",
- commit_sha: "ab".repeat(20),
+ repository_owner: "super-user-root",
+ repository_name: "commonsops",
+ pull_request_number: "17",
+ url: "https://github.com/super-user-root/commonsops/pull/17",
+ commit_sha: "1032733c2115b9e9c038d50617f6e151ac8bd59d",
digest_sha256:
- "03970fbdf3f5f23e4be7a57548214c1a85ba8ecafac0cc3a34d9f52881e94773",
+ "09936627004b6e19664173302ff9d52613f5febef27ab34cfa488b54b83d71b8",
});
assert.deepEqual(
contributor.intents.map((intent) => intent.method),
@@ -87,43 +98,60 @@ test("derives claimed contributor identity, evidence and all four disabled inten
);
for (const intent of contributor.intents) {
assert.equal(intent.network, "TESTNET");
- assert.equal(intent.receiverId, "");
+ assert.equal(intent.receiverId, "commonsops.testnet");
assert.equal(intent.attachedDepositYocto, "0");
assert.equal(intent.signingEnabled, false);
- assert.match(intent.disabledReason, /No reviewed testnet registry/);
+ assert.match(intent.disabledReason, /Wallet connector is not configured/);
}
+ const claim = contributor.intents.find(
+ (intent) => intent.method === "claim_cycle",
+ );
const release = contributor.intents.find(
(intent) => intent.method === "release_claim",
);
+ const expire = contributor.intents.find(
+ (intent) => intent.method === "expire_cycle_or_claim",
+ );
const submit = contributor.intents.find(
(intent) => intent.method === "submit_evidence",
);
- assert(release && submit);
+ assert(claim && release && expire && submit);
+ assert.equal(claim.disabledReason, expectedClaimUnavailableReason);
assert.equal(release.actionAllowed, true);
- assert.equal(release.disabledReason, expectedContributorNoDeploymentReason);
+ assert.equal(release.disabledReason, expectedWalletOnlyReason);
+ assert.equal(expire.disabledReason, expectedExpireUnavailableReason);
assert.equal(submit.actionAllowed, true);
assert.deepEqual(submit.args, {
- cycle_key: { template_id: "7", period_index: "3" },
- repository_owner: "example",
- repository_name: "docs",
- pull_request_number: "42",
- commit_sha: "ab".repeat(20),
+ cycle_key: { template_id: "0", period_index: "0" },
+ repository_owner: "super-user-root",
+ repository_name: "commonsops",
+ pull_request_number: "17",
+ commit_sha: "1032733c2115b9e9c038d50617f6e151ac8bd59d",
});
assert.equal("payout_recipient_id" in submit.args, false);
assert.equal("digest_sha256" in submit.args, false);
});
-test("derives authentic proposal-63 PAID provenance and keeps Approved alone unpaid", async () => {
+test("derives the recorded proposal-0 PAID provenance and keeps Approved alone unpaid", async () => {
const journey = await buildFixtureJourney();
const reviewer = journey.reviewer;
assert.equal(reviewer.cycle.status, "SUBMITTED");
assert.equal(reviewer.cycle.proposal_link?.key.network, "TESTNET");
- assert.equal(reviewer.cycle.proposal_link?.key.proposal_id, "63");
+ assert.equal(
+ reviewer.cycle.proposal_link?.key.dao_account_id,
+ "commonsops-dao.sputnikv2.testnet",
+ );
+ assert.equal(reviewer.cycle.proposal_link?.key.proposal_id, "0");
assert.equal(reviewer.model.link.status, "LINKED_IMMUTABLE");
assert.equal(reviewer.model.link.intent, null);
- assert.match(reviewer.model.fixtureNotice, /P3-T01 authentic proposal-63/);
+ assert.equal(reviewer.model.profileSupported, false);
+ assert.equal(reviewer.model.profileVerifiable, true);
+ assert.match(
+ reviewer.model.fixtureNotice,
+ /Recorded testnet tx_status snapshot for the proposal-0 payout/,
+ );
assert.deepEqual(reviewer.model.payment, {
state: "PAID",
reason: "Verified one successful causal native NEAR payout",
@@ -134,7 +162,7 @@ test("derives authentic proposal-63 PAID provenance and keeps Approved alone unp
actionReceiptId: fixtureJourneyConstants.actionReceiptId,
payoutReceiptId: fixtureJourneyConstants.payoutReceiptId,
evidenceKey:
- "testnet:marmaj.sputnikv2.testnet:63:4Rag51CLP56uPZqovgMzEiNKNeSLF8Bn7G3WiNhuGtuJ:H7tr1j2QAcFx3ibwqKeSHtBwpiaFYRaUfPbj6YzBddgJ",
+ "testnet:commonsops-dao.sputnikv2.testnet:0:kBH4oqQh8mMy6oGW727XZmQgVobUv1r8r11ExMrsPro:EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2",
blockHash: fixtureJourneyConstants.blockHash,
});
@@ -151,19 +179,19 @@ test("derives authentic proposal-63 PAID provenance and keeps Approved alone unp
);
});
-test("marks every snapshot as immutable fixture-only testnet data with no deployment", async () => {
+test("marks every snapshot as an immutable recorded-journey testnet snapshot", async () => {
const first = await buildFixtureJourney();
const second = await buildFixtureJourney();
assert.deepEqual(first.provenance, {
mode: "FIXTURE_ONLY",
network: "TESTNET",
- registryDeployment: null,
+ registryDeployment: "commonsops.testnet",
walletAvailable: false,
liveReadsRan: false,
notice:
- "Stages 1–3 are separate immutable fixture snapshots of one journey, not one mutable live record.",
- fixtureSource: "tests/fixtures/sputnik/proposal-63-paid.json",
+ "Stages 1–3 are separate immutable fixture snapshots of the recorded live testnet journey (docs/testnet-journey.md), not one mutable live record.",
+ fixtureSource: "apps/web/lib/fixtures/proposal-0-paid.json",
});
assert.notStrictEqual(first, second);
assert.notStrictEqual(first.contributor.cycle, first.reviewer.cycle);
@@ -210,4 +238,15 @@ test("defines explicit semantic rendering patterns without claiming live request
?.detail ?? "",
/canonical adapter derived PAID/,
);
+ assert.match(
+ journey.statePatterns.find((pattern) => pattern.key === "VERIFIED_FIXTURE")
+ ?.detail ?? "",
+ /node scripts\/reconcile-live\.mjs/,
+ );
+ assert.doesNotMatch(
+ journey.statePatterns.find(
+ (pattern) => pattern.key === "EMPTY_UNCONFIGURED",
+ )?.detail ?? "",
+ /testnet is unconfigured/i,
+ );
});
diff --git a/apps/web/lib/fixture-journey.ts b/apps/web/lib/fixture-journey.ts
index afc0a4f..1171bdf 100644
--- a/apps/web/lib/fixture-journey.ts
+++ b/apps/web/lib/fixture-journey.ts
@@ -1,5 +1,5 @@
import type { Cycle } from "@commonsops/domain";
-import paidProposal63ReceiptGraph from "../../../tests/fixtures/sputnik/proposal-63-paid.json" with { type: "json" };
+import recordedProposal0ReceiptGraph from "./fixtures/proposal-0-paid.json" with { type: "json" };
import {
contributorCycleIntent,
deriveContributorCycleView,
@@ -24,21 +24,28 @@ import {
type ReviewerRequestState,
} from "../src/features/reviewer/reviewer.ts";
-const templateId = "7";
-const periodIndex = "3";
-const claimantId = "chloe.testnet";
-const daoAccountId = "marmaj.sputnikv2.testnet";
-const payoutAmountYocto = "81900000000000000000000000";
+// Every value below is the exact recorded onchain data of the executed
+// CommonsOps testnet journey documented in docs/testnet-journey.md
+// (registry commonsops.testnet, template 0, cycle 0:0, DAO proposal 0).
+const templateId = "0";
+const periodIndex = "0";
+const claimantId = "commonsops.testnet";
+const daoAccountId = "commonsops-dao.sputnikv2.testnet";
+const payoutAmountYocto = "500000000000000000000000";
+const registryAccountId = "commonsops.testnet";
export const fixtureJourneyConstants = Object.freeze({
templateId,
periodIndex,
- observedBlockTimestampNs: "1795558800000000000",
- fixtureSource: "tests/fixtures/sputnik/proposal-63-paid.json",
- transactionHash: "2Nk6t5MJJ6uRUrpoNcY7Hhuaupw9do7ybvxpxZYYS1Dm",
- actionReceiptId: "4Rag51CLP56uPZqovgMzEiNKNeSLF8Bn7G3WiNhuGtuJ",
- payoutReceiptId: "H7tr1j2QAcFx3ibwqKeSHtBwpiaFYRaUfPbj6YzBddgJ",
- blockHash: "8VY8wMxeLmDnncFcU6bpECgJfycypv22qAGm1HeNtygy",
+ // Block-header timestamp of the recorded claim_cycle execution block
+ // (8cpCnL53LBMi5N2TKTsG6SLj5EwpgUPN7vpUo4YRAAci, journey step 4); at this
+ // instant the recorded claim was live.
+ observedBlockTimestampNs: "1793910672509596700",
+ fixtureSource: "apps/web/lib/fixtures/proposal-0-paid.json",
+ transactionHash: "ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7",
+ actionReceiptId: "kBH4oqQh8mMy6oGW727XZmQgVobUv1r8r11ExMrsPro",
+ payoutReceiptId: "EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2",
+ blockHash: "5ukLMpiXjTW9y9bojhM2cPRgD25v9fMa7MkKxFat1Se6",
});
export type StatePatternKey =
@@ -55,7 +62,7 @@ export interface FixtureJourney {
readonly provenance: {
readonly mode: "FIXTURE_ONLY";
readonly network: "TESTNET";
- readonly registryDeployment: null;
+ readonly registryDeployment: string | null;
readonly walletAvailable: false;
readonly liveReadsRan: false;
readonly notice: string;
@@ -99,14 +106,14 @@ function claimedCycleFixture(): Cycle {
return deepFreeze({
key: { template_id: templateId, period_index: periodIndex },
status: "CLAIMED",
- opens_at_ns: "1795555200000000000",
- deadline_at_ns: "1795987200000000000",
+ opens_at_ns: "1793910183011867400",
+ deadline_at_ns: "1794342183011867400",
dao_account_id: daoAccountId,
amount_yocto: payoutAmountYocto,
claim: {
claimant_id: claimantId,
payout_recipient_id: claimantId,
- expires_at_ns: "1795641600000000000",
+ expires_at_ns: "1794083472509596599",
},
evidence: null,
submitted_at_ns: null,
@@ -120,31 +127,33 @@ function submittedLinkedCycleFixture(
return deepFreeze({
key: { template_id: templateId, period_index: periodIndex },
status: "SUBMITTED",
- opens_at_ns: "1795555200000000000",
- deadline_at_ns: "1795987200000000000",
+ opens_at_ns: "1793910183011867400",
+ deadline_at_ns: "1794342183011867400",
dao_account_id: daoAccountId,
amount_yocto: payoutAmountYocto,
claim: {
claimant_id: claimantId,
payout_recipient_id: claimantId,
- expires_at_ns: "1795641600000000000",
+ expires_at_ns: "1794083472509596599",
},
evidence: { ...evidence },
- submitted_at_ns: "1795562400000000000",
+ submitted_at_ns: "1793910689442132732",
proposal_link: {
key: {
network: "TESTNET",
dao_account_id: daoAccountId,
- proposal_id: "63",
+ proposal_id: "0",
},
- linked_at_ns: "1795566000000000000",
+ linked_at_ns: "1793910838179857287",
},
} satisfies Cycle);
}
-function proposal63Fixture() {
+function proposal0Fixture() {
+ // Exact recorded kind/status of DAO proposal 0 (docs/testnet-journey.md
+ // step 6 and the recorded act_proposal VoteApprove transaction).
return deepFreeze({
- id: 63,
+ id: 0,
status: "Approved",
kind: {
Transfer: {
@@ -158,26 +167,31 @@ function proposal63Fixture() {
}
/**
- * Builds deterministic server-rendered snapshots. It never reads the clock,
- * generates identifiers, performs RPC, or mutates the imported P3-T01 fixture.
+ * Builds deterministic server-rendered snapshots of the recorded live
+ * testnet journey (docs/testnet-journey.md). It never reads the clock,
+ * generates identifiers, performs RPC, or mutates the imported fixture.
*/
export async function buildFixtureJourney(): Promise {
const draft = deepFreeze({
daoAccountId,
- title: "Recurring public documentation maintenance",
+ title: "Public documentation maintenance",
description:
- "Review and update the community's public contributor documentation.",
- startAtNs: "1793740800000000000",
+ "Recurring maintenance of the CommonsOps public documentation: review and merge community pull requests.",
+ startAtNs: "1793910183011867400",
intervalNs: "604800000000000",
workWindowNs: "432000000000000",
- claimDurationNs: "86400000000000",
+ claimDurationNs: "172800000000000",
amountYocto: payoutAmountYocto,
} satisfies CreateTemplateDraft);
const operatorIntents = deepFreeze([
- createTemplateIntent(null, draft, TEMPLATE_MIN_DEPOSIT_YOCTO.toString()),
+ createTemplateIntent(
+ registryAccountId,
+ draft,
+ TEMPLATE_MIN_DEPOSIT_YOCTO.toString(),
+ ),
templateActionIntent(
- null,
+ registryAccountId,
"materialize_cycle",
templateId,
undefined,
@@ -187,10 +201,10 @@ export async function buildFixtureJourney(): Promise {
const contributorCycle = claimedCycleFixture();
const evidenceDraft = deepFreeze({
- repositoryOwner: "Example",
- repositoryName: "Docs",
- pullRequestNumber: "42",
- commitSha: "ab".repeat(20),
+ repositoryOwner: "super-user-root",
+ repositoryName: "CommonsOps",
+ pullRequestNumber: "17",
+ commitSha: "1032733c2115b9e9c038d50617f6e151ac8bd59d",
} satisfies GithubEvidenceDraft);
const observedBlockTimestampNs =
fixtureJourneyConstants.observedBlockTimestampNs;
@@ -203,28 +217,28 @@ export async function buildFixtureJourney(): Promise {
claimantId,
),
contributorCycleIntent(
- null,
+ registryAccountId,
"claim_cycle",
contributorCycle,
observedBlockTimestampNs,
claimantId,
),
contributorCycleIntent(
- null,
+ registryAccountId,
"release_claim",
contributorCycle,
observedBlockTimestampNs,
claimantId,
),
contributorCycleIntent(
- null,
+ registryAccountId,
"expire_cycle_or_claim",
contributorCycle,
observedBlockTimestampNs,
claimantId,
),
evidenceSubmissionPreview(
- null,
+ registryAccountId,
contributorCycle,
observedBlockTimestampNs,
claimantId,
@@ -239,15 +253,15 @@ export async function buildFixtureJourney(): Promise {
submissionPreview.intent,
] satisfies readonly ContributorTransactionIntent[]);
const reviewerCycle = submittedLinkedCycleFixture(submissionPreview.evidence);
- const proposal = proposal63Fixture();
+ const proposal = proposal0Fixture();
const reviewerBase = {
cycle: reviewerCycle,
profile: {
- version: "2.0.0",
- codeHash: "ZGdM2TFdQpcXrxPxvq25514EViyi9xBSboetDiB3Uiq",
+ version: "2.3.1",
+ codeHash: "AA4nA45vauiac3jdVwJTzSdUP3oj2r1fDaPAb2mHdgcH",
},
- policy: { proposal_bond: "19000000000000000000000" },
- reviewedRegistryAccountId: null,
+ policy: { proposal_bond: "0" },
+ reviewedRegistryAccountId: registryAccountId,
proposalIdForLink: null,
} as const;
@@ -257,9 +271,9 @@ export async function buildFixtureJourney(): Promise {
observation: {
kind: "FIXTURE",
fixtureLabel:
- "P3-T01 authentic proposal-63 RPC snapshot imported in place from tests/fixtures/sputnik/proposal-63-paid.json; not a live read",
+ "Recorded testnet tx_status snapshot for the proposal-0 payout, imported in place from apps/web/lib/fixtures/proposal-0-paid.json; not a live read",
proposal,
- receiptResponse: paidProposal63ReceiptGraph,
+ receiptResponse: recordedProposal0ReceiptGraph,
},
}),
buildReviewerModel({
@@ -286,7 +300,7 @@ export async function buildFixtureJourney(): Promise {
label: "Empty / unconfigured",
role: "status",
detail:
- "No live request ran. No reviewed testnet registry deployment exists, so the current safe read state contains no live records.",
+ "Rendering example only; no live request ran. Mainnet stays isolated and unconfigured; a read against an unconfigured network would show this empty state.",
},
{
key: "PENDING",
@@ -305,7 +319,7 @@ export async function buildFixtureJourney(): Promise {
key: "VERIFIED_FIXTURE",
label: "Verified fixture",
role: "status",
- detail: `Fixture success pattern only; no live request ran. The canonical adapter derived ${reviewerModel.payment.state}: ${reviewerModel.payment.reason}.`,
+ detail: `Recorded-snapshot success pattern only; no live request ran. The canonical adapter derived ${reviewerModel.payment.state} from the recorded proposal-0 receipt graph: ${reviewerModel.payment.reason}. Re-derive it with node scripts/reconcile-live.mjs.`,
},
] satisfies readonly StatePattern[]);
@@ -313,11 +327,11 @@ export async function buildFixtureJourney(): Promise {
provenance: {
mode: "FIXTURE_ONLY",
network: "TESTNET",
- registryDeployment: null,
+ registryDeployment: registryAccountId,
walletAvailable: false,
liveReadsRan: false,
notice:
- "Stages 1–3 are separate immutable fixture snapshots of one journey, not one mutable live record.",
+ "Stages 1–3 are separate immutable fixture snapshots of the recorded live testnet journey (docs/testnet-journey.md), not one mutable live record.",
fixtureSource: fixtureJourneyConstants.fixtureSource,
},
operator: {
diff --git a/apps/web/lib/fixtures/README.md b/apps/web/lib/fixtures/README.md
new file mode 100644
index 0000000..0757e34
--- /dev/null
+++ b/apps/web/lib/fixtures/README.md
@@ -0,0 +1,28 @@
+# Recorded receipt-graph fixture
+
+`proposal-0-paid.json` is the exact `EXPERIMENTAL_tx_status` JSON-RPC response
+for the executed CommonsOps testnet payout — transaction
+`ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7`, the `act_proposal
+VoteApprove` call on proposal `0` of `commonsops-dao.sputnikv2.testnet`
+(recorded in `docs/testnet-journey.md`, steps 7 and 9). Its causal receipt
+graph contains the act receipt `kBH4oqQh8mMy6oGW727XZmQgVobUv1r8r11ExMrsPro`
+and the direct-child payout Transfer receipt
+`EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2` with payout block
+`5ukLMpiXjTW9y9bojhM2cPRgD25v9fMa7MkKxFat1Se6`.
+
+Origin and fidelity:
+
+- Captured read-only from NEAR testnet (`EXPERIMENTAL_tx_status`, one RPC
+ read; nothing was signed, spent, or mutated) and written here verbatim —
+ no field was added, removed, or edited.
+- The file is a bare RPC envelope on purpose: `apps/web/lib/fixture-journey.ts`
+ imports it and passes it unchanged to the production
+ `buildReviewerModel`/adapter path, which parses it strictly.
+- Re-derive the same attribution from the live chain with
+ `node scripts/reconcile-live.mjs` (read-only). A successful run reports
+ `APPLIED` / adapter state `PAID` with payout receipt
+ `EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2` and writes
+ `deployments/testnet/replay-record.json`.
+
+This file is data captured from the public chain; it contains no keys or
+personal data.
diff --git a/apps/web/lib/fixtures/proposal-0-paid.json b/apps/web/lib/fixtures/proposal-0-paid.json
new file mode 100644
index 0000000..4ed49eb
--- /dev/null
+++ b/apps/web/lib/fixtures/proposal-0-paid.json
@@ -0,0 +1,743 @@
+{
+ "jsonrpc": "2.0",
+ "result": {
+ "final_execution_status": "FINAL",
+ "receipts": [
+ {
+ "predecessor_id": "commonsops.testnet",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "FunctionCall": {
+ "args": "eyJhY3Rpb24iOiJWb3RlQXBwcm92ZSIsImlkIjowfQ==",
+ "deposit": "0",
+ "gas": 100000000000000,
+ "method_name": "act_proposal"
+ }
+ }
+ ],
+ "gas_price": "1000000000",
+ "input_data_ids": [],
+ "is_promise_yield": false,
+ "output_data_receivers": [],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "kBH4oqQh8mMy6oGW727XZmQgVobUv1r8r11ExMrsPro",
+ "receiver_id": "commonsops-dao.sputnikv2.testnet"
+ },
+ {
+ "predecessor_id": "commonsops-dao.sputnikv2.testnet",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "Transfer": {
+ "deposit": "500000000000000000000000"
+ }
+ }
+ ],
+ "gas_price": "1000000000",
+ "input_data_ids": [],
+ "is_promise_yield": false,
+ "output_data_receivers": [
+ {
+ "data_id": "7yGmS2GfxjhaLYwkz7H6d4Ao425qqTHo6oMSLvNu1q6a",
+ "receiver_id": "commonsops-dao.sputnikv2.testnet"
+ }
+ ],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2",
+ "receiver_id": "commonsops.testnet"
+ },
+ {
+ "predecessor_id": "system",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "Transfer": {
+ "deposit": "200864306250000000000"
+ }
+ }
+ ],
+ "gas_price": "0",
+ "input_data_ids": [],
+ "is_promise_yield": false,
+ "output_data_receivers": [],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "2ZgfCXg9MWRkkboiKhpv2uqfRoMkt95BJShXZcLHZXup",
+ "receiver_id": "commonsops.testnet"
+ },
+ {
+ "predecessor_id": "commonsops-dao.sputnikv2.testnet",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "FunctionCall": {
+ "args": "eyJwcm9wb3NhbF9pZCI6MH0=",
+ "deposit": "0",
+ "gas": 10000000000000,
+ "method_name": "on_proposal_callback"
+ }
+ }
+ ],
+ "gas_price": "1000000000",
+ "input_data_ids": ["7yGmS2GfxjhaLYwkz7H6d4Ao425qqTHo6oMSLvNu1q6a"],
+ "is_promise_yield": false,
+ "output_data_receivers": [],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "H7Uu9meqBeux4eeNa3DhcWBnEYzVCdTy5Jmu7iT4pugD",
+ "receiver_id": "commonsops-dao.sputnikv2.testnet"
+ },
+ {
+ "predecessor_id": "commonsops-dao.sputnikv2.testnet",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "Transfer": {
+ "deposit": "0"
+ }
+ }
+ ],
+ "gas_price": "1000000000",
+ "input_data_ids": [],
+ "is_promise_yield": false,
+ "output_data_receivers": [],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "3quMbqcvdkTsMsxQQ74wCrWJnAYR3JsPk7gZfJMPTjbE",
+ "receiver_id": "commonsops.testnet"
+ },
+ {
+ "predecessor_id": "system",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "Transfer": {
+ "deposit": "200864306250000000000"
+ }
+ }
+ ],
+ "gas_price": "0",
+ "input_data_ids": [],
+ "is_promise_yield": false,
+ "output_data_receivers": [],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "5E3UD6czYoC19Ap9kbun6VriUibqEHQEx1Lh7HLViK6b",
+ "receiver_id": "commonsops.testnet"
+ },
+ {
+ "predecessor_id": "system",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "Transfer": {
+ "deposit": "10422529935091200000000"
+ }
+ }
+ ],
+ "gas_price": "0",
+ "input_data_ids": [],
+ "is_promise_yield": false,
+ "output_data_receivers": [],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "ExtpqjudWDD78THHwNMop7qXwbRuUB8uXHTZqf23nfxn",
+ "receiver_id": "commonsops.testnet"
+ },
+ {
+ "predecessor_id": "system",
+ "priority": 0,
+ "receipt": {
+ "Action": {
+ "actions": [
+ {
+ "Transfer": {
+ "deposit": "89478579098941200000000"
+ }
+ }
+ ],
+ "gas_price": "0",
+ "input_data_ids": [],
+ "is_promise_yield": false,
+ "output_data_receivers": [],
+ "signer_id": "commonsops.testnet",
+ "signer_public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39"
+ }
+ },
+ "receipt_id": "2XDkQGv8NqED7mi1fv2tcZdWcrg5GVgYCsUVwf2Wjaa2",
+ "receiver_id": "commonsops.testnet"
+ }
+ ],
+ "receipts_outcome": [
+ {
+ "block_hash": "ABGisBUKHk6pTY22LifpyXAEbvswyV3abVz4d286v8Tv",
+ "id": "kBH4oqQh8mMy6oGW727XZmQgVobUv1r8r11ExMrsPro",
+ "outcome": {
+ "executor_id": "commonsops-dao.sputnikv2.testnet",
+ "gas_burnt": 2982517541628,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "AA4nA45vauiac3jdVwJTzSdUP3oj2r1fDaPAb2mHdgcH"
+ }
+ ],
+ "gas_profile": [
+ {
+ "cost": "FUNCTION_CALL_BASE",
+ "cost_category": "ACTION_COST",
+ "gas_used": "200000000000"
+ },
+ {
+ "cost": "FUNCTION_CALL_BYTE",
+ "cost_category": "ACTION_COST",
+ "gas_used": "82729558"
+ },
+ {
+ "cost": "NEW_ACTION_RECEIPT",
+ "cost_category": "ACTION_COST",
+ "gas_used": "289092464624"
+ },
+ {
+ "cost": "TRANSFER",
+ "cost_category": "ACTION_COST",
+ "gas_used": "115123062500"
+ },
+ {
+ "cost": "BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "8737347663"
+ },
+ {
+ "cost": "CONTRACT_LOADING_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "35445963"
+ },
+ {
+ "cost": "CONTRACT_LOADING_BYTES",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "587834778865"
+ },
+ {
+ "cost": "READ_CACHED_TRIE_NODE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "50160000000"
+ },
+ {
+ "cost": "READ_MEMORY_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "39147948000"
+ },
+ {
+ "cost": "READ_MEMORY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "2227581138"
+ },
+ {
+ "cost": "READ_REGISTER_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "20137321488"
+ },
+ {
+ "cost": "READ_REGISTER_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "84664758"
+ },
+ {
+ "cost": "STORAGE_READ_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "281784228745"
+ },
+ {
+ "cost": "STORAGE_READ_KEY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "1888104513"
+ },
+ {
+ "cost": "STORAGE_READ_VALUE_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "3024331156"
+ },
+ {
+ "cost": "STORAGE_WRITE_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "128393472000"
+ },
+ {
+ "cost": "STORAGE_WRITE_EVICTED_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "9956365170"
+ },
+ {
+ "cost": "STORAGE_WRITE_KEY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "986760138"
+ },
+ {
+ "cost": "STORAGE_WRITE_VALUE_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "12159267288"
+ },
+ {
+ "cost": "TOUCHING_TRIE_NODE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "52440000000"
+ },
+ {
+ "cost": "UTF8_DECODING_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "6223558122"
+ },
+ {
+ "cost": "UTF8_DECODING_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "14579023950"
+ },
+ {
+ "cost": "WASM_INSTRUCTION",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "213370250016"
+ },
+ {
+ "cost": "WRITE_MEMORY_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "25234153749"
+ },
+ {
+ "cost": "WRITE_MEMORY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "2383300500"
+ },
+ {
+ "cost": "WRITE_REGISTER_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "25789702374"
+ },
+ {
+ "cost": "WRITE_REGISTER_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "3486034188"
+ }
+ ],
+ "version": 4
+ },
+ "receipt_ids": [
+ "EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2",
+ "H7Uu9meqBeux4eeNa3DhcWBnEYzVCdTy5Jmu7iT4pugD",
+ "2XDkQGv8NqED7mi1fv2tcZdWcrg5GVgYCsUVwf2Wjaa2"
+ ],
+ "status": {
+ "SuccessValue": ""
+ },
+ "tokens_burnt": "298251754162800000000"
+ },
+ "proof": []
+ },
+ {
+ "block_hash": "5ukLMpiXjTW9y9bojhM2cPRgD25v9fMa7MkKxFat1Se6",
+ "id": "EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2",
+ "outcome": {
+ "executor_id": "commonsops.testnet",
+ "gas_burnt": 223182562500,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "Avdrs3wmsssxb3nDDAJsMyFqc137NaSSpF4JNuXGP4zP"
+ }
+ ],
+ "gas_profile": [],
+ "version": 4
+ },
+ "receipt_ids": ["2ZgfCXg9MWRkkboiKhpv2uqfRoMkt95BJShXZcLHZXup"],
+ "status": {
+ "SuccessValue": ""
+ },
+ "tokens_burnt": "22318256250000000000"
+ },
+ "proof": [
+ {
+ "direction": "Right",
+ "hash": "6HRxRQ49bzHgdmoEmqnkf7S6cG2Uzr6qwssZSjKZZjvU"
+ }
+ ]
+ },
+ {
+ "block_hash": "94AKufcsXWgn6KHKkUbVMFUQoqjXEf5fsJeXn52zHRaH",
+ "id": "2ZgfCXg9MWRkkboiKhpv2uqfRoMkt95BJShXZcLHZXup",
+ "outcome": {
+ "executor_id": "commonsops.testnet",
+ "gas_burnt": 223182562500,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "Avdrs3wmsssxb3nDDAJsMyFqc137NaSSpF4JNuXGP4zP"
+ }
+ ],
+ "gas_profile": [],
+ "version": 4
+ },
+ "receipt_ids": [],
+ "status": {
+ "SuccessValue": ""
+ },
+ "tokens_burnt": "0"
+ },
+ "proof": [
+ {
+ "direction": "Right",
+ "hash": "AJR2EirCaj2ZAx5nKLEk9FUpqNpjCXCxu5v4qMkaCzZH"
+ }
+ ]
+ },
+ {
+ "block_hash": "94AKufcsXWgn6KHKkUbVMFUQoqjXEf5fsJeXn52zHRaH",
+ "id": "H7Uu9meqBeux4eeNa3DhcWBnEYzVCdTy5Jmu7iT4pugD",
+ "outcome": {
+ "executor_id": "commonsops-dao.sputnikv2.testnet",
+ "gas_burnt": 2424297319668,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "AA4nA45vauiac3jdVwJTzSdUP3oj2r1fDaPAb2mHdgcH"
+ }
+ ],
+ "gas_profile": [
+ {
+ "cost": "NEW_ACTION_RECEIPT",
+ "cost_category": "ACTION_COST",
+ "gas_used": "108059500000"
+ },
+ {
+ "cost": "TRANSFER",
+ "cost_category": "ACTION_COST",
+ "gas_used": "115123062500"
+ },
+ {
+ "cost": "BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "8207811441"
+ },
+ {
+ "cost": "CONTRACT_LOADING_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "35445963"
+ },
+ {
+ "cost": "CONTRACT_LOADING_BYTES",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "587834778865"
+ },
+ {
+ "cost": "PROMISE_RETURN",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "560152386"
+ },
+ {
+ "cost": "READ_CACHED_TRIE_NODE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "50160000000"
+ },
+ {
+ "cost": "READ_MEMORY_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "26098632000"
+ },
+ {
+ "cost": "READ_MEMORY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "1744811847"
+ },
+ {
+ "cost": "READ_REGISTER_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "20137321488"
+ },
+ {
+ "cost": "READ_REGISTER_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "99054810"
+ },
+ {
+ "cost": "STORAGE_READ_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "169070537247"
+ },
+ {
+ "cost": "STORAGE_READ_KEY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "464287995"
+ },
+ {
+ "cost": "STORAGE_READ_VALUE_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "3484433484"
+ },
+ {
+ "cost": "STORAGE_WRITE_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "128393472000"
+ },
+ {
+ "cost": "STORAGE_WRITE_EVICTED_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "12589984344"
+ },
+ {
+ "cost": "STORAGE_WRITE_KEY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "986760138"
+ },
+ {
+ "cost": "STORAGE_WRITE_VALUE_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "12159267288"
+ },
+ {
+ "cost": "TOUCHING_TRIE_NODE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "52440000000"
+ },
+ {
+ "cost": "UTF8_DECODING_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "3111779061"
+ },
+ {
+ "cost": "UTF8_DECODING_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "5248448622"
+ },
+ {
+ "cost": "WASM_INSTRUCTION",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "172246436868"
+ },
+ {
+ "cost": "WRITE_MEMORY_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "25234153749"
+ },
+ {
+ "cost": "WRITE_MEMORY_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "2780971212"
+ },
+ {
+ "cost": "WRITE_REGISTER_BASE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "25789702374"
+ },
+ {
+ "cost": "WRITE_REGISTER_BYTE",
+ "cost_category": "WASM_HOST_COST",
+ "gas_used": "4094284428"
+ }
+ ],
+ "version": 4
+ },
+ "receipt_ids": [
+ "3quMbqcvdkTsMsxQQ74wCrWJnAYR3JsPk7gZfJMPTjbE",
+ "ExtpqjudWDD78THHwNMop7qXwbRuUB8uXHTZqf23nfxn"
+ ],
+ "status": {
+ "SuccessReceiptId": "3quMbqcvdkTsMsxQQ74wCrWJnAYR3JsPk7gZfJMPTjbE"
+ },
+ "tokens_burnt": "242429731966800000000"
+ },
+ "proof": [
+ {
+ "direction": "Left",
+ "hash": "8cE47UdmrQ6DJYv44VXiui9bvS1Xh8j6R7sf6dFyGL59"
+ }
+ ]
+ },
+ {
+ "block_hash": "3WAV73gneXZBqCQJEC9qTCHecYUbRxU76g7Mpv3YW2ck",
+ "id": "3quMbqcvdkTsMsxQQ74wCrWJnAYR3JsPk7gZfJMPTjbE",
+ "outcome": {
+ "executor_id": "commonsops.testnet",
+ "gas_burnt": 223182562500,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "Avdrs3wmsssxb3nDDAJsMyFqc137NaSSpF4JNuXGP4zP"
+ }
+ ],
+ "gas_profile": [],
+ "version": 4
+ },
+ "receipt_ids": ["5E3UD6czYoC19Ap9kbun6VriUibqEHQEx1Lh7HLViK6b"],
+ "status": {
+ "SuccessValue": ""
+ },
+ "tokens_burnt": "22318256250000000000"
+ },
+ "proof": [
+ {
+ "direction": "Right",
+ "hash": "2pmD3VuRtHtYSnzuEsPkWVpR5sTSTpGUQzuKyhSdKPxF"
+ }
+ ]
+ },
+ {
+ "block_hash": "8PZrGcBCBj8iZYRtnP18WsuBLerQPRmRXs75BNb9rzyq",
+ "id": "5E3UD6czYoC19Ap9kbun6VriUibqEHQEx1Lh7HLViK6b",
+ "outcome": {
+ "executor_id": "commonsops.testnet",
+ "gas_burnt": 223182562500,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "Avdrs3wmsssxb3nDDAJsMyFqc137NaSSpF4JNuXGP4zP"
+ }
+ ],
+ "gas_profile": [],
+ "version": 4
+ },
+ "receipt_ids": [],
+ "status": {
+ "SuccessValue": ""
+ },
+ "tokens_burnt": "0"
+ },
+ "proof": []
+ },
+ {
+ "block_hash": "3WAV73gneXZBqCQJEC9qTCHecYUbRxU76g7Mpv3YW2ck",
+ "id": "ExtpqjudWDD78THHwNMop7qXwbRuUB8uXHTZqf23nfxn",
+ "outcome": {
+ "executor_id": "commonsops.testnet",
+ "gas_burnt": 223182562500,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "Avdrs3wmsssxb3nDDAJsMyFqc137NaSSpF4JNuXGP4zP"
+ }
+ ],
+ "gas_profile": [],
+ "version": 4
+ },
+ "receipt_ids": [],
+ "status": {
+ "SuccessValue": ""
+ },
+ "tokens_burnt": "0"
+ },
+ "proof": [
+ {
+ "direction": "Left",
+ "hash": "Ax3gRNvS3tz1uwfJGauHmR3kdkxqyaMs7RC9QHegximx"
+ }
+ ]
+ },
+ {
+ "block_hash": "5ukLMpiXjTW9y9bojhM2cPRgD25v9fMa7MkKxFat1Se6",
+ "id": "2XDkQGv8NqED7mi1fv2tcZdWcrg5GVgYCsUVwf2Wjaa2",
+ "outcome": {
+ "executor_id": "commonsops.testnet",
+ "gas_burnt": 223182562500,
+ "logs": [],
+ "metadata": {
+ "contracts": [
+ {
+ "local": "Avdrs3wmsssxb3nDDAJsMyFqc137NaSSpF4JNuXGP4zP"
+ }
+ ],
+ "gas_profile": [],
+ "version": 4
+ },
+ "receipt_ids": [],
+ "status": {
+ "SuccessValue": ""
+ },
+ "tokens_burnt": "0"
+ },
+ "proof": [
+ {
+ "direction": "Left",
+ "hash": "Hu7Fj4TRqFcpA2u8DKfjaNkVRr6ZSabeuictqr6SAVq2"
+ }
+ ]
+ }
+ ],
+ "status": {
+ "SuccessValue": ""
+ },
+ "transaction": {
+ "actions": [
+ {
+ "FunctionCall": {
+ "args": "eyJhY3Rpb24iOiJWb3RlQXBwcm92ZSIsImlkIjowfQ==",
+ "deposit": "0",
+ "gas": 100000000000000,
+ "method_name": "act_proposal"
+ }
+ }
+ ],
+ "hash": "ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7",
+ "nonce": 276384918000011,
+ "priority_fee": 0,
+ "public_key": "ed25519:HDMbog4TVvtNoh4XMZqfiD4R5h4uLumRcwgNvQyuwq39",
+ "receiver_id": "commonsops-dao.sputnikv2.testnet",
+ "signature": "ed25519:4E7NWZQj3jjdFnHfj4XbFULSutUZ7rLszScAbhLwYWmYPM2RfqCD9UxeGhDWGBfRhby9fLRTG3kWcDsdLwJ7T9be",
+ "signer_id": "commonsops.testnet"
+ },
+ "transaction_outcome": {
+ "block_hash": "CYQhVDskbB1D7jrUXtDgqE72g9HqjsNcbhXxR7Z3ifXB",
+ "id": "ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7",
+ "outcome": {
+ "executor_id": "commonsops.testnet",
+ "gas_burnt": 310109899745,
+ "logs": [],
+ "metadata": {
+ "gas_profile": null,
+ "version": 1
+ },
+ "receipt_ids": ["kBH4oqQh8mMy6oGW727XZmQgVobUv1r8r11ExMrsPro"],
+ "status": {
+ "SuccessReceiptId": "kBH4oqQh8mMy6oGW727XZmQgVobUv1r8r11ExMrsPro"
+ },
+ "tokens_burnt": "31010989974500000000"
+ },
+ "proof": []
+ }
+ },
+ "id": "t"
+}
diff --git a/apps/web/lib/production-accessibility.test.mjs b/apps/web/lib/production-accessibility.test.mjs
index 3ec3f6d..f3a0be7 100644
--- a/apps/web/lib/production-accessibility.test.mjs
+++ b/apps/web/lib/production-accessibility.test.mjs
@@ -6,7 +6,8 @@ import test from "node:test";
const webRoot = path.resolve(import.meta.dirname, "..");
const builtPage = path.join(webRoot, ".next", "server", "app", "index.html");
const stylesPath = path.join(webRoot, "app", "globals.css");
-const exactCommitSha = "ab".repeat(20);
+// The recorded immutable evidence commit SHA (docs/testnet-journey.md step 5).
+const exactCommitSha = "1032733c2115b9e9c038d50617f6e151ac8bd59d";
function attributeValues(html, name) {
return [...html.matchAll(new RegExp(`\\b${name}="([^"]+)"`, "g"))].map(
diff --git a/apps/web/src/components/operator-panel.tsx b/apps/web/src/components/operator-panel.tsx
index af2d6d9..5d2a315 100644
--- a/apps/web/src/components/operator-panel.tsx
+++ b/apps/web/src/components/operator-panel.tsx
@@ -56,8 +56,9 @@ export function OperatorPanel({
{eyebrow}
{label}
- This preview uses exact integer wire values. Signing remains disabled
- until a reviewed testnet registry and wallet connector exist.
+ This preview uses exact integer wire values. Signing stays disabled:
+ no wallet connector is integrated; the executed transactions are
+ recorded in docs/testnet-journey.md.
diff --git a/apps/web/src/components/reviewer-panel.tsx b/apps/web/src/components/reviewer-panel.tsx
index d721c64..99d2859 100644
--- a/apps/web/src/components/reviewer-panel.tsx
+++ b/apps/web/src/components/reviewer-panel.tsx
@@ -180,7 +180,11 @@ export function ReviewerPanel({
- Profile
-
{model.profile.version} · {model.profile.codeHash}
- {model.profileSupported ? " · supported" : " · unsupported"}
+ {model.profileSupported
+ ? " · supported"
+ : model.profileVerifiable
+ ? " · payout verification supported · unsigned request builder unavailable"
+ : " · unsupported"}
diff --git a/apps/web/src/features/reviewer/reviewer.test.ts b/apps/web/src/features/reviewer/reviewer.test.ts
index 396ca7f..95ce0b5 100644
--- a/apps/web/src/features/reviewer/reviewer.test.ts
+++ b/apps/web/src/features/reviewer/reviewer.test.ts
@@ -394,7 +394,7 @@ test("requires a supplied SUBMITTED cycle", async () => {
);
});
-test("builds exact zero-deposit link_proposal ABI only for a positive U64 ID", async () => {
+test("builds exact zero-deposit link_proposal ABI for a canonical U64 ID including zero", async () => {
const model = await buildReviewerModel(input());
assert.equal(model.link.status, "PREVIEW_READY");
assert.deepEqual(model.link.intent, {
@@ -415,7 +415,13 @@ test("builds exact zero-deposit link_proposal ABI only for a positive U64 ID", a
assert.equal(absent.link.status, "AWAITING_PROPOSAL_ID");
assert.equal(absent.link.intent, null);
- for (const invalid of ["0", "00", "01", "-1", "1.5", `${U64_MAX}0`]) {
+ // Sputnik numbers proposals from zero and the deployed registry accepted a
+ // proposal-0 link onchain (docs/testnet-journey.md step 8).
+ const zero = await buildReviewerModel(input({ proposalIdForLink: "0" }));
+ assert.equal(zero.link.status, "PREVIEW_READY");
+ assert.equal(zero.link.intent?.args.proposal_id, "0");
+
+ for (const invalid of ["00", "01", "-1", "1.5", `${U64_MAX}0`]) {
await assert.rejects(
buildReviewerModel(input({ proposalIdForLink: invalid })),
/proposalIdForLink/,
diff --git a/apps/web/src/features/reviewer/reviewer.ts b/apps/web/src/features/reviewer/reviewer.ts
index 3aab01e..c143ec3 100644
--- a/apps/web/src/features/reviewer/reviewer.ts
+++ b/apps/web/src/features/reviewer/reviewer.ts
@@ -129,6 +129,7 @@ export interface ReviewerModel {
readonly cycle: Cycle;
readonly profile: PinnedSputnikProfileInput;
readonly profileSupported: boolean;
+ readonly profileVerifiable: boolean;
readonly policy: SputnikPolicySnapshot;
readonly proposal: NativeTransferProposalPreview;
readonly addProposalIntent:
@@ -511,6 +512,26 @@ function profileSupported(profile: PinnedSputnikProfileInput): boolean {
return isSupportedSputnikProfile(profile);
}
+// Profiles the canonical adapter can verify a payout against. Pinned to the
+// same observed graphs as packages/near/src/proposal-builder.ts: 2.0.0 pays
+// without a callback receipt, while the factory-deployed 2.3.1 DAO keeps the
+// payout as a direct child of the act receipt plus a sibling
+// on_proposal_callback receipt that never gates PAID derivation. The executed
+// testnet journey used 2.3.1 (docs/testnet-journey.md). Request construction
+// stays gated by profileSupported above.
+const VERIFIABLE_SPUTNIK_PROFILES: Readonly> =
+ Object.freeze({
+ "2.0.0": Object.freeze(["ZGdM2TFdQpcXrxPxvq25514EViyi9xBSboetDiB3Uiq"]),
+ "2.3.1": Object.freeze(["AA4nA45vauiac3jdVwJTzSdUP3oj2r1fDaPAb2mHdgcH"]),
+ });
+
+function profileVerifiable(profile: PinnedSputnikProfileInput): boolean {
+ return (
+ VERIFIABLE_SPUTNIK_PROFILES[profile.version]?.includes(profile.codeHash) ??
+ false
+ );
+}
+
function freezeCycle(cycle: Cycle): Cycle {
const cloned = cloneFixtureValue(cycle, "cycle");
if (!isObject(cloned)) throw new Error("cycle: plain object required");
@@ -594,11 +615,14 @@ async function submittedCycleSnapshot(cycle: Cycle): Promise {
if (snapshot.proposal_link.key.dao_account_id !== snapshot.dao_account_id) {
throw new Error("cycle.proposal_link: DAO mismatch");
}
+ // The deployed registry accepts any canonical U64 proposal id, including
+ // 0 (Sputnik numbers proposals from zero; the recorded journey linked
+ // proposal 0 onchain — docs/testnet-journey.md step 8).
canonicalDecimal(
snapshot.proposal_link.key.proposal_id,
"cycle.proposal_link.key.proposal_id",
64,
- true,
+ false,
);
}
return snapshot;
@@ -704,7 +728,9 @@ function linkPreview(
proposalIdForLink === null
? null
: typeof proposalIdForLink === "string"
- ? (canonicalDecimal(proposalIdForLink, "proposalIdForLink", 64, true),
+ ? // Canonical U64 including 0: the deployed registry accepted a
+ // proposal-0 link onchain (docs/testnet-journey.md step 8).
+ (canonicalDecimal(proposalIdForLink, "proposalIdForLink", 64, false),
proposalIdForLink)
: (() => {
throw new Error("proposalIdForLink: string or null required");
@@ -717,7 +743,7 @@ function linkPreview(
existingLink: null,
intent: null,
reason:
- "A canonical positive U64 proposal ID is required before a link_proposal preview can be constructed. No browser state is changed.",
+ "A canonical U64 proposal ID is required before a link_proposal preview can be constructed. No browser state is changed.",
});
}
@@ -882,7 +908,7 @@ function derivePaymentFromSnapshot(
);
}
- if (!profileSupported(profile)) {
+ if (!profileVerifiable(profile)) {
return display("UNKNOWN", "Unsupported Sputnik deployment profile");
}
@@ -1077,6 +1103,7 @@ export async function buildReviewerModel(
cycle,
profile,
profileSupported: supported,
+ profileVerifiable: profileVerifiable(profile),
policy: policyInput,
proposal,
addProposalIntent: addProposalIntent(
diff --git a/demo/README.md b/demo/README.md
index 9511c1b..17abe7d 100644
--- a/demo/README.md
+++ b/demo/README.md
@@ -18,37 +18,43 @@ finalized causal receipt graph — never from a client-supplied flag.
## Screenshot map
-Each screenshot shows the fixture-backed web workspace (`pnpm start`, build of
-this repository). Stages 1–3 are separate immutable fixture snapshots of one
-journey, as the app's own banners state; the reviewer stage pairs the registry
-data with the authentic Sputnik proposal-63 RPC fixture
-(`tests/fixtures/sputnik/proposal-63-paid.json`) imported in place.
-
-| File | Journey step | Live testnet evidence in `docs/testnet-journey.md` |
-| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `screenshots/01-hero.png` | Product hero and the unsigned-fixture-preview banner (stages 1–3 are immutable snapshots; no wallet, RPC, or GitHub request) | Journey overview (network `testnet`, registry `commonsops.testnet`, DAO `commonsops-dao.sputnikv2.testnet`) |
-| `screenshots/02-network-boundary.png` | Connection boundary: testnet and mainnet are separate configurations, both unconfigured, reads and signing fail closed | Same network facts; live operations stay in scripts (below) |
-| `screenshots/03-operator.png` | Stage 1 — operator defines the recurring work: unsigned `create_template` and `materialize_cycle` intent previews with exact integer wire values, signing disabled | Journey steps 2–3: template `0` registration ([`FWqT2A6oT8xXRuVkmdxFQbigcLwG9Jao2DfGmie92WH7`](https://explorer.testnet.near.org/transactions/FWqT2A6oT8xXRuVkmdxFQbigcLwG9Jao2DfGmie92WH7)) and cycle `0:0` opened ([`3MV5x4hUjC8Z7uQmcdRJebiyUR4sq67dFJcnUo2nXJCt`](https://explorer.testnet.near.org/transactions/3MV5x4hUjC8Z7uQmcdRJebiyUR4sq67dFJcnUo2nXJCt)) |
-| `screenshots/04-contributor.png` | Stage 2 — contributor claims and binds evidence: `CLAIMED` cycle with claimant = payout recipient (`chloe.testnet` fixture), recipient-binding panel, canonical GitHub evidence preview | Journey steps 4–5: claim binds recipient ([`8LB4W2qWxaKsRRJMyfyw1NYWiDK6bDjh7ejyRKQ42tYe`](https://explorer.testnet.near.org/transactions/8LB4W2qWxaKsRRJMyfyw1NYWiDK6bDjh7ejyRKQ42tYe)) and immutable evidence submission ([`6sqUZaFfoLacYihADUmBSqKbMS7mxvBsQKcUbzfAWz99`](https://explorer.testnet.near.org/transactions/6sqUZaFfoLacYihADUmBSqKbMS7mxvBsQKcUbzfAWz99)) |
-| `screenshots/05-reviewer-approved-only.png` | Stage 3 — reviewer boundary: "Approved alone is not payment" (`PENDING_PAYMENT_EVIDENCE` without a finalized causal receipt graph), trust/authorization boundary | Journey steps 6–7: DAO transfer proposal ([`CAX6EJvvN71BxTP8NDdwmrPiryYeRKcJXK3tPhfp591F`](https://explorer.testnet.near.org/transactions/CAX6EJvvN71BxTP8NDdwmrPiryYeRKcJXK3tPhfp591F)) and council approval ([`ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7`](https://explorer.testnet.near.org/transactions/ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7)) |
-| `screenshots/06-reviewer-paid.png` | Stage 3 — verified cycle: `SUBMITTED` + `LINKED_IMMUTABLE` proposal, derived payment state `PAID` with full provenance (transaction hash, action receipt, payout receipt, evidence key, payout block) | Journey steps 7–9: payout receipt `EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2`, registry link ([`FkT3cB5KQpVHZYoXp7cMb3eWhQPYyvqeeXfbcT33yMX2`](https://explorer.testnet.near.org/transactions/FkT3cB5KQpVHZYoXp7cMb3eWhQPYyvqeeXfbcT33yMX2)), reconciliation `APPLIED` / `PAID` |
-| `screenshots/07-state-gallery.png` | Honest state handling patterns (loading, empty/unconfigured, pending, error, verified fixture) — no live request ran | Design invariant: failed/unknown states never render as paid |
-| `screenshots/08-truth-panel.png` | "Implemented today" facts panel: journey data fixture-only, live registry not deployed in the web app, wallet signing disabled | The live journey lives in `scripts/` and `docs/testnet-journey.md`, not in the browser |
+Each screenshot shows the recorded-journey web workspace (`pnpm start`, build
+of this repository). Stages 1–3 are separate immutable snapshots of the
+recorded live journey, as the app's own banners state; the reviewer stage
+pairs the registry cycle with the recorded proposal-0 payout receipt graph
+(`apps/web/lib/fixtures/proposal-0-paid.json` — the recorded
+`EXPERIMENTAL_tx_status` response for transaction `ApRvPUsw…`) imported in
+place.
+
+| File | Journey step | Live testnet evidence in `docs/testnet-journey.md` |
+| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `screenshots/01-hero.png` | Product hero and the recorded-live-journey banner (stages 1–3 are immutable recorded snapshots; no wallet, RPC, or GitHub request) | Journey overview (network `testnet`, registry `commonsops.testnet`, DAO `commonsops-dao.sputnikv2.testnet`) |
+| `screenshots/02-network-boundary.png` | Connection boundary: testnet registry deployed (`commonsops.testnet`), mainnet isolated and unconfigured; no in-app RPC and no signing by design | Same network facts; live operations stay in scripts (below) |
+| `screenshots/03-operator.png` | Stage 1 — operator defines the recurring work: unsigned `create_template` and `materialize_cycle` intent previews with the exact recorded template-0 wire values, signing disabled | Journey steps 2–3: template `0` registration ([`FWqT2A6oT8xXRuVkmdxFQbigcLwG9Jao2DfGmie92WH7`](https://explorer.testnet.near.org/transactions/FWqT2A6oT8xXRuVkmdxFQbigcLwG9Jao2DfGmie92WH7)) and cycle `0:0` opened ([`3MV5x4hUjC8Z7uQmcdRJebiyUR4sq67dFJcnUo2nXJCt`](https://explorer.testnet.near.org/transactions/3MV5x4hUjC8Z7uQmcdRJebiyUR4sq67dFJcnUo2nXJCt)) |
+| `screenshots/04-contributor.png` | Stage 2 — contributor claims and binds evidence: recorded `CLAIMED` cycle `0:0` with claimant = payout recipient (`commonsops.testnet`), recipient-binding panel, canonical GitHub evidence preview | Journey steps 4–5: claim binds recipient ([`8LB4W2qWxaKsRRJMyfyw1NYWiDK6bDjh7ejyRKQ42tYe`](https://explorer.testnet.near.org/transactions/8LB4W2qWxaKsRRJMyfyw1NYWiDK6bDjh7ejyRKQ42tYe)) and immutable evidence submission ([`6sqUZaFfoLacYihADUmBSqKbMS7mxvBsQKcUbzfAWz99`](https://explorer.testnet.near.org/transactions/6sqUZaFfoLacYihADUmBSqKbMS7mxvBsQKcUbzfAWz99)) |
+| `screenshots/05-reviewer-approved-only.png` | Stage 3 — reviewer boundary: "Approved alone is not payment" (`PENDING_PAYMENT_EVIDENCE` without a finalized causal receipt graph), trust/authorization boundary | Journey steps 6–7: DAO transfer proposal ([`CAX6EJvvN71BxTP8NDdwmrPiryYeRKcJXK3tPhfp591F`](https://explorer.testnet.near.org/transactions/CAX6EJvvN71BxTP8NDdwmrPiryYeRKcJXK3tPhfp591F)) and council approval ([`ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7`](https://explorer.testnet.near.org/transactions/ApRvPUsw2d5gn3N4SxTe7Wbv7nPcJRDTtWc7j9LwAoP7)) |
+| `screenshots/06-reviewer-paid.png` | Stage 3 — verified cycle: real `SUBMITTED` + `LINKED_IMMUTABLE` proposal 0, derived payment state `PAID` with full provenance (transaction hash, action receipt, payout receipt, evidence key, payout block) | Journey steps 7–9: payout receipt `EnL1wpFNdEsrvJtEP3Uh7cn9CsFpYTmwSX3HDyzYquW2`, registry link ([`FkT3cB5KQpVHZYoXp7cMb3eWhQPYyvqeeXfbcT33yMX2`](https://explorer.testnet.near.org/transactions/FkT3cB5KQpVHZYoXp7cMb3eWhQPYyvqeeXfbcT33yMX2)), reconciliation `APPLIED` / `PAID` |
+| `screenshots/07-state-gallery.png` | Honest state handling patterns (loading, empty/unconfigured, pending, error, verified fixture) — no live request ran | Design invariant: failed/unknown states never render as paid |
+| `screenshots/08-truth-panel.png` | "Implemented today" facts panel: journey data = recorded live journey, live registry `commonsops.testnet` — deployed, wallet signing disabled | The live journey lives in `scripts/` and `docs/testnet-journey.md`, not in the browser |
## Provenance statement
-- The web workspace shown in every screenshot is fixture-backed **by design**:
- it performs no RPC, no wallet signature, no GitHub request. The app's own
- honesty banners are visible in the frames and were not cropped away.
+- The web workspace shown in every screenshot renders the recorded live
+ journey **by design**: it performs no RPC, no wallet signature, no GitHub
+ request. Every displayed value is the exact recorded onchain data imported
+ in place. The app's own honesty banners are visible in the frames and were
+ not cropped away.
- The command output recorded in the next section is verbatim from real
read-only commands run while producing this folder (see "Reproduce" below).
It is not a mockup; re-running the commands re-derives the same values
from the live chain.
-- Screenshot `06-reviewer-paid.png` renders the authentic proposal-63 RPC
- fixture (`tests/fixtures/sputnik/proposal-63-paid.json`) through the
+- Screenshot `06-reviewer-paid.png` renders the recorded proposal-0 payout
+ receipt graph (`apps/web/lib/fixtures/proposal-0-paid.json`, the recorded
+ `EXPERIMENTAL_tx_status` response for transaction `ApRvPUsw…`) through the
production `buildReviewerModel` path; its provenance line in the UI states
- this explicitly. The recorded live journey that the same adapter logic
- reconciled to `PAID` is documented in `docs/testnet-journey.md`.
+ this explicitly and names `node scripts/reconcile-live.mjs` as the
+ re-derivation command. The same recorded journey is documented in
+ `docs/testnet-journey.md`.
## Actual command output recorded for the demo
@@ -81,7 +87,7 @@ pnpm install --frozen-lockfile
pnpm verify:offline # format/syntax, TypeScript, unit tests, Rust tests, production build, WASM, local HTTP
node scripts/reconcile-live.mjs # read-only live RPC: APPLIED / PAID
node scripts/smoke-testnet.mjs commonsops.testnet # read-only live RPC: onchain deployment checks
-pnpm start # the fixture-backed web workspace on http://127.0.0.1:3000
+pnpm start # the recorded-journey web workspace on http://127.0.0.1:3000
```
The two scripts sign nothing and spend nothing; each run re-derives its
@@ -104,5 +110,5 @@ results from the live chain. See `docs/testnet-journey.md` and
There is no hosted demo deployment. The public demo artifacts are the
screenshots in this folder — attached to the demo post — and the repository
itself: the README's do-X steps let a judge open and run the working demo
-without credentials — the fixture-backed workspace (`pnpm start`) and the two
-read-only live verification commands above.
+without credentials — the recorded-journey workspace (`pnpm start`) and the
+two read-only live verification commands above.
diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md
index 30bbf41..a7204a4 100644
--- a/docs/COMMANDS.md
+++ b/docs/COMMANDS.md
@@ -63,13 +63,13 @@ pnpm smoke:testnet --
This satisfies the frozen `pnpm smoke:testnet` required check through the accepted narrow `package.json` scope exception. The smoke command is read-only RPC verification, not deployment authorization.
-## View the shell
+## View the workspace
```text
pnpm dev
```
-Open `http://127.0.0.1:3000` — see a static CommonsOps overview labeled **Foundation / not live**. Stop with Ctrl+C.
+Open `http://127.0.0.1:3000` — see the workspace rendering the recorded testnet journey (registry `commonsops.testnet`, template 0, cycle 0:0, real proposal-0 payout). Stop with Ctrl+C.
For a production preview:
@@ -78,7 +78,7 @@ pnpm build
pnpm start
```
-Open the same loopback URL — see the production foundation page, no live records, wallet prompts, or transaction buttons. No secret environment defaults exist. `NEXT_TELEMETRY_DISABLED=1` is supplied by the verification runner/CI, not a credential. No deployment commands are provided.
+Open the same loopback URL — see the same recorded journey in production mode, no wallet prompts, no live RPC, and no transaction buttons. The workspace imports the recorded onchain data in place; live verification stays in the read-only scripts. No secret environment defaults exist. `NEXT_TELEMETRY_DISABLED=1` is supplied by the verification runner/CI, not a credential. No deployment commands are provided.
## CI