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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
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
4 changes: 4 additions & 0 deletions .changeset/truthfulness-guard-import-set-7117.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
---
---

Test-only change (objectui#7117): `exclusion-reason-truthfulness.test.ts` now imports the six `views/*-renderer.tsx` leaves and `@object-ui/plugin-detail`, so the three `PALETTE_EXCLUSIONS` keys that have a renderer outside the old import set (`app:launcher`, `global:notifications`, `record:chatter`) are actually visible to the guard, and a new renderer leaf that is not imported reds the suite instead of shrinking its coverage silently. No published behaviour changes.
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@
*
* ## Why the assertion is shaped this way
*
* The interesting direction is cheap to get wrong. Three hazards, each with its
* The interesting direction is cheap to get wrong. Four hazards, each with its
* own guard below:
*
* 1. **A degenerate (empty) registry passes every negative assertion.** If the
Expand All@@ -38,14 +38,58 @@
* contains a no-renderer claim to check` fails in that case.
* 3. **Scope is bounded by the import set.** A renderer registered in a
* package NOT imported here reads as unregistered, which would let a false
* "no renderer" pass. The imports below are therefore the packages that
* could plausibly register a page block for the excluded types
* (`@object-ui/components` for `element:*`, `@object-ui/plugin-chatbot` for
* the AI surface, `@object-ui/plugin-form` for the form family). Widen the
* set — and its positive probes — when a new package starts registering
* page blocks.
* "no renderer" pass.
* 4. **The import set drifting behind the registrations** — hazard 3 coming
* true silently, which is what objectui#7117 measured and what the last
* guard below now refuses. See the next section.
*
* ## objectui#7117 — the import set had already drifted, and nothing said so
*
* Hazard 3 was stated in prose here ("widen the set when a new package starts
* registering page blocks") and prose was not enough. `app-shell` became such a
* package twice — #6757 (`global:search`, `global:notifications`) and #7091
* (`app:launcher`, `nav:menu`) — and the set was not widened either time, so
* for two ledger keys this file could not see a renderer that exists. Measured
* on `44ea62d29`: setting `PALETTE_EXCLUSIONS['app:launcher']` to
* `'no renderer ZZMUTZZ'` — a string that DOES match {@link CLAIMS_NO_RENDERER}
* — still passed 4/4, while `views/app-launcher-renderer.tsx` registers a
* renderer for it. A guard passing the exact false claim it exists to refuse.
*
* Two things follow, and the second is the one that keeps this from recurring:
*
* - The set is widened to every package that registers a renderer for a
* CURRENT ledger key: the six `views/*-renderer.tsx` leaves of this package,
* and `@object-ui/plugin-detail` (which registers `record:chatter` — blind
* here for the same reason, measured the same way).
* - The app-shell half of the import set is CHECKED rather than trusted. `the
* import set covers every page block app-shell registers` derives the leaf
* list from the directory and fails when a leaf is not imported here — so
* the seventh renderer leaf reds this file instead of quietly shrinking its
* coverage. A hand-maintained list is exactly what drifted; deriving it is
* the point.
*
* Why the leaves and not the package barrel: `../../../../index.js` would track
* the package automatically, but it costs 6105ms to load against 553ms for the
* leaves (measured on `44ea62d29`, same harness) because it drags the console,
* marketplace, cloud and diagnostics graphs in with it. This file is a
* pure-logic gate in the cheap `unit` project, whose whole design point is not
* paying for graphs it does not touch (`vitest.config.mts`). The derived guard
* buys the barrel's one real advantage — tracking the package — for 0ms.
*
* ## What is deliberately NOT imported
*
* `registerPlaceholders()`. It registers the whole `PROTOCOL_COMPONENTS`
* vocabulary against `PlaceholderRenderer` under `namespace:
* 'protocol-placeholder'`, which would make this file answer "has a renderer"
* for types that have only the dashed "Component Placeholder" scaffold —
* `user:profile` is one (measured). This repo's own language is clear that the
* scaffold is not a renderer: `views/app-launcher-renderer.tsx` describes the
* state before it existed as "nothing rendered it, so a page that authored it
* drew a dashed box", with placeholders registered the whole time. Opting in
* would make the guard assert a falsehood in the opposite direction.
*/

import { readdirSync, readFileSync } from 'node:fs';
import { describe, it, expect } from 'vitest';
import { ComponentRegistry } from '@object-ui/core';
// Side-effect imports: these register the components under test. The app-shell
Expand All@@ -54,6 +98,21 @@ import { ComponentRegistry } from '@object-ui/core';
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
// `record:chatter` is a ledger key whose renderer lives here (registered
// alongside `record:discussion` against the same component). Both sibling
// suites in this directory already import it for the same reason.
import '@object-ui/plugin-detail';
// This package's own page-block registrations (#6757, #7091). All six
// `views/*-renderer.tsx` leaves, not just the two currently in the ledger:
// which of them the ledger names is a palette decision that has moved before,
// and `the import set covers every page block app-shell registers` below holds
// this list to the directory.
import '../../../app-launcher-renderer.js';
import '../../../global-notifications-renderer.js';
import '../../../global-search-renderer.js';
import '../../../nav-menu-renderer.js';
import '../../../record-approvals-renderer.js';
import '../../../record-attachments-renderer.js';
import { PALETTE_EXCLUSIONS } from '../block-types';

/**
Expand All@@ -67,11 +126,29 @@ const claimingNoRenderer = Object.entries(PALETTE_EXCLUSIONS).filter(([, reason]
CLAIMS_NO_RENDERER.test(reason),
);

/** Where this package keeps its page-block registrations, one block per file. */
const VIEWS_DIR = new URL('../../../', import.meta.url);

/**
* The page blocks this package registers, derived from the leaf files rather
* than restated — see the header. `register('menu', C, { namespace: 'nav' })`
* writes the key `nav:menu`, so the two captures spell the key the ledger and
* the registry both use.
*/
const appShellRegistrations = readdirSync(VIEWS_DIR)
.filter((f) => f.endsWith('-renderer.tsx'))
.map((file) => {
const src = readFileSync(new URL(file, VIEWS_DIR), 'utf8');
const m = /ComponentRegistry\.register\(\s*'([^']+)'[\s\S]{0,400}?namespace:\s*'([^']+)'/.exec(src);
return { file, key: m ? `${m[2]}:${m[1]}` : null };
});

describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"', () => {
it('the registry under test is actually populated (guards a vacuous green)', () => {
// One probe per side-effect import above. If any of these is falsy the
// negative assertion below proves nothing, so it must fail LOUDLY here
// rather than passing quietly there.
// rather than passing quietly there. (The six app-shell leaves get their
// probes from the derived guard below, which cannot be forgotten.)
expect(
ComponentRegistry.get('element:text'),
'@object-ui/components did not register — every "not registered" check below would pass vacuously',
Expand All@@ -84,6 +161,46 @@ describe('objectui#6071 — PALETTE_EXCLUSIONS reasons that claim "no renderer"'
ComponentRegistry.get('object-form'),
'@object-ui/plugin-form did not register — the form family is not actually covered',
).toBeTruthy();
expect(
ComponentRegistry.get('record:chatter'),
'@object-ui/plugin-detail did not register — `record:chatter` is a ledger key and would read as unregistered',
).toBeTruthy();
});

it('the import set covers every page block app-shell registers (objectui#7117)', () => {
// The guard the prose maintenance rule failed to be. A new
// `views/<block>-renderer.tsx` that this file does not import reads as
// unregistered, which is precisely how #6757 and #7091 left two ledger keys
// unguarded for two releases.
//
// Control first: a derivation that finds nothing would pass every check
// under it vacuously, and this file's whole subject is a guard that stopped
// seeing its population.
expect(
appShellRegistrations.length,
'no views/*-renderer.tsx leaves were found — this coverage check has stopped checking',
).toBeGreaterThan(0);
expect(
appShellRegistrations.filter((r) => r.key === null).map((r) => r.file),
'a renderer leaf whose ComponentRegistry.register(...) call could not be read — the key below cannot be derived',
).toEqual([]);
// Anchor the derivation on the two keys objectui#7117 measured, so a
// regex that silently stops matching cannot leave this green.
expect(
appShellRegistrations.map((r) => r.key),
'the derived registration list no longer contains the keys #7117 was filed about',
).toEqual(expect.arrayContaining(['app:launcher', 'global:notifications']));

for (const { file, key } of appShellRegistrations) {
expect(
ComponentRegistry.get(key as string),
`views/${file} registers '${key}' and this file does not import it, so '${key}' reads as UNREGISTERED here. ` +
"Add `import '../../../" +
file.replace(/\.tsx$/, '.js') +
"';` to the side-effect imports above. Until then, an exclusion reason claiming " +
`'no renderer' over '${key}' would pass this suite — the defect objectui#7117 filed.`,
).toBeTruthy();
}
});

it('the ledger still contains a no-renderer claim to check (guards a vacuous loop)', () => {
Expand Down
Loading