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:
@@ -1,40 +1,16 @@
|
||||
/**
|
||||
* Doc-sync gate: verify every ` ```ts type-equiv ` block in the docs is a
|
||||
* VERBATIM copy of the source type definition it documents.
|
||||
*
|
||||
* The core-data-structures docs paste real type definitions so a reader sees
|
||||
* the exact shape. A paste drifts the moment source changes — this script is
|
||||
* the drift guard. For each block it extracts the documented symbol's
|
||||
* declaration from source via the TypeScript compiler API, whitespace-
|
||||
* normalizes both the source text and the block, and asserts they are equal.
|
||||
*
|
||||
* Provenance lives in a central manifest (`scripts/type-equiv.manifest.json`),
|
||||
* NOT in the doc prose: each entry names `{ doc, symbol, source }`. The script
|
||||
* enforces a 1:1 correspondence — every type-equiv block in the docs has
|
||||
* exactly one manifest entry (keyed by doc + declared symbol), and every
|
||||
* manifest entry resolves to exactly one block. An orphan on either side fails,
|
||||
* so a block can never be silently unchecked and an entry can never rot.
|
||||
*
|
||||
* doc-typecheck.ts recognizes the same ` ```ts type-equiv ` fence and skips it
|
||||
* (it is not standalone-compilable and is not counted in the opt-out ratio);
|
||||
* the two scripts share the fence, this one owns the verification.
|
||||
*
|
||||
* Run: `tsx scripts/verify-type-equiv.ts`.
|
||||
* Verify every `ts type-equiv` block against the source symbol named by the
|
||||
* manifest. Blocks and entries have a one-to-one relationship; comparison
|
||||
* ignores comments and whitespace but preserves declaration structure.
|
||||
*/
|
||||
|
||||
import { globSync, readFileSync, existsSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { resolve, sep } from 'node:path'
|
||||
import ts from 'typescript'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
|
||||
/**
|
||||
* Markdown globs scanned for ` ```ts type-equiv ` blocks — the SAME scope
|
||||
* doc-typecheck uses. Scanning every doc (not only the docs the manifest names)
|
||||
* is what makes the 1:1 guarantee real in both directions: a type-equiv block
|
||||
* added to a doc with NO manifest entry is still discovered here and reported as
|
||||
* an orphan, instead of being silently skipped.
|
||||
*/
|
||||
/** Scan doc-typecheck's full Markdown scope so unmanifested blocks also fail. */
|
||||
const MARKDOWN_GLOBS = ['README.md', 'docs/**/*.md', 'packages/*/*.md', 'packages/*/*/*.md', 'website/zh-CN/**/*.md']
|
||||
|
||||
/** One manifest entry: a documented type-equiv block and its source symbol. */
|
||||
@@ -58,12 +34,11 @@ interface EquivBlock {
|
||||
code: string
|
||||
}
|
||||
|
||||
/** Collapse a declaration to its structural form for comparison: drop comments
|
||||
* (block + line), then collapse all whitespace runs to single spaces. This lets
|
||||
* a doc block show a CLEAN definition (without source's verbose inline JSDoc)
|
||||
* while still guaranteeing the field shapes match — drift in a field name or
|
||||
* type fails; a reworded inline comment does not. Adequate for our own type
|
||||
* source (no string literal contains `//` or `/* */`); not a general tokenizer. */
|
||||
/**
|
||||
* Remove comments and normalize whitespace so prose-only edits do not drift
|
||||
* structural copies. This is intentionally not a general tokenizer: repo type
|
||||
* declarations do not contain comment delimiters inside string literals.
|
||||
*/
|
||||
function normalize(code: string): string {
|
||||
return code
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
@@ -72,8 +47,7 @@ function normalize(code: string): string {
|
||||
.trim()
|
||||
}
|
||||
|
||||
/** Strip a leading `export ` / `export default ` modifier — the doc block shows
|
||||
* the bare declaration, the source carries the export modifier. */
|
||||
/** Strip source-only export modifiers. */
|
||||
function stripExport(code: string): string {
|
||||
return code.replace(/^export\s+(default\s+)?/, '')
|
||||
}
|
||||
@@ -147,7 +121,7 @@ const keyOf = (x: { doc: string; symbol: string }): string => `${x.doc}::${x.sym
|
||||
// as an orphan rather than silently skipped.
|
||||
const docSet = new Set<string>()
|
||||
for (const pattern of MARKDOWN_GLOBS) {
|
||||
for (const match of globSync(pattern, { cwd: root })) docSet.add(match)
|
||||
for (const match of globSync(pattern, { cwd: root })) docSet.add(match.split(sep).join('/'))
|
||||
}
|
||||
const blocks: EquivBlock[] = [...docSet].sort().flatMap(extractEquivBlocks)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user