Skip to content

ci: the PR report becomes one compact view: this head's preview, a modified-components table, collapsed visual changes, package size, and one not-run line - #9171

Merged
cixzhang merged 18 commits into
mainfrom
ci/pr-status-table
Oct 31, 2026

Conversation

@cixzhang

@cixzhang cixzhang commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Maintainer impact

Reviewers and contributors open the PR report to answer: where is this head's preview, what did the change do to each component it touches, did any browser suite flag it, and did anything people download grow. Today those answers are spread across long sections, the preview links can show an earlier push, and every suite that didn't run takes a paragraph. GitHub already lists the CI checks and CodeBunny posts its own review, so the report can be one compact view of what ran on the pull request.

Intended invariant

The report is, in order and with nothing expanded by default:

  1. Preview (### 🔎 Preview · <sha>): Storybook and Sandbox links to this head's own Vercel deployment when Vercel reports it ready. Until then it says "⏳ Building…" and links the branch alias (the previous ready deployment). A failed build says so, and a draft shows nothing. A fork's head is looked up the same way, since Vercel builds fork previews too. The fallback report (no trusted analysis) keeps the same preview line.
  2. Modified components: one table, | Component | Size | LoC | Visual · A11y · RTL |.
    • The component name links to its docs page in the preview, with a Storybook link after it. Core components go by name; other packages' components carry their prefix (lab/…, richtext/…).
    • Size is JS and CSS gzip import cost with each change against main. LoC is source lines with its change. Growth is in bold, and 🔴 marks touch-only budget overruns (advisory).
    • The last column gives one mark per suite in fixed positions: ✓ passed, ✗ failed or changed, — not run. The column is dropped when no suite has a result.
    • Three rows are visible, ordered by importance (a failed or changed result first, then budget overruns, then the biggest growth). The rest go under "N more components".
  3. Visual changes, only when shots changed: one collapsed section, "Visual changes: N snapshots in M components", listing the 12 largest changes (changed with its pixel count, added, or removed); the rest are counted. Before / After / Diff thumbnails come in ci: the PR report shows before/after/diff thumbnails for visual changes, from frames a frames-only job publishes #9201.
  4. Package size: one line per published package whose download changed, plus new runtime dependencies. More than three collapse.
  5. One line for browser suites that didn't run, naming their ci:run-<suite> labels. It is left out when the Storybook build failed, because GitHub's check already shows that. Per-component results from the touched-components check, and its wide-change line, come in a follow-up PR.

Change

  • lib/pr-status.js (new) renders all of the above. generate-pr-comment.js is reduced to reading its inputs and composing the report; the old size, bundle, component-stat and job tables are gone.
  • lib/pr-preview.mjs:
    • headPreview reads Vercel's deployment for the exact head SHA. Only vercel[bot]'s Preview deployments and *.vercel.app addresses count.
    • sourceRunJobs reads which suites ran.
  • pr-comment.yml changes in the comment job:
    • adds deployments: read, to read the preview state.
  • No new job, and no workflow runs on a deployment event, which ci: PR report links the branch preview at once and measures the base from main's build #8892 removed.

Permissions: the comment job gains deployments: read (this head's preview state) only. It writes no repository contents.

Evidence

  • Failure before the change: the report has no per-component view tying size, lines of code and suite results together. Its preview links can trail the head, and each suite that didn't run takes a paragraph.
  • Success after the change: the new generator was rendered from [Component Audit] FieldStatus: long validation messages wrap inside narrow fields #9126's real CI run (47 sized components, 241 changed shots, this head's ready preview) and checked in headless Chrome. The preview, docs and Storybook links resolve.
  • Product/runtime behavior verified unchanged: CI report only.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • vitest run --project node .github/scripts: all pass except one Chromium-launch test this machine cannot run (rtl-audit-cli, unrelated).
  • lib/pr-status.test.mjs covers:
    • the preview states;
    • size and LoC cells (growth in bold, 🔴, new, unmeasured);
    • fixed-position marks, and the column dropped when nothing ran;
    • importance order, three rows, then the disclosure;
    • the non-core prefix;
    • the collapsed visual section, largest change first, at most 12 rows, absent for a failed run;
    • a failed visual run never shows ✓, and a base main did not measure shows "not measured", never "new";
    • package lines collapsing after three;
    • the not-run line, left out after a failed Storybook build.
  • pr-preview.test.mjs covers:
    • a ready deployment linking its own address;
    • foreign deployments and addresses ignored;
    • the fallback report's preview line.
  • Mutation checks, each caught:
    • four visible rows;
    • always showing the checks column;
    • trusting any deployment creator;

…h PR, in the build job

Measures every published package's npm download and unpacked size in
build-storybook, after its build, and compares it with the merge base's
measurement that Main now keeps for 14 days. Adds a Published Package Size
section to the PR report and the job summary, and fails the job when a
package's download grows more than 5% (and at least 1 KB) unless the PR
description has a 'Size budget:' line.
…e ci:heavy-lanes

Each label opts a pull request into one heavy suite. A run-labels gate job
reads the event's labels and, for each suite label, the newest labeler's
role (write, maintain or admin; fail closed), from a decision script in the
merge commit's sparse checkout; each suite's owners need it and run only on
their own suite's output. A label still takes effect on the next push or
reopen, ci:run stays the fork gate, and the release check is unchanged.
The pull-request comment names the label for a suite that did not run.
AST-030, CONTRIBUTING and the visual and RTL docs name the new labels.
…ize, visual, accessibility, RTL, perf when it ran, and the CI jobs; touched components link their docs and stories
…helper; the check also follows root build inputs

- A package over its budget is flagged (job summary, PR report, a workflow
  warning) and fails nothing, like the component touch-only flags; the
  'Size budget:' line and the enforce step are gone.
- The package-size merge-base step deepens once when the 50-commit fetch
  holds no merge base, as size-base does.
- Report counts render through num().
- tsconfig.json and scripts/ are in the check's paths scope.
- size-base and the package-size lookup share lib/main-artifact.cjs.
@vercel

vercel Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
astryx Error Error Oct 31, 2026 10:00pm UTC

Request Review

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

astracat-bot Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 31, 2026, 10:11 PM UTC

@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Preview: the links below open this branch's latest Vercel deployment, which can trail a new push until that push's deployment is ready.

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

No new or modified components detected.

Bundle Size Summary

No component packages changed.

Published Package Size

No published package changed size (11 measured).

Download is the gzip tarball npm fetches; unpacked is what lands in node_modules.

Accessibility Audit

Status: Accessibility did not run on this pull request; add ci:run-a11y to run it. It runs after merges to main and before each release.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: the diff rewrites AST-030 FR12 (and the matching INV7 in rtl-audit-evidence) to replace ci:heavy-lanes with per-suite ci:run-visual / ci:run-a11y / ci:run-rtl labels that count only when applied with write access, and the new status table and advisory package budget have no spec claim yet. Any engineering owner, the author included, can record the decision by commenting /approve-spec <full head SHA>, and should say whether AST-030 must state the status table and package budget before they land. Independently of that decision, please:

  • Land #9161 and #9165 first so this pull request carries its own ~1,300 lines instead of ~3,100.
  • Stop the CI jobs table telling every pull request to add ci:run-a11y for the release-only Accessibility spec-test contracts, and telling a labelled pull request to add the label it already has when build-storybook failed.
  • Reuse one byte formatter instead of the new copies in pr-status.js and package-size.js, and drop the Perf row, which no CI job can produce (ci:run-perf does not exist).

[Automated review]

{
id: 'a11y',
label: 'Accessibility',
jobs: /^(pr-a11y|Accessibility spec-test)/i,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Accessibility spec-test contracts (and its shards) run only for release-check (ci.yml a11y-contracts), so on every pull request this pattern makes the job table say the suite did not run and to add ci:run-a11y, even when pr-a11y ran. Dropping it here moves those jobs into the hidden count.

Comment thread .github/scripts/lib/pr-status.js Outdated
jobs: /^(pr-a11y|Accessibility spec-test)/i,
},
{id: 'rtl', label: 'RTL', jobs: /^pr-rtl/i},
{id: 'perf', label: 'Perf', jobs: /^(perf|pr-perf)/i},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No job in the CI workflow matches ^(perf|pr-perf), so the Perf row can never render, and its not-run sentence names a ci:run-perf label nothing honours. Consider removing Perf until a job and label exist.

Comment thread .github/scripts/lib/pr-status.js Outdated
return parts.length > 0 ? parts.join(' · ') : 'no published package changed';
}

function bytes(value) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is a third copy of the byte formatter (sizeText in generate-pr-comment.js and package-size.js), and the copies already disagree past 1 MB. One export from lib/report-text.js would keep them in step.

…ied-components table (name links docs, Storybook, size, LoC, Visual · A11y · RTL marks), collapsed visual thumbnails, package size lines, one not-run hint

Changed shots' frames are published to the PR's assets branch by
publish-frames.mjs so the report can show before/after/diff thumbnails;
the touched-components report (wide-change flag included) is read from its
own artifact.
@cixzhang cixzhang changed the title ci: the PR report leads with one status table (this head's preview first), links touched components, and lists which CI jobs ran ci: the PR report becomes one compact view: this head's preview, a modified-components table, collapsed visual thumbnails, package size, and one not-run line Oct 31, 2026
…s/pr-<n>, and the daily pruner removes that branch once its PR closes
…rked .pr-report-frames); hand-pushed assets branches keep the existing rule

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current record covers the compact PR report, publishing visual frames to an assets/pr-<n> branch with contents: write (and pruning those branches), the advisory package budget, or reading draft #9178's touched-components report. Any engineering owner, the author included, can record that decision by commenting /approve-spec <full head SHA> and saying whether AST-030 must state these first; the per-suite label rewording is already on main and needs nothing more. Independently of that decision, please:

  • Rebase onto main and keep its run-labels.cjs (the branch now conflicts with the merged #9165), land #9161 first, and split out the frame publishing so this pull request carries only its own change.
  • Show a missing merge-base size as unmeasured instead of (new), and render only the frames publish-frames actually committed.
  • Drop "full suite" from the not-run and wide-change lines, since a labelled pull request's accessibility stays scoped to changed components.
  • Check the frame URL against this pull request's number and head, keep contents: write to a frames-only job, and share one byte formatter.

[Automated review]

Comment thread .github/scripts/lib/pr-status.js Outdated

/** A change against main; growth in bold, so it stands out. */
function delta(value, format) {
if (value === null) return 'new';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

null here also means the merge base was not measured (no size-base report, or one for another head), so every modified component then reads (new); the base report said N/A. Consider new only for analysis.newComponents or a key missing from a measured base, and — otherwise.

Comment thread .github/scripts/lib/pr-status.js Outdated
if (!row.kinds.includes(kind)) return '—';
if (!base) return 'frame unavailable';
const key = encodeURIComponent(String(row.key ?? ''));
return `<img src="${base}${kind}/${key}.png" height="120" alt="${kind} frame">`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

publish-frames skips a frame that is missing, not PNG/WebP, or over 500 KB, but this still links it, so the thumbnail renders broken. Could publish-frames output the paths it committed, and this render only those?

Comment thread .github/scripts/lib/pr-status.js Outdated
labels.length === 1
? labels[0]
: `${labels.slice(0, -1).join(', ')} or ${labels.at(-1)}`;
return `Not run on this PR: add ${list} to run ${labels.length === 1 ? 'it' : 'a full suite'}.\n\n`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A label runs the suite scoped to what the change reaches, and a labelled PR's accessibility stays scoped to changed components (AST-030 FR12, FR13), so "a full suite" overstates it; "to run them" would be accurate. The same applies to the wide-change line above.

Comment thread .github/scripts/lib/pr-preview.mjs Outdated
? ['--preview-state', previewState(storybook, sandbox)]
// Frames publish-frames.mjs put on this PR's assets branch, and only
// from raw.githubusercontent.com.
...(/^https:\/\/raw\.githubusercontent\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/assets\/pr-\d+\/visual\/[0-9a-f]{40}\/$/.test(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This accepts any owner, repository, PR number and SHA, while the description says the frame URL is passed only for this head and this PR's assets branch. Comparing against assets/pr-${identity.prNumber}/visual/${identity.headSha}/ in this repository would match the claim.

Comment thread .github/workflows/pr-comment.yml Outdated
# on the pull request's assets branch (publish-frames.mjs writes only
# that branch).
actions: read
contents: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Only the frames step writes, but job-level contents: write also reaches the artifact downloads and the reconcile script that parse pull-request artifacts. A separate frames job holding only this scope would keep the rest read-only.

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026
@cixzhang
cixzhang marked this pull request as ready for review October 31, 2026 18:47

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current record covers the compact PR report (including reading the exact head's Vercel deployment), publishing visual frames to an assets/pr-<n> branch with contents: write and pruning those branches, or reading draft #9178's touched-components report. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner, saying whether AST-030 must state these first. Independently of that decision, please:

  • Split out the frame publishing and pruner (and ideally the exact-head preview read) so this change stays under 2,000 authored lines.
  • Give contents: write to a frames-only job whose checkout does not persist credentials.
  • Never show ✓ for a visual run that failed, and show a missing merge-base size as unmeasured rather than (new).
  • Render only the frames publish-frames committed, and drop "a full suite" from the not-run line.
  • Check the frame URL against this pull request's number and head, and reuse sizeText from report-text.js instead of new byte formatters.

[Automated review]

Comment thread .github/scripts/lib/pr-status.js Outdated
Comment on lines +216 to +219
(visualVerdict && visualVerdict.status !== 'skipped'
? visualChanges.some(entry => entry.component === name)
? 'changed'
: 'pass'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When the visual verdict's status is failed (shots could not be captured), this marks every component without a changed shot ✓, including one whose own shot is in failures. Could a failed verdict mark components named in failures ✗ and the rest —, so the table never shows a pass the run did not produce?

Comment thread .github/scripts/lib/pr-status.js Outdated
return typeof value === 'number' && Number.isFinite(value);
}

function kb(bytes) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

report-text.js already exports sizeText, documented as shared by every size the PR report shows, and package-size.js still uses it for the job summary. Adding kb, signedSize and packed here means the same package reads 97.7KB in the summary and 97.7 kB in the comment; extending sizeText keeps one formatter.

…move to their own PR); a failed visual run never shows a pass; an unmeasured base says so; shared sizeText; shorter not-run line
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 7271897

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Changes requested: the compact report's direction is approved at this head, but a few things need fixing before it can land.

  • The pull request is 62 lines over the 2,000 authored-line limit; moving the touched-components reading into #9178, which produces that artifact, brings it under.
  • headPreview reports "no preview" for every fork, but Vercel does build fork previews here (#9102 has one), so fork contributors lose their preview link.
  • The title promises collapsed visual thumbnails that this head no longer renders, so retitle it and drop the frame-URL claims and stale comments.
  • The fallback report keeps its own preview wording, the wide-change line says the labels "run the full suite", and a failed Storybook build is reported as a missing ci:run-visual label.

[Automated review]

Comment thread .github/scripts/lib/pr-status.js Outdated
/** One line for the browser suites that did not run, with the labels that run them. */
function notRunLine({jobs, a11yReport, visualVerdict, touched = null}) {
if (touched?.wideChange === true)
return `Wide change (${num(touched.componentCount)} components): add \`ci:run-visual\` / \`ci:run-a11y\` / \`ci:run-rtl\` to run the full suite.\n\n`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AST-030 FR12 keeps a labelled pull request's accessibility scoped to changed components (and FR13 scopes labelled visual and RTL to the affected set), so "to run the full suite" overstates what these labels do. "… to run them", as the not-run line now says?

Comment thread .github/scripts/lib/pr-status.js Outdated
Comment on lines +45 to +48
/**
* Changed shots shown with frames, largest change first; publish-frames.mjs
* publishes the same shots in the same order.
*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

publish-frames.mjs is not in this head and no frames are shown any more; this comment, the @output above (lines 12-13) and the test file's header still describe thumbnails.

* jobs was skipped by its run condition (the label was absent). A suite with
* no jobs in the run is unknown (null).
*/
function suiteRunState(suite, jobs) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

pr-a11y and pr-rtl also skip when build-storybook fails (needs: [build-storybook, run-labels]), and visual requires build-storybook success, so a labelled pull request with a failed Storybook build is told to add the label it already has. Name the labels only when build-storybook succeeded in jobs?

Comment thread .github/scripts/lib/pr-preview.mjs Outdated
return 'none';
}

// The same preview wording as the generator's report: the branch alias

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is no longer the generator's wording: the fallback report still links the branch alias (and a fork-… alias for forks, which headPreview says Vercel never builds). Could the fallback read headPreview and render previewLine too, so the two report paths share one preview renderer?

…its fallback, leaves the not-run line out when the Storybook build failed, and drops the touched-components reading (it moves to its own PR)
@cixzhang cixzhang changed the title ci: the PR report becomes one compact view: this head's preview, a modified-components table, collapsed visual thumbnails, package size, and one not-run line ci: the PR report becomes one compact view: this head's preview, a modified-components table, collapsed visual changes, package size, and one not-run line Oct 31, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec ef9b4bc

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks ready to merge: an engineering owner approved the report's direction at this head, and every earlier finding is fixed (fork previews, the fallback's preview line, the not-run line after a failed Storybook build, the title, and the size). A few leftovers are worth a small follow-up and do not block:

  • pr-comment.yml:13-14 and report-text.js:43 still describe branch-alias links and the old 1.5KB format.
  • pr-status.js:40 keeps a perf suite that no CI job matches, and pr-preview.test.mjs still writes a touched-components.json fixture nothing reads.

[Automated review]

jobs: /^(pr-a11y|Accessibility spec-test)/i,
},
{id: 'rtl', label: 'RTL', short: 'RTL', jobs: /^pr-rtl/i},
{id: 'perf', label: 'Perf', short: 'Perf', jobs: /^(perf|pr-perf)/i},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No CI job matches ^(perf|pr-perf), so this entry never renders today; if one ever did, the not-run line would ask for ci:run-perf, which run-labels.cjs (SUITE_LABELS) does not define. Consider dropping it until a perf job and label exist. Non-blocking.

Comment on lines +223 to +227
const touchedPath = path.join(
path.dirname(paths.analysis),
'touched-components.json',
);
if (touched) fs.writeFileSync(touchedPath, JSON.stringify(touched));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

reconcilePrComment no longer reads a touched-components report, so this fixture (and touched/touchedPath at lines 205 and 245) is dead now that the reading moved to its own PR. Non-blocking.

Comment on lines +9 to +11
* visible and the rest collapsed, most important first; the visual deltas
* with frames for the published shots only; the package line; and the one
* not-run or wide-change line.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This header still mentions frames for published shots and a wide-change line; neither exists at this head. Non-blocking.

@cixzhang
cixzhang merged commit aa7d656 into main Oct 31, 2026
51 of 52 checks passed

This branch had an error being deployed

1 failed deployment
Preview — ef9b4bcd Deployed Oct 31, 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