# Move Auditor

> Audits Move contracts (Sui & Aptos) for security bugs.

- Skill: `pantheraudits/move-auditor` (Agent Skill, multi-file: 19 files)
- Install (CLI): `npx skillmds@latest add pantheraudits/move-auditor`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pantheraudits/move-auditor/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: pantheraudits (https://skillmd.com/u/pantheraudits)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pantheraudits/move-auditor

---


# Move Auditor

> Fast, systematic security feedback on Move smart contracts (Sui & Aptos).
> Activates automatically on `.move` files — no setup, no copy-pasting.

---

## Activation

This skill activates whenever:
- `.move` files are detected in the working directory or opened in the editor
- The user asks to audit, review, check security, or find vulnerabilities in Move code
- Keywords like `module`, `struct`, `entry fun`, `public fun`, `sui::`, `aptos_framework::` appear in scope

When activated, immediately begin **Phase 1** without waiting for instructions.

## When NOT to Use

- Non-Move contracts (Solidity, Rust/Anchor, EVM, TEAL, FunC)
- General code review for style, performance, or refactoring
- Writing Move code, generating patches, or fixing bugs
- When user explicitly requests a quick scan without full verification

---

## Reference Files

All reference files are in the **same directory as this SKILL.md**.
When the instructions below say "read `filename.md`", use the Read tool on the
file in this skill's directory (for Codex this is typically
`~/.codex/skills/move-auditor/`; for Claude Code this is typically
`~/.claude/commands/move-auditor/`).

| File | When to load |
|------|-------------|
| `common-move.md` | **Always** — chain-agnostic checks (sections 1–13), verification checklist |
| `verification-policy.md` | **Always** — evidence hierarchy, mock rejection rule, feasibility gates, severity discipline |
| `checklist-router.md` | **Always** — deterministic coverage plan; maps detected protocol features to files and mandatory follow-up checks |
| `move-fp-catalog.md` | **Always** — rationalizations to reject, Move FP catalog, self-hallucination check |
| `evidence-chains.md` | **Phase 7** — structured evidence templates for data flow, math proofs, PoC |
| `confidence-gates.md` | **Phase 7** — confidence gating, hard evidence requirements per finding type |
| `sui-patterns.md` | When chain is **Sui** (imports `sui::object`, `sui::transfer`, etc.) — SUI-01 to SUI-46 |
| `aptos-patterns.md` | When chain is **Aptos** (imports `aptos_framework`, `aptos_std`, etc.) — APT-01 to APT-25 |
| `defi-vectors.md` | When protocol involves tokens, swaps, lending, staking, or oracles — DEFI-01 to DEFI-10 + subcategory router |
| `semantic-gap-checks.md` | When the protocol has accumulators, checkpoints, rewards, lending state, cross-module accounting, or multi-step state transitions |
| `defi/defi-staking.md` | When staking/yield detected (`stake`, `unstake`, `reward_per_share`, `accumulator`, `last_index`, `reward_debt`, `last_reward_per_share`) — DEFI-11 to DEFI-16, DEFI-88 |
| `defi/defi-oracle.md` | When oracle usage detected (`get_price`, `oracle`, `price_feed`, `price_info`, `oracle_update`, `refresh_oracle`, `observation`) — DEFI-17 to DEFI-24, DEFI-95 |
| `defi/defi-lending.md` | When lending/borrowing or limiter logic detected (`borrow`, `repay`, `collateral`, `health_factor`, `limiter`, `rate_limit`, `outflow`, `margin`, `leverage`) — DEFI-25 to DEFI-34, DEFI-80, DEFI-82, DEFI-84, DEFI-90, DEFI-93 (post-trade health check) |
| `defi/defi-math-precision.md` | When complex financial math detected (`PRECISION`, `DECIMAL`, `float`, `Decimal`, `WAD`, fee/share math) OR when reward/accumulator/liquidity_mining patterns detected — DEFI-35 to DEFI-42, DEFI-85 to DEFI-87, DEFI-92 (check-vs-settlement divergence) |
| `defi/defi-slippage.md` | When swap/DEX patterns detected (`swap`, `min_amount_out`, `slippage`, AMM pool) — DEFI-43 to DEFI-49 |
| `defi/defi-liquidation.md` | When liquidation mechanisms detected (`liquidat`, `seize`, `bad_debt`, `insurance`) — DEFI-50 to DEFI-66, DEFI-81, DEFI-83, DEFI-91 (check-vs-settle price-basis revert), DEFI-94 (self-trade extraction) |
| `defi/defi-auction-clm.md` | When auction or CLM patterns detected (`bid`, `auction`, `TWAP`, `tick`, `concentrated`) — DEFI-67 to DEFI-73 |
| `defi/defi-signatures.md` | When signature verification detected (`ed25519`, `secp256k1`, `verify_signature`, `nonce`) OR a mutable signer-set/threshold/quorum is verified against (`threshold`, `signers`, `quorum`, `multisig`, `approvers`, `guardians`) — DEFI-74 to DEFI-79, DEFI-89 |
| `defi/defi-lending-design-patterns.md` | When lending/borrowing detected — known-good patterns (DESIGN-L1 to L4) that should NOT be reported as bugs |
| `audit-prompts.md` | Optional — deep-dive prompts and Move vulnerability pattern pack |
| `sample-finding.md` | Reference for output format — do not load during audits |

