force-run: hand the runner the install dir as CRON_DIR - #268
Conversation
Both runners resolve DIR="${CRON_DIR:-$PWD}", so a child spawned without
the variable reads the caller's working directory as the install dir and
refuses at startup — force-run only worked when invoked from inside the
install dir. The plan now carries CRON_DIR in its own argv via env(1),
canonical like the flake ref beside it, so --no-run previews the exact
invocation the runners' headers document and the tests can kill any
mutant that drops it.
Closes #264
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Walkthrough
ChangesForce-run CRON_DIR propagation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
… paste-able preview The plan carries CRON_DIR as a typed env field applied to the child, so argv[0] is nix again and a runner that cannot start is the tool's own exit-2 diagnostic rather than env(1)'s 127 dressed as a run result. The previews render assignments-first with values shell-quoted only when sh needs it, which makes the printed line the README's own documented form and still paste-able for an install dir with whitespace. The install dir resolves flag > INSTALL_DIR > CRON_DIR, an ambient CRON_DIR losing to the resolved dir says so out loud, and the comments now state the real pre-fix worst case: a silent paid run against the wrong cwd. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Reviewed f97898f: approve — env rides ForceRunPlan as typed data applied via .envs(), so a missing runner is the tool's own exit-2 diagnostic again (integration-proven against a nix-less PATH without starting a run); fallback chain flag > INSTALL_DIR > CRON_DIR with a precedence-discriminating test; preview renders from the same plan data the spawn uses and is byte-for-byte the README/header form when nothing needs quoting, correctly quoted when it does; ambient-CRON_DIR override is announced with canonical comparison; comments state the true pre-fix worst case; suite 1278+124/0 on the merged tree; 5 mutants killed with one declared residual (.envs() glue, untestable under the no-run invariant, its feeding data mutant-covered); CI green; 0 unresolved threads. |
Closes #264.
Defect
force-runvalidates, canonicalizes and fast-forwards the install dir, then spawns the runner with inherited env and inherited cwd — noCRON_DIR. Both runners resolveDIR="${CRON_DIR:-$PWD}"(review-run.sh:25,campaign-run.sh:28), so the runner reads the caller's cwd as the install dir: a cwd that is some checkout of this repo silently starts a PAID run against the wrong dir, and any other cwd refuses at startup, as observed:Fix
ForceRunPlancarries the export as typed data —env: vec![("CRON_DIR", <canonical dir>)]— applied to the child with.envs().argv[0]staysnix, so a runner that cannot start remains the tool's own exit-2cannot startdiagnostic and never exits looking like a run that ran and failed. The previews (would run:/running:) render assignments-first with values shell-quoted only when sh needs it, making the printed line byte-for-byte the manual invocation the runner headers andREADME.md:3196document, and still paste-able when the install dir carries whitespace.The install dir resolves flag >
INSTALL_DIR>CRON_DIR(the runners' own export is the more specific name; empty means unset at every level). An ambientCRON_DIRthat disagrees with the resolved dir loses to the plan's export — never silently:note: CRON_DIR=<ambient> in the environment is overridden by CRON_DIR=<dir> for this run(review finding 7, implemented rather than refuted).QA
the_dir_in_the_flake_ref_is_canonical(extended) — thewould run:line carriesCRON_DIR=<canonical dir> nix runeven for a..-laden input.cron_dir_is_an_install_dir_fallback_but_install_dir_wins(new) —CRON_DIRalone resolves the install dir; besideINSTALL_DIRit loses (its value is a nonexistent dir, so reading it first would exit 2, not 0).an_overridden_ambient_cron_dir_is_said_out_loud(new) — disagreeing ambientCRON_DIRprints the note; the same dir spelled via..does not.a_runner_that_cannot_start_is_a_diagnostic_not_a_run_result(new) — PATH holds git alone sonixcannot resolve: exit 2 withcannot starton stderr, after the fast-forward succeeded. No run can start (the file's no-money invariant holds —nixis unreachable).the_plan_pulls_the_dir_it_then_builds_the_runner_from(unit, extended) — planenvcontent for both roles; flake ref located by content, not index.a_previewed_invocation_is_pasteable(unit, new) — unquoted README form when nothing needs quoting;CRON_DIR='/tmp/install dir'value-quoting and whole-word argv quoting when it does.git statusafter each):envemptied (vec![]) → killed by the unit plan test ANDthe_dir_in_the_flake_ref_is_canonical(each0 passed; 1 failed)..or_else(|| env_dir("CRON_DIR"))deleted → killed by the fallback test (0 passed; 1 failed).return 2→return 127→ killed by the cannot-start test (0 passed; 1 failed).if false && !same) → killed by the said-out-loud test (0 passed; 1 failed).shell_wordalways plain) → killed by the pasteable unit test (0 passed; 1 failed)..envs()/.args()application onCommanditself — unreachable under the suite's no-run-ever-starts invariant; the plan data feeding it is what the mutants above prove covered.review-run.sh:25,78,campaign-run.sh:28,89,README.md:3196-3197), and POSIX sh word/assignment reading rules for the preview quoting — independent of the implementation.--install-dirintoCRON_DIRso the runner works from any cwd, canonicalized, with the contract visible/testable in the--no-runpreview; covered for both roles, plus the review's findings: spawn-failure exit split,CRON_DIRaccepted as env fallback with documented precedence, quoted paste-able previews, ambient-override notice, corrected worst-case comments.Full suite on the merged tree (
origin/main= 2f11a52 merged): 1278 unit + 124 integration tests, 0 failures.🤖 Generated with Claude Code