Skip to content

sync(audit): port toly upstream/master 3969c31..2052807 to fork-engine — 6 of 7 audit-validated SF ports + ENG-PORT-4 fixup - #90

Merged
dcccrypto merged 9 commits into
mainfrom
feat/sync/engine-bulk-2026-05
May 10, 2026
Merged

sync(audit): port toly upstream/master 3969c31..2052807 to fork-engine — 6 of 7 audit-validated SF ports + ENG-PORT-4 fixup#90
dcccrypto merged 9 commits into
mainfrom
feat/sync/engine-bulk-2026-05

Conversation

@dcccrypto

@dcccrypto dcccrypto commented May 10, 2026

Copy link
Copy Markdown
Owner

Summary

Bulk sync of toly upstream aeyakovenko/percolator master into dcccrypto/percolator main, executed via per-fn manual port (Option 3 from the bulk-sync strategy) after the naive git merge approach catastrophically violated KL deferrals on attempt #1 (see ~/wrapper-engine-deep-audit/SYNC_FAILURE_LOG.md).

Closes 6 of 7 audit-validated security ports + 1 deferred. Includes ENG-PORT-4 test fixup + 1 cleanup commit.

Source / target

Field Value
Source upstream/master at 2052807 ("Fix terminal epoch recovery close", 2026-05-09)
Target base origin/main at c2a8a9a (PR #88 v12.19-engine sync, 2026-04-28)
Merge-base 3969c31 ("Update full Kani audit timings", 2026-04-25)
Fork commits ahead of merge-base 168
Toly commits ahead of merge-base 88
Sync branch feat/sync/engine-bulk-2026-05
Approach Per-fn manual port (Option 3) — see SYNC_FAILURE_LOG.md for why naive git merge was abandoned

PR #88 already absorbed 96 toly commits through 3969c31 via its merge: upstream/master @ 3969c31 (v12.19 baseline) into fork main commit, so this sync is the remaining 88 toly commits — most of which are documentation, Kani test refinements, or KL-deferred subsystem evolution.

Commits (9 total, in chronological order)

# Commit Closes
1 7ef8be7 ENG-PORT-1 (CRITICAL-5) — withdraw_live_insurance_not_atomic empty-market gate
2 486381d ENG-PORT-3 prerequisite — account_has_unsettled_live_effects helper
3 60bedc6 ENG-PORT-2 (CRITICAL-6) — sync_account_fee loss-safe anchor + ensure_fee_draw_does_not_precede_loss
4 61938f2 ENG-PORT-3 (CRITICAL-7) — wire 4 callsites
5 0206a59 ENG-PORT-4 (CRITICAL-8) — enforce_side_mode_oi_gate 6-arg + enforce_side_mode_account_gate
6 e2f1c67 ENG-PORT-4 fixup — 9 test callsites updated for 6-arg signature
7 fe1d6fd ENG-PORT-5a (CRITICAL-9) — resolve_market_not_atomic 4 SF hunks (pre-state, K-zero, dust ADAPTED, drain-guard)
8 7d7689f Port 6 — force_close_resolved_not_atomic position-basis early-return
9 dac8bf6 Cleanup — remove last_crank_slot + gc_cursor refs from examples/detailed_offsets.rs (pre-existing build break)

ADAPTED ports per KEEP_LIST

Three commits applied adapted ports because toly's hunks reference KL-deferred subsystems:

  • account_has_unsettled_live_effects (commit 2) — toly's 5-predicate form drops to 4-predicate (epoch / A / K / F) per KL-FORK-ENGINE-B-TRACKING-1. The B-snap predicate is SKIPPED; fork has no B-tracking subsystem.
  • withdraw_live_insurance_not_atomic (commit 1) — toly's 11-condition gate drops to 8 conditions per KL-FORK-ENGINE-BANKRUPT-CLOSE-1 + KL-FORK-ENGINE-STRESS-ENVELOPE-1. The 3 SKIPPED conditions reference fields fork doesn't have (active_close_present, stress_consumed_bps_e9_since_envelope, bankruptcy_hmax_lock_active).
  • resolve_market_not_atomic (commit 7) — toly's certified+potential phantom-dust zero block ADAPTS to fork's single-bound schema (phantom_dust_bound_<side>_q = 0 instead of clearing the toly-side 4 fields) per KL-PHANTOM-DUST-SCHEMA-1.

Deferred (with documentation)

Item Parent KL Why deferred
Port 7 — credit_account_from_insurance_not_atomic live_reconciliation_lock_active KL-FORK-ENGINE-BANKRUPT-CLOSE-1 + KL-FORK-ENGINE-STRESS-ENVELOPE-1 Helper depends on 3 KL-deferred subsystems. The "pre-state assertion + envelope check" portions of Port 7 were already absorbed by PR #88 — only the lock-active call remains, and that defers entirely.
Toly commit 2052807 "Fix terminal epoch recovery close" KL-FORK-ENGINE-B-TRACKING-1 + KL-FORK-ENGINE-FIELDS Entirely within toly-only b_epoch_snap / loss_weight_sum / social_loss_remainder_* machinery. SKIPS for fork.
record_uninsured_protocol_loss durable accumulator KL-FORK-ENGINE-FIELDS Requires adding explicit_unallocated_protocol_loss + explicit_unallocated_loss_saturated fields to RiskEngine, which would BREAK the NFT compile-time EXPECTED_RISK_ENGINE_SIZE assertion. SKIP per NFT_BASELINE.md.
All ensure_no_active_bankrupt_close / loss_stale_positive_pnl_lock_active / stress_gate_active checks (~7 hunks across multiple fns) KL-FORK-ENGINE-BANKRUPT-CLOSE-1 / KL-FORK-ENGINE-STRESS-ENVELOPE-1 / KL-FORK-ENGINE-FIELDS Subsystem ports for ENG-PORT-5b.

Verification (all 5 gates green at HEAD)

Gate Result
cargo build --release ✅ clean (26 dead-code warnings, none fatal)
cargo test --features test (FULL — incl. examples after cleanup) 335 passed / 0 failed (matches Phase 0 baseline exactly)
scripts/verify-engine-preservation.sh ✅ silent (all 39 fork-only customizations preserved)
scripts/verify-engine-deferred-absent.sh ✅ silent (no toly-only-deferred subsystem leaked)
scripts/verify-eng-port-preservation.sh ✅ silent (ENG-PORT-1..5a artifacts present)

NFT regression check (Phase 1.3): cargo build --release on ~/percolator-nft succeeded — all 54 compile-time size_of!/offset_of! assertions hold. Symbol-level diff identical to ~/wrapper-engine-deep-audit/NFT_BASELINE.diff.txt (0 regression lines).

Out of scope (deferred subsystems)

Per ~/wrapper-engine-deep-audit/KEEP_LIST.md:

  • Bankrupt-close subsystem (KL-FORK-ENGINE-BANKRUPT-CLOSE-1) — active_close_* fields + ensure_no_active_bankrupt_close helper + bankruptcy_hmax_lock_active. Deferred to ENG-PORT-5b.
  • Stress envelope subsystem (KL-FORK-ENGINE-STRESS-ENVELOPE-1) — stress_consumed_bps_e9_since_envelope + stress_envelope_* fields + clear_stress_envelope helper. Deferred to ENG-PORT-5b.
  • B-index / social-loss subsystem (KL-FORK-ENGINE-B-TRACKING-1, KL-FORK-ENGINE-FIELDS) — 26 toly-only RiskEngine fields + b_target_for_account + account_has_unsettled_b + record_uninsured_protocol_loss durable accumulator + social_loss_remainder_* machinery + proofs_b_index.rs test file.
  • Phantom-dust certified/potential split (KL-PHANTOM-DUST-SCHEMA-1) — fork keeps single-bound; ADAPTED port rule encoded.
  • RiskError::RecoveryRequired variant + EngineRecoveryRequired PercolatorError (KL-FORK-ENGINE-BANKRUPT-CLOSE-1) — orphan with parent.
  • Dynamic trade-fee API (KL-DYNAMIC-TRADE-FEE-1) — fork keeps static trading_fee_bps model.

Test plan

  • cargo build --release — clean
  • cargo test --features test — 335/0/0 (full, no --lib --tests workaround needed)
  • All 3 verify scripts silent
  • NFT regression check passes
  • Pending pre-merge: full Kani suite (cargo kani --workspace) — deferred to post-PR-review verification gate; reviewer can run before merge
  • Pre-merge: cargo build for ~/percolator-prog (wrapper) against this engine — confirms wrapper still builds via path dep (Phase 2 verification)

Cross-references

  • ~/wrapper-engine-deep-audit/AUDIT_WORK_PRESERVATION.md — confirms the prior session's 5 ENG-PORT commits are subsumed
  • ~/wrapper-engine-deep-audit/SYNC_FAILURE_LOG.md — records why naive git merge was abandoned
  • ~/wrapper-engine-deep-audit/FORK_INVENTORY.md §5 — conflict-zone table
  • ~/wrapper-engine-deep-audit/KEEP_LIST.md — 34 KL-* groups (the audit's semantic rules; per-hunk porting honors each one)
  • ~/wrapper-engine-deep-audit/NFT_BASELINE.md — regression-detection baseline (passed)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced safeguards for Live-mode account operations to prevent inconsistent settlement states.
    • Improved fee logic to prevent fee charges from exceeding realized losses.
    • Strengthened side-mode trading restrictions with account-level validation.
    • Stricter validation for market resolution and account closure procedures.
  • Tests

    • Updated test coverage for revised trading validation and fee mechanisms.

Review Change Stack

dcccrypto and others added 9 commits May 10, 2026 01:23
…te — closes ENG-PORT-1 (CRITICAL-5)

Empty-market gate from toly upstream (toly-engine:10250-10292) — refuses live insurance
withdrawal unless market is provably empty + fully accrued. ADAPTED port: 3 toly-only
conditions (active_close_present, stress_consumed_bps_e9_since_envelope,
bankruptcy_hmax_lock_active) SKIPPED per KL-FORK-ENGINE-BANKRUPT-CLOSE-1 +
KL-FORK-ENGINE-STRESS-ENVELOPE-1 (fork has no such fields).

8 surviving conditions all map to existing fork RiskEngine fields:
- oi_eff_long_q == 0 && oi_eff_short_q == 0   (no live OI)
- stored_pos_count_long == 0 && stored_pos_count_short == 0   (no stored positions)
- stale_account_count_long == 0 && stale_account_count_short == 0   (no stale accounts)
- neg_pnl_account_count == 0   (no negative-PnL accounts)
- current_slot == last_market_slot   (market accrued)

Without this gate, an admin holding insurance_authority can drain the live insurance fund
while users hold positions whose maintenance margin assumes that insurance is there. With
8 conditions enforced, the fund is only drainable when the market has no live obligations.

Cross-ref: ENGINE_BODY_DIFF.md §Vault accounting Hunk 1 (CRITICAL severity), AUDIT_WORK_PRESERVATION.md row 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… 4-predicate form) — ENG-PORT-3 prerequisite

Adds toly's account_has_unsettled_live_effects predicate (toly-engine:4884) as a
post-touch invariant detector for sync_account_fee_to_slot_not_atomic, execute_trade,
withdraw, convert_released_pnl, and close_account.

ADAPTED port per KL-FORK-ENGINE-B-TRACKING-1: toly's 5th predicate (B-snap mismatch via
b_target_for_account) is SKIPPED — fork has no B-tracking subsystem. The 4 retained
predicates (epoch / A / K / F) catch K/F/A_basis/epoch staleness; the residual narrow
case (B drift with epoch/A/K/F in sync) is documented in KL-FORK-ENGINE-B-TRACKING-1.

