diff --git a/AGENTS.md b/AGENTS.md index 444e912221..eb59352d55 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,7 +74,7 @@ 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` (tier discipline + jsdoc ratchet + 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 nine stages in cheap-to-expensive order: `contract-tiers-test`, `contract-tiers`, `jsdoc-ratchet`, `build`, `consumer-typecheck-matrix`, `deep-type-audit-supported-root`, `package-shape`, `export-snapshots`, `root-classification-closure`. Legacy alias: `pnpm run check:public-contract`. -- `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 check:public:docapi` - Document API public surface only. Wraps four stages: `contract-parity`, `contract-outputs`, `examples`, `overview-alignment`. 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 / legacy-raw / asset / deprecated). Read-only, not a gate. Use `check:public:superdoc` (or its `contract-tiers` stage) to enforce. Source of truth: `packages/superdoc/scripts/type-surface.config.cjs`. diff --git a/package.json b/package.json index d206948dfe..ac9a86ab93 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "docapi:sync:check": "pnpm run generate:docapi && pnpm run check:public:docapi", "check:types": "tsc -b tsconfig.references.json", "check:public:superdoc": "node scripts/check-public-contract.mjs", - "check:public:docapi": "pnpm exec tsx packages/document-api/scripts/check-contract-parity.ts && pnpm exec tsx packages/document-api/scripts/check-contract-outputs.ts && pnpm exec tsx packages/document-api/scripts/check-examples.ts && pnpm exec tsx packages/document-api/scripts/check-overview-alignment.ts", + "check:public:docapi": "node scripts/check-public-docapi.mjs", "check:public": "pnpm run check:public:superdoc && pnpm run check:public:docapi", "generate:docapi": "pnpm exec tsx packages/document-api/scripts/generate-contract-outputs.ts", "report:public:superdoc": "node scripts/report-public-contract.mjs", diff --git a/packages/superdoc/scripts/README.md b/packages/superdoc/scripts/README.md index aabe3dc2c1..a4a51636d3 100644 --- a/packages/superdoc/scripts/README.md +++ b/packages/superdoc/scripts/README.md @@ -178,12 +178,17 @@ canonical source, and several artifacts are generated from it. | `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` 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). +The four `check-*` scripts run together via `check:public:docapi`, +which invokes the staged wrapper at `scripts/check-public-docapi.mjs`. +Same shape as `check:public:superdoc`: cheap-to-expensive ordering, +named stages (`contract-parity`, `contract-outputs`, `examples`, +`overview-alignment`), stage headers + final elapsed time, and a +re-run hint on failure. + +**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). --- diff --git a/scripts/check-public-docapi.mjs b/scripts/check-public-docapi.mjs new file mode 100644 index 0000000000..19b51cbf8c --- /dev/null +++ b/scripts/check-public-docapi.mjs @@ -0,0 +1,109 @@ +#!/usr/bin/env node +/** + * Document API public-surface gate. Sibling of `check-public-contract.mjs` + * (SuperDoc); same staged shape, same failure UX. + * + * Non-mutating, clean-checkout safe: stage 2 builds gitignored + * artifacts in memory so `generate:docapi` is not a prerequisite. + * Cheap-to-expensive ordering — contract drift fails in seconds. + * + * Stages: + * 1. contract-parity - operation IDs, member maps, runtime API + * shape must agree. + * 2. contract-outputs - tracked outputs (reference docs, overview + * block) match the contract; gitignored + * outputs (schemas, agent artifacts) are + * built in memory so builder errors still + * surface. The longest stage. + * 3. examples - required workflow example headings exist + * in `packages/document-api/src/README.md`. + * 4. overview-alignment - `apps/docs/document-api/available- + * operations.mdx` structural correctness: + * reference link, section markers, every + * `editor.doc.*` path references a known + * member. + * + * Local usage: + * pnpm check:public (umbrella, runs SuperDoc + Document API) + * pnpm check:public:docapi (Document API only, this script) + * + * Legacy alias preserved: `pnpm run docapi:check`. + */ + +import { spawnSync } from 'node:child_process'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..'); + +const stages = [ + { + name: 'contract-parity', + cwd: REPO_ROOT, + cmd: 'pnpm', + args: ['exec', 'tsx', 'packages/document-api/scripts/check-contract-parity.ts'], + blurb: + 'Operation IDs, operation/member maps, and runtime API shape must agree. ' + + 'Fast; runs first so contract drift fails before slower stages.', + }, + { + name: 'contract-outputs', + cwd: REPO_ROOT, + cmd: 'pnpm', + args: ['exec', 'tsx', 'packages/document-api/scripts/check-contract-outputs.ts'], + blurb: + 'Tracked outputs (reference docs, overview block) compared byte-for-byte. ' + + 'Gitignored outputs (schemas, agent artifacts) built in memory; no need to ' + + 'run `pnpm run generate:docapi` first.', + }, + { + name: 'examples', + cwd: REPO_ROOT, + cmd: 'pnpm', + args: ['exec', 'tsx', 'packages/document-api/scripts/check-examples.ts'], + blurb: + 'Required workflow example headings exist in ' + + 'packages/document-api/src/README.md.', + }, + { + name: 'overview-alignment', + cwd: REPO_ROOT, + cmd: 'pnpm', + args: ['exec', 'tsx', 'packages/document-api/scripts/check-overview-alignment.ts'], + blurb: + 'Overview page structure: reference link, section markers, no stale ' + + 'placeholders, every `editor.doc.*` path references a known member.', + }, +]; + +const HR = '='.repeat(72); +const start = Date.now(); + +let failed = null; +for (const [i, s] of stages.entries()) { + console.log(''); + console.log(HR); + console.log(`[${i + 1}/${stages.length}] ${s.name}`); + console.log(s.blurb); + console.log(HR); + const result = spawnSync(s.cmd, s.args, { cwd: s.cwd, stdio: 'inherit' }); + if (result.status !== 0) { + failed = { stage: s.name, status: result.status ?? 1 }; + break; + } +} + +const elapsed = ((Date.now() - start) / 1000).toFixed(1); +console.log(''); +console.log(HR); +if (failed) { + console.log(`FAIL: stage "${failed.stage}" exited ${failed.status} (after ${elapsed}s)`); + console.log(''); + console.log('Re-run the failing stage directly to iterate:'); + const failedStage = stages.find((s) => s.name === failed.stage); + console.log(` cd ${failedStage.cwd}`); + console.log(` ${failedStage.cmd} ${failedStage.args.join(' ')}`); + process.exit(failed.status); +} else { + console.log(`PASS: ${stages.length} stages, ${elapsed}s`); +}