Repository navigation
feat(adapter): Gate P4 composition adapters and compile-time port conformance - #27
Merged
Merged
Conversation
…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.
Deploying with
|
| 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.
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.
Summary
Makes lane B ready for Gate P4. Follow-up to #24, which merged before these two
commits landed.
docs/GATE_P4_CHECKLIST.mdsteps 1 and 2 replaceSimulatorSettlementPortandSimulatorAuthorizationPortinapps/worker/src/composition.tswith lane-Bimplementations. 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.
ArcSettlementAdapterandPrivyAuthorizationAdapterin@oneshot/privy-adapter, conforming to the canonical result shapes inpackages/contracts/src/ports.ts, declaringcontractVersion = '1.0.0'andnetwork = 'eip155:5042002'ascomposeWorkerverifies.src/p4-conformance.ts: a compile-time guard mirroring the worker's portinterfaces and statically asserting assignability.
docs/settlement/GATE_P4_LANE_B_READINESS.md: injection recipe, the requiredWalletProvidersurface, and the naming disagreement below.Corrects the lane boundary test, which wrongly forbade
@oneshot/contracts.Product and security invariants
Invariant notes:
No durable state or tenant boundary here; the adapters are injected into A's
worker, which owns both. What they preserve:
POSSIBLY_SUBMITTED.DEFINITELY_NOT_SUBMITTEDisreturned 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.
CONFIRMEDneeds exactly onematching Transfer from the configured token, to the expected recipient, for
the exact amount.
UNAVAILABLE, notDENIED. A drifted policy makes everyanswer untrustworthy rather than making one intent unauthorized.
attempt id derives the same key and a duplicate collapses provider-side.
transferLogIndexis checkedbefore 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
The conformance guard was verified to bite rather than pass vacuously: renaming
submitmakestscreportIndependent review evidence
Gate A
1228c2f02b947cbffbd2bac8205c0bb77f736fb0(commit72f643d)free-pi-cli0.2.19glm-5.3-flashINTEGRATION_PASS), blocking findings NoneAsked specifically whether the adapters match what
composeWorkerexpects,whether the conformance mirror can pass vacuously, whether any error path that
could have broadcast returns
DEFINITELY_NOT_SUBMITTED, and whether allowing@oneshot/contractsis justified undermilestones/CONTRACTS.md.Non-blocking finding, fixed in
93fe9f9after the reviewed tree:transferLogIndexreachedCONFIRMEDstraight from provider data with nobounds check. Now validated at the boundary, failing closed to
POSSIBLY_SUBMITTED— the settlement may well have happened, so claiming itdid not would be wrong, and a malformed
CONFIRMEDwould throw a contracterror inside the worker.
Evidence caveat: the reviewer's literal
VERDICT:line was lost to terminalviewport truncation. The captured evidence is
REVIEWED_TARGET: milestone/b-lane-integration @ 72f643d, an empty blocking-findings section, andthe terminal
INTEGRATION_PASStoken.Decision needed from you and Coder A
The P4 checklist reserves package slots
@oneshot/adapter-arcand@oneshot/adapter-privy. The merged packages are@oneshot/arc-adapterand@oneshot/privy-adapter, already referenced bypnpm-workspace.yaml, the roottsconfig.json, fixtures, and docs. The classes have the names thechecklist expects; the packages do not.
Both adapters live in
@oneshot/privy-adapterrather than split across twopackages, 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
stay
NOT VERIFIED;docs/settlement/LIVE_EVIDENCE.mdstill readsLIVE_NOT_RUN, and the live gaps are listed inCOMPATIBILITY_MANIFEST.liveGapsForGateP4.p4-conformance.tsmirrors A's interfaces by hand. If A changes them, thisfails 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