Skip to content

Commit d2b97c3

Browse files
docs(spec): give six zod modules a true module-header doc block (#6145) (#6447)
The six modules already carried a genuine module introduction, written glued to the module's first declaration. Under #5059's strict selection rule that block is the SYMBOL's TSDoc — its hover text — so the pages stopped opening with it and printed nothing instead. The prose was never the problem; its attachment was. Each block is promoted VERBATIM to a top-level header that documents no symbol (column 0, header region, followed by the imports rather than by a declaration) — the shape 74 of the 183 sources with a module header already use. `content/docs/references/**` is regenerated through the standard `check:generated --fix` flow: exactly the six listed pages change, 110 insertions and 0 deletions, every other page byte-identical. Fixes #6145 Claude-Session: https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9bc846b commit d2b97c3

14 files changed

Lines changed: 209 additions & 47 deletions

File tree

.changeset/hip-otters-smash.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Give six `*.zod.ts` modules a true module-header doc block, so their reference pages open with their introduction again
6+
7+
`postgres` / `mysql` / `sqlite` driver config, `cloud/template-manifest`, `system/doc` and
8+
`api/error-code-ledger` each already carried a real module introduction — but written glued
9+
to the module's first declaration, which under #5059's strict selection rule is that
10+
symbol's TSDoc and therefore not the module's description. The prose was never the problem;
11+
its attachment was. Each block is promoted verbatim to a top-level header that documents no
12+
symbol, the shape 183 of the reference sources already use, and
13+
`content/docs/references/**` is regenerated: six pages gain their opening paragraphs, no
14+
other page changes and no schema byte moves.

content/docs/references/api/error-code-ledger.mdx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,51 @@ description: Error Code Ledger protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8+
Error-Code Ledger (ADR-0112 D3).
9+
10+
The top-level `error.code` vocabulary is two-tier:
11+
12+
1. **Standard catalog**`StandardErrorCode` (`errors.zod.ts`): a small,
13+
closed set with platform-wide HTTP semantics. It does NOT grow when a
14+
service invents a code.
15+
2. **Registered extension codes** — THIS ledger: every service-specific code
16+
a route may put in `error.code`, registered under its owning package.
17+
18+
`ErrorCode` (exported below) is the union, and is what `ApiErrorSchema.code`
19+
validates against. An unregistered code fails schema parse — which fails the
20+
envelope conformance suites — which fails CI. That friction is the point
21+
(ADR-0112: "no silent fourth state" for error codes, per ADR-0049/0078).
22+
23+
## Registering a new code
24+
25+
Add it to your package's entry (create the entry if your package has none),
26+
SCREAMING_SNAKE (`^[A-Z][A-Z0-9_]*$` — lint-enforced by
27+
`error-code-ledger.test.ts`), with a trailing `//` comment when the name
28+
alone doesn't carry the meaning. Prefer a domain prefix for anything not
29+
self-evidently global (`ATTACHMENT_*`, `REPORT_*`, `SETTINGS_*`). If the
30+
condition is generic (not found / permission / validation / rate limit),
31+
use the standard catalog instead of registering a synonym.
32+
33+
A code emitted by several packages is listed once per emitting package —
34+
the union dedupes; the per-package rows are provenance, not identity.
35+
36+
## Retiring a code
37+
38+
A row whose last EMITTER is deleted comes out with it. The admission rules
39+
below check casing, duplication and shadowing — never whether anyone still
40+
throws the code — so a registered-but-unemittable row stays green forever
41+
while promising a client a code no response can carry. That is ADR-0112's
42+
"no silent fourth state" read backwards, and it is not hypothetical:
43+
`OVERLAY_PERSISTENCE_FAILED` outlived its only producer by one PR (#5264
44+
deleted `saveMetaItem`'s legacy raw-engine branch; #5783 unregistered the
45+
code). Before deleting a row, check that no producer remains repo-wide AND
46+
that no consumer — including `objectui` and `cloud` — reads the literal;
47+
tests that merely CONSTRUCT the code are not producers, and a test pinned to
48+
a producerless code is pinning nothing (#4984's phantom-check family).
49+
50+
Field-level codes (`FieldErrorSchema.code`, the `fields[]` array) are a
51+
SEPARATE vocabulary and do not belong here — see #3977 (ADR-0112 D6).
52+
853
<Callout type="info">
954
**Source:** `packages/spec/src/api/error-code-ledger.zod.ts`
1055
</Callout>

content/docs/references/cloud/template-manifest.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ description: Template Manifest protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8+
`objectstack.manifest.json` — on-disk descriptor for a template / package
9+
source tree. Strict projection of `CreatePackageRequestSchema` (server-
10+
managed fields excluded) plus scaffold-time extras (name slug,
11+
specVersion, skills, preview, scaffold, readmePath).
12+
813
<Callout type="info">
914
**Source:** `packages/spec/src/cloud/template-manifest.zod.ts`
1015
</Callout>

content/docs/references/data/driver-mysql.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ description: Driver Mysql protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8+
MySQL / MariaDB driver configuration — the `config` slot of a `datasource`
9+
whose `driver` resolves to `mysql` (`mysql2`).
10+
11+
The driver id was offered by the connection form and buildable by the shared
12+
factory long before #4410, but had no config shape at all in `packages/spec`
13+
— postgres, mongo and memory each had one and mysql did not, so its `config`
14+
was the one slot with neither a gate nor a documented shape.
15+
16+
Every key here is read by `createDefaultDatasourceDriverFactory`
17+
(→ `SqlDriver`, knex `mysql2`). Postgres-only knobs are deliberately absent:
18+
`mysql2` has no `application_name` and no `statement_timeout`, so declaring
19+
them would advertise settings the client drops.
20+
821
<Callout type="info">
922
**Source:** `packages/spec/src/data/driver/mysql.zod.ts`
1023
</Callout>

content/docs/references/data/driver-postgres.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ description: Driver Postgres protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8+
PostgreSQL driver configuration — the `config` slot of a `datasource` whose
9+
`driver` resolves to `postgres` (`pg` / `postgresql`).
10+
11+
ENFORCED as of #4410: `DatasourceSchema` parses `config` against this schema,
12+
so a misspelled connection key fails at authoring time instead of leaving the
13+
datasource on the client's localhost defaults. Every key here is read by
14+
`createDefaultDatasourceDriverFactory` (→ `SqlDriver`, knex `pg`).
15+
16+
Pool sizing is NOT here: it lives in the driver-agnostic `datasource.pool`
17+
block, which the factory now honours for every SQL driver.
18+
819
<Callout type="info">
920
**Source:** `packages/spec/src/data/driver/postgres.zod.ts`
1021
</Callout>

content/docs/references/data/driver-sqlite.mdx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ description: Driver Sqlite protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8+
SQLite driver configuration — the `config` slot of a `datasource` whose
9+
`driver` resolves to `sqlite` (native `better-sqlite3`, with the dev-only
10+
step-down to wasm then in-memory, #2229) or to `sqlite-wasm` (pure-JS).
11+
12+
The one key that matters is `filename`, and it is exactly the key the silent
13+
strip used to hide: an author who wrote `path:` got no error, the connection
14+
fell back to `:memory:`, and their data vanished on restart with every signal
15+
saying the datasource was configured.
16+
17+
`file` and `database` once also worked, purely because the factory read them
18+
as undeclared `??` fallbacks. That tolerance has graduated into the declared
19+
ADR-0087 conversion `datasource-config-driver-key-aliases` (#4456): stored
20+
rows are rewritten to `filename` at load, the factory reads one spelling,
21+
and authoring rejects both with the rename hint below.
22+
823
<Callout type="info">
924
**Source:** `packages/spec/src/data/driver/sqlite.zod.ts`
1025
</Callout>

content/docs/references/system/doc.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,27 @@ description: Doc protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8+
Package Documentation Metadata Protocol (ADR-0046)
9+
10+
One `doc` item per Markdown file under the package's flat `src/docs/`
11+
directory (no subdirectories — flatness is the contract that keeps
12+
cross-references stable). The CLI compiles each file into this shape at
13+
build time; TS-first stacks may also declare items inline via
14+
`defineStack({ docs: [...] })`.
15+
16+
Identity model: `name` = filename stem (lowercase snake_case). A namespace
17+
prefix (`crm_lead_guide`) is a *recommended convention*, no longer required:
18+
per ADR-0048, single-doc resolution is package-scoped (`getItem('doc', name,
19+
packageId)` via `?package=` on the detail route), so two packages may ship a
20+
doc with the same bare name and each resolves within its own package — just
21+
like `page`/`dashboard`/`report`. The prefix stays useful for readable,
22+
globally-unique filenames but is not load-bearing for uniqueness.
23+
24+
Docs are inert data: the kernel registers them without parsing
25+
`content`, and they participate in no runtime behavior. Renderers
26+
resolve relative links between docs (`[guide](./crm_lead_guide.md)`)
27+
by stripping `./` and `.md` to obtain the target doc name.
28+
829
<Callout type="info">
930
**Source:** `packages/spec/src/system/doc.zod.ts`
1031
</Callout>

packages/spec/scripts/file-description.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,37 @@ describe('corpus — no reference source donates a symbol comment to its page',
616616
expect(openingOf('api/websocket.zod.ts')).toBe('WebSocket Event Protocol');
617617
expect(openingOf('ui/sharing.zod.ts')).toBe('@module ui/sharing');
618618
});
619+
620+
/**
621+
* #6145 — the other side of #5059's ledger. Six more modules had written a
622+
* real module introduction and then glued it to their first declaration, so
623+
* the strict rule (correctly) read it as that symbol's TSDoc and their pages
624+
* went on opening with nothing. The prose was never the problem; its
625+
* attachment was. Each block was promoted VERBATIM to a true module header,
626+
* and this pins the result per file.
627+
*
628+
* Deliberately an assertion about the six SOURCES, not about the emitted
629+
* `.mdx`: `check:docs` compares the artifact to the source, so it stays green
630+
* while a re-glued header quietly empties the page — which is exactly how the
631+
* original six survived two rounds on `main`.
632+
*/
633+
it('opens each of the six #6145 modules with its own module header', () => {
634+
const openingOf = (rel: string) =>
635+
opening(findModuleDocBlock(fs.readFileSync(path.join(SRC_DIR, rel), 'utf-8')));
636+
637+
expect(openingOf('data/driver/postgres.zod.ts'))
638+
.toBe('PostgreSQL driver configuration — the `config` slot of a `datasource` whose');
639+
expect(openingOf('data/driver/mysql.zod.ts'))
640+
.toBe('MySQL / MariaDB driver configuration — the `config` slot of a `datasource`');
641+
expect(openingOf('data/driver/sqlite.zod.ts'))
642+
.toBe('SQLite driver configuration — the `config` slot of a `datasource` whose');
643+
expect(openingOf('cloud/template-manifest.zod.ts'))
644+
.toBe('`objectstack.manifest.json` — on-disk descriptor for a template / package');
645+
expect(openingOf('system/doc.zod.ts'))
646+
.toBe('Package Documentation Metadata Protocol (ADR-0046)');
647+
expect(openingOf('api/error-code-ledger.zod.ts'))
648+
.toBe('Error-Code Ledger (ADR-0112 D3).');
649+
});
619650
});
620651

621652
/**

packages/spec/src/api/error-code-ledger.zod.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

3-
import { z } from 'zod';
4-
import { StandardErrorCode } from './errors.zod';
5-
63
/**
74
* Error-Code Ledger (ADR-0112 D3).
85
*
@@ -49,6 +46,10 @@ import { StandardErrorCode } from './errors.zod';
4946
* Field-level codes (`FieldErrorSchema.code`, the `fields[]` array) are a
5047
* SEPARATE vocabulary and do not belong here — see #3977 (ADR-0112 D6).
5148
*/
49+
50+
import { z } from 'zod';
51+
import { StandardErrorCode } from './errors.zod';
52+
5253
export const ERROR_CODE_LEDGER = {
5354
'@objectstack/rest': [
5455
'ALREADY_REVERTED',

packages/spec/src/cloud/template-manifest.zod.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2-
import { z } from 'zod';
3-
import { lazySchema } from '../shared/lazy-schema';
4-
import { CreatePackageRequestSchema } from './package.zod';
52

63
/**
74
* `objectstack.manifest.json` — on-disk descriptor for a template / package
85
* source tree. Strict projection of `CreatePackageRequestSchema` (server-
96
* managed fields excluded) plus scaffold-time extras (name slug,
107
* specVersion, skills, preview, scaffold, readmePath).
118
*/
9+
10+
import { z } from 'zod';
11+
import { lazySchema } from '../shared/lazy-schema';
12+
import { CreatePackageRequestSchema } from './package.zod';
13+
1214
export const TemplateManifestSchema = lazySchema(() =>
1315
CreatePackageRequestSchema
1416
.omit({ ownerOrgId: true, createdBy: true })

0 commit comments

Comments
 (0)