- 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",