Skip to content

Commit d627082

Browse files
committed
test(ui-merge): the governance pins on objectui's workflows retire to their roster rows and root gates; the Node anchor reads the root workflows
GitHub runs only the repository root's .github/workflows/. The last eight ui/scripts/__tests__ files that scan ui/.github/workflows/ as a population stop reading it. - dependabot-merge-gate: the bucket partition against the check names objectui's pull_request workflows produce, its exemption control, and the six pins on dependabot-auto-merge.yml retire as roster row 9 (sub-issue 4). The gate's one caller runs nowhere and no root workflow merges Dependabot pull requests. The verdict, deadline and legibility cases stay. - merge-queue-reporting: the #3523 step 1 and step 2 pins and the floor derived from REQUIRED_CONTEXTS read objectui's workflows. The root check:required-contexts (assertions 6 and 7) and check:workflow-status-functions carry both holes for the contexts the repository requires today. The CI page describe stays, scanning the map. - check-lockfile-dedupe, check-lockfile-integrity: the wiring pins on objectui's lockfile-dedupe.yml and lockfile-integrity.yml retire as roster rows 18 and 19 (kept, follow-up port). The dedupe gate's live-reading scan reads the root workflows. - ci-setup-pnpm-wiring: the population cases over objectui's workflows retire; the four root ui jobs are held to the root setup-pnpm composite. - doc-version-claims: the node-version anchor reads the root workflows, and the census counter skips whole-line YAML comments. - workflow-cache-save-bound and labeler-package-coverage are deleted: every case read objectui's workflows or its labeler config. The root ui jobs' cache saves belong to the card that bounds them; the labeler pins retire as roster row 14, and ui/.github/labeler.yml stays until the label taxonomy is decided. - markdown-test-inputs.mjs drops the deleted labeler test's entry and the merge-queue test's workflow walker. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
1 parent 2a728ba commit d627082

9 files changed

Lines changed: 175 additions & 1655 deletions

‎ui/scripts/__tests__/check-lockfile-dedupe.test.ts‎

Lines changed: 38 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,10 @@ import { DEDUPE_SENTINEL } from '../check-lockfile-dedupe.mjs';
118118
import { FLOORS, TEST_FILE } from '../check-test-path-roots.mjs';
119119
import { NOT_A_GATE, OPTIONAL_CONTEXTS, REQUIRED_CONTEXTS } from '../dependabot-merge-gate.mjs';
120120
import { maskComments } from '../js-comment-mask.mjs';
121-
import { pullRequestTrigger, readWorkflows, repoRoot, subscribesMergeGroup } from './workflow-checks.js';
121+
import { rootWorkflow, rootWorkflowFiles, rootWorkflowsDir, withoutComments } from './helpers/root-ci';
122+
import { repoRoot } from './workflow-checks.js';
122123

123124
const SCRIPT = 'scripts/check-lockfile-dedupe.mjs';
124-
const WORKFLOW = 'lockfile-dedupe.yml';
125125
const CONTEXT = 'Lockfile Dedupe Check';
126126