---

## Auditor Mindset

You are a senior Move security researcher. Find real, exploitable vulnerabilities — not theoretical ones. Think like an attacker, trace value flows end-to-end, question every assumption, chain low-severity issues into critical ones, and verify with PoC scenarios rather than guessing. Consult `move-fp-catalog.md` to avoid common false positives.

---

## Workflow

### Phase 1 — Initial Assessment (auto-run on activation)

**Detect chain and framework:**
- Presence of `sui::object`, `sui::transfer`, `sui::tx_context` → **Sui Move**
- Presence of `aptos_framework`, `aptos_std`, `#[test_only]` → **Aptos Move**
- Load reference files from the skill directory (see Reference Files table above):
  - **Always** → read `common-move.md`
  - **Always** → read `verification-policy.md`
  - **Always** → read `checklist-router.md`
  - **Always** → read `move-fp-catalog.md`
  - **Sui** → also read `sui-patterns.md`
  - **Aptos** → also read `aptos-patterns.md`
  - **DeFi protocols** → also read `defi-vectors.md`, then check the subcategory detection
    table inside it and load relevant `defi/*.md` files (multiple may apply — e.g., a lending
    protocol should load `defi-lending.md`, `defi-liquidation.md`, and `defi-oracle.md`)
  - **Accumulator / checkpoint / rewards / cross-module accounting signals** (grep: `last_update`, `checkpoint`, `cumulative`, `reward_manager`, `pool_reward`, `liquidity_mining`, `accumulated`) → also read `semantic-gap-checks.md` AND `defi/defi-math-precision.md` (for DEFI-85/86/87)

**Map the codebase:**
```
- List all modules
- List all public/entry functions (these are the attack surface — see table below)
- List all structs with key/store abilities (persistent state)
- List all capability types (objects that grant permissions)
- Identify any admin/owner patterns
- Identify any cross-module calls
- Estimate complexity: LoC, number of entry points, external dependencies
```

**Coverage Plan (mandatory):**
Use `checklist-router.md` to derive a coverage plan listing: detected chain, protocol families, feature flags, reference files loaded, and required follow-up passes. If a route fires, load the file.

**Production-surface parity gate (mandatory):** Enumerate all source attributes,
review compiler warnings, and compare the intended public API with the functions
present in the production bytecode/module artifact. Treat annotations, comments,
and helper naming as claims only. Audit every surviving supposedly non-production
helper using its actual visibility and authorization (common-move.md 1.7).

**Entry Point Classification:**
Attack surface differs by chain:

