Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions apps/console/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -739,11 +739,59 @@ export default defineConfig({
{ name: 'vendor-i18n', test: /[\\/]node_modules[\\/](i18next|react-i18next)[\\/]/, priority: 90 },
// Workspace packages — match by realpath, since pnpm may resolve
// through node_modules/@object-ui/<pkg> symlinks to packages/<pkg>.
//
// ## Why two tiers and not one (objectui#7399)
//
// These five groups were ALL `priority: 80` with `framework` written
// first, and that tie was NOT benign. Measured on `e307c9896`, the
// emitted `framework` chunk held 166 modules and only 145 of them
// came from `core|react|types` — its own test:
//
// | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
// | packages/core | 67 mod | 9.6% |
// | packages/react | 63 mod | 4.7% |
// | packages/data-objectstack | 5 mod | 4.6% |
// | packages/types | 15 mod | 2.3% |
//
// `framework`'s regex does not match EITHER of the two intruders, and
// `data-adapter` — declared below since objectui#5490 — emitted no
// chunk at all. On a tie the subgraph reached through
// `@object-ui/react` was absorbed by the group listed first, so
// `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on
// the TRANSLATION CATALOGUE: 523,959 gzipped bytes against a 524,000
// ceiling, 41 bytes of headroom for the whole repository, and a gate
// whose message ("don't grow core|react|types") pointed away from its
// own cause. Five PRs adding ruled user-facing copy were parked on it.
//
// So the two groups the tie was swallowing are lifted one tier ABOVE
// it, and `i18n` leaves `infrastructure`'s alternation because a
// dedicated group now owns it (a dead alternative in a live regex is
// the next silent drift). Tier 84 sits above the general workspace
// groups and below the vendor tier at 85+; its two members' tests are
// disjoint from each other and from every vendor test, so lifting
// them introduces no new tie.
//
// ⛔ This moves NO MODULE BYTES. The eager closure holds the same
// module set before and after; what changes is which file each
// module is written to. Measured cost of the extra chunk
// boundaries: eager closure 3,255,233 -> 3,256,012 gzipped, +779 B
// (+0.024%), raw +837 B — all of it `import` bookkeeping in the
// chunks that now name two files where they named one. Nothing here
// may be read as headroom that was earned.
//
// ⚠️ Disclosed rather than smoothed over: `data-adapter` now also
// holds 5 modules (8.5 KB raw) from `core`/`types` that are reached
// ONLY through `data-objectstack`. That is the same shared-module
// pull-in this comment is about, one tier down and three orders of
// magnitude smaller. It is rolldown's behaviour, not a choice
// available here: `framework` cannot be lifted above these two
// without re-absorbing the catalogue, which is the whole defect.
{ name: 'i18n-locales', test: /[\\/]packages[\\/]i18n[\\/]/, priority: 84 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 84 },
{ name: 'framework', test: /[\\/]packages[\\/](core|react|types)[\\/]/, priority: 80 },
{ name: 'ui-components', test: /[\\/]packages[\\/](components|fields)[\\/]/, priority: 80 },
{ name: 'ui-layout', test: /[\\/]packages[\\/]layout[\\/]/, priority: 80 },
{ name: 'data-adapter', test: /[\\/]packages[\\/]data-objectstack[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant|i18n)[\\/]/, priority: 80 },
{ name: 'infrastructure', test: /[\\/]packages[\\/](auth|permissions|tenant)[\\/]/, priority: 80 },
// Plugins — one chunk per plugin so dynamic imports cleave cleanly.
{ name: 'plugin-grid', test: /[\\/]packages[\\/]plugin-grid[\\/]/, priority: 70 },
{ name: 'plugin-form', test: /[\\/]packages[\\/]plugin-form[\\/]/, priority: 70 },
Expand Down
150 changes: 148 additions & 2 deletions scripts/__tests__/check-eager-closure-budget.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -389,6 +389,146 @@ describe('per-chunk ceilings', () => {
* plus the three per-chunk lines objectui#5490 added), each weighed against its
* own measurement in the report.
*/
/**
* The composition pin (objectui#7399).
*
* These ceilings are keyed on chunk NAMES, and until this card nothing checked
* that a name still described its contents. It did not: `framework`'s group
* test is `packages/(core|react|types)`, and the emitted `framework` chunk held
* all ten `packages/i18n` locale catalogues — 78.7% of its bytes — because five
* workspace groups shared `priority: 80` and `framework` was written first. So
* `PER_CHUNK_GZIP_CEILINGS['framework']` was in operation a budget on the
* translation catalogue, with 41 bytes of headroom, and its failure message
* named the wrong cause.
*
* ⚠️ This is deliberately a check on the DECIDING INPUT, not on prose. The
* config's own comment said `core|react|types` throughout the defect and was
* true about the regex the whole time; what was false was the assumption that a
* group only takes what its regex matches. The predicate below is the one that
* was actually violated — a TIE between a group whose test matches the
* catalogue and one whose test does not.
*
* The byte-level backstop is the re-baselined ceiling itself: `framework` is
* now pinned at 71,000 over a 61,465 payload, so a regression that puts the
* 446 KB catalogue back would red the gate six times over. That verdict is
* loud but mute about the cause. This one names it.
*
* ⛔ Every case here fails CLOSED. The parse yielding nothing, or a probe id
* matching no group at all, is an ERROR and not a pass — a matcher that matches
* nothing agrees with a correctly-attributed bundle on every assertion below.
*/
describe('chunk attribution (objectui#7399)', () => {
/** A group as `advancedChunks.groups` declares it. */
type Group = { name: string; priority: number; test: RegExp | null };

/**
* Parse the groups out of the console's vite config.
*
* `test` is `null` for a group whose test is an IDENTIFIER rather than a
* regex literal (`vendor-objectstack` reads a computed test, so that the
* `OBJECTSTACK_SPEC_DIST` override cannot change the chunk layout —
* objectui#5388). Those are refused a verdict below rather than guessed at.
*/
function parseGroups(): Group[] {
const source = fs.readFileSync(viteConfigPath, 'utf8');
const entry =
/\{\s*name:\s*'([^']+)',\s*test:\s*(\/(?:[^/\\\n]|\\.|\[[^\]\n]*\])+\/[a-z]*|[A-Za-z_$][\w$]*)\s*,\s*priority:\s*(\d+)\s*\}/g;
return [...source.matchAll(entry)].map(([, name, test, priority]) => {
const literal = /^\/(.*)\/([a-z]*)$/s.exec(test);
return {
name,
priority: Number(priority),
test: literal ? new RegExp(literal[1], literal[2]) : null,
};
});
}

const groups = parseGroups();

/** Rolldown matches group tests against REALPATHS, measured on objectui#7399. */
const moduleId = (relative: string) => path.join(repoRoot, relative);

const LOCALE_MODULE = moduleId('packages/i18n/src/locales/zh-CN.ts');
const DATA_MODULE = moduleId('packages/data-objectstack/src/index.ts');
const CORE_MODULE = moduleId('packages/core/src/index.ts');

/** The groups whose test matches this id, highest priority first. */
function claimants(id: string): Group[] {
return groups
.filter((g) => g.test?.test(id))
.sort((a, b) => b.priority - a.priority);
}

describe('the parse itself — a matcher that matches nothing agrees with everything', () => {
it('finds the whole group table, not a fragment of it', () => {
// ~35 groups are declared. A reformat that breaks this parse must red
// here rather than quietly reduce every case below to a tautology.
expect(groups.length).toBeGreaterThan(20);
expect(groups.map((g) => g.name)).toEqual(expect.arrayContaining([
'framework',
'i18n-locales',
'data-adapter',
'ui-components',
'infrastructure',
]));
});

it('reads a control module to the group that owns it', () => {
expect(claimants(CORE_MODULE)[0]?.name).toBe('framework');
});

it('refuses a verdict on a group whose test it could not read', () => {
// Exactly one group takes a computed test today. A second one appearing
// reds this case, because such a group could claim the probe ids below
// without this parse ever seeing it.
expect(groups.filter((g) => g.test === null).map((g) => g.name)).toEqual([
'vendor-objectstack',
]);
});
});

describe('the defect this pin exists to stop', () => {
it('`framework`s test matches NEITHER intruder — which is why the config read as correct', () => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework?.test?.test(LOCALE_MODULE)).toBe(false);
expect(framework?.test?.test(DATA_MODULE)).toBe(false);
});

it.each([
['the locale catalogue', LOCALE_MODULE, 'i18n-locales'],
['the ObjectStack data adapter', DATA_MODULE, 'data-adapter'],
])('routes %s to `%s` at a priority `framework` cannot tie', (_what, id, expected) => {
const framework = groups.find((g) => g.name === 'framework');
expect(framework).toBeDefined();

const claiming = claimants(id);
// Fails closed: no claimant is an error, never a silent pass.
expect(claiming.length).toBeGreaterThan(0);
expect(claiming[0].name).toBe(expected);

// The pin. A TIE is what put the catalogue in `framework`, so equality
// here is a failure exactly like inversion is.
expect(claiming[0].priority).toBeGreaterThan(framework!.priority);
});

it('leaves no second claimant at the winner`s priority', () => {
for (const id of [LOCALE_MODULE, DATA_MODULE]) {
const claiming = claimants(id);
const top = claiming[0].priority;
expect(claiming.filter((g) => g.priority === top)).toHaveLength(1);
}
});
});

it('budgets the chunk the catalogue now lands in', () => {
// A re-attribution that moved 446 KB into a chunk with no ceiling would
// pass every case above while weakening the gate: the aggregate is the only
// line left over those bytes, and it is the loosest one.
expect(PER_CHUNK_GZIP_CEILINGS).toHaveProperty('i18n-locales');
expect(claimants(LOCALE_MODULE)[0].name).toBe('i18n-locales');
});
});

describe('ceiling sensitivity, judged live (objectui#5924)', () => {
/**
* A v2 report totalling exactly `totalGzipBytes`, carrying the budgeted
Expand DownExpand Up@@ -645,10 +785,16 @@ describe('main', () => {
// the checker weighs BOTH halves, and a report missing the budgeted chunks is
// an error — which is the per-chunk half working, not a fixture detail.
it('exits 0 and publishes the measurement when within budget', () => {
const { code, outputs } = run(budgeted());
const fixture = budgeted();
const { code, outputs } = run(fixture);
expect(code).toBe(0);
expect(outputs.closure_status).toBe('pass');
expect(outputs.closure_chunks).toBe('5');
// DERIVED from the fixture, not retyped. This was the literal `'5'`, which
// counted the budgeted chunks plus `index` and `rest-of-closure` — so
// objectui#7399 adding a fourth per-chunk ceiling reddened an assertion
// about the FIXTURE while the gate under test behaved correctly. The number
// this case is actually about is "the report's chunk count, echoed".
expect(outputs.closure_chunks).toBe(String(fixture.files.length));
expect(outputs.closure_gzip_kb).toBe('3146.8');
});

Expand Down
99 changes: 90 additions & 9 deletions scripts/check-eager-closure-budget.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,6 +386,76 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
* headroom convention rather than widening it: 9,137 bytes (0.10x), against the
* 9,595 (0.11x) the retired pair carried. objectui#6631 still owns the drift.
*
* ## Why `framework` moved DOWN, and where its bytes went — objectui#7399
*
* From 524,000 over 514,863 to 71,000 over 61,465, and a fourth line appears:
* `i18n-locales`, 455,000 over 446,076. Neither number is a payload change. The
* console downloads the same eager closure before and after; it is written to
* two more files.
*
* What this ceiling was actually weighing, measured on `e307c9896` from the
* emitted chunk's own module list — the `framework` chunk held 166 modules and
* only 145 of them came from `core|react|types`, its own test:
*
* | packages/i18n | 16 mod | 78.7% of the chunk's bytes |
* | packages/core | 67 mod | 9.6% |
* | packages/react | 63 mod | 4.7% |
* | packages/data-objectstack | 5 mod | 4.6% |
* | packages/types | 15 mod | 2.3% |
*
* Five workspace groups in `apps/console/vite.config.ts` sat at `priority: 80`
* with `framework` written first, and on that tie the subgraph reached through
* `@object-ui/react` was absorbed by the group listed first — a group whose
* regex matches NEITHER intruder. So this line was, in operation, a budget on
* the TEN LOCALE CATALOGUES: 523,959 measured against 524,000, forty-one bytes
* of headroom for the whole repository, while `data-adapter` — declared since
* objectui#5490 — emitted no chunk at all.
*
* ⛔ The expensive half is not the arithmetic. A gate whose message says "you
* grew `core|react|types`" when the cause is a translation key teaches a FALSE
* RULE, and it was followed: two changes were publicly blamed for bytes they do
* not ship (objectui#7399's own retraction). Naming the chunk for what it holds
* is what makes the constraint legible where it is authored.
*
* Lifting the two swallowed groups one tier above the tie (priority 84) gives
* each ceiling a subject its name states. Measured across the change:
*
* | chunk | before | after |
* | `framework` | 523,959 | 61,465 | 166 modules -> 140, all core/react/types
* | `i18n-locales` | — | 446,076 | 22 modules, 100% packages/i18n
* | `data-adapter` | — | 17,846 | 10 modules, 91.8% data-objectstack
* | aggregate |3,255,233|3,256,012| +779 B, +0.024%
*
* The +779 is the cost of two more chunk boundaries — `import` statements in
* the chunks that now name two files where they named one — and it is stated
* here because it is the ONE thing this change spends. ⛔ It is not headroom to
* borrow against, and {@link MAX_EAGER_CLOSURE_GZIP_BYTES} was deliberately NOT
* touched: the maintainer ruling of 2026-09-03 authorised a re-attribution and
* the per-chunk re-baseline it forces, and nothing else. Its own words:
*
* ⛔ The aggregate ceiling is not touched. A′ is a pure re-attribution —
* the browser downloads exactly the same bytes.
*
* ⚠️ Both new pairs are why the re-baseline is not optional. Left at 524,000
* over a 61,465 payload, `framework` sits 5.08x above its own measurement and
* {@link evaluateHeadroomSensitivity} returns exit 2 — the blind-gauge verdict,
* correctly. The new pairs keep this line's own headroom convention rather than
* widening it: 9,535 bytes (0.10x) for `framework`, 8,924 (0.10x) for
* `i18n-locales`, against the 9,137 (0.10x) the retired `framework` pair
* carried.
*
* `data-adapter` gets no ceiling. 17,846 bytes is smaller than a dozen
* unbudgeted eager chunks, and this constant is a line per BIG chunk, not a
* line per named group; inventing one for it would be a number with no
* incident behind it.
*
* ⭐ Read the new `i18n-locales` headroom for what it is. 8,924 bytes is about
* sixty translation keys at the measured ~147 gzipped bytes a short key costs
* across ten locales — enough for the five PRs this unparked, and then the
* AGGREGATE line (11,988 bytes of headroom) becomes the binding one. That is the
* correct place for the constraint to live, and it is the argument for taking
* the catalogues out of the eager closure rather than for raising anything.
*
* ## Raising one
*
* Same discipline as {@link MAX_EAGER_CLOSURE_GZIP_BYTES}, and the same two
Expand All@@ -401,7 +471,8 @@ export const REGRESSION_THIS_GATE_MUST_CATCH_BYTES = 89 * 1024;
*/
export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
'vendor-objectstack': 967_000,
framework: 524_000,
'i18n-locales': 455_000,
framework: 71_000,
'ui-components': 399_000,
});

Expand All@@ -412,13 +483,22 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
* three numbers taken on two is the drift objectui#6631 is open about:
*
* - `vendor-objectstack`, `ui-components` — `2c8474c04` (objectui#5490).
* - `framework` — the merged head of objectui#7173's branch; see "Why
* `framework` moved again" above for the three-build attribution. It
* supersedes `a64e96ca8` (objectui#6759), whose reading the paragraph above
* that one still explains. Safe to state rather than hope, for the reason {@link BASELINE}
* gives about its own commit: the console build's turbo `inputs` cover
* `scripts/vite-*.ts`, not `scripts/check-*.mjs`, so the commit that
* records this figure cannot have changed the figure.
* - `framework`, `i18n-locales` — `e307c9896` plus objectui#7399's own
* re-attribution diff; see "Why `framework` moved DOWN" above. Both were
* read from ONE console build, so they are directly comparable to each
* other and to the 523,959 the same tree measured with the groups still
* tied. This `framework` reading supersedes objectui#7173's, whose
* three-build attribution the paragraph above that one still explains, and
* objectui#6759's `a64e96ca8` before it.
*
* ⚠️ Unlike every other entry here, these two are NOT a reading of an
* unmodified tree: the chunks they name do not exist without the diff that
* recorded them, because that diff is what creates the second one. The
* `scripts/vite-*.ts`-versus-`scripts/check-*.mjs` argument {@link BASELINE}
* makes about its own commit does NOT cover them — `apps/console/vite.config.ts`
* IS a build input, deliberately, and moving it is the change. What keeps
* them honest instead is that the gate re-reads them on every CI build of
* the branch that carries the diff.
*
* Exported so the ceilings are CHECKED against it instead of merely asserted
* in this comment.
Expand DownExpand Up@@ -482,7 +562,8 @@ export const PER_CHUNK_GZIP_CEILINGS = Object.freeze({
*/
export const PER_CHUNK_BASELINE = Object.freeze({
'vendor-objectstack': 948_461,
framework: 514_863,
'i18n-locales': 446_076,
framework: 61_465,
'ui-components': 391_095,
});

Expand Down
Loading