This is the helper-definition commit; callsite wiring (4 callsites) follows in subsequent
commits as Port 2 (sync_account_fee) and Port 3-callsites lands on the 4 trade/withdraw/
convert/close paths.

Cross-ref: ENGINE_BODY_DIFF.md §execute_trade_not_atomic Hunk 2; AUDIT_WORK_PRESERVATION.md row 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…w_does_not_precede_loss helper — closes ENG-PORT-2 (CRITICAL-6)

Closes the loss-juniority bug in sync_account_fee_to_slot_not_atomic. Without these
changes, the fee cursor could advance past last_market_slot and charge fees for slots
the market hasn't accrued through, ranking the fee draw senior to losses that should
have been booked first.

Three sub-changes:

1. New helper ensure_fee_draw_does_not_precede_loss (toly-engine equivalent):
   - Rejects with Undercollateralized if pnl < 0 (realized loss already on the account)
   - On Live, also rejects if account_has_unsettled_live_effects (epoch/A/K/F drift —
     unrealized loss not yet booked). ADAPTED 4-predicate form per
     KL-FORK-ENGINE-B-TRACKING-1; B-snap predicate dropped (no fork B-tracking subsystem).

2. live_loss_safe_anchor block in sync_account_fee_to_slot_not_atomic:
   - When Live + now_slot > last_market_slot + account holds position basis, anchor
     to last_market_slot instead of now_slot.
   - Threaded through both shape_anchor (validation) and the final commit anchor.

