ci(test): six heavy packages split test into test and test:repo so package suites cache again - #16560
Conversation
… so package suites cache again Every file stays where it is. spec, core, types, runtime, objectql and rest each define two vitest projects in one config -- `local` (every other test file) and `repo` (the tests that read outside the package, listed in a vitest.repo-tests.json sidecar) -- and two turbo tasks: `test` keeps package-local inputs, `test:repo` takes the wide `$TURBO_ROOT$` inputs that used to invalidate the whole suite on any change inside the radius. check-cross-package-test-inputs gains the split rule: for a package whose manifest declares `test:repo`, the declared globs must hash on `PKG#test:repo`, `PKG#test` must carry no repo-wide input, and the sidecar list must equal the gate's own scan in both directions (16 new self-test cases in a declared battery). check-test-completeness reads `test:repo` summaries, group headers, bare headers and `Failed:` rosters as the same package. ci.yml's Test Core whole leg runs `turbo run test test:repo`; the cli slice leg is unchanged. Collected files before/after (vitest list --filesOnly): spec 482 = 456 + 26, core 51 = 49 + 2, types 20 = 19 + 1, runtime 238 = 236 + 2, objectql 284 = 283 + 1, rest 187 = 186 + 1; no overlap; each repo project equals its sidecar. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
…po-conformance-task
A root-level `env` / `disableConsoleIntercept` is inert for a project run (measured on vitest 4.1.10; check-registry-log-declared and check-console-intercept-disarm both pin it), so `local` and `repo` carry the root block's options themselves. Collected files re-proved on the merged tree against main's own config: 484 = 458 + 26, 51 = 49 + 2, 20 = 19 + 1, 238 = 236 + 2, 285 = 284 + 1, 188 = 187 + 1; overlap 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
…po-conformance-task
📓 Docs Drift Check1 anchor(s) derived from 6 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 141 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b9626589ffe8881fee379247420a8f4991ad9c61 && git checkout b9626589ffe8881fee379247420a8f4991ad9c61
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ce8caba91403c8f160cb7764c63b08371a13db99 b62f8d451b1d95a7694631af30270be2d91b2975 && git checkout -B drift-repro ce8caba91403c8f160cb7764c63b08371a13db99 && git merge --no-ff b62f8d451b1d95a7694631af30270be2d91b2975
node scripts/docs-audit/affected-docs.mjs --json ce8caba91403c8f160cb7764c63b08371a13db99 |
Fixes #16466
Ruling: option D, seat comment 5565158780 on #16466 (six conditions), replacing the card's Ruling and Acceptance after the measured separability failure (table: comment 5565114830; first report: 5565121423). Contract tier, clause-② yes: what CI verifies per package at merge time changes shape. Draft PR, ordinary queue landing — the seat lands it.
What changes
Every test file stays where it is.
@objectstack/spec,core,types,runtime,objectqlandresteach split their suite into two vitest projects in onevitest.config.ts—local(every other test file) andrepo(the tests that read outside the package, listed in avitest.repo-tests.jsonsidecar) — and two turbo tasks:testkeeps package-local inputs ($TURBO_DEFAULT$),test:repotakes the wide$TURBO_ROOT$inputs that used to sit ontestand re-run the whole suite on any change inside the radius.pnpm testat the root runs both tasks.scripts/check-cross-package-test-inputs.mjs(Layer B, condition 1): for a package whose manifest declares atest:reposcript, the declared globs must hash onPKG#test:repo,PKG#testmust carry NO$TURBO_ROOT$input, and the sidecar must equal the gate's own scan in both directions (an escaping file left intestreds naming it; a listed file that no longer escapes reds; an unreadable list reds prescribing the scan). New self-test batterythe SPLIT test:repo task (#16466), 16 cases, both directions,readSplitdriven on disk; battery roster and floor bumped (7 → 8). Real tree:OK: 28 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split "test:repo" task).—All 133 self-test cases passed.scripts/check-test-completeness.mjs:PKG:test:repo:summary prefixes,::group::and barePKG:test:repoheaders andFailed: PKG#test:reporosters attribute to the package;test:smokestill attributes nothing; 9 new self-test cases. Real two-task turbo logs graded: types + trigger-api602 test(s) declared and all accounted for; core + types + rest5008; spec13149; runtime3361; objectql4806..github/workflows/ci.yml: the Test Core whole leg runspnpm turbo run test test:repo $FILTERS ...; the cli slice leg staystest-only (cli is not split). turbo neither runs nor counts theNONEXISTENTtest:repoof an unsplit package (Tasks: 7 successful, 7 totalfor types + trigger-api, zerotrigger-api:test:repolines), so the guard's run-completed rule is unchanged.turbo.json: basetest:repotask; sixPKG#test:repooverrides carrying the moved inputs (spec 30, core 1, types 1, runtime 4, objectql 2, rest 5); the sixPKG#testoverrides keep only the four package-local inputs. ci: e2e and live tiers move to a nightly run on main; PR and queue runs keep unit, integration and conformance (maintainer-directed, part B tiering) #16455'senv: ["OS_TEST_TIERS"]stays ontestand everyPKG#test;test:repodeclares no env because nothing under it reads one —scripts/nightly-tiers.mjsis the one reader and onlypackages/cli/vitest.config.tsconsults it; the nightly workflow runsturbo run testalone, which is right (the repo projects hold no tiered file).disableConsoleIntercept,env.OS_REGISTRY_LOG,globals,environment,testTimeoutwhere the root had them): a root-level value is inert for a project run on vitest 4.1.10, whichcheck-registry-log-declaredandcheck-console-intercept-disarmboth pin (the first went red on the first cut and is green now).Condition 2 — the CI turbo cache precondition, measured before the ci.yml edit
Test Core DOES restore a turbo cache:
actions/cache/restore@v6on.turbo/cache, keyRUNNER_OS-turbo-JOB-SHARD-REF-SHAwith prefixrestore-keysdown toRUNNER_OS-turbo-JOB-, and the save stepSave Turbo cache (main only)runsif: always() && github.event_name == 'push'. So the first case of the ruling holds: the shard runstest test:repofor the selected packages and the restored per-shard cache carries an unmovedPKG#test. Stated caveat: a package's seed lives under the shard INDEX that ran it on the last main push, and a PR or merge-group run partitions a different (affected) package list, so a hit needs the same index to draw the package — LPT places the heaviest packages first, so spec (the floor) is the stable one; the partitioner's own header records that legs of one shard index range from fully replayed to fully cold. Task-level selection (the ruling's second case) is therefore still the deterministic answer for docs-only groups and is left as the follow-up question in the report, not done here.Condition 5 — the ablation in the new shape (scratch commits, both reset away, never pushed)
Tree
22d3581888with a warm local cache for all six split packages (eachtestandtest:reporun once through turbo). Two scratch commits on the branch, each followed byturbo run test test:repo --dry-run=json, each reset to the pushed head withgit reset --hardand proven (HEAD==PRE yes · diff-empty yes · porcelain [] · blob == HEAD yes; remote head unchanged before and after).Docs-only scratch commit
2296808208(content/docs/concepts/north-star.mdx): exactly 3 task hashes move →@objectstack/rest#test:repo,@objectstack/spec#test:repo,create-objectstack#test..tscommit935ce423fd(packages/connectors/connector-slack/src/index.ts)@objectstack/spec#test@objectstack/spec#test:repo@objectstack/core#test@objectstack/core#test:repo@objectstack/types#test@objectstack/types#test:repo@objectstack/runtime#test@objectstack/runtime#test:repo@objectstack/objectql#test@objectstack/objectql#test:repo@objectstack/rest#test@objectstack/rest#test:repocreate-objectstack#testThe leaf commit moves 11 hashes in all: the three genuinely affected packages' tasks (
connector-slack,example-showcase,dogfood) plus the fivetest:repotasks that hashpackages/**/*.ts; the sixtesthashes do not move. Before this PR the same two edits movedspec#test,rest#test,create-objectstack#test(docs) andspec/core/types/runtime/objectql#test(leaf) — the whole suites (first report, comment 5565121423).*
spec#test:reporeads MISS on the clean tree because spec's declaredpackages/**/*.jsonsweeps vitest's ownnode_modules/.vite/vitest/ID/results.json, rewritten by every run: a diff of the task's 9404 inputs between two consecutive dry-runs moves exactly that one key. It was the whole spec suite's property until this PR confined it to the 33s task; filed as #16555; this PR does not address it.Condition 3 — no file lost, none counted twice
vitest list --filesOnlyon the merged tree (22d3581888, main78e8485d67merged): the pre-split count is main's own config run against the same tree (a probe config written into the package and deleted after).localrepoPartitioner: unchanged. It weighs PACKAGES, the shard runs both tasks for a package, and each package's timings row was measured with the repo files inside
test, so the row still covers both tasks until the next refresh; a package without a row is estimated atcountTestFiles × 1.522sby the existing fallback.partition-test-shards --self-test:self-test OK (71 measured packages → 72 shard items, 6 shards, max/mean 1.00x at most 1.3x, floor 404s, bins 672/672/672/671/672/671s). The next refresh's generator keeps onlytask === 'test'samples and would droptest:reposeconds — filed as sub-issue #16550 of #16464, whose branch rewrites that generator.The suites in the new shape (local, shared box, every leg through the verify lock)
Through turbo (
pnpm turbo run test test:repo --filter=PKG --concurrency=4 --summarize --log-order=stream), every leg exit 0: speclocal458 files / 12759 tests (369s) +repo26 / 390 (33s); runtime 236 / 3292 (211s) + 2 / 69 (14s); objectql 284 / 4801 (210s) + 1 / 5 (0.7s); rest 187 + 1 / 8; core 49 + 2 / 42; types 19 / 571 + 1 / 7. One local-only red on the way:packages/types/src/node.test.ts's host-only-package pin fails oncepackages/plugins/organizations/distexists on the box (#16215 made that package a workspace member; CI stays green only because that dist is never built on types' shard) — reproduced CI's condition by removing that one local artefact; filed as #16552; out of scope here.Measurement (ruling step 1, from the first delivery)
27 packages / 113 escaping files; the six heavy packages hold 33, of which 20 import private internals; every wide glob's holder on spec, runtime and rest cannot import through a public entry (verified against the built
dist/*.d.ts). Escaping share of each suite (local full suites): spec 7.3%, core 4.0%, types 32.1% of a 13s suite, runtime 1.0%, objectql 0.1%, rest 0.1% — the win is the cacheability of the remaining 93–99.9%. Full 113-row table with durations, globs and import style: comment 5565114830 on #16466. Options B (four-file move) and C (publishingREST_ROUTE_LEDGERand runtime's two bindings) are out of scope per the ruling's condition 6, recorded on the card, not filed.Serial proofs and gates
git clone --bare --shared,merge.os-regen.driverunset there): minef5f116e173× ci(test): e2e and live filename tiers move to a nightly run on main #16481's then-current head07464ab227→ exit 0, tree3c24652828, no names (the merged tree read back:envontestand everyPKG#test, the radius onPKG#test:repo, both ci.yml hunks present);origin/mainb1b978c8df× mine22d3581888→ exit 0, tree0976e1390c, no names. ci(test): e2e and live filename tiers move to a nightly run on main #16481 landed at 08:33:55Z (f48f3f1b21) and is merged into this branch (b62f8d451b); the mergedturbo.jsonandci.ymlcompose exactly as the probe's tree did.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackatb62f8d451b: 99 commands (the family gainedcheck:sourcemap-no-sources-contentfrom main), every exit captured before any pipe: 99 exit 0 (check:dual-build-cjs-loadsrefused with exit 3 whileorganizations/distwas deliberately absent, and reads104 published require entry point(s) across 67 package(s) loadonce it was rebuilt).check-governed-merges --testover the 23 paths:0 of 23 path(s) hit the register — NOT governed.pnpm check:nul-bytes:OK (scanned 8143 text file(s) ...). Whole-repopnpm lint(eslint . --no-inline-config) atb62f8d451bunder the verify lock: exit 0.skip-changeset. The diff publishes nothing from any released package: every touched package publishesdist,README.md,CHANGELOG.md(spec adds its json-schema/liveness/prompts trees) — the sidecar and the vitest config are outside those whitelists, and apackage.jsonscriptsline is dev-only.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
Generated by Claude Code