diff --git a/README.md b/README.md index 9f408ea..fa4e99a 100644 --- a/README.md +++ b/README.md @@ -106,16 +106,16 @@ See - Public application: `https://commit-protocol.vercel.app` - Deployed application source commit: - `d25e689b0d5c681755751da257700f0d3f7974d1` + `53fb52f2d8d7c5a3636810501b740d859d5a2c14` - Deployed application source tree: - `5553aa58ab006c85000c5a2b106988da2b61f002` + `91c5f458dd77f7d38f0c8107f00eb15a74c3ce37` - Vercel production deployment: - `dpl_Cwh63QydtcGyVD9MeZ7jrSo3A1QE` + `dpl_BvXSZgubPSzs3Z9cyck6eY5dzvDt` - Served coordinator: `0xEE21cCFF8f3755487f774BFd5Da9Ff51D5688581` - Production smoke checks: `/`, `/app`, `/verify`, health, typed index lookup, typed transaction lookup, security headers, and browser verification action: **passed** -- GitHub Actions `Verification` run `35140950751`: **passed** +- GitHub Actions `Verification` run `35142284628`: **passed** The complete production release record is in [`docs/PRODUCTION_RELEASE_2026-09-16.md`](./docs/PRODUCTION_RELEASE_2026-09-16.md). diff --git a/components/application/claim-mission-flow.tsx b/components/application/claim-mission-flow.tsx index b6d2069..426cf14 100644 --- a/components/application/claim-mission-flow.tsx +++ b/components/application/claim-mission-flow.tsx @@ -9,9 +9,7 @@ import { ShieldCheck, WalletCards, } from "lucide-react"; -import { - useState, -} from "react"; +import { useState } from "react"; import { formatGenAmount, trackCommitTransaction, @@ -97,6 +95,13 @@ export function ClaimMissionFlow({ setDeliveryBusy, ] = useState(false); + const activeClaimRecord = + claimRecord !== null + && claimRecord.missionId === missionId + && claimRecord.beneficiary.toLowerCase() === wallet.address.toLowerCase() + ? claimRecord + : null; + function resetReview() { setQuote(null); setProgress(null); @@ -152,6 +157,8 @@ export function ClaimMissionFlow({ txId, }); setClaimRecord({ + missionId: quote.snapshot.mission.missionId, + beneficiary: quote.snapshot.beneficiary, transactionId: txId, amount: quote.snapshot.missionClaimable, }); @@ -209,7 +216,7 @@ export function ClaimMissionFlow({ async function refreshDelivery() { if ( - claimRecord === null + activeClaimRecord === null || deliveryBusy ) { return; @@ -221,17 +228,17 @@ export function ClaimMissionFlow({ setDelivery( await observeExternalDelivery( wallet.client, - claimRecord.transactionId, + activeClaimRecord.transactionId, { recipient: wallet.address, - amount: claimRecord.amount, + amount: activeClaimRecord.amount, }, ), ); } catch (caught: unknown) { setDelivery( unverifiedExternalDelivery( - claimRecord.transactionId, + activeClaimRecord.transactionId, caught instanceof Error ? caught.message : "The delivery observation could not be completed. No delivery outcome is claimed.", @@ -515,7 +522,7 @@ export function ClaimMissionFlow({ ) : null} - {claimRecord !== null ? ( + {activeClaimRecord !== null ? (
@@ -552,7 +559,7 @@ export function ClaimMissionFlow({
Parent claim
-
{claimRecord.transactionId}
+
{activeClaimRecord.transactionId}
Triggered child
diff --git a/docs/AGENT_TANK_SUBMISSION.md b/docs/AGENT_TANK_SUBMISSION.md index 60460d5..694ba07 100644 --- a/docs/AGENT_TANK_SUBMISSION.md +++ b/docs/AGENT_TANK_SUBMISSION.md @@ -24,14 +24,14 @@ uses the SDK's authoritative eligibility, charge, and appeal operations. - Public app: `https://commit-protocol.vercel.app` - GitHub: `https://github.com/Manablaq/commit-protocol` - Production source commit: - `d25e689b0d5c681755751da257700f0d3f7974d1` + `53fb52f2d8d7c5a3636810501b740d859d5a2c14` - Production source tree: - `5553aa58ab006c85000c5a2b106988da2b61f002` + `91c5f458dd77f7d38f0c8107f00eb15a74c3ce37` - Vercel production deployment: - `dpl_Cwh63QydtcGyVD9MeZ7jrSo3A1QE` + `dpl_BvXSZgubPSzs3Z9cyck6eY5dzvDt` - Served coordinator: `0xEE21cCFF8f3755487f774BFd5Da9Ff51D5688581` -- GitHub Actions `Verification` run: `35140950751` — all jobs passed +- GitHub Actions `Verification` run: `35142284628` — all jobs passed Production smoke checks and the exact served-bundle binding are recorded in [`PRODUCTION_RELEASE_2026-09-16.md`](./PRODUCTION_RELEASE_2026-09-16.md). diff --git a/docs/EXTERNAL_DELIVERY_OBSERVATION.md b/docs/EXTERNAL_DELIVERY_OBSERVATION.md index 8064418..dc3204c 100644 --- a/docs/EXTERNAL_DELIVERY_OBSERVATION.md +++ b/docs/EXTERNAL_DELIVERY_OBSERVATION.md @@ -34,8 +34,11 @@ and the finalized execution result is `FINISHED_WITH_ERROR`. | `UNVERIFIED` | No child ID, malformed/ambiguous child set, unreadable child, mismatched recipient/value, unknown result, or RPC failure. | Show that delivery is unresolved; do not infer success or terminal non-delivery. | The UI exposes an explicit `Observe child transaction` action and keeps the -parent claim ID plus exact amount in beneficiary-scoped browser storage. This -allows re-observation after navigation without a page reload or a new write. +parent claim ID, exact mission ID, beneficiary, and amount in beneficiary-scoped +browser storage. This allows re-observation after navigation without a page +reload or a new write. A record is shown only when both the mission and the +connected beneficiary match; switching wallets cannot surface another wallet's +claim observation. Browser storage is only a convenience cache; it is not trusted contract state. ## What this intentionally does not claim diff --git a/docs/PRODUCTION_RELEASE_2026-09-16.md b/docs/PRODUCTION_RELEASE_2026-09-16.md index 6c32371..f622aa2 100644 --- a/docs/PRODUCTION_RELEASE_2026-09-16.md +++ b/docs/PRODUCTION_RELEASE_2026-09-16.md @@ -6,16 +6,17 @@ canonical Vercel alias and the exact Studio Next coordinator it targets. ## Release identity - Repository: `https://github.com/Manablaq/commit-protocol` -- Deployed application source commit: `d25e689b0d5c681755751da257700f0d3f7974d1` -- Deployed application source tree: `5553aa58ab006c85000c5a2b106988da2b61f002` +- Deployed application source commit: `53fb52f2d8d7c5a3636810501b740d859d5a2c14` +- Deployed application source tree: `91c5f458dd77f7d38f0c8107f00eb15a74c3ce37` - Public application: `https://commit-protocol.vercel.app` - Vercel project: `mr-albert-s-projects/commitprotocol-genlayer` -- Vercel production deployment: `dpl_Cwh63QydtcGyVD9MeZ7jrSo3A1QE` +- Vercel production deployment: `dpl_BvXSZgubPSzs3Z9cyck6eY5dzvDt` - Deployment state: `READY` / `PRODUCTION` The source commit and tree above are the exact release built and published by the recorded Vercel deployment. The deployment includes the narrow-viewport -overflow fix certified by the browser E2E gate. +overflow fix and wallet-bound claim-observation fix certified by the browser +E2E gate and regression suite. ## Served contract binding @@ -51,7 +52,7 @@ Read-only checks against `https://commit-protocol.vercel.app` passed: client-side error - deployed JavaScript bundle contains the certified coordinator anchor and the strict `EXTERNAL MESSAGE OBSERVATION` claim surface -- GitHub Actions `Verification` run `35140950751` for the repository release: +- GitHub Actions `Verification` run `35142284628` for the application release: completed successfully across Python, Direct Runtime, and frontend jobs The verification API remains read-only. It does not reconstruct chain truth or diff --git a/lib/genlayer-delivery.ts b/lib/genlayer-delivery.ts index c79469a..752ec06 100644 --- a/lib/genlayer-delivery.ts +++ b/lib/genlayer-delivery.ts @@ -52,6 +52,8 @@ export type ExternalDeliveryExpectation = { }; export type PersistedClaimTransaction = { + missionId: string; + beneficiary: `0x${string}`; transactionId: TransactionHash; amount: bigint; }; @@ -356,7 +358,12 @@ export function readPersistedClaimTransaction( const record = candidate as Record; if ( - typeof record.transactionId !== "string" + typeof record.missionId !== "string" + || typeof record.beneficiary !== "string" + || !/^0x[0-9a-fA-F]{40}$/.test(record.beneficiary) + || record.missionId !== missionId + || record.beneficiary.toLowerCase() !== beneficiary.toLowerCase() + || typeof record.transactionId !== "string" || !isTransactionHash(record.transactionId) || typeof record.amount !== "string" || !/^[1-9][0-9]*$/.test(record.amount) @@ -365,6 +372,8 @@ export function readPersistedClaimTransaction( } return { + missionId: record.missionId, + beneficiary: record.beneficiary as `0x${string}`, transactionId: record.transactionId, amount: BigInt(record.amount), }; @@ -387,6 +396,8 @@ export function persistClaimTransaction( window.localStorage.setItem( claimTransactionStorageKey(missionId, beneficiary), JSON.stringify({ + missionId, + beneficiary: beneficiary.toLowerCase(), transactionId, amount: amount.toString(), }), diff --git a/tests/frontend/genlayer-delivery.test.ts b/tests/frontend/genlayer-delivery.test.ts index 0df2e6f..8e7c8b3 100644 --- a/tests/frontend/genlayer-delivery.test.ts +++ b/tests/frontend/genlayer-delivery.test.ts @@ -211,6 +211,18 @@ describe( expect( readPersistedClaimTransaction(missionId, RECIPIENT)?.amount, ).toBe(AMOUNT); + expect( + readPersistedClaimTransaction( + "different-mission", + RECIPIENT, + ), + ).toBeNull(); + expect( + readPersistedClaimTransaction( + missionId, + OTHER_RECIPIENT, + ), + ).toBeNull(); window.localStorage.setItem( claimTransactionStorageKey(missionId, RECIPIENT), "not-a-hash",