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/memwal@0.0.4`; `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) <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* justfile: point modules into offchain/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* just: fix import + .nightly-version paths after offchain/ move
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: repoint coverage, sync-docs, pre-commit deps to offchain/ paths
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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 (http@0.2.12, http@1.4.0).
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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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) <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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/workflow-dispatch@v4.0.0` — 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* add concurrency group
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…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