Skip to content

fix(research): isolate prediction recovery artifacts - #75

Merged
proerror77 merged 1 commit into
mainfrom
codex/prediction-recovery-isolation
Jul 16, 2026
Merged

proerror77 merged 1 commit into
mainfrom
codex/prediction-recovery-isolation

Conversation

@proerror77

@proerror77 proerror77 commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Change contract

Prediction snapshot and runner retries never trust prior writable extraction or output state. Symlinked artifact leaves are rejected before a compiler or runner starts; JSON evidence rechecks its output leaf at publication. Local file results are staged, synced, and atomically published without overwrite.

Out of scope

  • Snapshot-compiler failure bundles or evidence.
  • Descriptor-relative, no-follow parent-directory traversal against an adversary concurrently replacing an already validated parent directory.
  • Live trading, execution authority, OMS, risk policy, and the independent alpha live-capability gate.

Dependency / merge order

None. Rebased on current main at 1ec9add1.

Focused validation

  • cargo test -p alpha-harness -- --test-threads=1 (62 passed)
  • cargo clippy -p alpha-harness --all-targets -- -D warnings
  • cargo fmt --check --package alpha-harness
  • git diff --check
  • Counterexamples cover stale output, bundle, and result symlinks; pre-start rejection of symlinked runner/compiler artifact leaves; stale work-dir extraction; untrusted local results; and a failed local publication leaving no destination file.

Rollout / rollback

Research-only CLI and artifact-path behavior. Roll back this one commit to restore the former retry/output behavior; no live execution path changes.

Summary by CodeRabbit

  • New Features
    • Retry and resume attempts now use fresh, isolated work areas and revalidate mission, policy, and snapshot identity before continuing.
    • Runner and compiler evidence is preserved through safer atomic output handling.
  • Bug Fixes
    • Rejects symlinked paths, unsafe directories, stale results, and tampered local snapshot data before execution.
    • Prevents failed or incomplete publications from overwriting existing results.
  • Documentation
    • Expanded guidance on retry behavior, validation requirements, and resume safeguards.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The harness adds symlink-resistant directory validation, tempfile-based atomic publication, isolated snapshot compilation and extraction per retry, empty-results preconditions, and atomic persistence of compiler and runner evidence. Documentation records the retry and resume requirements.

Changes

Retry-safe artifact handling

Layer / File(s) Summary
Filesystem safety and atomic publication
rust_hft/alpha-harness/app/Cargo.toml, rust_hft/alpha-harness/app/src/data_mission.rs
Adds tempfile, real-directory checks, temporary output helpers, atomic JSON persistence, and Unix/macOS filesystem safety tests.
Mission artifact publication
rust_hft/alpha-harness/app/src/mission_runner.rs
Downloads and bundles are written to temporary files before publication, while result parents are validated as real directories; symlink safety tests cover these paths.
Isolated prediction retries
rust_hft/alpha-harness/app/src/prediction_runner.rs, rust_hft/alpha-harness/app/src/prediction_snapshot.rs, rust_hft/alpha-harness/README.md
Prediction execution requires empty results, extracts and compiles snapshots in fresh temporary directories, persists compiler and runner evidence atomically, and documents retry identity requirements with coverage for stale symlinks and retry state.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PredictionRunner
  participant SnapshotCompiler
  participant TemporarySnapshot
  participant ArtifactStore
  PredictionRunner->>PredictionRunner: validate workspace and empty results
  PredictionRunner->>TemporarySnapshot: extract snapshot into fresh private directory
  SnapshotCompiler->>TemporarySnapshot: compile isolated snapshot
  SnapshotCompiler->>ArtifactStore: persist compiler evidence atomically
  PredictionRunner->>ArtifactStore: persist runner evidence atomically
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: isolating prediction recovery artifacts.
Description check ✅ Passed The description covers the contract, out-of-scope items, validation, and rollback, with only minor template heading mismatches.
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.
✨ 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 codex/prediction-recovery-isolation

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.