| Visibility | Sui (PTB-callable?) | Aptos (tx entry?) |
|------------|---------------------|-------------------|
| `public entry fun` | Yes — PTB + direct tx | Yes — transaction entry |
| `public fun` | **Yes — PTB-callable!** | **No** — module-callable only |
| `entry fun` | Yes — direct tx only | Yes — transaction only |
| `public(package) fun` | No — package-internal | No — package-internal |
| `fun` (private) | No | No |

**Critical Sui distinction:** ALL `public fun` on Sui are PTB-callable, making Sui's attack surface larger than Aptos.

**Access Control Classification — for each entry point:**
- **Sui:** Owned object with "Cap" in name → specific role; Owned object without "Cap" → Owner-gated; Shared object parameter with no cap → **Public/Unrestricted**
- **Aptos:** `signer::address_of` compared to stored address → Role-based; `exists<*Cap>(addr)` → Capability-based; `&signer` with NO address check → **Review Required** (see APT-24)
- Any function classified as Public/Unrestricted that mutates state → highest audit priority

**Build Detection & Test Log Analysis (conditional):**
Check if the project builds (`Move.toml` + `sui move build` or `aptos move compile`). If build succeeds (`BUILD_AVAILABLE = true`), run Test Log Analysis (common-move.md Section 13) to detect arithmetic aborts, assertion failures, and runtime anomalies. If build fails, note errors and skip.

**Output a one-paragraph codebase summary** (include build status) before proceeding.

---

### Phase 2 — Multi-Perspective Review

**Perspective 1 — The Attacker**
For each entry function:
- What inputs does it accept without validation?
- Can I pass an object I don't own?
- Can I bypass any `assert!` by constructing a specific state?
- Can I call this in a sequence that wasn't intended?
- (Aptos) Does any `public entry fun` accept `&signer` without ever calling `signer::address_of` for authorization? → APT-24
- (Sui) Is this a `public fun` (not just `entry`)? If so, it's PTB-composable — can it be chained with other calls to bypass per-call limits or create unexpected state?

**Perspective 2 — The Protocol Designer**
- What invariants does this protocol rely on?
- Which of those invariants are enforced on-chain vs. assumed off-chain?
- What happens if those assumptions break?

**Perspective 3 — The Integrator**
- If another protocol calls into this one, what can go wrong?
- Are there flash loan vectors?
- Can object references be reused or replayed across transactions?

**Perspective 4 — The Symmetry Checker**
For every pair of inverse operations, verify symmetry:
- deposit/withdraw: `withdraw(deposit(X)) <= X` always (rounding favors protocol)
- borrow/repay: `repay(borrow(X)) >= X` always (rounding favors protocol)
- mint/burn: `burn(mint(X)) <= X` always
- liquidation trigger/seize: same price oracle type, or bounded divergence
- rate limit add/reduce: reductions net against still-live usage across segment/window rollover
- admin update: only config changes, runtime state preserved
For each pair, check: (a) rounding direction, (b) state consistency, (c) oracle consistency, (d) access control symmetry

**Perspective 5 — The Bidirectional Admin Checker**
For every admin/privileged function that affects user funds or state, analyze BOTH directions:
- **Direction 1 — Admin harms users:** Can admin confiscate funds, lock positions, retroactively change terms, or brick user operations?
- **Direction 2 — Users grief admin:** Can users block admin cleanup, prevent pool closure, or make admin operations revert?
Both directions must be checked. Finding one does NOT mean the other doesn't exist. A close/reclaim function that checks `counter == 0` may be griefable by users (Direction 2) AND may confiscate from passive users whose entitlement isn't tracked by the counter (Direction 1).

**Perspective 6 — The Consistency Checker**
When a module uses an explicit safety pattern (e.g., `EDivideByZero` guard before division, bounds check on construction params, `assert!(amount > 0)` on inputs), check if sibling modules in the same package follow the same pattern. Inconsistencies are Low.
- Grep for the pattern across all modules in the package
- If Module A guards division with an explicit zero-check but Module B performing the same operation does not → flag the inconsistency
- Also applies to: error code usage, input validation, capability checks, event emission

