| name | warp-update-propose |
| description | Orchestrator skill that takes a warp-apply receipts directory and proposes each batch to its corresponding multisig signer — Safe Transaction Service via `safes/propose-warp-batch.ts` for EVM batches (auto-detects governance context: AW / Foundation / Regular / etc. per filename), Squads via `squads/propose-warp-batch.ts` for SVM batches. Persists a per-ticket proposal summary at `~/.hyperlane/proposals/<ticket-id>.yaml` so the human can track signing progress in Heimdall or Squads. |
Warp Update — Propose
Run AFTER /warp-update (Wave 5; pending) — or any other flow — has executed hyperlane warp apply and emitted a receipts directory of per-chain Safe TX Builder JSON files + Squads instruction files. This skill takes those receipts and proposes each batch to its appropriate signer, dispatching automatically by protocol + governance context.
The heavy lifting lives in two infra scripts:
typescript/infra/scripts/safes/propose-warp-batch.ts — reads combined-chainId<id>-safe<addr8>-<ts>-receipts.json, auto-detects which governance Safe owns each batch (AW / Foundation / Regular / Irregular / oUSDT), and posts via Safe Transaction Service using the Turnkey EvmLegacyDeployer signer.
typescript/infra/scripts/squads/propose-warp-batch.ts — reads <chain>-file-<ts>-receipts.json, rehydrates each tx's transaction_base58 back into Solana TransactionInstruction[], and proposes via submitProposalToSquads using the Turnkey SealevelDeployer signer.
This skill orchestrates running both, surfaces a CONFIRM gate before any propose call lands on chain, and writes a summary the human can use to find proposals in Heimdall / Squads UI.
Input
- Linear ticket ID (required, e.g.
AW-123) — namespaces the proposal summary artifact.
- Receipts directory (required) — absolute path to the dir
warp apply wrote into. Typically /tmp/<customer>-<warp-route-id>-txs/ per the /warp-update strategy file's --receipts-dir arg.
- Dry-run (optional, default false) — if set, both underlying scripts run with
--dry-run (deserialize + log without posting).
- Chain filter (optional, comma-separated) — limits which files are proposed. Passed through to both scripts as
--chain-filter.
If the ticket ID or receipts dir is missing, ask the user.
Artifact Context (Optional)
If ~/.hyperlane/update-context/<ticket-id>.yaml exists (produced by /warp-update-resolve-artifacts), auto-load it for cross-referencing. Use it to enrich the inventory output (Step 1) with the artifact types each batch covers (router / proxyAdmin / fee:routing / fee:linear / ism / hook). If the file is missing, fall back to filename-only classification — the propose scripts work either way.
Step 1: Inventory the Receipts Directory
List the JSON files in the directory and classify each by filename pattern:
ls <receipts-dir>/*.json
Two recognized patterns (from typescript/cli/src/apply/warp.ts:writeCombinedBundles and typescript/deploy-sdk/src/AltVMFileSubmitter.ts):
combined-chainId<id>-safe<addr8>-<ts>-receipts.json → EVM Safe batch (gnosisSafeTxBuilder submitter)
<chain>-file-<ts>-receipts.json → a file-submitter batch (AltVMFileSubmitter). This is NOT SVM-specific: the file submitter is used for any chain/owner without a native submitter — Sealevel/Squads, but also Cosmos/Starknet/Radix/Aleo, and EVM chains whose owner routes to file (Turnkey / timelock / custom multisig / ICA-controlled-by-EOA, per /warp-update Step 4a). Derive the protocol from the chain name, not from the -file- suffix.
For each file, derive:
- Chain name, then its protocol from the chain's metadata — do NOT assume
-file- means SVM.
- For EVM Safe batches: the 8-hex safe prefix → resolve to a full safe address by cross-referencing every
GovernanceType via the existing getSafesByGovernanceForChain(chain) helper. The first matching governance type wins.
- For a Sealevel file batch: the chain's Squads
multisigPda from squadsConfigs[chain] — these are the only file batches the Squads propose script (§4b) handles.
- For any other file batch (Cosmos/Starknet/Radix/Aleo, or an EVM
file-routed owner): there is no auto-propose path — mark it for explicit human hand-off (§4b), do not classify it as SVM.
- Tx count from the file body (parse JSON, count
transactions[] for EVM Safe batches or PrintableSvmTransaction[] for file batches).
Files matching neither pattern: list as "skipped" with a clear reason. The propose scripts also skip them, so this is just a visibility pass.
Show the user the inventory:
EVM Safe batches (3 files):
- combined-chainId1-safe3965AC3D-1234-receipts.json
→ ethereum, AW Safe (0x3965AC3D295641E452E0ea896a086A9cD7C6C5b6), 4 txs
- combined-chainId42161-safe645FE065-1234-receipts.json
→ arbitrum, AW ICA (0x645FE06507C8a188494d3E755B248a8dbF3bd875), 3 txs
- combined-chainId8453-safe8Ff4c563-1234-receipts.json
→ base, Foundation warpFees Safe (0x8Ff4c563f26db00e65bD93d9f662A51c304C09b0), 2 txs
SVM Squads batches (1 file):
- solanamainnet-file-1234-receipts.json
→ solanamainnet, Squads multisig EvptYJrjGUB3FXDoW8w8LTpwg1TTS4W1f628c1BnscB4, 2 instructions
Skipped (0 files): —
If an artifact context is loaded, append per-batch artifact-coverage info ("this batch covers router on arbitrum + proxyAdmin on arbitrum") drawn from groupedByOwner in the context.
Step 2: Cross-Reference Against Artifact Context (if loaded)
For each EVM batch:
- Look up the resolved safe address in the artifact context's
groupedByOwner table.
- Confirm the governance type matches. If the context says
Foundation but Step 1 resolved AW, surface a warning — likely a stale artifact context or a deploy.yaml drift.
For each SVM batch:
- Look up the multisig PDA in
squadsConfigs[chain].
- Cross-check against any Squads-type owners in the artifact context.
These are warnings only — they don't block the propose. If anything is genuinely wrong the human catches it at the CONFIRM gate.
Step 3: Confirmation Gate
Show the full proposal plan in a single message:
About to propose <N> batches across <M> chains for ticket <ticket-id>.
EVM:
- ethereum / AW Safe / 4 txs
- arbitrum / AW ICA / 3 txs
- base / Foundation warpFees Safe / 2 txs
SVM:
- solanamainnet / Squads (EvptYJrj…URwJ) / 2 instructions
Signers:
- EVM via Turnkey EvmLegacyDeployer (0xa7ECcdb9…d9Ba — AW + Foundation owner)
- SVM via Turnkey SealevelDeployer
Dry-run: <yes/no>
Output artifact: ~/.hyperlane/proposals/<ticket-id>.yaml
End the message with:
[CONFIRM: Propose <N> batches for <ticket-id>]
Note: [CONFIRM: ...] is a Haggis-specific harness primitive — Haggis renders it as an inline approve/reject button. In other Claude Code contexts it is just text.
Do not proceed to Step 4 until the user confirms.
Step 4: Execute Per Protocol
Run the two scripts in sequence (EVM first, then SVM). Both scripts handle per-file partial-success internally; one file failing doesn't block the rest.
4a. EVM Safe batches (skip if zero EVM files)
pnpm -C typescript/infra exec tsx scripts/safes/propose-warp-batch.ts \
--directory <receipts-dir> \
[--dry-run] \
[--chain-filter <chains>]
The script:
- Walks
combined-*-safe*-receipts.json files in the directory
- Auto-detects governance type per filename safe prefix via
getSafesByGovernanceForChain
- Initializes the Turnkey EvmLegacyDeployer signer via
createTurnkeySigner('mainnet3', TurnkeyRole.EvmLegacyDeployer)
- Wraps each file's
transactions[] into a single multiSend via createSafeTransaction(safeSdk, txData, /*onlyCalls=*/true)
- Calls the refactored
proposeSafeTransaction(...) which uses signer._signTypedData (Turnkey signs the EIP-712 hash directly — no raw key extraction)
- Prints per-file result + final summary table
Capture the script's stdout/stderr. Map each per-file result back to the inventory.
4b. SVM Squads batches (skip if zero SVM files)
pnpm -C typescript/infra exec tsx scripts/squads/propose-warp-batch.ts \
--directory <receipts-dir> \
[--dry-run] \
[--chain-filter <chains>]
The script:
- Walks
<chain>-file-*-receipts.json files
- Filters to Sealevel chains
- Initializes the Turnkey SealevelDeployer signer via
getTurnkeySealevelDeployerSigner(...)
- Rehydrates each tx's
transaction_base58 via VersionedTransaction.deserialize + TransactionMessage.decompile
- Calls
submitProposalToSquads(chain, instructions, mpp, signerAdapter, memo) per file
- Prints per-file result + final summary table
Non-Sealevel file batches are silently filtered out here. The Filters to Sealevel chains step drops any <chain>-file-* batch whose chain isn't Sealevel — so a Cosmos/Starknet/Radix/Aleo batch, or an EVM file-routed owner batch, is neither proposed nor reported as failed. Cross-check the §1 inventory: every file batch this script did not propose must be surfaced for explicit human hand-off (§6). Never report the ticket's proposals complete while a non-Sealevel file batch sits unproposed.
Capture the script's output similarly.
Per-script failure handling
If a script exits non-zero (every file failed), DO NOT abort the orchestrator — log the protocol's failure, mark all its inventory entries as failed, and continue to the other protocol. Total-orchestrator-failure (both scripts had zero successes) results in a non-zero exit at the end.
Step 5: Persist Proposal Summary
Write ~/.hyperlane/proposals/<ticket-id>.yaml:
ticket: <ticket-id>
proposedAt: '<ISO-8601 timestamp>'
receiptsDirectory: <abs-path>
dryRun: <true|false>
evm:
proposed:
- file: combined-chainId1-safe3965AC3D-1234-receipts.json
chain: ethereum
safe: '0x3965AC3D295641E452E0ea896a086A9cD7C6C5b6'
governanceType: AW
safeTxHash: '0xabc...'
txCount: 4
failed:
- file: <filename>
chain: <chain>
safe: <safe>
reason: <error>
txCount: <n>
skipped:
- file: <filename>
reason: <reason>
svm:
proposed:
- file: solanamainnet-file-1234-receipts.json
chain: solanamainnet
multisigPda: EvptYJrjGUB3FXDoW8w8LTpwg1TTS4W1f628c1BnscB4
txCount: 2
failed: []
skipped: []
The artifact is consumed by humans who need to track proposal signing progress. Heimdall's UI also surfaces the EVM proposals once Safe Transaction Service has them indexed; Squads UI surfaces the SVM ones.
Step 6: Hand Off
Tell the user:
Proposal summary saved to ~/.hyperlane/proposals/<ticket-id>.yaml.
Where to track signing:
- AW Safe + Foundation Safe proposals: visible in Heimdall (https://hyperlane.usehaggis.com) where executioners — including Haggis — can sign + execute. Heimdall polls Safe Transaction Service periodically; allow a minute for new proposals to appear.
- Squads proposals: surface in the Squads UI at https://app.squads.so/ (or chain-specific equivalent). Solana signers review + sign there.
- Customer / Regular Safe proposals: pushed to Safe Transaction Service directly. The customer signs in their own Safe app at
https://app.safe.global/transactions/queue?safe=<chain-shortname>:<safe-address>. They'll need to import the safe in their app first if it's not already there.
- Other file batches (Cosmos / Starknet / Radix / Aleo, or an EVM
file-routed owner like Turnkey / timelock / custom multisig): no auto-propose path — these were emitted as tx-file batches for manual submission. List each one (chain, owner, file path) and hand it to the owner to execute through their own signing tool; they are NOT proposed on-chain by this skill.
Notes
- Side effects: this skill POSTs proposals to Safe Transaction Service / creates Squads on-chain proposals. The actions are reversible — Safe TX Service has a delete-tx API, Squads has a cancel-proposal instruction — but human signers can move quickly. Use
--dry-run first if anything in the inventory looks off.
- Idempotency: re-running with the same receipts dir produces DUPLICATE proposals. Either delete the prior proposals (via the safes script's
safes/delete-tx.ts or squads' cancel-proposal.ts) or don't re-run the same dir.
- No GCP secrets in this skill: secret access happens inside the propose scripts, not at the orchestrator level. The skill just shells out.
- What "auto-detects governance" means: the safes script walks every
GovernanceType enum value, calls getGovernanceSafes(type)[chain], and matches the filename's 8-hex prefix against the resolved safe. A single receipts dir can mix multiple governance contexts (router via AW, fee contracts via Foundation, etc.) — the script handles them all in one run.