Skip to content

docs(x402): record the self-facilitated settlement; split the two claims - #96

Merged
gnanam1990 merged 1 commit into
mainfrom
docs/x402-settled
Aug 8, 2026
Merged

docs(x402): record the self-facilitated settlement; split the two claims#96
gnanam1990 merged 1 commit into
mainfrom
docs/x402-settled

Conversation

@gnanam1990

@gnanam1990 gnanam1990 commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Records the 2026-08-08 self-facilitated x402 settlement (tx 0x0d39b5…dccc, seller 0→40000 atomic) and splits the conflated "no x402 settled BECAUSE Gateway never ran" into two independent statements everywhere it appeared.

Two independent facts, stated on every surface:

  1. A 0.04 USDC x402 payment has settled on Arc, self-facilitated — settlement did not require Gateway.
  2. The Circle Gateway facilitator client is built and has never run against the live service.
  • New: docs/spine-runs/2026-08-08-first-x402-settlement.md — the proof entry (payer/payee are two operator-controlled wallets, stated plainly; the EIP-712 domain bug write-up).
  • Split into two claims: PRODUCT.md, dashboard/app/page.tsx, CP2-SUBMISSION.md, HARD-QUESTIONS.md, CIRCLE-FEEDBACK.md.
  • Leak fixes: SPINE-RUNS.md, PRD.md, H3-sidecar-api.md — 'SETTLED is unreachable' / 'pending a funded Circle Gateway deposit' scoped to the Gateway path (self-facilitator reaches SETTLED, consistent with H3 §8).
  • Reconciled: 2026-08-07 run doc V1 line, spine-runs.tsv comment.

Summary by cubic

Documents the first x402 settlement on Arc (0.04 USDC, self‑facilitated; tx 0x0d39b5…dccc) and clarifies everywhere that settlement did not require Circle Gateway while the Gateway client is built and has never run against the live service.

  • Refactors
    • Added docs/spine-runs/2026-08-08-first-x402-settlement.md with proof details and the EIP‑712 domain bug write‑up.
    • Split the prior conflated claim into two facts across PRODUCT.md, dashboard/app/page.tsx, docs/CP2-SUBMISSION.md, docs/HARD-QUESTIONS.md, and docs/CIRCLE-FEEDBACK.md.
    • Scoped “SETTLED is unreachable”/“pending Gateway deposit” to the Gateway path; noted the self‑facilitator reaches SETTLED; reconciled the 2026‑08‑07 run doc and docs/spine-runs/spine-runs.tsv.

Written for commit 4b29734. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Documentation
    • Recorded the completed 0.04 USDC x402 settlement on Arc testnet through the self-facilitated path.
    • Added transaction details, balances, facilitator metadata, and settlement run history.
    • Clarified that Circle Gateway’s live facilitator flow remains untested and requires credentials.
    • Updated product disclosures and status documentation to distinguish verified settlement from outstanding integration gaps.
  • Bug Fixes
    • Corrected documentation describing settlement status, delivery semantics, and facilitator behavior.

A 0.04 USDC x402 payment settled on Arc on 2026-08-08, self-facilitated
(tx 0x0d39b5…dccc, seller 0→40000 atomic). Separately, the Circle Gateway
facilitator client is built and has never run against the live service.

These are two independent facts — settlement did not require Gateway — and
this pass makes every surface state both:

- New proof entry docs/spine-runs/2026-08-08-first-x402-settlement.md
  (payer and payee are two operator-controlled wallets, stated plainly;
  the domain bug: seller signed EIP-712 name "USD Coin" where Arc USDC is
  "USDC"/"2", latent until a broadcast ran).
- Split "no x402 settled BECAUSE Gateway never ran" into two independent
  statements across PRODUCT.md, dashboard/app/page.tsx, CP2-SUBMISSION.md,
  HARD-QUESTIONS.md, CIRCLE-FEEDBACK.md.
- Remove the "SETTLED is unreachable" / "pending a funded Circle Gateway
  deposit" leaks in SPINE-RUNS.md, PRD.md, H3-sidecar-api.md — scoped each
  to the Gateway path; the self-facilitator reaches SETTLED, consistent
  with H3 §8 (a bearer authorization can be settled by any facilitator).
- Reconcile the 2026-08-07 run doc V1 line and the spine-runs.tsv comment.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR records a completed 0.04 USDC self-facilitated x402 settlement on Arc. It updates the sidecar contract and project documentation while retaining Circle Gateway’s untested status and its live-service prerequisites.

Changes

x402 settlement status

Layer / File(s) Summary
Settlement evidence
docs/spine-runs/..., docs/SPINE-RUNS.md
Documents the successful Arc settlement, transaction evidence, balances, simulation and broadcast flow, and the remaining Circle Gateway validation gap.
Settlement contract semantics
docs/handshakes/H3-sidecar-api.md
Defines self-facilitated SETTLED results with transaction hashes and distinguishes them from Gateway and dry-run NOT_BROADCAST results.
Product and submission status
PRODUCT.md, dashboard/app/page.tsx, docs/CIRCLE-FEEDBACK.md, docs/CP2-SUBMISSION.md, docs/HARD-QUESTIONS.md, docs/PRD.md
Updates product and submission materials with the completed settlement, Gateway prerequisites, untested live Gateway behavior, and remaining compliance and evidence gaps.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • gnamam1990/snapfall#95: Documents the self-facilitated x402 settlement and its remaining Circle Gateway gaps.
  • gnanam1990/snapfall#72: Introduced the facilitator and seller-flow changes described by this status update.
  • gnanam1990/snapfall#74: Updated the same Gateway and x402 documentation before the self-facilitated settlement evidence was available.

Suggested reviewers: vasanthdev2004

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the documented self-facilitated settlement and the separation of the two claims.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/x402-settled

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/CIRCLE-FEEDBACK.md`:
- Line 122: Update the sentence in docs/CIRCLE-FEEDBACK.md around the referenced
wording to hyphenate “end-to-end” and insert a comma before “both,” preserving
the rest of the sentence.

In `@docs/CP2-SUBMISSION.md`:
- Around line 153-164: Update docs/CP2-SUBMISSION.md lines 153-164 to scope the
no-settlement statements to the Circle Gateway rail, preserve the proven
self-facilitated x402 settlement, and replace the obsolete no-spine-run claim
with the current logged-run status; do not claim docs/spine-runs/ is empty.
Apply the same Circle Gateway scoping in docs/HARD-QUESTIONS.md lines 137-142
while retaining the self-facilitated x402 settlement as proven.

In `@docs/handshakes/H3-sidecar-api.md`:
- Around line 221-230: Update the Section 5 introduction near the
facilitator-state discussion to say that Circle Gateway settlement and
reconciliation remain unexercised, while acknowledging that the self-facilitated
SETTLED path has been proven. Remove the broader claim that all facilitator
states are unexercised or that the sidecar behaves exactly like the dry run.

In `@docs/PRD.md`:
- Line 59: Update the document-status metadata in §0.1 and the document-control
row to reflect August 8, 2026, matching the latest implementation event
described in the Spine runs entry. Keep the newly added August 8 facts in the
existing status section.

In `@docs/spine-runs/2026-08-08-first-x402-settlement.md`:
- Around line 27-34: Qualify the “same payload” simulation claim in “The method:
simulate, then broadcast the same payload” by documenting the eth_call caller,
gas limit, block tag/state context, and timestamp, then compare the mined
transaction’s complete calldata with the simulated calldata. Limit the
conclusion to the observed execution context, and remove the x402-simulate.ts,
x402-broadcast.ts, and sidecar/src/self-facilitator.ts reproducibility
references unless those artifacts are added.
- Around line 41-47: Update the Arc EIP-712 domain configuration in the H3
example and sidecar/seller.ts local verifier to use name "USDC" and version "2",
matching Arc Testnet USDC. Keep buyer.ts signing accept.extra unchanged so the
advertised domain and on-chain settlement use the corrected values.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5fbe7654-2cd9-4c5d-9469-5742da7e0d7a

📥 Commits

Reviewing files that changed from the base of the PR and between 41fe80b and 4b29734.

⛔ Files ignored due to path filters (1)
  • docs/spine-runs/spine-runs.tsv is excluded by !**/*.tsv
📒 Files selected for processing (10)
  • PRODUCT.md
  • dashboard/app/page.tsx
  • docs/CIRCLE-FEEDBACK.md
  • docs/CP2-SUBMISSION.md
  • docs/HARD-QUESTIONS.md
  • docs/PRD.md
  • docs/SPINE-RUNS.md
  • docs/handshakes/H3-sidecar-api.md
  • docs/spine-runs/2026-08-07-first-automated-settlement.md
  • docs/spine-runs/2026-08-08-first-x402-settlement.md

Comment thread docs/CIRCLE-FEEDBACK.md
`settlement: NOT_BROADCAST` and **the Gateway path has never settled a payment**. We reached
SETTLED a different way — **self-facilitating the EIP-3009 authorization** (broadcasting
`transferWithAuthorization` directly, no Gateway): a real 0.04 USDC x402 payment settled on Arc
(`docs/spine-runs/2026-08-08-first-x402-settlement.md`). So the loop is now end to end both

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Hyphenate “end-to-end” at Line [122].

Use the compound-adjective form and add a comma before “both.”

Proposed wording
-... the loop is now end to end both cryptographically and financially ...
+... the loop is now end-to-end, both cryptographically and financially ...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(`docs/spine-runs/2026-08-08-first-x402-settlement.md`). So the loop is now end to end both
(`docs/spine-runs/2026-08-08-first-x402-settlement.md`). So the loop is now end-to-end, both
🧰 Tools
🪛 LanguageTool

[grammar] ~122-~122: Use a hyphen to join words.
Context: ...-settlement.md`). So the loop is now end to end both cryptographically and financial...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/CIRCLE-FEEDBACK.md` at line 122, Update the sentence in
docs/CIRCLE-FEEDBACK.md around the referenced wording to hyphenate “end-to-end”
and insert a comma before “both,” preserving the rest of the sentence.

Source: Linters/SAST tools

Comment thread docs/CP2-SUBMISSION.md
Comment on lines +153 to +164
- **A 0.04 USDC x402 purchase has settled, self-facilitated. The Circle Gateway path is built and
has never run.** Two independent facts. The full 402 → sign → retry → 200 loop is real and runs
end to end, with the EIP-3009 authorization signed by a real key and verified by the seller —
and that authorization was then **broadcast directly** (`transferWithAuthorization`), settling
0.04 USDC operator → seller on Arc ([`0x0d39b5…dccc`](https://testnet.arcscan.app/tx/0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc);
`docs/spine-runs/2026-08-08-first-x402-settlement.md`). Settlement did not require Gateway.
Separately, the Circle Gateway facilitator client (written 2 Aug, `sidecar/src/facilitator.ts`)
has never run against the live service — that path needs a Circle account, key, and Gateway
allowlisting we do not have. A self-facilitated settlement is marked as such (`self:` facilitator
markers), and the fixture capture (AT-18) refuses it as *Circle* evidence. So the loop is now
end to end both cryptographically and financially; what stays unexercised is the Gateway path
specifically, not settlement itself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope stale settlement statements to Circle Gateway.

Both documents now prove a self-facilitated x402 payment, but adjacent text still uses the old generic claim that no agent-payment settlement occurred. docs/CP2-SUBMISSION.md also retains an obsolete claim that no spine run was logged.

  • docs/CP2-SUBMISSION.md#L153-L164: Scope Lines [144-149] to the Circle Gateway rail, and replace Lines [165-167] with the current logged-run status. Do not state that docs/spine-runs/ is empty.
  • docs/HARD-QUESTIONS.md#L137-L142: Scope Lines [144-149] to the Circle Gateway rail while keeping the self-facilitated x402 settlement as proven.
📍 Affects 2 files
  • docs/CP2-SUBMISSION.md#L153-L164 (this comment)
  • docs/HARD-QUESTIONS.md#L137-L142
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/CP2-SUBMISSION.md` around lines 153 - 164, Update docs/CP2-SUBMISSION.md
lines 153-164 to scope the no-settlement statements to the Circle Gateway rail,
preserve the proven self-facilitated x402 settlement, and replace the obsolete
no-spine-run claim with the current logged-run status; do not claim
docs/spine-runs/ is empty. Apply the same Circle Gateway scoping in
docs/HARD-QUESTIONS.md lines 137-142 while retaining the self-facilitated x402
settlement as proven.

