Skip to content

feat: add --per-depth-timeout option for kontrol prove#1141

Open
Stevengre wants to merge 2 commits into
masterfrom
progressive-depth-timeout
Open

feat: add --per-depth-timeout option for kontrol prove#1141
Stevengre wants to merge 2 commits into
masterfrom
progressive-depth-timeout

Conversation

@Stevengre
Copy link
Copy Markdown
Contributor

@Stevengre Stevengre commented May 12, 2026

Summary

Adds --per-depth-timeout SECONDS to kontrol prove. Each prove attempt runs in a forked subprocess with current_depth * per_depth_timeout seconds as a stall window: as long as the proof keeps writing to disk (any file under proofs_dir/<test.id> gets a new mtime), the window resets. When a full window passes with no writes, the whole session is SIGKILL'd, current_depth is halved (floor 1), and the next attempt resumes from the disk-persisted KCFG. Default 0 disables the wrapper.

Example: --max-depth 1000 --per-depth-timeout 0.5 → 500s window at depth 1000, halving to 250s @ 500, … 0.5s @ 1.

Why this shape

  • Stall window, not total budget — progressive halving exists to detect when execute_depth is too coarse to break out of a stuck step. As long as new nodes appear, we leave the proof alone.
  • Subprocess + session SIGKILL, not in-process callbackadvance_proof's maintenance callback only fires after step_proof returns. A single long step (deep symbolic execution, expensive SMT) is uninterruptible from inside the prover. Killing the session is the only way to enforce wall-clock regardless of what the prover is doing.

Implementation

  • cli.py / options.py: new --per-depth-timeout flag and ProveOptions.per_depth_timeout field (default 0).
  • prove.py: when per_depth_timeout > 0, init_and_run_proof dispatches to _init_and_run_proof_progressive, which loops over halving depths via _run_attempt_under_timeout:
    • mp.get_context('fork').Process → child inherits everything via CoW; child os.setsid() so the parent's killpg reaps Python + KoreServer + worker threads.
    • Parent calls proc.join(timeout=budget_s) then compares max mtime under proofs_dir/<test.id> against the previous snapshot. Changed → another window. Unchanged → SIGKILL, halve, retry.

Test plan

  • kontrol prove --help lists --per-depth-timeout with documented behavior.
  • Default-off: kontrol prove --match-test TokenValueLibProofs.init --max-depth 100 (no --per-depth-timeout) passed in 28s; process tree showed a single python + one kore-rpc-booster (no fork), confirming the run_prover path is unchanged.
  • Stall detection on a real stuck proof (vault-contracts-aave-v4 test_amountToValueDown, --per-depth-timeout 0.5 --max-depth 1000): observed 10 halvings 1000→1, KCFG advanced 1437 steps in the first attempt then stayed pinned at the deepest unresolvable node — proof stopped exactly where it should.
  • No false positives on a productive proof (steady progress completes without being killed).
  • Cleanup: after the 10-cycle halving run above, pgrep kore-rpc-booster returned 0 — session-group SIGKILL reaped every kore-rpc subprocess across all kill cycles.

Caveats

  • POSIX-only (already a kontrol requirement).
  • Each halving spawns a fresh KoreServer (a few seconds startup overhead per attempt).
  • proof.write_proof_data() is assumed atomic (temp + rename); a SIGKILL mid-write would at worst lose one step.
  • Kill latency ≤ one extra budget_s window beyond actual stall onset.

@Stevengre Stevengre force-pushed the progressive-depth-timeout branch from 1f0cc6a to 36d97eb Compare May 13, 2026 02:38
@Stevengre Stevengre force-pushed the progressive-depth-timeout branch from 36d97eb to a20aacb Compare May 26, 2026 03:07
@Stevengre Stevengre force-pushed the progressive-depth-timeout branch from a20aacb to bd56284 Compare May 26, 2026 09:55
@Stevengre Stevengre changed the title feat: add --per-depth-timeout option for progressive depth-halving prove feat: add --per-depth-timeout option for kontrol prove (blocked by kevm-pyk #2850) May 26, 2026
@Stevengre Stevengre force-pushed the progressive-depth-timeout branch from bd56284 to a0fce21 Compare May 26, 2026 12:16
@Stevengre Stevengre changed the title feat: add --per-depth-timeout option for kontrol prove (blocked by kevm-pyk #2850) feat: add --per-depth-timeout option for kontrol prove May 26, 2026
@Stevengre Stevengre force-pushed the progressive-depth-timeout branch 8 times, most recently from 71e977c to 3a1e97d Compare May 29, 2026 08:45
Adds progressive depth-halving with a per-attempt **stall window**.
When `--per-depth-timeout S` is set, each attempt is given an initial
window of `current_depth * S` seconds. The parent polls the proof's
on-disk subdir every window: if any file mtime changed (i.e. the prove
committed at least one step), the window is reset for another round.
If no progress is observed across a full window, the entire subprocess
session is reaped with `os.killpg(..., SIGKILL)` (Python + KoreServer
+ parallel-frontier worker threads), `current_depth` is halved (floor
1), and the next attempt resumes from the disk-persisted KCFG state.

Each attempt runs in a forked subprocess that calls `os.setsid()` to
become its own session leader, so a single `killpg` reaps the entire
subtree. The proof state is persisted by `advance_proof`'s maintenance
loop (maintenance_rate=1, default), so on-disk KCFG state is current
up to the last committed step at the moment of the kill.

Default 0 disables the wrapper: the existing single-attempt
`run_prover(...)` path is taken with no subprocess overhead.
@Stevengre Stevengre force-pushed the progressive-depth-timeout branch from 3a1e97d to a2d3a09 Compare May 29, 2026 08:57
@Stevengre Stevengre self-assigned this May 29, 2026
@Stevengre Stevengre marked this pull request as ready for review May 29, 2026 13:16
@ehildenb
Copy link
Copy Markdown
Member

This seems like something we can probably build into Prover.advance_proof, and have a generic solution rather than duplicating a bunch of code here. At the very least, do we need a new _init_and_run_proof_progressive, or can it be integrated into the existing _init_and_run_proof?

Drops the standalone `_init_and_run_proof_progressive` helper and moves
the halving loop directly under the `per_depth_timeout > 0` branch at
the top of `init_and_run_proof`. The progressive block is followed by
the unchanged single-attempt body, separated by a blank line. Per-attempt
session management stays in `_run_attempt_under_timeout` (separate
concern, ~70 lines).

Addresses review feedback on PR #1141.
@Stevengre
Copy link
Copy Markdown
Contributor Author

@ehildenb Instead of providing this feature through a long dependency, I'd like to have a pr here to prove the concept and use it earlier.

  1. inlined _init_and_run_proof_progressive into _init_and_run_proof.
  2. introduce an issue in k repo Support progressive depth halving as a generic policy in Prover.advance_proof k#4924, and may provide this feature in k later.

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.

2 participants