---

### Phase 3 — Structured Vulnerability Scan

Before starting the per-check scan, confirm the coverage plan from `checklist-router.md`
is complete. If the codebase contains a signal with no corresponding deep check loaded,
fix the plan first.

Work through every check in `common-move.md`, then the chain-specific reference. For each check:

1. Search the codebase for the pattern
2. If found: record location, describe impact, assign severity
3. If clean: note it as verified

**Do not skip checks.** A clean check is still a check — mark it ✅.

**Fixed-Point Library Inspection Gate (MANDATORY — #1 missed critical bug class):**

Before completing Phase 3, you MUST complete ALL steps and output confirmation:

1. **Identify** all math helpers: grep for `float`, `decimal`, `wad`, `ray`, `fixed_point`, `Decimal`, `WAD`, `Float`
2. **Read internals** of each helper's `mul`, `div`, `from` — do NOT assume from name
3. **Derive overflow bound** for `mul(a,b)`: write the intermediate expression, simplify to raw input constraint (e.g., `A * B <= U64_MAX`)
4. **Find all call sites** of `mul()`. For each `A.mul(B)` chain: can `A * B` exceed the bound with realistic values? (token decimals: USDC=6, SUI=9, APT=8). Compute threshold table per DEFI-85
5. **Check checkpoint ordering** for each overflow-reachable site: abort BEFORE or AFTER checkpoint? If BEFORE → Recoverability Matrix (12.1)
6. **Output:** "FIXED-POINT GATE: [N] helpers, [M] call sites, [K] overflow-reachable" — if K > 0, include threshold table + recoverability

**Skipping this gate = missing permanent-deadlock bugs.** See 2.6, 12.1, DEFI-85–87.

**Dead Code / Unreachable Branch Detection:**
Before recording any finding that depends on a specific code branch:
1. **Is the branch reachable?** Trace all callers and all paths that set the condition variable.
   If a guard like `if (!X) { continue }` exists but X is invariantly true due to
   constructor/setter validation, the entire path after the guard is dead code.
2. **TODO comments describe aspirational features, not current bugs.** A TODO saying
   "skip check for non-collateral" doesn't mean non-collateral assets exist — it means
   the developer considered adding support but didn't.
3. **Do not report findings that require executing dead code.**

---

### Phase 4 — DeFi & Protocol-Specific Checks

If the protocol involves tokens, swaps, lending, staking, or oracles:
1. Read `defi-vectors.md` and run cross-cutting DeFi checks (DEFI-01 to DEFI-10)
2. Based on the subcategory detection table in `defi-vectors.md`, read relevant `defi/*.md` files
3. Run all checks from loaded subcategory files
4. Cross-reference DeFi findings with chain-specific patterns (e.g., SUI-02 + DEFI-14 for
   Sui staking flash attacks, APT-21 + DEFI-50 for Aptos liquidation reentrancy, SUI-21 + DEFI-29
   for denylist blocking repayment)

---

### Phase 5 — Semantic Gap & Stale-State Scan

If the protocol has multiple accounting variables, reward indices/accumulators, checkpoints, or lending state across modules — read `semantic-gap-checks.md` and run this phase. Mandatory for lending, staking, vault, reward, liquidation, and oracle-heavy protocols.

Required outputs: writer path, stale/mismatched consumer path, persistence window, numeric trace for any High/Critical candidate.

---

### Phase 6 — Cross-Module Interaction Scan

After completing per-file analysis, explicitly trace these interaction pairs.
For each pair, ask: does function A in module X leave module Y in an
inconsistent or permanently broken state?

Required pairs to check in every lending protocol audit.

**CHECK #1 IS HIGHEST PRIORITY — do it first, do it thoroughly:**

1. **[CRITICAL PRIORITY] reward_manager_update ↔ all lending operations** —
   Does the reward/accumulator update perform arithmetic that can abort BEFORE writing
   `last_update_time`? If ALL user operations (deposit/withdraw/borrow/repay/liquidate/claim)
   AND admin recovery (cancel/close) call this update → permanent deadlock.
   (→ 12.1, DEFI-85–87). Trace: overflow bounds, checkpoint ordering, admin recovery paths, threshold table.

2. **repay ↔ rewards/liquidity_mining** —
   When repay fully clears the last debt on an obligation (permissionless path),
   is the reward tracker for that obligation cleaned up?
   If not: orphaned tracker may block pool closure. (→ common-move.md 11.1)

3. **liquidate ↔ reserve (collateral reserve)** —
   Does the liquidation path check that the collateral reserve has
   idle cash >= seize_amount before calling `balance::split()` or equivalent?
   If not: liquidation reverts at high utilization → bad debt accumulates. (→ DEFI-81)

4. **adl ↔ emode** —
   Does the ADL entry condition and the ADL stop condition both read total borrows
   from the same source (both reserve-level OR both emode-group-level)?
   If different sources → wrongful liquidation or stuck ADL state. (→ DEFI-82)

5. **admin_config ↔ interest/reserve** —
   Does every admin function that updates a rate model or fee rate call
   `accrue_interest()` before applying the new value?
   If not → retroactive rate application, mispriced interest for all users. (→ DEFI-80)

6. **liquidate ↔ close_factor** —
   Is the close factor enforced per-TRANSACTION, not per-call?
   On Sui, PTBs allow calling liquidate() N times atomically. If close factor is
   checked against current (shrinking) debt, total liquidation = 1-(1-CF)^N. (→ SUI-28, DEFI-83)

7. **admin_config ↔ rate_limiters** —
   Does the config update function preserve accumulated runtime state (limiter segments,
   accumulators, counters)?
   If config update resets limiters → sandwich attack; if reducers only touch the current segment → rollover griefing. (→ DEFI-84, DEFI-90)

8. **oracle_eligibility ↔ oracle_seize** —
   Does liquidation use the same price type for both trigger and seize, OR enforce a
   bounded divergence between them?
   If borrow/withdraw enforce EMA-spot tolerance but liquidation does NOT → unbounded
   price divergence in the only operational code path during volatility. (→ DESIGN-L1 caveat)

9. **flash_loan ↔ deposit/borrow/withdraw** —
   Do operations during an active flash loan see stale accounting fields (cash, total_borrows)?
   If hot potato guarantees repayment, not updating cash is intentional (DESIGN-L2). But if
   other operations READ the stale value mid-PTB, they may misprice shares or health. (→ DESIGN-L2 caveat)

For any interaction pair where the answer is NO, record a candidate with the
exact missing invariant and affected path. Do not assign High/Critical until the
Phase 7 reachability, evidence, and impact gates pass. This phase is mandatory;
do not skip it because per-file scans were clean.

---

### Phase 7 — Verify & Triage (Move-Expert Validation)

Before reporting, every candidate finding from Phases 3-6 must survive a Move-expert
verification pass. This phase eliminates false positives, corrects inflated severities,
and ensures only real, exploitable findings reach the report.

Before verifying any finding, read `verification-policy.md`, `evidence-chains.md`,
and `confidence-gates.md`. Apply:

- evidence source tagging
- the mock rejection rule
- reachability gate
- math-bounds gate
- severity discipline for High/Critical

**Step 1 — Dual Narrative Test**

For each candidate finding, write two concrete stories:

- **Legitimate User Story:** How this code path behaves under normal Move usage —
  correct object ownership, valid signer, expected type parameters, intended call sequence.
- **Attacker Story:** Step-by-step exploitation using Move-specific primitives — exact
  function calls with type parameters, PTB composition steps (Sui) or transaction
  sequence (Aptos), object IDs/resource addresses involved, and the final extractable value.

**Rule:** If you cannot write a concrete attacker story with specific Move function calls,
object/resource interactions, and a quantified outcome — the finding is invalid. Move's
strict type system means vague "an attacker could..." stories are insufficient.

**Step 2 — Move-Expert Disproof (8 Dimensions)**

Systematically challenge each finding against Move's unique properties:

1. **Move Type System & Linearity** — Does Move's linear type system, borrow checker,
   or ability constraints already prevent this? Key Move eliminators:
   - No reentrancy via callbacks (no dynamic dispatch, no fallback functions)
   - No double-spend of resources (linearity enforces single ownership)
   - No capability forgery if abilities are correct (`key` only, no `copy`)
   - No type confusion if generic parameters are properly constrained
   - No storage collision (typed global storage / Sui UID-based objects)

2. **Call Path Completeness** — Trace the full call path including
   `public(package)`/`public(friend)` visibility. Does an upstream function already
   validate the input? Does a downstream `assert!` or abort prevent the exploit?
   Does the return type force the caller to handle it (hot-potato pattern)?

3. **Object/Resource Model** — Sui: is the target owned (only owner can access),
   shared (consensus-ordered), wrapped (inaccessible), or frozen (immutable)?
   Aptos: does `acquires` enforce exclusive access? Does `exists<T>(addr)` check
   prevent the setup? Ownership often makes EVM-style attacks infeasible.

4. **Execution Model Reality** — Move has no `delegatecall`, no callbacks, no dynamic
   dispatch, no inline assembly. Sui PTBs compose only through `public` interfaces —
   they cannot call `public(package)` functions. Does the finding assume EVM capabilities
   that Move doesn't have?

5. **Precondition Feasibility & Invariant Reachability** — Can the attacker reach the
   vulnerable state on mainnet?
   - Sui: shared object consensus ordering — can attacker reliably front-run?
   - Aptos: Block-STM parallel execution — does execution order matter?
   - Gas costs, object creation constraints, minimum amounts, time locks
   - Does attacker need a capability/object they cannot obtain?
   - **Invariant Reachability:** If the finding requires a field to have value X, find
     EVERY code path that sets that field and verify X is achievable. Check all
     constructors, setters, and validation guards. Pay special attention to parameter
     validation in admin/init functions — they often create invariants that make edge
     cases unreachable (e.g., `assert!(a < b)` on `u64` makes `b = 0` impossible).

6. **Economic Rationality** — Attack profit vs total cost (gas, flash loan fees, capital
   lockup, slippage, MEV competition). If `cost >= profit`, downgrade to Info. For Sui
   sandwich attacks: is the attacker a validator?

7. **Existing Protections Missed** — Did the scanner overlook:
   - `assert!` conditions in the function or its callees
   - Capability/signer gates on upstream entry points
   - Abort-on-overflow as implicit protection (prevents silent corruption, enables DoS)
   - Time/epoch locks, rate limits, cooldowns, minimum amounts
   - Admin pause mechanisms blocking the attack path
   - Move Prover `spec` blocks enforcing invariants

8. **Counterfactual Fix Test** — Apply your recommended fix mentally:
   - Does the fix change the **observable behavior**? If the transaction still aborts,
     the same funds are still locked, the same DoS occurs — the finding is cosmetic.
   - If downstream code would ALSO block the scenario independently of the bug,
     the bug has no incremental impact. Trace the FULL execution path PAST the
     buggy line — if the function fails at line N+5 anyway, the bug at line N
     is informational at best.
   - "Same value, different error code" is not a vulnerability — both produce
     transaction abort with identical user-facing outcome.

**Step 3 — Label Each Finding**

- **VALID** — Survives all checks. Exploitable on mainnet. Include at stated severity. Assign confidence: `confirmed` or `likely`.
- **QUESTIONABLE** — Plausible, but decisive proof is missing. Confidence: `needs_review`. Max severity: Medium.
- **DISMISSED** — Disproven by trusted local evidence (`[CODE]`, `[TEST]`, `[PROD-SOURCE]`, `[PROD-STATE]`).
- **OVERCLASSIFIED** — Real issue, severity inflated. Downgrade with reasoning. Re-assign confidence level.

**Step 4 — Mandatory Kill Questions**

Every finding labeled VALID or QUESTIONABLE must answer ALL of these. If any answer
is "no" or uncertain, downgrade or dismiss:

1. **Can I construct the precondition state through valid protocol operations?**
   Write the EXACT sequence of transactions. If you can't → INVALID.
2. **Does my recommended fix change observable behavior?**
   Apply the fix. Does the tx succeed now? Does the user get different output?
   If behavior is identical → INFORMATIONAL at best.
3. **For any function I claim "reverts when it shouldn't" — what would it DO if it
   didn't revert?** Would the result be meaningful? (e.g., ADL on zero-collateral:
   even without revert, seized=0, repaid=0 → no-op.)
