Update Cargo.toml with 40 changed files (#4809) - #4833
Conversation
Automatic checkpoint to preserve work in progress. Tests and implementation saved before refactoring phase.
rysweet
left a comment
There was a problem hiding this comment.
Comprehensive Code Review — Step 17b
Scope: src/disk_reclaim/{mod,guard,executor}.rs + docs + disk-reclaim.yaml (widen routine reclaim to per-repo target/, add per-candidate skip tracing). Verified against PR head c32790e48; all 75 disk_reclaim tests pass locally, including the two new regressions.
Verdict: Approve — one substantive non-blocking finding (doc/guarantee accuracy)
The core design is sound: rooting the allow-root at the target/ parent (not target/debug) is the minimal change that respects is_safe_to_delete's strict-inside containment, and guard.rs::rail_refuses_candidate_equal_to_allow_root_but_allows_its_child locks that boundary. Deny-set precedence, symlink refusal, and fields-only (log-forging-safe) tracing are all correct. The before/after regression test (routine_reclaim_frees_target_only_when_target_root_is_in_scope) is exactly the right proof.
🟡 Finding 1 (Medium, non-blocking): the LiveProcess rail does not reliably protect an in-flight cargo build of a repo's own target/ — but the docs/comments claim it does
The live-process rail is the only thing standing between routine reclaim and an active build now that bare target/debug is a direct candidate. But ProcfsLiveProcessProbe::worktree_has_live_process (src/worktree_gc/liveness.rs:100-131) matches a process only when /proc/<pid>/cwd is at/under the candidate path:
if target.starts_with(&canon) { return true; } // canon = the candidate, e.g. <repo>/target/debugA cargo build (and the rustc children it spawns) runs with cwd = the repo root (<repo>), never under <repo>/target/debug. So <repo> does not starts_with <repo>/target/debug, the probe returns false, and the candidate is not refused. There is no compensating check — I grepped for a .cargo-lock/flock, /proc/<pid>/exe, or open-FD check in disk_reclaim/ and found none.
Before this PR <repo>/target was out of scope, so bare target/debug was never a direct candidate for the primary checkout; the widened allow-root now makes it one, so routine reclaim can delete target/debug mid-build.
The stated guarantee is inaccurate for this common case:
docs/reference/disk-reclaim-api.md:233-234— "the live-PID rail (an in-flightcargo buildholdingtarget/debugis refused withLiveProcess)"docs/concepts/agentic-disk-reclamation.md:151— "an in-flightcargo buildholdingtarget/debugis … gated by every hard rail below"
Why non-blocking: impact is bounded — the recipe itself argues "a running cargo build simply rebuilds a deleted artifact directory, so this is only a cost, never a correctness risk," and emergency_cleanup already removes these same dirs. A killed build is recoverable.
Recommend one of:
- Correct the docs/comments to state the rail keys on process cwd and therefore does not guarantee mid-build protection for a build launched from the repo root — reclaim relies on cargo's own rebuild resilience, not the rail. (Cheapest; removes the false guarantee.)
- Strengthen the rail to honor Cargo's advisory
target/.cargo-lock(try_lockfails ⇒ build active ⇒ skip). This reliably detects an in-flight build regardless of cwd and would make the existing guarantee true.
Option 1 is sufficient to merge; option 2 is the more robust follow-up.
Checklist
- Code quality / standards — idiomatic;
tracingfields-only, noprint!/println!, no "Bridge" naming (grep-confirmed). - Test coverage — strong: before/after routine regression + equal-vs-child boundary +
allow_rootsand bare-$HOMEunit assertions. Gap: no test exercises the live-build-vs-cwd scenario in Finding 1 (would be the natural test if option 2 is taken). - No TODOs/stubs/swallowed exceptions — none added.
- No unimplemented functions — n/a.
- Logic correctness — allow-root widening, strict-inside containment, deny-set precedence all correct.
- Edge cases — bare-
target/equality refused;<repo>/src&.gitstillOutsideAllowRoot; symlink refused;worktrees/maindeny-set overrides allow-root. Uncovered edge = Finding 1.
Additive, non-breaking; EMERGENCY path untouched. Approve pending a doc-accuracy fix for Finding 1 (or acceptance of the bounded risk with docs corrected).
rysweet
left a comment
There was a problem hiding this comment.
Security Review — Step 17c (MANDATORY)
Scope: PR #4833 (issue #4809) — widen routine disk-reclaim allow-roots to per-repo <repo>/target, add per-candidate structured skip tracing. Reviewed against PR head c32790e48; src/disk_reclaim/{mod,guard,executor}.rs + docs + disk-reclaim.yaml. 75/75 disk_reclaim tests pass.
Checklist
- Security requirements met — fail-closed destructive-path primitive (
is_safe_to_delete) left untouched; widening is additive and still gated by every rail. - No new vulnerabilities — new allow-root cannot escalate deletion beyond rebuildable Cargo children (see analysis).
- Sensitive-data handling — no secrets/tokens/PII read, logged, or written; tracing emits only path/reason/kind.
- AuthZ / capability boundary — deletion capability defined by allow-root ∧ ¬deny-set containment; precedence preserved (deny checked first).
- Injection vectors — no shell/SQL/path-injection surface introduced; log-forging specifically mitigated.
Analysis of the security boundary (unchanged, re-verified)
is_safe_to_delete (maintenance.rs:226) remains the single audited fail-closed gate. Every property the fix depends on holds:
- Symlink refusal (SR-5) —
symlink_metadataon the final component;is_symlink()→ refuse. A swappedtarget/debugsymlink cannot redirect a delete outside the root. - Traversal defeat —
canonicalizeresolves../links before the containment test, so<repo>/target/../../etccannot escape. - Strictly-inside containment —
real != r && real.starts_with(&r)(maintenance.rs:252). This is the crux of the widening: rooting at<repo>/target(the parent) makestarget/debugdeletable but the baretarget/root itself is refused (OutsideAllowRoot), so a hostile/hallucinated candidate equal to the allow-root can never triggerrm -rf target/. New regressionrail_refuses_candidate_equal_to_allow_root_but_allows_its_childlocks this. - Deny-set precedence —
vet_candidatechecksprotected.contains()first (guard.rs:234), so protected paths win even inside the new allow-root.ProtectedDenySet::resolveunions daemon cwds +worktrees/main+ operator env; confirmed it does not include managed-repo roots, so the new<repo>/targetroot is not shadowed and, conversely, no protected location is exposed by it. - Symlink/
.gitworktree rail —path.join(".git").symlink_metadata()still gates each dir; unaffected.
Log-forging (injection) — mitigated
The new tracing::info! in executor.rs is fields-only (path = %…, reason = ?…, kind = ?…) with a static message. The attacker-influenceable reason/path are structured field values, never interpolated into the format string, so a candidate path containing newlines/ANSI/level=ERROR cannot forge or inject audit-log lines. No print!/println! added.
Non-blocking observation (defense-in-depth, carried from Step 17b)
The LiveProcess rail keys only on /proc/<pid>/cwd (liveness.rs:126). cargo build runs with cwd at the repo root, not under target/debug, so the liveness rail does not by itself prevent deleting target/debug during an in-flight build. This is a safety/availability concern (rebuildable artifacts, bounded impact) rather than a confidentiality/integrity vulnerability — data loss is limited to regenerable Cargo output. Recommend either honoring Cargo's target/.cargo-lock or correcting the docs that claim such a candidate is "refused with LiveProcess". Non-blocking.
Verdict
No blocking security findings. The destructive-path gate is unchanged and fail-closed; the allow-root widening is provably confined to rebuildable children by the strictly-inside rule; tracing is injection-safe. Approve from a security standpoint, with the one non-blocking availability hardening note above.
Philosophy Guardian Review — Step 17dVerdict: ✅ COMPLIANT — approved, one non-blocking Zero-BS observation on documentation accuracy. Assessed the committed diff at Compliance checklist
🟡 Non-blocking observation (Zero-BS / documentation accuracy)The docs assert a safety guarantee — "an in-flight ConclusionThe implementation embodies ruthless simplicity and Zen-like minimalism: a two-line functional fix, thoroughly tested at the exact safety boundary, confined to one brick, with observability that makes rejections auditable rather than silent. No philosophy-blocking changes required. |
The live-PID rail keys on /proc/<pid>/cwd, so an in-flight cargo build (cwd at repo root) is NOT caught by LiveProcess for a target/ candidate. Corrected the docs to describe the actual cwd-inside-candidate guarantee and clarify that build artifacts are safe as the rebuildable StaleBuildCache class. Addresses non-blocking doc-accuracy finding from code/security/philosophy reviews (Step 17e). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implemented reviewer suggestions; fixed identified issues; updated per security review; addressed philosophy compliance items
📊 Coverage Summary
Coverage data from CI run. Test files matching |
Point cross-references to the correct '#configuration-accessors' anchor (heading is 'Configuration accessors') in overseer-backoff-gate-api.md, resolving the two broken intra-doc links flagged under mkdocs --strict. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…suite (#4809) The reclaim-effectiveness reference/concept docs declared ReclaimEffectivenessGate / EffectivenessDecision / src/disk_reclaim/ effectiveness.rs as implemented, but the module and types did not exist — docs ahead of code, and the actual churn fix for #4809/#4825/#4810 was missing. Add src/disk_reclaim/effectiveness.rs: the suppress-only per-key exponential-backoff cooldown that stops the OODA daemon re-firing a proven-ineffective reclaim run every ~15-min cycle. Mirrors the already -tested overseer BackoffGate peek/record split, with a hard %-used ceiling bypass so a genuinely filling disk always reclaims. Encodes the documented contract as 21 unit tests (TDD, Step 7): ceiling bypass, unseen/elapsed/backwards-clock admission, in-cooldown suppression, exponential growth + cap, long-silence reset, effective-run immediate re-admit, independent per-key backoff, saturating no-overflow, and injectable config accessors (defaults + clamps). Additive telemetry name simard.disk.reclaim.suppressed_cycles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…4809) Step 8 implementation. The ReclaimEffectivenessGate module + its 21 TDD tests landed in step 7, but the gate was never called: the Tier-3 disk-reclaim block in run_ooda_daemon re-fired run_disk_reclaim every ~15-min cycle whenever %-used crossed SIMARD_DISK_RECLAIM_PCT, regardless of whether prior runs freed a byte — the churn on the ~94%-full host (#4809 / #4825 / #4810). Wire the suppress-only backoff in front of the (unchanged) propose/dispose path: - construct the gate once as daemon loop state (from_env + kill switch), - peek(key, used_pct, now) before each would-be run; on Suppress, emit the additive simard.disk.reclaim.suppressed_cycles counter, trace at info, and skip — a disk at/above the hard ceiling always bypasses, - record(key, was_effective, now) after a real run so a no-op arms/grows the per-partition cooldown and an effective run re-admits next cycle; a failed run is left unrecorded so a transient error never arms a cooldown. Add ReclaimReport::was_effective() (bytes_freed>0 || used_pct dropped) as the documented gate signal, with a unit test; a dry-run frees nothing and is therefore not effective, so the default dry-run daemon backs off the proven no-op churn. Align the API doc's suppress log level (info, routine) and dedup-key prefix with the implementation. Additive / non-breaking: every safety rail and the dry-run default are unchanged; the gate is on by default and fully disabled by SIMARD_DISK_RECLAIM_EFFECTIVENESS_GATE=off. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Concise workflow-generated PR for Cargo.toml.
Issue
Closes #4809
Changed files
Diff stat
Behavior
Implemented behavior through these branch commits:
c32790e wip: checkpoint after implementation (steps 7-8)
Validation
ℹ NODE_OPTIONS=--max-old-space-size=32768 (saved preference). To change: /home/azureuser/.amplihack/config
I'll start by inspecting the branch and changes to understand what this PR touches.
● Get branch, diff stat, and recent commits (shell)
│ cd /home/azureuser/src/Simard-deploy-4049/worktrees/feat/issue-4809-nodeoptions-max-old-space-si
│ ze32768-saved-preferen && git branch --show-current && echo "---DIFF STAT---" && git --no-pager
│ diff --stat main...HEAD 2>/dev/null | tail -40 && echo "---LOG---" && git --no-pager log
│ --oneline -10
└ 22 lines…
This is a Rust change to disk-reclaim (issue #4809). Let me examine the actual code changes.
Risk
No high-risk subsystem pattern detected from changed paths.
Checklist
This PR was created as a draft for review before merging.
Step 16b: Outside-In Testing Results
Tested from the PR branch as a consumer of the
disk_reclaimbehavior, using the qa-team skill's Rust-CLI path (cargo test, native test fixtures) rather than the gadugi framework.Detected toolchains
Cargo.toml, cratesimard). Changed code issrc/disk_reclaim/{executor,guard,mod}.rs.package.json, CLI wrapperbin.js) — no JS changed in this PR.docs/**) and recipe (prompt_assets/simard/recipes/disk-reclaim.yaml).Chosen strategy
Rust CLI repo →
cargo testagainst the affected module. Note: root fs/was 100% full (28G, 0 avail), but the worktree lives on/dev/sda(/home/azureuser, 209G free), so compilation succeeded onceTMPDIRwas redirected off the full root fs.Scenarios
target/debugoncetarget/is an allow-root (issue #4809 no-op)cargo test --lib disk_reclaim::executor::tests::routine_reclaim_frees_target_only_when_target_root_is_in_scopetest result: ok. 1 passed; 0 faileddisk_reclaimmodule: allow-root scope (mod.rs), guard safety boundary (candidate == allow-root refused, child reclaimable), executor skip-tracingcargo test --lib disk_reclaim::test result: ok. 75 passed; 0 failedFix count: 0. No product code changes were required — both the simple and edge/integration scenarios passed.
Note on a transient false failure: An initial run of scenario 2 showed 4 failures (
worktree_branch_none_on_missing_repo,deriving_remover_rejects_unresolvable_worktree_removal,exec_refuses_path_outside_allow_roots,exec_allows_orphan_inside_allow_root_but_dry_run_deletes_nothing). Root cause was environmental:TMPDIRhad been set inside the git worktree, so these tests' git-worktree/allow-root detection saw the enclosing repo. Re-running withTMPDIRoutside any git repo produced75 passed; 0 failed. This is a test-harness placement artifact, not a defect in the PR.