3. Loss-juniority guard call when fee_rate_per_slot > 0 && shape_anchor > last_fee_slot.

Plus a defensive narrowing of the live envelope check to fire only on now_slot >
current_slot (avoids redundant envelope check on repeated same-slot fee syncs).

Cross-ref: ENGINE_BODY_DIFF.md §sync_account_fee_to_slot_not_atomic (CRITICAL severity);
AUDIT_WORK_PRESERVATION.md row 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…— closes ENG-PORT-3 (CRITICAL-7)

Adds the post-touch invariant guard at 4 fork-engine entrypoints that mutate account
state through touch_account_live_local. Each guard rejects with Undercollateralized
before any downstream mutation that would otherwise operate on stale K/F/A_basis/epoch.

- execute_trade_not_atomic: both counterparties checked after the dual touch (Step 11).
  Prevents trade attaching at stale basis when a side mode change post-touch leaves
  K/F drifted.
- withdraw_not_atomic: post-touch, before finalize_touched (which sweeps fees against
  capital using current K/F).
- convert_released_pnl_not_atomic: post-touch, before convert_released_pnl_core
  mutates released-PnL bookkeeping.
- close_account_not_atomic: post-touch, before effective_pos_q_checked (which derives
  from A_basis/K snaps) is used in the position==0 check.

