Jovian Upgrade Pipeline — Orchestrator Protocol
§1 ORCHESTRATOR RULE (read first, read every run)
You are the orchestrator. You do NOT run anvil, cast, forge, bootstrap.sh,
exec-*.sh, just sign, or any heavy bash yourself. Your only direct actions are:
TodoWrite— create and update the fixed checklist.Task(subagent_type="oh-my-claudecode:executor", model="sonnet", ...)— dispatch ONE subagent per TODO, passing the full §CONTEXT_BLOCK and the CARD text for that TODO.mcp_question— only at checkpoint gates (interactive mode) and final cleanup gate.- A short final report summarising what happened.
Everything else is delegated. This keeps your context clean and prevents drift.
§1.1 Hard rules (non-negotiable)
- Localhost only. Every subagent runs against
http://127.0.0.1:$RPC_PORT. If the user requests real mainnet/sepolia execution → REFUSE, print the commands, explain why. - The TODO list is fixed. You MUST create the full list in §THE PROTOCOL at the start of every run, verbatim, in order, no merges, no renames, no omissions.
- No skipping. A "skippable" step still appears as a TODO; the subagent either
executes it or returns
STATUS: SKIPwith a reason. Silently dropping a TODO is a binding violation. - Subagent per TODO. You never batch multiple TODOs into one subagent. You never
run two TODOs in parallel (the pipeline is sequential by construction).
5a. Evidence-gated completion (subagent TODOs). You only mark a TODO executed by a
subagent
completedwhen the subagent's report contains theREQUIRED EVIDENCEblock named by that TODO's card. If not, re-dispatch with a corrective prompt. 5b. Orchestrator-executed TODOs. The orchestrator-only TODOs — T14/T23/T29/T34 checkpoint gates (CP0..CP3), T40 CP4 gate, T42 cleanup gate — have NO subagent. Their evidence is whatever the orchestrator itself emits: themcp_questionresponse value (interactive) or a one-line autonomous decision string. Rule 5a does not apply to these — you mark them completed the moment you record the decision. Note: T00 is NOT in this list — T00 does dispatch a minimalhaikusubagent to register the cleanup plan, then stayspendingthroughout the pipeline per rule 6 below (it's completed at the end of T41, not T00's own subagent return). - Cleanup TODO exists BEFORE anvil starts. TODO
T00is created in the firstTodoWritecall as statuspendingand stayspending(NOTin_progress, NOTcompleted) throughout the entire pipeline. It is the visible sentinel that cleanup is owed. Mark itcompletedonly at the end of T41 (after cleanup succeeded). Multiple simultaneouspendingitems is allowed; TodoWrite only requires at most onein_progressat a time. - No double-running. If a script fails mid-pipeline, the only recovery is
cleanup + re-fork from scratch. Never re-run a partial
bootstrap.shorexec-*.sh. - Checkpoints are four TODOs, not one. Every checkpoint (CP0..CP4) is represented
by four TODOs:
.1queries,.2PASS/FAIL table,.3x-ray offer,.4gate. You may not fold them together.
§1.2 Recovery protocol on FAIL
When a subagent returns STATUS: FAIL, the orchestrator's response depends on which
TODO failed:
| Failed TODO range | Recovery |
|---|---|
| T01..T07 | Fix the root cause (wrong path, missing tool, busy port), re-dispatch the same TODO. No re-fork needed. |
| T08..T10 | Bootstrap/plant partial state. Jump to T41 cleanup, re-fork from scratch at T04. |
| T11..T14 (CP0) | If validation fails (not a query error), the fork is bad. Jump to T41 cleanup, re-fork from scratch. |
| T15..T17 (sign) | Fix signing env (LOCAL_RPC_URL, mise, TEST_PK), re-dispatch. No re-fork needed. |
| T18 / T24 (sim) | Only FAIL on actual simulate failure. On SKIP (pre-flight), record reason, continue. |
| T19 / T25 / T30 | Exec failure (revert). Jump to T41 cleanup, re-fork from scratch. Do NOT retry in place. |
| T20..T23 (CP1) | Post-v4 invariant broken. Jump to T41 cleanup, re-fork from scratch. |
| T26..T29 (CP2) | Post-v5 invariant broken. Jump to T41 cleanup, re-fork from scratch. |
| T31..T34 (CP3) | Post-succ-v2 invariant broken (esp. SG_ASR == OLD_ASR). Jump to T41 cleanup, re-fork from scratch. |
| T35 (Phase 7) | Comparison table failure — pipeline already done, report and proceed to T36. |
| T36..T40 (CP4) | Basefee failure — cleanup, investigate, re-fork if the operator wants to retry. |
| T41 (cleanup) | Manual cleanup required. Log a warning in final summary so the operator knows. |
"Re-fork from scratch" means: reset the TodoWrite list, re-create T00..T42 from the fixed template, and start over at T01. Do NOT patch the TodoWrite list in place.
§1.3 CORE INVARIANTS (memory anchor — read after §1.1)
A one-glance summary of things that MUST be true of every run, regardless of NETWORK or MODE. If any of these is not true at any point, the pipeline is FAILED.
| # | Invariant |
|---|---|
| 1 | Every bash command targets http://127.0.0.1:$RPC_PORT — never a real network RPC. |
| 2 | The TodoWrite list has exactly 43 items (T00..T42), created in one call after §2 mode detection. |
| 3 | T00 is pending the entire run, not in_progress, and completes only after T41 cleanup succeeds. |
| 4 | Exactly ONE subagent is in_progress at a time. Sequential dispatch. No parallel TODOs. |
| 5 | Checkpoint gates (T14, T23, T29, T34, T40, T42) are orchestrator-only — NO subagent dispatch. |
| 6 | X-ray offers (T13, T22, T28, T33, T39) are their own TODOs, each a subagent that ONLY builds text. |
| 7 | Simulations (T18, T24) are mandatory unless a documented pre-flight reason forces SKIP. |
| 8 | SKIP TODOs still dispatch a subagent (or are orchestrator-only); silent dropping = binding violation. |
| 9 | Dynamic slots (OPCM_V4, OPCM_V5, NEW_CSC, NEW_ASR) are filled by specific TODOs per §3.2. |
| 10 | Real DEPLOYER_PK flows forward in prompts; evidence reports redact it as 0x***. |
| 11 | Every exit path — success, failure, user stop, crash — lands at T41 cleanup. |
| 12 | On CP3 FAIL (SG_ASR == OLD_ASR) or any other irrecoverable failure: cleanup, re-fork from scratch. |
§1.4 What goes to a subagent vs. to main agent
| Action | Main agent | Subagent |
|---|---|---|
TodoWrite |
✅ | ❌ |
| Dispatching Task | ✅ | ❌ |
mcp_question (checkpoint gate) |
✅ | ❌ |
mcp_question (prerequisites) |
✅ | ❌ |
anvil, cast, forge, jq, lsof |
❌ | ✅ |
bootstrap.sh, mock-*.sh, exec-*.sh |
❌ | ✅ |
just sign, just simulate |
❌ | ✅ |
| Reading logs / verifying state | ❌ | ✅ |
| Rendering PASS/FAIL table | ❌ | ✅ |
§2 MODE DETECTION (Step 0 — first action of every run)
Detect mode by scanning your invoking prompt for any of:
non-interactive, background task, autonomous, headless, no questions,
do not ask, just run it, make decisions and proceed.
- Match → Autonomous mode.
- No match AND you can call
mcp_question→ Interactive mode.
Announce the mode in your first message: Detected mode: Interactive|Autonomous.
§2.1 Autonomous mode — required details
In autonomous mode you MUST validate every required detail BEFORE any TodoWrite, BEFORE any subagent dispatch. Missing detail → emit the missing-detail quit block from §2.2 and stop.
Required for both modes:
| # | Detail | Example |
|---|---|---|
| 1 | Execution mode | A (mocked) or B (real sigs) |
| 2 | CELO_ROOT |
e.g. /path/to/Celo (your local checkout) |
| 3 | Network | mainnet, sepolia, chaos |
| 4 | Fork block | numeric or alias (see §APPENDIX B) |
| 5 | Upstream RPC URL | Tenderly / archive Alchemy / archive Infura |
| 6 | Anvil port | default 8545 |
| 7 | Deployer PK | derives to network-specific SENDER (§APPENDIX B) |
Required in Mode A only:
| # | Detail | Notes |
|---|---|---|
| 8a | Succinct config filename | opsuccinctfdgconfig.<network>.json under $SUCCINCT_REPO/contracts/ — OR generate via just fetch-fdg-config. See §3.4 for repo resolution and §F.1 for per-network config references. |
| 9a | include_basefee |
MUST be no — Mode A rejects yes |
Required in Mode B only:
| # | Detail | Notes |
|---|---|---|
| 8b | include_basefee |
yes or no |
Optional with defaults:
- Sibling repo paths → default: sibling directories of
$CELO_ROOT(e.g.$CELO_ROOT/../Optimism,$CELO_ROOT/../Optimism2,$CELO_ROOT/../SuperchainOps,$CELO_ROOT/../CeloSuperchainOps,$CELO_ROOT/../Succinct,$CELO_ROOT/../Succinct2,$CELO_ROOT/../x-ray) SUCCINCT_ROOT_MAINNET→ default$CELO_ROOT/../Succinct2(see §3.4)- Mocked signer addresses/PKs → defaults in §APPENDIX B
§2.2 Missing-detail quit block (autonomous mode)
AUTONOMOUS MODE FAILURE: missing required detail(s).
Missing:
- <detail name>: not provided or ambiguous
- ...
This skill runs in autonomous mode and cannot ask the user. Re-invoke with all
required details explicitly passed in the prompt.
Aborting before any bash commands. No cleanup needed (nothing was started).
§2.3 Interactive mode — prerequisites collection
Dispatch one subagent (T01, see §THE PROTOCOL) that asks the user for the missing
details via printed questions (the subagent does NOT use mcp_question — only the
main orchestrator does). The subagent returns a filled §CONTEXT_BLOCK as evidence.
§3 THE CONTEXT BLOCK (build once, then grow across TODOs)
Every subagent dispatch starts with the same CONTEXT_BLOCK. You assemble it once after T01 completes and mutate a single slot only after it is explicitly discovered or written by a prior TODO. Never let a subagent "guess" a value.
CELO_ROOT=<path>
OP_ROOT_V4=<path> # default $CELO_ROOT/../Optimism (overridable)
OP_ROOT_V5=<path> # default $CELO_ROOT/../Optimism2
SUPERCHAIN_OPS=<path> # default $CELO_ROOT/../SuperchainOps
CELO_SUPERCHAIN_OPS=<path> # default $CELO_ROOT/../CeloSuperchainOps
SUCCINCT_ROOT=<path> # default $CELO_ROOT/../Succinct
XRAY_ROOT=<path> # default $CELO_ROOT/../x-ray (clone of https://github.com/celo-org/x-ray)
NETWORK=<mainnet|sepolia|chaos>
MODE=<A|B>
INCLUDE_BASEFEE=<yes|no> # Mode B only; Mode A forces 'no'
RPC_PORT=<port> # default 8545
RPC_URL=http://127.0.0.1:<port>
LOCAL_RPC_URL=http://127.0.0.1:<port> # SuperchainOps reads this
XRAY_PORT=<port> # default 8080 (collision check at T07)
UPSTREAM_RPC_URL=<archive RPC> # see §3.1 redaction policy
FORK_BLOCK=<number>
FORK_CHAIN_ID=<1 | 11155111>
DEPLOYER_PK=<0x...> # see §3.1 handling policy
SENDER=<network-specific; §APPENDIX B.6>
PARENT_SAFE=<§APPENDIX B.3>
CLABS_SAFE=<§APPENDIX B.3>
COUNCIL_SAFE=<§APPENDIX B.3>
SYSTEM_CONFIG=<§APPENDIX B.4 or B.4b>
DGF=<§APPENDIX B.4 or B.4b>
OLD_ASR=<§APPENDIX B.4 or B.4b>
OLD_CSC=<§APPENDIX B.4 or B.4b>
SUCCINCT_IMPL_ADDR=<§APPENDIX B.4 or B.4b>
EXPECTED_NEW_ASR=<§APPENDIX B.4 or B.4b> # deterministic post-v4 ASR
SUCCINCT_CONFIG_FILE=<Mode A only; filename under $SUCCINCT_REPO/contracts/>
SUCCINCT_REPO=<resolved Succinct repo; see §3.4>
SUCCINCT_PROPOSAL_ADDR=<expected impl addr from CeloSuperchainOps proposal; §F.2>
SUCCINCT_DEPLOY_STRATEGY=<pending|deploy|setcode; see §3.2 & §5.10>
OPCM_V4=<see §3.2 dynamic slots>
OPCM_V5=<see §3.2 dynamic slots>
NEW_CSC=<see §3.2 dynamic slots>
NEW_ASR=<see §3.2 dynamic slots>
§3.1 Secret & RPC handling
DEPLOYER_PK: the orchestrator holds the real value in memory and passes it verbatim into each subagent dispatch prompt that needs it (T04 anvil fork, T08 bootstrap v4, T09 bootstrap v5, T19 exec v4, T25 exec v5, T30 exec succ-v2, T36 exec basefee). In evidence returned by subagents, the value MUST be redacted as0x***. In TodoWrite task descriptions, never include the raw PK. Rule: "real PK flows forward in prompts, redacted PK flows back in evidence."UPSTREAM_RPC_URL: treated the same way. Real URL flows forward, redacted as<upstream-archive>in evidence.- CONTEXT_BLOCK shown in this skill file uses placeholders — when the orchestrator passes it to a subagent, it substitutes the real values.
§3.2 Dynamic slots (mutated by specific TODOs)
Dynamic slots are populated between dispatches (not all at T01). The orchestrator MUST update the CONTEXT_BLOCK between TODOs when each slot is filled:
| Slot | Filled by | Source |
|---|---|---|
OPCM_V4 |
Mode A: T08 output. Mode B: T01 static from §B.5 (not actually dynamic in Mode B) | Mode A: jq -r .opcm .../op-deployer/v4/config-upgrade.json / Mode B: §B.5 literal |
OPCM_V5 |
Mode A: T09 output. Mode B: T01 static from §B.5 | Mode A: same for v5 / Mode B: §B.5 literal |
NEW_CSC |
T20 | cast call $SYSTEM_CONFIG "superchainConfig()(address)" |
NEW_ASR |
T20 | cast call <PERM_GAME_V4> "anchorStateRegistry()(address)" |
OLD_CSC |
Mode A mainnet: §B.4 literal at T01. Other networks: T11 dynamic capture from SystemConfig.superchainConfig() on the pre-v4 fork state |
see §B.4b sepolia note |
SUCCINCT_IMPL_ADDR |
Mainnet: §B.4 literal at T01 (initial). Sepolia: T01 reads it from the matching "sepolia-succ-vX") case's CALLDATA in exec-upgrade.sh (impl embedded in the calldata blob, and in the setImplementation(42, 0x...) comment above the case where present) — succ-v2 → 0x67cd626e1c2534cd5a129ba9208de69b305ffbd3, succ-v210 → 0x36ad817e6a273a05d6f7638bc3a68a0233b0c33e. Chaos: operator-provided via the SUCCINCT_IMPL env to ./exec-upgrade.sh chaos succ-v2 (the planted/deployed impl captured at T01/T10) — NOT grep-ed from any script. T10 may override if Strategy A (deploy) succeeds — see §5.10. |
§B.4 / §B.4b / §F.2 |
SUCCINCT_PROPOSAL_ADDR |
T01 from §F.2 per NETWORK. | §F.2 literal |
SUCCINCT_DEPLOY_STRATEGY |
T10 — set to deploy if Strategy A succeeds, setcode if fallback used. |
§5.10 |
SUCCINCT_REPO |
T01 — resolved per §3.4: mainnet → $SUCCINCT_ROOT_MAINNET, sepolia/chaos → $SUCCINCT_ROOT. |
§3.4 |
Rule: after a TODO whose evidence contains one of these slots, the orchestrator's
very next action is to append the new value to its in-memory CONTEXT_BLOCK. All
subsequent dispatches use the grown block. Subagents downstream of T20 (T21, T26,
T27, T31, T32) therefore see NEW_CSC and NEW_ASR as concrete addresses, not
<placeholder>.
§3.3 Sibling repo path portability
The OP_ROOT_V4/OP_ROOT_V5/SUPERCHAIN_OPS/CELO_SUPERCHAIN_OPS/SUCCINCT_ROOT/
SUCCINCT_ROOT_MAINNET defaults assume a single operator's laptop layout. On a
different machine, the orchestrator MUST override these in T01 (interactive: ask;
autonomous: orchestrator prompt must pass them).
§3.4 Succinct repo resolution (NETWORK-dependent)
Succinct has a per-network repo problem: different networks were deployed with different repo versions, branches, and commits — each producing different vkeys and therefore different contract addresses. This is a temporary state; eventually all versions should converge into tagged releases in the public Succinct repo.
Current state (Jovian era):
| Network | Repo variable | Default path | Why |
|---|---|---|---|
| mainnet | SUCCINCT_ROOT_MAINNET |
$CELO_ROOT/../Succinct2 |
Requires a specific repo, branch, or commit to produce vkeys matching the mainnet deployment. Operator determines the correct source. |
| sepolia | SUCCINCT_ROOT |
$CELO_ROOT/../Succinct |
Public repo at specific commit produces vkeys matching sepolia deployment. |
| chaos | SUCCINCT_ROOT |
$CELO_ROOT/../Succinct |
Public repo at specific commit produces vkeys matching chaos deployment. |
At T01, set SUCCINCT_REPO to the resolved path:
NETWORK=mainnet→SUCCINCT_REPO=$SUCCINCT_ROOT_MAINNETNETWORK=sepolia|chaos→SUCCINCT_REPO=$SUCCINCT_ROOT
The FDG config file at $SUCCINCT_REPO/contracts/opsuccinctfdgconfig.$NETWORK.json
(or the generic opsuccinctfdgconfig.json generated by just fetch-fdg-config)
contains aggregationVkey and rangeVkeyCommitment — these are constructor
parameters to OPSuccinctFaultDisputeGame and directly affect the deployed
contract address via CREATE (deployer + nonce + initcode w/ constructor args).
Vkey → address chain: repo commit → SP1 ELF binaries → aggregationVkey + rangeVkeyCommitment → constructor args → initcode hash → with deployer nonce →
CREATE address. Wrong commit = wrong vkeys = wrong address = proposal mismatch.
See §APPENDIX F for per-network commit references, config URLs, and expected proposal addresses.
§4 THE PROTOCOL — FIXED TODO CHECKLIST
Create the list below in ONE TodoWrite call, immediately after §2 mode detection.
Items are numbered T00..T42 in BOTH Mode A and Mode B (43 items total — the list
has the same length in both modes; only the subjects differ between the tables below,
with skipped phases marked as explicit SKIP TODOs). Use these exact subjects verbatim
(you may add the activeForm field as shown).
§4.1 Mode A (mocked, full canonical flow)
| ID | Subject | Card § |
|---|---|---|
| T00 | CLEANUP PRECOMMIT: kill anvil on $RPC_PORT at end of run | §5.0 |
| T01 | Collect prerequisites + build CONTEXT_BLOCK | §5.1 |
| T02 | Validate repo branches & build artifacts (5 sibling repos) | §5.2 |
| T03 | Validate CLI tools (anvil, cast, forge, jq, just, mise) | §5.3 |
| T04 | Start anvil fork at $FORK_BLOCK and verify block number | §5.4 |
| T05 | Mock Safe storage via mock-$NETWORK.sh | §5.5 |
| T06 | Validate mock state (thresholds=2, owners, nonces) | §5.6 |
| T07 | Start x-ray dashboard on :$XRAY_PORT | §5.7 |
| T08 | Bootstrap v4 OPCM via op-deployer/v4/bootstrap.sh | §5.8 |
| T09 | Bootstrap v5 OPCM via op-deployer/v5/bootstrap.sh | §5.9 |
| T10 | Plant Succinct v2 impl (Strategy A: deploy from repo; Strategy B: setCodeAtAddress fallback) | §5.10 |
| T11 | CP0.1 — run post-deploy baseline validation queries | §5.11 |
| T12 | CP0.2 — render PASS/FAIL table | §5.12 |
| T13 | CP0.3 — x-ray offer (interactive mcp_question OR headless ack) | §5.13 |
| T14 | CP0.4 — checkpoint gate (mcp_question / autonomous proceed) | §5.14 |
| T15 | Sign v4 (clabs + council) via CeloSuperchainOps | §5.15 |
| T16 | Sign v5 (clabs + council) via CeloSuperchainOps | §5.16 |
| T17 | Sign succ-v2 (clabs + council) via CeloSuperchainOps | §5.17 |
| T18 | Simulate v4 via SuperchainOps (REQUIRED; skip only with documented reason) | §5.18 |
| T19 | Execute v4 (exec-mocked.sh mainnet / exec-upgrade.sh sepolia v4 / exec-upgrade.sh chaos v4) | §5.19 |
| T20 | CP1.1 — run post-v4 validation queries (discover $NEW_ASR, $NEW_CSC) | §5.20 |
| T21 | CP1.2 — render PASS/FAIL table | §5.21 |
| T22 | CP1.3 — x-ray offer | §5.22 |
| T23 | CP1.4 — checkpoint gate | §5.23 |
| T24 | Simulate v5 via SuperchainOps (REQUIRED; skip only with documented reason) | §5.24 |
| T25 | Execute v5 (exec-mocked.sh mainnet / exec-upgrade.sh sepolia v5 / exec-upgrade.sh chaos v5) | §5.25 |
| T26 | CP2.1 — run post-v5 validation queries | §5.26 |
| T27 | CP2.2 — render PASS/FAIL table | §5.27 |
| T28 | CP2.3 — x-ray offer | §5.28 |
| T29 | CP2.4 — checkpoint gate | §5.29 |
| T30 | Execute succ-v2 (exec-mocked.sh mainnet / exec-upgrade.sh sepolia succ-v2 / exec-upgrade.sh chaos succ-v2) | §5.30 |
| T31 | CP3.1 — run post-succ-v2 validation queries | §5.31 |
| T32 | CP3.2 — render PASS/FAIL table | §5.32 |
| T33 | CP3.3 — x-ray offer | §5.33 |
| T34 | CP3.4 — checkpoint gate | §5.34 |
| T35 | Phase 7 — final comparison table (CP0..CP3 progression) | §5.35 |
| T36 | Phase 8 — SKIP (Mode A incompatible) | §5.36 |
| T37 | CP4.1 — SKIP (Phase 8 not executed) | §5.37 |
| T38 | CP4.2 — SKIP | §5.38 |
| T39 | CP4.3 — SKIP | §5.39 |
| T40 | CP4.4 — SKIP | §5.40 |
| T41 | CLEANUP — kill anvil, kill x-ray, verify port free, print summary | §5.41 |
| T42 | CLEANUP GATE — mcp_question (interactive) / final print (autonomous) | §5.42 |
§4.2 Mode B (real signatures, post-impls block)
| ID | Subject | Card § |
|---|---|---|
| T00 | CLEANUP PRECOMMIT: kill anvil on $RPC_PORT at end of run | §5.0 |
| T01 | Collect prerequisites + build CONTEXT_BLOCK | §5.1 |
| T02 | Validate repo state (Celo only; sibling builds not required) | §5.2 |
| T03 | Validate CLI tools | §5.3 |
| T04 | Start anvil fork at $FORK_BLOCK (post-impls) and verify block | §5.4 |
| T05 | SKIP mock — Mode B keeps real Safes (emit explicit SKIP report) | §5.5 |
| T06 | SKIP mock validation — n/a in Mode B | §5.6 |
| T07 | Start x-ray dashboard on :$XRAY_PORT | §5.7 |
| T08 | SKIP bootstrap v4 — real OPCM v4 already on-chain | §5.8 |
| T09 | SKIP bootstrap v5 — real OPCM v5 already on-chain | §5.9 |
| T10 | SKIP Succinct plant — real impl already on-chain at fork block | §5.10 |
| T11 | CP0.1 — post-deploy baseline queries | §5.11 |
| T12 | CP0.2 — render PASS/FAIL table | §5.12 |
| T13 | CP0.3 — x-ray offer | §5.13 |
| T14 | CP0.4 — checkpoint gate | §5.14 |
| T15 | SKIP sign v4 — real sigs in secrets/.env.signers.v4 | §5.15 |
| T16 | SKIP sign v5 — real sigs in secrets/.env.signers.v5 | §5.16 |
| T17 | SKIP sign succ-v2 — real sigs in secrets/.env.signers.succinct200 | §5.17 |
| T18 | Simulate v4 via SuperchainOps (REQUIRED; skip only with documented reason) | §5.18 |
| T19 | Execute v4 via exec-upgrade.sh mainnet v4 | §5.19 |
| T20 | CP1.1 — run post-v4 validation queries | §5.20 |
| T21 | CP1.2 — render PASS/FAIL table | §5.21 |
| T22 | CP1.3 — x-ray offer | §5.22 |
| T23 | CP1.4 — checkpoint gate | §5.23 |
| T24 | Simulate v5 via SuperchainOps (REQUIRED) | §5.24 |
| T25 | Execute v5 via exec-upgrade.sh mainnet v5 | §5.25 |
| T26 | CP2.1 — run post-v5 validation queries | §5.26 |
| T27 | CP2.2 — render PASS/FAIL table | §5.27 |
| T28 | CP2.3 — x-ray offer | §5.28 |
| T29 | CP2.4 — checkpoint gate | §5.29 |
| T30 | Execute succ-v2 via exec-upgrade.sh mainnet succ-v2 | §5.30 |
| T31 | CP3.1 — post-succ-v2 queries | §5.31 |
| T32 | CP3.2 — render PASS/FAIL table | §5.32 |
| T33 | CP3.3 — x-ray offer | §5.33 |
| T34 | CP3.4 — checkpoint gate | §5.34 |
| T35 | Phase 7 — final comparison table | §5.35 |
| T36 | Phase 8 — execute exec-basefee.sh (if INCLUDE_BASEFEE=yes) OR SKIP | §5.36 |
| T37 | CP4.1 — post-basefee queries (only if T36 executed) | §5.37 |
| T38 | CP4.2 — render PASS/FAIL table | §5.38 |
| T39 | CP4.3 — x-ray offer | §5.39 |
| T40 | CP4.4 — checkpoint gate | §5.40 |
| T41 | CLEANUP — kill anvil, kill x-ray, verify port free, print summary | §5.41 |
| T42 | CLEANUP GATE | §5.42 |
In Mode A:
T36..T40stay in the list as explicit SKIP TODOs (Mode A incompatible). In Mode B withINCLUDE_BASEFEE=no:T36..T40stay in the list as SKIPs.
§5 EXECUTION CARDS — per-TODO subagent prompt templates
Each card tells you (a) what the subagent should do, (b) the exact REQUIRED EVIDENCE
block the subagent must return, (c) the PASS condition, (d) the model tier.
All subagents are dispatched with this wrapper:
Task(
subagent_type="oh-my-claudecode:executor",
model="<tier>",
description="Jovian T<NN>",
prompt="""
<§1.1 safety reminder: localhost only; no retries; fail-fast>
<§CONTEXT_BLOCK verbatim>
TODO: T<NN>
CARD: §5.<NN>
TASK:
<card body>
REQUIRED EVIDENCE (return ALL of these, labelled, in your final report):
<card evidence list>
OUTPUT FORMAT:
---
TODO: T<NN>
STATUS: PASS | FAIL | SKIP
EVIDENCE:
<labelled evidence block>
NEXT: <one sentence>
---
If the task fails: report FAIL with the exact error and DO NOT attempt recovery.
"""
)
Orchestrator verifies evidence → marks TODO complete. Missing evidence → re-dispatch.
§5.0 — T00 Cleanup precommit (SENTINEL — stays pending)
Task: Lightweight sentinel subagent that records the cleanup plan so it is
visible from the very first TodoWrite. It lists the files/processes that T41
will kill/preserve but does NOT kill anything itself. After the subagent returns
PASS, the orchestrator keeps T00 on the TodoWrite list as pending (per §1.1
rule 6 — NOT in_progress, NOT completed) until T41 cleanup succeeds, at which
point the orchestrator marks T00 completed.
Files the subagent registers for cleanup: /tmp/anvil-$RPC_PORT.pid,
/tmp/anvil-$RPC_PORT.log, /tmp/xray-$RPC_PORT.pid, /tmp/xray-$RPC_PORT.log.
Ports to verify free at T41: $RPC_PORT, $XRAY_PORT.
Tier: haiku.
Evidence: ARMED: rpc_port=$RPC_PORT; xray_port=$XRAY_PORT; files=/tmp/anvil-$RPC_PORT.pid,/tmp/anvil-$RPC_PORT.log,/tmp/xray-$RPC_PORT.pid,/tmp/xray-$RPC_PORT.log; cleanup_todo=T41
§5.1 — T01 Collect prerequisites
Task: Build the §CONTEXT_BLOCK. Steps (applied in order):
- Alias normalization: if
FORK_BLOCKis a string alias (oldest block,pre-impls,mode-a block,post-impls,mode-b block,impls-deployed), resolve via §B.1 for the chosen NETWORK. After resolution, verify the resolved numeric block matches the declaredMODE(Mode A = pre-impls row; Mode B = post-impls row). Mismatch → FAIL withmode-block mismatch: <mode> expects <n>, got <actual>. - Address resolution: populate all static slots (
SENDER,PARENT_SAFE,CLABS_SAFE,COUNCIL_SAFE,SYSTEM_CONFIG,DGF,OLD_ASR) from §B.3/§B.4 (mainnet) or §B.4b (sepolia/chaos). On mainnet also populateOLD_CSCandSUCCINCT_IMPL_ADDRfrom §B.4 literals; on sepolia/chaos,OLD_CSCis deferred (set to empty placeholder — captured later at T11 fromSystemConfig.superchainConfig()on the pre-v4 fork and copied into CONTEXT_BLOCK before T12 dispatches). On sepolia,SUCCINCT_IMPL_ADDRis read at T01 from the matching"sepolia-succ-vX")case's CALLDATA in$CELO_ROOT/packages/op-tooling/exec/exec-upgrade.sh(the impl is embedded in the calldata blob, and in thesetImplementation(42, 0x...)comment above the case where present): succ-v2 →0x67cd626e1c2534cd5a129ba9208de69b305ffbd3, succ-v210 →0x36ad817e6a273a05d6f7638bc3a68a0233b0c33e. On chaos,SUCCINCT_IMPL_ADDRis operator-provided via theSUCCINCT_IMPLenv to./exec-upgrade.sh chaos succ-v2(the planted/deployed impl captured at T01/T10) — NOT grep-ed from any script.EXPECTED_NEW_ASRis populated from §B.4 on mainnet and left empty on non-mainnet runs (compared dynamically at T20). - OPCM resolution: Mode B sets
OPCM_V4andOPCM_V5from §B.5. Mode A leaves them empty (filled by T08/T09). - Secret handling:
DEPLOYER_PKandUPSTREAM_RPC_URLare passed through to downstream cards verbatim; in the evidence report from T01, redactDEPLOYER_PKas0x***andUPSTREAM_RPC_URLas<upstream-archive>. - Interactive round-trip: in Interactive mode, if any required detail is
missing, the subagent prints numbered questions to stdout and returns
STATUS: FAILwith evidencemissing_details=<list>. The orchestrator then callsmcp_questionitself (only the orchestrator is allowed to, per §1.4) to collect answers, then re-dispatches T01 with the merged details. - Autonomous mode: on any missing or ambiguous detail →
STATUS: FAILwith the §2.2 missing-detail quit block. Do NOT guess.
Tier: haiku.
Evidence: full CONTEXT_BLOCK filled in; DEPLOYER_PK redacted as 0x***;
UPSTREAM_RPC_URL redacted as <upstream-archive>; plus, in interactive mode,
a numbered missing_details=<list> block if applicable.
§5.2 — T02 Validate repos
Task (Mode A): for each of $OP_ROOT_V4, $OP_ROOT_V5, $SUPERCHAIN_OPS,
$CELO_SUPERCHAIN_OPS, $SUCCINCT_ROOT: check branch (git branch --show-current),
binary presence (op-deployer/bin/op-deployer for v4/v5), forge artifact count,
task directories. SCOps task directories:
NETWORK=mainnet:$SUPERCHAIN_OPS/src/tasks/eth/048-opcm-upgrade-v410-celo/and.../eth/049-opcm-upgrade-v500-celo/NETWORK=sepolia:$SUPERCHAIN_OPS/src/tasks/sep/048-opcm-upgrade-v410-celo/and.../sep/049-opcm-upgrade-v500-celo/(path issep/at the TOP level ofsrc/tasks/, NOT undereth/sep/)NETWORK=chaos: no SCOps tasks exist; T02 MUST return a chaos-specific evidence linescops_chaos=N/A; T18/T24 will SKIP for the same reason. If artifacts missing → runforge build(may take 5-10 min). This is the ONE exception to "no extra steps" (§7 dispatch rules): artifact bootstrap is explicitly allowed in T02.
Succinct build & FDG config validation (Mode A):
- Resolve
$SUCCINCT_REPOper §3.4 (mainnet →$SUCCINCT_ROOT_MAINNET, else →$SUCCINCT_ROOT). Verify the directory exists and is a git repo. - Report
git -C $SUCCINCT_REPO log --oneline -1andgit -C $SUCCINCT_REPO branch --show-currentas evidence (succinct_commit=<hash>; succinct_branch=<br>). - Check for pre-existing per-network config:
$SUCCINCT_REPO/contracts/opsuccinctfdgconfig.$NETWORK.json. If present, use it asSUCCINCT_CONFIG_FILE. If absent, check for the generic$SUCCINCT_REPO/contracts/opsuccinctfdgconfig.json. - If neither config exists AND the operator has a
.env.$NETWORKin the Succinct repo: attemptcd $SUCCINCT_REPO && just fetch-fdg-config .env.$NETWORKto generate it. This may fail if RPC access is not configured — on failure, recordfdg_config_generated=falseas evidence and continue (T10 Strategy BsetCodeAtAddressfallback will be used). - If a config file is found/generated: extract and report
aggregationVkeyandrangeVkeyCommitmentfrom it as evidence. Store the config path asSUCCINCT_CONFIG_FILEin the CONTEXT_BLOCK. - Build Succinct contracts:
cd $SUCCINCT_REPO/contracts && forge build. If build fails, recordsuccinct_build=failed— T10 will use Strategy B fallback. - Report
succinct_build=ok|failed; fdg_config=$SUCCINCT_CONFIG_FILE|missing.
Task (Mode B): only check $CELO_ROOT/packages/op-tooling/exec/exec-upgrade.sh
exists (unified runner for mainnet, sepolia, and chaos) AND
$CELO_ROOT/secrets/.env.signers.{v4,v5,succinct200} are decrypted. If
INCLUDE_BASEFEE=yes, ALSO check $CELO_ROOT/secrets/.env.signers.basefee — this
is the pre-check so Phase 8 does not late-fail at T36.
Tier: sonnet.
Evidence: per-repo branch=<x>, binary=<ok|missing|built>, artifacts=<N> lines;
scops_tasks=<eth|sep|N/A>; exec_script=<file>; for Mode B: signers.v4=ok,
signers.v5=ok, signers.succinct200=ok, signers.basefee=ok|N/A.
§5.3 — T03 Validate CLI tools
Task: which anvil cast forge jq just mise. Report missing.
Tier: haiku.
Evidence: anvil=<path>; cast=<path>; forge=<path>; jq=<path>; just=<path>; mise=<path> one per line.
§5.4 — T04 Start anvil fork
Task:
lsof -ti:$RPC_PORT— if non-empty, FAIL ("port busy; orchestrator must resolve").anvil --port $RPC_PORT --fork-url "$UPSTREAM_RPC_URL" --fork-chain-id $FORK_CHAIN_ID --fork-block-number $FORK_BLOCK &>/tmp/anvil-$RPC_PORT.log & echo $! > /tmp/anvil-$RPC_PORT.pidsleep 3 && cast block-number -r $RPC_URLTier:haiku.
Evidence: anvil_pid=<N>; current_block=<matches $FORK_BLOCK ±1>
§5.5 — T05 Mock Safe storage (Mode A only; SKIP in Mode B)
Script selection by network (the skill does NOT have mock-chaos.sh or
mock-mainnet.sh symlinks — these are the real script names):
NETWORK=mainnet:./mock-mainnet.shNETWORK=sepolia:NETWORK=sepolia ./mock-sepolia.shNETWORK=chaos:NETWORK=chaos ./mock-sepolia.sh(same script, different env)
Task (Mode A):
cd $CELO_ROOT/packages/op-tooling/fork
RPC_URL=$RPC_URL \
NETWORK=$NETWORK \
MOCKED_SIGNER_1=0x865d05C8bB46E7AF16D6Dc99ddfb2e64BBec1345 \
MOCKED_SIGNER_2=0x899a864C6bE2c573a98d8493961F4D4c0F7Dd0CC \
MOCKED_SIGNER_3=0x480C5f2340f9E7A46ee25BAa815105B415a7c2e2 \
MOCKED_SIGNER_4=0x8Af6f11c501c082bD880B3ceC83e6bB249Fa32c9 \
./<script-from-above>
Task (Mode B): STATUS: SKIP with evidence SKIP: Mode B keeps real Safes.
Tier: haiku.
Evidence (Mode A): mock_script=<name>; exit_code=0
Evidence (Mode B): SKIP: Mode B keeps real Safes
§5.6 — T06 Validate mock state (Mode A only; SKIP in Mode B)
Task (Mode A, NETWORK ∈ {mainnet, sepolia}):
cast call $PARENT_SAFE "getThreshold()(uint256)" -r $RPC_URL
cast call $CLABS_SAFE "getThreshold()(uint256)" -r $RPC_URL
cast call $COUNCIL_SAFE "getThreshold()(uint256)" -r $RPC_URL
cast call $PARENT_SAFE "getOwners()(address[])" -r $RPC_URL
cast call $CLABS_SAFE "getOwners()(address[])" -r $RPC_URL
cast call $COUNCIL_SAFE "getOwners()(address[])" -r $RPC_URL
cast call $PARENT_SAFE "nonce()(uint256)" -r $RPC_URL
cast call $CLABS_SAFE "nonce()(uint256)" -r $RPC_URL
cast call $COUNCIL_SAFE "nonce()(uint256)" -r $RPC_URL
Task (Mode A, NETWORK=chaos): flat Safe — query getThreshold(), getOwners(),
nonce() on the single chaos Safe 0x6F8DB5...66a9 only.
Task (Mode B): STATUS: SKIP with evidence SKIP: Mode B keeps real Safes.
Tier: haiku.
Evidence (mainnet+sepolia Mode A): three thresholds (all 2), three owner
arrays (match §B.7 mocked defaults), three nonces (match §B.8 row for NETWORK+MODE).
Evidence (chaos Mode A): threshold=1; owner=<EOA>; nonce=<baseline>.
Evidence (Mode B): SKIP: Mode B keeps real Safes.
§5.7 — T07 Start x-ray dashboard
Task:
lsof -ti:$XRAY_PORT— if non-empty, FAIL withxray port $XRAY_PORT busy; orchestrator must resolve or override XRAY_PORT in CONTEXT_BLOCK.- Verify the x-ray checkout exists:
[ -d "$XRAY_ROOT" ]— if missing, FAIL withXRAY_ROOT ($XRAY_ROOT) not found — x-ray now lives in its own repo; clone https://github.com/celo-org/x-ray. cd $XRAY_ROOT && python3 -m http.server $XRAY_PORT &>/tmp/xray-$RPC_PORT.log & echo $! > /tmp/xray-$RPC_PORT.pidsleep 2 && curl -sSf http://localhost:$XRAY_PORT | head -c 200Tier:haiku.
Evidence: xray_pid=<N>; xray_port=$XRAY_PORT; curl_status=200.
§5.8 — T08 Bootstrap v4 OPCM (Mode A only; SKIP in Mode B)
PRECONDITION: T06 STATUS=PASS (Mode A) OR T06 STATUS=SKIP (Mode B — then T08 also SKIPs).
Task (Mode A):
cd $CELO_ROOT/packages/op-tooling/op-deployer/v4
RPC_URL=$RPC_URL \
NETWORK=$NETWORK \
OP_ROOT=$OP_ROOT_V4 \
MULTISIG_ADDRESS=<§APPENDIX B.3 "Multisig (bootstrap)" row for NETWORK> \
DEPLOYER_PK=<real DEPLOYER_PK, not 0x***> \
./bootstrap.sh
OPCM_V4=$(jq -r '.opcm' $CELO_ROOT/packages/op-tooling/op-deployer/v4/config-upgrade.json)
cast codesize $OPCM_V4 -r $RPC_URL
Return OPCM_V4 as labelled evidence so the orchestrator can mutate CONTEXT_BLOCK
per §3.2.
Task (Mode B): STATUS: SKIP with evidence
`SKIP: Mode B — OPC
…(truncated)