diff --git a/AGENTS.md b/AGENTS.md index aa75e3b98b..8c94c73787 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,8 +74,8 @@ Do not hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFE - `pnpm check:types` - raw TS compile across all referenced projects (`tsc -b tsconfig.references.json`). Does NOT run the public-interface chain. Legacy alias: `pnpm run type-check`. - `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (vite build + postbuild chain + consumer typecheck matrix + deep-type audit + package-shape + snapshots + classification closure) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`. - `pnpm check:public:superdoc` - SuperDoc public package surface only. Wraps six stages: build + matrix + deep-type audit + package-shape + snapshots + closure. Legacy alias: `pnpm run check:public-contract`. -- `pnpm check:public:docapi` - Document API public surface only. Requires generated artifacts to be current; if it fails on missing files, run `pnpm generate:docapi`. Legacy alias: `pnpm run docapi:check`. -- `pnpm generate:docapi` - regenerate Document API outputs after editing the contract (alias of `docapi:sync`). Writes gitignored generated artifacts under the Document API package. Run before `check:public:docapi` if it fails on missing files. +- `pnpm check:public:docapi` - Document API public surface only. Clean-checkout safe: gitignored generated artifacts are built in memory; tracked outputs (reference docs, overview block) are compared byte-for-byte. No mutation. Legacy alias: `pnpm run docapi:check`. +- `pnpm generate:docapi` - regenerate Document API outputs after editing the contract (alias of `docapi:sync`). Writes gitignored Document API generated artifacts. Run only when you need the artifacts materialized locally (SDK builds, publishing); `check:public:docapi` does not require it. - `pnpm generate:all` - regenerate schemas, SDK clients, tool catalogs, reference docs. - `pnpm report:public:superdoc` - print public-contract tier metadata (supported / legacy / asset / deprecated). Read-only, not a gate. Source of truth: `packages/superdoc/scripts/type-surface.config.cjs`. diff --git a/packages/document-api/scripts/check-contract-outputs.ts b/packages/document-api/scripts/check-contract-outputs.ts index 41ff086375..3d8a4dde7d 100644 --- a/packages/document-api/scripts/check-contract-outputs.ts +++ b/packages/document-api/scripts/check-contract-outputs.ts @@ -3,7 +3,16 @@ * Caller: Main CI/local gate for generated Document API artifacts. * Reads: Contract snapshot + generated schemas/agent artifacts/reference docs + overview. * Writes: None (exit code + console output only). - * Fails when: Any generated output is missing/extra/stale or overview block is out of sync. + * Fails when: A tracked generated output (reference docs, overview block) + * is missing/extra/stale, or any artifact builder throws. + * + * Clean-checkout safe: the schemas/ and agent/ outputs live under + * `packages/document-api/generated/` which is gitignored. Those + * artifacts are built in memory (so any builder error still surfaces) + * but their on-disk presence is not required. Reference docs and the + * overview block ARE committed and continue to be compared + * byte-for-byte against the in-memory build. Run `pnpm generate:docapi` + * to materialize the gitignored artifacts locally before publishing. */ import { buildStableSchemaArtifacts, @@ -22,7 +31,12 @@ runScript('contract output artifacts check', async () => { const files = [...buildStableSchemaArtifacts(), ...buildAgentArtifacts(), ...buildReferenceDocsArtifacts()]; const issues = await checkGeneratedFiles(files, { - roots: [getStableSchemaRoot(), getAgentArtifactRoot(), getReferenceDocsOutputRoot()], + // Tracked output: committed reference docs must match the in-memory + // build (existence, content, and no extras on disk). + roots: [getReferenceDocsOutputRoot()], + // Gitignored: validate the builders produce the artifacts in memory, + // but don't require the files to exist on a clean checkout. + inMemoryRoots: [getStableSchemaRoot(), getAgentArtifactRoot()], }); await checkReferenceDocsExtras(files, issues); diff --git a/packages/document-api/scripts/lib/generation-utils.ts b/packages/document-api/scripts/lib/generation-utils.ts index 17bf22b47f..4975b6c2e8 100644 --- a/packages/document-api/scripts/lib/generation-utils.ts +++ b/packages/document-api/scripts/lib/generation-utils.ts @@ -87,18 +87,45 @@ async function pathExists(path: string): Promise { } } +/** + * Compare expected generated files against the on-disk state. + * + * - `roots`: tracked directories whose committed contents must match the + * in-memory build. Files under these roots are checked for existence, + * content equality, and "extras on disk that should not be there." + * Use for outputs that live in the repo (e.g. `apps/docs/.../reference/`). + * - `inMemoryRoots`: directories whose contents are gitignored. Files + * under these roots are NOT checked for existence or extras on disk, + * because a clean checkout has none. The in-memory build is still + * exercised (any error thrown by the builder still propagates), + * confirming the artifacts CAN be produced. Use for outputs that the + * developer regenerates locally with the matching `generate:*` command. + * + * The "content matches when the file is present" check still runs for + * `inMemoryRoots` files — if a developer has a previously-generated + * stale copy on disk, surface it; but a missing file is not an issue. + */ export async function checkGeneratedFiles( expectedFiles: GeneratedFile[], options: { roots?: string[]; + inMemoryRoots?: string[]; } = {}, ): Promise { const issues: GeneratedCheckIssue[] = []; const expected = new Map(expectedFiles.map((file) => [file.path, file])); + const inMemoryRoots = options.inMemoryRoots ?? []; + + const isUnderInMemoryRoot = (path: string): boolean => + inMemoryRoots.some((root) => path === root || path.startsWith(`${root}/`)); for (const [path, file] of expected.entries()) { + const onDiskOptional = isUnderInMemoryRoot(path); if (!(await pathExists(path))) { - issues.push({ kind: 'missing', path }); + // Missing is only an issue when the file is expected to be tracked + // on disk. For in-memory roots, the build succeeded (it produced + // the GeneratedFile entry), which is what we needed to verify. + if (!onDiskOptional) issues.push({ kind: 'missing', path }); continue; } diff --git a/packages/superdoc/scripts/README.md b/packages/superdoc/scripts/README.md index 95407ad59c..1adac6bfd0 100644 --- a/packages/superdoc/scripts/README.md +++ b/packages/superdoc/scripts/README.md @@ -53,7 +53,7 @@ but have separate script chains because the validation needs differ. |---|---|---| | `check:public` | `check:public:superdoc` + `check:public:docapi` | Both public interfaces. The umbrella to run before merging. | | `check:public:superdoc` | `check:public-contract` (legacy alias) | SuperDoc package: vite build + postbuild chain, consumer typecheck matrix, deep-type audit. | -| `check:public:docapi` | `docapi:check` (legacy alias) | Document API: contract parity, generated outputs are not stale, examples compile, overview alignment. **Requires generated artifacts to be up-to-date** — if it fails on staleness, run `generate:docapi` to refresh. | +| `check:public:docapi` | `docapi:check` (legacy alias) | Document API: contract parity, generated outputs are not stale, examples compile, overview alignment. Clean-checkout safe: gitignored outputs (`packages/document-api/generated/`) are built in memory; tracked outputs (`apps/docs/document-api/reference/`, overview block) are still compared byte-for-byte. | | `report:public:superdoc` | `report:public-contract` (legacy alias) | Read-only tier metadata (supported / legacy / asset / deprecated). Not a gate. | ### TypeScript compiler @@ -67,7 +67,7 @@ but have separate script chains because the validation needs differ. | Command | Runs | Mutates? | |---|---|---| -| `generate:docapi` | `docapi:sync` (legacy alias) | yes — writes Document API artifacts under `packages/document-api/generated/` (gitignored; run before `check:public:docapi` if it fails on missing files). | +| `generate:docapi` | `docapi:sync` (legacy alias) | yes — writes Document API artifacts under `packages/document-api/generated/` (gitignored). Run this when you want the artifacts materialized locally (e.g. for SDK builds or before publishing). `check:public:docapi` does NOT require running this first. | | `generate:all` | schemas, SDK clients, tool catalogs, reference docs | yes — multi-target generator. Some outputs are gitignored (`generated/`), others are committed (e.g. `apps/docs/document-api/reference/`); any tracked generated changes should be committed. | ### Legacy aliases @@ -143,14 +143,16 @@ canonical source, and several artifacts are generated from it. |---|---|---| | `generate-contract-outputs.ts` | generate | Writes `generated/schemas/**` + `generated/agent/**` from the contract. Called by `generate:docapi`. | | `check-contract-parity.ts` | check | Asserts derived maps (`operation-registry`, `invoke`, etc.) project from `operation-definitions` correctly. | -| `check-contract-outputs.ts` | check | Asserts the committed `generated/**` files match what regeneration would produce. Fails if stale. | +| `check-contract-outputs.ts` | check | Builds all artifacts in memory and compares against the on-disk state. For tracked outputs (`apps/docs/document-api/reference/`, overview block) the disk must match exactly. For gitignored outputs (`packages/document-api/generated/`) the in-memory build is verified to succeed, but the files are not required on disk; if they happen to be present, content is still checked. | | `check-examples.ts` | check | Asserts contract examples compile. | | `check-overview-alignment.ts` | check | Asserts the documentation overview reflects the current operation set. | -The four `check-*` scripts run together via `check:public:docapi`. -Currently they require `generate:docapi` to have run first (the -stale-file check trips otherwise). Making the check self-contained -(in-memory generate and compare) is tracked as a follow-up. +The four `check-*` scripts run together via `check:public:docapi` and +are **clean-checkout safe**: a fresh `git clone` followed by +`pnpm install && pnpm check:public` succeeds without `generate:docapi` +having run first. `generate:docapi` remains the explicit way to +materialize the gitignored artifacts when you need them locally (SDK +builds, publishing). ---