Comment on lines +221 to +230
`status` is safe to poll. **Reality of the Gateway facilitator path:** the Gateway broadcast is
wired but dormant without `CIRCLE_API_KEY`. With no key, `DELIVERED` is the committed-final success
state (§5, §7) and consumers MUST NOT treat `SETTLED` as reachable *through Gateway*. With a key,
`SETTLED` becomes reachable — but the reconciliation transitions (`RECONCILING → SETTLED|FAILED`)
still need a settlement-status read the client does not have, so an unconfirmed settlement is
reconciled by an operator rather than automatically. **Update (2026-08-08):** a payment **has**
settled — through the *self-facilitator* (`SNAPFALL_SELF_FACILITATOR=1`), a different, permissionless
path that broadcasts the EIP-3009 authorization directly and reaches `SETTLED` with a real tx hash
(`docs/spine-runs/2026-08-08-first-x402-settlement.md`). The Gateway-path statements above are
unchanged: that facilitator specifically has still never run.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Narrow the state-machine caveat to Circle Gateway.

This update records a self-facilitated SETTLED payment. However, the introduction to Section 5 at Lines 359-362 still says that all facilitator states are “unexercised in fact” and that the sidecar behaves exactly as the dry run.

Update Lines 359-362 to state that Circle Gateway settlement and reconciliation remain unexercised. The self-facilitated SETTLED path is now proven.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/handshakes/H3-sidecar-api.md` around lines 221 - 230, Update the Section
5 introduction near the facilitator-state discussion to say that Circle Gateway
settlement and reconciliation remain unexercised, while acknowledging that the
self-facilitated SETTLED path has been proven. Remove the broader claim that all
facilitator states are unexercised or that the sidecar behaves exactly like the
dry run.

Comment thread docs/PRD.md
| V7 / V10 / V11 "renders live" | One green spine run with a human watching. |
| V12 reset → spine → reset → spine | Two consecutive green runs. |
| Spine runs (schedule, §16) | **First automated settlement logged 2026-08-07, verdict UNVERIFIED** — the waterfall settled on chain and only the x402 beat is unproven (settlement rests at `NOT_BROADCAST`, pending a funded Circle Gateway deposit). **Three prior FAIL runs the same day** are recorded in `docs/spine-runs/spine-runs.tsv` and kept deliberately: the live runs surfaced what CI could not — the missing `startWork`/`submitDelivery` caller, and an operator-owned pool. **No daily cadence exists**: the Wed 29 Jul gate was missed; runs are on-demand, not scheduled. |
| Spine runs (schedule, §16) | **First automated settlement logged 2026-08-07, verdict UNVERIFIED** — the waterfall settled on chain; the x402 beat rested at `NOT_BROADCAST` in that run (Gateway facilitator, no key). **The x402 beat has since settled separately**: a real 0.04 USDC x402 payment settled on Arc 2026-08-08, **self-facilitated** — broadcasting the EIP-3009 authorization directly, not through Circle Gateway, which is built and has never run against the live service (`docs/spine-runs/2026-08-08-first-x402-settlement.md`). **Three prior FAIL runs the same day** are recorded in `docs/spine-runs/spine-runs.tsv` and kept deliberately: the live runs surfaced what CI could not — the missing `startWork`/`submitDelivery` caller, and an operator-owned pool. **No daily cadence exists**: the Wed 29 Jul gate was missed; runs are on-demand, not scheduled. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Refresh the implementation-status date.

Line [59] now includes events from August 8, 2026, but §0.1 and the document-control row still say that the status was updated on August 2, 2026. Update the section metadata to August 8, 2026, or move the new facts into a dated addendum.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/PRD.md` at line 59, Update the document-status metadata in §0.1 and the
document-control row to reflect August 8, 2026, matching the latest
implementation event described in the Spine runs entry. Keep the newly added
August 8 facts in the existing status section.

Comment on lines +27 to +34
## The method: simulate, then broadcast the same payload

The operator signed an `operator → seller` EIP-3009 authorization, then ran
`transferWithAuthorization` as an `eth_call` (a read — no transaction). Only after that simulation
came back clean was the **same signed payload** broadcast through the real self-facilitator
(`sidecar/src/self-facilitator.ts`). Simulating the exact payload first means a clean simulation
and a reverting send can only differ if something changed on chain between the two — and it did
not. Reproducible via `x402-simulate.ts` / `x402-broadcast.ts`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 'eth_call|blockTag|from:|gas|validAfter|validBefore|transferWithAuthorization' \
  --glob '*.ts' .

Repository: gnanam1990/snapfall

Length of output: 37875


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- target document ---'
cat -n docs/spine-runs/2026-08-08-first-x402-settlement.md | sed -n '20,60p'

printf '%s\n' '--- candidate simulation and broadcast files ---'
git ls-files | rg '(^|/)(x402-(simulate|broadcast)|.*x402.*)\.(ts|js|mjs|md)$|spine-runs/2026-08-08-first-x402-settlement\.md$'

printf '%s\n' '--- references to the scripts and settlement flow ---'
rg -n -C 5 'x402-simulate|x402-broadcast|self-facilitator|eth_call|sendTransaction|transferWithAuthorization|validAfter|validBefore|blockTag|gas' \
  docs sidecar scripts .github --glob '*.{ts,js,mjs,md,json,yml,yaml}' 2>/dev/null || true

Repository: gnanam1990/snapfall

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- relevant tracked files ---'
git ls-files | rg -i 'self[-_]?facilitator|facilitator|simulate|broadcast|x402'

printf '%s\n' '--- self-facilitator references ---'
rg -n -C 8 'self-facilitator|SNAPFALL_SELF_FACILITATOR|simulate|broadcast' \
  sidecar docs --glob '*.{ts,js,mjs,md,json,yml,yaml}' | head -n 500

