Skip to content

feat(X402-46): D.3 indexer-state probe + upstream_stuck composite verdict#81

Merged
fardinvahdat merged 2 commits into
mainfrom
feat/X402-46-d3-indexer-state-probe
May 22, 2026
Merged

feat(X402-46): D.3 indexer-state probe + upstream_stuck composite verdict#81
fardinvahdat merged 2 commits into
mainfrom
feat/X402-46-d3-indexer-state-probe

Conversation

@fardinvahdat
Copy link
Copy Markdown
Owner

Summary

X402-46 (D.3) — closes Max's polyodds.bet pain pattern: settles complete against CDP /settle returning {bazaar: {status: "processing"}}, but discovery shows 0 resources for the payTo — queue stalled. Per ADR-004 Pillar 1, this becomes upstream_stuck (distinct attribution from generic upstream_issue like propagation drift or network errors).

Also implements ADR-004 Pillar 3 facilitator-aware semantics across D.2 + D.3.

What this PR adds

New shared helper: src/bazaar/facilitator-detect.ts

  • Manifest-claim-based detection per ADR-004 Pillar 3
  • Recognizes "coinbase-cdp" / "cdp" / "coinbase" (case-insensitive) as cdp; any other non-empty string as non-cdp; everything else as unknown
  • Used by both D.2 (propagation) and D.3 (indexing)
  • v0.3.3+ deferred: TomSmart's facilitator_inferred field + empirical probe (require external fixture data still pending Sunday)

