# Method — Agent Payability Funnel (0989)

Companion to `funnel.json` and `funnel.csv`. Documents stage definitions,
counting rules, drop-off reasons, and the two measurement regimes we mix
in a single table.

## What this measures

For every ERC-8004 agent registered on Base (and Ethereum mainnet), how far
it gets along a 10-stage path from "registered on-chain" to "accepted my
payment and delivered a response." Stages are cumulative: an agent that
reaches stage N also reaches every stage 1..N-1 by construction.

## Chains

Base (`chain_id` 8453) and Ethereum mainnet (`chain_id` 1). Other chains
(Arbitrum, Optimism, BNB, Gnosis, Linea, Soneium, Mantle, Metis, Scroll,
Taiko, Abstract, Goat, SKALE Base, Avalanche, Polygon, Celo) are measured
separately in `data/census/` and are out of scope for this funnel.

## Population

`~/.openclaw/erc8004_agent_profiles.json` — the live-enriched profile store
written by `erc8004_discovery.py`, `agent_reenrich.py`, and
`live_probes.py`. One row per `agent_key = f"{chain}:{agent_id}"`. Rows are
not deleted when discovery sees a revoked tokenURI; stage 1 is therefore an
inclusive count of all registered agents the system has ever seen on each
chain, matching the on-chain `IdentityRegistry.ownerOf()` lifetime count.

## Stage definitions (precise)

| Stage | ID | Rule |
|---|---|---|
| 1 | `s1_registered` | profile row exists with `chain == <c>` |
| 2 | `s2_advertises_payment` | `_x402_supported(profile)` returns True, OR `metadata_fields.{payment,payment_options,payments}` is truthy, OR `live_endpoints.well_known.{payment,payment_options}` is truthy |
| 3 | `s3_endpoint_answers` | `live_endpoints.host_health.reachable is True` OR `host_health.http_status` is an integer in `200..599` |
| 4 | `s4_valid_402_challenge` | `live_endpoints.x402_challenge.accepts[]` is non-empty, OR `live_endpoints.well_known.accepts[]` is non-empty |
| 5 | `s5_includes_payto` | at least one `accepts[*].payTo` (or `.address`) is a 0x-prefixed 40-hex string |
| 6 | `s6_parseable_price` | `readiness._has_valid_x402_payment_config(profile)` returns True — payTo + positive price field found in canonical shape |
| 7 | `s7_price_normal_range` | min advertised `price_usd ≤ $1.00/call`. USDC is converted from µUSDC (6-decimals). Non-USDC assets return `None` and drop this stage |
| 8 | `s8_payable_no_auth` | **observed:** our test-purchase attempt returned a parseable 402 that passed facilitator verify-only (`isValid=true`) — i.e. no 401/403 auth gate, no `missing accepts`, no 405 method mismatch |
| 9 | `s9_settled_onchain` | **observed:** the signed EIP-3009 `transferWithAuthorization` settled on-chain to the advertised payTo, discovered via `pay.tx_hash` in the server response OR a USDC `Transfer` log scan from the payer wallet |
| 10 | `s10_delivered` | **observed:** HTTP 200 with substantive body AFTER the payment settled (= Proven, path b) |

### Deterministic vs observed

Stages 1-7 are deterministic — they read today's profile store and return
the same answer for every call with the same input. Stages 8-10 are
observed from pilot evidence files
(`docs/0982b-test-purchase-pilot/evidence.json`,
`docs/0983-product-readiness/pilot-evidence.json`,
`docs/0988-buy-round/pilot-evidence.json`). They are not a sample — they
are a census of every test purchase Agent Zero has attempted to date.

### Pool restriction and the "observed outside pool" column

Stages 8-10 counts in the main funnel are restricted to agents in today's
stage-7 pool, so the funnel reads monotonically. Pilot attempts against
agents that have since dropped out of stage 7 (e.g. because
`live_endpoints.x402_challenge.accepts[]` was never written back for them —
the price lives only in a POST body or on a sub-path that the probe URL
list didn't carry) are counted separately as `observed_outside_pool`.
Those are real deliveries, just not visible from today's deterministic
funnel. The `delivered_agents[]` list in `funnel.json` is the full set,
irrespective of pool membership.

## Owner clusters

For each stage we count distinct `payTo` addresses (lowercased) as a
proxy for operator identity. A single operator who registers 10 ERC-8004
agents behind one treasury payTo contributes 10 to the agent count but 1
to the operator count. This is a lower bound on operator distinctness —
one operator may use multiple payTos — and does not deduplicate across
chains.

## Drop-off reason enum

```
not_advertised        no payment metadata at stages 1→2
unreachable           host_health unreachable or http=0 at stages 2→3
no_402_challenge      reachable but no stored accepts[] at stages 3→4
no_payto              accepts[] present but no valid payTo at stages 4→5
price_missing         payTo present but no parseable price at stages 5→6
price_above_1usd      parseable price but > $1/call at stages 6→7
auth_walled           pilot attempt returned 401/403 (login/API key gated)
missing_accepts       pilot attempt returned 402 with empty accepts[]
method_not_allowed    pilot attempt returned 405 (POST-only probed GET)
facilitator_rejects   verify-only came back isValid=false
pay_failed            verify-only valid but POST non-200 (no settlement)
settled_no_delivery   payment settled but response was still 402/non-body
```

## Price distribution

Reported as a histogram over the stage-6 agent set, bucketed by USD:
`[0, 0.001)`, `[0.001, 0.01)`, `[0.01, 0.1)`, `[0.1, 1.0)`, `[1.0, 10.0)`,
`[10.0, 100.0)`, `[100.0, +∞)`. We report min / median / mean / max, plus
the count of agents whose min advertised price exceeds $1/call
(`above_1usd_count`). Non-USDC-denominated prices are excluded from the
USD distribution and reported separately only via the `price_missing`
drop-off reason at stage 6.

## Honesty rules

- Stages 1-7 counts update whenever the profile store rescans — expect
  movement from day to day as enrichment catches up. We timestamp every
  snapshot in `funnel.json.generated_at`.
- Stages 8-10 counts update only when a new pilot evidence file lands.
  Backfilled snapshots reflect the pilot cohort that existed on their
  stated date; we do not retroactively credit later deliveries to earlier
  snapshots.
- Each snapshot has a `backfilled` flag set to True if `as_of_date` is
  earlier than `generated_at` and the pilot evidence used was not yet
  present on `as_of_date`. (Today's snapshot is NOT backfilled.)
- No agent is named in aggregate rollups. The `delivered_agents[]` list
  in `funnel.json` is the single exception — those agents are already
  publicly identified via on-chain tx hashes in prior task reports and
  reproduce the pre-registered Proven cohort.

## Reproducing a snapshot

```bash
python3 scripts/metrics/payability_funnel_snapshot.py \
    --date YYYY-MM-DD --write
```

Reads the live profile store, the 0946 payable artifact, and every pilot
evidence file in `docs/0982b-test-purchase-pilot/`,
`docs/0983-product-readiness/`, `docs/0988-buy-round/`. Writes:

- `metrics/payability-funnel/<date>/funnel.json` — the full structured
  snapshot (schema v1).
- `metrics/payability-funnel/<date>/funnel.csv` — one row per
  (chain, stage), flat for Grafana / spreadsheet consumption.

## Licence

Dataset released under CC BY 4.0.