printf '%s\n' '--- facilitator implementation outline ---'
if [ -f sidecar/src/self-facilitator.ts ]; then
  ast-grep outline sidecar/src/self-facilitator.ts
  wc -l sidecar/src/self-facilitator.ts
  cat -n sidecar/src/self-facilitator.ts
else
  printf '%s\n' 'sidecar/src/self-facilitator.ts is not present'
fi

printf '%s\n' '--- package scripts and operation entry points ---'
find . -maxdepth 3 -type f \( -name package.json -o -name '*.ts' -o -name '*.js' -o -name '*.mjs' \) -print0 |
  xargs -0 rg -n -C 4 'x402-simulate|x402-broadcast|transferWithAuthorization|eth_call|simulateContract|sendTransaction|writeContract'

Repository: gnanam1990/snapfall

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

TX='0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc'
RPC='https://rpc.testnet.arc.network'

python3 - <<'PY'
import json
import urllib.request

rpc = "https://rpc.testnet.arc.network"
tx_hash = "0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc"

def call(method, params):
    body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
    req = urllib.request.Request(rpc, data=body, headers={"content-type": "application/json"})
    with urllib.request.urlopen(req, timeout=15) as response:
        result = json.load(response)
    print(f"\n{method}")
    print(json.dumps(result, indent=2))
    return result.get("result")

tx = call("eth_getTransactionByHash", [tx_hash])
receipt = call("eth_getTransactionReceipt", [tx_hash])
if tx and tx.get("blockNumber"):
    call("eth_getBlockByNumber", [tx["blockNumber"], False])
PY

printf '%s\n' '--- repository evidence for the claimed reproducibility artifacts ---'
git status --short
git ls-files sidecar scripts | sort | rg -i 'self|facilit|x402|simulate|broadcast' || true
find sidecar scripts -maxdepth 3 -type f \( -iname '*self*' -o -iname '*facilit*' -o -iname '*x402*' -o -iname '*simulat*' -o -iname '*broadcast*' \) -print 2>/dev/null | sort || true

Repository: gnanam1990/snapfall

Length of output: 2479


🏁 Script executed:

#!/bin/bash
set -euo pipefail

TX='0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc'
RPC='https://rpc.testnet.arc.network'

printf '%s\n' '--- transaction and receipt ---'
for method in eth_getTransactionByHash eth_getTransactionReceipt; do
  curl -ksS --fail-with-body "$RPC" \
    -H 'content-type: application/json' \
    --data "$(printf '{"jsonrpc":"2.0","id":1,"method":"%s","params":["%s"]}' "$method" "$TX")" |
    jq .
done

printf '%s\n' '--- repository artifacts and status ---'
git status --short
git ls-files sidecar scripts | sort | rg -i 'self|facilit|x402|simulate|broadcast' || true
find sidecar scripts -maxdepth 3 -type f \
  \( -iname '*self*' -o -iname '*facilit*' -o -iname '*x402*' -o -iname '*simulat*' -o -iname '*broadcast*' \) \
  -print 2>/dev/null | sort || true

Repository: gnanam1990/snapfall

Length of output: 5614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

TX='0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc'
RPC='https://rpc.testnet.arc.network'

python3 - <<'PY'
import json
import subprocess

tx = "0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc"
rpc = "https://rpc.testnet.arc.network"

def rpc_call(method, params):
    raw = subprocess.check_output([
        "curl", "-ksS", "--fail-with-body", rpc,
        "-H", "content-type: application/json",
        "--data", json.dumps({
            "jsonrpc": "2.0", "id": 1, "method": method, "params": params
        }),
    ])
    result = json.loads(raw)["result"]
    return result

transaction = rpc_call("eth_getTransactionByHash", [tx])
receipt = rpc_call("eth_getTransactionReceipt", [tx])

input_data = transaction["input"]
assert input_data.startswith("0xe3ee160e"), input_data[:10]
words = [input_data[10 + i * 64:10 + (i + 1) * 64] for i in range(9)]

decoded = {
    "from_argument": "0x" + words[0][-40:],
    "to_argument": "0x" + words[1][-40:],
    "value": int(words[2], 16),
    "valid_after": int(words[3], 16),
    "valid_before": int(words[4], 16),
    "nonce": "0x" + words[5],
    "v": int(words[6], 16),
    "r": "0x" + words[7],
    "s": "0x" + words[8],
}

block = rpc_call("eth_getBlockByNumber", [transaction["blockNumber"], False])
block_timestamp = int(block["timestamp"], 16)

print(json.dumps({
    "transaction_from": transaction["from"],
    "calldata_from": decoded["from_argument"],
    "calldata_to": decoded["to_argument"],
    "value": decoded["value"],
    "valid_after": decoded["valid_after"],
    "valid_before": decoded["valid_before"],
    "block_timestamp": block_timestamp,
    "status": receipt["status"],
    "gas_limit": int(transaction["gas"], 16),
    "gas_used": int(receipt["gasUsed"], 16),
    "calldata_matches_sender": transaction["from"].lower() == decoded["from_argument"].lower(),
    "valid_at_mined_block": decoded["valid_after"] <= block_timestamp < decoded["valid_before"],
}, indent=2))
PY

Repository: gnanam1990/snapfall

Length of output: 588


Qualify the eth_call equivalence claim.

A clean eth_call does not guarantee the mined transaction result. The caller, gas limit, block tag, state snapshot, and block timestamp can differ. EIP-3009 validity windows are time-sensitive. Record the simulation parameters and compare the full mined calldata with the simulated calldata. Limit the claim to the observed execution context. The referenced x402-simulate.ts, x402-broadcast.ts, and sidecar/src/self-facilitator.ts files are not present, so add them or remove the reproducibility statement.

🧰 Tools
🪛 LanguageTool

