Skip to content

ci(test): six heavy packages split test into test and test:repo so package suites cache again - #16560

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-16466-repo-conformance-task
Sep 7, 2026
Merged

ci(test): six heavy packages split test into test and test:repo so package suites cache again#16560
os-steve merged 4 commits into
mainfrom
claude/issue-16466-repo-conformance-task

Conversation

@claude

@claude claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

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, objectql and rest each split their suite into two vitest projects in one vitest.config.tslocal (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 ($TURBO_DEFAULT$), test:repo takes the wide $TURBO_ROOT$ inputs that used to sit on test and re-run the whole suite on any change inside the radius. pnpm test at the root runs both tasks.

  • scripts/check-cross-package-test-inputs.mjs (Layer B, condition 1): for a package whose manifest declares a test:repo script, the declared globs must hash on PKG#test:repo, PKG#test must carry NO $TURBO_ROOT$ input, and the sidecar must equal the gate's own scan in both directions (an escaping file left in test reds naming it; a listed file that no longer escapes reds; an unreadable list reds prescribing the scan). New self-test battery the SPLIT test:repo task (#16466), 16 cases, both directions, readSplit driven 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 bare PKG:test:repo headers and Failed: PKG#test:repo rosters attribute to the package; test:smoke still attributes nothing; 9 new self-test cases. Real two-task turbo logs graded: types + trigger-api 602 test(s) declared and all accounted for; core + types + rest 5008; spec 13149; runtime 3361; objectql 4806.
  • .github/workflows/ci.yml: the Test Core whole leg runs pnpm turbo run test test:repo $FILTERS ...; the cli slice leg stays test-only (cli is not split). turbo neither runs nor counts the NONEXISTENT test:repo of an unsplit package (Tasks: 7 successful, 7 total for types + trigger-api, zero trigger-api:test:repo lines), so the guard's run-completed rule is unchanged.
  • turbo.json: base test:repo task; six PKG#test:repo overrides carrying the moved inputs (spec 30, core 1, types 1, runtime 4, objectql 2, rest 5); the six PKG#test overrides 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's env: ["OS_TEST_TIERS"] stays on test and every PKG#test; test:repo declares no env because nothing under it reads one — scripts/nightly-tiers.mjs is the one reader and only packages/cli/vitest.config.ts consults it; the nightly workflow runs turbo run test alone, which is right (the repo projects hold no tiered file).
  • Each project re-declares the root block's test options (disableConsoleIntercept, env.OS_REGISTRY_LOG, globals, environment, testTimeout where the root had them): a root-level value is inert for a project run on vitest 4.1.10, which check-registry-log-declared and check-console-intercept-disarm both 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@v6 on .turbo/cache, key RUNNER_OS-turbo-JOB-SHARD-REF-SHA with prefix restore-keys down to RUNNER_OS-turbo-JOB-, and the save step Save Turbo cache (main only) runs if: always() && github.event_name == 'push'. So the first case of the ruling holds: the shard runs test test:repo for the selected packages and the restored per-shard cache carries an unmoved PKG#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 22d3581888 with a warm local cache for all six split packages (each test and test:repo run once through turbo). Two scratch commits on the branch, each followed by turbo run test test:repo --dry-run=json, each reset to the pushed head with git reset --hard and 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.

task clean docs-only commit leaf .ts commit 935ce423fd (packages/connectors/connector-slack/src/index.ts)
@objectstack/spec#test HIT HIT (hash same) HIT (hash same)
@objectstack/spec#test:repo MISS* MISS (hash moved) MISS (hash moved)
@objectstack/core#test HIT HIT HIT
@objectstack/core#test:repo HIT HIT MISS (moved)
@objectstack/types#test HIT HIT HIT
@objectstack/types#test:repo HIT HIT MISS (moved)
@objectstack/runtime#test HIT HIT HIT
@objectstack/runtime#test:repo HIT HIT MISS (moved)
@objectstack/objectql#test HIT HIT HIT
@objectstack/objectql#test:repo HIT HIT MISS (moved)
@objectstack/rest#test HIT HIT HIT
@objectstack/rest#test:repo HIT MISS (moved) HIT
create-objectstack#test MISS (never run locally) MISS (moved) MISS (same)

The leaf commit moves 11 hashes in all: the three genuinely affected packages' tasks (connector-slack, example-showcase, dogfood) plus the five test:repo tasks that hash packages/**/*.ts; the six test hashes do not move. Before this PR the same two edits moved spec#test, rest#test, create-objectstack#test (docs) and spec/core/types/runtime/objectql#test (leaf) — the whole suites (first report, comment 5565121423).

*spec#test:repo reads MISS on the clean tree because spec's declared packages/**/*.json sweeps vitest's own node_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 --filesOnly on the merged tree (22d3581888, main 78e8485d67 merged): the pre-split count is main's own config run against the same tree (a probe config written into the package and deleted after).

package pre-split (main's config) local repo local + repo overlap repo == sidecar
spec 484 458 26 484 0 yes
core 51 49 2 51 0 yes
types 20 19 1 20 0 yes
runtime 238 236 2 238 0 yes
objectql 285 284 1 285 0 yes
rest 188 187 1 188 0 yes

Partitioner: 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 at countTestFiles × 1.522s by 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 only task === 'test' samples and would drop test:repo seconds — 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: spec local 458 files / 12759 tests (369s) + repo 26 / 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 once packages/plugins/organizations/dist exists 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 (publishing REST_ROUTE_LEDGER and runtime's two bindings) are out of scope per the ruling's condition 6, recorded on the card, not filed.

Serial proofs and gates

  • Merge-tree, driver-less bare clone (git clone --bare --shared, merge.os-regen.driver unset there): mine f5f116e173 × ci(test): e2e and live filename tiers move to a nightly run on main #16481's then-current head 07464ab227 → exit 0, tree 3c24652828, no names (the merged tree read back: env on test and every PKG#test, the radius on PKG#test:repo, both ci.yml hunks present); origin/main b1b978c8df × mine 22d3581888 → exit 0, tree 0976e1390c, 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 merged turbo.json and ci.yml compose exactly as the probe's tree did.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at b62f8d451b: 99 commands (the family gained check:sourcemap-no-sources-content from main), every exit captured before any pipe: 99 exit 0 (check:dual-build-cjs-loads refused with exit 3 while organizations/dist was deliberately absent, and reads 104 published require entry point(s) across 67 package(s) load once it was rebuilt).
  • check-governed-merges --test over the 23 paths: 0 of 23 path(s) hit the register — NOT governed.
  • pnpm check:nul-bytes: OK (scanned 8143 text file(s) ...). Whole-repo pnpm lint (eslint . --no-inline-config) at b62f8d451b under the verify lock: exit 0.
  • Changeset: none — skip-changeset. The diff publishes nothing from any released package: every touched package publishes dist, README.md, CHANGELOG.md (spec adds its json-schema/liveness/prompts trees) — the sidecar and the vitest config are outside those whitelists, and a package.json scripts line is dev-only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox


Generated by Claude Code

… 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
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
@github-actions github-actions Bot added the size/l label Sep 7, 2026
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 7, 2026
@github-actions github-actions Bot added ci/cd dependencies Pull requests that update a dependency file tests labels Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 6 changed package(s); no hand-written page names any of them. ⚠️ 6 changed file(s) yielded no anchor (packages/core/vitest.repo-tests.json, packages/objectql/vitest.repo-tests.json, packages/rest/vitest.repo-tests.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 6 changed file(s) yielded no anchor (packages/core/vitest.repo-tests.json, packages/objectql/vitest.repo-tests.json, packages/rest/vitest.repo-tests.json, …) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 141 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json ce8caba91403c8f160cb7764c63b08371a13db99packageMentionDocs.

Which tree this was computed on

This run read content/docs from b9626589ffe8881fee379247420a8f4991ad9c61 — the merge of head b62f8d451b1d95a7694631af30270be2d91b2975 into base ce8caba91403c8f160cb7764c63b08371a13db99, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants