Merge remote-tracking branch 'origin/master' into fs-tool-clean

# Conflicts:
#	docs/architecture.md
#	docs/cordis-catalog/events-and-services.md
#	docs/module-graph.md
#	packages/README.md
This commit is contained in:
Tianyi Cui
2026-07-02 01:41:58 +08:00
90 changed files with 5710 additions and 293 deletions

View File

@@ -23,8 +23,8 @@
*
* 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` tag — the
* generator hard-errors on a missing tag, and where the signature shape is
* 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. The INHERITED
* tier (cordis core + loader/hmr/timer) is pinned vendor source a plugin author
@@ -48,7 +48,7 @@ const OUT = 'docs/cordis-catalog/events-and-services.md'
const FENCE = 'ts cordis-catalog'
/** A dispatch mode, rendered as the badge after an event name. */
type Mode = 'emit' | 'waterfall' | 'parallel'
type Mode = 'emit' | 'waterfall' | 'parallel' | 'serial'
/**
* Cross-link map: a type name that appears in a signature → the
@@ -175,7 +175,7 @@ function parseJsDoc(raw: string): { doc: string; mode: Mode | null } {
para = []
}
for (const line of inner) {
const m = /^@mode\s+(emit|waterfall|parallel)\s*$/.exec(line)
const m = /^@mode\s+(emit|waterfall|parallel|serial)\s*$/.exec(line)
if (m) { mode = m[1] as Mode; continue }
if (line.startsWith('@')) { flushPara(); continue } // other tags end the prose
if (line.trim() === '') { flushPara(); continue }
@@ -233,11 +233,11 @@ export function collectEvents(scanRoot: string = root): EventEntry[] {
const { doc, mode } = parseJsDoc(rawJsDoc(text, member))
const src = pointer(rel, sf, member)
if (!mode) {
throw new Error(`gen-cordis-catalog: event '${name}' (${src}) is missing an @mode tag. Add '@mode emit|waterfall|parallel' to its JSDoc (see AGENTS.md).`)
throw new Error(`gen-cordis-catalog: event '${name}' (${src}) is missing an @mode tag. Add '@mode emit|waterfall|parallel|serial' to its JSDoc (see AGENTS.md).`)
}
// Conclusive structural check: a trailing `next: () => …` parameter is a
// waterfall. (emit vs parallel is not structurally distinguishable, so
// it is trusted from the tag.)
// waterfall. (emit vs parallel vs serial is not structurally
// distinguishable, so it is trusted from the tag.)
const last = member.parameters.at(-1)
const hasNext = !!last && last.name.getText(sf) === 'next'
if (hasNext && mode !== 'waterfall') {
@@ -341,7 +341,7 @@ const INHERITED_EVENTS: InheritedEntry[] = [
const INHERITED_SERVICES: InheritedEntry[] = [
{ name: 'ctx.on / ctx.once', summary: 'Register an event listener (disposable).', source: 'vendor/cordis/src/events.ts:29' },
{ name: 'ctx.emit / ctx.parallel / ctx.serial / ctx.bail / ctx.waterfall', summary: 'Dispatch an event (sync / awaited / first-non-nullish / veto-chain).', source: 'vendor/cordis/src/events.ts:29' },
{ name: 'ctx.emit / ctx.parallel / ctx.serial / ctx.bail / ctx.waterfall', summary: 'Dispatch an event (sync / awaited / first-bail / veto-chain).', source: 'vendor/cordis/src/events.ts:29' },
{ name: 'ctx.plugin / ctx.inject', summary: 'Load a plugin / declare required services.', source: 'vendor/cordis/src/registry.ts:144' },
{ name: 'ctx.effect', summary: 'Register a disposable side effect tied to the fiber.', source: 'vendor/cordis/src/fiber.ts:9' },
{ name: 'ctx.get / ctx.set / ctx.provide / ctx.accessor / ctx.mixin', summary: 'Low-level service-store access and binding.', source: 'vendor/cordis/src/reflect.ts:7' },
@@ -404,7 +404,7 @@ function render(events: EventEntry[], services: ServiceEntry[]): string {
'',
'## Events',
'',
`Dispatch modes: **emit** (fire-and-forget), **waterfall** (each listener gets \`next()\` and may transform or veto — see [waterfall semantics](../architecture.md#cordis-waterfall-semantics-important)), **parallel** (awaited fan-out, no veto). The harness declares ${events.length} events across ${new Set(events.map(e => e.scope)).size} scopes.`,
'Dispatch modes: **emit** (fire-and-forget), **waterfall** (each listener gets `next()` and may transform or veto — see [waterfall semantics](../architecture.md#cordis-waterfall-semantics-important)), **parallel** (awaited fan-out; all listeners run), **serial** (awaited in registration order until one returns a bail value — anything other than `null`, `false`, or `undefined`).',
'',
]
const scopes = [...new Set(events.map(e => e.scope))].sort()
@@ -417,7 +417,7 @@ function render(events: EventEntry[], services: ServiceEntry[]): string {
lines.push(
'## Services',
'',
`The ${services.length} \`ctx.<key>\` services the harness provides. An abstract seam (e.g. \`ctx.bash\`) is implemented by a separate package; the interface is what consumers code against.`,
'The `ctx.<key>` services the harness provides. An abstract seam (e.g. `ctx.bash`) is implemented by a separate package; the interface is what consumers code against.',
'',
)
for (const s of services) lines.push(...renderService(s))

View File

@@ -16,6 +16,7 @@
{ "doc": "docs/core-data-structures/llm-streaming.md", "symbol": "ContentBlockMap", "source": "packages/llm/llm/src/types.ts" },
{ "doc": "docs/core-data-structures/session.md", "symbol": "SessionEventMap", "source": "packages/core/session/src/types.ts" },
{ "doc": "docs/core-data-structures/session.md", "symbol": "TodoItem", "source": "packages/core/session/src/types.ts" },
{ "doc": "docs/core-data-structures/session.md", "symbol": "SessionEvent", "source": "packages/core/session/src/types.ts" },
{ "doc": "docs/core-data-structures/session.md", "symbol": "TurnTriggerMap", "source": "packages/core/session/src/types.ts" },
{ "doc": "docs/core-data-structures/session.md", "symbol": "TurnEndReasonMap", "source": "packages/core/session/src/types.ts" },

View File

@@ -20,12 +20,13 @@
* resolved against the linking file's directory, and the result must exist on
* disk. This is checker, not fixer: it reports and never rewrites.
*
* Scope is the other doc-sync gates' set plus the two AGENTS.md files AND the
* repo-authored agent-skill Markdown under `.agents/skills/` — those skill
* files cross-link into the docs tree (e.g. the dsh-code-review skill cites the
* RFC index), so a rename must not silently break them either: README.md,
* docs/** /*.md, packages/* /README.md, AGENTS.md, packages/AGENTS.md,
* .agents/skills/** /*.md. The root and packages/ CLAUDE.md are symlinks to the
* Scope is the other doc-sync gates' set plus example Markdown, AGENTS.md
* files in those checked trees, AND the repo-authored agent-skill Markdown under
* `.agents/skills/` — those skill files cross-link into the docs tree (e.g. the
* dsh-code-review skill cites the RFC index), so a rename must not silently
* break them either: README.md, docs/** /*.md, packages/* /README.md,
* examples/** /*.md, AGENTS.md, packages/AGENTS.md, .agents/skills/** /*.md.
* The root, packages/, and examples/ CLAUDE.md files are symlinks to the
* AGENTS.md files, so they are deduped by real path.
*
* Run: `tsx scripts/verify-md-links.ts`.
@@ -42,14 +43,15 @@ import type { Nodes } from 'mdast'
const root = resolve(import.meta.dirname, '..')
/**
* Files to check: doc-typecheck's scope, the AGENTS.md pair, and repo-authored
* agent-skill Markdown (which this repo's own docs reorg rewrites links in).
* Files to check: doc-typecheck's scope, example Markdown, the AGENTS.md pair,
* and repo-authored agent-skill Markdown.
*/
const PATTERNS = [
'README.md',
'docs/**/*.md',
'packages/*/*.md',
'packages/*/*/*.md',
'examples/**/*.md',
'AGENTS.md',
'packages/AGENTS.md',
'.agents/skills/**/*.md',