Skip to content

feat(adapter): Gate P4 composition adapters and compile-time port conformance - #27

Merged
selezenart merged 5 commits into
developfrom
milestone/b-p4-adapters
Sep 7, 2026
Merged

selezenart merged 5 commits into
developfrom
milestone/b-p4-adapters

Conversation

@selezenart

Copy link
Copy Markdown
Collaborator

Summary

Makes lane B ready for Gate P4. Follow-up to #24, which merged before these two
commits landed.

docs/GATE_P4_CHECKLIST.md steps 1 and 2 replace SimulatorSettlementPort and
SimulatorAuthorizationPort in apps/worker/src/composition.ts with lane-B
implementations. Lane B exported port interfaces and pure classification logic
but no concrete classes to inject, so that step could not be executed against
what was built. These are those classes.

Scope and acceptance criteria

  • The change is limited to the stated milestone or issue.

  • Acceptance criteria are listed and satisfied.

  • No unrelated cleanup is included.

  • ArcSettlementAdapter and PrivyAuthorizationAdapter in
    @oneshot/privy-adapter, conforming to the canonical result shapes in
    packages/contracts/src/ports.ts, declaring contractVersion = '1.0.0' and
    network = 'eip155:5042002' as composeWorker verifies.

  • src/p4-conformance.ts: a compile-time guard mirroring the worker's port
    interfaces and statically asserting assignability.

  • docs/settlement/GATE_P4_LANE_B_READINESS.md: injection recipe, the required
    WalletProvider surface, and the naming disagreement below.

  • Corrects the lane boundary test, which wrongly forbade @oneshot/contracts.

Product and security invariants

  • Tenant isolation remains fail-closed.
  • Sponsor authorization, auditability, and daily caps remain enforced where applicable.
  • Recipients cannot modify sponsor controls or access sponsor-only data.
  • No secret, token, production identifier, or personal data is committed.
  • Any non-applicable invariant is explained below.

Invariant notes:

No durable state or tenant boundary here; the adapters are injected into A's
worker, which owns both. What they preserve:

  • Doubt defaults to POSSIBLY_SUBMITTED. DEFINITELY_NOT_SUBMITTED is
    returned only for a proven pre-broadcast transport failure, a local rejection
    before sending, or an on-chain revert. An unreadable receipt, an absent
    receipt, and a receipt that does not prove our Transfer are all possibly
    submitted.
  • A successful receipt is not confirmation. CONFIRMED needs exactly one
    matching Transfer from the configured token, to the expected recipient, for
    the exact amount.
  • Drift reports UNAVAILABLE, not DENIED. A drifted policy makes every
    answer untrustworthy rather than making one intent unauthorized.
  • Native value is always zero, asserted by test.
  • The idempotency key is stable per Business Intent, so a retry under a new
    attempt id derives the same key and a duplicate collapses provider-side.
  • Provider data is validated at the boundary. transferLogIndex is checked
    before it reaches a CONFIRMED.

Nothing here opens a socket or reads a credential: the provider is an injected
interface, so the whole path stays exercisable offline.

Validation

pnpm install --frozen-lockfile : PASS (11 workspace projects)
pnpm lint                      : PASS
pnpm typecheck                 : PASS
pnpm build                     : PASS
pnpm exec vitest run           : PASS, 489 tests across 34 files
pnpm format:check              : PASS

The conformance guard was verified to bite rather than pass vacuously: renaming
submit makes tsc report

error TS2344: Type 'ArcSettlementAdapter' does not satisfy the constraint
'WorkerInjectableSettlementPort'.

Independent review evidence

Gate A

  • Reviewed tree: 1228c2f02b947cbffbd2bac8205c0bb77f736fb0 (commit 72f643d)
  • Reviewer tool: free-pi-cli 0.2.19
  • Reviewer model: glm-5.3-flash
  • Verdict: PASS (INTEGRATION_PASS), blocking findings None

Asked specifically whether the adapters match what composeWorker expects,
whether the conformance mirror can pass vacuously, whether any error path that
could have broadcast returns DEFINITELY_NOT_SUBMITTED, and whether allowing
@oneshot/contracts is justified under milestones/CONTRACTS.md.

Non-blocking finding, fixed in 93fe9f9 after the reviewed tree:
transferLogIndex reached CONFIRMED straight from provider data with no
bounds check. Now validated at the boundary, failing closed to
POSSIBLY_SUBMITTED — the settlement may well have happened, so claiming it
did not would be wrong, and a malformed CONFIRMED would throw a contract
error inside the worker.

Evidence caveat: the reviewer's literal VERDICT: line was lost to terminal
viewport truncation. The captured evidence is REVIEWED_TARGET: milestone/b-lane-integration @ 72f643d, an empty blocking-findings section, and
the terminal INTEGRATION_PASS token.

Decision needed from you and Coder A

The P4 checklist reserves package slots @oneshot/adapter-arc and
@oneshot/adapter-privy. The merged packages are @oneshot/arc-adapter and
@oneshot/privy-adapter, already referenced by pnpm-workspace.yaml, the root
tsconfig.json, fixtures, and docs. The classes have the names the
checklist expects; the packages do not.