[grammar] ~33-~33: Use a hyphen to join words.
Context: ... can only differ if something changed on chain between the two — and it did not. ...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/spine-runs/2026-08-08-first-x402-settlement.md` around lines 27 - 34,
Qualify the “same payload” simulation claim in “The method: simulate, then
broadcast the same payload” by documenting the eth_call caller, gas limit, block
tag/state context, and timestamp, then compare the mined transaction’s complete
calldata with the simulated calldata. Limit the conclusion to the observed
execution context, and remove the x402-simulate.ts, x402-broadcast.ts, and
sidecar/src/self-facilitator.ts reproducibility references unless those
artifacts are added.

Comment on lines +41 to +47
We signed `transferWithAuthorization` with EIP-712 domain name **`"USD Coin"`** — Ethereum
*mainnet* USDC's name. Arc's precompile uses **`"USDC"`**. The mismatch is invisible during the
handshake: the buyer signs with the seller's advertised name and the seller verifies locally with
the same name, so 402 → sign → verify → 200 passes. But `transferWithAuthorization` recovers the
signer against the precompile's *own* domain at settle — so a `"USD Coin"` signature recovers to a
different address, and every broadcast we could have made would have reverted. On Circle Gateway
or a self-facilitator alike.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 5 '"name": "(USD Coin|USDC)"|signAuthorization|transferWithAuthorization' \
  docs sidecar

Repository: gnanam1990/snapfall

Length of output: 25381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- candidate files ---'
git ls-files \
  'docs/handshakes/H3-sidecar-api.md' \
  'sidecar/src/buyer.ts' \
  'sidecar/src/self-facilitator.ts' \
  'deployments/arc-testnet.json' \
  'docs/spine-runs/2026-08-08-first-x402-settlement.md'

echo '--- buyer outline ---'
ast-grep outline sidecar/src/buyer.ts

echo '--- relevant buyer implementation ---'
sed -n '264,325p' sidecar/src/buyer.ts

echo '--- relevant H3 sections ---'
sed -n '70,100p' docs/handshakes/H3-sidecar-api.md
sed -n '400,430p' docs/handshakes/H3-sidecar-api.md

echo '--- self-facilitator symbols and implementation ---'
rg -n -C 8 'domain|name|version|chainId|transferWithAuthorization|signAuthorization|verifyTypedData' \
  sidecar/src/self-facilitator.ts sidecar/src/facilitator.ts sidecar/src/seller.ts sidecar/src/x402.ts

echo '--- Arc configuration ---'
cat deployments/arc-testnet.json

Repository: gnanam1990/snapfall

Length of output: 23997


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- deployment metadata ---'
cat deployments/arc-testnet.json

echo '--- payment and broadcast files ---'
git ls-files | rg '(^|/)(.*(broadcast|simulate|settle|facilitator).*)\.(ts|js|json|md)$|self-facilitator'
echo '--- relevant references ---'
rg -n -C 6 'SNAPFALL_SELF_FACILITATOR|transferWithAuthorization|signTypedData|verifyTypedData|USDC_ADDRESS|ARC_CHAIN_ID|5042002|USD Coin|USDC' \
  sidecar scripts docs deployments

echo '--- settlement implementation candidates ---'
rg -l 'transferWithAuthorization|SNAPFALL_SELF_FACILITATOR' . --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**'

Repository: gnanam1990/snapfall

Length of output: 50376


🌐 Web query:

Arc testnet USDC precompile EIP-712 domain name transferWithAuthorization "USDC" "USD Coin"

💡 Result:

On the Arc Testnet (Chain ID 5042002), USDC is used as both the native gas token and an ERC-20 token [1][2][3]. The ERC-20 implementation resides at the address 0x3600000000000000000000000000000000000000 [1][3][4]. The EIP-712 domain name for USDC is standard across networks, including Arc Testnet [2][5]. To interact with the transferWithAuthorization function (EIP-3009), developers must construct an EIP-712 domain object using the following parameters [6][7][5]: Name: "USD Coin" [8][6][5] Version: "2" [8][6][9][5] Chain ID: 5042002 [1][2][4] Verifying Contract: 0x3600000000000000000000000000000000000000 [1][3][4] The transferWithAuthorization function allows for gas-abstracted transfers [2][10][7]. When signing this typed data, the structure must include the parameters: from, to, value, validAfter, validBefore, and nonce [2][6][5]. Because USDC on Arc exists in two distinct forms—native gas (18 decimals) and ERC-20 (6 decimals)—it is critical to use the 6-decimal precision when interacting with the ERC-20 contract's transferWithAuthorization function [3][4]. Failure to align the domain separator parameters exactly (including the string "USD Coin" and version "2") will result in signature verification failures (e.g., FiatTokenV2: invalid signature) [6][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- deployment summary ---'
python3 - <<'PY'
import json
from pathlib import Path
p = Path('deployments/arc-testnet.json')
data = json.loads(p.read_text())
def walk(x, prefix=''):
    if isinstance(x, dict):
        for k, v in x.items():
            key = f'{prefix}.{k}' if prefix else k
            if isinstance(v, (dict, list)):
                walk(v, key)
            else:
                print(f'{key}={v}')
    elif isinstance(x, list):
        print(f'{prefix}=[{len(x)} items]')
walk(data)
PY

echo '--- settlement source files ---'
git ls-files | rg '(^|/)([^/]*(broadcast|simulate|settle|facilitator)[^/]*)\.(ts|js|json)$|self-facilitator'
echo '--- exact settlement references in source ---'
rg -n -C 10 'transferWithAuthorization|SNAPFALL_SELF_FACILITATOR' \
  sidecar scripts --glob '*.ts' --glob '*.js' 2>/dev/null || true

echo '--- RPC and Arc endpoint references ---'
rg -n -C 3 'arc.*(rpc|RPC)|RPC.*arc|testnet\.arc|5042002|3600000000000000000000000000000000000000' \
  deployments sidecar scripts package.json README.md docs --glob '*.json' --glob '*.ts' --glob '*.js' --glob '*.md' \
  | head -n 300

Repository: gnanam1990/snapfall

Length of output: 36041


🌐 Web query:

site:docs.arc.network USDC "USD Coin" EIP-712 transferWithAuthorization Arc Testnet

💡 Result:

The USDC contract on the Arc network maintains compatibility with standard Ethereum Virtual Machine (EVM) interfaces, providing familiar ERC-20 functions such as transferFrom, approve, and allowance management [1]. While the standard USDC contract on many chains supports EIP-712-based authorization via functions like transferWithAuthorization, interaction on the Arc network is primarily facilitated through the Circle App Kit SDK for standard token operations, including sending, swapping, and bridging [2][3][4][5]. On the Arc network, native USDC transfers are managed via EIP-7708, which emits standard ERC-20 Transfer logs for all native balance movements [1]. For developers integrating USDC on Arc Testnet, standard contract execution patterns—such as calling approve for escrow or interacting with other smart contracts—are supported using standard EVM transaction structures [6]. If you are implementing gasless or signature-based transfers, verify the specific USDC contract implementation on the Arc network, as Arc also leverages opt-in privacy features (Arc Privacy Sector) that may impact how standard EVM bytecode and interfaces interact with confidential states [7]. Refer to the official Arc network documentation for the most current contract addresses and ABI specifications [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

RPC='https://rpc.testnet.arc.network'
TOKEN='0x3600000000000000000000000000000000000000'
TX='0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc'

python3 - <<'PY'
import json, subprocess

rpc = 'https://rpc.testnet.arc.network'
token = '0x3600000000000000000000000000000000000000'
tx = '0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc'

calls = [
    ('chainId', '0x1', '0x'),
    ('name', '0x2', '0x06fdde03'),
    ('version', '0x3', '0x54fd4d50'),
    ('symbol', '0x4', '0x95d89b41'),
    ('DOMAIN_SEPARATOR', '0x5', '0x3644e515'),
]

for label, ident, data in calls:
    body = json.dumps({
        'jsonrpc': '2.0',
        'id': int(ident, 16),
        'method': 'eth_call' if label != 'chainId' else 'eth_chainId',
        'params': ([{'to': token, 'data': data}, 'latest'] if label != 'chainId' else []),
    })
    out = subprocess.check_output([
        'curl', '-sS', '--fail-with-body',
        '-H', 'content-type: application/json',
        '--data', body, rpc,
    ], text=True)
    result = json.loads(out)
    print(label, json.dumps(result, separators=(',', ':')))
    if label in {'name', 'version', 'symbol'} and 'result' in result:
        raw = bytes.fromhex(result['result'][2:])
        if len(raw) >= 64:
            n = int.from_bytes(raw[32:64], 'big')
            text = raw[64:64+n].decode('utf-8', errors='replace')
            print(f'{label}_decoded={text!r}')

body = json.dumps({
    'jsonrpc': '2.0',
    'id': 6,
    'method': 'eth_getTransactionReceipt',
    'params': [tx],
})
out = subprocess.check_output([
    'curl', '-sS', '--fail-with-body',
    '-H', 'content-type: application/json',
    '--data', body, rpc,
], text=True)
print('receipt', out)
PY

Repository: gnanam1990/snapfall

Length of output: 3978


Use "USDC" for Arc EIP-712 domains.

Arc Testnet USDC reports name() == "USDC" and version() == "2". The H3 example and sidecar/src/seller.ts use "USD Coin". Update the Arc challenge and local verifier; buyer.ts signs accept.extra verbatim, so local verification can pass while on-chain settlement rejects the signature.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/spine-runs/2026-08-08-first-x402-settlement.md` around lines 41 - 47,
Update the Arc EIP-712 domain configuration in the H3 example and
sidecar/seller.ts local verifier to use name "USDC" and version "2", matching
Arc Testnet USDC. Keep buyer.ts signing accept.extra unchanged so the advertised
domain and on-chain settlement use the corrected values.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 issues found across 11 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/PRD.md">

<violation number="1" location="docs/PRD.md:59">
P3: The added §0.1 row records the 2026-08-08 settlement inside a section still stamped "built vs. proven (2 Aug 2026)" / "updated 2 Aug 2026" (section header, §0 status field, and TOC). Since this section is the judge-facing implementation-status record and the PR is precisely about recording the 8 Aug event, update those date stamps when landing so the section header and its content don't contradict each other.</violation>
</file>

<file name="docs/CP2-SUBMISSION.md">