fn create_bundle_replaces_a_stale_symlink_without_following_it() {
use std::os::unix::fs::symlink;

let root = std::env::temp_dir().join(format!(
fn publish_result_rejects_a_symlinked_parent_directory() {
use std::os::unix::fs::symlink;

let root = std::env::temp_dir().join(format!(

@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: 3

🤖 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 `@rust_hft/alpha-harness/app/src/data_mission.rs`:
- Around line 334-380: The path validation in ensure_real_directory_at and
temporary_output_file is vulnerable to ancestor replacement after validation.
Replace path-based create_dir, tempfile_in, and persistence operations with held
directory handles and descriptor-relative, no-follow creation/rename semantics,
preserving the validated-tree boundary; add a deterministic counterexample test
that swaps an ancestor with a symlink between validation and use.

In `@rust_hft/alpha-harness/app/src/mission_runner.rs`:
- Line 405: Update the create_new result-publishing flow to stage and sync the
bundle under a private temporary name, then atomically publish it to the final
destination with no-overwrite semantics. Ensure interruptions during copying
never expose the final path, while preserving retryability after failure. Add a
targeted interruption counterexample test asserting the final destination
remains absent and a subsequent retry succeeds.

In `@rust_hft/alpha-harness/README.md`:
- Around line 131-133: Update the README description of harness verification to
state that resumed execution verifies three hashes: mission, snapshot, and
resume-bundle. Replace the inaccurate “both outer hashes” wording while
preserving the surrounding behavior and retry/artifact details.
🪄 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: e1a9f79b-0b0a-4afc-97c7-64546ec5fa13

📥 Commits

Reviewing files that changed from the base of the PR and between 57d5e80 and 5a18d40.

⛔ Files ignored due to path filters (1)
  • rust_hft/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • rust_hft/alpha-harness/README.md
  • rust_hft/alpha-harness/app/Cargo.toml
  • rust_hft/alpha-harness/app/src/data_mission.rs
  • rust_hft/alpha-harness/app/src/mission_runner.rs
  • rust_hft/alpha-harness/app/src/prediction_runner.rs
  • rust_hft/alpha-harness/app/src/prediction_snapshot.rs

Comment on lines +334 to +380
if let Some(parent) = path.parent().filter(|parent| *parent != path) {
ensure_real_directory_at(parent, label)?;
}
let temporary = path.with_extension("tmp");
std::fs::write(&temporary, serde_json::to_vec_pretty(value)?)?;
std::fs::rename(temporary, path)?;
let metadata = match std::fs::symlink_metadata(path) {
Ok(metadata) => metadata,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
match std::fs::create_dir(path) {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {}
Err(error) => {
return Err(error)
.with_context(|| format!("create {label} directory {}", path.display()))
}
}
std::fs::symlink_metadata(path)
.with_context(|| format!("inspect {label} directory {}", path.display()))?
}
Err(error) => {
return Err(error)
.with_context(|| format!("inspect {label} directory {}", path.display()))
}
};
if metadata.file_type().is_symlink() {
bail!(
"{label} directory cannot be a symbolic link: {}",
path.display()
);
}
if !metadata.is_dir() {
bail!("{label} path must be a directory: {}", path.display());
}
Ok(())
}

pub(crate) fn temporary_output_file(
path: &Path,
prefix: &str,
) -> anyhow::Result<tempfile::NamedTempFile> {
let parent = path
.parent()
.filter(|parent| !parent.as_os_str().is_empty())
.unwrap_or_else(|| Path::new("."));
ensure_real_directory(parent, "temporary output parent")?;
tempfile::Builder::new()
.prefix(prefix)
.tempfile_in(parent)
.with_context(|| format!("create private temporary output in {}", parent.display()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Close the symlink check/use race.

The directory walk validates paths, then later performs create_dir, tempfile_in, and persistence through those paths. A writable ancestor can be replaced with a symlink between these operations, redirecting publication outside the validated tree.

Use held directory handles with descriptor-relative, no-follow creation/rename semantics, and add a deterministic ancestor-swap counterexample test.

As per coding guidelines, “Every safety boundary requires a targeted counterexample test.”

Also applies to: 394-399, 443-467

🤖 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 `@rust_hft/alpha-harness/app/src/data_mission.rs` around lines 334 - 380, The
path validation in ensure_real_directory_at and temporary_output_file is
vulnerable to ancestor replacement after validation. Replace path-based
create_dir, tempfile_in, and persistence operations with held directory handles
and descriptor-relative, no-follow creation/rename semantics, preserving the
validated-tree boundary; add a deterministic counterexample test that swaps an
ancestor with a symlink between validation and use.

Source: Coding guidelines

Comment thread rust_hft/alpha-harness/app/src/mission_runner.rs Outdated
Comment thread rust_hft/alpha-harness/README.md Outdated
@proerror77
proerror77 force-pushed the codex/prediction-recovery-isolation branch from 5a18d40 to b67a8e2 Compare July 16, 2026 16:30

@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.

🧹 Nitpick comments (1)
rust_hft/alpha-harness/app/src/mission_runner.rs (1)

509-514: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer tempfile::tempdir() for secure temporary directory creation.

Using std::env::temp_dir() with a predictably formatted name triggers static analysis warnings due to potential predictable temp path vulnerabilities in shared environments. More importantly, it requires manual cleanup which can leak the directory if a test panics. Both sites can be refactored to use tempfile::tempdir() which securely creates a uniquely named directory and automatically cleans it up via RAII (as is already done in publish_result_does_not_leave_a_destination_when_the_bundle_is_missing).

  • rust_hft/alpha-harness/app/src/mission_runner.rs#L509-L514: Replace the manual root path and create_dir_all with let root = tempfile::tempdir().unwrap(); (and update subsequent path usages to root.path().join(...), removing the manual remove_dir_all at the end).
  • rust_hft/alpha-harness/app/src/mission_runner.rs#L557-L562: Apply the same refactoring for the parent symlink test.
🤖 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 `@rust_hft/alpha-harness/app/src/mission_runner.rs` around lines 509 - 514,
Replace manual temporary-directory creation in both mission_runner.rs sites
(lines 509-514 and 557-562) with tempfile::tempdir(), update subsequent joins to
use root.path(), and remove the corresponding manual remove_dir_all cleanup so
RAII handles cleanup on test failure or panic.

Source: Linters/SAST tools

🤖 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.

Nitpick comments:
In `@rust_hft/alpha-harness/app/src/mission_runner.rs`:
- Around line 509-514: Replace manual temporary-directory creation in both
mission_runner.rs sites (lines 509-514 and 557-562) with tempfile::tempdir(),
update subsequent joins to use root.path(), and remove the corresponding manual
remove_dir_all cleanup so RAII handles cleanup on test failure or panic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: de408b4c-0cf9-4e1b-a6e0-1248ecc83555

📥 Commits

Reviewing files that changed from the base of the PR and between 5a18d40 and b67a8e2.

⛔ Files ignored due to path filters (1)
  • rust_hft/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • rust_hft/alpha-harness/README.md
  • rust_hft/alpha-harness/app/Cargo.toml
  • rust_hft/alpha-harness/app/src/data_mission.rs
  • rust_hft/alpha-harness/app/src/mission_runner.rs
  • rust_hft/alpha-harness/app/src/prediction_runner.rs
  • rust_hft/alpha-harness/app/src/prediction_snapshot.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • rust_hft/alpha-harness/app/Cargo.toml
  • rust_hft/alpha-harness/app/src/prediction_runner.rs
  • rust_hft/alpha-harness/README.md
  • rust_hft/alpha-harness/app/src/data_mission.rs
  • rust_hft/alpha-harness/app/src/prediction_snapshot.rs

@proerror77
proerror77 merged commit 0b8ce2b into main Jul 16, 2026
39 of 40 checks passed
@proerror77
proerror77 deleted the codex/prediction-recovery-isolation branch July 16, 2026 17:46
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