Repository navigation
feat(arc-adapter): Arc profiles, money, config, and readiness probe (B01 core) - #11
Merged
Merged
Conversation
Implements the B01 core for Coder B as a standalone package with its own install, lint, typecheck, test, and build commands, so the packet closes without root workspace composition or any credential. Arc Testnet is the only enabled deployment profile and pins the constants frozen in milestones/CONTRACTS.md: chain 5042002, eip155:5042002, the USDC interface at 0x3600...0000, and six-decimal precision. The Mainnet profile is structurally present but carries no chain ID, RPC, explorer, or token value at all. develop@d6758dd removed a previously asserted mainnet chain and launch date as unverified guesses, and a test now asserts no profile contains any endpoint so they cannot creep back in. RPC and explorer URLs are operator configuration rather than profile constants for the same reason. Money is integer atomic units and bigint only. parseAmountAtomic rejects rather than normalizes non-canonical input, because accepting both "1" and "01" would let two strings describe one amount and break the payload fingerprint the duplicate-settlement guard depends on. Display formatting is string slicing, never division. The readiness probe separates UNAVAILABLE from MISMATCH. A wrong chain ID or a token address holding no bytecode is a permanent, human-fix condition and must never be retried into working; an unreachable endpoint may resolve on its own. Both block readiness, and the probe is driven through a small RPC interface so it runs fully offline. Configuration classifies every variable as public, secret, optional, or human-only, and fails closed on a missing, malformed, or contradictory value. Enabling a mainnet profile requires pinned values, an enabled flag, and explicit human authorization together; authorization alone is refused. Redaction is deny-by-default on key name and on secret-shaped content, so an unclassified new provider field is redacted rather than leaked. Key matching strips case and separators after a test caught x-api-key slipping past an apikey pattern. Toolchain pinned by the B01.1 compatibility spike: Node >=22.12, TypeScript 5.9.3, viem 2.56.3, Vitest 5.0.0, ESLint 9.39.1. TypeScript 7.0.2 is rejected because typescript-eslint constrains typescript to <6.1.0 at every published version.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
oneshot | 798f00e | Sep 07 2026, 01:35 PM |
Completes B01.2 verification and the B01.3 spike against primary sources, and adds the Privy policy scope model with a deny fixture per constrained dimension. Source URLs and evidence are recorded in .agent/research/20260907-b01-arc-privy-verification.md. Arc verification. Chain 5042002 and the USDC interface at 0x3600...0000 are confirmed correct against docs.arc.io, so no frozen value changed. The check did surface one gap: Arc's native gas asset and its USDC ERC-20 interface are both named USDC but use different precision, 18 decimals for gas and 6 for the ERC-20 interface, a factor of 10^12 apart. Profiles now carry nativeDecimals separately from tokenDecimals, and the readiness probe reports MISMATCH if a profile equates them or declares native decimals as anything but 18. This is the separation B01.2 asks for between settlement amounts and gas accounting. The verification also confirmed that the RPC hostname guessed in the first draft was wrong as well as against policy: Arc publishes four testnet endpoints under arc.io, not one under arc.network. Endpoints stay operator configuration. Memo policy spike, B01.3. Recorded NOT_SUPPORTED. Privy policy conditions decode the arguments of the function the wallet actually calls. On the Arc Memo path that is the Memo function, so the forwarded transfer's recipient and amount sit in an inner call no documented condition reaches. B01.3 permits SUPPORTED only with deny fixtures for every wrong dimension, and those two have none available, so claiming support would be false. v1 settles with a direct USDC ERC-20 transfer, which is fully constrainable, and memo_id stays unused. evaluateScope mirrors the remote policy locally with an independent deny reason per dimension: wrong chain, wrong destination contract, non-zero native value, wrong method, wrong recipient, wrong amount, and malformed calldata. The duplication is deliberate. A Privy policy lives in provider configuration and can drift, and the local check can only refuse, never grant. Calldata length is checked exactly so appended bytes cannot ride through a selector prefix match. Also publishes the settlement-config-v1 handoff artifact and generates .env.example from the config schema so the two cannot drift.
…narios Adds the B01 readiness simulator so the probe can be exercised across every outcome with no network and no credential, which is what lets the packet close independently of live Arc access. The simulator covers the healthy path, the two permanent misconfigurations (wrong chain, and a token address holding no code, including a null code answer), and the transient faults (unreachable endpoint, partial availability where the chain reads but the token read fails, and an oversized provider error body). Tests assert the distinction the probe exists to make: wrong chain and missing bytecode set hasMismatch, while unreachable endpoints do not. Misclassifying a transient fault as a mismatch would send an operator hunting a configuration bug that does not exist; misclassifying a mismatch as transient would invite a retry loop against the wrong chain. An unknown scenario name throws rather than returning a healthy probe, per the repository rule that simulators never silently default an unknown enum to a successful or retryable result.
…network-compatibility
This was referenced Sep 7, 2026
6 of 9 tasks
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
Implements the core of B01 — SDK and Arc network compatibility for the Coder B
lane as a standalone package,
packages/arc-adapter.It pins the toolchain, encodes the Arc deployment profiles, defines the
settlement-config-v1variable surface, and adds a fail-closed readiness probeand a sanitized redaction boundary. The package installs, lints, typechecks,
tests, and builds on its own, with no root workspace composition and no
credential, so the packet closes independently as the lane's independence model
requires.
This is a partial B01. See "Anything a reviewer should know" below.
Scope and acceptance criteria
Acceptance criteria addressed, per
milestones/coder-b/B01-sdk-network-compatibility.md:ESLint tested together in an isolated package. Rejected combination recorded
below.
present but valueless; settlement amounts separated from display formatting;
no silent network, token, or precision override.
optional / human-only, with placeholder-only example rendering.
identity-format validation that prints no credential, and a hard split
between "unavailable" and "identity mismatch".
assertion helper usable as a test guard on committed fixtures.
Not in this PR: B01.3 (Memo and policy compatibility spike) and the
testkit-settlementfixture package.Product and security invariants
Invariant notes:
No durable state, no domain table, no migration, and no transaction submission
is touched, so the tenant-isolation and sponsor-control rows are not applicable
to this diff. The invariants this change does carry:
bigint. There is nonumberarithmetic on a monetary value and no parserfrom
number. Comparison against the spending cap isbigint, so an amountabove the cap cannot slip through by float rounding.
"01","1.0","+1","1e6"and
" 1"are errors. Accepting them would let two distinct strings describeone amount and break the payload fingerprint that the duplicate-settlement
guard depends on.
profile requires pinned values, an enabled flag, and explicit human
authorization together; authorization alone is refused, and the mainnet
profile has no values to authorize.
ONESHOT_PRIVY_APP_SECRETis classifiedsecretand is read bynothing in this package. Redaction is deny-by-default on both key name and
value shape, so an unclassified new provider field is redacted rather than
leaked.
Validation
Commands and results:
Independent review evidence
Gate A — exact candidate tree before push
Base commit SHA:
9dc541d08daf4e9a9c338c562fb1fbe6ac6be04a(branch point)Candidate tree SHA:
5e49be7a785149da36b40861ad55a6ad6e8e2a26Candidate commit SHA:
a1d8599afef17e668afe26fc26cccca1b7db4103Reviewer tool:
free-pi-cliReviewer model:
glm-5.3-flashVerdict: PASS, but bound to a superset tree, not this exact head.
Findings or residual risks: a FreePi review returned
VERDICT: PASSwith zeroblocking findings over tree
3351a19e455024cf65de3f9fb400d8eac5df4080, whichis the B02 branch containing B01 and B02 together. Every file in this PR was
reviewed, but that tree is not this PR's head tree
(
b7b6942d46f0cc05d4c5784eee49ed68300777d0), so the exact-tree binding that.agent/IMPLEMENTATION_LOOP.mdrequires is not satisfied. Treat the contentas reviewed and the tree identity as unbound.
The reviewed tree equals the committed tree.
Gate B — exact remote PR head
Reviewer tool:
free-pi-cliVerdict: NOT RUN
Gate B reviewed the current remote head and matches Gate A's approved tree.
Agent policy / repository-policyand all applicable CI checks pass (repository-policy,Markdown and Mermaid,ESLint and TypeScriptall pass;Workers Builds: oneshotfails ondeveloptoo and is pre-existing).Risk and rollback
Residual risks:
Chain
5042002and the USDC interface0x3600...0000are taken frommilestones/CONTRACTS.md, not from a primary source. B01.2 requires theprimary-source check before these are treated as pinned. The readiness probe
is what catches a wrong value at runtime, but the constants themselves still
need confirming.
eslint@9.39.1already reports as outside its supported window.IDENTIFIER_SHAPEfor Privy wallet and policy IDs is a conservative shapeguess. B02 should replace it with the real documented format.
Rollback: additive new package on a short-lived branch. Revert the commit or
close the PR. No migration, deployment, shared configuration, or durable state
is involved.
Human merge
Anything else a reviewer should know
Base is
develop, notmain..agent/AGENTS.mdand.agent/IMPLEMENTATION_LOOP.mdforbid targetingmainfor feature work.Two judgment calls worth checking:
No endpoints in the profile table. The first draft of
profiles.tscontained invented
rpcUrlandexplorerUrlhostnames. That is exactly theguessed default that
develop@d6758ddremoved from the plan, so they weredeleted. RPC and explorer are now operator configuration, and
profiles.test.tsasserts that no profile contains anyhttp(s)://stringso they cannot creep back.
TypeScript 7 rejected. TypeScript
7.0.2is published, buttypescript-eslintconstrainstypescriptto>=4.8.4 <6.1.0at everypublished version including the latest
8.69.0. Choosing TS 7 would meandropping type-aware linting on the package that validates chain identity and
money. Pinned TypeScript
5.9.3instead.One bug the tests caught: the redaction key matcher originally missed
x-api-key, because the pattern list heldapikeyandapi_keybut not thehyphenated spelling. Key matching now strips case and separators before
comparing, so one pattern covers every spelling a provider might use.