generated-files guard: a regeneration from the base's inputs is not a hand edit
Cause
698b3ef64cf changed cmux-tui/spec/sdk-schema.json (the new side field on new-pane-right) without rerunning scripts/cmux-next/gen-phone-lane-fields.py, so Packages/macOS/CmuxNext/Sources/CmuxNextMobile/DaemonLane/DaemonLanePolicy+Fields.generated.swift is stale on feat-cmux-next and the "cmux-next checks" job prints stale: ... (run scripts/cmux-next/gen-phone-lane-fields.py).
The fix is to regenerate it, but no pull request could. scripts/ci/generated_files_guard.py's inputs rule (hand_edits) refuses a change to a generated output unless the same pull request changes the generator or one of its inputs. The input already changed on the base, so an honest regeneration looks like a hand edit. A lane's PR that regenerated it was refused this way (run 40833725341, job 123313867859).
The other route, making it a generated_outputs.py export the base autofix regenerates, doesn't fit:
rule_changesrefuses a new export pattern on purpose ("a new output belongs at build time, not in the base autofix's list").- This table is the phone lane's field allowlist. The generator's docstring says a new daemon field "is refused until this script runs again and the change is reviewed", so a bot regenerating it on base would skip that review.
Fix
A generator in scripts/ci/generated-files.json may name a check: arguments that make the generator (a .py script) exit 0 only when every output equals what it writes from the tree's inputs. When the base's manifest gives one and it passes at the head, an output change with no input change is a regeneration, so the guard lets it through. Nothing a later run would undo gets in:
- A hand edit still fails the check, so it is still refused, and the error now says why (
not a regeneration (python3 -I ... --check exits 1: ...)). - The check comes from the base's rules, so a pull request can't add a check and use it on its own change.
- A check that can't run (missing script, OSError, a 120 s timeout) counts as failed.
- The check must be the generator's own script; the manifest can't name an arbitrary command.
The seven stdlib Python generators under scripts/cmux-next that already have a --check comparing every output they write (settings-sources, motion-scale, type-scale, status-glyphs, phone-lane-fields, protected-folders, chrome-styles) get "check": ["--check"]. The guard job already runs head code (its own tests) on pull_request with contents: read, so running the head's generator adds no new trust.
Order: rules are read from --base, so this PR doesn't apply to itself (it doesn't touch any output). Once it lands, a follow-up PR that only regenerates DaemonLanePolicy+Fields.generated.swift passes the guard.
Risk: low. The change is CI-only (one guard plus its manifest), and it only lets through output changes that the generator's own check confirms.
Evidence
New tests in tests/test_generated_files_guard.py, each in a scratch repo whose base changed an input without regenerating:
-
test_a_regeneration_from_the_bases_inputs_passes_when_the_generator_has_a_check -
test_a_hand_edit_still_fails_when_the_generator_has_a_check(a hand edit of the fresh output and of the stale one) -
test_a_regeneration_fails_without_a_check -
test_the_check_comes_from_the_base -
test_a_check_that_cannot_run_counts_as_failed -
test_a_check_must_be_arguments_to_a_python_generator -
The repository-manifest test also asserts every
checkgenerator exists. -
Green: run 40835005990. All 28 tests pass at 60dc961b, and
generated_files_guard.py checkexits 0. -
Red: run 40835328515. The same tests against the base guard (621568b3) give
FAILED (failures=5, errors=1), includingtest_a_regeneration_from_the_bases_inputs_passes_when_the_generator_has_a_check; the rest are the new error wording and the newcheckfield.
Agent session: agent session
🤖 Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Allows the generated-files guard to accept a regenerated output when the base changed its inputs but left the output stale. Hand edits still fail unless the generator’s check confirms the output matches its inputs.
- Adds
--checkvalidation for seven Python generators and tests the regeneration, hand-edit, and failure cases.