Defense-in-depth post-touch invariant — does not fire on any current happy-path
because touch_account_live_local correctly settles snaps. Catches regressions in the
touch machinery before they propagate to the trade/withdraw/convert/close mutation
paths. ADAPTED 4-predicate form per KL-FORK-ENGINE-B-TRACKING-1; B-snap predicate
absent (no fork B-tracking subsystem). Residual narrow case (B drift with epoch/A/K/F
in sync) closes when the B-tracking subsystem ports.

Cross-ref: ENGINE_BODY_DIFF.md §execute_trade_not_atomic Hunk 2 (HIGH severity);
AUDIT_WORK_PRESERVATION.md row 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ew account_gate helper — closes ENG-PORT-4 (CRITICAL-8)

Closes the maker-replaces-taker hazard in enforce_side_mode_oi_gate. The pre-port
aggregate-OI gate alone admits a trade where one counterparty closes long (releasing
OI) while the other opens long (consuming the same OI) — net aggregate OI change
is zero, so the side-mode (DrainOnly / ResetPending) check passes, even though a
NEW account just landed on a side mid-reset.

Three sub-changes (toly-engine equivalent at toly-engine:8021-8063):

1. New helper enforce_side_mode_account_gate(old_eff, new_eff). Verbatim port.
   Rejects any new entrant onto a ResetPending side regardless of OI movement;
   on DrainOnly, rejects unless the account is reducing its existing same-side
   basis.

2. enforce_side_mode_oi_gate widens to 6-arg signature with per-account positions
   (old_eff_a, new_eff_a, old_eff_b, new_eff_b, oi_long_after, oi_short_after).
   Body keeps the existing aggregate-OI checks AND adds the account-gate calls
   for both counterparties.

3. The single execute_trade_not_atomic callsite now passes the per-account
   positions captured earlier in the trade flow.

The gate is additive: it widens what gets rejected without weakening what gets
accepted. All happy paths are unaffected.

