M6 Phase A — ChannelIngress interface + x402 conformance - #27
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M6 Phase A —
ChannelIngressinterface + x402 conformanceIntroduces 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.mdv3 §3-A; directivemovement-6-PHASE-A-KOV-directive.md.Manifest (7 files)
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 sharedHandlerInput— no duplicated handler types.grey-core/src/channels/x402Adapter.ts—X402Adapter implements ChannelIngressas a boot-wiring shell over the existingbuildServer(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:registerOfferingrecords the catalog foridentity()/observability only — routes stay statically mounted fromPAID(server/routes/offerings.ts).grey-core/src/index.ts— exportChannelIngress/OfferingRegistration/ChannelIdentity+X402Adapter/X402AdapterOptions.grey-core/src/start.ts— boot x402 throughX402Adapter.start()in place of the inlinebuildServer+listen(real conformance, not paper). Registers the 7PAIDofferings from the single price source (invariant fix(db): restore refuel_log append-only (FDQ-52) #20). No per-request code changed.test/channels/x402Adapter.test.ts— conformance: interface satisfaction,identity()returns payTo/DID,registerOfferingrecords the catalog, and a real bound socket proving/health→200 and a paid route→402.scripts/dist-boot-smoke.ts(+package.jsonscript) — M5-pattern dist-boot smoke.Deviation flag (minor, sound)
Beyond the minimal directive ask,
start.tsalso registers the 7 offerings on the adapter at boot (registerOffering×PAID_SLUGS, price viapriceUsdFor). 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 fromPAID.Zero-touch held
verify.ts/settle.ts/challenge.ts/preHandler.ts(payment path),handlers/*(shared entrypoint — only referenced), andofferings.tsroute logic +PAIDstatic mounting — all unchanged.Verification (cite: run locally)
typecheck+lint+build— clean.vitest run— 96 tests pass (16 files; +5 new conformance tests).dist/start.jsboots throughX402Adapter;/health→200 {"status":"ok"};POST /v1/offerings/legitimacy_scan→402 {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
mainawaits Forces' explicit authorization.🤖 Generated with Claude Code