feat: force-run, and the recommendation the corpus reading was missing - #255
Conversation
The user asked for a command "forcing producer and vetter, watching, reviewing tool corpus then making optimization recommendation" — four things. #253 tooled the middle two and dropped the outer two. This restores both. FORCING IS A TYPED CALL. `force-run <producer|vetter> --install-dir <dir>` fast-forwards the install dir first — the runner builds from that dir's own git HEAD, so a run against a stale checkout silently exercises old code — refuses with exit 3 if it will not fast-forward, then invokes the runner with `--force` and streams it through the same filter `watch-run` applies to the log. It streams the child rather than handing off because a run started in one call and watched in the next has a gap, and every lifecycle line written inside that gap belongs to the run and would be missed. `watch-run` stays as the reattach path. THE RECOMMENDATION IS BACK, computed in `corpus-report` so it is testable and usable without the command. A hand-roll is shrinking when the runs that STILL DO IT do less of it than they used to — measured over sightings, not over the full series, which is the whole of "the render harness is rebuilt by every run that takes a screenshot item". Shrinking shapes are ruled out; among the rest the one seen in the most traces wins. Frequency is the discriminant because novelty is not: the tarball extraction is newer, at its own peak, and in one trace, and it is the answer the issue names as wrong. Against the live corpus it names the harness scaffold — the same answer the 2026-08-09 session reached by hand — and prints the per-run counts underneath it, because a recommendation nobody can check is worse than a table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WalkthroughThe PR updates the ChangesObservation tooling
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ForceRunCLI
participant force_run_mode
participant GitRepository
participant Runner
ForceRunCLI->>force_run_mode: parse role and install directory
force_run_mode->>GitRepository: git pull --ff-only
GitRepository-->>force_run_mode: fast-forward result
force_run_mode->>Runner: run selected role with --force
Runner-->>ForceRunCLI: filtered output and exit status
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.45.0)pr-review-report-rs/src/main.rsast-grep timed out on this file 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 |
#254 landed `render-component` on main; both it and this branch append a self-contained block at the same anchor — the line before `/// The CLI surface.` — so git returned both blocks whole rather than aligning two independent insertions. Resolved as the union: each block keeps its own closing braces, neither side's trailer is shared with the other. Nothing is dropped or reconciled: the merged file's diff against each side is hunk-for-hunk identical to that side's diff against the merge base.
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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 `@plugins/human-fsm/commands/observe-run.md`:
- Line 3: Update the observe-run command documentation’s argument hint from a
positional install directory to producer|vetter --install-dir <install-dir>, and
revise the related usage prose to state that the directory value follows
--install-dir. Keep the existing INSTALL_DIR environment-variable behavior and
other command guidance unchanged.
- Line 30: Update every runnable command fence in observe-run.md, including the
fences at the referenced locations, to specify the shell language by changing
each opening fence to use sh.
- Around line 112-118: Update the recommendation-ranking explanation in the
hand-roll section to document the complete tie-break order: trace frequency
first, then recency, followed by volume, and finally name. Ensure the prose
makes clear that each later field resolves ties from the preceding metric.
In `@pr-review-report-rs/src/main.rs`:
- Around line 71885-71893: In the test containing the measured_corpus() and want
iteration, assert that measured_corpus().len() equals want.len() before calling
zip. Keep the existing per-row assertions unchanged so every fixture row is
still validated after the count check.
- Around line 37218-37235: Add a pr-review-report subcommand that repairs stale
or diverged install directories after fast-forward failure, then update the
exit-3 message in force_run_mode to name the exact executable repair command
callers should run before retrying. Ensure the new command performs the required
checkout synchronization and is wired into the CLI dispatch.
In `@pr-review-report-rs/tests/force_run.rs`:
- Around line 16-26: Update tmp_dir to return an RAII temporary-directory guard,
preferably by using the existing temporary-directory helper if available, so the
root directory and all test repositories beneath it are removed on Drop.
Preserve the current unique-directory creation behavior and update callers in
the force-run tests to use the guard’s path.
🪄 Autofix
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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 9c65f397-564d-4aed-a7c3-8025fea48be6
📒 Files selected for processing (5)
.claude-plugin/marketplace.jsonplugins/human-fsm/.claude-plugin/plugin.jsonplugins/human-fsm/commands/observe-run.mdpr-review-report-rs/src/main.rspr-review-report-rs/tests/force_run.rs
| argument-hint: producer|vetter [install-dir] | ||
| allowed-tools: Bash(pr-review-report:*), Bash(nix run:*), Bash(git:*) | ||
| description: Force a producer or vetter run and watch it, measure what its context cost, read the retained trace corpus, and name the hand-roll worth tooling next — with the per-run counts that pick it, so a human can disagree. | ||
| argument-hint: producer|vetter <install-dir> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Make the install-directory syntax consistent.
The argument hint and prose define INSTALL-DIR as the second positional argument. force-run requires --install-dir <dir> unless INSTALL_DIR is set. A user who follows this syntax can receive a usage error instead of starting the observation.
Change the hint to producer|vetter --install-dir <install-dir>. State that the directory value follows --install-dir.
Also applies to: 9-13
🤖 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 `@plugins/human-fsm/commands/observe-run.md` at line 3, Update the observe-run
command documentation’s argument hint from a positional install directory to
producer|vetter --install-dir <install-dir>, and revise the related usage prose
to state that the directory value follows --install-dir. Keep the existing
INSTALL_DIR environment-variable behavior and other command guidance unchanged.
| `git -C <install-dir> pull --ff-only` and say what it moved to; a checkout that | ||
| will not fast-forward is a stop, not a thing to force past — report it and do | ||
| not start a run. | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Specify the shell language for each command fence.
markdownlint reports MD040 for these runnable fences. Add sh to each opening fence.
Also applies to: 65-65, 80-80, 94-94
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)
[warning] 30-30: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 `@plugins/human-fsm/commands/observe-run.md` at line 30, Update every runnable
command fence in observe-run.md, including the fences at the referenced
locations, to specify the shell language by changing each opening fence to use
sh.
Source: Linters/SAST tools
| The rule it applies is worth understanding before you relay it. A hand-roll is | ||
| **shrinking** when the runs that _still do it_ do less of it than they used to — | ||
| the signature of a tool that already landed and is killing it — and shrinking | ||
| shapes are ruled out. Among what is left, the one seen in the **most traces** | ||
| wins, ties broken by the most recent sighting. Frequency is the discriminant | ||
| precisely because novelty is not: the tarball extraction is newer, bigger and at | ||
| its own peak, and it still loses to something last seen days earlier. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Document all recommendation tie-breakers.
The documented ranking stops after trace frequency and recency. The required ranking also uses volume and name. When two metrics tie on the documented fields, this text can make the selected recommendation appear incorrect.
State the complete ordering: trace frequency, recency, volume, then name.
🤖 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 `@plugins/human-fsm/commands/observe-run.md` around lines 112 - 118, Update the
recommendation-ranking explanation in the hand-roll section to document the
complete tie-break order: trace frequency first, then recency, followed by
volume, and finally name. Ensure the prose makes clear that each later field
resolves ties from the preceding metric.
| let ff = Command::new(&plan.fast_forward[0]) | ||
| .args(&plan.fast_forward[1..]) | ||
| .output(); | ||
| match ff { | ||
| Ok(out) if out.status.success() => {} | ||
| Ok(out) => { | ||
| eprint!("{}", String::from_utf8_lossy(&out.stderr)); | ||
| eprintln!( | ||
| "force-run: {dir} will not fast-forward — a stale or diverged install dir is a \ | ||
| STOP, not something to force past. Fix the checkout, then force the run." | ||
| ); | ||
| return 3; | ||
| } | ||
| Err(e) => { | ||
| eprintln!("error: cannot run {:?}: {e}", plan.fast_forward.join(" ")); | ||
| return 2; | ||
| } | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Look for an existing install-dir repair transition in the CLI surface.
set -euo pipefail
# List every clap subcommand variant declared in the CLI enum.
ast-grep outline pr-review-report-rs/src/main.rs --items all --type enum --match 'Cmd'
# Search for any existing repair/reset/fast-forward transition.
rg -n --type=rust -C3 'ff-only|fast_forward|fast-forward|reset --hard|install_dir'Repository: rainlanguage/issue-pr-cron
Length of output: 947
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the relevant definitions without running repo code or installing deps.
wc -l pr-review-report-rs/src/main.rs
echo '--- ForceRun definition ---'
sed -n '41280,41390p' pr-review-report-rs/src/main.rs
echo '--- ForceRun dispatch snippet ---'
sed -n '45228,45255p' pr-review-report-rs/src/main.rs
echo '--- force_run_mode ---'
sed -n '37190,37240p' pr-review-report-rs/src/main.rs
echo '--- command matches containing install/fast-forward/repair/reset/diverge ---'
rg -n --type=rust -C2 'Fast|Install|InstallDir|reset --hard|checkout|fast.forward|ff-only|diverge|rerun|force-run|force_run|RepairQaBlock|WeakenCloses' pr-review-report-rs/src/main.rsRepository: rainlanguage/issue-pr-cron
Length of output: 50383
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- exact force-run mode error string ---'
rg -n 'will not fast-forward|Fix the checkout|force-run|no run' pr-review-report-rs/src/main.rs
echo '--- command names likely candidates ---'
sed -n '41110,41255p' pr-review-report-rs/src/main.rs | rg -n 'Repair|Reset|Rebase|Checkout|Fast|Pull|Force' \
|| true
echo '--- tests around force-run fast-forward failure message ---'
rg -n -C4 'will not fast-forward|force-run .* Fix|fast-forward.*dive' pr-review-report-rs/src/main.rsRepository: rainlanguage/issue-pr-cron
Length of output: 3648
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- exact force-run mode error string ---'
rg -n 'will not fast-forward|Fix the checkout|force-run|no run' pr-review-report-rs/src/main.rs
echo '--- command names likely candidates ---'
sed -n '41110,41255p' pr-review-report-rs/src/main.rs | rg -n 'Repair|Reset|Rebase|Checkout|Fast|Pull|Force' \
|| true
echo '--- tests around force-run fast-forward failure message ---'
rg -n -C4 'will not fast-forward|force-run .* Fix|fast-forward.*dive' pr-review-report-rs/src/main.rsRepository: rainlanguage/issue-pr-cron
Length of output: 3648
Provide an executable install-dir repair transition for failed fast-forwards.
force_run_mode exits 3 and says "Fix the checkout, then force the run", but no pr-review-report subcommand performs that repair. Add or extend a pr-review-report subcommand that handles a stale or diverged install dir and point callers at it from the exit-3 message.
🤖 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 `@pr-review-report-rs/src/main.rs` around lines 37218 - 37235, Add a
pr-review-report subcommand that repairs stale or diverged install directories
after fast-forward failure, then update the exit-3 message in force_run_mode to
name the exact executable repair command callers should run before retrying.
Ensure the new command performs the required checkout synchronization and is
wired into the CLI dispatch.
Source: Coding guidelines
| for (row, (run, p, g, t, i, helpers, h)) in measured_corpus().iter().zip(want.iter()) { | ||
| assert_eq!(&row.run, run); | ||
| assert_eq!(row.probes, *p, "{run} probes"); | ||
| assert_eq!(row.gh_api, *g, "{run} gh api"); | ||
| assert_eq!(row.tarball, *t, "{run} tarball"); | ||
| assert_eq!(row.interpreters, *i, "{run} interpreters"); | ||
| assert_eq!(row.helper_scripts, *helpers, "{run} helper scripts"); | ||
| assert_eq!(row.harness_scaffold, *h, "{run} harness scaffold"); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Assert the fixture row count before zipping.
zip stops at the shorter iterator. If measured_corpus() later returns fewer rows, this test still passes and the fixture-integrity guarantee is silently lost. Every later test builds on this fixture.
♻️ Proposed fix to make the fixture check total
- for (row, (run, p, g, t, i, helpers, h)) in measured_corpus().iter().zip(want.iter()) {
+ let rows = measured_corpus();
+ assert_eq!(rows.len(), want.len(), "the fixture lost a run");
+ for (row, (run, p, g, t, i, helpers, h)) in rows.iter().zip(want.iter()) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (row, (run, p, g, t, i, helpers, h)) in measured_corpus().iter().zip(want.iter()) { | |
| assert_eq!(&row.run, run); | |
| assert_eq!(row.probes, *p, "{run} probes"); | |
| assert_eq!(row.gh_api, *g, "{run} gh api"); | |
| assert_eq!(row.tarball, *t, "{run} tarball"); | |
| assert_eq!(row.interpreters, *i, "{run} interpreters"); | |
| assert_eq!(row.helper_scripts, *helpers, "{run} helper scripts"); | |
| assert_eq!(row.harness_scaffold, *h, "{run} harness scaffold"); | |
| } | |
| let rows = measured_corpus(); | |
| assert_eq!(rows.len(), want.len(), "the fixture lost a run"); | |
| for (row, (run, p, g, t, i, helpers, h)) in rows.iter().zip(want.iter()) { | |
| assert_eq!(&row.run, run); | |
| assert_eq!(row.probes, *p, "{run} probes"); | |
| assert_eq!(row.gh_api, *g, "{run} gh api"); | |
| assert_eq!(row.tarball, *t, "{run} tarball"); | |
| assert_eq!(row.interpreters, *i, "{run} interpreters"); | |
| assert_eq!(row.helper_scripts, *helpers, "{run} helper scripts"); | |
| assert_eq!(row.harness_scaffold, *h, "{run} harness scaffold"); | |
| } |
🤖 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 `@pr-review-report-rs/src/main.rs` around lines 71885 - 71893, In the test
containing the measured_corpus() and want iteration, assert that
measured_corpus().len() equals want.len() before calling zip. Keep the existing
per-row assertions unchanged so every fixture row is still validated after the
count check.
| fn tmp_dir(tag: &str) -> PathBuf { | ||
| let dir = std::env::temp_dir().join(format!( | ||
| "force-run-{tag}-{}-{}", | ||
| std::process::id(), | ||
| std::time::SystemTime::now() | ||
| .duration_since(std::time::UNIX_EPOCH) | ||
| .unwrap() | ||
| .as_nanos() | ||
| )); | ||
| std::fs::create_dir_all(&dir).expect("scratch dir"); | ||
| dir |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Clean up the test repositories.
tmp_dir creates a new root directory for every test, but no test removes it. Repeated local or CI runs retain bare repositories, clones, and Git objects in the system temporary directory.
Return an RAII cleanup guard for the root directory, or use an existing temporary-directory helper with Drop cleanup.
🤖 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 `@pr-review-report-rs/tests/force_run.rs` around lines 16 - 26, Update tmp_dir
to return an RAII temporary-directory guard, preferably by using the existing
temporary-directory helper if available, so the root directory and all test
repositories beneath it are removed on Drop. Preserve the current
unique-directory creation behavior and update callers in the force-run tests to
use the guard’s path.
|
Reviewed 20826c7: pass This PR restores two things I dropped when I wrote #252. The user asked for a command that embodies "forcing producer and vetter, watching, reviewing tool corpus then making optimization recommendation"; #253 shipped the middle two because my issue and brief narrowed the ask — I wrote "gathers and measures, it does not decide" and instructed the agent not to name what to build. That was my invention, not their words.
The recommendation lives in The merge resolution. Git split the conflict into three regions by aligning ACCIDENTAL common trailers ( Rulings-conformance:
25 mutants over the new lines, 25 killed after the first pass exposed two weak assertions in the decay-table test. One pre-existing thing correctly flagged and not touched: a local CI: all checks pass; the single non-pass is |
Refs #252
This completes the original request. The user asked for
— four things. #253 tooled the
middle two and dropped the outer two: forcing stayed prose the caller
retyped, and the recommendation was replaced with an explicit refusal to make
one. Both are restored here. Nothing in
watch-run,token-profileor theexisting
corpus-reporttable changes behaviour.1. Forcing is a typed call now
producerorvetter. Refused otherwise (exit 2) rather than defaulted — a typo'dvetterthat fell through to the producer is a whole run spent on the wrong queue--install-dirINSTALL_DIR; empty counts as absent. Refused (exit 2) if neither is set: a run forced against a guessed dir spends real money somewhere nobody is watching. Canonicalised, because it lands verbatim in agit+file://URL where..resolves against nothing--no-runIt does the two steps that were prose in
observe-run.md:git -C <dir> pull --ff-onlyfirst (the runner is a flake package built from that dir's own gitHEAD, so a run against a stale checkout silently exercises old code — which
happened twice on 2026-08-09 at a full run's cost each time), then
nix run git+file://<dir>#<campaign|review>-run -- --force.It streams the child's stdout rather than handing off to
watch-run, throughthe same
watch_line_signalfilter. Three reasons, and the first is the one thatdecided it:
between them, and every lifecycle line the runner writes inside it (
FORCED past …,usage-gate: …,run START) belongs to that run and would bemissed. A child's stdout has no gap and carries nothing but this run, so
neither half of the attribution can go wrong — which is the exact bug class
human-fsm command: force a run, watch it, measure it, mine the corpus, recommend the next subcommand #252 is about, arriving from the opposite direction to the tail-replay one.
same call it started.
flock (an open descriptor on the runner process). A detached run outlives a
killed watcher and keeps spending, blind.
watch-runis unchanged and still shipped: it is the reattach path for a runthis command did not start, or one whose stream was cut, and
observe-run.mdsays so.
--forcesemantics are untouched — the policy/correctness split stays entirelythe runner's. The fast-forward refusal is force-run's own correctness stop and
there is no flag that walks past it.
2. The recommendation is back
Computed in
corpus-report, so it is testable and available without the command,and relayed by the command's final step.
corpus-reportnow ends on aBUILD NEXT:block; the JSON gains arecommendationobject.The rule, encoded rather than left to the reader:
runs that still do it —
latest_sightingagainstpeak— not over the fullseries. That normalisation is the whole of "the render harness is rebuilt by
every run that takes a screenshot item": the harness scaffold's full series is
11 → 7 → 7 → 0and reads as a collapse, while the runs that actually took ascreenshot item did 11, 7, 7 and did not shrink at all.
most recent sighting, then volume, then name (a total order, so the pick never
depends on the order the metrics are declared in).
Frequency is the discriminant because novelty is not, and there is a test named
after the mistake: the tarball extraction is newer than everything else, is at
its own peak, and reads
holding— and it still loses, because it is in onetrace. That is the "the newest run wasted a worker on a tarball" answer the issue
says would have been wrong.
The evidence travels with the recommendation. The block names the traces that
exhibited the pick and the count in each, everything it beat and on what, and
everything ruled out as shrinking with the ratio that ruled it out. A
recommendation nobody can check is worse than a table.
Where the corpus has no case — every hand-roll absent or already shrinking — it
says so instead of promoting the least-bad row.
What it says against the live corpus today
corpus-report /home/gildlab/issue-pr-cron/runs, read-only, 21 traces:It names harness scaffold — the same answer the 2026-08-09 session reached by
hand, reached here from the counts. It beats raw
gh api(2 traces, last seen20260802) and the tarball (1 trace, last seen 20260809), and rules out probes,
interpreters and helper scripts as already shrinking — each of which is a tool
that landed and is working.
Also
observe-run.mdrewritten: step 1 is theforce-runcall, step 2 iswatch-runas the reattach path, and the final step names the recommendationand prints its counts. The "do not name the next subcommand to build"
paragraph is gone — it was never the user's instruction.
allowed-toolsnarrows toBash(pr-review-report:*): with forcing typed,the command has no reason to reach for
nixorgitat all.QA
observation_recommendation_tests+ 9 integration tests inpr-review-report-rs/tests/force_run.rs(real git repos againstfile://remotes; no run is ever started —--no-runthroughout, bothDISABLEDflags untouched). All fail on base: the subjects do not exist there. Headline:the_corpus_names_the_hand_roll_worth_tooling_next,the_newest_runs_most_visible_waste_does_not_win_on_novelty,shrinking_is_read_over_the_runs_that_still_do_it,an_install_dir_that_will_not_fast_forward_is_a_stop,the_dir_in_the_flake_ref_is_canonical.the_corpus_names_the_hand_roll_worth_tooling_next: it checked that the evidence line contained a count (so a mutant that ran the counts into the run id beside them still passed) and never checked the recency the ranking breaks ties on. It now asserts the whole line and the pick'slast_seenoutright, and both mutants die. Full list:shrinking is read off the newest run, not the newest sighting-> killed byshrinking_is_read_over_the_runs_that_still_do_itthe shrinking test is inverted-> killed bythe_corpus_names_the_hand_roll_worth_tooling_nextnothing is ever shrinking-> killed bya_corpus_where_everything_is_shrinking_recommends_nothingcandidates and shrinking are swapped-> killed bythe_corpus_names_the_hand_roll_worth_tooling_nextthe rarest hand-roll wins instead of the commonest-> killed bythe_newest_runs_most_visible_waste_does_not_win_on_noveltythe recency tie-break is dropped-> killed bythe_ranking_never_depends_on_declaration_orderthe volume tie-break is dropped-> killed bythe_ranking_never_depends_on_declaration_orderthe name tie-break is dropped, so a full tie follows declaration order-> killed bythe_ranking_never_depends_on_declaration_ordernever-seen metrics are ranked as candidates-> killed bya_corpus_with_no_evidence_recommends_nothingsightings include the runs that did not exhibit it-> killed bythe_frequency_the_recency_and_the_counts_are_one_serieslast seen is the OLDEST sighting-> killed bythe_corpus_names_the_hand_roll_worth_tooling_nextthe evidence line drops the per-run counts-> killed bythe_corpus_names_the_hand_roll_worth_tooling_nextan unknown role defaults to the producer-> killed bya_role_is_producer_or_vetter_and_nothing_elsethe roles reach each other's runner-> killed bythe_plan_pulls_the_dir_it_then_builds_the_runner_fromthe roles reach each other's log-> killed bythe_plan_pulls_the_dir_it_then_builds_the_runner_fromthe runner is invoked without --force-> killed bythe_plan_pulls_the_dir_it_then_builds_the_runner_fromthe stream is unfiltered-> killed bythe_forced_runs_own_stdout_is_filtered_by_the_same_rulethe forced stream stops at the run's END line, truncating it-> killed bythe_forced_stream_does_not_stop_at_the_runs_end_linethe pull rebases instead of refusing to fast-forward-> killed byan_install_dir_that_will_not_fast_forward_is_a_stopa refused fast-forward reports success-> killed byan_install_dir_that_will_not_fast_forward_is_a_stopthe install dir is used as given, not canonicalised-> killed bythe_dir_in_the_flake_ref_is_canonical--no-run starts the runner anyway-> killed bya_stale_install_dir_is_fast_forwarded_before_the_runner_is_namedan absent install dir is guessed instead of refused-> killed bya_missing_install_dir_is_refused_rather_than_guessedan empty INSTALL_DIR is taken literally-> killed byan_empty_install_dir_in_the_environment_is_not_an_install_dirthe environment fallback is dropped-> killed bythe_install_dir_may_come_from_the_environmentthe_fixture_reproduces_the_live_per_run_counts), and the live run above is reproduced in the body.force-run) and recommendation (corpus-report'sBUILD NEXTblock, relayed by the command) — covered A,B,C,D. Not built: no issue is filed from the recommendation, no cron wiring, no prompt edit; the command still writes no GitHub state.Summary by CodeRabbit
New Features
force-runworkflow to update and run producer or vetter tools in the foreground.Bug Fixes
Chores