Test callsites in tests/proofs_*.rs (#![cfg(kani)]-gated) need the 4-arg expansion
— follow-up commit "ENG-PORT-4 fixup" updates them.

Cross-ref: ENGINE_BODY_DIFF.md §enforce_side_mode_oi_gate Hunk 1 (HIGH severity);
AUDIT_WORK_PRESERVATION.md row 4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ignature — ENG-PORT-4 fixup

Mechanical update — ENG-PORT-4 widened the gate signature from 2-arg
(oi_long_after, oi_short_after) to 6-arg (old_eff_a, new_eff_a, old_eff_b, new_eff_b,
oi_long_after, oi_short_after). Without these test updates the engine library still
builds clean, but proofs_*.rs Kani harnesses fail to compile.

- proofs_invariants.rs:580/585/594/599/607 (proof_side_mode_gating): pass 0i128
  per-account positions; the test isolates the aggregate-OI gate, and the per-account
  gate is a no-op when new_eff == 0.
- proofs_instructions.rs:596: pass per-account positions in (b, a) order matching
  the bilateral_oi_after call directly above.
- proofs_liveness.rs:69, 79: pass (old_a, new_a, old_b, new_b) in scope.
- proofs_checklist.rs:393: pass (eff_a_before, new_eff_a, eff_b_before, new_eff_b)
  in scope.

Verification:
- cargo build --release: clean (lib unchanged from ENG-PORT-4 production commit).
- Kani build verification deferred to the post-port verification gate (cargo kani
  --workspace, run after all 7 ports + cleanup commit).

Cross-ref: AUDIT_WORK_PRESERVATION.md row 5 (the original 38b551e fixup commit had
a 4-file pattern; this fixup matches plus adds proofs_checklist.rs:393 which the
prior session also touched).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-zero, dust-zero ADAPTED, drain-guard) — closes ENG-PORT-5a (CRITICAL-9)

4 SF hunks from toly upstream's resolve_market_not_atomic body (toly-engine:9518-9674).
2 sibling toly hunks (ensure_no_active_bankrupt_close, clear_stress_envelope) are
SKIPPED per KL parents — fork has neither subsystem.

Hunks ported:

1. Hunk 2 — pre-state snapshot. 8 locals (pre_mode_long/short, pre_a_long/short,
   pre_oi_long/short, pre_stored_long/short) capture the pre-resolve view BEFORE
   any mutation. Downstream gates read snapshots so per-side decisions reflect
   the pre-resolve state.

2. Hunk 3 — K-terminal-delta zero-on-zero-OI. A side with no pre-resolve OI
   cannot accumulate a non-zero terminal delta (would attribute a settlement
   shift to non-existent positions). Forces delta to 0 when pre_oi_<side> == 0.

3. Hunk 5 — phantom-dust zero on pre_stored_<side> == 0. ADAPTED to fork's
   single-bound schema per KL-PHANTOM-DUST-SCHEMA-1 (toly uses certified+
   potential pair; fork uses single-bound phantom_dust_bound_<side>_q).
   Semantically equivalent: no stored positions ⇒ no dust to track.

4. Hunk 6 — drain-reset pre_stored guard. Only enter drain reset when
   pre_stored_<side> > 0. The pre-port code unconditionally entered drain
   even on sides with zero stored positions, performing spurious side-mode
   transitions.

Hunks SKIPPED (per parent KL — see KEEP_LIST):

- Hunk 1 (ensure_no_active_bankrupt_close) — KL-FORK-ENGINE-BANKRUPT-CLOSE-1
  (no fork bankrupt-close subsystem; deferred to ENG-PORT-5b).
- Hunk 4 (clear_stress_envelope) — KL-FORK-ENGINE-STRESS-ENVELOPE-1
  (no fork stress-envelope subsystem; deferred to ENG-PORT-5b).

Cross-ref: ENGINE_BODY_DIFF.md §resolve_market_not_atomic Hunks 2/3/5/6
(SECURITY-FORWARD HIGH/MEDIUM); AUDIT_WORK_PRESERVATION.md row 6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ly-return — closes Port 6

SF guard from toly upstream's force_close_resolved_with_fee_not_atomic body. Refuses
terminal close when reconcile_resolved_not_atomic left position_basis_q non-zero —
guards against partial-progress reconcile (e.g., social loss spread that didn't fully
unwind basis). Without this guard, close_resolved_terminal_not_atomic silently
mis-accounts the residual, OR fails later in a less recoverable way.