Extended src/bazaar/indexing.ts

  • Accepts new manifest?: WellKnownManifest option for facilitator detection
  • Short-circuits to not_applicable_non_cdp BEFORE network probe when manifest declares non-CDP (fast, no HTTP cost, no false-positive)
  • Every result emits detail.indexer_state: indexed | processing | unknown | not_applicable_non_cdp
  • 404 + empty resources both map to processing (matches Max's case + @0xdespot's bucket taxonomy from #2207)
  • Legacy detail.status field preserved for backward-compat

Extended src/bazaar/propagation.ts

  • Same short-circuit pattern for non-CDP services (ADR-004 Pillar 3 consistency)
  • metadata_propagation enum gains 5th value not_applicable_non_cdp

New verdict: upstream_stuck

  • src/bazaar/verdict.ts synthesizer prefers upstream_stuck over upstream_issue when indexer_state: processing fires
  • Exit code 3 (same as upstream_issue, preserves CI contract per ADR-004 Pillar 1)
  • Distinct verdict prose: names Max's polyodds.bet case + #2207 cluster explicitly
  • not_applicable_non_cdp paths roll up to looks_correct (WAI per ADR-004 Pillar 3)

Acceptance criteria (X402-46)

  • Add not_applicable_non_cdp to the indexer_state enum (4 states total: indexed, processing, unknown, not_applicable_non_cdp)
  • Add facilitator-detection logic (option 1: manifest claim per ADR-004 Pillar 3)
  • Verdict rollup: not_applicable_non_cdplooks_correct (NOT upstream_issue)
  • New top-level upstream_stuck composite verdict; rolls up to exit code 3
  • Unit tests: ≥6 — 22 new total (8 facilitator-detect + 7 indexing state coverage + 2 propagation NA-non-CDP + 5 verdict upstream_stuck)
  • Snapshot fixture regenerated to include indexer_state field on indexing detail
  • CHANGELOG entries: ### Added (D.3) + ### Changed (indexer_state field on indexing) + ### JSON API subsection (D.3 additive changes)

v0.3.3+ deferred — processing_fresh vs processing_stale

Per the X402-46 ticket, the original AC included staleness-threshold logic (--processing-stale-after-hours, default 24h). Deferred because it requires settle-timestamp data x402trace doesn't collect without driving live settles or accepting operator-supplied evidence (a --settle-tx or --last-settle-at flag). Filing as a v0.3.3+ follow-up; the indexer_state: processing value catches both fresh + stale cases under one bucket for v0.3.2 ship.

Strict audit gate — abbreviated (user-in-loop mode)

  • Stage 1 pre-work: branched off 7972da3 (PR feat(X402-45): D.2 metadata propagation diff (5th check) #79 X402-45 D.2 merged HEAD); .gitignore WIP not staged
  • Stage 2 implementation: 1 new module + 4 modified source files + types extension + verdict synthesizer + 4 test files extended + CHANGELOG entries
  • Stage 3 correctness: ✅ typecheck + lint + 499 tests pass (was 477; +22 new) + build + check:publish-surface 100 files / 389 KB / 0 devDep imports (under 400 KB cap)
  • Stage 4 edge cases: ≥10 — manifest undefined (--endpoint mode), manifest with no extensions, facilitator field as non-string, whitespace-only facilitator value, case-insensitive CDP claim matching, short-circuit ordering (must precede payTo check), pipeline test rename (upstream_issueupstream_stuck), backward-compat preservation of legacy detail.status, multi-result aggregation in verdict (indexing + propagation both info), facilitator-detect helper's unknown default
  • Stage 5 gap check: all 7 X402-46 AC items satisfied; ADR-004 Pillar 1 (upstream_stuck verdict) + Pillar 3 (facilitator-aware semantics across D.2 + D.3) both honored; v0.3.3+ deferral on staleness threshold documented
  • Stage 6 ship: this PR

What unblocks on merge

  • X402-47 (fixture consumption) — Max's polyodds.bet becomes the canonical upstream_stuck fixture; anchor-x402's JPYC Polygon rail becomes the canonical not_applicable_non_cdp fixture once it's wired
  • X402-48 (release cut) — D.1 + D.2 + D.3 + D.4 + D.5 all shipped; only fixture consumption + release prep remain

v0.3.2 progress after merge

6 of 8 tickets done: X402-41 ADR-004, X402-42 D.4, X402-43 D.5, X402-44 JSON API stability, X402-45 D.2, X402-46 D.3. Remaining: X402-47 fixture consumption, X402-48 release cut. TomSmart's Sunday cdp-mature fixture drop ~24h out.

🤖 Generated with Claude Code

…dict

Closes Max's polyodds.bet pain pattern: settles complete against CDP
/settle returning {bazaar: {status: "processing"}}, but discovery
shows 0 resources for the payTo — queue stalled. Per ADR-004 Pillar 1,
this is `upstream_stuck` (distinct attribution from generic
`upstream_issue` like propagation drift or network errors).

New file `src/bazaar/facilitator-detect.ts`:
- Shared D.2 + D.3 helper, manifest-claim-based detection
- Recognizes "coinbase-cdp" / "cdp" / "coinbase" (case-insensitive) as cdp;
  any other non-empty string as non-cdp; everything else as unknown
- Default behavior is `unknown` when manifest doesn't declare —
  conservative; existing CDP-discovery probe path handles unknown
- v0.3.3+ deferred: TomSmart's `facilitator_inferred` field + empirical
  probe (require external fixture data still pending Sunday)

`src/bazaar/indexing.ts`:
- Now accepts `manifest?: WellKnownManifest` for facilitator detection
- Short-circuits to `not_applicable_non_cdp` BEFORE network probe when
  manifest declares non-CDP (fast, no HTTP cost, no false-positive)
- Every result emits `detail.indexer_state`: indexed | processing |
  unknown | not_applicable_non_cdp
- 404 + empty resources both map to `processing` (matches Max's case
  + 0xdespot's bucket taxonomy from #2207)
- Legacy `detail.status` field preserved for backward-compat

`src/bazaar/propagation.ts`:
- Now also short-circuits to `not_applicable_non_cdp` when manifest
  declares non-CDP (ADR-004 Pillar 3 consistency: both D.2 and D.3
  honor the same attribution)
- `metadata_propagation` enum gains 5th value `not_applicable_non_cdp`

`src/bazaar/verdict.ts`:
- New `upstream_stuck` composite verdict (exit code 3, same as
  upstream_issue; preserves CI contract)
- Synthesizer prefers `upstream_stuck` over `upstream_issue` when
  indexer_state=processing fires
- `not_applicable_non_cdp` paths roll up to `looks_correct` (WAI per
  ADR-004 Pillar 3)

`src/bazaar/types.ts`:
- `BazaarVerdict` union extended with `upstream_stuck`
- New `IndexerState` type

Tests: +22 new (8 facilitator-detect + 7 indexing state coverage +
2 propagation NA-non-CDP + 5 verdict upstream_stuck). Snapshot fixture
regenerated to include indexer_state on the indexing result. Existing
"upstream_issue when only indexing surfaces info" pipeline test
renamed + retargeted to upstream_stuck (the new attribution).

499 tests pass (was 477). 100 files / 389 KB / 0 devDep imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
x402trace-dogfood Ready Ready Preview, Comment May 22, 2026 10:49pm

v0.3.0 shipped at 96 files. PR #69 fixtures pushed to 100. v0.3.2
cycle adds (X402-44 json-api stability + X402-43 D.5 extensions-bazaar
+ X402-45 D.2 propagation + X402-46 D.3 facilitator-detect) bring it
to 102 — over the v0.3.0-era cap.

Raise to 110 with breathing room for X402-47 fixture consumption +
future v0.3.x facets. Existing 400 KB unpacked cap unchanged
(currently 389 KB, well below).

Discovered when PR #81 (X402-46) CI failed on the publish-surface
check; my local dist/ was stale and reported 100. Fresh local build
matches CI's 102.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@fardinvahdat fardinvahdat merged commit 6624e21 into main May 22, 2026
4 checks passed
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