Skip to content

Commit 9c8694b

Browse files
committed
tooling(cutover): check:published-files leaves the imported ui/ tree to its aftercare card (ruling K1, objectstack#25803)
Ruling K1 (maintainer, 2026-10-25 15:07Z, bill item 34): the cutover PR scopes this gate to objectstack's own packages, and the @object-ui manifests converge under aftercare objectstack#25753. Measured on the merged tree before the cutover, 39 of the imported publishable packages fail objectstack's publish policy (LICENSE listed in `files` x36, console plugin.* and app-shell src/styles.css published, CHANGELOG.md not whitelisted x3, no `exports` map x2), and the GATED census floor falls below half the 112 live publishable members. Conforming those manifests changes what the packages publish, which is the release lane's surface, so it is not done here. IMPORTED_TREE_OUT_OF_SCOPE = { root: 'ui', until: 'objectstack#25753' } and inImportedTree(dir); the gate walk and the self-test's GATED census both skip the tree, and the success line counts what was skipped. Six self-test cases pin ui, ui/packages/*, ui/apps/* in and packages/ui, packages/ui-kit, uix/* out, plus two guards (the root is a declared workspace root; `until` names a card). The ui/* ROOT_DIR_WATCH_HINTS stay: a manifest change under ui/ is still a true lead for this gate, the one that reds once the scope is lifted. Measured on 9e37acd + the series: - --self-test: exit 1 ("EXPORTS_CENSUS_FLOOR (50) is at least half the live publishable count (112)") -> exit 0 (floor 50 vs 73 of 73 live, 6 imported-tree scope cases). - the gate: exit 1 ("39 packages publish the wrong thing") -> exit 0 (73 publishable of 84 members; 47 members under ui/ not judged until objectstack#25753). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CUXv64KLMfYv3ia2sWi6te
1 parent 8d5ae1e commit 9c8694b

1 file changed

Lines changed: 70 additions & 0 deletions

File tree

‎scripts/check-published-files.mjs‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,37 @@ const workspaceGlobs = () => readWorkspaceGlobs(ROOT);
486486
/** Workspace member directories, relative to the repo root. */
487487
const workspaceDirs = () => workspacePackageDirs(ROOT);
488488

489+
/**
490+
* ── The imported objectui tree is out of scope for now (objectstack#25677) ──
491+
*
492+
* objectui arrives under `ui/` by `git subtree add` with its own publish
493+
* conventions, and this gate judges them by objectstack's: measured on the
494+
* merged tree before the cutover, 39 of its 41 publishable packages fail —
495+
* LICENSE listed in `files` (36), console `plugin.*` and app-shell
496+
* `src/styles.css` published (3), CHANGELOG.md not whitelisted (3), no
497+
* `exports` map (2) — and the GATED census floor falls below half of the
498+
* 112 live publishable members. Conforming 39 manifests changes what those
499+
* packages publish, which is the release lane's surface, so the maintainer
500+
* ruled (K1) that the cutover PR scopes this gate to objectstack's own
501+
* packages and the @object-ui manifests converge under the aftercare card
502+
* named in `until`. That card deletes this constant and the two `continue`s
503+
* that read it; nothing else in the gate knows the tree exists.
504+
*
505+
* The `ui/*` roots stay declared in ROOT_DIR_WATCH_HINTS above: the
506+
* declaration self-test reconciles the hints against the live
507+
* pnpm-workspace.yaml globs in both directions, and a manifest card under
508+
* `ui/` is still a true lead for this gate — it is the one that will red once
509+
* the scope is lifted.
510+
*/
511+
const IMPORTED_TREE_OUT_OF_SCOPE = Object.freeze({
512+
root: 'ui',
513+
until: 'objectstack#25753',
514+
});
515+
516+
/** True for the imported tree's root manifest and every member under it. */
517+
const inImportedTree = (dir) =>
518+
dir === IMPORTED_TREE_OUT_OF_SCOPE.root || dir.startsWith(`${IMPORTED_TREE_OUT_OF_SCOPE.root}/`);
519+
489520
/** Package-relative POSIX paths of every file that is not build output. */
490521
function walk(absDir, prefix = '', out = []) {
491522
for (const entry of readdirSync(absDir, { withFileTypes: true })) {
@@ -995,6 +1026,33 @@ function selfTest() {
9951026
expect(ok, `ROOT_DIR_WATCH_HINTS: ${name}`);
9961027
}
9971028

1029+
// ── the imported-tree scope (objectstack#25677, ruling K1) ────────────────
1030+
//
1031+
// Pinned in both directions: the scope must take the tree's root manifest
1032+
// and every member under it, and must take NOTHING that merely begins with
1033+
// the same letters, or a future `packages/ui-*` member slips out of the gate
1034+
// under a name that looks like a hit.
1035+
const scopeCases = [
1036+
['ui', true, "the imported tree's own root manifest"],
1037+
['ui/packages/core', true, 'a member under the imported tree'],
1038+
['ui/apps/console', true, 'an app under the imported tree'],
1039+
['packages/ui', false, 'an objectstack member whose basename is the root'],
1040+
['uix/core', false, 'a sibling root sharing a prefix'],
1041+
['packages/ui-kit', false, 'an objectstack member whose name starts with the root'],
1042+
];
1043+
for (const [dir, expected, why] of scopeCases) {
1044+
const actual = inImportedTree(dir);
1045+
expect(actual === expected, `inImportedTree("${dir}") === ${actual}, expected ${expected} (${why})`);
1046+
}
1047+
expect(
1048+
declaredRoots.includes(IMPORTED_TREE_OUT_OF_SCOPE.root),
1049+
`the scoped-out root "${IMPORTED_TREE_OUT_OF_SCOPE.root}" is one of the declared workspace roots (a scope on a tree the workspace does not have is dead text)`,
1050+
);
1051+
expect(
1052+
/^objectstack#\d+$/.test(IMPORTED_TREE_OUT_OF_SCOPE.until),
1053+
`the scope names the card that lifts it (is "${IMPORTED_TREE_OUT_OF_SCOPE.until}")`,
1054+
);
1055+
9981056
// ── GATED and its census floor (#12879) ───────────────────────────────────
9991057
battery('GATED and its census floor (#12879)');
10001058
//
@@ -1040,6 +1098,7 @@ function selfTest() {
10401098
let liveDeclaring = 0;
10411099
let livePublishable = 0;
10421100
for (const dir of workspaceDirs()) {
1101+
if (inImportedTree(dir)) continue;
10431102
let m;
10441103
try {
10451104
m = JSON.parse(readFileSync(join(ROOT, dir, 'package.json'), 'utf8'));
@@ -1392,6 +1451,7 @@ function selfTest() {
13921451
`population-declaration case(s), ${verdictCases.length} \`exports\` verdict case(s), ` +
13931452
`${floorCases.length} census-floor case(s) (floor ${EXPORTS_CENSUS_FLOOR} vs ` +
13941453
`${liveDeclaring} of ${livePublishable} live), ${cellCases.length} ANNOUNCED cell case(s), ` +
1454+
`${scopeCases.length} imported-tree scope case(s), ` +
13951455
`${resolveCases.length} subpath-resolution case(s), ${announceCases.length} announcement ` +
13961456
`case(s) and ${controlCases.length} base-read-control case(s) (plus the base reader itself, ` +
13971457
'exercised against this repository), and the shared workspace enumerator\'s own ' +
@@ -1423,7 +1483,13 @@ let members = 0;
14231483
let publishable = 0;
14241484
let exportsDeclaring = 0;
14251485

1486+
let scopedOut = 0;
1487+
14261488
for (const dir of workspaceDirs()) {
1489+
if (inImportedTree(dir)) {
1490+
scopedOut++;
1491+
continue;
1492+
}
14271493
const manifestPath = posix.join(dir, 'package.json');
14281494
let manifest;
14291495
try {
@@ -1776,6 +1842,10 @@ console.log(
17761842
'than dist/ + README.md + CHANGELOG.md, each with a registered reason; ' +
17771843
`${exportsDeclaring} declare an \`exports\` map gating what of that is resolvable ` +
17781844
`(census control: floor ${EXPORTS_CENSUS_FLOOR}); ` +
1845+
(scopedOut > 0
1846+
? `${scopedOut} member(s) under ${IMPORTED_TREE_OUT_OF_SCOPE.root}/ not judged until ` +
1847+
`${IMPORTED_TREE_OUT_OF_SCOPE.until}; `
1848+
: '') +
17791849
(announcedRan
17801850
? 'none narrows its resolvable surface against the merge base without a `minor` changeset ' +
17811851
'naming the deep paths that stop resolving.'

0 commit comments

Comments
 (0)