Toly's matching fee-charging line (sync_account_fee_to_slot at resolved_slot) is
FEATURE-DIVERGENCE per ENGINE_BODY_DIFF Hunk 3 — SKIPPED. Fork doesn't charge
maintenance fees at resolved close.

Cross-ref: ENGINE_BODY_DIFF.md §force_close_resolved_not_atomic Hunk 2 (SECURITY-FORWARD HIGH);
audit-validated independent of bankruptcy/stress/active-close subsystems.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…offsets.rs example — restore cargo test clean

Pre-existing build break in fork-only example examples/detailed_offsets.rs (added in
9340977 "chore: add detailed_offsets example for layout verification"). Both fields
were removed without rename in the v12.1 sync (9a1c627 + 84a9293):
- last_crank_slot retired; conceptually replaced by last_market_slot (already printed
  on a separate line in this file)
- gc_cursor retired in 84a9293 ("Simplify ADL: replace windowed crank with full-scan")

Cleanest fix per Phase 0 baseline analysis: delete the 2 broken lines, leaving the
rest of the example intact (it correctly references current RiskEngine fields incl.
fork-only phantom_dust_bound_<side>_q, materialized_account_count, etc.).

Restores `cargo test --features test` to clean (was failing at compile due to E0609
errors in the example), so the post-Phase-1 verification gate uses the standard
SESSION_START_CHECKLIST baseline command without the --lib --tests workaround.

Per BULK_SYNC_PLAN.md Phase 0 finding #3: applied as final cleanup commit on the
sync branch before push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request strengthens the RiskEngine's invariant enforcement by introducing post-touch guards that detect Live-mode account inconsistencies, hardening fee juniority and side-mode account-level entrant restrictions across withdraw, trade, convert, close, fee synchronization, and market resolution operations.

Changes

Risk Engine Invariant Guards & Side-Mode Hardening

Layer / File(s) Summary
New Invariant Guards (API Layer)
src/percolator.rs
Three test-visible helpers added: account_has_unsettled_live_effects detects epoch/snapshot mismatches, ensure_fee_draw_does_not_precede_loss blocks fee draws when loss precedes, enforce_side_mode_account_gate enforces per-account entrant restrictions. Signature of enforce_side_mode_oi_gate expanded from 2 to 6 arguments to include per-account old/new effective positions.
Post-Touch Invariant Enforcement
src/percolator.rs
withdraw_not_atomic, convert_released_pnl_not_atomic, and close_account_not_atomic now reject operations after touch when the account has unsettled Live effects, preventing inconsistent state.
Trade Side-Mode Hardening
src/percolator.rs
execute_trade_not_atomic adds pre-trade unsettled-effect checks for both counterparties and updates enforce_side_mode_oi_gate call to pass per-account old/new positions, enabling account-level entrant gate instead of aggregate-only gating.
Fee Synchronization & Resolution
src/percolator.rs
sync_account_fee_to_slot_not_atomic introduces "loss-safe fee anchor" using last_market_slot in Live mode when position basis is non-zero. resolve_market_not_atomic snapshots pre-state for K-delta computation and gates drain-reset by pre-stored side. force_close_resolved_not_atomic early-returns ProgressOnly when position basis remains non-zero. withdraw_live_insurance_not_atomic includes stricter empty-market gate requiring slot equality.
Test Updates & Documentation
tests/proofs_checklist.rs, tests/proofs_instructions.rs, tests/proofs_invariants.rs, tests/proofs_liveness.rs, examples/detailed_offsets.rs
All proof tests updated to match new 6-argument enforce_side_mode_oi_gate signature. New Kani proof proof_fee_credits_never_i128_min verifies fee_debt_u128_checked safety for all i128 inputs. Example file comments document last_crank_slot rename to last_market_slot and gc_cursor retirement.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • dcccrypto/percolator#68: Introduces last_market_slot and settlement fields that this PR builds upon for loss-safe fee anchoring.
  • dcccrypto/percolator#87: Related changes to side-mode OI gating logic and the same enforce_side_mode_oi_gate signature expansion and test updates.
  • dcccrypto/percolator#88: Parallel modifications to RiskEngine side-mode gating API with matching 6-argument enforce_side_mode_oi_gate signature updates across tests.