Both adapters live in @oneshot/privy-adapter rather than split across two
packages, because settlement is a Privy wallet action carrying an Arc transfer;
splitting would put half of one call path in each package.

This needs an explicit decision rather than being discovered during composition.

Risk and rollback

  • The adapters are exercised only against injected fakes. Privy and Arc claims
    stay NOT VERIFIED; docs/settlement/LIVE_EVIDENCE.md still reads
    LIVE_NOT_RUN, and the live gaps are listed in
    COMPATIBILITY_MANIFEST.liveGapsForGateP4.
  • p4-conformance.ts mirrors A's interfaces by hand. If A changes them, this
    fails the build — which is the intent — but the mirror must then be updated
    deliberately as an agreed contract change.

Rollback: additive. Revert the commits or close the PR; nothing else imports
these adapters yet.

Human merge

  • A human owner has reviewed the evidence and will perform the merge.

…ormance

Gate P4 replaces the simulator ports in apps/worker/src/composition.ts with
lane-B implementations. Until now lane B exported port interfaces and pure
classification logic but no concrete classes to inject, so the checklist's
replacement step could not be executed against what was built.

Adds ArcSettlementAdapter and PrivyAuthorizationAdapter conforming to the
canonical result shapes in @oneshot/contracts, both declaring the 1.0.0
contract version the worker verifies, and the settlement adapter declaring the
enabled Arc network.

They satisfy the worker's interfaces structurally rather than by import.
apps/worker is Coder A's package and the lane rule forbids importing another
owner's implementation, so p4-conformance.ts mirrors those interfaces and
statically asserts assignability, including the contractVersion and network
fields composeWorker reads. The guard was verified to bite: renaming submit
makes tsc report that ArcSettlementAdapter no longer satisfies the port. A
change to A's interfaces now fails the build here instead of surfacing during
composition.

Corrects the lane boundary test, which wrongly forbade @oneshot/contracts.
That package is the sanctioned cross-lane seam named by milestones/CONTRACTS.md,
not an owned implementation. Implementation packages remain forbidden.

Both adapters take their provider as an injected interface, so nothing opens a
socket or reads a credential and the whole settlement path stays exercisable
offline. Behaviour preserves the lane invariants: drift reports UNAVAILABLE
rather than DENIED because a drifted policy makes every answer untrustworthy
rather than making one intent unauthorized; doubt defaults to
POSSIBLY_SUBMITTED, with DEFINITELY_NOT_SUBMITTED reserved for a proven
pre-broadcast failure or an on-chain revert; a successful receipt without
exactly one matching Transfer is not confirmation; and native value is always
zero.

submit takes no SettlementContext. Fewer parameters still satisfy the port, and
submission identity derives from the Business Intent so a retry under a new
attempt id still produces the same idempotency key.

Documents the injection recipe, the required WalletProvider surface, and the
package naming disagreement: the checklist reserves @oneshot/adapter-arc and
@oneshot/adapter-privy, while the merged packages are @oneshot/arc-adapter and
@oneshot/privy-adapter. That needs an explicit decision rather than being
discovered during composition.
The review noted that transferLogIndex reaches a CONFIRMED result straight
from provider data with no bounds check. parseSettlementResult would reject a
bad value downstream, but that surfaces as a thrown contract error inside the
worker rather than a result the intent can be reconciled from.

Validated at the boundary instead, per the invariant that untrusted external
data is checked where it enters. An invalid index fails closed to
POSSIBLY_SUBMITTED: the settlement may well have happened, so claiming it did
not would be wrong, and claiming a malformed CONFIRMED would break the caller.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
oneshot 62d7ea7 Sep 07 2026, 10:04 PM

The Gate P4 checklist and composition manifest name @oneshot/adapter-arc and
@oneshot/adapter-privy. Those packages do not exist. The merged packages are
@oneshot/arc-adapter and @oneshot/privy-adapter, already referenced by
pnpm-workspace.yaml, the root tsconfig, the fixtures, and the lane docs.

Nothing imports the reserved names, so this was documentation drift rather than
a build failure waiting to happen. Corrected the docs rather than renaming the
packages: the rename would touch every consumer and every import for a cosmetic
gain, right as composition begins.

The class names in the replacement instructions were already correct. Adds a
note that both adapters ship from one package, so that an Arc settlement
adapter living in privy-adapter does not read as a mistake: settlement is a
Privy wallet action carrying an Arc transfer, and splitting it would put half of
one call path in each package.

Also refreshes the verification-commands paragraph, which said the lane B
packages keep their own npm toolchains and are checked by a dedicated
settlement-packages CI job, and that consolidation may happen at P4. That
consolidation already happened ahead of P4, because those npm lockfiles broke
pnpm install --frozen-lockfile on develop. The job is gone and the packages are
covered by the root runs.

Edited with the repository owner's explicit permission, since these files sit
in Coder A's lane.
@selezenart
selezenart merged commit 4295bd9 into develop Sep 7, 2026
4 checks passed
@SuPuHe
SuPuHe deleted the milestone/b-p4-adapters branch September 8, 2026 22:09
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