feat(ci): Build all branch β protected implementation - #313
Merged
Conversation
Deterministically rebuilds the wildcard `all` branch β develop + main + every open same-repo PR (stacks included) β with stack-aware ordering, -X theirs newest-wins conflict policy plus a matching structural-conflict fallback, per-PR skip-not-fail, a committed ALL_BRANCH.md manifest, tree-identical push dedup, and .github pinned to develop so the default GITHUB_TOKEN can force-push. Self-test wired into control-plane CI advisories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Textually-clean merges can still collide semantically (duplicate helper declarations across PRs), breaking the union build with no git conflict anywhere. After each input-changed rebuild the union build now runs (install β vite client β nitro server) with a deterministic lockfile repair, then up to two edit-files-only Claude rounds (resolver action pin, model waterfall, and security posture) fix the tree; a mechanical step reverts out-of-scope edits, scans for credential leaks, commits, and re-verifies. Doctor commits ride on the all branch and are cherry-pick-replayed onto the next rebuild, dropping silently once the source PRs heal. Unchanged inputs skip the check and every model round. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Local live-fire against the real 64-PR union showed collision layers surface one build-stage at a time (4 layers today), so a third unrolled round is added; deeper stacks converge across scheduled runs via replay. An exhausted doctor now pushes its failure marker even when the tree is otherwise identical, and the leading marker streak on the tip is a retry ledger: identical inputs get exactly one more attempt before waiting for input movement, so an unfixable union cannot burn model rounds hourly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
These examples are warning-only. They do not fail a required check or block this PR.
Sanitized tail '.github/actions/rebase-conflict-round/action.yml',
+ '.github/workflows/all-branch.yml',
'.github/workflows/rebase-pr-stacks.yml',
'.github/workflows/resolve-pr-conflicts.yml'
]
at assertAdminModelRouting (file:///home/runner/work/thingtime/thingtime/.github/scripts/workflow-control-plane-contract.mjs:358:10)
at assertControlPlaneContract (file:///home/runner/work/thingtime/thingtime/.github/scripts/workflow-control-plane-contract.mjs:744:3)
at file:///home/runner/work/thingtime/thingtime/.github/scripts/workflow-control-plane-contract.mjs:874:3
at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [
'.github/actions/rebase-conflict-round/action.yml',
'.github/workflows/all-branch.yml',
'.github/workflows/rebase-pr-stacks.yml',
'.github/workflows/resolve-pr-conflicts.yml'
],
expected: [
'.github/actions/rebase-conflict-round/action.yml',
'.github/workflows/rebase-pr-stacks.yml',
'.github/workflows/resolve-pr-conflicts.yml'
],
operator: 'deepStrictEqual',
diff: 'simple'
}
Node.js v22.23.2
Sanitized tail '.github/workflows/resolve-pr-conflicts.yml'
]
at assertAdminModelRouting (file:///home/runner/work/thingtime/thingtime/.github/scripts/resolve-pr-conflicts-routing-contract.mjs:291:10)
at assertWorkflowSource (file:///home/runner/work/thingtime/thingtime/.github/scripts/resolve-pr-conflicts-routing-contract.mjs:207:3)
at selfTest (file:///home/runner/work/thingtime/thingtime/.github/scripts/resolve-pr-conflicts-routing-contract.mjs:440:3)
at file:///home/runner/work/thingtime/thingtime/.github/scripts/resolve-pr-conflicts-routing-contract.mjs:445:3
at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [
'.github/actions/rebase-conflict-round/action.yml',
'.github/scripts/rebase-stack/refresh-promotion-graphify.sh',
'.github/workflows/all-branch.yml',
'.github/workflows/rebase-pr-stacks.yml',
'.github/workflows/resolve-pr-conflicts.yml'
],
expected: [
'.github/actions/rebase-conflict-round/action.yml',
'.github/scripts/rebase-stack/refresh-promotion-graphify.sh',
'.github/workflows/rebase-pr-stacks.yml',
'.github/workflows/resolve-pr-conflicts.yml'
],
operator: 'deepStrictEqual',
diff: 'simple'
}
Node.js v22.23.2
|
The lighter client+server check let a plugin-array hunk race (dropped previewFreshnessHtmlPlugin) survive to Vercel's verify:vercel-output stage. The check now runs remix's full build script β Vercel's exact chain β so nothing lighter can leak past the doctor again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Build all branch β protected implementation
Implementation half of the new Build all branch automation. It rebuilds the wildcard
allbranch βdevelop+main+ every open, same-repo PR (stacked branch β branch PRs included) β from scratch on every relevant event, merges everything newest-wins with automatic conflict resolution, and force-pushes only when the resulting tree actually changed. The companion thin listener for product branches is in the develop-side PR (linked below); merge this PR first β until it lands, triggered listener runs fail with "workflow not found".Files
.github/workflows/all-branch.ymlβ reusableworkflow_callimplementation (plusworkflow_dispatchon this ref). Fulldevelopcheckout with the defaultGITHUB_TOKEN, plus a depth-1github-actionscheckout for the controller script (same pattern asdevelop-pr-preview.yml). No PAT and no inherited secret is read..github/scripts/build-all-branch.mjsβ the builder (full design notes in its header):gh pr list, then skip fork PRs (routing unreviewed fork code into this repo's Vercel builds would bypass Vercel's fork-authorization step),no-all-labelled PRs, base-branch heads (e.g. the standing developβmain PR Promote develop to mainΒ #289), and duplicate heads. The rest merge in topological layers seeded bydevelop/main: parents before their stacked children, ascending PR number within a layer. Dangling stacks (base is neither develop/main nor an open PR head) are skipped β this same rule keeps the strippedgithub-actions-based PRs (Batch PR conflict workers and activate Lopu fixesΒ #234/[Promote] Sync main into develop (#221)Β #266) from tree-nuking the union with their thousands of deletions.git merge --no-ff -X theirs(a later PR wins contested hunks) plus a matching theirs-biased fallback for the structural conflicts strategy options cannot settle (modify/delete,-merge-attributed paths). The repo's graphify union merge driver is overridden to take-theirs since CI has no graphify binary. A PR whose merge cannot complete is skipped and reported in the manifest β never fatal..githubis reset todevelop's exact tree (workflow files never execute onall, and the pin keeps force-pushes within the defaultGITHUB_TOKEN's powers; if a pending workflow-file delta still gets refused, the builder re-pins to the previously pushedallstate and retries once). RootAGENTS.md/CLAUDE.mdare restored as symlinks toAI_ALL.md, andpath~refs_all-build_pr-Ncollision artifacts from file-vs-symlink conflicts are dropped.ALL_BRANCH.mdcommitted at the branch root: base tips, every merged PR with how it merged, every skip with its reason. Deliberately timestamp-free, so identical inputs produce identical trees and the push (and its Vercel rebuild) is skipped.--self-testruns the pure planning/rendering examples..github/workflows/control-plane-ci.ymlβ advisoryrun_contractline for the new self-test.Verified live
Three full local builds against the real 64-PR topology using exactly this code:
github-actions-based, the standing developβmain PR, 1 dangling stack on a merged parent).origin/allwas created from this code (e47bce9c), and an immediately following from-scratch rebuild produced a bit-identical tree and skipped the push β determinism and push-dedup both confirmed.node --check, self-test, and YAML parses all green.AI build doctor
Textually-clean merges can still collide semantically β two PRs each declaring the same helper merges without any git conflict, then the union fails to build (and
developwould break identically when the second PR lands; the resolver can never see it because there is no conflict to resolve). The doctor heals exactly this, on the union only β source PRs keep healing one-by-one through the existing resolver as they merge:pnpm install(with a deterministic lockfile-reset repair before any AI is spent), then the full Vercel-parity remixbuild(pre-dev, client + embed + verify, nitro-template sync, vercel-preset server build, output patch + verify) β anything lighter let a collision in the later stages reach the deployment. The failure log is captured for the model. Unchanged inputs skip everything.pr-conflict-auto-resolver-model-waterfallβ one dial governs both automations), and security posture (no shell/git,persist-credentials: falseeverywhere, tool-level path bans). After each round a mechanical step reverts out-of-scope edits, scans staged content for raw/base64 credentials, commits, and re-runs the build.allabove the manifest commit and are cherry-pick-replayed onto the next rebuild; a fixup that stops applying (the source PRs healed for real, as intended) is dropped silently. An exhausted doctor leaves a non-replayable marker commit: identical inputs get exactly one retry, then wait for any PR/base movement β an unfixable union can never burn model rounds hourly.GH_TOKENcredential helper (no token ever sits in.git/configwhile the model runs).Live-fired locally against the real 64-PR union (doctor rounds executed by Claude locally with the exact workflow prompts; the guard/commit/replay/push plumbing is the shipped code): five collision layers healed in sequence β duplicate
searchParams/formatCompactCount/formatDwelldeclarations (Feed.tsx, PostCard.tsx), averify:embedscript lost to a package.json hunk race, an embed-bundle verifier false-positive on bundled CSS-injector strings, duplicate server-side notification-prefs helpers plus a duplicate JSON key, and a droppedpreviewFreshnessHtmlPlugin()in a vite plugin-array hunk race (caught only by the Vercel-parity verify stage β which is why the check now runs the full chain) β ending in a fully green install + client + embed + server + output-verify build. The healed branch was pushed, and a following rebuild on drifted inputs replayed the fixups cleanly and stayed green with zero AI spend (origin/all@56cb7e8b). The five-layer day is also why rounds went from two to three (deeper stacks converge across scheduled runs via replay).Knobs
ALL_BRANCH_NAME(all),ALL_BASE_BRANCHES("develop main"),ALL_SKIP_LABEL(no-all),ALL_INCLUDE_FORKS=1,ALL_PUSH=0; outside Actions the script refuses to run withoutALL_BRANCH_FORCE=1since it hard-switches branches.Listener PR (merge second): #314
π€ Generated with Claude Code