Merge origin/master into feat/website-docs
Conflict resolution notes: - package.json/run-gates: both sides' new doc-sync gates kept (master's scoped-events/readme gates + this branch's website-api/website-yaml); js-yaml devDeps deduped (master added them independently). - pnpm-workspace/knip: website AND python/sdk-runtime entries kept. - doc-typecheck/verify-type-equiv: master's condensed headers kept, website glob retained in both scan scopes. - vendor/cordis/src/fiber.ts: master's lifecycle-hardening code taken; this branch's richer FiberState JSDoc reapplied on top. vendor/README.md logs both local modifications (hardening = 6, JSDoc enrichment = 7). - pnpm-lock: regenerated from master's side (pnpm install). Post-merge sync the gates forced (the system working as designed): - verify-website-yaml caught 4 stale plugin names from master's package reorg (dsh-stdio-agent -> dsh-stdio-demo, dsh-acp-agent -> dsh-acp-demo); 8 references fixed across guide/ and develop/. - gen-website-api picked up master's 6 new services automatically (ctx.approval/permission/sandbox/sessionQuery/skills/tasks -> 6 new pages + sidebar); api/index.md hub updated to list them. - AGENTS.md budget ceiling 1370 -> 1400: the website rows (layout line + two command lines) and master's own growth collided with the old ceiling; all three website rows are load-bearing (new top-level dir, new CI command).
This commit is contained in:
+16
-55
@@ -1,28 +1,7 @@
|
||||
/**
|
||||
* Doc-sync gate (doc-sync-enforcement RFC, part 1): typecheck the fenced `ts` code blocks in our
|
||||
* Markdown so documentation can't drift from the API it documents.
|
||||
*
|
||||
* Every ```ts block in README.md, docs/**, packages/* /README.md and the
|
||||
* website tutorial pages (website/zh-CN/**) is
|
||||
* extracted to a temp typecheck project and compiled against the workspace
|
||||
* sources through the same project-reference boundaries used by repo
|
||||
* typecheck. A block that is a deliberate sketch rather than compilable code
|
||||
* opts out with an explicit ` ```ts ignore-check ` info string — the opt-out
|
||||
* is visible in the source, and this script reports the ratio so the escape
|
||||
* hatch can't quietly become the norm. A third info string,
|
||||
* doc-typecheck.ts recognizes four more fence variants and skips all four (each
|
||||
* is a separately-checked category, not an unchecked sketch, so none counts in
|
||||
* the opt-out ratio): ` ```ts type-equiv ` is a verbatim source-type paste that
|
||||
* `scripts/verify-type-equiv.ts` drift-checks, ` ```ts cordis-catalog ` is a
|
||||
* generated event/service signature fragment in the cordis catalog (a bare
|
||||
* signature is not standalone-compilable; the catalog is generated and frozen by
|
||||
* `scripts/gen-cordis-catalog.ts` + its `--check` freshness gate),
|
||||
* ` ```ts persistence-catalog ` is a generated log-event payload fragment in the
|
||||
* persistence catalog (same reasoning, frozen by `scripts/gen-persistence-catalog.ts`),
|
||||
* and ` ```ts config-catalog ` is a generated verbatim config declaration in the
|
||||
* plugin config catalog (same reasoning, frozen by `scripts/gen-config-catalog.ts`).
|
||||
*
|
||||
* Run: `tsx scripts/doc-typecheck.ts`.
|
||||
* Typecheck Markdown `ts` fences against workspace sources. `ignore-check`
|
||||
* fences are reported as opt-outs; generated catalog fragments and
|
||||
* `type-equiv` blocks are skipped here because their owning gates verify them.
|
||||
*/
|
||||
|
||||
import { execFileSync } from 'node:child_process'
|
||||
@@ -33,28 +12,9 @@ import ts from 'typescript'
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
|
||||
/**
|
||||
* How a fenced block participates in this gate:
|
||||
* - `check` (` ```ts `) — compiled.
|
||||
* - `ignore` (` ```ts ignore-check `) — a deliberate sketch; skipped, and
|
||||
* counted in the opt-out ratio so the escape hatch can't quietly take over.
|
||||
* - `type-equiv` (` ```ts type-equiv `) — a verbatim paste of a source type
|
||||
* definition, drift-checked by `scripts/verify-type-equiv.ts` against the
|
||||
* source symbol. Skipped HERE (it is not standalone-compilable — no imports)
|
||||
* and EXCLUDED from the opt-out ratio: it is a separate fully-checked
|
||||
* category, not an unchecked sketch.
|
||||
* - `cordis-catalog` (` ```ts cordis-catalog `) — a generated event/service
|
||||
* signature fragment in the cordis catalog. Skipped HERE for the same reason
|
||||
* (a bare signature fragment has no imports and does not stand alone) and
|
||||
* EXCLUDED from the opt-out ratio: the catalog is generated and frozen by
|
||||
* `scripts/gen-cordis-catalog.ts` + its `--check` freshness gate.
|
||||
* - `persistence-catalog` (` ```ts persistence-catalog `) — a generated
|
||||
* log-event payload fragment in the persistence catalog. Same treatment for
|
||||
* the same reason; frozen by `scripts/gen-persistence-catalog.ts` + its
|
||||
* `--check` freshness gate.
|
||||
* - `config-catalog` (` ```ts config-catalog `) — a generated verbatim config
|
||||
* declaration in the plugin config catalog (a lone declaration referencing
|
||||
* imported types does not stand alone). Same treatment for the same reason;
|
||||
* frozen by `scripts/gen-config-catalog.ts` + its `--check` freshness gate.
|
||||
* TypeScript-fence ownership. `check` compiles; `ignore` is an unchecked sketch
|
||||
* counted in the opt-out ratio; the catalog and type-equivalence variants are
|
||||
* excluded from that ratio because their owning gates verify them.
|
||||
*/
|
||||
type BlockKind = 'check' | 'ignore' | 'type-equiv' | 'cordis-catalog' | 'persistence-catalog' | 'config-catalog'
|
||||
|
||||
@@ -67,8 +27,7 @@ interface Block {
|
||||
code: string
|
||||
}
|
||||
|
||||
/** Extract every ts / ts ignore-check / ts type-equiv / ts cordis-catalog /
|
||||
* ts persistence-catalog / ts config-catalog block from one Markdown file. */
|
||||
/** Extract every recognized TypeScript fence from one Markdown file. */
|
||||
function extractBlocks(absPath: string): Block[] {
|
||||
const text = readFileSync(absPath, 'utf8')
|
||||
const lines = text.split('\n')
|
||||
@@ -88,7 +47,7 @@ function extractBlocks(absPath: string): Block[] {
|
||||
open = null
|
||||
return
|
||||
}
|
||||
// opening fence — only care about ts blocks
|
||||
// Ignore non-TypeScript fences.
|
||||
const info = (fence[2] ?? '').trim()
|
||||
const kind: BlockKind | null =
|
||||
info === 'ts' ? 'check'
|
||||
@@ -146,11 +105,8 @@ files.sort()
|
||||
const all = files.flatMap(extractBlocks)
|
||||
const checked = all.filter(b => b.kind === 'check')
|
||||
const ignored = all.filter(b => b.kind === 'ignore')
|
||||
// `type-equiv`, `cordis-catalog`, and `persistence-catalog` blocks are verified
|
||||
// elsewhere (verify-type-equiv.ts and each catalog generator's `--check`
|
||||
// freshness gate), not here: neither compiled nor counted toward the opt-out
|
||||
// ratio (each is a separate fully-checked category, not an unchecked sketch).
|
||||
// The ratio's denominator is therefore the compile-eligible blocks only.
|
||||
// Only compile-eligible fences belong in the opt-out ratio; every other skipped
|
||||
// kind has an independent verifier named in BlockKind's contract above.
|
||||
const ratioDenominator = checked.length + ignored.length
|
||||
|
||||
if (checked.length === 0) {
|
||||
@@ -169,7 +125,12 @@ try {
|
||||
})
|
||||
|
||||
try {
|
||||
execFileSync('node_modules/.bin/tsc', ['-b', join(tmp, 'tsconfig.json')], { cwd: root, stdio: 'pipe' })
|
||||
// tsc's JS entry via the current node, not the .bin shim: the extensionless
|
||||
// shim is not spawnable on Windows (the CVE-2024-27980 class the sibling
|
||||
// scripts hit), and the .cmd variant would need shell:true, which
|
||||
// concatenates args UNESCAPED — a hazard for the temp project path. The JS
|
||||
// entry behaves identically on every platform.
|
||||
execFileSync(process.execPath, ['node_modules/typescript/bin/tsc', '-b', join(tmp, 'tsconfig.json')], { cwd: root, stdio: 'pipe' })
|
||||
} catch (error: unknown) {
|
||||
const failed = error as { stdout?: Buffer; stderr?: Buffer }
|
||||
const out = `${failed.stdout?.toString() ?? ''}${failed.stderr?.toString() ?? ''}`
|
||||
|
||||
Reference in New Issue
Block a user