payments-stripe: retrofit into offchain pipeline conventions - #36
Closed
tuky191 wants to merge 5 commits into
Closed
payments-stripe: retrofit into offchain pipeline conventions#36tuky191 wants to merge 5 commits into
tuky191 wants to merge 5 commits into
Conversation
Adds the `memory-memwal` tool bundle: seven operations (`remember`, `remember_bulk`, `recall`, `ask`, `analyze`, `forget`, `stats`) against a MemWal relayer for SEAL-encrypted memory on Walrus over Sui. Pinned to relayer tag `@mysten-incubation/[email protected]`; `health_check` enforces the version against `GET /health` at runtime. Wire protocol: Ed25519-signed requests with canonical message `{ts}.{METHOD}.{path}.{sha256hex(body)}.{nonce}.{account_id}` and a fresh UUID v4 nonce per request. Boot-time URL validation (`https://` only; `MEMWAL_ALLOW_INSECURE=1` for dev). Delegate key parsed once into an `Arc<SigningKey>`; hex intermediates zeroized; `KeyValidation` `Debug` redacts the `Ok` variant. Upstream errors map to terse client reasons; full body logged at `target = "memwal::upstream"`. Process-wide `reqwest::Client` via `OnceLock` with `gzip`/`brotli`. `429` surfaced as `RateLimited { retry_after_secs }`. Polling uses exponential backoff (100 ms → 4 s within a 60 s budget) with 0–25% jitter. `MAX_BULK_ITEMS = 20`; 1 MiB cap on text inputs. A `just memwal …` module wraps server lifecycle, per-tool curl invocations, and an end-to-end `demo` recipe.
* docs: offchain tools pipeline design * docs: offchain tools pipeline implementation plan Bite-sized task plan implementing the design in docs/superpowers/specs/2026-05-19-offchain-tools-pipeline-design.md. Eight phases: repo reorg, per-tool conventions (tools.json + build.rs across 7 crates), shared Dockerfile, five composite GH actions, five callable workflows (discover/deploy/prepare/register/readiness), top-level ci.yml orchestrator, README + bootstrap notes, final verification. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * offchain: move workspace, tools, and helpers into offchain/ Prepares the repo for a future onchain/ sibling. Files relocated via git mv to preserve history. Workspace builds verified with cargo check. * onchain: add placeholder dir for future Move tools Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * justfile: point modules into offchain/ Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * just: fix import + .nightly-version paths after offchain/ move Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * ci: repoint coverage, sync-docs, pre-commit deps to offchain/ paths Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * pre-commit: cd into offchain/ before cargo invocations Recipes run with PWD = .pre-commit/ after the offchain/ move, so cargo could no longer find Cargo.toml. Each cargo-* recipe now cds into ../offchain first. Verified cargo-check and cargo-clippy succeed. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * tools/math: add tools.json, build.rs, [[bin]] declaration Establishes the per-tool convention used by the offchain CI pipeline. build.rs validates the bin/tool name match at compile time and injects TOOL_FQN_VERSION from env (default "1" for local builds). * tools/exchanges-coinbase: add tools.json, build.rs, [[bin]] declaration * tools/http: add tools.json, build.rs, [[bin]] declaration * tools/llm-openai-chat-completion: add tools.json, build.rs, [[bin]] declaration * tools/social-twitter: add tools.json, build.rs, [[bin]] declaration * tools/storage-walrus: add tools.json, build.rs, [[bin]] declaration * tools/templating-jinja: add tools.json, build.rs, [[bin]] declaration * offchain: update Cargo.lock for toml build-dep build.rs in each tool crate pulls in toml = "0.8". The lockfile update was missed across the seven per-tool commits, breaking cargo check --workspace --locked on a fresh clone. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * offchain: add shared multi-stage Dockerfile Build args PACKAGE, BINARY, TOOL_FQN_VERSION. Three stages: rust slim builder with buildkit cache mounts, debian lib-collector via ldd, and a distroless cc-debian12 final image. Smoke-tested locally with math. * ci/actions: add install-sui composite (suiup + cache) * ci/actions: add install-nexus-cli composite * ci/actions: add gcp-auth-protocol composite * ci/actions: add gcp-auth-infra composite * ci/actions: add retrigger-pr composite (signed empty commit) * ci: add offchain-tools.discover callable workflow Globs offchain/tools/*/tools.json, computes per-tool content versions (sha256(git rev-parse HEAD:<dir>/) first 8 hex chars as u32), and emits two matrices: all tools and tools changed since base-ref. A change to a shared file (Cargo.toml/Lock, Dockerfile, rust-toolchain) treats every tool as changed. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * ci: add offchain-tools.deploy callable workflow Per-tool matrix build of offchain/Dockerfile. Pushes to GHCR (ghcr.io/<repo>/<tool>:sha-<7>) and GCR (gcr.io/<infra>/nexus-tools/<tool>:sha-<7>) when dry-run=false; only builds (no push) when dry-run=true. * ci: add offchain-tools.prepare callable workflow Per-tool matrix: pulls the pushed image, runs <command> --meta, renders the Cloud Run config, generates signed-HTTP keys (idempotent on FQN set), uploads tool config to GCS. Aggregator job builds the manifest and the offchain-tools-prepare GitHub artifact consumed by register. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * ci: add offchain-tools.register callable workflow Single-job (serialized Sui state) registration. Skips already-registered FQNs; for new ones, runs the PTB, extracts OwnerCap<OverTool>, saves registration JSON to GCS, registers signing key, reconciles the toolkit-config secret with the on-chain tool_kid (sole-writer rule). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * ci: add offchain-tools.readiness PR merge gate Per-tool matrix that checks GCS for the Cloud Run config + at least one FQN registration JSON. Fails red if either is missing, with a message pointing the author at the publish workflow_dispatch. * ci: add top-level orchestrator ci.yml Wires discover/deploy/prepare/register/readiness per the trigger matrix: - PR (any base): discover, dry-run deploy on changed matrix. - PR with base testnet/mainnet: also readiness gate. - Push to main: discover, full deploy push (no chain ops). - Push to testnet/mainnet: discover, deploy, prepare, register on full matrix. - workflow_dispatch with PR#: resolves PR head, runs full chain, then retrigger-pr flips readiness green. - ci-gate aggregates job results for branch protection. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * docs: README — document offchain/ layout and deploy flow * docs: add offchain bootstrap notes for first deploy * ci/register: drop workflow-level env block vars.* lookups in a workflow-level env block are resolved before the job's environment: is bound, so NEXUS_TAG and SUI_CHANNEL would be empty. Reference vars.* directly in the steps that need them. * ci: fix PR target-ref env-resolution On pull_request events, github.ref_name is the merge-ref (e.g. "123/merge"), not the base branch. Without explicit handling, PRs to mainnet/testnet would resolve to the wrong environment in callable workflows. Prefer github.base_ref on pull_request. * ci: align matrix selection with spec + tighten dispatch gating Two changes: - Push to main now uses matrix-changed (per the design.md trigger table). matrix-all on main was a deviation from spec. - workflow_dispatch without a pr-number no longer fires prepare/register. Previously, a bare dispatch from any branch (including main) would try to register against the resolved environment. * ci/discover: quote literal path in case pattern case ${DIR}/*) was unquoted; quoting the literal prefix avoids issues with shell metacharacters in DIR (low real risk; DIR comes from dirname over a hardcoded glob, but the quoted form is cleaner). * ci/deploy: consolidate image tags into a single output Dry-run runs no longer emit an empty trailing tag line. The "Compute image tags" step writes a heredoc-encoded multi-line list, and the build-push step consumes a single output. * pre-commit: use 'set working-directory' for cargo recipes Cleaner than prefixing every cargo recipe with `cd ../offchain &&`. Recipes that need repo root (md-lint, typos, shebang checks) already cd themselves; check-container-runtime doesn't touch the filesystem. Works on just >= 1.33 (the file-level setting predates the per-recipe [working-directory] attribute from 1.38). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/deploy: build via --manifest-path to fix http crate-name ambiguity cargo build -p http was ambiguous because the workspace member `http` shares its name with transitive crates.io deps ([email protected], [email protected]). Switch the Dockerfile to use --manifest-path scoped to the tool's Cargo.toml, which uniquely identifies the workspace member. Pass TOOL_DIR as a build-arg from deploy.yml so the matrix's `dir` field flows through. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/coverage: run cargo llvm-cov from offchain/ workspace dir Cargo.toml lives under offchain/ now; running cargo llvm-cov from repo root failed to locate it. Set working-directory: offchain on the Generate Coverage step; emit lcov.info one level up so codecov-action still picks it up at ./lcov.info. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * helpers/npm-install-g: resolve txt path via script dir The previous `done < helpers/npm-install-g.txt` was relative to PWD = repo root, which broke after the helpers/ move under offchain/. Use BASH_SOURCE-derived SCRIPT_DIR so the txt is found regardless of where the script is invoked from. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * docs: gitignore docs/superpowers/, untrack planning artifacts The spec/plan/notes under docs/superpowers/ are internal brainstorming output and don't need to be in git. They follow brainstorming/writing-plans conventions (mixed ordered-list styles, fence variations) that conflict with the project's MD029/MD031/MD032 markdownlint rules, causing spurious md-lint failures. Move them to local-only and drop the README back-reference. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci: enable deploy-from-branch iteration paths Adds two ways to fire the full chain pipeline (deploy + prepare + register) against testnet/mainnet without merging to main: 1. Push trigger on iterate/testnet/** and iterate/mainnet/** git push origin HEAD:iterate/testnet/bootstrap-1 fires the chain against testnet using that branch's content. Branch prefix selects the target env. 2. workflow_dispatch input target-env (testnet|mainnet) gh workflow run ci.yml --ref <branch> -f target-env=testnet runs the chain against testnet using the dispatched ref. Requires ci.yml on the default branch for the dispatch button to be discoverable; once landed, this is the ergonomic iteration loop. Existing paths unchanged: PR (dry-run), push to main (build only), push to testnet/mainnet (full chain), dispatch with pr-number (full chain against PR head + retrigger-pr). The env-resolution chain in deploy/prepare/register's target-ref now honors (in order): PR base from resolve-target, inputs.target-env, iterate/<env>/ branch prefix, PR base on pull_request, github.ref_name. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/register: rename SUI_DEPLOYER_MNEMONIC to SUI_DEPLOYER_PK The secret holds a `suiprivkey...` bech32 private key (sui keytool export format), not a mnemonic. Rename to match reality. Same secret will drive future onchain Move deploys via `sui client publish`, so the bech32 form is the right canonical store. Workflow flow unchanged: sui keytool import converts the bech32 PK to the base64 keystore entry, then we read it back and pass to nexus conf set --sui.pk. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/prepare: bake NEXUS_TAG into each tool's Cloud Run config blob Each per-(tool, version) blob written to gs://<bucket>/<network>/offchain/tools/<tool>-v<version>.json now carries nexus_contracts_tag captured from vars.NEXUS_TAG at prepare time. Terraform reads this per-entry so the allowed-leaders mount path stays pinned to the nexus version the tool was deployed against. Upgrades become content-addressed: bump NEXUS_TAG, push, and only new deployments pick up the new tag. Existing services keep their original mount until explicitly torn down (delete the GCS blob and apply). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci: trigger tf-nexus-tools terraform apply after successful register After deploy + prepare + register complete, dispatch tf-nexus-tools' terraform.yml workflow against the `production` ref so Cloud Run services / ALB / DNS records pick up the new tool's GCS state blob. Uses the org-level GH_ACCESS_TOKEN PAT (same pattern ava-game uses for all cross-repo gh operations) because the default GITHUB_TOKEN is single-repo-scoped and cannot dispatch workflows in other repos. Fires on every chain-op path: iterate/* push, push to testnet/mainnet, and workflow_dispatch with pr-number or target-env. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/actions: drop token_format from gcp-auth-infra (use Direct WIF) token_format: 'access_token' requires google-github-actions/auth to impersonate a real service account, which we don't have configured for this WIF setup. Removing token_format makes the action use Direct WIF — the federated token is still exposed via the auth_token output, which docker/login-action consumes for `oauth2accesstoken` GCR auth. Mirrors ava-game's pattern in protocol.deploy.yml. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/deploy: split build (no auth) from push (auth) to avoid token expiry Previously a single build-push-action did both compile + push, which holds the GCP access token across the slow Rust compile. With cold buildkit caches, that compile can exceed the 1h token lifetime and the push fails at the end. Mirror ava-game's protocol.deploy.yml pattern: 1. Build (push=false) — slow Rust compile, no auth needed. 2. Login to GHCR + GCP auth — fresh tokens minted here. 3. Push (push=true) — reuses cached layers via cache-from=gha, completes within the token's 1h window. Same gha cache scope is used for cache-from/cache-to on both steps so the push step pulls the just-built layers instead of recompiling. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci: rename WIF provider from nexus-tools-protocol to nexus-tools ava-game uses two separate WIF providers per env (ava-game and ava-game-protocol). For nexus-tools we only configured a single nexus-tools provider in tf-talus-infra/params/production/github.json, and granted GCS bucket / Secret Manager IAM on the same principal in tf-nexus-tools (iam.tf + bucket.tf). The -protocol suffix was inherited from blindly mirroring ava-game's prepare/register/readiness scaffolds. Drop it; both gcp-auth-protocol and gcp-auth-infra now hit the same nexus-tools provider. Permissions verified: artifactregistry.writer/reader (tf-talus-infra) storage.objectAdmin on bucket (tf-nexus-tools/bucket.tf) secretmanager.admin prefix=nexus-tools-* (tf-nexus-tools/iam.tf) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci: add missing checkout to prepare aggregate + readiness jobs Both jobs reference local composite actions (./.github/actions/ gcp-auth-protocol) but never check out the repo, so the action's action.yml is not present on disk when GHA tries to resolve it. Per-tool prepare matrix already has checkout. Only the aggregate fan-in job and the readiness gate job were missing it. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/register: add tracing + explicit meta-file check Previous run exited 1 right after "Using coin: ..." with no visible error. set -e killed the script silently somewhere in the first for-loop iteration. Add: - set -x for full command tracing - ls of $(dirname MANIFEST) and META_DIR before the loop - cat of the manifest - explicit `[ -f $META ]` guard with directory listing on miss - per-tool echo at loop top + values dump Strictly diagnostic; once we see the failing command we can revert or keep the parts that pay for themselves. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/register: tolerate empty string in to_bytes() The exchanges-coinbase tool's `get-spot-price` FQN returns an empty .description from --meta. The to_bytes() pipe `... | grep '0x' | ...` finds no match on empty input → grep exits 1 → pipefail propagates → the surrounding `DESC_VEC=$(to_bytes "$DESC")` assignment fires set -e and kills the register script silently right after "Using coin: ...". Short-circuit to_bytes() to return empty (and exit 0) when input is empty. The resulting Move literal `vector[]` is a valid empty vector, so the PTB registers the tool with an empty description string. (Empty descriptions are still a quality issue worth fixing upstream in the tool's --meta output, but that's separate.) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * tools: thread TOOL_FQN_VERSION into every fqn!() call site Every FQN literal across the 7 tools was hardcoded as @1, ignoring the TOOL_FQN_VERSION env that build.rs + Dockerfile + CI matrix plumb through end-to-end. As a result, register-on-chain saw @1 forever and no content-addressed versioning ever reached the chain. Mirror ava-game's pattern: each `fqn!("ns.tool@1")` becomes `fqn!(concat!("ns.tool@", env!("TOOL_FQN_VERSION")))`. The fqn! macro from nexus-sdk accepts any &'static str expression, so the concat!+env! expression works inline; no per-tool TOOL_FQN const wrapper required. Verified locally: TOOL_FQN_VERSION=999 cargo build -p math --release ./target/release/math --meta | jq -r '.[].fqn' → xyz.taluslabs.math.i64.{add,mul,cmp,sum}@999 48 FQN sites rewritten across all 7 tools. No stray @1 left. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci/register: drive registration via `nexus tool register offchain --from-meta` Replaces the hand-rolled PTB loop (which drained a single COIN_ID across all FQNs and failed with InsufficientGas after ~33 calls) with one CLI invocation per FQN. The CLI acquires/releases a gas coin per call, so the loop is no longer gas-bound. Idempotent: already-registered FQNs return `{"already_registered": true}` and the loop preserves the existing GCS record. GCS layout adds `owner_cap_over_gas` alongside `owner_cap_over_tool` per FQN. Existing fields (`signing_key_hash`, `tool_kid`) survive via read-modify-write merge. Drops the PTB-only `Load Nexus objects` step and the `to_bytes`, `COIN_ID`, and `set -x` plumbing that went with it. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: treat MoveAbort in register_tool_@14 as already-registered The v1.0.0 CLI flags already-registered by string-matching the outer entry name `register_off_chain_tool_`, but EFqnAlreadyExists fires from the inner helper `register_tool_` (tool_registry.move:594), so the CLI surfaces it as a generic error. Detect that exact MoveAbort signature (function_name=register_tool_, instruction=14) and treat it as already-registered so re-runs over prior on-chain state succeed. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: pin gas + collateral coins per call to avoid drain The v1.0.0 CLI builds its gas pool with a single coin (cli/src/sui.rs:294 `with_gas(vec![one_coin])`) picked by-order=0 at startup; collateral picks by-order=1 from a fresh fetch (tool_register_offchain.rs:88). Repeated CLI invocations kept hitting the same coin and draining it below the 100M budget, yielding InsufficientGas after ~11 successful PTBs. Re-query the wallet per FQN, pin the largest coin as --sui-gas-coin and the smallest distinct coin as --collateral-coin. Fresh object refs each call, no implicit collision between the gas and collateral picks. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: instrument loop with wallet/CLI debug dumps Add per-FQN diagnostic groups so we can see what coins were picked, what the CLI saw on stdin/stderr, and what stdout came back when registration fails. Splits CLI stderr/stdout into separate buffers so the JSON output stays parseable. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: bump --sui-gas-budget to 500M to cover tool storage cost The default 100M budget covers computation + ~67M storage for a small schema (verified against the v0.8.2 spot-price register tx), but larger schemas (twitter response shapes, llm chat-completion, etc.) hit InsufficientGas at execution time with `command: None`. Wallet's largest coin is 569M MIST, leaving 500M as the safe budget ceiling. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: consolidate to 4 coins + 1 SUI budget + pre-flight balance check Heavy schemas (twitter get-tweet, llm chat-completion) need ~1 SUI of gas for storage cost — confirmed from the prior static deploy (tf-talus-nexus-v2/.../social-twitter.json:gasBudgetAmount=1e9). The deployer wallet's 22 small coins (largest 0.6 SUI) can't individually cover that budget. Three changes: - Add a Consolidate Wallet step that merges the two smallest coins iteratively until 4 remain, levelling balances to ~2.7 SUI each. - Bump --sui-gas-budget on each register call to 1 SUI. - Pre-flight balance check: if the largest available coin is below the budget, fail loudly with "wallet drained" instead of surfacing an opaque RPC error from Sui validation. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: pick any coin ≥ budget, rotate when prior drained Previously: pick the largest coin, fail with "wallet drained" if it's below budget. But the prior-largest can drop below budget after a call while other coins still have plenty — failing on it instead of rotating wastes the wallet headroom. Now: filter to coins with balance ≥ budget, take the largest of those. Only fail if no coin in the wallet meets budget. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci: trigger-tf-apply waits for dispatched run; drop retrigger-pr Two cleanups now that register is green and trigger-tf-apply successfully reaches tf-nexus-tools: - trigger-tf-apply now polls for the run it just dispatched (since `gh workflow run` returns no ID), then `gh run watch --exit-status` to inherit the conclusion. Register+apply become one atomic gate; ci-gate fails if terraform fails. - Drop retrigger-pr (job + composite action). It was a hack for the workflow_dispatch+PR case to nudge readiness back to green; no longer needed once the apply is awaited inline. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: preserve per-FQN URL path + force trailing slash Two related fixes to make registered URLs leader-compatible: 1. Each FQN has its own URL path (e.g. /i64/add, /get-tweet) baked into the meta JSON by the toolkit binary. Previously we overrode --url with a base hostname for the entire tool, losing the path and causing the leader to call the wrong endpoint. Now we extract the path from the meta URL and combine with the per-network internal hostname. 2. The leader appends `invoke` via RFC 3986 relative-URL join: /i64/add/ + invoke = /i64/add/invoke ✓ /i64/add + invoke = /invoke ✗ (basename replace) Force a trailing slash so the join produces the right endpoint. This matches the legacy v0.8 static-deploy URL convention. Smoke-tested with the math DAG end-to-end on testnet — add/cmp/mul all returned 200 from /i64/<op>/invoke. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci: replace gh run watch with the-actions-org/workflow-dispatch `gh run watch` fetches check annotations, which requires the `checks:read` permission. Fine-grained PATs cannot carry that scope (GitHub limitation), so our trigger-tf-apply job was failing with 403 even though the dispatched terraform run itself succeeded. Switch to `the-actions-org/[email protected]` — the same action ava-game uses for the same dispatch+wait pattern. It polls `actions:read` endpoints which fine-grained PATs CAN carry. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/register: skip already-registered FQNs via on-chain snapshot Take a single `nexus tool list --json` snapshot at the top of the register step and use it to skip the CLI call entirely for FQNs already on chain. Each register call costs ~1-2s even when it MoveAborts on EFqnAlreadyExists, so this turns a re-run from minutes into seconds. The existing post-call MoveAbort handler stays as a defensive net against snapshot races (e.g. a parallel run registers the same FQN between the snapshot and our call). We still verify the GCS reg file exists for skipped FQNs so the signing-key + toolkit-config steps don't run blind. Co-Authored-By: Claude Opus 4.7 <[email protected]> * tools: cargo +nightly fmt across all 48 fqn!(concat!()) sites The earlier perl rewrite of fqn!("...@1") -> fqn!(concat!("...@", env!("TOOL_FQN_VERSION"))) left long single-line invocations. Nightly rustfmt wraps them across three lines, which the pre-commit 03-cargo-nightly-fmt hook enforces. Co-Authored-By: Claude Opus 4.7 <[email protected]> * docs: README — current state of the pipeline + tool-author guide Replaces the original PR-opening README with one that reflects how the pipeline actually works after the v1.0.0 cutover: - Tool-author conventions (tools.json, build.rs, [[bin]], fqn!() threading, per-FQN URL paths). - Branch model: main (no chain ops), iterate/testnet/**, iterate/mainnet/**, workflow_dispatch with target-env / pr-number. - What "full pipeline" does end-to-end (discover → deploy → prepare → register → trigger-tf-apply). - Operational notes on deployer wallet, NEXUS_TAG pinning, stale config cleanup, and the parallel-ALB cost tradeoff. Co-Authored-By: Claude Opus 4.7 <[email protected]> * docs: README — explicit lifecycle (PR → main → promote to env) Add a Lifecycle section walking from "open a PR" through "merge to main" to "promote to a deploy env" (long-lived branch merge, iterate branch, or manual workflow_dispatch). Expanded the trigger table to also list the long-lived testnet/mainnet branches that are wired into ci.yml. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/codecov: make patch coverage informational Patch check was blocking PRs whose changes are mechanical and don't warrant new tests — e.g. the 48-site fqn!(concat!()) rewrite where every tool's `fn fqn()` body changed but is still a one-line const return. Project-level check stays strict and still gates real coverage regressions. Co-Authored-By: Claude Opus 4.7 <[email protected]> * pre-commit: check-tool-conventions enforces fqn!() threading Catches the one tool-author contract rule the build system can't: fqn!("xyz...@<literal-version>") compiles fine but ships the wrong content-version on chain (or collides with legacy @1 registrations). The hook scans offchain/tools/*/src/ for any fqn!("...@<digits>") literal and fails the commit with a pointer to the correct pattern. Also asserts each tool dir has tools.json + build.rs. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci/discover: source BLOCKED_TOOLS from repo variable Skip-list now read from the repo-level `BLOCKED_TOOLS` variable (space-separated tool_name values). Security blocks can be flipped via repo settings → Variables without a code change. Empty/unset variable means no tools blocked. Initial value (set via gh variable set): BLOCKED_TOOLS=http Blocks the http tool pending its SSRF guard. Both the on-chain unregister and this CI block keep it from re-shipping. Co-Authored-By: Claude Opus 4.7 <[email protected]> * ci: tag-driven deploy (testnet-*/mainnet-*); drop long-lived deploy branches Switch the canonical promotion path from "merge to long-lived testnet/mainnet branch" to "push a testnet-*/mainnet-* tag". Tags are immutable, so each deployment is attributable to an exact ref — no merge-strategy gotchas (squash collapsing shared ancestry and breaking future promotes), no long-lived branches to keep clean, no ephemeral promote/* branches needed. ci.yml changes: - `on.push.tags: [testnet-*, mainnet-*]` triggers the full chain. - Target-env resolution adds tag-prefix matching above the existing iterate/<env>/** rules. - Prepare gate accepts tag pushes; drops the now-unused testnet / mainnet long-lived branch matches. - ref_type guards distinguish tag pushes from same-named branches. README: branch & tag model section + lifecycle promotion step rewritten around the new flow. Co-Authored-By: Claude Opus 4.7 <[email protected]> * memory-memwal: cargo +nightly fmt across 7 fqn!(concat!()) sites Same wrap-across-three-lines pattern nightly fmt applied to the other 48 sites after the original perl rewrite. Co-Authored-By: Claude Opus 4.7 <[email protected]> * add concurrency group --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
…to add-payments-stripe-tool
Mike's PR #32 added payments-stripe at the legacy `tools/` path with per-tool deploy workflows. Main has since restructured (PR #33) so all tools live under `offchain/tools/` and share a single `offchain-tools.*` CI pipeline matrix. This branch realigns payments-stripe to the new layout so the CI picks it up automatically. Layout move: tools/payments-stripe/ → offchain/tools/payments-stripe/ Added (per the new conventions enforced by .pre-commit/14-check-tool-conventions): - tools.json (tool_name, command, environment) - build.rs (byte-identical to other tools; threads TOOL_FQN_VERSION env var via cargo:rustc-env) - Cargo.toml [[bin]] + [build-dependencies] sections Removed (legacy per-tool deploy artifacts replaced by the shared offchain-tools.{discover,deploy,prepare,register}.yml matrix): - tools/payments-stripe/deploy/{Dockerfile,cloud-run.*,register.sh} - tools/payments-stripe/paths.json (paths now come from each tool's --meta output) - .github/workflows/deploy-payments-stripe-{mainnet,testnet}.yml Source modifications: - 6 fqn!() macros across src/tools/*.rs threaded to the fqn!(concat!("...@", env!("TOOL_FQN_VERSION"))) form so the on-chain FQN version stays in sync with the content-hashed image tag CI produces. Verification: - cargo check -p payments-stripe → passes - .pre-commit/14-check-tool-conventions → ✅ Tool conventions OK
tuky191
changed the base branch from
add-nexus-tool-builder-skill
to
add-payments-stripe-tool
May 25, 2026 16:00
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
Successor to #32 (Mike Hanono's original payments-stripe addition). #32 landed at the pre-#33 layout (
tools/, per-tool deploy workflows, rootCargo.lock); this branch realigns the tool to the offchain pipeline conventions that landed in #33 so CI picks it up automatically.Mike's tool code is unchanged in intent — this is purely a layout/convention conversion.
What changed
tools/payments-stripe/offchain/tools/payments-stripe/tools/payments-stripe/paths.json(hand-maintained)--metaoutput frombootstrap!()(auto)tools.json+build.rs(byte-identical to math's)[[bin]]/[build-dependencies]fqn!()macrosfqn!("...@1")fqn!(concat!("...@", env!("TOOL_FQN_VERSION")))deploy-payments-stripe-{mainnet,testnet}.yml(per-tool)offchain-tools.{discover,deploy,prepare,register}.ymlmatrix picks the tool uptools/payments-stripe/deploy/{Dockerfile,cloud-run.*,register.sh}offchain/Dockerfileis used byoffchain-tools.deploy.ymlVerification
cargo check -p payments-stripe→ passes.pre-commit/14-check-tool-conventions→ ✅Tool conventions OKTest plan
xyz.taluslabs.payments.stripe.<endpoint>@<version>form preserved)testnet-*tag frommainpost-merge to fire the full chain pipeline against testnetOriginal PR
Closes #32 once merged.
🤖 Generated with Claude Code