127127
/** This file, by its own path — a rename may not silently drop what it pins. */
@@ -177,16 +177,19 @@ interface LiveScan {
177177
*
178178
* Two carriers, one population (objectui#9693): the workflows, which say what a
179179
* job runs, and the files those jobs execute — every tracked test file plus the
180-
* vitest runtime modules. Workflow comment LINES are stripped by `readWorkflows`;
181-
* source comments are blanked by `scripts/js-comment-mask.mjs`, the tree's one
182-
* answer to "comment, or code?", so the paragraphs in this very file explaining
183-
* the hazard cannot count as the hazard.
180+
* vitest runtime modules. The workflows are the root `.github/workflows/`, the
181+
* only ones GitHub runs under the monorepo (objectstack#25712), read with their
182+
* comment LINES stripped; `ui/.github/workflows/` runs nowhere, so a live
183+
* reading there is no reading at all. Source comments are blanked by
184+
* `scripts/js-comment-mask.mjs`, the tree's one answer to "comment, or code?",
185+
* so the paragraphs in this very file explaining the hazard cannot count as the
186+
* hazard.
184187
*/
185188
function scanForLiveReading(): LiveScan {
186-
const workflows = readWorkflows();
189+
const workflows = rootWorkflowFiles();
187190
const sites = workflows
188-
.filter((workflow) => takesLiveReading(workflow.lines.join('\n')))
189-
.map((workflow) => `.github/workflows/${workflow.file}`);
191+
.filter((file) => takesLiveReading(withoutComments(rootWorkflow(file))))
192+
.map((file) => path.relative(repoRoot, path.join(rootWorkflowsDir, file)));
190193

191194
const population = trackedFiles().filter((file) => TEST_FILE.test(file) || VITEST_RUNTIME.test(file));
192195
for (const file of population) {
@@ -208,16 +211,22 @@ function scanForLiveReading(): LiveScan {
208211
/**
209212
* The only places a live reading may be taken, and why each may:
210213
*
211-
* - the path-filtered, non-required workflow this gate was built around;
212214
* - this file, which drives the checker ONLY through the `PATH` stub above —
213215
* every run asserts `pnpmArgv`, so a leg that reached the real pnpm reds
214216
* rather than going quiet, which is what makes this exemption checkable
215217
* instead of trusted.
216218
*
219+
* The second entry this list carried, objectui's path-filtered
220+
* `lockfile-dedupe.yml`, left with objectstack#25712: it sits under
221+
* `ui/.github/workflows/`, which runs nowhere. It is row 18 of the
222+
* objectstack#25697 workflow roster, "kept, follow-up port", and the port adds
223+
* its root workflow back here. Until then the equality below has no workflow
224+
* member, so its workflow half rests on the read floor alone.
225+
*
217226
* ⛔ Nothing else belongs here. An addition is a live, registry-dependent
218227
* reading inside a REQUIRED context — objectui#9562, again.
219228
*/
220-
const LIVE_READING_SITES = [`.github/workflows/${WORKFLOW}`, THIS_FILE].sort();
229+
const LIVE_READING_SITES = [THIS_FILE];
221230

222231
/**
223232
* Real `pnpm@10.31.0` output, same capture objectui#8333 took and the checker's
@@ -499,23 +508,21 @@ describe('--report-only — the mode objectui#9562 ruled (letter A, comment 5717
499508
});
500509
});
501510

502-
describe('the lockfile-dedupe gate is wired the way its header claims', () => {
503-
const workflows = readWorkflows();
504-
const workflow = workflows.find((w) => w.file === WORKFLOW);
505-
/** The workflow with comment lines stripped — prose may not satisfy wiring. */
506-
const body = () => workflow!.lines.join('\n');
507-
508-
it('has a workflow that runs the checker', () => {
509-
expect(workflow, `${WORKFLOW} not found — the gate would never run`).toBeDefined();
510-
// Comment-stripped throughout: a header comment mentioning the script must
511-
// not be able to satisfy a wiring assertion about what the job RUNS.
512-
expect(body()).toContain(`node ${SCRIPT}`);
513-
expect(body()).toContain(`name: ${CONTEXT}`);
514-
});
511+
describe('the lockfile-dedupe gate is fenced and classified the way its header claims', () => {
512+
// Retired with objectstack#25712: `has a workflow that runs the checker`, the
513+
// `--report-only` pin, `is path-filtered, and the filter lists the gate’s own
514+
// runtime closure`, `declares no merge_group trigger` and `uses the shared
515+
// pnpm setup rather than a bare corepack call`. Each read objectui's
516+
// `lockfile-dedupe.yml`, which runs nowhere under the monorepo: GitHub runs
517+
// the root `.github/workflows/` alone. That workflow is row 18 of the
518+
// objectstack#25697 workflow roster, "kept, follow-up port", re-aimed at the
519+
// one root `pnpm-lock.yaml`; the port brings these pins back against its root
520+
// workflow, by path.
515521

516522
it('is the only place the LIVE reading runs, in workflows AND in what they run (objectui#9562, objectui#9693)', () => {
517-
// The live, registry-dependent execution belongs to one path-filtered
518-
// workflow and nowhere else. ⭐ "Nowhere else" is the whole assertion, and
523+
// The live, registry-dependent execution belongs to this file's stubbed legs
524+
// and, once roster row 18 is ported, to one path-filtered root workflow:
525+
// nowhere else. ⭐ "Nowhere else" is the whole assertion, and
519526
// it used to be measured over the workflows alone — while the way a
520527
// required job actually grew a `pnpm dedupe`, the time it happened, was
521528
// through a TEST FILE, which is not a workflow. So the population is both
@@ -531,14 +538,15 @@ describe('the lockfile-dedupe gate is wired the way its header claims', () => {
531538
expect(scan.workflows, 'the workflow read collapsed').toBeGreaterThan(20);
532539
expect(scan.population.length, 'the source read collapsed').toBeGreaterThan(FLOORS.testFiles);
533540
// …and it reached this file's PEERS and the runtime modules, not just the
534-
// two files the expectation names — a scan that only ever looked at itself
541+
// file the expectation names — a scan that only ever looked at itself
535542
// would satisfy the equality below exactly as well.
536543
expect(scan.population.filter((f) => f.startsWith('scripts/__tests__/') && f !== THIS_FILE).length).toBeGreaterThan(50);
537544
expect(scan.population).toContain('vitest.setup.base.ts');
538545

539-
// The equality is also the positive control for both halves: one member is
540-
// a workflow and the other a test file, so a carrier that stopped being
541-
// read goes red here rather than going quiet.
546+
// The equality was also the positive control for both halves while one
547+
// member was a workflow and the other a test file. Until the row-18 port
548+
// restores the workflow member, the workflow half rests on the floor above
549+
// (see `LIVE_READING_SITES`).
542550
expect(scan.sites, 'a live, registry-dependent reading inside a REQUIRED context — objectui#9562, again').toEqual(
543551
LIVE_READING_SITES,
544552
);
@@ -568,37 +576,6 @@ describe('the lockfile-dedupe gate is wired the way its header claims', () => {
568576
expect(takesLiveReading(maskComments('const dedupe = new Set<string>();'))).toBe(false);
569577
});
570578

571-
it('passes `--report-only`, which is where objectui#9562’s ruling actually lands', () => {
572-
// Comment-stripped: the header explains the mode at length, and prose may
573-
// not satisfy wiring. The flag on the step is the entire deliverable — the
574-
// script supports both modes, so the workflow is what chooses one.
575-
expect(body(), 'the gate is blocking again — objectui#9562, undone').toContain(
576-
`node ${SCRIPT} --report-only`,
577-
);
578-
});
579-
580-
it('is path-filtered, and the filter lists the gate’s own runtime closure', () => {
581-
const trigger = pullRequestTrigger(workflow!);
582-
expect(trigger.subscribes, 'the gate must run on pull requests').toBe(true);
583-
expect(trigger.filtered, 'a path filter is what keeps it out of the REQUIRED set').toBe(true);
584-
// Without these the checker can ship having never run once, and the wiring
585-
// bug then surfaces on somebody else's next lockfile pull request.
586-
for (const file of ['pnpm-lock.yaml', `.github/workflows/${WORKFLOW}`, SCRIPT, 'scripts/invoked-as.mjs']) {
587-
expect(body(), `${file} missing from the path filter`).toContain(file);
588-
}
589-
});
590-
591-
it('declares no merge_group trigger, per objectui#3523', () => {
592-
// A path-filtered, non-required gate that subscribes the queue stalls it
593-
// until the ruleset timeout.
594-
expect(subscribesMergeGroup(workflow!)).toBe(false);
595-
});
596-
597-
it('uses the shared pnpm setup rather than a bare corepack call', () => {
598-
expect(body()).toContain('bash scripts/ci-setup-pnpm.sh');
599-
expect(body()).not.toMatch(/^\s*run:.*corepack/m);
600-
});
601-
602579
it('stays an OPTIONAL context, because the JOB still gates even though its verdict does not', () => {
603580
// ⚠️ The load-bearing assertion, and objectui#9562 did NOT move it: the
604581
// ruling relaxed the VERDICT, not the job. A failed checkout, a broken

‎ui/scripts/__tests__/check-lockfile-integrity.test.ts‎

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import path from 'node:path';
2424
import { describe, expect, it } from 'vitest';
2525
import { lockfilePath } from '../workspace-root.mjs';
2626

27-
import { readWorkflows, repoRoot } from './workflow-checks';
27+
import { repoRoot } from './workflow-checks';
2828
import { NOT_A_GATE, OPTIONAL_CONTEXTS, REQUIRED_CONTEXTS } from '../dependabot-merge-gate.mjs';
2929
import {
3030
compareLockfiles,
@@ -190,19 +190,14 @@ describe('cause 2 — a declared dependency forks with no version moving (object
190190
});
191191
});
192192

193-
describe('the gate is wired, and wired the way the dispatch reserved', () => {
194-
const workflow = readWorkflows().find((w) => w.file === 'lockfile-integrity.yml');
195-
196-
it('exists and runs the gate', () => {
197-
expect(workflow, '.github/workflows/lockfile-integrity.yml is missing').toBeTruthy();
198-
expect(workflow?.text).toContain('node scripts/check-lockfile-integrity.mjs');
199-
});
200-
201-
it('checks out deep enough to have a merge base', () => {
202-
// A shallow checkout has no merge base and the script exits 2 rather than
203-
// guessing — which would be a check that reports nothing and looks fine.
204-
expect(workflow?.text).toContain('fetch-depth: 0');
205-
});
193+
describe('the gate is classified the way the dispatch reserved', () => {
194+
// `exists and runs the gate` and `checks out deep enough to have a merge
195+
// base` retired with objectstack#25712. They read objectui's
196+
// `lockfile-integrity.yml`, which runs nowhere under the monorepo: GitHub
197+
// runs the root `.github/workflows/` alone. That workflow is row 19 of the
198+
// objectstack#25697 workflow roster, "kept, follow-up port", re-aimed at the
199+
// one root `pnpm-lock.yaml`; the port brings both pins back against the root
200+
// job that runs the gate.
206201

207202
it('⛔ is classified as NOT a blocking context (the maintainer floor)', () => {
208203
expect(Object.keys(NOT_A_GATE)).toContain(CHECK_NAME);

‎ui/scripts/__tests__/ci-setup-pnpm-wiring.test.ts‎

Lines changed: 29 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import fs from 'node:fs';
44
import path from 'node:path';
55
import { fileURLToPath } from 'node:url';
66

7+
import { jobBlock, rootCi, UI_JOBS, withoutComments } from './helpers/root-ci';
8+
79
const ROOT = path.resolve(fileURLToPath(import.meta.url), '../../..');
8-
const WORKFLOW_DIR = path.join(ROOT, '.github', 'workflows');
910
const SETUP = 'scripts/ci-setup-pnpm.sh';
1011

1112
/**
@@ -62,15 +63,6 @@ const SETUP = 'scripts/ci-setup-pnpm.sh';
6263
* which is the failure mode this repository has recorded more than once.
6364
*/
6465

65-
/** Every workflow file, as `{ file, yaml }`. */
66-
function workflows(): { file: string; yaml: string }[] {
67-
return fs
68-
.readdirSync(WORKFLOW_DIR)
69-
.filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'))
70-
.sort()
71-
.map((file) => ({ file, yaml: fs.readFileSync(path.join(WORKFLOW_DIR, file), 'utf8') }));
72-
}
73-
7466
/**
7567
* Every `run:` step body in a piece of workflow YAML — single-line and block
7668
* scalar alike, and nothing else.
@@ -119,89 +111,38 @@ describe('the shared CI pnpm setup is wired everywhere pnpm is used', () => {
119111
expect(run.stdout).toContain('self-test OK');
120112
});
121113

122-
it('no workflow runs corepack outside the shared script', () => {
123-
const files = workflows();
124-
expect(files.length, 'no workflow files found — this whole file would be vacuous').toBeGreaterThan(0);
125-
126-
const offenders = files
127-
.filter(({ yaml }) => runSteps(yaml).some((s) => /\bcorepack\b/.test(s)))
128-
.map(({ file }) => file);
129-
130-
expect(
131-
offenders,
132-
'these workflows invoke corepack directly instead of `bash ' +
133-
SETUP +
134-
'`. The download it performs is a registry read that has reddened a REQUIRED check ' +
135-
'before any gate logic ran (objectui#8099); route it through the shared script so the ' +
136-
'retry and the self-describing step name apply here too.',
137-
).toEqual([]);
138-
139-
// Anti-vacuity for the reader above: the comment-only mentions must still be
140-
// THERE, or this assertion is passing because the corpus emptied out.
141-
const commentOnly = files.filter(
142-
({ yaml }) => /\bcorepack\b/.test(yaml) && !runSteps(yaml).some((s) => /\bcorepack\b/.test(s)),
143-
);
144-
expect(
145-
commentOnly.map((w) => w.file),
146-
'the two comment-only corepack mentions must stay visible to this scan and stay unjudged — ' +
147-
'if they are gone, re-derive whether this rule still reads what it claims to read',
148-
).toEqual(['changelog.yml', 'dependabot-auto-merge.yml']);
149-
});
150-
151-
it('every workflow that invokes pnpm also runs the shared setup', () => {
152-
const files = workflows();
153-
154-
const usesPnpm = files.filter(({ yaml }) => runSteps(yaml).some((s) => /\bpnpm\b/.test(s)));
155-
expect(
156-
usesPnpm.length,
157-
'no workflow appears to invoke pnpm, which cannot be true — the step reader has drifted',
158-
).toBeGreaterThan(0);
114+
// `no workflow runs corepack outside the shared script`, `every workflow that
115+
// invokes pnpm also runs the shared setup` and `every job that calls the
116+
// setup script checks the repository out first` retired with
117+
// objectstack#25712. They swept objectui's workflows under
118+
// `ui/.github/workflows/` for calls to this script, and those run nowhere
119+
// under the monorepo: GitHub runs the root `.github/workflows/` alone. What
120+
// objectui's CI became is the four root `* UI` jobs (objectstack#25697),
121+
// which set pnpm up through the root composite action
122+
// `./.github/actions/setup-pnpm`, objectstack's answer to the same Corepack
123+
// fetch. The describe below asks the same three questions of those jobs, by
124+
// path. This script and its self-test stay; no root job calls it.
125+
});
159126

160-
const missing = usesPnpm
161-
.filter(({ yaml }) => !runSteps(yaml).some((s) => s.includes(SETUP)))
162-
.map(({ file }) => file);
127+
describe('the root ui jobs set pnpm up through the root shared setup', () => {
128+
const SHARED = 'uses: ./.github/actions/setup-pnpm';
129+
const jobs = Object.values(UI_JOBS).map((key) => ({ key, block: withoutComments(jobBlock(rootCi(), key)) }));
163130

164-
expect(
165-
missing,
166-
`these workflows run pnpm without calling \`bash ${SETUP}\` first. pnpm is not on the runner: ` +
167-
'the first invocation downloads it from registry.npmjs.org, and unrouted that download lands ' +
168-
"inside whichever step happens to touch pnpm first — including `actions/setup-node`'s own " +
169-
'cache probe, where a red is hardest to attribute (objectui#8099).',
170-
).toEqual([]);
131+
it.each(jobs)('$key runs no corepack outside the shared setup', ({ block }) => {
132+
expect(runSteps(block).filter((s) => /\bcorepack\b/.test(s))).toEqual([]);
171133
});
172134

173-
it('every job that calls the setup script checks the repository out first', () => {
174-
const files = workflows().filter(({ yaml }) => yaml.includes(SETUP));
175-
expect(files.length, `no workflow references ${SETUP} — the sweep was undone`).toBeGreaterThan(0);
176-
177-
const bad: string[] = [];
178-
for (const { file, yaml } of files) {
179-
const lines = yaml.split('\n');
180-
// Job blocks: two-space keys inside the `jobs:` mapping.
181-
const jobsAt = lines.findIndex((l) => /^jobs:[ \t]*$/.test(l));
182-
expect(jobsAt, `${file} must have a top-level \`jobs:\` mapping`).toBeGreaterThan(-1);
183-
184-
const starts: number[] = [];
185-
for (let i = jobsAt + 1; i < lines.length; i++) {
186-
if (/^ {2}[a-z0-9][a-z0-9-]*:[ \t]*$/.test(lines[i])) starts.push(i);
187-
}
188-
starts.push(lines.length);
189-
190-
for (let s = 0; s + 1 < starts.length; s++) {
191-
const block = lines.slice(starts[s], starts[s + 1]);
192-
const setupAt = block.findIndex((l) => l.includes(SETUP));
193-
if (setupAt === -1) continue;
194-
const checkoutAt = block.findIndex((l) => /uses:\s*actions\/checkout@/.test(l));
195-
if (checkoutAt === -1 || checkoutAt > setupAt) {
196-
bad.push(`${file} :: ${block[0].trim()}`);
197-
}
198-
}
199-
}
135+
it.each(jobs)('$key checks the repository out, then sets pnpm up, before its first pnpm', ({ block }) => {
136+
const checkoutAt = block.search(/uses:\s*actions\/checkout@/);
137+
const setupAt = block.indexOf(SHARED);
138+
expect(checkoutAt, 'the job checks nothing out').toBeGreaterThan(-1);
139+
expect(setupAt, `the job has no \`${SHARED}\` step after its checkout`).toBeGreaterThan(checkoutAt);
200140

141+
const firstPnpm = runSteps(block).find((s) => /\bpnpm\b/.test(s));
142+
expect(firstPnpm, 'the job runs no pnpm at all, so this reading has drifted').toBeDefined();
201143
expect(
202-
bad,
203-
`${SETUP} is a file in this repository, so a job that runs it before \`actions/checkout\` ` +
204-
'dies with "No such file or directory" — a failure that reads nothing like a toolchain problem.',
205-
).toEqual([]);
144+
block.indexOf(firstPnpm!.split('\n')[0]),
145+
'the job runs pnpm before the shared setup has put it on the runner',
146+
).toBeGreaterThan(setupAt);
206147
});
207148
});

0 commit comments

Comments
 (0)