From 94e842dad1f713f574eedddef28bccadaa015bfa Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 7 Sep 2026 10:13:05 +0000 Subject: [PATCH] docs(readme): compile the root README's snippets against the shipped surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root README's five ts/tsx fences held six semantic diagnostics across three blocks, and the page was the LAST row on `UNGATED_DOCS`. Each block is its own module in the gate's program, so each repair is local: * fence 99 (minimal integration) declared `myAPI` and `MySidebar` — the two pieces a reader brings — as typed stand-ins: `DataSource` from `@object-ui/types`, which is what `ObjectView.dataSource` REQUIRES further down the same block, and React's `FC`, because `AppShellProps.sidebar` takes a `ReactNode` and the page renders ``. * fence 382 imports its own `SchemaRenderer` instead of continuing an earlier block, and declares `schema` as `BaseSchema` — what `SchemaRendererProps` accepts. * fence 398's `// ... other methods` elision (TS2420 + TS2355) became an ambient `declare class … implements DataSource` carrying all six required members with no bodies: the contract complete instead of hidden, and re-judged by the gate whenever `DataSource` moves. * fence 137's `const schema` literal was measured UNBOUND — neither its member names nor their values were checked by anything — and is now annotated `ObjectFormSchema`. The ledger row is deleted, so `UNGATED_DOCS` reaches `{}` and the gate prints `0 ungated`. Its declaration, header, readers, summary line and export are untouched, and the strictness region hashes byte-identical to the base. The objectui#7115 pin flips with the row: from "DECLARED debt" to covered AND contributing blocks to the compiled tier, so leaving the ledger with no block left cannot pass for coverage. Part of #5174 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr --- README.md | 31 ++++++++-- .../__tests__/check-doc-snippet-types.test.ts | 32 +++++++--- scripts/check-doc-snippet-types.mjs | 61 ++++++------------- 3 files changed, 65 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index b6c8467fdf..d086dd5d0e 100644 --- a/README.md +++ b/README.md @@ -97,9 +97,16 @@ npm install @object-ui/app-shell @object-ui/plugin-view @object-ui/providers Then build your own console in ~100 lines: ```tsx +import type { FC } from 'react'; import { AppShell } from '@object-ui/app-shell'; import { ObjectView } from '@object-ui/plugin-view'; import { ThemeProvider, DataSourceProvider, useDataSource } from '@object-ui/providers'; +import type { DataSource } from '@object-ui/types'; + +// The two pieces you bring: the backend adapter you implement (see "Custom +// Data Sources" below) and your own sidebar component. +declare const myAPI: DataSource; +declare const MySidebar: FC; function MyConsole() { return ( @@ -135,13 +142,15 @@ See [examples/byo-backend-console](examples/byo-backend-console) for a complete **Stop Writing Repetitive UI Code** ```tsx +import type { ObjectFormSchema } from '@object-ui/types'; + // Traditional React: 200+ lines function UserForm() { // ... useState, validation, handlers, JSX } // Object UI: 20 lines -const schema = { +const schema: ObjectFormSchema = { type: "object-form", objectName: "user", mode: "create", @@ -381,6 +390,11 @@ npm install @object-ui/data-objectstack ```typescript import { createObjectStackAdapter } from '@object-ui/data-objectstack'; +import { SchemaRenderer } from '@object-ui/react'; +import type { BaseSchema } from '@object-ui/types'; + +// Your page schema — "Basic Usage" above writes one out in full. +declare const schema: BaseSchema; const dataSource = createObjectStackAdapter({ baseUrl: 'https://api.example.com', @@ -398,11 +412,16 @@ You can create adapters for any backend (REST, GraphQL, Firebase, etc.) by imple ```typescript import type { DataSource, QueryParams, QueryResult } from '@object-ui/types'; -class MyCustomDataSource implements DataSource { - async find(resource: string, params?: QueryParams): Promise { - // Your implementation - } - // ... other methods +// The members `DataSource` REQUIRES — declared here without bodies, so the +// contract is complete instead of elided. Every other member of the interface +// is optional: implement the ones your backend supports. +declare class MyCustomDataSource implements DataSource { + find(resource: string, params?: QueryParams): Promise>; + findOne(resource: string, id: string | number, params?: QueryParams): Promise; + create(resource: string, data: Partial): Promise; + update(resource: string, id: string | number, data: Partial, opts?: { ifMatch?: string }): Promise; + delete(resource: string, id: string | number, opts?: { ifMatch?: string }): Promise; + getObjectSchema(objectName: string): Promise; } ``` diff --git a/scripts/__tests__/check-doc-snippet-types.test.ts b/scripts/__tests__/check-doc-snippet-types.test.ts index b807369a41..dd9d620866 100644 --- a/scripts/__tests__/check-doc-snippet-types.test.ts +++ b/scripts/__tests__/check-doc-snippet-types.test.ts @@ -584,21 +584,35 @@ describe('this repository', () => { * `check-doc-component-types.mjs` walked `content/docs`, and the repository's * landing page fell between them. * - * ⚠️ Read the second assertion carefully. Being ON the ungated ledger is NOT a - * claim that this file compiles — it does not; objectui#7417 carries its nine - * measured diagnostics. It is the objectui#5174 distinction, which this script's - * own header states: a document outside the walk is "neither covered NOR - * declared ungated", invisible to the gate's own accounting, while a ledgered - * one is named, counted, re-derived every run and shrink-only. + * ⚠️ Read the second assertion carefully — and read what it USED to say, because + * the flip is the point. It pinned this page as DECLARED debt: on the ungated + * ledger, with a reason naming objectui#7417. That was never a claim the page + * compiled; it was the objectui#5174 distinction this script's own header states + * — a document outside the walk is "neither covered NOR declared ungated", + * invisible to the gate's own accounting, while a ledgered one is named, + * counted, re-derived every run and shrink-only. + * + * objectui#5174's last batch paid the debt down: the page's five ts/tsx blocks + * compile against the built `dist/*.d.ts`, so the row came off and the ledger + * reached ZERO. The assertion therefore pins the far end of that walk — NOT on + * the ledger, and actually contributing blocks to the compiled tier. Both halves + * are load-bearing: a page can leave the ledger by having no ts/tsx block left + * at all, which is coverage of nothing, and only the second half tells the two + * apart. */ describe('objectui#7115 — the root README is in the scan set', () => { it('listDocuments reaches it', () => { expect(listDocuments(repoRoot)).toContain('README.md'); }); - it('is DECLARED debt rather than absent, and its reason names the card that carries it', () => { - expect(Object.keys(UNGATED_DOCS as Record)).toContain('README.md'); - expect((UNGATED_DOCS as Record)['README.md']).toContain('objectui#7417'); + it('is COVERED and actually judged — off the ledger, with blocks in the compiled tier', () => { + expect(Object.keys(UNGATED_DOCS as Record)).not.toContain('README.md'); + const state = analyze({}); + expect(state.covered as string[]).toContain('README.md'); + expect( + (state.compiled as Array<{ doc: string }>).filter((b) => b.doc === 'README.md').length, + 'off the ledger with no block left would be coverage of nothing', + ).toBeGreaterThan(0); }); it('root pages are collected BY NAME, not by the packages walk', () => { diff --git a/scripts/check-doc-snippet-types.mjs b/scripts/check-doc-snippet-types.mjs index 4a75cc3b32..b99a56855b 100644 --- a/scripts/check-doc-snippet-types.mjs +++ b/scripts/check-doc-snippet-types.mjs @@ -615,18 +615,22 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']); * Documents whose snippets are NOT compiled, each with the reason. The default * is covered; this list is the debt, by name, and it can only shrink. * - * ⚠️ ZERO of these entries are now `.md` pages under `content/docs`. There were 19 - * when objectui#5174 made them visible — the collector reads `.md`, and an entry - * with a measured reason is what a page that cannot pass yet is owed — and that - * card then walked every one of them back OFF this list rather than re-wording its - * reason. The direction on that card was entries LEAVING, and it finished: what - * remains here is `.mdx` pages under `content/docs` plus package READMEs, and the - * ENTRIES BELOW are that list — re-derived every run and shrink-only, so the names - * in it are the count. This sentence carried the literal `12 .mdx pages and 32 - * package READMEs` until objectui#5174's batch 7, by which point BOTH halves had - * drifted: the README count has been 31 since objectui#5259, and the `.mdx` count - * moves with every batch. Nothing fails on a stale number written here, which is - * why it is a pointer to the list now rather than a copy of its length. + * ⚠️ The ledger is EMPTY, and that is objectui#5174's finished state rather than a + * gap: every document the collector reaches is in the covered tier, so the default + * is now the only tier. There were 19 `.md` entries under `content/docs` when that + * card made them visible — the collector reads `.md`, and an entry with a measured + * reason is what a page that cannot pass yet is owed — and the card then walked + * every one of them, then the `.mdx` pages, then the package READMEs, and last the + * root `README.md`, back OFF this list rather than re-wording their reasons. Each + * page left by compiling, never by softening this gate. + * + * ⛔ An empty object is NOT an invitation to park the next page that fails. A new + * entry is new debt and owes the same thing every entry above owed: a reason that + * says WHAT would have to change, measured on the page rather than estimated. The + * sentence this replaces carried the literal `12 .mdx pages and 32 package + * READMEs` until objectui#5174's batch 7, by which point BOTH halves had drifted; + * nothing fails on a stale number written here, which is why no count is written + * here at all. * * Batch 1 took ten: `api/schema-reference`, `plugins/index`, and the `guide/` pages * `architecture-overview`, `deployment`, `expressions`, `notifications`, @@ -744,38 +748,7 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']); * * @type {Record} */ -const UNGATED_DOCS = { - // objectui#7115 put the root README into the scan surface; this entry is what - // that bought on THIS gate's question. The file is now VISIBLE to the ledger - // instead of invisible to the walk — the objectui#5174 distinction quoted in - // the header — and the debt below is measured, not estimated. ⚠️ Read as debt, - // never as a pass: these 6 diagnostics are real. - // - // It read 9, and named the three TS2305s as the ones that mattered, until - // objectui#7417 paid exactly those down — three names the page taught that no - // built `dist/index.d.ts` exports. What replaced each was already in the tree, - // so none of the three widened a public surface: `ObjectRenderer` (no export of - // @object-ui/app-shell bears that name; the page now composes `ObjectView` from - // @object-ui/plugin-view, the spelling examples/byo-backend-console/src/App.tsx - // already runs), `registerDefaultRenderers` (@object-ui/components registers its - // renderers as an import side effect — `sideEffects: true`, and its barrel's - // `import './renderers'` — and exports no such function, so the page now imports - // the package for the side effect), and `createObjectStackAdapter`, which ships - // from @object-ui/data-objectstack, not @object-ui/core, exactly as - // packages/plugin-dashboard/README.md already writes it. - // - // ⚠️ The remaining 6 are fragment shape, and no gate protects this page's - // import names from a fourth phantom: check-readme-exports.mjs states its - // surface as `packages/NAME/README.md`, and the root README imports from - // several packages rather than owning one, so that gate's rule would have to be - // restated before its surface could move (objectui#7417 triage). - 'README.md': - '4 undefined-name diagnostic(s) — blocks use ambient names the page never defines (`myAPI`, ' + - '`MySidebar`) or continue an earlier block (`SchemaRenderer`, `schema`); 2 elided-body ' + - 'diagnostic(s) (TS2420, TS2355) — a `DataSource` implementation written as `// ... other ' + - 'methods`. This entry read 9 until objectui#7417 paid down the three TS2305s it carried; ' + - 'what is left is fragment shape, and no gate reads this page\'s import names.', -}; +const UNGATED_DOCS = {}; // ── Fence scanning ───────────────────────────────────────────────────────────