Repository navigation
Conversation
Replace the six CI jobs (changes, policy, rust, node, npm-audit, gate) with one job, tier1, on one self-hosted server per run. .github/scripts/ci-run.sh starts every part at the same time through ci-parts.sh; the job summary shows the parts table. CI runs on pull requests (fast mode, slow tests left out), merge-queue groups, every 6 hours on main and on dispatch (full mode); pushes to main run nothing. A red scheduled run opens one issue; the report job gets a pinned GitHub CLI, which the CI images lack. rust-toolchain.toml pins Rust 1.94.1 and .config/nextest.toml fails a test at 90 s with no retries.
The previous commit copied files from an older base and reverted #30's changes to repo-policy.sh, its test and ENGINEERING.md. Restore them. The report job now installs gh through .github/scripts/install-tool.sh with the same pin as maestro-nightly, instead of its own download.
ice-zeus
left a comment
There was a problem hiding this comment.
Review of a1fdcf5 against #25.
Blocking
None.
The diff matches the design in #25: one tier1 job plus report, the four triggers with no push, concurrency, the draft guard, Scope/Tools/parts/CI time/upload steps, the parts table and skip rules in ci-run.sh, the nextest ci/full profiles (90 s kill, no retries, slow_ filter on PRs), rust-toolchain.toml with its Dockerfile check, and .gitignore. No CARGO_*/RUSTUP_TOOLCHAIN/npm_config_cache per-run env. Untrusted values (BASE_SHA, event name) reach run: only through env:. tests/ci-run.test.mjs covers each branch of ci-run.sh (full/fast, docs-only, Rust-only skip, package skip, dispatch audit, failed dependency chain, stale docs, missing variable). The repo-policy.sh change is safe: the exit status of a process substitution is ignored. Size (~445 production lines) is explained in the body and stays under 800.
Notes
ci.ymlheader and ENGINEERING.md say a draft's job "is namedtier1 (skipped on drafts)", but the PR body found that GitHub shows the raw expression for a skipped job. A short "(GitHub may show the raw expression)" would keep the docs accurate. The behavior itself is correct: notier1result on drafts.- On a docs-only PR the "CI time" step is skipped, so "Upload ci-timing" is skipped too, and the logs of
policy/docs/actionlintare not kept. This matches the issue ("once step 5 ran"), but the PR body's "Part logs are kept in the ci-timing artifact" holds only when a test part ran. vendorrewritesweb/runtime/vendor/whilepolicy(and other parts) may still read the working tree. It is harmless while the vendored output equals the committed files, and the 5 stable runs agree.
Existing
None found in the code this PR touches.
Closes #25
Replaces #31. That branch's commits carried AI-attribution trailers, which
maestro-policyrejects, and pushed history is never rewritten. So this branch holds the same change as #31's last head (ee24bef) as clean commits on top of current main, which now includes #30. The proof runs below ran on #31's heads; tier1 is unchanged since then.CI becomes one job,
tier1, on one self-hosted server per run, with every part running at the same time through.github/scripts/ci-run.sh(parts runnerci-parts.shfrom #22). It runs on pull requests (fast mode), merge-queue groups, every 6 hours on main and on dispatch (full mode); pushes to main run nothing. A red scheduled run opens one issue throughreport.rust-toolchain.tomlpins 1.94.1 with rustfmt and clippy;.config/nextest.tomlfails a test at 90 s and never retries. ENGINEERING.md "Continuous integration" describes it all, and docs/engineering.html is re-rendered.Size: about 445 changed production lines (ci.yml +121/−193, ci-run.sh 93, nextest.toml 27, rust-toolchain.toml 5, .gitignore 2, repo-policy.sh 2). It replaces one workflow; splitting it would leave main with two CI designs at once. Tests (tests/ci-run.test.mjs, 8 tests) and docs are extra.
Changes beyond the issue text, each found by a proof run:
reportinstalls the GitHub CLI 2.103.3 through.github/scripts/install-tool.sh, with the same pin as maestro-nightly (Maestro checks: maestro-policy, maestro-queue and a nightly cargo and npm audit #30). No CI image hasgh, soreport-red.shfailed withgh: command not found(run 42387373242). Adding gh to the ciod-std image would be the lasting fix, and that belongs to Maestro.--target-dir..config/nextest.tomlpins[store] dir = "target/nextest", so the reports are attarget/nextest/<profile>/junit.xml.|| trueinside a process substitution, where its exit status is ignored anyway, so the no-escape-hatch grep is empty.ci-timingartifact. A passing part's log is not printed, and the nextest "skipped" count is only in that log.Measurement
The 9 runs used head e157f78, warm, after a PR run of that head. The job time runs from job start to job end. Lifetime and sccache come from the ciod line. Cost is lifetime × the hourly price.
<size>= ci-8cpu. It is the smallest size whose three runs all have the parts step under 90 s (22–24 s) and the job under 4 minutes (33–34 s). Most of the wall time is the 90–115 s wait for a fresh server, and that wait does not shrink with a bigger size.sccache: warm PR runs show 100% (e.g. 42385595834:
ciod: c-8 · … · sccache 100% · 0m32s). The very first run with the new--target-dirfolders showed 32% (cold), and every run since shows 100%.Same coverage
The old CI is run 42371471477. The new CI is the final head ee24bef: PR run 42387982599 and the stability runs below.
RUSTUP_TOOLCHAIN= Dockerfilecargo fmt --checkcargo clippy --all-targets --locked -- -D warnings--target-dir target/clippyREDIS_TEST_URL)--ignore-scripts--prefer-offline(warm image npm cache)node scripts/vendor.mjsThe JS difference comes only from new tests. Run on a server, the 14 baseline files give exactly 152 passed. The 5 files added before this rebase give 80: ci-parts, ci-timing and report-red (#22), repo-policy (#23), and ci-run (this PR). #30, now on main, adds 10 more:
install-tool.test.mjs(9) and one repo-policy test. 152 + 80 + 10 = 242. The "CI time" summary table shows fewer, becauseci-timing.pykeys tests by name and some names repeat across files. node's own count in the part log is the real one.Every part can fail the run
Each proof branch was a PR head plus one breaking commit, dispatched with
gh workflow run ci.yml --ref proof/<case>and then deleted. The first 13 cases ran on aa5d180. kill -9 and report ran on ee24bef, which only adds the report job's gh step;tier1is the same on both heads.policyfailed ("tracked text contains an absolute home-directory path")¹docsfailed ("…is stale")¹actionlintfailedfmtfailedneedless_return)clippyfailed (exit 101)rust-testfailed (exit 100)node-testfailed;vendornot run (depends on it)vendorfailednpm-cifailed;node-testandvendornot run (depend on it)npm-auditfailedrust-test(killed at 90 s) andnode-test(timed out at 90 s) failed, and "CI time" failed on both ("took 90.006 s / 90.002 s: 90 s or more fails")kill -9policy: "no result (crashed or killed)"²reportopens one issuetier1red (failing JS test) andreportgreen: it opened #35, then #37, and each was closed as not planned with no comment¹
node-testalso failed in these two cases, each time in a test that deliberately re-checks the repository's own tree:repo-policy.test.mjs"passes on the repository's own tree" andrender-readme.test.mjs"D2 … committed HTML is current". Those are existing guards of the same rule. Weakening them to make the proof look cleaner would be wrong.² The first kill -9 attempt (42387359526) edited ci-run.sh unconditionally, so tests/ci-run.test.mjs (which runs that script) failed too. The second attempt kills only on the CI server.
Slow set
The throwaway branch added the Rust test
slow_proofandtests/slow/proof.test.mjs, in PR #33 (closed, branch deleted).Docs-only
Throwaway PR #34 (AGENTS.md only; closed, branch deleted), run 42387409969:
tier1green, withpolicy,docsandactionlintpassed. Every other part reads "skipped: only documentation changed (.github/scripts/changed-scope.sh)".Drafts
This PR opened as a draft. Run 42384711204 skipped the job, so no
tier1result was reported, and marking the PR ready rantier1. GitHub does not evaluate a skipped job'sname:expression, so the skipped job shows the raw expression text rather than "tier1 (skipped on drafts)". Either way it is never atier1check.No escape hatches
(empty, exit 1)
Stable
5 dispatch runs of the final head a1fdcf5 ran at the same time, with the same result and counts:
No test was flaky. #31's head ee24bef gave the same result across 5 runs earlier (42388181296, 42388183881, 42388186222, 42388188773, 42388191300: 31/11 and 232).
Merge
Pending: after review and QA, and once #24 has merged, I send the orchestrator the
maestro-init settingsdry run and apply it only after they confirm. Then this PR merges through the queue, and the time from enqueue to merge is recorded here.