ci: the settings export stamp is derived, never committed
What
The settings export stamp (schemas/settings-export/settings-schema.sources.sha256) is no longer committed. It is now gitignored and derived when needed: defaults_guard.py source-stamp --ref REF prints it for any commit.
- PR gates:
check-stampis gone. It compared the committed stamp with the sources, in the generated files job, the guards workflow and fast-gates. Instead, the generated files job of cmux-next.yml (Mac, already builds CmuxNext) runs the settings export fresh and fails when the committedsettings-schema.jsonordocs/mdm/*differ. It only does this when the event changed the settings sources (their derived stamp differs between HEAD^1 and HEAD), the same scopecheck-stamp --basehad; otherwise any staleness is the base's, and the base autofix regenerates it after the merge. - Base autofix: "the inputs did not move since the exports were made" now compares
source-stampof the made-at commit with the tip's, instead of reading the stamp out of the export. Where a script asked whether the committed exports at the tip are current (only the stamp could answer that on Linux), it now treats them as not known current and regenerates or keeps them; landing an identical file is a no-op. - SettingsSchemaExportTests: no longer writes or compares a stamp, and
sourceStamp()is removed. - Lists: the stamp is removed from every list of settings exports (regenerate-settings-exports.sh, base-autofix-push.sh, generated_outputs.py, next_batch.py, pr-autofix-apply.sh, run-swift-testing-suites.sh artifacts, .gitattributes).
Why
Leo, 2026-10-21: "JUST GENERATE AND DON'T COMMIT. DERIVE MORE." The stamp had one line per CmuxNextSettings source, so every PR that touched a settings source changed it. It conflicted on nearly every base move, and its red needed a 30-minute Mac export to clear. #22674 has been stuck in that loop: each base move brought a new stamp conflict and a new export.
This is the first PR of the generated-files cutover. settings-schema.json and docs/mdm stay committed for now: cmux-tui include_str!s the schema and webviews imports it, so un-committing them needs a Linux generator (or a build-time fetch) first.
Testing
Tests first: the red commit (015092bf722) adds tests/test_settings_stamp_not_committed.py, with 4 failures.
tests/test_settings_stamp_not_committed.py: 7 OK (the stamp is untracked and ignored, nothing reads it, nocheck-stampgate; the generated files job's step, run in a temp repo, exports nothing when only non-settings Swift changed and exports when a settings source changed). Red at fd8c4041a03 after the first review.tests/test_defaults_guard.py: 38 OK. The stamp tests are replaced bysource-stamptests.tests/test_cmux_next_fast_gates.py: 38 OK.tests/test_cmux_next_pr_autofix_settings.py: 6 OK.tests/test_cmux_next_route.py: 35 OK.tests/test_next_batch.py: 128 OK.scripts/cmux-next/tests/{base-autofix-changed,base-autofix-settings-landed,base-autofix-push,base-autofix-action-exports,base-autofix-land,generated-ownership,pr-autofix}.test.sh: all 7 exit 0. They need GNUtimeoutand unpaddedwcshims on macOS; CI's Linux has the real tools.tests/test_cmux_next_base_autofix_finishes.py: 35 OK.- Base autofix behavior:
settings-landednow takes MADE_AT and the run's exports dir. It passes when the tip's settings exports are the ones the run made for the tip's sources. The "already current on the tip" drop in base-autofix-push.sh is removed: held settings exports now wait and fail the run, and the next run makes them. The stale-at-HEAD trigger in base-autofix-changed.sh is removed: the settings area comes from changed paths only. tests/test_swift_testing_suite_timeout.py: 30 of 32 OK.SettingsExportCatalogTests.test_warm_export_uses_current_catalogfails on this machine in both entrypoints, the same way at baseorigin/feat-cmux-next(xcrun DEVELOPER_DIR in the fake toolchain). Its expected artifact count is updated from 7 to 6.
Observed / Not run
- Observed: the tests above, locally on cmux16.
- Not run: the Swift test edit was not compiled locally (no local Swift builds); CI's fleet compile covers it. The generated files job's new step has not been seen on a real PR yet; the first settings PR after this lands will show it.
Take-backs
The settings export's freshness has been repaired three times in the last week, each time by making
the committed stamp behave better. This PR removes the committed stamp instead, so those three
changes go with it; that is the point of the PR, not a regression of their intent. Each one's
behaviour is kept by the fresh-export comparison in
scripts/cmux-next/check-settings-exports.sh.
Takes back #21985: it made the stamp merge line by line (a stamp entry per source instead of one
schema_hash line) so a base move would conflict on fewer lines. A derived stamp conflicts on no
lines at all, so the per-source stamp file is deleted. Its scripts/ci/next_batch.py entry for the
exports stays, now read from the one list rather than hand-kept.
Takes back #23056: it made a stale settings export fail only the change that made it stale, by
comparing the committed stamp with the sources. The fresh-export comparison keeps exactly that
behaviour -- an export whose fresh bytes are the same from either side's sources is the base's lag
and only warns -- without a committed file to go stale.
Takes back #23620: it named a stamp a base merge kept as the pull request's own red, which was
needed because a merge could carry an older stamp beside newer sources. With nothing committed
there is no stamp for a merge to carry, and the same case is now caught by exporting from the base
parent's sources and comparing.
Changelog
none
Leo-ask: ask-1e4fb32bd7
Agent session: cmux16
š¤ Generated with Claude Code