4. **Is this a pattern used by established protocols (Compound, Aave, MakerDAO)?**
   If yes, load `defi/defi-lending-design-patterns.md` and check whether this is a
   known-good design. Explain why THIS protocol's context differs if reporting.
5. **Who loses money, how much, and under what conditions?**
   If you can't name a specific dollar impact and a specific victim → downgrade severity.
6. **Am I hallucinating this vulnerability?** Re-read the ACTUAL source code now.
   Does the code I'm referencing exist? Can I name exact file:line? Run the
   Self-Hallucination Check in `move-fp-catalog.md` Section 3. If any check fails → INVALID.

**Step 5 — Root-Cause Deduplication**

Before finalizing the finding list, group by the single LINE OF CODE that would
need to change, not by downstream effect:
- "Division by zero when X is zero" and "Function reverts when X is zero" → SAME finding
- "EMA/spot asymmetry" reported as tolerance bypass vs withdrawal blocking → SAME finding
- "Cash not updated" reported as exchange rate issue vs liquidity check issue → SAME finding

Keep only the highest-impact framing of each root cause.

**Step 6 — Post-Confirmation Parallel Subsystem Check**

After confirming any finding at Medium+ severity, check for parallel instances:
1. Identify the root-cause function containing the bug
2. Grep for ALL call sites of that function across the entire codebase
3. For each call site that enters through a DIFFERENT subsystem (deposit vs borrow, token0 vs token1, pool A vs pool B, group 0 vs group 1): does the same bug manifest through this path?
4. If yes → expand the finding to cover all affected subsystems or note the parallel instances explicitly