Poem

🐰 A rabbit's ode to invariants bold:
Post-touch guards hold accounts true,
No unsettled effects slip through,
Fees know their place, losses come first,
And side modes yield to per-account thirst!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a bulk sync of upstream commits into the fork-engine, specifically noting it covers 6 of 7 audit-validated ports plus a fixup. This directly aligns with the changeset's core objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sync/engine-bulk-2026-05

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/percolator.rs`:
- Around line 4924-4935: The predicate in account_has_unsettled_live_effects
currently flags adl_a_basis drift as an unsettled live effect, but adl_a_basis
is intentionally left stale by settle_side_effects_live unless q_eff_new == 0;
remove the adl_a_basis comparison to avoid false positives. Specifically, in
account_has_unsettled_live_effects (which already checks position_basis_q,
adl_epoch_snap vs get_epoch_side, and compares adl_k_snap/get_k_side and
f_snap/get_f_side), drop the check "account.adl_a_basis !=
self.get_a_side(side)"; alternatively, if you prefer to keep the check, update
the live-touch path in settle_side_effects_live to fully refresh/rewrite
position_basis_q and adl_a_basis before these guards run. Ensure references to
adl_a_basis, adl_k_snap, f_snap, adl_epoch_snap, position_basis_q,
settle_side_effects_live, get_a_side, get_k_side, get_f_side, and get_epoch_side
are used to locate the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6251db56-8e10-4e85-8694-4b032b2e216f

📥 Commits

Reviewing files that changed from the base of the PR and between c2a8a9a and dac8bf6.

📒 Files selected for processing (6)
  • examples/detailed_offsets.rs
  • src/percolator.rs
  • tests/proofs_checklist.rs
  • tests/proofs_instructions.rs
  • tests/proofs_invariants.rs
  • tests/proofs_liveness.rs

Comment thread src/percolator.rs
Comment on lines +4924 to +4935
fn account_has_unsettled_live_effects(&self, idx: usize) -> Result<bool> {
let account = &self.accounts[idx];
if account.position_basis_q == 0 {
return Ok(false);
}
let side = side_of_i128(account.position_basis_q).ok_or(RiskError::CorruptState)?;
if account.adl_epoch_snap != self.get_epoch_side(side) {
return Ok(true);
}
Ok(account.adl_a_basis != self.get_a_side(side)
|| account.adl_k_snap != self.get_k_side(side)
|| account.f_snap != self.get_f_side(side))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

adl_a_basis drift is a false positive here.

In this engine, same-epoch ADL shrink is represented lazily: settle_side_effects_live refreshes adl_k_snap/f_snap/adl_epoch_snap, but it does not rewrite position_basis_q or adl_a_basis unless q_eff_new == 0. That makes account.adl_a_basis != self.get_a_side(side) a normal post-touch state, not an inconsistency. With this predicate in place, any account that has seen ordinary ADL shrink will start tripping the new guards and get blocked from withdraw/trade/convert/close and recurring fee sync even though its state is otherwise canonical. Either drop the A-basis comparison here, or make the live-touch path fully reattach basis/A snapshots before these guards run.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/percolator.rs` around lines 4924 - 4935, The predicate in
account_has_unsettled_live_effects currently flags adl_a_basis drift as an
unsettled live effect, but adl_a_basis is intentionally left stale by
settle_side_effects_live unless q_eff_new == 0; remove the adl_a_basis
comparison to avoid false positives. Specifically, in
account_has_unsettled_live_effects (which already checks position_basis_q,
adl_epoch_snap vs get_epoch_side, and compares adl_k_snap/get_k_side and
f_snap/get_f_side), drop the check "account.adl_a_basis !=
self.get_a_side(side)"; alternatively, if you prefer to keep the check, update
the live-touch path in settle_side_effects_live to fully refresh/rewrite
position_basis_q and adl_a_basis before these guards run. Ensure references to
adl_a_basis, adl_k_snap, f_snap, adl_epoch_snap, position_basis_q,
settle_side_effects_live, get_a_side, get_k_side, get_f_side, and get_epoch_side
are used to locate the changes.

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