Skip to content

Commit 8d41194

Browse files
committed
test(changeset-gate): the copy leg's battery — #22812's shape discharged, a console patch refused, Clause-② no unaffected
Claude-Session: https://claude.ai/code/session_01Cw7JSJKE4EqGJ1uKPJZnSu Co-authored-by: Claude <noreply@anthropic.com>
1 parent e23b473 commit 8d41194

1 file changed

Lines changed: 169 additions & 2 deletions

File tree

‎scripts/check-changeset-no-major.mjs‎

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2531,6 +2531,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
25312531
'The GRAIN: a PR-scoped declaration judged at PR scope (#16361)': 25,
25322532
'THE DEPTH: a nested package is a candidate the axis can refuse (#16713)': 21,
25332533
'THE ROOT: a packed `bin` target is a published surface the axis can refuse (#16692)': 37,
2534+
'THE COPY: a repo file a package build copies into what it packs (#15204)': 22,
25342535
'#18263: the refusal says its reason, and says it where the API can read it': 35,
25352536
'#19008: the level headline is the parsed declaration, not a literal': 30,
25362537
'The protocol lockstep evidence, in BOTH directions (ruling 6056808625)': 39,
@@ -2540,8 +2541,9 @@ const SELF_TEST_BATTERIES = Object.freeze({
25402541
// zeroing it, so the roster's own size is pinned too. Lowered 20 → 19 when the
25412542
// carrier-axis battery left with the gate label it read (ruling record
25422543
// 5770886272 on #19061): the ordinary direction, one battery, one row. Raised
2543-
// 19 → 20 with the protocol lockstep battery (ruling record 6056808625).
2544-
const SELF_TEST_BATTERY_FLOOR = 20;
2544+
// 19 → 20 with the protocol lockstep battery (ruling record 6056808625). Raised
2545+
// 20 → 21 with the copy-leg battery (#15204).
2546+
const SELF_TEST_BATTERY_FLOOR = 21;
25452547

25462548
// The key an assertion is filed under when no battery is open. It is not a
25472549
// declared battery, so it reds by the same set difference rather than silently
@@ -4225,6 +4227,171 @@ function selfTest() {
42254227
);
42264228
}
42274229

4230+
// ── THE COPY: a repo file a package's build copies into what it packs (#15204)
4231+
//
4232+
// `@objectstack/console` has no `src/**` and no `bin`; the one tracked file
4233+
// its `dist` ships is the repo-root `sdui.manifest.json`, copied there by
4234+
// `scripts/build-console.sh`. PR #22812 moved only that file (and spec
4235+
// citations), graded the console `minor` under a true `Clause-②: yes
4236+
// (widening)`, and was refused because no leg named the console.
4237+
battery('THE COPY: a repo file a package build copies into what it packs (#15204)');
4238+
{
4239+
const CONSOLE = '@objectstack/console';
4240+
const SPEC = '@objectstack/spec';
4241+
const yesWidening = { value: 'yes', arm: 'widening', payload: true, readings: ['declaration line: `Clause-②: yes (widening)`'] };
4242+
const declaredNo = { value: 'no', arm: null, payload: true, readings: ['declaration line: `Clause-②: no`'] };
4243+
const ROOT_LINE = 'FRAMEWORK_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"';
4244+
const BUILD = [
4245+
'#!/usr/bin/env bash',
4246+
ROOT_LINE,
4247+
'PINNED_SHA="$(cat "${FRAMEWORK_ROOT}/.objectui-sha")"',
4248+
'BUILD_ROOT="${FRAMEWORK_ROOT}/.cache/objectui-${PINNED_SHA:0:12}"',
4249+
'TARGET="${FRAMEWORK_ROOT}/packages/console/dist"',
4250+
'CONSOLE_DIST="${BUILD_ROOT}/apps/console/dist"',
4251+
'cp -R "$CONSOLE_DIST" "$TARGET"',
4252+
'MANIFEST_SRC="${FRAMEWORK_ROOT}/sdui.manifest.json"',
4253+
'MANIFEST_RECORD="${FRAMEWORK_ROOT}/scripts/sdui-manifest.record.json"',
4254+
'cp "$MANIFEST_SRC" "${TARGET}/sdui.manifest.json"',
4255+
'',
4256+
].join('\n');
4257+
const pairs = (text) => JSON.stringify(packedCopiesIn(text));
4258+
const MANIFEST_PAIR = '[{"source":"sdui.manifest.json","destination":"packages/console/dist/sdui.manifest.json"}]';
4259+
4260+
// ── The reader ────────────────────────────────────────────────────────
4261+
assert(pairs(BUILD) === MANIFEST_PAIR, `the copy of a fixed repo file into a package dir is read, and ONLY it — got ${pairs(BUILD)}`);
4262+
assert(
4263+
!pairs(BUILD).includes('record'),
4264+
'control: a variable the script ASSIGNS but never copies (the manifest record, read for a warning line) is not a shipped file',
4265+
);
4266+
assert(
4267+
pairs(BUILD.replace(ROOT_LINE, 'FRAMEWORK_ROOT="/somewhere"')) === '[]',
4268+
'control: with no variable holding the script\'s own parent dir there is no repo root to resolve against, so nothing is claimed',
4269+
);
4270+
assert(
4271+
pairs(`${ROOT_LINE}\nT="\${FRAMEWORK_ROOT}/packages/a/dist"\nS="\${FRAMEWORK_ROOT}/x.json"\nS="\${FRAMEWORK_ROOT}/y.json"\ncp "$S" "$T/x.json"\n`) ===
4272+
'[{"source":"y.json","destination":"packages/a/dist/x.json"}]',
4273+
'assignments are evaluated in ORDER — a reassigned variable is read at the value it has on the `cp` line',
4274+
);
4275+
assert(
4276+
pairs(`${ROOT_LINE}\ncp "\${UNSET}/x.json" "\${FRAMEWORK_ROOT}/packages/a/dist/x.json"\n`) === '[]',
4277+
'control: a source that keeps an unexpanded `$` is not one fixed path and is skipped rather than guessed at',
4278+
);
4279+
4280+
// ⭐ THE PIN on the real script. The leg reads `build-console.sh`, so the
4281+
// day it stops copying the manifest (or respells the copy so the reader
4282+
// cannot see it) this row reds in lint instead of the leg retiring quietly.
4283+
const realBuild = readFileSync(resolve(REPO_ROOT, PACKED_COPY_BUILD_SCRIPTS[0]), 'utf8');
4284+
const realPairs = packedCopiesIn(realBuild);
4285+
assert(
4286+
realPairs.some((p) => p.source === 'sdui.manifest.json' && p.destination === 'packages/console/dist/sdui.manifest.json'),
4287+
`this repository's ${PACKED_COPY_BUILD_SCRIPTS[0]} copies the repo-root sdui.manifest.json into packages/console/dist/ as read by this leg — got ${JSON.stringify(realPairs)}`,
4288+
);
4289+
assert(
4290+
!realPairs.some((p) => p.source === 'scripts/sdui-manifest.record.json'),
4291+
`measured, not chosen: the real build does not ship the manifest record — got ${JSON.stringify(realPairs)}`,
4292+
);
4293+
4294+
// ── The owner of a destination ────────────────────────────────────────
4295+
const consoleManifest = (files) => (dir) => (dir === 'packages/console' ? { name: CONSOLE, version: '0.0.0', files } : null);
4296+
assert(packedCopyOwner('packages/console/dist/sdui.manifest.json', consoleManifest(['dist', 'README.md'])) === 'packages/console', 'a destination under a packed `files` entry names its package');
4297+
assert(packedCopyOwner('packages/console/dist/sdui.manifest.json', consoleManifest(undefined)) === 'packages/console', 'an absent `files` packs everything — npm\'s rule');
4298+
assert(packedCopyOwner('packages/console/dist/sdui.manifest.json', consoleManifest(['README.md'])) === null, 'control: a destination its package does not pack ships nothing, and owns nothing');
4299+
assert(packedCopyOwner('packages/console/dist/sdui.manifest.json', (dir) => (dir === 'packages/console' ? false : null)) === 'packages/console', '#4690: an unparseable manifest is a candidate that cannot be named, never "no owner"');
4300+
assert(packedCopyOwner('dist/sdui.manifest.json', () => ({ name: 'x' })) === null, 'control: a destination outside `packages/` is no workspace package');
4301+
4302+
// ── End to end, on real temp git repositories ─────────────────────────
4303+
const consoleJson = JSON.stringify({ name: CONSOLE, version: '0.0.0', files: ['dist', 'README.md', 'CHANGELOG.md'] });
4304+
const specJson = JSON.stringify({ name: SPEC, version: '0.0.0', files: ['dist'] });
4305+
const changeset = (entries) => `---\n${entries.map(([p, b]) => `"${p}": ${b}`).join('\n')}\n---\n\nbody\n`;
4306+
const copyRepo = ({ head, build = BUILD }) =>
4307+
makeRepo(
4308+
{
4309+
'scripts/build-console.sh': build,
4310+
'packages/console/package.json': consoleJson,
4311+
'packages/spec/package.json': specJson,
4312+
'packages/spec/src/ui/x.ts': '/** cites objectui@aaaa */\nexport const x = 1;\n',
4313+
'sdui.manifest.json': '{"components":["a","b"]}\n',
4314+
'scripts/sdui-manifest.record.json': '{"objectuiSha":"aaaa"}\n',
4315+
},
4316+
head,
4317+
);
4318+
const levelOf = ({ dir, base }, declaration) => {
4319+
const scanned = scan({ cwd: dir, base });
4320+
const touched = packagesTouched({ cwd: dir, from: scanned.base, head: 'HEAD' });
4321+
const result = judgeLevel({ levels: scanned.levels, touched, declaration });
4322+
return { touched, result, exitCode: renderLevel(result).exitCode };
4323+
};
4324+
const PR22812 = {
4325+
'sdui.manifest.json': '{"components":["a","b","c"]}\n',
4326+
'scripts/sdui-manifest.record.json': '{"objectuiSha":"bbbb"}\n',
4327+
'packages/spec/src/ui/x.ts': '/** cites objectui@bbbb */\nexport const x = 1;\n',
4328+
'.changeset/console-bbbb.md': changeset([[CONSOLE, 'minor']]),
4329+
'.changeset/citations-bbbb.md': changeset([[SPEC, 'patch']]),
4330+
};
4331+
4332+
// THE CARD: #22812's shape is DISCHARGED by the console `minor`.
4333+
const card = levelOf(copyRepo({ head: PR22812 }), yesWidening);
4334+
assert(card.touched.packages.join() === `${CONSOLE},${SPEC}` && card.touched.unreadable.length === 0, `THE CARD: the manifest move names ${CONSOLE} beside the spec — got ${JSON.stringify(card.touched)}`);
4335+
assert(card.result.verdict === 'discharged' && card.exitCode === 0, `THE CARD: console \`minor\` + spec \`patch\` + \`Clause-②: yes (widening)\` is discharged, exit 0 — got ${card.result.verdict} / ${card.exitCode}`);
4336+
assert(card.result.raised.some((r) => r.pkg === CONSOLE && r.bump === 'minor'), 'and it is the CONSOLE line that discharges it, not anything else');
4337+
4338+
// FIRING control: the same diff with the build script gone is the
4339+
// pre-leg reading — refused. So the green above is this leg, not a lane
4340+
// that moved.
4341+
const noLeg = levelOf(copyRepo({ head: { ...PR22812, 'scripts/build-console.sh': null } }), yesWidening);
4342+
assert(
4343+
!noLeg.touched.packages.includes(CONSOLE) && noLeg.result.verdict === 'enforce' && noLeg.exitCode === 1,
4344+
`FIRING control: without the copy to read, the console is unseen and the same PR is refused, exactly as CI job 114433350057 was — got ${JSON.stringify(noLeg.touched)} / ${noLeg.result.verdict}`,
4345+
);
4346+
4347+
// The manifest moved with NO console changeset at `minor` stays red —
4348+
// both when the console is graded `patch` and when it is not graded.
4349+
const consolePatch = levelOf(copyRepo({ head: { ...PR22812, '.changeset/console-bbbb.md': changeset([[CONSOLE, 'patch']]) } }), yesWidening);
4350+
assert(
4351+
consolePatch.result.verdict === 'enforce' && consolePatch.exitCode === 1 && consolePatch.result.offenders.some((o) => o.packages.includes(CONSOLE)),
4352+
`the manifest moved with the console graded \`patch\` under \`yes\` is REFUSED, naming the console — got ${consolePatch.result.verdict}`,
4353+
);
4354+
const manifestOnly = { 'sdui.manifest.json': PR22812['sdui.manifest.json'], '.changeset/console-bbbb.md': changeset([[CONSOLE, 'patch']]) };
4355+
const onlyPatch = levelOf(copyRepo({ head: manifestOnly }), yesWidening);
4356+
assert(
4357+
onlyPatch.touched.packages.join() === CONSOLE && onlyPatch.result.verdict === 'enforce' && onlyPatch.exitCode === 1,
4358+
`a manifest-only diff with the console at \`patch\` is refused on the console alone — got ${JSON.stringify(onlyPatch.touched)} / ${onlyPatch.result.verdict}`,
4359+
);
4360+
const ungraded = levelOf(copyRepo({ head: Object.fromEntries(Object.entries(PR22812).filter(([path]) => path !== '.changeset/console-bbbb.md')) }), yesWidening);
4361+
assert(
4362+
ungraded.result.verdict === 'enforce' && ungraded.exitCode === 1,
4363+
`the manifest moved with no console changeset at all (spec \`patch\` only) stays red — got ${ungraded.result.verdict}`,
4364+
);
4365+
4366+
// `Clause-②: no` is unaffected: the axis stands down whatever it sees.
4367+
const no = levelOf(copyRepo({ head: { ...PR22812, '.changeset/console-bbbb.md': changeset([[CONSOLE, 'patch']]) } }), declaredNo);
4368+
assert(no.result.verdict === 'not-declared' && no.exitCode === 0, `the manifest moved under \`Clause-②: no\` is unaffected — got ${no.result.verdict} / ${no.exitCode}`);
4369+
4370+
// The record moves with every regeneration but ships nothing.
4371+
const recordOnly = levelOf(
4372+
copyRepo({ head: { 'scripts/sdui-manifest.record.json': '{"objectuiSha":"bbbb"}\n', '.changeset/console-bbbb.md': changeset([[CONSOLE, 'patch']]) } }),
4373+
yesWidening,
4374+
);
4375+
assert(
4376+
recordOnly.touched.packages.length === 0 && recordOnly.touched.unreadable.length === 0 && recordOnly.result.verdict === 'clean',
4377+
`control: the record alone moves no published file — got ${JSON.stringify(recordOnly.touched)} / ${recordOnly.result.verdict}`,
4378+
);
4379+
4380+
// #4690 on this leg: a build script that is THERE but yields no copy is
4381+
// a reading not made, reported beside the verdict rather than read as
4382+
// "copies nothing"; and an unparseable destination manifest is unreadable.
4383+
const blind = levelOf(copyRepo({ head: manifestOnly, build: '#!/usr/bin/env bash\ninstall -m 644 sdui.manifest.json packages/console/dist/\n' }), yesWidening);
4384+
assert(
4385+
blind.touched.unreadable.includes('scripts/build-console.sh') && renderLevel(blind.result).stdout.join('\n').includes('scripts/build-console.sh'),
4386+
`a build script the reader yields no copy from is REPORTED and printed, never silent — got ${JSON.stringify(blind.touched)}`,
4387+
);
4388+
const broken = levelOf(copyRepo({ head: { ...manifestOnly, 'packages/console/package.json': '{ "name": \n' } }), yesWidening);
4389+
assert(
4390+
broken.touched.unreadable.includes('packages/console') && !broken.touched.packages.includes(CONSOLE),
4391+
`an unparseable console manifest lands the copy in \`unreadable\`, never in neither set — got ${JSON.stringify(broken.touched)}`,
4392+
);
4393+
}
4394+
42284395
} finally {
42294396
for (const dir of repos) rmSync(dir, { recursive: true, force: true });
42304397
}

0 commit comments

Comments
 (0)