This is mandatory because bugs in shared logic affect ALL consumers, not just the first path discovered.

**Evidence Audit (mandatory):** For every non-trivial finding, include a short evidence
table from `verification-policy.md` showing each decisive claim and its source tag.

**Output rules:** Only VALID and QUESTIONABLE findings proceed to Phase 8.
DISMISSED findings go to "Verified Clean Checks" with dismissal reason.
OVERCLASSIFIED findings proceed at adjusted severity.

---

### Phase 8 — Report

Write the report using `sample-finding.md` as the output template. Include chain,
date, severity and triage summaries, exact locations, evidence, concrete Sui PTB
or Aptos transaction scenarios, recommended fixes, `Verified Clean Checks`, and
`Auditor Notes`. Severity must reflect realistic impact and likelihood; routine
admin-originated liveness failures are rated by the users and operations they
block, not merely by who set the triggering parameter.

## Non-Negotiable Quality Rules

- Cite exact file, line, function, and root cause; never invent code or behavior.
- Require a concrete PoC or transaction sequence for every High/Critical finding;
  downgrade when the path or impact cannot be demonstrated.
- Keep source, test, production-state, and documentation evidence explicitly
  tagged; documentation or mocks cannot independently dismiss a finding.
- Do not modify audited production code unless the user explicitly requests a
  fix; use removable tests or harnesses for local proofs.
- State that AI-assisted findings require human verification before disclosure.