<violation number="1" location="docs/CP2-SUBMISSION.md:158">
P3: This PR adds proof entries to docs/spine-runs/ and references the new file in the changed text, but the gap statement right below still claims "docs/spine-runs/ is empty" — that is now factually false and contradicts the README 'must not misrepresent' constraint. Update the line (e.g. drop 'is empty' and acknowledge the settlement entry isn't a full end-to-end spine run) so the submission doesn't carry a stale contradiction.</violation>
</file>

<file name="docs/spine-runs/2026-08-08-first-x402-settlement.md">

<violation number="1" location="docs/spine-runs/2026-08-08-first-x402-settlement.md:32">
P3: The doc cites files that don't exist in the repo, making its reproducibility and code-path claims unverifiable. `sidecar/src/self-facilitator.ts`, `x402-simulate.ts`, `x402-broadcast.ts`, and `sidecar/src/usdc-domain-chain-test.ts` are absent from the tree (the full `sidecar/src/` listing and repo-wide find/rg return nothing for them), and this docs-only PR adds none of them. Point readers at the real implementation (e.g. the broadcast path in `sidecar/src/seller.ts`/`facilitator.ts`) or drop the references.</violation>
</file>

<file name="docs/spine-runs/spine-runs.tsv">

<violation number="1" location="docs/spine-runs/spine-runs.tsv:7">
P2: The comment says the x402 gap that left the 06:30/07:12 rows at NOT_BROADCAST 'is closed', but those rows failed precisely because the automated seller.ts:217 facilitator broadcast was never wired, and this self-facilitated settlement was performed manually via x402-broadcast.ts — an independent code path that does not change seller.ts. A fresh spine run would still hit NOT_BROADCAST on 3-SPEND, so the phrase is easy to misread as the automated beat now passing; suggest framing it as 'a first x402 settlement now exists (self-facilitated, manual), the automated facilitator broadcast that left these runs at NOT_BROADCAST is not wired yet' to avoid defeating the claim-splitting this PR intends.</violation>
</file>

<file name="docs/CIRCLE-FEEDBACK.md">

<violation number="1" location="docs/CIRCLE-FEEDBACK.md:121">
P2: This line (and the linked spine-run 'proof entry') claims a self-facilitated x402 settlement, but the self-facilitator source, the domain-chain test, and the 'Reproducible via x402-simulate.ts / x402-broadcast.ts' scripts do not exist anywhere in the repo (clean working tree, no such symbols in sidecar/). A reader can't regenerate or verify the claimed settlement from a clean checkout, which contradicts the repo's 'must not fabricate / reproducible' ground rule for claims. Either land the self-facilitator code and scripts, or qualify the claim (e.g. one-off, uncommitted) instead of presenting it as reproducible.</violation>
</file>

<file name="docs/SPINE-RUNS.md">

<violation number="1" location="docs/SPINE-RUNS.md:13">
P3: Calling the settlement half of V1 "proven" overstates the clause, which requires "a real four-cent purchase completes on testnet." The 08-08 event was a standalone manual operator->operator transfer, not a purchase that ran through the live flow to SETTLED, so that half of the clause arguably remains open; suggest wording it as the settlement mechanism being demonstrated rather than the clause half closed.</violation>
</file>

<file name="docs/handshakes/H3-sidecar-api.md">

<violation number="1" location="docs/handshakes/H3-sidecar-api.md:227">
P2: The added lines present `SNAPFALL_SELF_FACILITATOR=1` as an existing configuration knob, but no such env var exists in the codebase (not in sidecar/.env.example, not read anywhere in sidecar/src), and no self-facilitator source exists. A reader following this normative spec would wire up a switch that does nothing; either add the env var plus the mechanism, or reword the doc to describe the operator-run script without naming a non-existent config flag.</violation>

<violation number="2" location="docs/handshakes/H3-sidecar-api.md:477">
P3: The new SETTLED-has-happened claim (§2.3, §8) directly contradicts the near-by §5 header that SETTLED is "unexercised in fact" and the SETTLED meaning that urges treating "the first live purchase as the test of it". The PR splits the Gateway claims but leaves §5 worded for a world where SETTLED has never fired; update the §5 header and SETTLED meaning to say the Gateway-confirmed reconciliation is the unexercised part, so the spec is internally consistent.</violation>
</file>

<file name="docs/HARD-QUESTIONS.md">

<violation number="1" location="docs/HARD-QUESTIONS.md:142">
P2: The new statement claims a 0.04 USDC x402 agent purchase has settled on Arc (self-facilitated), but the text immediately below (unchanged here) still asserts the opposite: "What has not happened is the agent-payment leg settling real money" and "**the money mechanism is proven; the agent-payment rail is not.**". This is precisely the conflated claim the PR set out to split, and section 5 now contradicts itself — a reader sees the settlement declared, then is told the agent-payment leg never settled. The surrounding lines need to be reconciled with the new settlement fact (e.g. the rail is proven self-facilitated; the Gateway facilitator rail is not).</violation>
</file>

<file name="dashboard/app/page.tsx">

<violation number="1" location="dashboard/app/page.tsx:192">
P2: The new text states "A 0.04 USDC agent purchase has settled end to end on Arc through the x402 path, self-facilitated — its transaction is on the audit page" and points the reader at the audit page. But `dashboard/app/audit/page.tsx` (the very surface named) still lists `claim: 'No agent purchase has settled through the x402 path.'` and `claim: 'No end-to-end run has been logged.'` with detail "docs/spine-runs/ contains only .gitkeep.". The landing page and the audit page now assert opposite facts about the same settlement, which contradicts the PR's goal of stating both independent facts on every surface. The audit surface needs the same split update so it does not under/contradict the landing page.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

2026-08-07T06:14:17Z 695d1a8+dirty FAIL unknown 1-FUND - seed_demo failed; nothing was escrowed, so no later beat can mean anything
2026-08-07T06:30:45Z 695d1a8+dirty FAIL price=1.00 3-SPEND 0xd37ec9bb2ab90be9c3d911634082b61ad104571537a923f00a7aad8bb5675fc5 the 0.04 payment rests at DELIVERED, not SETTLED (id pay_f962cb080f58ae5d): seller.ts:217 returns settlement NOT_BROADCAST, so the x402 handshake and signature are real but nothing reached Arc. V1's clause stays open until a facilitator broadcast is wired; see docs/V3-CIRCLE-SETUP.md
2026-08-07T07:12:03Z 695d1a8+dirty UNVERIFIED price=0.60 3-SPEND 0xb02580600dbcb1025b892f6251d3c91f36ff54fb4319df4b046b167c10c20bc8 the 0.04 payment rests at DELIVERED, not SETTLED (id pay_f962cb080f58ae5d): seller.ts:217 returns settlement NOT_BROADCAST, so the x402 handshake and signature are real but nothing reached Arc. V1's clause stays open until a facilitator broadcast is wired; see docs/V3-CIRCLE-SETUP.md
# 2026-08-08: the x402 gap that left the runs above at NOT_BROADCAST is closed. First x402 settlement on Arc, self-facilitated (NOT Circle Gateway): tx 0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc, seller 0->40000 atomic. Standalone settlement, not a spine_run row. See 2026-08-08-first-x402-settlement.md. The Circle Gateway path is built and has never run against the live service.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The comment says the x402 gap that left the 06:30/07:12 rows at NOT_BROADCAST 'is closed', but those rows failed precisely because the automated seller.ts:217 facilitator broadcast was never wired, and this self-facilitated settlement was performed manually via x402-broadcast.ts — an independent code path that does not change seller.ts. A fresh spine run would still hit NOT_BROADCAST on 3-SPEND, so the phrase is easy to misread as the automated beat now passing; suggest framing it as 'a first x402 settlement now exists (self-facilitated, manual), the automated facilitator broadcast that left these runs at NOT_BROADCAST is not wired yet' to avoid defeating the claim-splitting this PR intends.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/spine-runs/spine-runs.tsv, line 7:

<comment>The comment says the x402 gap that left the 06:30/07:12 rows at NOT_BROADCAST 'is closed', but those rows failed precisely because the automated seller.ts:217 facilitator broadcast was never wired, and this self-facilitated settlement was performed manually via x402-broadcast.ts — an independent code path that does not change seller.ts. A fresh spine run would still hit NOT_BROADCAST on 3-SPEND, so the phrase is easy to misread as the automated beat now passing; suggest framing it as 'a first x402 settlement now exists (self-facilitated, manual), the automated facilitator broadcast that left these runs at NOT_BROADCAST is not wired yet' to avoid defeating the claim-splitting this PR intends.</comment>

<file context>
@@ -4,3 +4,4 @@
 2026-08-07T06:14:17Z	695d1a8+dirty	FAIL	unknown	1-FUND	-	seed_demo failed; nothing was escrowed, so no later beat can mean anything
 2026-08-07T06:30:45Z	695d1a8+dirty	FAIL	price=1.00	3-SPEND	0xd37ec9bb2ab90be9c3d911634082b61ad104571537a923f00a7aad8bb5675fc5	the 0.04 payment rests at DELIVERED, not SETTLED (id pay_f962cb080f58ae5d): seller.ts:217 returns settlement NOT_BROADCAST, so the x402 handshake and signature are real but nothing reached Arc. V1's clause stays open until a facilitator broadcast is wired; see docs/V3-CIRCLE-SETUP.md
 2026-08-07T07:12:03Z	695d1a8+dirty	UNVERIFIED	price=0.60	3-SPEND	0xb02580600dbcb1025b892f6251d3c91f36ff54fb4319df4b046b167c10c20bc8	the 0.04 payment rests at DELIVERED, not SETTLED (id pay_f962cb080f58ae5d): seller.ts:217 returns settlement NOT_BROADCAST, so the x402 handshake and signature are real but nothing reached Arc. V1's clause stays open until a facilitator broadcast is wired; see docs/V3-CIRCLE-SETUP.md
+# 2026-08-08: the x402 gap that left the runs above at NOT_BROADCAST is closed. First x402 settlement on Arc, self-facilitated (NOT Circle Gateway): tx 0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc, seller 0->40000 atomic. Standalone settlement, not a spine_run row. See 2026-08-08-first-x402-settlement.md. The Circle Gateway path is built and has never run against the live service.
</file context>
Suggested change
# 2026-08-08: the x402 gap that left the runs above at NOT_BROADCAST is closed. First x402 settlement on Arc, self-facilitated (NOT Circle Gateway): tx 0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc, seller 0->40000 atomic. Standalone settlement, not a spine_run row. See 2026-08-08-first-x402-settlement.md. The Circle Gateway path is built and has never run against the live service.
# 2026-08-08: first x402 settlement on Arc, self-facilitated (NOT Circle Gateway): tx 0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc, seller 0->40000 atomic. Standalone settlement, not a spine_run row; the automated facilitator broadcast that left the runs above at NOT_BROADCAST is not wired yet, so a re-run still hits seller.ts:217 NOT_BROADCAST on 3-SPEND. See 2026-08-08-first-x402-settlement.md. The Circle Gateway path is built and has never run against the live service.

Comment thread docs/CIRCLE-FEEDBACK.md
dormant without a key, so with Gateway as the configured facilitator the seller reports
`settlement: NOT_BROADCAST` and **the Gateway path has never settled a payment**. We reached
SETTLED a different way — **self-facilitating the EIP-3009 authorization** (broadcasting
`transferWithAuthorization` directly, no Gateway): a real 0.04 USDC x402 payment settled on Arc

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This line (and the linked spine-run 'proof entry') claims a self-facilitated x402 settlement, but the self-facilitator source, the domain-chain test, and the 'Reproducible via x402-simulate.ts / x402-broadcast.ts' scripts do not exist anywhere in the repo (clean working tree, no such symbols in sidecar/). A reader can't regenerate or verify the claimed settlement from a clean checkout, which contradicts the repo's 'must not fabricate / reproducible' ground rule for claims. Either land the self-facilitator code and scripts, or qualify the claim (e.g. one-off, uncommitted) instead of presenting it as reproducible.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/CIRCLE-FEEDBACK.md, line 121:

<comment>This line (and the linked spine-run 'proof entry') claims a self-facilitated x402 settlement, but the self-facilitator source, the domain-chain test, and the 'Reproducible via x402-simulate.ts / x402-broadcast.ts' scripts do not exist anywhere in the repo (clean working tree, no such symbols in sidecar/). A reader can't regenerate or verify the claimed settlement from a clean checkout, which contradicts the repo's 'must not fabricate / reproducible' ground rule for claims. Either land the self-facilitator code and scripts, or qualify the claim (e.g. one-off, uncommitted) instead of presenting it as reproducible.</comment>

<file context>
@@ -115,15 +115,21 @@ itself the feedback.
+dormant without a key, so with Gateway as the configured facilitator the seller reports
+`settlement: NOT_BROADCAST` and **the Gateway path has never settled a payment**. We reached
+SETTLED a different way — **self-facilitating the EIP-3009 authorization** (broadcasting
+`transferWithAuthorization` directly, no Gateway): a real 0.04 USDC x402 payment settled on Arc
+(`docs/spine-runs/2026-08-08-first-x402-settlement.md`). So the loop is now end to end both
+cryptographically and financially; what has never run is the Gateway integration specifically,
</file context>

`SETTLED` becomes reachable — but the reconciliation transitions (`RECONCILING → SETTLED|FAILED`)
still need a settlement-status read the client does not have, so an unconfirmed settlement is
reconciled by an operator rather than automatically. **Update (2026-08-08):** a payment **has**
settled — through the *self-facilitator* (`SNAPFALL_SELF_FACILITATOR=1`), a different, permissionless

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The added lines present SNAPFALL_SELF_FACILITATOR=1 as an existing configuration knob, but no such env var exists in the codebase (not in sidecar/.env.example, not read anywhere in sidecar/src), and no self-facilitator source exists. A reader following this normative spec would wire up a switch that does nothing; either add the env var plus the mechanism, or reword the doc to describe the operator-run script without naming a non-existent config flag.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/handshakes/H3-sidecar-api.md, line 227:

<comment>The added lines present `SNAPFALL_SELF_FACILITATOR=1` as an existing configuration knob, but no such env var exists in the codebase (not in sidecar/.env.example, not read anywhere in sidecar/src), and no self-facilitator source exists. A reader following this normative spec would wire up a switch that does nothing; either add the env var plus the mechanism, or reword the doc to describe the operator-run script without naming a non-existent config flag.</comment>

<file context>
@@ -218,12 +218,16 @@ codes to `RECONCILING`, so the distinction holds the moment a key exists.)
+`SETTLED` becomes reachable — but the reconciliation transitions (`RECONCILING → SETTLED|FAILED`)
+still need a settlement-status read the client does not have, so an unconfirmed settlement is
+reconciled by an operator rather than automatically. **Update (2026-08-08):** a payment **has**
+settled — through the *self-facilitator* (`SNAPFALL_SELF_FACILITATOR=1`), a different, permissionless
+path that broadcasts the EIP-3009 authorization directly and reaches `SETTLED` with a real tx hash
+(`docs/spine-runs/2026-08-08-first-x402-settlement.md`). The Gateway-path statements above are
</file context>

