Skip to content

Commit a798ae8

Browse files
ci(nightly-tiers): four shards — the showcase smoke alone on one, the CLI cut across the rest (#28613)
<!-- claude-projects-attribution: {"github_login":"marchtian"} --> _Requested by **田騏** · [project thread](https://claude.ai/code/project/chan_01VmuUhXU9SSUzqJYyXSmYSi?thread=cmsg_01VmuUhXU9SSUzqJYyXSmYSi9paTx1Lu8ENYwwssthfK3h)_ Fixes #28580 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:夜间分层测试从 2 个分片改为 4 个。showcase 浏览器冒烟独占一个分片,CLI 的 e2e 层切成 3 片,放在另外 3 个分片上。分片器新增文档键 `ownShard`,用来表达“这个包独占一个分片”。 - **为什么改**:showcase 的分片要先构建 Console、安装 Chromium,再构建自己的依赖闭包,约 18 分钟,而它在数据集里只按 48 秒计权,所以总被放到最重的 CLI 切片旁边。11-01 这个 1 号分片需要约 52 分钟,在 45 分钟上限处被取消,没有任何测试失败。 - **风险与回滚**:只改夜间 workflow 的分片布局。没有测试被改名、跳过或删除,两个层级都没有收窄。回滚就是 revert 本 PR,回到 2 个分片。 - **先看**:① `.github/workflows/test-nightly-tiers.yml` 的矩阵和头注释里的数字;② `scripts/partition-test-shards.mjs` 的 `readOwnShard` 和 `planRun`;③ `scripts/nightly-tiers.mjs` 的 `OWN_SHARD_PACKAGES`。 - **两分钟验证**:`node scripts/nightly-tiers.mjs --packages > /tmp/t.json && node scripts/partition-test-shards.mjs /tmp/t.json --shard 4/4`,预期 stdout 只打印 `@objectstack/example-showcase`,日志有一行 `own shard: ... alone on shard 4/4`。 - **需要你做的**:本 PR 改到 `.github/workflows/**`,属于 Tier H 受管面,需要授权审批人批准,之后由席位落地。 ## What changes The nightly tiers job goes from two shards to four. Shards 1–3 each carry one slice of `@objectstack/cli`'s e2e tier, and shard 4 carries the showcase browser smoke alone. The timeout stays at 45 minutes. - `scripts/partition-test-shards.mjs` reads a new document key, `ownShard`. Each package it names runs whole and alone on one of the run's last shards. Everything else is planned over the shards left, so a `sliceAcrossShards` package is cut N − k ways. The key is checked strictly: it must be an array of distinct listed names, none also in `sliceAcrossShards`, with fewer reservations than shards. A reserved package that the run excludes is refused, because its shard would run nothing. The balance line grades only the shared shards, since a reserved shard is placed by the document and not by weight. - `scripts/nightly-tiers.mjs --packages` puts every tier-owning package in exactly one of the two keys. `OWN_SHARD_PACKAGES` (today only the showcase) goes to `ownShard`, and the rest go to `sliceAcrossShards`. - `.github/workflows/test-nightly-tiers.yml`: `matrix.shard` is `[1, 2, 3, 4]`, the job name is `/4`, the partitioner runs with `--shard k/4`, and the reports artifact is `-of-4`. That is four places. The header said three, and the artifact name was the one it missed. Two header statements were false, and the measurements below correct them: "≈ 13 min a shard" and "the closure's build tasks replay". - New self-test pins (live tree): the workflow's preparation step must name exactly the `ownShard` packages. The CLI must reach the N − k shared shards as one distinct slice each, with each reserved package alone on its own shard. A new battery, `ownShard`, has 12 cases: identity when the key is absent, 7 refusals, placement at 3 and 4 shards, the control (without the key the showcase rides beside CLI 1/3), and refusal of an excluded package. ## Why four shards, by measurement Run 41277375892 (2026-11-01, `890c2a6d`), shard 1 (job 124762188633). It carried `@objectstack/cli 1/2` and `@objectstack/example-showcase`: | segment | wall | |:--|--:| | setup through "Compute this shard's package set" | 0m52s | | "Prepare the showcase browser smoke" (Console build + Chromium) | 9m39s | | CLI 1/2 leg (closure build 6m00s + vitest 1631s) | 33m13s | | showcase leg | killed 1m43s in, still building its closure | Run 41009107101 is the reference for a complete showcase leg: it took 8m14s (closure build 5m33s, then 160s of vitest). Shard 1 therefore needed ≈ 52 min against 45. Shard 2 (CLI 2/2 alone) ran 25m19s. Shard 1 was killed at 45 minutes on 10-28 and 11-01. On 10-29, 10-30 and 10-31 it finished red, in 32–41 min. Options, each priced from that night's own numbers: | layout | slowest shard | note | |:--|--:|:--| | today: 2 shards, showcase beside CLI 1/2 | ≈ 52 min | over the budget | | (a) 2 shards, showcase moved beside CLI 2/2 | ≈ 44 min | about a minute of headroom; which slice is light depends on file hashes | | (b) 3 shards, CLI ×3, showcase by weight | ≈ 46 min | LPT breaks the tie by name and puts the showcase beside CLI 1/3, the heaviest slice | | 3 shards, CLI ×2, showcase alone | 34.6 min (measured CLI 1/2 shard) | that shard rose from 31.0 to 34.6 min over 10-28 to 11-01 | | **4 shards, CLI ×3, showcase alone (this PR)** | **≈ 29 min** | others ≈ 22 / 18 / 19 min; about 16 min of headroom; the whole run is ≈ 30 min, inside the hour | | (c) raise the budget, 2 shards | ≈ 52 min plus growth | the whole run is no longer bounded by the hour | The CLI rows at three slices are predicted, not measured. I reproduced vitest's own `--shard` order (sha1 of each file's root-relative path, cut by file count). It puts exactly the 42 integration files that 11-01's shard 1 ran into slice 1/2. I then summed that night's per-file durations: slices of 3671 / 2551 / 1759 s of file time, at the 2.86× parallelism measured that night, plus 6 min of closure build and ≈ 1.5 min of setup. <details><summary>Per-night shard readings (job logs)</summary> ``` night shard-1 items prepare CLI 1/2 vitest CLI 2/2 vitest shard-1 result 10-26 cli 1/2 - 1141s 788s success 24m42s 10-27 cli 1/2 - 1452s 840s success 30m46s 10-28 cli 1/2 + showcase 8m30s 1476s 1080s cancelled at 45m 10-29 cli 1/2 + showcase 0m31s 1542s 925s failure (a test) 32m35s 10-30 cli 1/2 + showcase 0m21s 1574s 902s failure (showcase) 40m55s 10-31 cli 1/2 + showcase 5m18s 1595s 1127s failure (a test) 38m25s 11-01 cli 1/2 + showcase 9m39s 1631s 1127s cancelled at 45m ``` Same 82 files, per-file time summed: 5532 s on 10-26, 7907 s on 11-01. </details> ## Tests Self-tests and gates were run at `a9fdb19a1d`. `origin/main` had not moved since the branch point (`cd065d3698`). - `node scripts/partition-test-shards.mjs --self-test`: exit 0. The roster is 18 batteries; the nightly battery has 17 cases and `ownShard` has 12. - `node scripts/nightly-tiers.mjs --self-test`: exit 0, `48 cases pass` (floor 47 → 48). - `node scripts/ci/plan-test-core-shards.mjs --self-test`: exit 0. This is the Test Core caller of `planRun()`, and it is unchanged. - The preparation-step pin was checked in both directions with a throwaway probe against the live file: live passes, a renamed package goes red, a removed step goes red. - Live partition at 4: shards 1–3 print `@objectstack/cli 1/3`, `2/3` and `3/3`, and shard 4 prints `@objectstack/example-showcase`. ## Acceptance notes - **No tier narrowed, no test touched.** The same 86 CLI e2e files and 2 showcase live files run every night, under the same `OS_TEST_TIERS=nightly`. - **The real-runner reading is this PR's own Nightly Tiers run.** The workflow's `pull_request` paths trigger fires on this diff. The ≈ 29 min figure above is a prediction until that run reports. - Raise the matrix when the slowest shard's test step nears 35 minutes. The document re-slices the CLI at the new count with no edit to it. - Observed, not filed: the CLI e2e tier's per-file time rose 43% over 82 identical files between 10-26 and 11-01 (for example, `jsx-gate-manifest-notice.e2e.test.ts` went from 208 s to 300 s). This is the trend that eats any shard's headroom. - Observed, not filed: the CLI legs replayed 0 of their ~62 build tasks from the restored Build Core turbo cache on every night measured. The header now says so; the cause is not diagnosed here. - Observed, not filed: vitest's `--shard` cuts by file count, so slices are unequal (4661 vs 3320 s of file time at two). A duration-aware sequencer in `packages/cli/vitest.config.ts` would flatten that, but it is outside this card's file surface. - Pre-existing and left alone: the partitioner's log line `(N of them file-level slices …)` prints items minus packages, which reads 2 for three slices. ## Verification <details><summary>Derived gate list (dispatch-gates --commands) and --ran reconciliation</summary> 51 families were derived from 3 paths against merge base `cd065d369`. All 51 were run and every one exited 0, `pnpm check:pm-dispatch-gates` included (fast tier: 1937 cases, 373 s). The `--ran` reconciliation reported: `51 derived famil(ies) accounted for — 51 run, 0 NOT-MEASURED (a DERIVED zero — all 51 recorded an exit code and none of them is 3)`. </details> Session: https://claude.ai/code/session_017ihL2gLghEqeDKTbDTC7Zd 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>
1 parent 68a2a21 commit a798ae8

3 files changed

Lines changed: 376 additions & 96 deletions

File tree

‎.github/workflows/test-nightly-tiers.yml‎

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,42 +17,58 @@ name: Nightly Tiers
1717
# RESTORE-ONLY caches (a scheduled run reads main's pnpm store and Build Core's
1818
# turbo seeds; the per-push workflows own saving them, and a nightly that saved
1919
# would churn the rationed 10 GB pool the way PR-side saves once did — see
20-
# ci.yml's "Restore Turbo cache"). The turbo cache restore is what keeps the
20+
# ci.yml's "Restore Turbo cache"). The turbo cache restore is meant to keep the
2121
# dependency closure's `build` tasks a replay: `OS_TEST_TIERS` is hashed into
22-
# the `test` tasks ONLY (turbo.json), so the builds hit main's entries while
23-
# every test task misses by construction and runs for real.
22+
# the `test` tasks ONLY (turbo.json), so every test task misses by construction
23+
# and runs for real. ⚠️ Measured, the builds do not replay either: a CLI leg
24+
# replayed 0 of its ~62 build tasks on every night from 2026-10-26 to 11-01 and
25+
# spent 5–6 min building them, and the budget below is sized with that cost in.
2426
#
25-
# ## Sharding — TWO shards, derived rather than picked
27+
# ## Sharding — FOUR shards: the CLI cut three ways, the showcase alone
2628
#
2729
# The package set is not the workspace: `scripts/nightly-tiers.mjs --packages`
2830
# enumerates exactly the packages that own a tier file, in the `turbo ls`
2931
# document shape, and `scripts/partition-test-shards.mjs` splits THAT with the
30-
# same measured weights, the same LPT placement and the same
31-
# `<pkg> k/n` slice grammar Test Core uses. Measured at 6eba38f5a3: one
32-
# package, `@objectstack/cli`, owning all 60 tier files; its whole suite was
33-
# 1231s on a hosted runner (run 34009395649) and the e2e tier is ~75% of it
34-
# (packages/cli/vitest.config.ts, the priced trade), so ≈ 15 min of tests plus
35-
# ~5 min of restore/install per runner. Under 60 minutes on ONE shard already —
36-
# but `FILE_SHARDED_PACKAGES` cuts the CLI into two vitest slices, and the
37-
# partitioner REFUSES to place two slices of one package in one bin
38-
# (`assertSlicesSpread`), so one shard is not a legal count and two is the
39-
# smallest one that is. Each shard then carries one slice, ≈ 8 min of tests.
40-
# Raise the matrix (three places below: `matrix.shard`, the job `name:`, and
41-
# `--shard k/N`) only when a second tier-owning package arrives heavy enough
42-
# for the partitioner's own balance line to say so.
32+
# same LPT placement and the same `<pkg> k/n` slice grammar Test Core uses.
33+
# Two packages own tier files today. The document reserves a shard of its own
34+
# for `@objectstack/example-showcase` (`ownShard`) and cuts `@objectstack/cli`
35+
# across the shards left (`sliceAcrossShards`): CLI 1/3, 2/3 and 3/3 on shards
36+
# 1-3, the showcase alone on shard 4.
4337
#
44-
# The second tier-owning package is `@objectstack/example-showcase`, and it is
45-
# not that heavy: one `*.live.test.ts` file (#26445), the showcase browser
46-
# smoke, which used to be its own scheduled workflow (Showcase Smoke) and
47-
# filed nothing when red. The partitioner keeps it whole ("not the run's
48-
# floor") and places it on one shard; that shard also builds the Console SPA
49-
# and installs Chromium for it (≈ 5 min, the "Prepare the showcase browser
50-
# smoke" step), which no other shard pays.
38+
# Why that shape, measured on run 41277375892 (2026-11-01, killed at the
39+
# 45-minute limit with no test red). The CLI's e2e tier is the heavy package:
40+
# 7981s of per-file test time, 1631s + 1127s of vitest wall as two slices
41+
# (vitest cuts a package by file COUNT in sha1 order, not by duration, so the
42+
# slices are unequal and the first is the heavy one). The showcase browser
43+
# smoke weighs 48s in the dataset, but its shard alone builds the Console SPA
44+
# and installs Chromium ("Prepare the showcase browser smoke", 9m39s that
45+
# night) and then builds the example's closure before its two
46+
# `*.live.test.ts` files run (8m14s with the smoke on run 41009107101). Placed
47+
# by weight it rode beside the first CLI slice: shard 1 ran 0m52s of setup,
48+
# 9m39s of preparation and a 33m13s CLI leg, and was killed still building the
49+
# showcase. That shard needed ≈ 52 min against a budget of 45; the old "≈ 13
50+
# min a shard" was never measured on this population.
51+
#
52+
# Predicted from that night's own per-file durations, with vitest's own file
53+
# order reproduced (it puts exactly the 42 integration files that night's
54+
# shard 1 ran into slice 1/2): the CLI shards ≈ 29 / 22 / 18 min (slices of
55+
# 3671s / 2551s / 1759s of file time, at the 2.86x parallelism measured that
56+
# night, plus 6 min of closure build and ≈ 1.5 min of setup), the showcase
57+
# shard ≈ 19 min. Three shards (the CLI cut twice) would leave the CLI 1/2
58+
# shard at its measured 34.6 min, which rose from 31.0 min over 2026-10-28 to
59+
# 11-01 with the tier's per-file time; four keep the slowest shard near 29.
60+
#
61+
# Raise the matrix (four places below: `matrix.shard`, the job `name:`,
62+
# `--shard k/N` and the reports artifact's `-of-N`) when the slowest shard's
63+
# `Run this shard's nightly tiers` step nears 35 minutes; the document
64+
# re-slices the CLI at the new count with no edit to it. The partitioner's
65+
# balance line grades the shared shards only — a reserved shard is placed by
66+
# the document, not by weight — so it cannot say when on its own.
5167
#
5268
# ## What lands where — the house idiom of `fleet-patrols.yml`'s `platform-checklist-watchdog` job
5369
#
5470
# On green this workflow files nothing, edits nothing and closes nothing. On
55-
# red the `report` job — ONE writer, after both shards, never the shards
71+
# red the `report` job — ONE writer, after all shards, never the shards
5672
# themselves (two matrix legs racing the same lookup would mint two cards) —
5773
# opens or refreshes ONE card: fixed title `nightly-tiers: red on main`, labels
5874
# `bug` · `domain:devx` · `priority:p1` on create only (grading is the devx
@@ -148,19 +164,19 @@ env:
148164

149165
jobs:
150166
tiers:
151-
name: Nightly tiers (${{ matrix.shard }}/2)
167+
name: Nightly tiers (${{ matrix.shard }}/4)
152168
runs-on: ubuntu-latest
153-
# Generous against the ≈ 13 min a shard is expected to take, and still
154-
# inside the hour the card set as the whole run's bound. A shard killed here
155-
# produces no vitest report; the report job says so rather than reading it
156-
# as "no failing files".
169+
# ≈ 16 min of headroom over the ≈ 29 min the slowest shard is predicted to
170+
# take (see "Sharding" above), and still inside the hour the card set as the
171+
# whole run's bound. A shard killed here produces no vitest report; the
172+
# report job says so rather than reading it as "no failing files".
157173
timeout-minutes: 45
158174
strategy:
159-
# Both shards run to completion: the card should carry every failing
175+
# All shards run to completion: the card should carry every failing
160176
# file, not the first shard's.
161177
fail-fast: false
162178
matrix:
163-
shard: [1, 2]
179+
shard: [1, 2, 3, 4]
164180

165181
steps:
166182
- name: Checkout repository
@@ -206,7 +222,7 @@ jobs:
206222
node scripts/nightly-tiers.mjs --check
207223
node scripts/nightly-tiers.mjs --packages > "$RUNNER_TEMP/tier-packages.json"
208224
node scripts/partition-test-shards.mjs "$RUNNER_TEMP/tier-packages.json" \
209-
--shard ${{ matrix.shard }}/2 \
225+
--shard ${{ matrix.shard }}/4 \
210226
> "$RUNNER_TEMP/shard-packages.txt"
211227
echo 'Items on this shard (a package name, or a package plus a k/n file-level slice):'
212228
cat "$RUNNER_TEMP/shard-packages.txt"
@@ -216,16 +232,18 @@ jobs:
216232
# two things `turbo run test` does not produce: the Console SPA the backend
217233
# serves at /_console (`packages/console/dist`, built by the `build:dist`
218234
# task — `pnpm console:build`), and a Chromium this `@playwright/test` can
219-
# launch. Only the shard the partitioner gave that package pays for them.
235+
# launch. Only the shard the partitioner gave that package pays for them —
236+
# a shard of its own, which the document reserves for exactly the packages
237+
# this step prepares (`OWN_SHARD_PACKAGES` in scripts/nightly-tiers.mjs;
238+
# `partition-test-shards.mjs --self-test` reds when the two disagree).
220239
# The test's own `^build` closure is the `test` task's, as for every
221240
# package here.
222241
#
223-
# `continue-on-error`, on purpose: the same shard carries a slice of the
224-
# CLI's e2e tier, and a Console build that fails one night must not take
225-
# that slice down with it. The night is still red, and still names the
226-
# file — the smoke then fails every test at once, and its failure text
227-
# says to read a whole-run red as the environment first; this step's red
228-
# in the log is the environment.
242+
# `continue-on-error`, on purpose: a Console build that fails one night
243+
# must not stop the test step from running. The night is still red, and
244+
# still names the file — the smoke then fails every test at once, and its
245+
# failure text says to read a whole-run red as the environment first;
246+
# this step's red in the log is the environment.
229247
- name: Prepare the showcase browser smoke
230248
continue-on-error: true
231249
run: |
@@ -253,11 +271,10 @@ jobs:
253271
# into a killed job with no report at all.
254272
#
255273
# One deliberate difference from ci.yml: the file-level SLICES run first
256-
# and the whole-package leg last. The whole packages here are the cheap
257-
# ones (the partitioner keeps a package whole when it is not the run's
258-
# floor) — today the showcase browser smoke — and a red in one of them
259-
# must not stop the shard's slice of the CLI's e2e tier from running.
260-
# The other way round, a red slice skips a few minutes of smoke.
274+
# and the whole-package leg last, so a red whole package never stops a
275+
# slice of the CLI's e2e tier from running. Today no shard carries both —
276+
# the showcase browser smoke is alone on its own shard — and the order
277+
# holds for the day a whole package shares a shard with a slice again.
261278
- name: Run this shard's nightly tiers
262279
env:
263280
OS_TEST_TIERS: nightly
@@ -332,7 +349,7 @@ jobs:
332349
if: always()
333350
uses: actions/upload-artifact@v7
334351
with:
335-
name: nightly-tiers-reports-${{ matrix.shard }}-of-2
352+
name: nightly-tiers-reports-${{ matrix.shard }}-of-4
336353
path: ${{ runner.temp }}/nightly-reports/
337354
if-no-files-found: ignore
338355
retention-days: 14

‎scripts/nightly-tiers.mjs‎

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@
6767
* request that introduces it, which is the open question the landing card
6868
* records.
6969
*
70-
* ## The nightly's shards: one slice of the tier on EVERY shard, by the nightly's own count
70+
* ## The nightly's shards: every shard runs something, by the nightly's own count
7171
*
72-
* `--packages` is the nightly's whole input to `scripts/partition-test-shards.mjs`,
73-
* and the one tier-owning package (`@objectstack/cli`, 82 tier files at
74-
* 4f81f31526) has to reach every one of the nightly's shards as a slice of its
75-
* own: an item the split does not place on a shard leaves that shard running
72+
* `--packages` is the nightly's whole input to `scripts/partition-test-shards.mjs`.
73+
* Two packages own tier files: `@objectstack/cli` (86 tier files at cd065d3698),
74+
* cut into slices, and `@objectstack/example-showcase`, kept whole on a shard of
75+
* its own. Every shard the reservation leaves must carry one slice of the CLI:
76+
* an item the split does not place on a shard leaves that shard running
7677
* nothing, reading green, while another carries the whole tier.
7778
*
7879
* The nightly used to get its slices by inheriting Test Core's
@@ -86,14 +87,19 @@
8687
* run 38517756588 killed the first shard at its 45-minute limit.
8788
*
8889
* So the document states the nightly's slicing itself: `sliceAcrossShards`
89-
* names every tier-owning package, and the partitioner cuts each one into as
90-
* many slices as the run's own `--shard k/N` has shards, still subject to its
91-
* per-run slice decision and the vitest file floor. Test Core's map no longer
92-
* reaches the nightly, and a change to the nightly's matrix re-slices it with
93-
* no edit here. `partition-test-shards.mjs --self-test` (run on every pull
94-
* request by lint.yml) pins it on the live tree: this document, at the shard
95-
* count `test-nightly-tiers.yml` declares, yields that many non-empty shards,
96-
* each carrying one distinct slice of the CLI.
90+
* names the tier-owning packages to cut, and the partitioner cuts each one into
91+
* as many slices as the run's own `--shard k/N` has shards left over by
92+
* `ownShard`, still subject to its per-run slice decision and the vitest file
93+
* floor. `ownShard` names the packages that run whole on a shard of their own
94+
* (`OWN_SHARD_PACKAGES` below): placed by weight, the showcase browser smoke
95+
* rode beside the first CLI slice, and that shard was killed at its 45-minute
96+
* limit on 2 of the 5 nights from 2026-10-28 (runs 40638682260, 41277375892).
97+
* Test Core's map no longer reaches the nightly, and a change to the nightly's
98+
* matrix re-slices it with no edit here. `partition-test-shards.mjs
99+
* --self-test` (run on every pull request by lint.yml) pins it on the live
100+
* tree: this document, at the shard count `test-nightly-tiers.yml` declares,
101+
* yields that many non-empty shards, each own-shard package alone on one and
102+
* one distinct slice of the CLI on each of the others.
97103
*
98104
* ## Where `OS_TEST_TIERS` has to be declared to reach vitest at all
99105
*
@@ -217,21 +223,45 @@ export function tierPackages(repoRoot = REPO_ROOT) {
217223
return out;
218224
}
219225

226+
/**
227+
* The tier-owning packages that run whole and alone on a nightly shard of their
228+
* own (the document's `ownShard`), rather than beside a slice of the CLI. One
229+
* today, the showcase browser smoke, and for a cost the partitioner's weights
230+
* cannot see: its shard alone builds the Console SPA and installs Chromium
231+
* (test-nightly-tiers.yml, "Prepare the showcase browser smoke"), then builds
232+
* the example's closure, before its two `*.live.test.ts` files run. Measured on
233+
* run 41277375892 (2026-11-01), shard 1: the preparation took 9m39s, the CLI
234+
* slice beside it 33m13s, and the 45-minute limit killed the shard while it was
235+
* still building the showcase's closure; on run 41009107101 (2026-10-30) that
236+
* closure and the smoke took 8m14s. Its weight in the dataset is 48s.
237+
*
238+
* ⛔ The workflow's preparation step names the same package, and
239+
* `partition-test-shards.mjs --self-test` reds when the two disagree.
240+
*/
241+
export const OWN_SHARD_PACKAGES = Object.freeze(['@objectstack/example-showcase']);
242+
220243
/**
221244
* `tierPackages()` in the `turbo ls --output=json` shape
222245
* `scripts/partition-test-shards.mjs` reads, so the nightly can shard exactly
223246
* the tier-owning packages with the same partitioner and the same slice
224247
* grammar the Test Core matrix uses. `count` is written because the
225248
* partitioner checks it against `items.length`.
226249
*
227-
* `sliceAcrossShards` names every tier-owning package: the nightly states its
228-
* own slicing rather than inheriting Test Core's `FILE_SHARDED_PACKAGES`, and
229-
* the partitioner cuts each named package into as many slices as the nightly
230-
* has shards -- see "The nightly's shards" in this file's header.
250+
* Every tier-owning package is named by exactly one of two keys. `ownShard`
251+
* names the `OWN_SHARD_PACKAGES` that own tier files (each gets a shard of its
252+
* own); `sliceAcrossShards` names the rest: the nightly states its own slicing
253+
* rather than inheriting Test Core's `FILE_SHARDED_PACKAGES`, and the
254+
* partitioner cuts each named package into as many slices as the shards the
255+
* reservations leave -- see "The nightly's shards" in this file's header.
231256
*/
232-
export function packageListDocument(repoRoot = REPO_ROOT) {
257+
export function packageListDocument(repoRoot = REPO_ROOT, ownShard = OWN_SHARD_PACKAGES) {
233258
const items = tierPackages(repoRoot).map(({ name, path: dir }) => ({ name, path: dir }));
234-
return { packages: { count: items.length, items }, sliceAcrossShards: items.map(({ name }) => name) };
259+
const own = items.filter(({ name }) => ownShard.includes(name)).map(({ name }) => name);
260+
return {
261+
packages: { count: items.length, items },
262+
sliceAcrossShards: items.filter(({ name }) => !own.includes(name)).map(({ name }) => name),
263+
ownShard: own,
264+
};
235265
}
236266

237267
// ---------------------------------------------------------------------------
@@ -497,8 +527,17 @@ export function selfTest() {
497527
check(() => assert(eq(doc.packages.items[1], { name: '@x/owner', path: 'packages/owner' }), 'items carry name and repo-relative path'));
498528
check(() =>
499529
assert(
500-
eq(doc.sliceAcrossShards, doc.packages.items.map((i) => i.name)),
501-
`the document states its own slicing, naming every tier-owning package -- got ${JSON.stringify(doc.sliceAcrossShards)}`
530+
eq(doc.sliceAcrossShards, doc.packages.items.map((i) => i.name)) && eq(doc.ownShard, []),
531+
`with no own-shard package in the tree, every tier-owning package is sliced -- got ${JSON.stringify(doc)}`
532+
)
533+
);
534+
// A reservation names only packages that own tier files, and moves each one
535+
// out of the slicing: every tier-owning package is named by exactly one key.
536+
const reserved = packageListDocument(root, ['@x/inner', '@x/plain', '@x/absent']);
537+
check(() =>
538+
assert(
539+
eq(reserved.ownShard, ['@x/inner']) && eq(reserved.sliceAcrossShards, ['@x/owner']),
540+
`ownShard keeps only tier-owning packages and leaves them out of sliceAcrossShards -- got ${JSON.stringify(reserved)}`
502541
)
503542
);
504543

@@ -536,7 +575,7 @@ export function selfTest() {
536575
check(() => assert(judgeCollection('p', tier, ['test/c.test.ts'], ['test/a.e2e.test.ts']).some((p) => /does NOT collect 1 nightly-tier/.test(p)), 'a tier file missing from nightly runs nowhere'));
537576
check(() => assert(judgeCollection('p', tier, [], []).length === 1, 'an empty nightly collection is exactly one problem: the tier files run nowhere'));
538577

539-
const FLOOR = 47;
578+
const FLOOR = 48;
540579
assert(cases >= FLOOR, `${cases} cases ran, below the pinned floor of ${FLOOR} -- cases stopped running`);
541580
console.log(`\u2713 nightly-tiers self-test: ${cases} cases pass.`);
542581
return SELF_TEST_VERDICT;

0 commit comments

Comments
 (0)