Skip to content

chore(operations): squads rotation rehearsal executed 2026-05-16 + offset fix#369

Merged
alrimarleskovar merged 2 commits into
mainfrom
chore/squads-rehearsal-executed-2026-05-16
May 16, 2026
Merged

chore(operations): squads rotation rehearsal executed 2026-05-16 + offset fix#369
alrimarleskovar merged 2 commits into
mainfrom
chore/squads-rehearsal-executed-2026-05-16

Conversation

@alrimarleskovar
Copy link
Copy Markdown
Owner

Summary

End-to-end Squads multisig rotation rehearsal executed on devnet 2026-05-16. Closes the operational gap flagged by external reviewer ("scripts ready, execution pending ~5 sprints").

Two artifacts:

  1. Rehearsal log (docs/operations/rehearsal-logs/2026-05-16-squads-rotation-rehearsal.md)` — 4 phases validated with on-chain tx evidence
  2. Offset fix in 2 rehearsal scripts — discovered during execution

Rehearsal evidence — 4 on-chain phases

Phase Tx Solscan
A — Propose 4pfiQLAEzpoz... view
B — Cancel (kill-switch drill) s1NDWguUm... view
C-a — Re-propose 2dhWa68945... view
C-b — Commit (post-timelock) 2xeWvuDTa4... view

Final state on parallel test deployment: live=6Y6BL1mq... (rotated), pending=default, eta=0 (clean).

Parallel deployment — canonical untouched

Rehearsal ran against a parallel roundfi_core deploy at 6WuSo1utWKg8gNyzzJyqCoeLa7VpEyu8ZN1EtLzJ7Rpn, NOT the canonical 8LVrgxKwKwqjcdq7rUUwWY2zPNk8anpo2JsaR9jTQQjw. Reason: the canonical devnet ProtocolConfig (3c9MmoM8...) is 317 bytes — predates PR #323 that added authority-rotation fields (current code expects 381 bytes). The parallel deploy reproduces the rehearsal without disturbing Pool 1/2/3 history.

Audit-worthy finding documented in rehearsal log §1.1: a realloc migration ix will be required to bring the canonical ProtocolConfig forward to the post-#323 layout. Tracked as gap for next devnet refresh sprint.

Offset fix (script bug found during execution)

scripts/devnet/squads-rehearsal-{verify,commit-authority}.ts had wrong offsets:

-const OFFSET_PENDING_AUTHORITY = 311;
+const OFFSET_PENDING_AUTHORITY = 313;
-const OFFSET_PENDING_AUTHORITY_ETA = 343;
+const OFFSET_PENDING_AUTHORITY_ETA = 345;

Root cause: source order in programs/roundfi-core/src/state/config.rs places lp_share_bps: u16 (2 bytes, line 122) BETWEEN commit_reveal_required (line 107) and pending_authority (line 136). The struct's own pub const SIZE comment block (line 193) lists pending_authority BEFORE lp_share_bps — script author trusted the SIZE comment instead of source declaration order. Borsh serializes in declaration order, not comment order.

Symptom: verify displayed garbage Pubkey (7jkAWw2ZGq1R1...), eta as out-of-range Date (caused RangeError: Invalid time value). commit aborted with "Timelock active" error path before sending the tx because the misread eta looked far in the future.

Verified end-to-end post-fix: verify correctly reads live=6Y6BL1mq..., pending=default, eta=0 after the rotation completed.

What this rehearsal validates

  • propose_new_authority ix exists, callable by current authority, atomically sets pending_authority + pending_authority_eta
  • cancel_new_authority ix exists, callable by current authority, resets pending state
  • commit_new_authority ix exists, callable by anyone, enforces eta window, rotates live authority
  • Post-commit state is correctly cleared
  • The 4 RoundFi rehearsal scripts (verify, propose-authority, cancel-authority, commit-authority) work end-to-end with the offset fix
  • pnpm aliases (devnet:squads-rehearsal-*) work (added in PR chore(operations): squads rehearsal quickstart + pnpm aliases #367)
  • Not validated by this rehearsal: actual Squads v4 multisig PDA derivation + member signing flow. This is exercised separately on the mainnet ceremony day via the Squads web UI per squads-multisig-procedure.md §3-§5.

Pre-flight blockers documented

Rehearsal log §1.1-§1.3 documents 3 blockers discovered + resolved during execution:

  1. Canonical devnet ProtocolConfig pre-PR feat(roundfi-core): timelocked protocol authority rotation (Squads #3.6) #323 (parallel deploy as workaround)
  2. DeclaredProgramIdMismatch after first deploy (resolved via anchor keys sync + rebuild)
  3. Insufficient SOL on solana config wallet (resolved via solana transfer between wallets)

These are documented for future operators — saves debugging time on next rehearsal.

Net delta

File Lines
docs/operations/rehearsal-logs/2026-05-16-squads-rotation-rehearsal.md (new) +139
scripts/devnet/squads-rehearsal-verify.ts +2 / -2
scripts/devnet/squads-rehearsal-commit-authority.ts +2 / -2

Test plan

  • All 4 rehearsal phases executed end-to-end with on-chain tx evidence
  • Offset fix verified — post-fix verify correctly reads live/pending/eta
  • Canonical 8LVrg...QQjw program + Pool 1/2/3 history untouched
  • Local cleanup: .env, Anchor.toml, lib.rs, constants.rs restored to canonical state (none of these commit in this PR)
  • (Recommended for reviewer) Re-run pnpm devnet:squads-rehearsal-verify against the rehearsal program-id (6WuSo1ut...7Rpn) — should show live=6Y6BL1mq... until that orphan deploy is wiped on next devnet refresh

Related

https://claude.ai/code/session_01YapZy1Z5gzbV5EammBkSQm


Generated by Claude Code

…fset fix

Two artifacts from end-to-end devnet rehearsal execution:

1. Rehearsal log (new): docs/operations/rehearsal-logs/2026-05-16-squads-rotation-rehearsal.md
   - 4 phases validated with tx evidence: propose, cancel, re-propose, commit
   - Parallel test deploy (program-id 6WuSo1ut...7Rpn) — canonical 8LVrg...QQjw untouched
   - Pre-flight blockers documented (devnet ProtocolConfig pre-PR #323, DeclaredProgramIdMismatch, insufficient SOL)
   - Post-rotation state: live=6Y6BL1mq..., pending=default, eta=0 (clean)

2. Offset fix in 2 scripts (squads-rehearsal-verify.ts + squads-rehearsal-commit-authority.ts):
   - OFFSET_PENDING_AUTHORITY: 311 -> 313
   - OFFSET_PENDING_AUTHORITY_ETA: 343 -> 345
   - Root cause: struct's SIZE comment listed pending_authority before lp_share_bps;
     Borsh serializes in source declaration order which has lp_share_bps (u16) FIRST.
   - Bug surfaced during rehearsal: verify displayed garbage Pubkey,
     commit aborted with false "Timelock active" before sending tx.
   - Fix verified end-to-end: post-fix verify correctly reads live/pending/eta.

Closes operational gap flagged by external reviewer (Squads rehearsal "scripts ready,
execution pending ~5 sprints"). Validates the propose/cancel/commit ix logic for the
eventual mainnet ceremony. Squads-side multisig setup remains a separate ceremony-day
artifact (mainnet only).
@vercel
Copy link
Copy Markdown

vercel Bot commented May 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
round_financial Ignored Ignored Preview May 16, 2026 11:30pm

@alrimarleskovar alrimarleskovar merged commit b22b830 into main May 16, 2026
6 checks passed
@alrimarleskovar alrimarleskovar deleted the chore/squads-rehearsal-executed-2026-05-16 branch May 17, 2026 00:09
alrimarleskovar pushed a commit that referenced this pull request May 17, 2026
- §Security & Audit severity table reconciled with internal-audit-findings.md (34/31 → 40/36; Critical 2→3, Low 10→12, Informational 6→9, won't-fix 2→3).
- §Development Status row 1: 195+ → 370+ PRs merged on main (496 commits since project start).
- §Development Status row 4: smart contract LoC 6,150 → 8,655 across the 3 in-scope Anchor programs (matches AUDIT_SCOPE.md).
- §Development Status row 5: test count 227 → 280+ across 22 spec files; added explicit breakdown (security/encoder/canary-control/audit-regression/lifecycle) + bankrun_compat shim mention (ADR 0007).
- §Development Status row 9 (security audit) rewritten: stale 'Halborn/Ottersec/Sec3 + bug bounty deferred — out of hackathon scope' framing replaced with current 'internal pre-audit complete, 40 findings, 36 closed, Adevar engagement in scoping' framing; added pointer to all 11 indexed security docs.
- §Development Status row 10 (devnet testing) tail: bankrun_compat shim (ADR 0007) + Squads multisig rotation rehearsal evidence (2026-05-16, devnet program-id 6WuSo1ut…7Rpn).
- §Development Status row 11 (mainnet migration): SEV PR range #326..#365#326..#372.
- §Core Mechanics line 107: '[Adevar SEV-025]' → 'internal pre-audit [SEV-025]' (matches the constraint enforced in wave 3).
- docs/status.md: §Recent additions title #322-#369#322-#372 to capture wave 3 tail.

Drift discovered after wave 3 by re-reading the README end-to-end against
the canonical sources (AUDIT_SCOPE.md, internal-audit-findings.md). The
prose surface had been refreshed at the top (§Security & Audit > stats)
but the dependent severity table and §Development Status rows were
written before the internal pre-audit completed and the wave 1-3 doc
refresh.

This is the same Adevar-attribution drift the wave 3 PR (#372) closed
across docs/security/, applied to the README sections that escaped
the wave 1-3 scope. Internal pre-audit framing preserved verbatim.

https://claude.ai/code/session_01YapZy1Z5gzbV5EammBkSQm
alrimarleskovar added a commit that referenced this pull request May 17, 2026
… sweep (#373)

- §Security & Audit severity table reconciled with internal-audit-findings.md (34/31 → 40/36; Critical 2→3, Low 10→12, Informational 6→9, won't-fix 2→3).
- §Development Status row 1: 195+ → 370+ PRs merged on main (496 commits since project start).
- §Development Status row 4: smart contract LoC 6,150 → 8,655 across the 3 in-scope Anchor programs (matches AUDIT_SCOPE.md).
- §Development Status row 5: test count 227 → 280+ across 22 spec files; added explicit breakdown (security/encoder/canary-control/audit-regression/lifecycle) + bankrun_compat shim mention (ADR 0007).
- §Development Status row 9 (security audit) rewritten: stale 'Halborn/Ottersec/Sec3 + bug bounty deferred — out of hackathon scope' framing replaced with current 'internal pre-audit complete, 40 findings, 36 closed, Adevar engagement in scoping' framing; added pointer to all 11 indexed security docs.
- §Development Status row 10 (devnet testing) tail: bankrun_compat shim (ADR 0007) + Squads multisig rotation rehearsal evidence (2026-05-16, devnet program-id 6WuSo1ut…7Rpn).
- §Development Status row 11 (mainnet migration): SEV PR range #326..#365#326..#372.
- §Core Mechanics line 107: '[Adevar SEV-025]' → 'internal pre-audit [SEV-025]' (matches the constraint enforced in wave 3).
- docs/status.md: §Recent additions title #322-#369#322-#372 to capture wave 3 tail.

Drift discovered after wave 3 by re-reading the README end-to-end against
the canonical sources (AUDIT_SCOPE.md, internal-audit-findings.md). The
prose surface had been refreshed at the top (§Security & Audit > stats)
but the dependent severity table and §Development Status rows were
written before the internal pre-audit completed and the wave 1-3 doc
refresh.

This is the same Adevar-attribution drift the wave 3 PR (#372) closed
across docs/security/, applied to the README sections that escaped
the wave 1-3 scope. Internal pre-audit framing preserved verbatim.

https://claude.ai/code/session_01YapZy1Z5gzbV5EammBkSQm

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant