docs: trim generated prose

This commit is contained in:
Tianyi Cui
2026-07-12 03:36:43 +08:00
parent 3dca90261c
commit 75838e10b5
323 changed files with 2857 additions and 11833 deletions
+8 -73
View File
@@ -1,55 +1,8 @@
/**
* Generate (and verify) the cordis events and services catalogs in
* docs/cordis-catalog/events.md and docs/cordis-catalog/services.md.
*
* The two pages are the WIRING-axis reference, one axis each: every cordis
* event a plugin can listen to (exact signature + dispatch mode) and every
* `ctx.<key>` service it can call (exact public interface). They complement the
* core-data-structures catalog (the VOCABULARY axis — the types these
* signatures move around).
*
* The catalogs are FULLY GENERATED from source — never hand-edit them. The
* codebase is disciplined enough that a pure-AST pass captures the whole
* truthful surface: every event/service is a string literal that round-trips
* to a static `interface Events` / `interface Context` declaration (no
* dynamically-named events, no runtime-only services). So the committed files
* are build artifacts and a regenerate-and-diff freshness check (`--check`)
* makes drift structurally impossible. Because generation enumerates source
* rather than checking a hand-written subset, a brand-new event cannot be
* silently undocumented — it appears in the next regenerate, and an
* un-regenerated file fails `--check`.
*
* `tsx scripts/gen-cordis-catalog.ts` → write both catalogs
* `tsx scripts/gen-cordis-catalog.ts --check` → exit 1 if a committed
* catalog is stale (CI /
* pre-push gate)
*
* The HARNESS tier (the `@deepseek-ai/dsh-*` events + services) is rendered in
* full from source: signature, the `@mode` badge, and the declaration's JSDoc.
* Every harness event MUST carry an `@mode emit|waterfall|parallel|serial` tag
* — the generator hard-errors on a missing tag, and where the signature shape is
* conclusive (a trailing `next: () => …` parameter is structurally a waterfall)
* it asserts the tag agrees and hard-errors on a contradiction. Beyond the tag,
* the walk enforces JSDoc COMPLETENESS on the whole harness surface (the
* jsdoc-completeness-gate RFC): every event and public service method carries
* description prose; every payload parameter has a non-empty `@param` (`this`
* receivers and the trailing waterfall `next` are exempt — next's semantics are
* documented once by the mode); a service method with a non-`void`/
* `Promise<void>` return carries a non-empty `@returns` and needs an EXPLICIT
* return type annotation (a pure-AST walk cannot classify an inferred return);
* a stale `@param` naming no real parameter errors. Violations aggregate into
* ONE error listing every offender. The tags are enforcement-only: parseJsDoc
* stops prose at the first block tag, so they never change the rendered
* catalog. The parsing + check helpers live in `scripts/jsdoc.ts`, shared with
* the whole-export-surface gate (`scripts/verify-export-jsdoc.ts`) so
* "documented" means the same thing on both surfaces. The INHERITED
* tier (cordis core + loader/hmr/timer) is pinned vendor source a plugin author
* also sees; it is rendered tersely (name + one-line + source pointer) from a
* curated table in this script, NOT elevated to the harness tier's prominence.
*
* Signature fences use the ` ```ts cordis-catalog ` info string: doc-typecheck
* recognizes it and skips compilation (the signatures are fragments, not
* standalone-compilable, like the ` ```ts type-equiv ` blocks).
* Generate the Cordis event and service catalogs from static declarations.
* The walk enforces event modes plus JSDoc parameter/return completeness;
* inherited Cordis services come from the curated table below. `--check`
* verifies both committed artifacts.
*/
import { globSync, readFileSync, writeFileSync } from 'node:fs'
@@ -65,20 +18,8 @@ const OUT_SERVICES = 'docs/cordis-catalog/services.md'
* doc-typecheck, since a bare signature fragment is not standalone-compilable). */
const FENCE = 'ts cordis-catalog'
/**
* Cross-link map: a type name that appears in a signature → the
* core-data-structures page that documents it (path relative to the catalogs'
* folder).
* Hand-curated and catalog-owned, NOT derived from type-equiv.manifest.json —
* that manifest documents the `…Map` symbols (`ContentBlockMap`) while
* signatures reference the derived UNION names (`ContentBlock`), and it lists a
* few symbols on two pages. Here each name resolves to exactly one PRIMARY page.
* Shared with `gen-config-catalog.ts` (each caller prefixes its own relative
* path to `core-data-structures/`), so both catalogs cross-link identically.
* TODO(catalog-type-links): add a verifier or generator for link-map coverage
* so new hook-era decision types like `PromptDecision` / `PreToolDecision` do
* not silently appear in signatures without a "Types:" link.
*/
/** Primary core-data-structures page for signature types shared by both catalog generators. */
// TODO(catalog-type-links): verify or generate link-map coverage.
export const LINK_MAP: Record<string, string> = {
Agent: 'core.md',
ContentBlock: 'core.md',
@@ -215,10 +156,7 @@ export function collectEvents(scanRoot: string = root): EventEntry[] {
violations.push(`${where} is tagged '@mode waterfall' but has no trailing 'next' parameter. A waterfall delegates via next().`)
}
if (!doc) violations.push(`${where} has no description prose. Say what happened / what a listener may do, above the block tags.`)
// Payload parameters need a non-empty @param each. Exempt the `this`
// receiver annotation (not payload) and the trailing waterfall `next`
// (mode machinery, documented once by @mode semantics). Documenting an
// exempt parameter anyway is allowed — only absence is checked.
// Payload parameters need a non-empty @param each.
const { params } = parseTags(raw)
checkParams(where, 'event', member.parameters, params, sf,
p => (ts.isIdentifier(p.name) && p.name.text === 'this') || (hasNext && p === last), violations)
@@ -269,10 +207,7 @@ export function collectServices(scanRoot: string = root): ServiceEntry[] {
const methods: string[] = []
for (const member of cls.members) {
if (!ts.isMethodDeclaration(member)) continue
// Only the PUBLIC callable surface a `ctx.<key>` consumer sees. Drop
// private/protected (a protected method like `notifyTaskDone` is a
// subclass hook, not something a plugin calls through `ctx.bash`) and
// static (not reachable through the instance).
// Only the PUBLIC callable surface a `ctx.<key>` consumer sees.
const nonPublic = member.modifiers?.some(m =>
m.kind === ts.SyntaxKind.PrivateKeyword
|| m.kind === ts.SyntaxKind.ProtectedKeyword