Repository navigation
ci: PR tier skips slow rows; main and release keep every row (CI-SLIM D1, CR-195) - #449
Merged
Merged
Conversation
…egistration floors, and defers the slow verifier self-test classes (D1, CR-195)
On pull_request the Release matrix leg and the kernel-only job now pass
--exclude-label slow, like Debug and sanitizers already did. Push to main,
workflow_dispatch and release keep every row and every class, so coverage is
unchanged and only its timing moves.
- ci_preflight.py: the two policy checks that forbade this (only PR Debug
matrix jobs may exclude slow rows; kernel-only must keep its full
population) become one exact rule with must-fail forms; docs/ci.md says the
same. This REVERSES the previous policy. The must-fail test for the
full-population rule edits the `with:` block of the reusable-workflow job
corpus-parity-subset (it has no `steps:`).
- ci_verify.py: EXCLUDED_REGISTERED_MIN gains release 895 and kernel 337
(KERNEL_MIN_TESTS), derived on one registered-count basis from the PR-world
job data, to be confirmed by a lab ctest -N. 337 keeps today's 34-row slack
and stays below the 371 registered in all 16 sampled pull-request and push
runs; 'release' stays 895.
- test_ci_verify.py classes are partitioned into FAST (13) and SLOW (9) in
scripts/ci_verifier_classes.py; the preflight verifier-tests stage runs the
FAST classes on every pull request and the SLOW ones only when
scripts/ci_verifier_paths.py finds a path the verifier reads; any unknown
path or class runs. Non-PR events run everything. HistoricalProviderPins is
skipped without the private overlay and is FAST by decision.
- preflight has `needs: changes` and `if: ${{ !cancelled() }}` (pinned in
ci_preflight.py); the docs say so.
- doc anchors: PRIVATE_FIXTURE_PROFILE_FLOORS moved from ci_verify.py:789 to
ci_verify.py:802 (CONTRIBUTING.md, docs/pages/contributing-llm.md,
docs/pages/native-engine.md, docs/ci.md).
- docs/ci.md gains L6's rule for the deferred rows: a red on main in an
excluded row or class blocks the next release cut and is fixed or reverted
within the hour. AGENTS.md lists Release and kernel-only among the jobs that
exclude the slow rows.
- New tests assert that push, dispatch and release carry no exclusion and no
class filter.
Doc sentences this change removes or rewrites ON PURPOSE (doc-reverts check):
- "The PR registration floors after lane FIX-E1E2 are 773 for Debug and
sanitizers and 782 for native ..." (docs/ci.md), introduced by b2a578c
("docs: make every README and doc page true before the release
(DOCS-ENGINE) (#300)"). Those numbers were already stale against
scripts/ci_verify.py (876/867/963) and are now the keys of
EXCLUDED_REGISTERED_MIN.
- "`preflight` waits for nothing." (docs/ci.md), introduced by 133f571 ("1.0
prep: CI flake fix, docs-only CI skip, benchmark refresh (supersedes #303,
#304, #307)"): preflight now waits for `changes`.
- "Preflight starts at once, and the proof jobs start alongside it" and
"`changes` and `preflight` wait for nothing and run on every event"
(docs/ci.md), same reason as 133f571 above.
- "than documentation runs preflight, both Release jobs, kernel-only and the
parity subset with their full sets" and "both Debug jobs, sanitizers and
native-live run the registered set excluding the 29 CTest rows labelled
`slow`" (docs/ci.md): now stated for Release and kernel-only too.
- "A Release leg runs its full population on every event" and "Every event
runs the whole kernel set" (the `build` and `kernel-only` rows of the
timeout table in docs/ci.md): a pull request now excludes the slow rows.
- "The two new keys are derived, not measured: each is the registered count in
the sampled public pull-request runs" (docs/ci.md): the kernel key differs
from the Release one.
Note: scripts/release.yml runs no CTest and no ci_verify, so "release keeps
every row" means only that no --exclude-label is present in any release path;
the release workflow itself is unchanged.
The registration floors for Release and kernel-only (895 and 337) are derived,
not measured; a lab run of the release and kernel steps confirms or corrects
them.
This is the squash of 11cd290, 1b01244 and ef8fc5d (doc-reverts judges
every commit with its own message; the three were folded so that one message
names every sentence).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
| import os | ||
| from pathlib import Path | ||
| import tempfile | ||
| import unittest |
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.
Supersedes #448 (same tree
cb1fc3d98f75, folded into ONE commit14c3365f0on main18d8f3c96). Why a new PR:scripts/check_doc_reverts.pyjudges every commit against its own parent with its own message, so the two deleted docs/ci.md sentences had to be named in the commit that deletes them; a commit on top cannot fix that, and the PR branch is never force-pushed. The commit body names both deletions (133f571, b2a578c).CI-SLIM D1: the PR tier skips slow rows; main and release keep every row (CR-195)
Owner order 10-20: CI simplification first. This is the D1 policy change (ruled by CORE-TOP; L6 reviews the coverage/floors table before merge).
What changes, on
pull_requestONLY:--exclude-label slow, with registration floors (release 895, kernel 309), so a dropped registration still fails.test_ci_verifyis split into 13 FAST / 9 SLOW classes. FAST always runs on PRs. SLOW runs on a PR only when the path helper finds a path the verifier reads; an unknown path runs SLOW.pull_request.Policy reversal (named on purpose): this reverses the documented "full Release/kernel set on every PR" policy at
scripts/ci_preflight.py:618and:628anddocs/ci.md:715,:1049and:1051. The docs are updated in this PR.Measured before (engine PR CI, successful attempt-1 runs, queue included): median 90.8 min, p90 124.8, max 140.3 (n=11). The after-number is measured on the first real PRs after merge and reported; nothing here is an estimate of it.
Coverage and floors table (all 29 slow rows and all 22 verifier classes: floor, where each still runs, delay to MAIN; plus the NOT-covered list and 8 re-check commands):
Engine CI decision D1: coverage and floors table
Status: draft by lane B (authoring seat), 2026-10-20, for L6 review before merge (L6 condition 1). Lane A writes the code, lane B writes this table and
docs/ci.md; both follow one contract. Nothing here has run on a real pull request. PR wall time after this change is to be measured on real PR runs (ten runs, typical and tail, cold and warm compiler cache; L6 condition 2). The 15-minute target is not claimed as met.What changes, in one place
On
pull_requestonly, and nothing on push,workflow_dispatchor release:slow(thePINEFORGE_PR_SLOW_TESTSpopulation,tests/CMakeLists.txt:4591-4621, label set at:7049-7056): the Release legs of thebuildmatrix andkernel-only. Debug, sanitizers and the native-live native cell already excluded it (scripts/ci_preflight.py:615-623pins those three).scripts/test_ci_verify.py(22 TestCase classes), is split into FAST classes, which the preflight stageverifier-testsruns on every PR, and SLOW classes, which it runs on a PR only when the path helperscripts/ci_verifier_paths.pysays the PR touches a path the verifier reads. An unknown or unreadable diff runs the SLOW classes. A class that is in neither list runs.test_ci_verifystays inPINEFORGE_PR_SLOW_TESTS: excluded by the label on the PR legs, run in full on push to main.EXCLUDED_REGISTERED_MINgetsreleaseandkernelkeys so a PR leg that excludes the label also bounds how many rows may leave the registry (section c).Coverage is unchanged and only its timing moves: every excluded row and class still runs on the push-to-main run for the merge commit. The delay is never zero (section a, note D-push).
Release tier:
.github/workflows/release.ymlruns no CTest and noci_verify.py(/usr/bin/grep -c 'ctest\|ci_verify' .github/workflows/release.ymlprints 0). Before a tag, today's full-row coverage is the maintainers' lab verification (docs/ci.md: the maintainers run the fullci_verify.pyprofiles before the merge gate; documented, not verified here). A GitHubverify-releasejob is CR-195 plan step 4 and is not part of this change. This change touches no release path, so release coverage is unchanged (note R below).How to read the numbers: every number carries a command from section (e) or a file:line. derived means computed in this table from the sampled data, not measured on the tree this change lands on. not verified means no command was run. Data directory
P=/Users/haoliangwen/.local/state/pineforge/runner/ORG-CX/proof/CI-SLIM/:PR-TSV=P/tests-pineforge-engine-ci-pull_request.tsv: ctest rows per CI job, ten PR runs (five on the pool labelpf-ci-x64-16, five on hostedubuntu-24.04), Ubuntu legs only (no macOS rows).PUSH-TSV=P/tests-pineforge-engine-ci-push.tsv: six push-to-main runs (five on the olderpf-linux-x64-16runners, 10-17 and 10-18, one hosted run39126947405).JOBS-PR,JOBS-PUSH=P/pineforge-engine-ci-pull_request-jobs.tsv,P/pineforge-engine-ci-push-jobs.tsv: job walls.LOG=P/serial-test_ci_verify.log: 214 tests in 2730.345 s, 9 failures, 8 errors, 8 skips (LOG:610,:612). It is a timing log, not a passing proof.(a) The 29
PINEFORGE_PR_SLOW_TESTSrowsRecount: the
set(PINEFORGE_PR_SLOW_TESTS ...)block lists 29 names, all distinct (command a1).scripts/ci_preflight.py:677-686pins the count at 29 and its sha256.Legs on
pull_request: R =buildmatrix Release legs (the Ubuntu leg is the one in PR-TSV; the macOS Release leg is not in the TSV), K =kernel-only, D =buildmatrix Debug legs, S =sanitizers, N =native-live (native). † marks a leg this change newly excludes; a leg without † already excluded the label before this change. A * on a floor marks a derived floor (section c).Population of each leg on the sampled base tree (commands a2 and a5; all numbers are ran + skipped rows, every skip a pinned private-fixture placeholder):
slowand registered there-LE slowkernel-only)R and K are the minimum over the ten sampled PR runs (957 and 371; the other runs carry up to 977 and 385 because their PR heads add rows) and equal the registries of the full push run
39126947405. D and S PR jobs already exclude, so their registered count is the PR selection plus the 29 rows the same push run registers in them. Five slow rows are not registered in the kernel profile at all (test_adapter_live_state_equivalence,test_adapter_live_state_scaling,test_adapter_reissue_binding,test_chart_day_memo,test_l10recon1_merge_fill_liveness: absent from every kernel job of both TSVs), so the kernel leg excludes 24 rows and the kernel cells below read "not in TSV" for those five.Three slow rows are pinned private-fixture placeholders:
test_l10recon1_merge_fill_liveness,test_local_time_fields,test_native_cpp_abiskip in every public job of both TSVs (result***Skipped, at most 0.15 s). They run only where the private overlay is present (the maintainers' lab). Excluding them removes no public coverage.D-push (the delay column): the row next runs, in full, on the push-to-main run for the merge commit, in every leg it is registered in. Measured job walls (command a4): hosted run
39126947405: Release 1876 s, kernel-only 3022 s, Debug 3416 s, sanitizers 2707 s, native-live native 3132 s, preflight 1453 s, whole run 3433 s. Olderpf-linux-x64-16runners, median of five runs: Release 1815 s, kernel-only 1812 s, Debug 1770 s, sanitizers 2050 s, native 1838 s. No push run on today'spf-ci-*pool is in the sample, so the push wall on the current pool is not measured. The delay is not zero and not bounded by these walls: a regression in an excluded row is not seen by the PR, it is seen by that push run, and earlier only if the maintainers' lab verification covers it (which lab step runs which row is not verified here; the lab runstest_ci_verifyin its full jobs,tests/CMakeLists.txt:7088-7090).Note R (release): see "What changes". For every row below, release coverage is unchanged by this change, and today's pre-tag full-row coverage is the maintainers' lab verification, not
release.yml.PR Release job and PR kernel job seconds are the ctest row seconds of PR-TSV (command a3): the median of five runs, pool / hosted.
skipmeans the row skipped (a placeholder). "not in TSV" means the row is not in that job's rows. Floors in force are the PR-leg registered floors of section (c); D, S and N are the existing keys.test_adapter_live_state_equivalencetest_adapter_live_state_scalingtest_adapter_reissue_bindingtest_aggregate_cpp_versionstest_broker_state_hash_coverage_mutationstest_chart_day_memotest_ci_verifytest_l10recon1_merge_fill_livenesstest_local_time_fieldstest_magnifier_endpoints4test_native_bar_open_hooktest_native_command_aftertest_native_cpp_abitest_native_cpp_versionstest_native_definition_indextest_native_direct_mutationtest_native_event_retentiontest_native_group_absorptiontest_native_group_keep_handletest_native_handle_stable_replacetest_native_host_readstest_native_lookup_memostest_native_match_band_prechecktest_native_match_row_reusetest_native_precommit_hooktest_native_quiet_pointtest_native_runtime_ambienttest_native_script_bucket_completionstest_native_settlement_carryReading the table: in the sampled PR runs
test_ci_verifyis 3544.58 s (Release) and 3584.52 s (kernel-only) on the pool, 2003.57 s and 2261.99 s hosted; no other excluded row has a median above 230 s in either job. The N column is the existing exclusion through theexclude_slowinput (.github/workflows/ci.yml:403); native-live ctest rows are not in either TSV, so which of the 29 rows the native profile registers is not verified. The macOS Release and macOS Debug legs run on push (JOBS-PUSH) but their rows are not in either TSV, so the macOS registries are not verified.(b) The 22 TestCase classes of
scripts/test_ci_verify.pyRule: a class is FAST only if its measured total in
LOGis under 5 s and its passing runtime is measured; otherwise SLOW; a class in neither list runs (defaults to RUNNING). The 22 classes are the^class .*TestCaselines of the file (:643to:3936, command b1): 9 SLOW + 13 FAST, none left out. Seconds are the per-class sums of the durationsLOGprints (LOG:431-606, command b2; 176 of 214 tests print one, durations under 0.001 s are hidden,LOG:608). Every per-class sum is recomputed with awk (command b2) and equals the reference design's per-class number; the FAST and SLOW totals (FAST 2.809 s, SLOW 2727.510 s, visible total 2730.319 s) differ from the reference design's 2.818 s and 2727.501 s only becauseReceiptReusemoved from FAST to SLOW.In the PR tier: FAST classes run on every PR in the preflight stage
verifier-tests(preflight runs on every event, docs-only changes included). SLOW classes run on a PR only whenscripts/ci_verifier_paths.py(the single source of truth for the path list) reports a touched path the verifier reads, or cannot read the diff. The paths are in the helper, not copied here; they are the product inputs the verifier reads (include/pineforge,src/c_abi.cpp,tests/,runner/, the workflows, the CI scripts and a few documents).Still runs: on push to main,
workflow_dispatchand release,verifier-testsruns all 22 classes, and the serial ctest rowtest_ci_verifyruns them again in the full Release, kernel-only, Debug and sanitizers jobs of the push run (PUSH-TSVhas the row,Passed, in all four jobs of all six runs, command a3; native-live native: not in TSV). Delay: the SLOW classes, when no touched path triggers them, are next seen on the push-to-main run for the merge commit (D-push; preflight there took 1453 s hosted, whole job, not the stage alone; the stage alone is not verified from these inputs); never zero. Release: note R.DriverOrderingAndAggregationverifier-tests; push: ctest rowPrivateFixtureFloorsRowFloorDriverExamplesAssertLiveStaleBinariesLiveProfilesWithFixturesAbsentPrExclusionWithFixturesAbsentReceiptRecipeReceiptReuseFileNotFoundError: '/usr/bin/c++'(LOG:165-346), so its passing runtime is unmeasuredRowFloorConfigCTestDeadlinesDiagnosticsCollectionFloorDocsPointAtTheTableGitObjectDiscoveryRunnerSanitizerCoverageProfileOptionsCAbiRuntimeInventoryConfigValidationRuntimeBudgetLanesCopyCacheIdentityLOG(every test under 0.001 s); class total not verifiedHistoricalProviderPinTableLOG; class total not verifiedHistoricalProviderPinsLOG: the class is skipped without the private overlay (scripts/test_ci_verify.py:1092-1099), so its runtime in this public run is not verified; it runs in the maintainers' overlay worldSums (command b2): SLOW classes (9) 2727.510 s including
ReceiptReuse's 0.009 s to error; FAST classes (13) 2.809 s over the ten classes that print a duration, plus three classes with none. Neither is a fresh passing runtime: the log carries nine failures, eight errors and eight skips. The three FAST classes without a printed duration (rows 20-22) are classified FAST by decision: the log prints no duration for them, so their passing runtime is not measured, and the stated rule alone would not classify them FAST.(c) Floors
Where the numbers live and what the code reads (
scripts/ci_verify.py):EXCLUDED_REGISTERED_MIN['debug']:646; registered count at873b25daplus the fifteen selected-window rows, counted before-LE(:704-707)EXCLUDED_REGISTERED_MIN['sanitizers']:646, same derivationEXCLUDED_REGISTERED_MIN['native']:646, same derivationEXCLUDED_REGISTERED_MIN['release']ctest -NEXCLUDED_REGISTERED_MIN['kernel']ctest -NRELEASE_MIN_TESTS,KERNEL_MIN_TESTS,:787-788)024fdf92c(:767-774)PRIVATE_FIXTURE_PROFILE_FLOORS,:789-792;overlay_aware_floor,:831-837)Derivation of the two new numbers (commands a2 and a5; derived, not measured on the tree this change lands on):
slowlabel removes in each profile (command a5, the full push run39126947405): Debug 29, sanitizers 29, Release 29, kernel 24.The basis matters: the existing keys count rows registered before
-LE, so the Debug PR job's 909 (rows after-LE) must not be compared with 876.What a PR leg enforces once it excludes the label (
scripts/ci_verify.py:2255-2311, the exclusion branch, which returns before the run floor):max(EXCLUDED_REGISTERED_MIN[profile], the profile's min_tests)(:2256-2257,:2263). On the public PR legsmin_testsis the overlay-absent run floor (603 and 306), so the binding number is 895 (Release) and 309 (kernel), 876 (Debug), 867 (sanitizers), 963 (native). With the overlay presentmin_testsis 910 and 337, so the two new keys do not bind there.:2267): the exclusion must remove something and must leave something.:2271-2283); overlay present: ran == selected, any skip fails (:2299).ctest-exclusion.logandci-summary.json(docs/ci.md).Slack on the public PR legs, in rows that could vanish from the registry unnoticed (sampled registries 957 and 371):
On the sampled data the kernel key narrows the slack by only 3 rows. Both derived keys are lower bounds chosen by the offset rule, not measured floors; the lab job's
ctest -Non the head this change lands on is the confirmation and may move them.The run floors (910 / 337 with the overlay present, 603 / 306 absent) are not read on an excluded run; they are still enforced on push,
workflow_dispatchand any other full run (ctest-floor,scripts/ci_verify.py:2312onward). On those runs the slack in the sample is 650 − 603 = 47 rows (Release) and 340 − 306 = 34 rows (kernel-only), command a2.(d) NOT covered by this change
pull_requesttrigger: decision D2.corpus-parity.ymland the parity subset are unchanged (the subset keeps its full 54-probe population,docs/ci.md).release.yml: unchanged. It runs no CTest (note R);verify-releaseis CR-195 step 4.test_ci_verifymode for the full tiers, compiler-cache changes, docs-build path filters, runner routing, and the measurement of PR wall time (to be measured on real PR runs).(e) How to check this table
Run these from the engine worktree root;
Pis the data directory named above. Every command is read-only.Confirmation of the derived floors is the lab job's
ctest -N(registered rows per profile) on the head this change lands on; until then both numbers stay derived.Expected PR wall and the next step (L6 condition 3)
Expected PR wall for an ordinary
src/PR, and the next step to 15 minutes (L6 condition 3)ESTIMATES. Nothing here has been measured on a changed workflow; the PR's own CI run is the first measurement and the numbers below are replaced by it.
What this PR changes for an ordinary
src/orinclude/PR. The Release leg andkernel-onlyno longer run the ctest rowtest_ci_verify(3530 s of a 3608 s Release ctest run, measured on the pool PR job of #412; 2004 s and 2262 s on hosted runs) nor the other 28slowrows. Their critical path falls to build + the fast ctest set. The preflightverifier-testsstage still runs the FAST verifier classes (about 2.8 s in total) on every PR.What it does NOT change.
scripts/ci_verifier_paths.pyreturns "run SLOW" for any path undersrc/,include/,tests/,scripts/,.github/,runner/,docs/, ... and for any unknown path, so the 9 SLOW verifier classes (2727.5 s serial; measured preflightverifier-testsstage 1480-2062 s on earlier hosted and pool PR runs, 4 shards on a 4-vCPU hosted runner) still run inside the preflight job of almost every engine PR. For an ordinary code PR the long pole is therefore still the PREFLIGHT job at roughly 25 minutes (ESTIMATE from those measured stages), not 15. Only a PR that touches nothing buttools/,tutorial/,benchmarks/,.claude/or top-level prose defers them. This PR alone does not make ordinary code PRs 15 minutes; it removes the Release/kernel ctest row.Dated next step (live by 10-21 18:00, earlier if the lab is green). Lane D2, branch
ci/d2-verifier-shardson top of this PR: move the SLOW classes out of preflight into their own jobverifier-slow, a matrix of 8 hosted time-balanced slices (per-test durations from the serial log committed as a table, longest-processing-time assignment, unknown tests round-robin so they always run), and make the aggregate wait for it. ESTIMATE for the slice: the ideal balanced slice is max(200 s slowest single test, 2727/8 = 341 s) plus checkout and setup, so about 6-9 minutes; with preflight reduced to FAST classes, the long pole of an ordinary PR becomes the build legs. Narrowing the SLOW triggers (a path list that is not "everything") is the second lever and is NOT proposed here: it trades detection for time and needs a decision.Measured PR wall times (successful attempt-1 runs, queue included, last 100 completed PR runs; optimistic): engine CI on code PRs, 11 heads: median 90.8 min, p90 124.8, max 140.3 (
proof/CI-SLIM/pr-wall/README.md). Release and data PR tails were attributed to pool queueing, not to run time (TAIL.md). These numbers are reported again after this PR and after D2, from real PR runs, typical and tail.Rule for the deferred rows (L6 condition 4)
A red on
mainin an excluded row or class (a Release or kernel-onlyslowrow, or a SLOW verifier class) blocks the next release cut and is fixed or reverted within the hour. It is written intodocs/ci.md("PR tier versus MAIN/RELEASE").Floors
Release 895 and kernel-only 337 are DERIVED from the sampled public PR/push runs, not measured: the lab job's
ctest -Non the head confirms them or the code takes the measured numbers; a macOS Release leg that registers fewer rows than 895 gets a floor per operating system, never a lower one (the PR's own run is that proof; read-based argument: Linux registers 19example_*rows more than Debug and macOS Debug already passes 876, so macOS Release registers at least 895).🤖 Generated with Claude Code