mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
* fix(ci): unbreak per-package clippy on bin-only crates; classify `.gitignore` Two latent CI bugs, one per lane, that #6965 is the first PR to trip. `Check production-target lints` runs `cargo clippy -p <changed package> --lib --bins`. `--lib` is a hard error on a bin-only package, so the first PR whose only changed package is `ironclaw` (crates/ironclaw_reborn_cli) dies on `no library targets found in package `ironclaw`` — exit 101, before a single lint runs. `--bins` alone is the quieter half of the same bug: on a lib-only package cargo warns `target filter `bins` specified, but no targets matched; this is a no-op` and the lane reports green having linted nothing. Cargo's default target set is already lib + bins, with tests, examples, and benches excluded, so the production-target lane needs no filter at all. `reborn_pr_test_plan.py` had no rule for root `.gitignore`, so its fail-closed arm raised `unclassified pull-request path: .gitignore` and failed the whole `Tests (Reborn)` roll-up on any PR that adds an ignore rule. It joins the decided-paths set rather than the repo-root prose set, because something does read it: Code Style filters on it for `has_code` and runs `Reject tracked files that match .gitignore`. That is the shape already recorded there for `scripts/no_panics_reborn_baseline.txt` — owned by a static check, read by no Reborn lane — and it leaves the decision in the plan's `reasons`. `validate_production_lint_targets` keeps the lane filter-free. It rejects every explicit target selector, not just the two that caused the outage: `--bin` pins a multi-bin package to one target, and `--test`/`--example`/ `--bench` and their plurals pull in what the lane exists to exclude. Flags are matched on word boundaries so `--bins` is not also reported as `--bin`, and `clippy_matrix` is checked too, since `${{ matrix.flags }}` expands into the same command. The check reads the step body rather than locating the command and parsing its arguments: a matcher is a thing to fool, and scanning has no match position to displace and no command formatting to get wrong. The trade — a command deliberately written to look inert would pass — is recorded beside the constant, along with the zero-target-package gap that is unreachable today. Verified red before the fix, green after: reverting the workflow to `--lib --bins` fails the contract; removing the `.gitignore` classification reproduces `ValueError: unclassified pull-request path: .gitignore`; unhooking the validator from `validate_workflow_texts` fails the top-level test. Suites: 30 workflow contracts, 44 planner, 6 shards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ci): reject edits that run the production lint and discard its verdict From review: the contract checked which flags the lint passes but not whether anyone reads its exit status. `|| true`, `|| :`, `set +e`, and a step-level `continue-on-error: true` all leave the lane running clippy and ignoring the result — the silent-green failure the whole contract exists to prevent. These are worth catching where a disguised command is not. Each is a plausible edit made on purpose and for a stated reason — unblock the queue, quiet a flaky lane — rather than an attempt to fool a validator, and the check is a substring scan over the same step body, so it adds no matcher to bypass. The `echo cargo clippy` case raised alongside it stays out of scope for the reason recorded beside the constant: it requires deliberate disguise in a file that only changes through reviewed PRs, and chasing it is what grew the previous parser through three bypasses. Verified: removing the check fails all four sabotage cases. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>