fix(ci): stop mise run ci racing bootstrap, and itself, on a fresh tree - #38
Merged
Conversation
…tree Two ways the pre-flight gate was unreliable. Both come from tasks in the same run reaching the same files at the same time. `depends` runs in parallel, so listing `bootstrap` first in `ci` did not make it finish first. `openapi:check` and `build:mcp` need the pnpm workspace installed and did not say so, so on a fresh clone or worktree they started before `bootstrap:js` and failed naming a missing npm package rather than a missing bootstrap -- with the whole gate aborting seconds in, before lint, typecheck, test:js or e2e had run. The repo already had the mechanism for this: `wait_for`, which `fmt:js`, `typecheck`, `test:js` and `e2e` all use. The four tasks that need the workspace and lacked it now declare it. `openapi` is the counterintuitive one and carries a comment: the script imports nothing from node_modules itself, it reaches them by loading the Hono router. `lint:loc`, `test:scripts`, `test:action` and `version:check` also shell out to node and are deliberately left alone -- they import only builtins and relative paths, and `version:check` passed in the failing run that proves it. Separately, `lint:docs` and `lint:svelte` each deliberately re-run a check that `typecheck` also reaches through `pnpm -r`, and their comments say why. That overlap has to be sequenced rather than merely tolerated. `astro check` syncs its content layer through a temporary file renamed over `packages/docs/node_modules/.astro/data-store.json`, and two of them racing that rename leaves the loser reporting an ENOENT from `MutableDataStore` for a path the other process already moved -- which reads as a broken docs build rather than as two copies of one check. Observed intermittently, and now ordered after `typecheck`. `lint:svelte` is ordered for the same structural reason: its first step is `svelte-kit sync`, generating into `.svelte-kit/`, and two copies of one generator writing one directory is a race whether or not it has surfaced. Verified by wiping every node_modules and running the gate from that state. Co-Authored-By: Claude Opus 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.
Two ways
mise run ciwas unreliable. Both are tasks in one run reaching the same files at the same time, so they are fixed the same way — withwait_for, which this repo already uses for exactly this.1. It raced
bootstrapon a fresh treedependsruns in parallel, so listingbootstrapfirst in[tasks.ci]did not make it finish first.openapi:checkandbuild:mcpneed the pnpm workspace installed and did not declare it, so on a fresh clone or worktree they started beforebootstrap:jsand failed with:The gate then aborted the whole DAG seconds in —
bootstrapnever even appeared in the output, and lint, typecheck,test:jsande2enever ran. So the firstmise run ciin any new worktree failed on something unrelated to the change under test, and named a missing npm package rather than a missing bootstrap step.That also quietly undercut the guarantee AGENTS.md makes about this command being a superset of
ci.yml: the real workflow works because it installs dependencies as an explicit earlier step.Four tasks now declare what they need:
openapi,openapi:check,test:mcp,build:mcp.openapicarries a comment because it is the counterintuitive one — the script imports nothing fromnode_modulesitself, it reaches them by loading the Hono router.Deliberately left alone:
lint:loc,test:scripts,test:actionandversion:checkalso shell out tonode, but their scripts import only Node builtins and relative paths, so they do not need the workspace.version:checkpassing during the failing run corroborates that.2. It raced itself
lint:docsandlint:svelteeach re-run a check thattypecheckalso reaches throughpnpm -r. That overlap is intentional and both tasks' comments explain why — this PR keeps it. But it has to be sequenced, not merely tolerated, because both halves write generated state:astro checksyncs its content layer through a temporary file renamed overpackages/docs/node_modules/.astro/data-store.json. Two of them racing that rename leaves the loser reporting anENOENTfromMutableDataStorefor a.tmppath the other process already moved — which reads as a broken docs build rather than as two copies of one check. Observed intermittently, and it passes when run on its own, which is what makes it look like a docs problem.lint:svelteis ordered for the same structural reason: its first step issvelte-kit sync, which generates into.svelte-kit/. Two copies of one generator writing one directory is a race whether or not it has surfaced. To be explicit: this one has not been observed failing — it is fixed because it is the same shape, not because it broke.Both now
wait_for = ["bootstrap:js", "typecheck"].Verification
Wiped every
node_modulesin the tree — root, all fivepackages/*,e2e,action— and ran the gate from that state, which is the exact condition that used to fail within seconds.openapi:checknow waits forbootstrap:jsand passes.[typecheck] Finished in 31.14sat line 2341, then[lint:svelte] $ …and[lint:docs] $ …at 2342-2343. No overlappingastro check.test:rust663,miri127,test:js1152,e2e101, plus the whole lint/typecheck/audit/deny set.Note on
wait_forsemantics: it orders tasks within a run that schedules both, so it does not turnmise run openapi:checkon a never-bootstrapped tree into a self-installing command —mise run bootstrapis still the documented setup step. That is the same contract every existingwait_forin this file has, and it keeps each leaf individually runnable as AGENTS.md promises.🤖 Generated with Claude Code