Repository navigation
base autofix: regenerate the settings exports on the newest tip, so they land - #23249
Conversation
…, or holds them Cases for the settings-export livelock: exports made at SETTINGS_MADE_AT land on a tip that did not move their inputs after it, an export made before its inputs moved never lands, a made-at commit outside BASE..tip holds them, an export someone else already made current on the tip is dropped without failing the run, the Mac regeneration retries on the newest tip at most N times and then holds and errors, and only a commit's subject marks it as base autofix's own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
…hey land A run takes about 28 minutes and feat-cmux-next takes a push about once a minute, so some push nearly always moved a settings-export input after the run's SHA, and base-autofix-push.sh held the exports on every run: they never landed (9 stale sources on the tip, #21897's sessions.promptSuggestions among them). - The Mac action-exports job regenerates the settings exports through base-autofix-settings-exports.sh: after each regeneration it reads the newest tip, and when the tip moved the settings inputs since the commit it made them at, it checks that tip out and makes them again, at most 3 times. It outputs settings_made_at. When the inputs still moved after the third, it hands on no settings export, prints ::error and fails the job. - base-autofix-push.sh takes SETTINGS_MADE_AT and judges the settings exports from it instead of the run's SHA: input moves and newer writes since then, and it lands every export file whose made copy differs from the tip's. A made-at commit outside BASE..tip holds them. An export made from inputs that changed after it is never landed as it is. - When the settings exports would wait but the tip's own stamp is current (defaults_guard.py check-stamp), they are dropped from the repair and the run does not fail for them. - Only a commit's subject marks it as base autofix's own; a body quoting one no longer hides a lane's write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Owned by land-and-ci on huge-blue · session |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
web and size checks: green |
|
Found 5 test failures on Blacksmith runners: Failures
|
|
Bead |
…ETTINGS_MADE_AT Review of #23249: a failed diff or area read on SETTINGS_MADE_AT..tip must never read as "the settings did not move"; a SETTINGS_MADE_AT that is a real commit off the branch holds the exports with an error even when the tip's stamp is current; and the slow settings step can't cost the action exports their upload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
…the settings step - base-autofix-push.sh reads SETTINGS_MADE_AT..tip's diff and its input areas as separate steps and fails the run on either (with pipefail, a failed diff made grep read nothing and pass as "settings did not move"). input_areas now returns the areas script's status. - An invalid SETTINGS_MADE_AT holds the settings exports with ::error and never takes the stamp-current drop. - base-autofix-settings-exports.sh counts a failed diff or area read as moved, and removes its temp dir on EXIT. - The Mac settings step gets timeout-minutes: 40 (the job has 60), so a slow regeneration fails only that step and the artifact upload still runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
|
Review (subagent, correctness first): SHIP at d443614 Push-script and finish tests pass (base-autofix-push.test.sh; test_cmux_next_base_autofix_finishes 20 OK). Follow-ups, not blocking: upload the action exports before the settings step, so a slow cold runner can't lose them to the 60-minute job timeout; an explicit ::error when the repair commit is empty; stage the settings copy, then move it into place. All five reds on this head are GitHub App installation 403s (API rate limit exceeded for installation), which count as infra and base reds: pr-capture-check, generated drift, action export areas, high-signal review link, and merge freeze. No reruns, because they spend the same exhausted installation budget. merge freeze could not read freezes, so I checked by hand: no open merge-freeze issue exists. |
…a shallow checkout Base red from #23249: tests/test_ci_no_shallow_dev_fetch.py flags base-autofix-settings-exports.sh:60 on every PR's cmux-next CI guard group (seen on #23264, job 119156226796). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
|
Shipped in:
|
…ng (#23315) * test: base autofix makes a held repair again on the moved tip, keeps its Mac autofix, and judges settings by their stamp Red against feat-cmux-next dfb5330: base-autofix-land.sh and base-autofix-repair.sh do not exist, the autofix job's concurrency group lets a newer push replace the pending autofix that carries the Mac exports, the push holds settings exports past a settings-area move that is no stamp input, and defaults_guard.py has no source-stamp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: make a held repair again on the moved tip, keep the Mac exports' autofix, judge settings by their stamp - base-autofix-land.sh pushes the repair (base-autofix-push.sh) and, when the push holds a file this job makes itself because the tip moved its inputs (the CI target graph, the SDK layers, the ratchet, the cloud clients, cargo fmt), checks out the tip, makes the repair again there and pushes that, at most three rounds. Held Mac exports still wait for the next run, which the failed run keeps in its "since". The repair never sees the push token. - base-autofix-repair.sh is the Repair step's logic, shared by the first round and the rounds on a moved tip (which apply no Mac export or webviews patch made at the older commit). - The autofix job of a run that carries Mac exports has a concurrency group of its own, so a newer push can no longer replace it while it waits behind another run's autofix. Groups are per branch. - The settings exports' inputs are what their stamp hashes (defaults_guard.py source-stamp): a settings-area move that leaves the tip's stamp the exports' own, with the same regeneration, neither holds them at the push nor makes the Mac job regenerate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * test: a push that never got through is no hold, and a held inspector page waits Red on aa0c3fd (review): the push script's held list survives a failed attempt, so base-autofix-land.sh remade only the graph and could pass without the settings exports; and remade() counted the Chief inspector page, which only the webviews patch makes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: only a hold hands on a held list, and the inspector page is never remade on the tip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: the settings regeneration fetches with --depth only in a shallow checkout Base red from #23249: tests/test_ci_no_shallow_dev_fetch.py flags base-autofix-settings-exports.sh:60 on every PR's cmux-next CI guard group (seen on #23264, job 119156226796). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: settings fetch expands an empty depth list safely under bash 3.2 Review of #23315: in a full clone the depth array is empty, and "${depth[@]}" under set -u stops on macOS /bin/bash 3.2. Same guarded expansion as the repo's other depth fetches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Summary
The base autofix never lands the settings exports. A run takes about 28 minutes and feat-cmux-next takes a push about once a minute, so by the time
base-autofix-push.sh(#22004) runs, some push has nearly always moved a settings-export input since the run's SHA, and the push holds the exports. It does that on every run. The exports are stale on the tip right now:python3 scripts/ci/defaults_guard.py check-stamplists 9 stale sources, #21897'ssessions.promptSuggestionsamong them.Now the exports are made again on the newest tip within the same run, and the push judges them from the commit they were made at:
action exportsjob's settings step runs the newscripts/cmux-next/base-autofix-settings-exports.sh. After each regeneration it fetches the newest tip. If the tip moved the settings inputs (autofix-areas.sh's settings area) since the commit it generated on, it checks that tip out and regenerates there, at most 3 times. It outputssettings_made_at, and hands on every export file whose made copy differs from the run's SHA, so the repair carries the whole export as made there. If the inputs still moved after the third try, it hands on no settings export, prints::errorand the job fails. A diff or area read that fails counts as moved. The step hastimeout-minutes: 40inside the job's 60, so a slow regeneration fails only that step (continue-on-error) and the action exports' upload still runs. Only the Mac job can regenerate and only the Linux job holds the write token, so the retry lives on the Mac side and the SHA crosses over as a job output.base-autofix-push.sh REMOTE BRANCH BASE_SHA [SETTINGS_MADE_AT]judges the settings exports by input moves and newer writes inSETTINGS_MADE_AT..tipinstead ofBASE_SHA..tip. When they land, they land whole: every listed export file whose made copy differs from the tip's (the list matchesregenerate-settings-exports.sh, and a test checks that). ASETTINGS_MADE_ATthat isn't a full SHA betweenBASE_SHAand the tip holds them and fails the run with::error, even when the tip's stamp is current. The diff ofSETTINGS_MADE_AT..tipand its input areas are read as separate steps, and the run fails closed if either fails. Everything else is still judged fromBASE_SHA.SETTINGS_MADE_ATis valid) butdefaults_guard.py check-stamp --ref <tip>passes, someone else already regenerated them. They're dropped from the repair and don't fail the run.newer_writesusedgit log --grep, which matches any line of a message. A lane commit whose body quotedchore(next): base autofix at ...(a squash merge, say) had its writes ignored, and the repair overwrote them. Now only the subject counts.build-web-bundles.sh, which does nothing when they're current).Invariant: an export generated from inputs that changed on the moved tip is never landed as-is. It is regenerated on the newest tip (and judged from there), or it is held. Case 19 below tests exactly this. Removing the
SETTINGS_MADE_AT..tipmove check makes it fail ("exports whose inputs moved after they were made passed the run").Catalog narrowing: not implemented. The exports do read the string catalog.
SettingsSchemaExport.json(catalog:)reads its key set, and every schema/MDM text comes fromModuleResourceBundle.text→String(localized:bundle:), the compiled table in the test process's preferred language. That language isn't pinned to English, so a non-English value change can change the exports on a runner whose language isn't English. "Only non-envalues changed" doesn't prove the exports are unchanged, so the catalog stays an input. The bounded regeneration covers that case anyway.Testing
Round 2 (review fixes)
Review found that, with
pipefail, a failedgit diff SETTINGS_MADE_AT tip(or areas script) madegrep -x settingsread nothing and exit 1, which read as "settings did not move", so stale exports could land. The tests came first (9ed66ae), then the fix (d443614). Same commands as below.SETTINGS_MADE_ATthat is a real commit off the branch (the repair itself), with the tip's stamp current, must fail with::errorand land nothing.SETTINGS_MADE_AT..tipmust fail the run and land nothing.timeout-minutes, below the job's, and the upload comes after it.Red on 9ed66ae (the bash test stops at its first failure, which is 21b; with
failmade non-fatal, 21c also fails with "exports landed although what moved after they were made was unreadable"):Green on d443614:
Round 1
Tests were committed first (97df8ab), then the fix (05d332f). Same commands for both:
New bash cases: 16 (subject-only match), 17 and 18 (a) exports made at a moved tip land, including a file the tip had stale and the regeneration left as at
BASE_SHA, 19 (b) the invariant, 20 (b) a made-at commit outsideBASE..tipholds, 21 (d) superseded drop passes the run, 22–24 regeneration at the run's SHA / once more on a moved tip / not for an unrelated move, 25 (c) three tries with inputs always moving: holds,::error, no export handed on. The fakedefaults_guard.pyin the test now answerscheck-stamp --ref. New Python cases cover the workflow shape (settings_made_atoutput, the push passes it) and the export list matchingregenerate-settings-exports.sh.Red on 97df8ab (the bash test stops at its first failure):
The same file with
failmade non-fatal, to show each new case on the base scripts: 16, 17, 18, 20, 21 and 22–25 fail. 17 and 18 fail with "changed the inputs or the files of everything this repair writes", 18 also with "docs/mdm as made at the tip did not replace the tip's stale copy: mdm stale", 21 with "a repair the tip already superseded failed the run", and 22–25 becausebase-autofix-settings-exports.shdoesn't exist. 19 already passes on the base, which held every export whose inputs moved sinceBASE_SHA. It's there to keep the invariant through this change.Green on 05d332f:
Observed / Not run
python3 -m unittest tests/test_cmux_next_checks_workflow.py(OK),python3 scripts/cmux-next/prepush-gate.py(no new violations), andactionlintoncmux-next-base-autofix.yml(clean; no local shellcheck, so its run-block shellcheck pass ran only in CI). I also checked by mutation that case 19 fails when theSETTINGS_MADE_AT..tipmove check is removed.base-autofix-settings-exports.sh, and the step timeout firing. Both were only read.settings_made_at. The push then judges them fromBASE_SHA, where their inputs moved, so they're held, but the copy isn't atomic.regenerate-settings-exports.sh. Checking out a newer tip mid-job, the incrementalswift buildthere, and the web-bundle rebuild have only been read, not run. The first real base run after merge is the first live check.Known gaps (not fixed here)
CmuxNextDesignfeeds the schema (ModuleResourceBundlelives there) but isn't a settings-area input and isn't in the stamp.BASE_SHA, whether or not this run regenerated the action exports.Changelog
none
Agent session: huge-blue
🤖 Generated with Claude Code