Repository navigation
Lanes pick the test classes that name a changed symbol, and catalog-wide tests always run (#5776) - #5777
Merged
Conversation
…ol; map-select adds them (#5776)
…tage=Census (#5776) Rule R4: a store-free class that reflects the [McpServerTool] catalog, calls GetTypes()/GetExportedTypes() on a product assembly, or reads every collector (CollectorCatalog.All) carries [Trait("Stage", "Census")], which the guard job runs beside Stage=Guard. No changed symbol names such a class, so a lane's symbol selection cannot find it. The nightly test map put the R4 set at 545 s of Darling.Tests (over the 120 s line), so it is a trait of its own. 90 Darling.Tests and 35 Lite.Tests classes are tagged; 25 Darling.Tests classes that need a store are on a pinned exemption list. Both GuardStageCensusTests gain an R4 real-tree case, a probe-text case and the R4 tagged/store census case.
…into fix/5776-symbol-test-selection-B
The guard job runs Stage=Census beside Stage=Guard, so the replay's guard_run check and map-select's guard-stage reason take it too; the no-store pass's guard scope (Stage=Guard only) is unchanged. The lane-orders run line gains -trait Stage=Census.
…hards (#5776) Lane C's correction of rule R4. The Stage=Census trait is gone: the 87 Darling.Tests and 35 Lite.Tests classes Lane B tagged with it now carry Stage=Guard, and build.yml, ci-select.py, GuardStageWorkflowTests and the lane-orders line are back to Stage=Guard alone. Three classes rule R4 flags took 501 s of the R4 set's 545 s in the nightly test map of 2026-10-13 (WebDataStartNoteTests 257 s, ConsumedTimestampFrameDisciplineTests 188 s, QueryStatsWriterCensusPins 56 s). They carry no Stage trait and sit on GuardStageScanner's pinned SlowCatalogClasses list (50 s or more, rule R4 alone), so the Guard-tagged R4 set costs about 44 s in Darling.Tests. The census pins the list at 3 and calls an entry stale when the class is tagged or R1/R2 also applies.
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.
Claude posting for Erik Darling.
Fixes #5776
Lane A
ci-select.py local-classeslists the test classes whose source names a symbol the change declares or edits.map-selecttakes the same answer (tight tokens only) as a new reason, "names a changed symbol". Commit 7018936.What changes
.github/scripts/ci-select.pylocal-classes --base origin/dev [--map FILE] [--wide] [--cap 25] [--root DIR](A1). Standard library only.--baseand HEAD to the working tree, so it works before a commit. It covers .cs, .sql, .js and .xaml, and untracked files count as all-added lines (A2).v_*anywhere, and CREATE/ALTER TABLE/VIEW/FUNCTION/PROCEDURE names in .sql.--wideadds every identifier of 6 or more characters on a changed line (A4).--captest files is dropped and printed asNOISY <token> <count>.suite Class tokens), most specific first: a changed test file's own classes, then by the fewest test files naming the class's rarest token;RUN <suite>: -class "*.X" ...lines, 40 classes a line. Classes whose file quotes"DARLING_TEST_PG"are markedneeds rigand get their own RUN line.<n> classes: CI will run FULL; run the first 300 belowand lists the first 300 in order.--map, a separateMAPblock gives the map's coverage answer for the changed files, as a count per suite and per reason.map_select()takes a new keywordnamed({suite: {Class}}). Each named class getssetdefault(cls, "names a changed symbol"), after every other reason, so a class keeps its earlier reason and the selection only grows (A7). Themap-selectCLI computesnamedonly when--baseis given; CI passes--base "${BASE_SHA}". If git fails,namedis None and the selection is what it was before.map_replaypasses nonamed, so the replay is unchanged.why.Lite.Tests/CiSelectLocalClassesGuardTests.cs(new, Stage=Guard, Reads=Darling; A8): 5 cases on a planted, uncommitted change in a throwaway git repository:needs rig;Server) selects nothing;--cap 1drops a token named in 2 files and printsNOISY FrobnicateWidget 2;map-select --baseaddsnames a changed symbolfor two classes, and without--baseit does not..claude/skills/lane-orders/SKILL.md(Build and test, A9): runpython .github/scripts/ci-select.py local-classes --base origin/dev, then its-classlines plus*Census*,*Ratchet*and-trait Stage=Guard.Measured (A10)
Each pull request's diff from its merge base with dev to its head, against today's test tree (3,000 test files, the same basis as the plan's prototype). The class counts include the classes of the pull request's own changed test files.
_fleetGateStats?.RecordSlot(and is found by--wide.Test plan
FrobnicateWidgetRenamed. ChangedMethod_SelectsTheClassThatNamesIt fails (1 of 5). Restored.pass. MapSelect_WithTheBase_AddsTheClassThatNamesAChangedSymbol fails (1 of 5). Restored.*CiSelect*, CrossAppGuardCiGateTests, DarlingReadsTraitGuardTests, TestMapShadowGuardTests, WatermarkPolicyTests (every Lite class that names ci-select),*Census*,*Ratchet*: 170 total, 0 failed.-trait Stage=Guard: 507 total, 0 failed.python .github/scripts/ci-select.py --replay: 731 rows, 2035 failing classes checked, 0 misses.local-classes --mapon a synthetic map: the MAP block printslite: 1 of 1 classes (names a changed symbol 1).--baseagainst a real nightly map in CI. Its first pull request run shows how many classes the new reason adds.For the coordinator
Lane B
Rule R4 in
GuardStageScanner: catalog-wide classes always run. Pushed ascde71dcae+1ff86809b, merged over Lane A's701893612. Lane C (below) replaced itsStage=Censustrait with Stage=Guard; this section describes the result after Lane C.What changes
B1. Rule R4 (
Darling/Darling.Tests/GuardStageScanner.cs, linked into Lite.Tests). A test class "reaches a whole catalog" when its code (with comments and strings blanked) contains any of these:McpServerToolTypeAttributeorWithToolsFromAssembly(the MCP tool catalog);GetTypes()/GetExportedTypes()on an assembly that is not a test class's own (typeof(XTests).AssemblyandGetExecutingAssembly()are excluded);CollectorCatalog.All.Such a class carries
[Trait("Stage", "Guard")], or is exempt (R3, it needs a store), or is on the scanner's slow list (see Lane C). R1, R2 and R3 are unchanged.B2. Trait: Stage=Guard (after Lane C). The R4 set sums to 545.1 s in Darling.Tests in the nightly test map. Three slow classes account for 501 s of that, and they stay off the guard stage. The rest sum to about 44 s in Darling.Tests and 38 s in Lite.Tests, so the existing trait is enough. build.yml,
ci-select.pyand the lane-orders line need no change for it.B3. Tags, applied by script: 87 Darling.Tests classes and 35 Lite.Tests classes carry Stage=Guard. Exemptions: 25 Darling.Tests R4 classes need a store (R3: live-postgres collection, a
DARLING_TEST_variable, or a store helper). They are in a news_exemptCatalogwith a pinned ceiling of 25.DarlingEmptyEnumerationInventoryTestswas already ins_exempt. Lite has no R4 store class, so its exemption list stays empty.B4. Both
GuardStageCensusTestsgain three cases:TheCatalogDetector_...(probe text: 6 planted reaches flagged; the test assembly's own types,GetExecutingAssembly,CollectorCatalog.Find/AllowsX, a single tool attribute, comments and literals not flagged);TheCensus_FlagsAnUntaggedCatalogClass_AndHonoursOnlyARuleR4OnlySlowEntry(Darling) andTheCensus_FlagsAnUntaggedCatalogClassAndATaggedStoreClass(Lite);EveryCatalogWideClass_CarriesTheStageGuardTrait_OrIsExempt...(real tree; floor 100 Darling / 35 Lite; the named classes must be R4 and tagged).The exemption ceilings are pinned in
TheExemptionList_HasItsPinnedSize(Darling) andTheExemptionList_StaysEmpty(Lite).B5. The red classes of plan section 1
Test plan (Lane B's own runs, on the Census version)
GuardStageCensusTests+GuardStageWorkflowTests(Darling) 28/28,GuardStageCensusTests(Lite) 12/12.DarlingWebEndpointsTestsmadeEveryCatalogWideClass_...fail and name it.*Census*+*Ratchet*: Darling 581 pass, 1 skipped (needs a store). Lite*CiSelect*+*Census*+*Ratchet*+*TestMap*: 144/144.Lane C reran all of these on the final version. Its numbers are below.
Lane C
Design correction: no new trait. Pushed as
e2696a2eb.What changes
C1. The 122 classes Lane B tagged
Stage=Census(87 Darling, 35 Lite) now carry[Trait("Stage", "Guard")]. A byte-level script did the retag. The three slow classes carry no Stage trait, the same as on dev.git grepfinds noStage=Censusand noTrait("Stage", "Census")in the tree.C2. These files are back to dev or to Lane A's text: build.yml's guard job,
GuardStageWorkflowTests, theci-select.pyreplay and map-selectStage=Censusarms, and the lane-orders line (Lane A's-trait Stage=Guardline is kept). The scanner'sCensusTraitText,Row.TaggedCensusandRow.AlwaysRunare removed, andHasGuardTraitreads Guard only again. One comment inci-select.py(thelocal-classesblind spot) now names the slow list.C3. Slow list.
GuardStageScanner.SlowCatalogClassesis a pinned list of R4 classes that stay off the guard stage because they take 50 s or more in the nightly map. Each entry gives its reason asslow: <N> s in the nightly map of 2026-10-13:The brief said "more than 60 s" but also named QueryStatsWriterCensusPins, which is 55.6 s. The coordinator ruled to keep all three and to write the threshold as 50 s or more.
Problems(rows, exempt, slow)exempts a listed class only when rule R4 alone flags it (Row.CatalogOnly). An entry is STALE when its class is tagged, gone, or also flagged by R1 or R2. An entry is REASON when it does not start withslow:. Lite passes no slow list. The 25-class store list is as Lane B left it.C4. Census tests. Darling's
TheExemptionList_HasItsPinnedSizepins the slow list at 3. The synthetic case checks four things: an R4-only slow entry is honoured; a slow entry for a guard-style-named class is MISSING and STALE; an untagged R4 class is MISSING; a tagged store class is STORE. The real-tree R4 case allows the slow list and requires each listed class to be an untagged, R4-only class. The probe-text case is unchanged.C5. Measured
Always-run time locally (Debug, this laptop,
-trait Stage=Guard, wall clock including startup):e7db662b9e2696a2ebNightly test map (run 38356641507, built 2026-10-13T07:05Z, sha 5805c0e): the Guard-tagged R4 classes sum to 44.1 s in Darling.Tests (87 classes, 1 not yet in the map) and 38.1 s in Lite.Tests (35 classes, 1 not in the map). Before the slow list, the Darling sum was 545.1 s.
Test plan
-trait Stage=Guard: Darling 3847 total, 0 failed. Lite 1093 total, 0 failed.*Census*,*Ratchet*, GuardStageWorkflowTests, LiteReadsTraitGuardTests (it namesGuardStageScanner) and the three slow classes: 683 total, 0 failed, 1 skipped (needs a store).*Census*,*Ratchet*,*CiSelect*: 110 total, 0 failed.python .github/scripts/ci-select.py --replay: 731 rows, 0 misses.WebPlainTextTestsmadeEveryCatalogWideClass_CarriesTheStageGuardTrait_OrIsExemptOrSlowfail. The full census failed too, withMISSING WebPlainTextTests.cs: WebPlainTextTests (reaches a whole catalog ... (R4); store: none).DarlingWebEndpointsTests) madeTheExemptionList_HasItsPinnedSizefail (Expected 3, Actual 4). The full census also reportedSTALE DarlingWebEndpointsTests: is on the slow list but now carries the trait.For the coordinator
-trait- Stage=Guard. Under Lane B's Census version the 122 classes ran on both the guard job and the shards. Now they run in the guard job only, the same as every Guard class. The three slow classes still run on the shards.SECTION: None
ENTRY: none (CI and test infrastructure only)