Skip to content

ci: build the docsite once, run its browser contracts on that build, keep docsite-test as the required join - #9877

Merged
cixzhang merged 2 commits into
mainfrom
ci/docsite-build-browser-split
Nov 5, 2026
Merged

cixzhang merged 2 commits into
mainfrom
ci/docsite-build-browser-split

Conversation

@cixzhang

@cixzhang cixzhang commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Why

docsite-test builds the docsite, runs its data tests and then its browser contracts in one 15-minute job. On recent green runs the job took 737 s (median; p90 760 s): docsite build 332 s, browser contracts 265 s, setup and package builds about 140 s, checkout 9 s. That leaves about two and a half minutes of slack, so any slow checkout or busy runner cancels it at the limit (#9795, #9725), and the required check goes red without a failing test.

What changes

docsite-test becomes three jobs. The docsite is still built once.

  • docsite-build does what docsite-test did up to the build. It validates spec records on the spec-only lane, builds the packages and the docsite, runs the data tests and the hidden production registry gate. When the browser contracts apply, it stamps the build with its commit (.next/BUILD_COMMIT) and uploads what next start reads: the build without its compile cache, the generated data and theme, and the playground's vendored assets (one-day retention).
  • docsite-browser downloads that build. It fails unless the build's commit is the commit it checked out, and it never builds. It runs the browser contracts as a planner-driven matrix of one leg: playwright-legs.mjs splits the spec files by recorded time, so a second leg is a one-line change.
  • docsite-test stays the required context (FR7) as a join on a slim runner. It keeps the outside-contributor, CI-is-full and deferred-heavy-CI stops and the Lab-only notice. It passes only when the build passed and the browser leg passed, or when the build's scope found no browser contract to run. A failed, cancelled or skipped leg, a failed build or an unknown scope fails it.
  • The browser contracts skip only when every changed file outside the docsite app is a test file (a __tests__/ file or a *.test/*.spec file in any package, CLI included), a story, a spec record or a changeset, and the trusted classifier does not call the change shared infrastructure. A harness, a11y-spec, Storybook-config or shared-test change sets full_suite and runs them, as for every owner (FR13). So does a change to apps/docsite/** (its own e2e specs and blog posts included), any non-test source, a theme, a README, a lockfile or the workflow. An undecidable scope runs them too.
  • The interaction-legs planner becomes playwright-legs.mjs, with .github/playwright-leg-durations.json. It now takes a config, an optional project and a times key, and records logs from a suite with one unnamed project. The interaction legs pass the same config and project as before.

Measured on this pull request's run

Measured on main with the docs page-weight fix (#9907) merged in:

Job Before (one job, 7 runs after #9907) After
docsite-test 591–754 s; job minus checkout up to 747 s the join: 3 s
docsite-build (part of the above) 520 s: build 321 s, package builds 80 s, data tests 50 s, upload 17 s
docsite-browser (part of the above) 274 s: contracts 219 s
  • Slack: a single job had no slack left under a 270 s checkout (900 − (747 + 270) = −117 s). Each new job keeps at least six minutes of its fifteen.
  • Artifact: the build artifact is about 70 MB (it was 906 MB before docsite: send docs pages a small nav model instead of the full registries #9907). It carries the generated ShadCN registry and leaves out the compile cache, traces, source maps and file traces, uses compression level 6, and is kept one day.
  • Reporting delay: the required context reports about two minutes later than before, because the browser contracts now start after the build's upload.

CI impact

  • Runner class: unchanged, ubuntu-latest for the build and the browser leg; the join runs on ubuntu-slim.
  • Docsite runs: in a sample of 400 recent pull-request runs, 36 built the docsite (about 9%, so about 105 a day of about 1,160 runs).
  • Extra time per docsite run: a second checkout and setup (about 35 s), the artifact upload and download (about 20 s) and the join (3 s), about +1 job-minute.
  • Saved by the skip: 3 of 22 sampled pull requests changed only tests, stories, spec records or changesets. They skip the browser leg, about 4.6 job-minutes each.
  • Net: about +0.4 job-minutes per docsite run, about +40 job-minutes a day.
  • Artifact traffic: about 7.4 GB a day uploaded (about 70 MB × about 105 runs), each artifact kept one day.
  • Concurrency: the first wave is unchanged (docsite-build replaces docsite-test), so admission's pull-request peak stays 16. No Actions cache is used (ci-no-dependency-cache.test.mjs still holds).

Tests

  • ci-docsite-browser.test.mjs is rewritten for the three jobs. It covers:
    • one docsite build in the whole workflow, with every build and data step on the active lane
    • the browser-input paths: what runs the contracts and what skips them
    • the stamp and the upload list
    • the browser leg serving only the downloaded build of its own commit, with a shell check against another commit's build
    • the join passing on success, or on no contracts to run, and failing on a failed, cancelled or skipped leg, a failed build or an unknown scope
  • playwright-legs.test.mjs adds the unnamed-project log reader and the docsite times within one leg's budget.
  • Updated to the new job names: ci-admission (the derived build-nothing list, and jobs gated through a guarded need), ci-duration-guard (docsite-test is a join), ci-lab-only-routing, ci-test-routing, ci-tooling-routing, shadcn-registry-ci, spec-owner-workflow and the duration-split guard.
  • Mutation checks, each caught:
    • the join passing a skipped leg
    • the browser leg rebuilding
    • no commit check
    • docsite e2e specs counted as tests
    • the browser leg running whatever the scope

@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 4, 2026 11:33pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 4, 2026
@astracat-bot

astracat-bot Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 4, 2026, 11:37 PM UTC

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · 612077a

Storybook · Sandbox · built from this PR's head

Package size: no published package changed; no new dependencies.

API changes: none in this pull request

Committed API report behind this build: cli 9 rows, core 4 rows; run pnpm api:report.

Not run on this PR: add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.

CI impact: +16.9 job-min per update · pool +21% · time to green −10.7 min · standard runners · 10 CI action or script files changed (not measured) ⚠️
Before After Change
Job-minutes per PR update 80.9 97.9 +16.9
Runner pool, runner-minutes a day 31,409 37,969 +6,561 (+21%)
Average concurrent jobs 21.8 26.4 +4.6
Time to green (median → this run) 26.8 min 16.1 min −10.7 min
Runs a day 388 388 0

Changed jobs: docsite-browser added: standard · docsite-build added: standard · docsite-test changed: standard · pr-interaction changed: standard · pr-interaction-webkit changed: standard

Not measured (the jobs that use these keep their recent minutes above): .github/scripts/ci-admission.test.mjs, .github/scripts/ci-docsite-browser.test.mjs, .github/scripts/ci-duration-guard.test.mjs, .github/scripts/ci-lab-only-routing.test.mjs, .github/scripts/ci-test-routing.test.mjs, .github/scripts/ci-tooling-routing.test.mjs, .github/scripts/playwright-legs.mjs, .github/scripts/playwright-legs.test.mjs, .github/scripts/shadcn-registry-ci.test.mjs, .github/scripts/spec-owner-workflow.test.mjs.

Flags: runner-pool usage +21%.

Assumptions: before is each job's average over the last 20 pull-request runs; after keeps that for unchanged jobs and uses this run's minutes for added or changed ones, weighted by how often each changed job ran in those runs; runs a day averages the seven whole days before today; time to green compares this run with the median of those runs that ran the same jobs.


Generated by PR Enrichment workflow | View full report

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes. The split runs green end to end on this head, but the new browser-contract skip covers more than AST-030 allows.

  • Decide whether the browser contracts apply (.github/workflows/ci.yml:639) reads only the paths regex, so a change limited to internal/interaction-harness/, internal/a11y-spec/, apps/storybook/.storybook/ or a test file outside a package (such as .github/scripts/*.test.mjs) skips the docsite browser contracts although the trusted classifier reports full_suite=true and AST-030 FR13 runs every owner's full suite for shared infrastructure; gate the step on touches == 'true' || full_suite == 'true' and add a test that such a change runs the contracts.

Two small follow-ups: the comments at ci.yml:570-571 and ci.yml:576-578 (and the test title) should name everything the regex skips, and playwright-legs.mjs:229 still prints the old interaction-legs.mjs usage. The split also makes docsite-test report later (about 105 s on this run; the description estimates three minutes), which FR19 asks reviewers to weigh.

[Automated review]

Comment thread .github/workflows/ci.yml Outdated
run: (set +e; pnpm -F @astryxdesign/docsite test > docsite-data.log 2>&1; echo $? > docsite-data.exit.tmp; mv docsite-data.exit.tmp docsite-data.exit) &
- name: Decide whether the browser contracts apply
id: browser
run: echo "applies=${{ (steps.scope.outputs.spec_only != 'true' && steps.scope.outputs.tooling_only != 'true' && steps.scope.outputs.lab_only != 'true') && steps.scope.outputs.touches == 'true' }}" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This decision reads only the paths regex (line 590), never the trusted classifier's full_suite. A change limited to internal/interaction-harness/, internal/a11y-spec/, apps/storybook/.storybook/ or a test file outside a package (.github/scripts/*.test.mjs) therefore skips the docsite browser contracts, though the classifier marks it shared infrastructure and AST-030 FR13 runs every owner's full suite for it. Gating on both keeps every skip the description claims for component tests, stories, spec records and changesets:

run: echo "applies=${{ (steps.scope.outputs.spec_only != 'true' && steps.scope.outputs.tooling_only != 'true' && steps.scope.outputs.lab_only != 'true') && (steps.scope.outputs.touches == 'true' || steps.scope.outputs.full_suite == 'true') }}" >> "$GITHUB_OUTPUT"

A test that a full_suite change runs the contracts would cover the fail-closed edge (FR9).

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes. The two new commits slim the uploaded docsite build soundly, but the browser-contract skip still covers more than AST-030 allows.

  • Decide whether the browser contracts apply (.github/workflows/ci.yml:639) still reads only the paths regex, so a change limited to internal/interaction-harness/, internal/a11y-spec/, apps/storybook/.storybook/ or a test file outside a package (such as .github/scripts/*.test.mjs) skips the docsite browser contracts although the trusted classifier reports full_suite=true and AST-030 FR13 runs every owner's full suite for shared infrastructure; gate the step on touches == 'true' || full_suite == 'true' and add a test that such a change runs the contracts.

Still open and non-blocking: the comments at ci.yml:570-571 and ci.yml:576-578 and the test title should name everything the regex skips, and playwright-legs.mjs:229 still prints the old interaction-legs.mjs usage. On this head the docsite jobs did not run because heavy CI was deferred, and the lint failure is in a DateInput spec this pull request does not touch.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: two issues block this head.

  • Decide whether the browser contracts apply (.github/workflows/ci.yml:641) still reads only the paths regex, so a change limited to internal/interaction-harness/, internal/a11y-spec/, apps/storybook/.storybook/ or a shared test file such as .github/scripts/*.test.mjs passes docsite-test without the docsite browser contracts, although the trusted classifier reports full_suite=true for these paths and AST-030 FR13 runs every owner's full suite for shared infrastructure; gating on touches == 'true' || full_suite == 'true' and adding a test for a harness-only change fixes it.
  • The merge of main rewrote internal/interaction-harness/src/busy.ts:910 to if (f.t >= window.from) {break;}, which fails the required lint check's formatting step; restore main's line or run pnpm exec prettier --write internal/interaction-harness/src/busy.ts.

While you are there, the skip comments at ci.yml:572-573 and ci.yml:578-580 and the interaction-legs.mjs usage string at playwright-legs.mjs:229 are out of date.

[Automated review]

let before: (typeof valued)[number] | undefined;
for (const f of valued) {
if (f.t >= window.from) break;
if (f.t >= window.from) {break;}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line came in with the merge of main (where it reads if (f.t >= window.from) break;) and fails the required lint check's Prettier step. Restore main's line, or run pnpm exec prettier --write internal/interaction-harness/src/busy.ts, which puts break; on its own line inside the block.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: one small fix remains.

  • The description's "Also" section says this pull request rewrites internal/interaction-harness/src/busy.ts to fix a lint failure from a merge of main, but this head has no such merge, leaves busy.ts unchanged (line 910 matches main), and passes lint, so please delete that section so the description matches the change.

Both earlier blockers are resolved: the browser-contract decision now also runs on the classifier's full_suite, and the busy.ts edit is gone. The split itself conforms to AST-030 (FR7, FR11, FR13, FR16, FR17, FR20, FR26), and its workflow tests cover the join and the skip. CI on this head deferred the heavy suites, so docsite-build and docsite-browser still need their run before merge.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving: splitting docsite-test into a build job and a browser job behind the same required join conforms to AST-030 (FR7, FR11, FR13), and every unknown or skipped state fails the join.

CI's own run at this head built the docsite once, matched the build's commit in the browser leg and passed the browser contracts, and the description now matches the change.

  • Non-blocking: the "Playground preview isolation" section of CONTRIBUTING.md still says the browser-contract step runs in docsite-test; it now runs in docsite-browser.
  • The pull-request comment flags +16% runner-pool usage against the description's estimate of about +40 job-minutes a day; that cost is a maintainer's call.

[Automated review]

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving: splitting docsite-test into docsite-build, docsite-browser and a required join conforms to AST-030 (FR7, FR11, FR13), and the join fails on every skipped, cancelled or unknown state. Required CI passed on this head: the docsite was built once, the browser contracts passed against that build, and docsite-test, test, build and lint are green. Nothing further is required.

  • Non-blocking: the build upload in .github/workflows/ci.yml (lines 670-684) leaves out apps/docsite/public/shadcn/, the generated ShadCN registry that next start serves, although the comment above it says the upload carries everything next start reads; add the directory or narrow the comment.
  • The pull-request comment flags +17% runner-pool usage against the description's estimate of about +40 job-minutes a day; that cost is a maintainer's call.

[Automated review]

Comment thread .github/workflows/ci.yml
apps/docsite/src/themes/astryx.css
apps/docsite/public/playground-types.json
apps/docsite/public/monaco/
apps/docsite/public/vendor/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: the docsite build also writes apps/docsite/public/shadcn/ (the generated ShadCN registry, apps/docsite/scripts/generate-data.mjs:2750-2752), which next start serves. Adding it here keeps the comment above accurate and lets a future browser contract fetch a registry item; otherwise narrow the comment.

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving: splitting docsite-test into docsite-build, docsite-browser and a required join conforms to AST-030 (FR7, FR11, FR13), and the join fails on every skipped, cancelled or unknown state. Required CI has now run on this head and passed: the docsite was built once, all 38 browser contract tests passed against that build, and docsite-test, test, build and lint are green.

  • Non-blocking: the header of .github/scripts/shadcn-registry-ci.test.mjs (line 8) still says the production gate runs in docsite-test; it now runs in docsite-build.
  • Miss: non-blocking, the comment above test-registry-e2e in .github/workflows/ci.yml (lines 1200-1201) says the same and needs the same one-word fix.
  • Non-blocking: the description gives the build artifact as 62 MB; on this head, which also uploads the ShadCN registry, it is 70 MB.
  • The pull-request comment flags +19% runner-pool usage against the description's estimate of about +40 job-minutes a day; that cost is a maintainer's call.

[Automated review]

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving: splitting docsite-test into docsite-build, docsite-browser and a required docsite-test join conforms to AST-030 (FR7, FR11, FR13), and the join passes only on a passed build with passed browser contracts or with none to run, failing on a failed, cancelled or skipped leg and on an unknown scope. Required CI has run on this head and passed, with the browser contracts run against the uploaded build of the same commit.

  • Non-blocking: re-running only a failed docsite-browser job more than a day after its build fails at the download, because the build artifact is kept one day; re-running all jobs recovers.
  • The pull-request comment flags +19% runner-pool usage against the description's estimate of about +40 job-minutes a day; weighing that cost is a maintainer's call.

[Automated review]

…s on that build in docsite-browser, and keep docsite-test as the required join

docsite-build builds the packages and the docsite once, runs the data tests
and the production registry gate, and uploads the build (about 70 MB: the
server and static output, generated data and theme, the ShadCN registry and
the playground's vendored assets; no compile cache, traces, source maps or
file traces) stamped with its commit. docsite-browser downloads that build,
fails unless it is of the commit it checked out, and runs the browser
contracts as a one-leg matrix planned by recorded time. docsite-test stays
the required context as a join that fails on a failed, cancelled or skipped
leg. The contracts skip only for component tests, stories, spec records and
changesets that are not shared infrastructure (full_suite). The interaction
planner becomes playwright-legs.mjs for all three leg lanes.

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved pending CI: a re-run of all jobs is in progress on this head; the previous run passed every required check, including all docsite browser contracts against the uploaded build of the same commit.

Splitting docsite-test into docsite-build, docsite-browser and a required docsite-test join conforms to AST-030 (FR7, FR11, FR13), and the join passes only on a passed build with passed browser contracts or with none to run. Nothing further is needed from you.

[Automated review]

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved pending CI: the first CI attempt on this head deferred the heavy suites, and a re-run of all jobs is now in progress, so build, test and docsite-test have not reported on this head yet.

Splitting docsite-test into docsite-build, docsite-browser and a required docsite-test join conforms to AST-030 (FR7, FR11, FR13), the join passes only on a passed build with passed browser contracts or with none to run, and the merge of main leaves this pull request's own changes untouched. Nothing further is needed from you.

[Automated review]

@cixzhang
cixzhang merged commit ef3db95 into main Nov 5, 2026
146 of 149 checks passed
@github-actions
github-actions Bot deleted the ci/docsite-build-browser-split branch November 5, 2026 06:03

This branch was successfully deployed

1 active deployment
Preview — 612077a6 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant