Skip to content

M6 Phase A — ChannelIngress interface + x402 conformance - #27

Merged
Mayakovsky merged 1 commit into
mainfrom
m6-phase-a-channel-ingress
Jul 19, 2026
Merged

M6 Phase A — ChannelIngress interface + x402 conformance#27
Mayakovsky merged 1 commit into
mainfrom
m6-phase-a-channel-ingress

Conversation

@Mayakovsky

Copy link
Copy Markdown
Owner

M6 Phase A — ChannelIngress interface + x402 conformance

Introduces the reusable channel-adapter seam (M6's core reframing) and makes x402 the first conforming channel, with zero change to per-request payment behavior. Zero-spend, no chain. Spec: movement-6-BUILD-SPEC-KOV.md v3 §3-A; directive movement-6-PHASE-A-KOV-directive.md.

Manifest (7 files)

  1. grey-core/src/channels/ingress.tsChannelIngress (start/stop/registerOffering/identity) + ChannelIdentity + OfferingRegistration. Slim, lifecycle+catalog only (A1); confirm/deliver/validation/envelope stay adapter-internal. Reuses the shared HandlerInput — no duplicated handler types.
  2. grey-core/src/channels/x402Adapter.tsX402Adapter implements ChannelIngress as a boot-wiring shell over the existing buildServer(deps, gate) + listen. Zero per-request payment logic; the relayer key never enters grey-core (invariant feat(refuel): relayer gas self-funding loop (M5 Phase F) #19 — the adapter takes only the relayer address for its boot log). FDQ-66(a) boot-wrapper: registerOffering records the catalog for identity()/observability only — routes stay statically mounted from PAID (server/routes/offerings.ts).
  3. grey-core/src/index.ts — export ChannelIngress/OfferingRegistration/ChannelIdentity + X402Adapter/X402AdapterOptions.
  4. grey-core/src/start.ts — boot x402 through X402Adapter.start() in place of the inline buildServer+listen (real conformance, not paper). Registers the 7 PAID offerings from the single price source (invariant fix(db): restore refuel_log append-only (FDQ-52) #20). No per-request code changed.
  5. test/channels/x402Adapter.test.ts — conformance: interface satisfaction, identity() returns payTo/DID, registerOffering records the catalog, and a real bound socket proving /health→200 and a paid route→402.
  6. scripts/dist-boot-smoke.ts (+ package.json script) — M5-pattern dist-boot smoke.

Deviation flag (minor, sound)

Beyond the minimal directive ask, start.ts also registers the 7 offerings on the adapter at boot (registerOffering × PAID_SLUGS, price via priceUsdFor). This makes FDQ-66(a)'s "records for observability" real in production — identity()/catalog are actually populated, not just exercised in the test. Zero per-request impact; routes still mount statically from PAID.

Zero-touch held

verify.ts/settle.ts/challenge.ts/preHandler.ts (payment path), handlers/* (shared entrypoint — only referenced), and offerings.ts route logic + PAID static mounting — all unchanged.

Verification (cite: run locally)

  • typecheck + lint + build — clean.
  • vitest run96 tests pass (16 files; +5 new conformance tests).
  • dist-boot smoke — PASS: built dist/start.js boots through X402Adapter; /health200 {"status":"ok"}; POST /v1/offerings/legitimacy_scan402 {scheme:exact, network:eip155:84532, maxAmountRequired:250000, payTo:0x7099…79C8} — byte-identical to pre-adapter behavior.

HOLD

Branch commit + push + PR-open is the authorized Phase A deliverable. Merge to main awaits Forces' explicit authorization.

🤖 Generated with Claude Code

M6 Phase A — introduce the reusable channel-adapter seam and make x402 the
first conforming channel, with zero change to per-request payment behavior.

- grey-core/src/channels/ingress.ts: ChannelIngress (start/stop/registerOffering/
  identity) + ChannelIdentity + OfferingRegistration. Slim, lifecycle+catalog only
  (A1); confirm/deliver/validation/envelope stay adapter-internal. Reuses the shared
  HandlerInput — no duplicated handler types.
- grey-core/src/channels/x402Adapter.ts: X402Adapter implements ChannelIngress as a
  boot-wiring shell over the existing buildServer(deps, gate) + listen. Zero
  per-request payment logic; the relayer key never enters grey-core (invariant #19).
  FDQ-66(a) boot-wrapper: registerOffering records the catalog for identity()/
  observability only — routes stay statically mounted from PAID (offerings.ts).
- grey-core/src/index.ts: export ChannelIngress/OfferingRegistration/ChannelIdentity
  + X402Adapter/X402AdapterOptions.
- grey-core/src/start.ts: boot x402 THROUGH X402Adapter.start() in place of the inline
  buildServer+listen (real conformance, not paper). Registers the 7 PAID offerings via
  the single price source (invariant #20). No per-request code changed.
- test/channels/x402Adapter.test.ts: conformance — interface satisfaction, identity()
  payTo/DID, registerOffering catalog, and a real bound socket proving /health→200 and
  a paid route→402.
- scripts/dist-boot-smoke.ts (+ package.json script): M5-pattern dist-boot smoke —
  boots built dist/start.js and asserts /health→200 and paid→402 byte-identical.

Zero-touch held: verify/settle/challenge/preHandler, handlers/*, offerings.ts route
logic + PAID static mounting all unchanged. Zero-spend, no chain.

Green: typecheck + lint + build clean; 96 tests pass (16 files, +5 new);
dist-boot smoke PASS (/health 200; legitimacy_scan 402 exact/eip155:84532/250000).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Mayakovsky
Mayakovsky merged commit 4ef8ed9 into main Jul 19, 2026
3 checks passed
@Mayakovsky
Mayakovsky deleted the m6-phase-a-channel-ingress branch August 14, 2026 01:46
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