Comment thread docs/HARD-QUESTIONS.md
self-facilitated (`docs/spine-runs/2026-08-08-first-x402-settlement.md`) — the authorization was
broadcast directly, not through Gateway. Separately, the Circle Gateway facilitator client is built
and has never run against Circle's live service; under that facilitator with no key the seller
records `NOT_BROADCAST`. Two independent facts: settlement happened, and it did not use Gateway.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new statement claims a 0.04 USDC x402 agent purchase has settled on Arc (self-facilitated), but the text immediately below (unchanged here) still asserts the opposite: "What has not happened is the agent-payment leg settling real money" and "the money mechanism is proven; the agent-payment rail is not.". This is precisely the conflated claim the PR set out to split, and section 5 now contradicts itself — a reader sees the settlement declared, then is told the agent-payment leg never settled. The surrounding lines need to be reconciled with the new settlement fact (e.g. the rail is proven self-facilitated; the Gateway facilitator rail is not).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/HARD-QUESTIONS.md, line 142:

<comment>The new statement claims a 0.04 USDC x402 agent purchase has settled on Arc (self-facilitated), but the text immediately below (unchanged here) still asserts the opposite: "What has not happened is the agent-payment leg settling real money" and "**the money mechanism is proven; the agent-payment rail is not.**". This is precisely the conflated claim the PR set out to split, and section 5 now contradicts itself — a reader sees the settlement declared, then is told the agent-payment leg never settled. The surrounding lines need to be reconciled with the new settlement fact (e.g. the rail is proven self-facilitated; the Gateway facilitator rail is not).</comment>

<file context>
@@ -134,9 +134,12 @@ you are asked.
+self-facilitated (`docs/spine-runs/2026-08-08-first-x402-settlement.md`) — the authorization was
+broadcast directly, not through Gateway. Separately, the Circle Gateway facilitator client is built
+and has never run against Circle's live service; under that facilitator with no key the seller
+records `NOT_BROADCAST`. Two independent facts: settlement happened, and it did not use Gateway.
 
 What *has* happened, on chain, is the full money lifecycle for two jobs: fund → advance → expense →
</file context>

Comment thread dashboard/app/page.tsx
about verifiability should be easiest to check where it is weakest, so the full list is
on the audit page beside the evidence.
A 0.04 USDC agent purchase has settled end to end on Arc through the x402 path,
self-facilitated — its transaction is on the audit page. What is still not proven: the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new text states "A 0.04 USDC agent purchase has settled end to end on Arc through the x402 path, self-facilitated — its transaction is on the audit page" and points the reader at the audit page. But dashboard/app/audit/page.tsx (the very surface named) still lists claim: 'No agent purchase has settled through the x402 path.' and claim: 'No end-to-end run has been logged.' with detail "docs/spine-runs/ contains only .gitkeep.". The landing page and the audit page now assert opposite facts about the same settlement, which contradicts the PR's goal of stating both independent facts on every surface. The audit surface needs the same split update so it does not under/contradict the landing page.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At dashboard/app/page.tsx, line 192:

<comment>The new text states "A 0.04 USDC agent purchase has settled end to end on Arc through the x402 path, self-facilitated — its transaction is on the audit page" and points the reader at the audit page. But `dashboard/app/audit/page.tsx` (the very surface named) still lists `claim: 'No agent purchase has settled through the x402 path.'` and `claim: 'No end-to-end run has been logged.'` with detail "docs/spine-runs/ contains only .gitkeep.". The landing page and the audit page now assert opposite facts about the same settlement, which contradicts the PR's goal of stating both independent facts on every surface. The audit surface needs the same split update so it does not under/contradict the landing page.</comment>

<file context>
@@ -188,10 +188,12 @@ export default function LandingPage() {
-          about verifiability should be easiest to check where it is weakest, so the full list is
-          on the audit page beside the evidence.
+          A 0.04 USDC agent purchase has settled end to end on Arc through the x402 path,
+          self-facilitated — its transaction is on the audit page. What is still not proven: the
+          Circle Gateway facilitator path is built and has never run against the live service, and
+          compliance screening is a labelled stub that fabricates no result. A product about
</file context>

Comment thread docs/PRD.md
| V7 / V10 / V11 "renders live" | One green spine run with a human watching. |
| V12 reset → spine → reset → spine | Two consecutive green runs. |
| Spine runs (schedule, §16) | **First automated settlement logged 2026-08-07, verdict UNVERIFIED** — the waterfall settled on chain and only the x402 beat is unproven (settlement rests at `NOT_BROADCAST`, pending a funded Circle Gateway deposit). **Three prior FAIL runs the same day** are recorded in `docs/spine-runs/spine-runs.tsv` and kept deliberately: the live runs surfaced what CI could not — the missing `startWork`/`submitDelivery` caller, and an operator-owned pool. **No daily cadence exists**: the Wed 29 Jul gate was missed; runs are on-demand, not scheduled. |
| Spine runs (schedule, §16) | **First automated settlement logged 2026-08-07, verdict UNVERIFIED** — the waterfall settled on chain; the x402 beat rested at `NOT_BROADCAST` in that run (Gateway facilitator, no key). **The x402 beat has since settled separately**: a real 0.04 USDC x402 payment settled on Arc 2026-08-08, **self-facilitated** — broadcasting the EIP-3009 authorization directly, not through Circle Gateway, which is built and has never run against the live service (`docs/spine-runs/2026-08-08-first-x402-settlement.md`). **Three prior FAIL runs the same day** are recorded in `docs/spine-runs/spine-runs.tsv` and kept deliberately: the live runs surfaced what CI could not — the missing `startWork`/`submitDelivery` caller, and an operator-owned pool. **No daily cadence exists**: the Wed 29 Jul gate was missed; runs are on-demand, not scheduled. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The added §0.1 row records the 2026-08-08 settlement inside a section still stamped "built vs. proven (2 Aug 2026)" / "updated 2 Aug 2026" (section header, §0 status field, and TOC). Since this section is the judge-facing implementation-status record and the PR is precisely about recording the 8 Aug event, update those date stamps when landing so the section header and its content don't contradict each other.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/PRD.md, line 59:

<comment>The added §0.1 row records the 2026-08-08 settlement inside a section still stamped "built vs. proven (2 Aug 2026)" / "updated 2 Aug 2026" (section header, §0 status field, and TOC). Since this section is the judge-facing implementation-status record and the PR is precisely about recording the 8 Aug event, update those date stamps when landing so the section header and its content don't contradict each other.</comment>

<file context>
@@ -56,7 +56,7 @@ a **committed artifact**. Code cannot satisfy those. A green CI run is not evide
 | V7 / V10 / V11 "renders live" | One green spine run with a human watching. |
 | V12 reset → spine → reset → spine | Two consecutive green runs. |
-| Spine runs (schedule, §16) | **First automated settlement logged 2026-08-07, verdict UNVERIFIED** — the waterfall settled on chain and only the x402 beat is unproven (settlement rests at `NOT_BROADCAST`, pending a funded Circle Gateway deposit). **Three prior FAIL runs the same day** are recorded in `docs/spine-runs/spine-runs.tsv` and kept deliberately: the live runs surfaced what CI could not — the missing `startWork`/`submitDelivery` caller, and an operator-owned pool. **No daily cadence exists**: the Wed 29 Jul gate was missed; runs are on-demand, not scheduled. |
+| Spine runs (schedule, §16) | **First automated settlement logged 2026-08-07, verdict UNVERIFIED** — the waterfall settled on chain; the x402 beat rested at `NOT_BROADCAST` in that run (Gateway facilitator, no key). **The x402 beat has since settled separately**: a real 0.04 USDC x402 payment settled on Arc 2026-08-08, **self-facilitated** — broadcasting the EIP-3009 authorization directly, not through Circle Gateway, which is built and has never run against the live service (`docs/spine-runs/2026-08-08-first-x402-settlement.md`). **Three prior FAIL runs the same day** are recorded in `docs/spine-runs/spine-runs.tsv` and kept deliberately: the live runs surfaced what CI could not — the missing `startWork`/`submitDelivery` caller, and an operator-owned pool. **No daily cadence exists**: the Wed 29 Jul gate was missed; runs are on-demand, not scheduled. |
 
 **Known gaps carried deliberately**
</file context>

Comment thread docs/CP2-SUBMISSION.md
end to end, with the EIP-3009 authorization signed by a real key and verified by the seller —
and that authorization was then **broadcast directly** (`transferWithAuthorization`), settling
0.04 USDC operator → seller on Arc ([`0x0d39b5…dccc`](https://testnet.arcscan.app/tx/0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc);
`docs/spine-runs/2026-08-08-first-x402-settlement.md`). Settlement did not require Gateway.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This PR adds proof entries to docs/spine-runs/ and references the new file in the changed text, but the gap statement right below still claims "docs/spine-runs/ is empty" — that is now factually false and contradicts the README 'must not misrepresent' constraint. Update the line (e.g. drop 'is empty' and acknowledge the settlement entry isn't a full end-to-end spine run) so the submission doesn't carry a stale contradiction.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/CP2-SUBMISSION.md, line 158:

<comment>This PR adds proof entries to docs/spine-runs/ and references the new file in the changed text, but the gap statement right below still claims "docs/spine-runs/ is empty" — that is now factually false and contradicts the README 'must not misrepresent' constraint. Update the line (e.g. drop 'is empty' and acknowledge the settlement entry isn't a full end-to-end spine run) so the submission doesn't carry a stale contradiction.</comment>

<file context>
@@ -149,14 +150,18 @@ We would rather state these than have them found.
+  end to end, with the EIP-3009 authorization signed by a real key and verified by the seller —
+  and that authorization was then **broadcast directly** (`transferWithAuthorization`), settling
+  0.04 USDC operator → seller on Arc ([`0x0d39b5…dccc`](https://testnet.arcscan.app/tx/0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc);
+  `docs/spine-runs/2026-08-08-first-x402-settlement.md`). Settlement did not require Gateway.
+  Separately, the Circle Gateway facilitator client (written 2 Aug, `sidecar/src/facilitator.ts`)
+  has never run against the live service — that path needs a Circle account, key, and Gateway
</file context>

@@ -0,0 +1,65 @@
# First x402 settlement — 8 Aug 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The doc cites files that don't exist in the repo, making its reproducibility and code-path claims unverifiable. sidecar/src/self-facilitator.ts, x402-simulate.ts, x402-broadcast.ts, and sidecar/src/usdc-domain-chain-test.ts are absent from the tree (the full sidecar/src/ listing and repo-wide find/rg return nothing for them), and this docs-only PR adds none of them. Point readers at the real implementation (e.g. the broadcast path in sidecar/src/seller.ts/facilitator.ts) or drop the references.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/spine-runs/2026-08-08-first-x402-settlement.md, line 32:

<comment>The doc cites files that don't exist in the repo, making its reproducibility and code-path claims unverifiable. `sidecar/src/self-facilitator.ts`, `x402-simulate.ts`, `x402-broadcast.ts`, and `sidecar/src/usdc-domain-chain-test.ts` are absent from the tree (the full `sidecar/src/` listing and repo-wide find/rg return nothing for them), and this docs-only PR adds none of them. Point readers at the real implementation (e.g. the broadcast path in `sidecar/src/seller.ts`/`facilitator.ts`) or drop the references.</comment>

<file context>
@@ -0,0 +1,65 @@
+The operator signed an `operator → seller` EIP-3009 authorization, then ran
+`transferWithAuthorization` as an `eth_call` (a read — no transaction). Only after that simulation
+came back clean was the **same signed payload** broadcast through the real self-facilitator
+(`sidecar/src/self-facilitator.ts`). Simulating the exact payload first means a clean simulation
+and a reverting send can only differ if something changed on chain between the two — and it did
+not. Reproducible via `x402-simulate.ts` / `x402-broadcast.ts`.
</file context>

Comment thread docs/SPINE-RUNS.md
| Task | Done when (WORK-SPLIT §2) | Closed by |
|---|---|---|
| V1 | "a real four-cent purchase completes on testnet **and the raw request/response pair is committed as a fixture**" | **NOT closed by any beat yet.** Beat 3 proves the x402 handshake, the EIP-3009 signature and the resource delivery are real, and stops at UNVERIFIED because `seller.ts:217` returns `settlement: NOT_BROADCAST`, so nothing reaches Arc and `SETTLED` is unreachable (H3 section 8). Both halves of the clause need the facilitator broadcast plus a funded wallet: see `docs/V3-CIRCLE-SETUP.md`. |
| V1 | "a real four-cent purchase completes on testnet **and the raw request/response pair is committed as a fixture**" | **Settlement half proven 2026-08-08; the Circle-specific fixture half stays open.** Beat 3 proves the x402 handshake, the EIP-3009 signature and the resource delivery are real. It rested at UNVERIFIED in the 2026-08-07 run because, under the (unconfigured) Gateway facilitator, `seller.ts` returned `settlement: NOT_BROADCAST`. `SETTLED` is **not** unreachable: a real 0.04 USDC x402 payment has since settled on Arc, **self-facilitated** — broadcasting the EIP-3009 authorization directly, no Gateway (tx [`0x0d39b5…dccc`](https://testnet.arcscan.app/tx/0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc); `docs/spine-runs/2026-08-08-first-x402-settlement.md`). The open half is the Circle-specific one: the committed fixture (`capture-v1-fixture`, AT-18) requires Circle's endpoints, and the Gateway path is built but has never run against the live service (`docs/V3-CIRCLE-SETUP.md`). |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Calling the settlement half of V1 "proven" overstates the clause, which requires "a real four-cent purchase completes on testnet." The 08-08 event was a standalone manual operator->operator transfer, not a purchase that ran through the live flow to SETTLED, so that half of the clause arguably remains open; suggest wording it as the settlement mechanism being demonstrated rather than the clause half closed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/SPINE-RUNS.md, line 13:

<comment>Calling the settlement half of V1 "proven" overstates the clause, which requires "a real four-cent purchase completes on testnet." The 08-08 event was a standalone manual operator->operator transfer, not a purchase that ran through the live flow to SETTLED, so that half of the clause arguably remains open; suggest wording it as the settlement mechanism being demonstrated rather than the clause half closed.</comment>

<file context>
@@ -10,7 +10,7 @@ that no unit test can close, because the clause names a live run:
 | Task | Done when (WORK-SPLIT §2) | Closed by |
 |---|---|---|
-| V1 | "a real four-cent purchase completes on testnet **and the raw request/response pair is committed as a fixture**" | **NOT closed by any beat yet.** Beat 3 proves the x402 handshake, the EIP-3009 signature and the resource delivery are real, and stops at UNVERIFIED because `seller.ts:217` returns `settlement: NOT_BROADCAST`, so nothing reaches Arc and `SETTLED` is unreachable (H3 section 8). Both halves of the clause need the facilitator broadcast plus a funded wallet: see `docs/V3-CIRCLE-SETUP.md`. |
+| V1 | "a real four-cent purchase completes on testnet **and the raw request/response pair is committed as a fixture**" | **Settlement half proven 2026-08-08; the Circle-specific fixture half stays open.** Beat 3 proves the x402 handshake, the EIP-3009 signature and the resource delivery are real. It rested at UNVERIFIED in the 2026-08-07 run because, under the (unconfigured) Gateway facilitator, `seller.ts` returned `settlement: NOT_BROADCAST`. `SETTLED` is **not** unreachable: a real 0.04 USDC x402 payment has since settled on Arc, **self-facilitated** — broadcasting the EIP-3009 authorization directly, no Gateway (tx [`0x0d39b5…dccc`](https://testnet.arcscan.app/tx/0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc); `docs/spine-runs/2026-08-08-first-x402-settlement.md`). The open half is the Circle-specific one: the committed fixture (`capture-v1-fixture`, AT-18) requires Circle's endpoints, and the Gateway path is built but has never run against the live service (`docs/V3-CIRCLE-SETUP.md`). |
 | V7 | "a full spine run renders every state transition live" | a human watching during a green run |
 | V9 | "clicking Accept produces the on-chain settlement tx" | beat 6, from JobVault status on chain |
</file context>
Suggested change
| V1 | "a real four-cent purchase completes on testnet **and the raw request/response pair is committed as a fixture**" | **Settlement half proven 2026-08-08; the Circle-specific fixture half stays open.** Beat 3 proves the x402 handshake, the EIP-3009 signature and the resource delivery are real. It rested at UNVERIFIED in the 2026-08-07 run because, under the (unconfigured) Gateway facilitator, `seller.ts` returned `settlement: NOT_BROADCAST`. `SETTLED` is **not** unreachable: a real 0.04 USDC x402 payment has since settled on Arc, **self-facilitated** — broadcasting the EIP-3009 authorization directly, no Gateway (tx [`0x0d39b5…dccc`](https://testnet.arcscan.app/tx/0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc); `docs/spine-runs/2026-08-08-first-x402-settlement.md`). The open half is the Circle-specific one: the committed fixture (`capture-v1-fixture`, AT-18) requires Circle's endpoints, and the Gateway path is built but has never run against the live service (`docs/V3-CIRCLE-SETUP.md`). |
| V1 | "a real four-cent purchase completes on testnet **and the raw request/response pair is committed as a fixture**" | **Settlement mechanism demonstrated 2026-08-08; the clause's real-purchase half and the Circle-specific fixture half both stay open.** Beat 3 proves the x402 handshake, the EIP-3009 signature and the resource delivery are real. It rested at UNVERIFIED in the 2026-08-07 run because, under the (unconfigured) Gateway facilitator, `seller.ts` returned `settlement: NOT_BROADCAST`. `SETTLED` is **not** unreachable: a real 0.04 USDC x402 payment has since settled on Arc, **self-facilitated** — broadcasting the EIP-3009 authorization directly, no Gateway (tx [`0x0d39b5…dccc`](https://testnet.arcscan.app/tx/0x0d39b5738f7042ae82ae0a17f24474e67c27db0cd837b791c112f8d264b6dccc); `docs/spine-runs/2026-08-08-first-x402-settlement.md`). But that settlement was a manual operator->operator transfer, not a purchase that completed through the live flow, so V1's "a real four-cent purchase completes on testnet" is not yet closed either. The Circle-specific half stays open too: the committed fixture (`capture-v1-fixture`, AT-18) requires Circle's endpoints, and the Gateway path is built but has never run against the live service (`docs/V3-CIRCLE-SETUP.md`). |


1. **"Circle-facilitator-only" is enforced by the OUTER guard**, not H3: Circle's Agent-Wallet spend policy (payee/amount allowlist at the wallet layer) is what actually binds settlement to the approved merchant and Circle's rails. H3's contribution is the pre-sign payee-equality check (§2.2 step 8), which ensures the treasury only ever signs an authorization payable to the approved `intent.merchant`. Fixing the AT-05 payee hole is therefore a **prerequisite** for this invariant to mean anything.
2. **Facilitator reconciliation is unimplemented in the dry run.** `DELIVERED → SETTLED` and `RECONCILING → SETTLED|FAILED` require a real broadcast; until it is wired, `settlement` stays `NOT_BROADCAST` and `SETTLED` is unreachable. §2.3/§5 mark this; consumers must not treat `SETTLED` as reachable yet.
2. **Facilitator reconciliation is unimplemented in the *Gateway* dry run.** `DELIVERED → SETTLED` and `RECONCILING → SETTLED|FAILED` require a real broadcast. With no facilitator configured, `settlement` stays `NOT_BROADCAST`. This is not a claim that `SETTLED` is out of reach: the self-facilitator (`SNAPFALL_SELF_FACILITATOR=1`) broadcasts the authorization directly and a real payment has reached `SETTLED` that way (`docs/spine-runs/2026-08-08-first-x402-settlement.md`) — consistent with point 1, since a bearer authorization can be settled by *any* facilitator. What remains unexercised is Gateway-confirmed reconciliation specifically: the Circle path is built and has never run against the live service.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new SETTLED-has-happened claim (§2.3, §8) directly contradicts the near-by §5 header that SETTLED is "unexercised in fact" and the SETTLED meaning that urges treating "the first live purchase as the test of it". The PR splits the Gateway claims but leaves §5 worded for a world where SETTLED has never fired; update the §5 header and SETTLED meaning to say the Gateway-confirmed reconciliation is the unexercised part, so the spec is internally consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/handshakes/H3-sidecar-api.md, line 477:

<comment>The new SETTLED-has-happened claim (§2.3, §8) directly contradicts the near-by §5 header that SETTLED is "unexercised in fact" and the SETTLED meaning that urges treating "the first live purchase as the test of it". The PR splits the Gateway claims but leaves §5 worded for a world where SETTLED has never fired; update the §5 header and SETTLED meaning to say the Gateway-confirmed reconciliation is the unexercised part, so the spec is internally consistent.</comment>

<file context>
@@ -469,7 +474,7 @@ This is exactly the AT-03 (`INTENT_NOT_APPROVED`, immediate full release, no sig
 
 1. **"Circle-facilitator-only" is enforced by the OUTER guard**, not H3: Circle's Agent-Wallet spend policy (payee/amount allowlist at the wallet layer) is what actually binds settlement to the approved merchant and Circle's rails. H3's contribution is the pre-sign payee-equality check (§2.2 step 8), which ensures the treasury only ever signs an authorization payable to the approved `intent.merchant`. Fixing the AT-05 payee hole is therefore a **prerequisite** for this invariant to mean anything.
-2. **Facilitator reconciliation is unimplemented in the dry run.** `DELIVERED → SETTLED` and `RECONCILING → SETTLED|FAILED` require a real broadcast; until it is wired, `settlement` stays `NOT_BROADCAST` and `SETTLED` is unreachable. §2.3/§5 mark this; consumers must not treat `SETTLED` as reachable yet.
+2. **Facilitator reconciliation is unimplemented in the *Gateway* dry run.** `DELIVERED → SETTLED` and `RECONCILING → SETTLED|FAILED` require a real broadcast. With no facilitator configured, `settlement` stays `NOT_BROADCAST`. This is not a claim that `SETTLED` is out of reach: the self-facilitator (`SNAPFALL_SELF_FACILITATOR=1`) broadcasts the authorization directly and a real payment has reached `SETTLED` that way (`docs/spine-runs/2026-08-08-first-x402-settlement.md`) — consistent with point 1, since a bearer authorization can be settled by *any* facilitator. What remains unexercised is Gateway-confirmed reconciliation specifically: the Circle path is built and has never run against the live service.
 
 This matches the two-layer authorization model in §0: inner guard (H3 + policy + approval) proposes and signs only approved terms; outer guard (Agent-Wallet policy) is the settlement-route and payee enforcer.
</file context>

@gnanam1990
gnanam1990 merged commit 6824f88 into main Aug 8, 2026
8 checks passed
@gnanam1990
gnanam1990 deleted the docs/x402-settled branch August 8, 2026 02:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant