diff --git a/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md b/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md index 44d1b17a5b..a5c15686ae 100644 --- a/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md +++ b/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md @@ -18,6 +18,8 @@ Locale home projections retain only the canonical YAML frontmatter. The reposito The projector parses Markdown links without reserializing the document. A link to another published source becomes a site-relative route; a link to an unpublished repository file becomes a GitHub source link; a repository image becomes a raw GitHub URL. Missing relative targets fail projection. Unit tests pin these transformations, and `docs:check` runs the projector tests plus a production VitePress build as part of `doc-sync` and the parallel documentation gates. +`website/AGENTS.md` is the only maintained Markdown file in the website subtree. The projector test enumerates tracked and unignored files and rejects any other website Markdown, so site-specific locale, route, API, or generated source copies cannot bypass the publication manifest. + Mermaid renders the canonical diagrams. The website workspace explicitly declares the five packages that `vitepress-plugin-mermaid` asks Vite to prebundle because pnpm's strict dependency isolation otherwise makes those transitive packages unavailable to the local development server; Knip records this runtime-only use as an intentional dependency exception. Site publication remains separate from site construction. A dedicated GitHub Actions workflow runs the existing documentation gates, uploads `website/.dist` as a Pages artifact, and deploys only after the build succeeds. `actions/configure-pages` supplies the destination's base path to VitePress at build time, so the private Pages origin, a later public project path, and a custom domain do not require distinct checked-in configurations. Pages visibility remains a repository hosting setting rather than a workflow permission. @@ -38,6 +40,6 @@ Site publication remains separate from site construction. A dedicated GitHub Act ## Consequences -Documentation facts have one editable home, public routes remain stable across source moves, and the site can include generated references without committing another generated copy. Local development watches canonical inputs and regenerates the disposable projection. Merges that affect the documentation site deploy the checked result to Pages, while manual dispatch provides a recovery and validation entry point. +Documentation facts have one editable home, public routes remain stable across source moves, and the site can include generated references without committing another generated copy. Local development watches canonical inputs and regenerates the disposable projection. The layout gate makes an obsolete site-specific Markdown tree a merge failure instead of ignored build input. Merges that affect the documentation site deploy the checked result to Pages, while manual dispatch provides a recovery and validation entry point. The publication manifest is a maintained allowlist, and link projection adds a small repository-specific build adapter. A new kind of Markdown link behavior needs a projector test. Mermaid support also increases the client bundle size, but preserves diagrams already used by the canonical documentation. diff --git a/.agents/skills/dsh-doc-site-sync/SKILL.md b/.agents/skills/dsh-doc-site-sync/SKILL.md index bee1b0dfe8..4d88d3f04f 100644 --- a/.agents/skills/dsh-doc-site-sync/SKILL.md +++ b/.agents/skills/dsh-doc-site-sync/SKILL.md @@ -24,7 +24,7 @@ Repository translations follow the sibling pairing contract: English `foo.md`, C - **Publish a generated catalog:** map the generated `docs/` file, but change its generator or source metadata rather than editing the catalog by hand. - **Change site structure:** update the manifest for ordinary pages; update VitePress configuration only when the existing sidebar, section, or locale model cannot express the change. -Never edit or commit `website/.generated/`, `website/.cache/`, or `website/.dist/`. Never copy a maintained `docs/` page into `website/`. +Never edit or commit `website/.generated/`, `website/.cache/`, or `website/.dist/`. Except for `website/AGENTS.md`, never add Markdown under `website/`; locale and route directories such as `website/zh-CN/`, `website/en/`, and `website/api/` are invalid source layouts. Keep generated catalogs under `docs/`, freshness-gate them there, and publish them through the manifest. ## Add or update a manifest entry diff --git a/scripts/project-doc-site.spec.ts b/scripts/project-doc-site.spec.ts index bd6cfb14c7..7d1c3c1550 100644 --- a/scripts/project-doc-site.spec.ts +++ b/scripts/project-doc-site.spec.ts @@ -1,13 +1,19 @@ /** Tests for the documentation website projection adapter. */ -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { execFileSync } from 'node:child_process' +import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' -import { join } from 'node:path' +import { join, resolve } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { docsPages, type DocsPage } from '../website/docs.ts' import { addProjectionFrontmatter, projectedPageContent, rewriteMarkdown } from './project-doc-site.ts' const roots: string[] = [] +const repositoryRoot = resolve(import.meta.dirname, '..') + +function unexpectedWebsiteMarkdown(files: readonly string[]): string[] { + return files.filter(file => file.endsWith('.md') && file !== 'website/AGENTS.md').sort() +} afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }) @@ -34,6 +40,29 @@ function fixture(): { root: string; pages: DocsPage[] } { } } +describe('website source layout', () => { + it('rejects Markdown outside the subtree instructions', () => { + expect(unexpectedWebsiteMarkdown([ + 'website/AGENTS.md', + 'website/docs.ts', + 'website/zh-CN/api/harness/service.md', + ])).toEqual(['website/zh-CN/api/harness/service.md']) + }) + + it('contains no tracked or unignored documentation copies', () => { + const files = execFileSync( + 'git', + ['ls-files', '--cached', '--others', '--exclude-standard', '--', 'website'], + { cwd: repositoryRoot, encoding: 'utf8' }, + ).split('\n').filter(file => file !== '' && existsSync(resolve(repositoryRoot, file))) + + expect( + unexpectedWebsiteMarkdown(files), + 'Keep canonical Markdown under docs/ and publish it through website/docs.ts.', + ).toEqual([]) + }) +}) + describe('rewriteMarkdown', () => { it('maps published pages and pins unpublished source links', () => { const { root, pages } = fixture() diff --git a/website/AGENTS.md b/website/AGENTS.md new file mode 100644 index 0000000000..333041fd28 --- /dev/null +++ b/website/AGENTS.md @@ -0,0 +1,13 @@ +# AGENTS.md — Documentation website adapter + +Follow the [root instructions](../AGENTS.md), the [documentation standard](../docs/AGENTS.md), and the [documentation-site sync workflow](../.agents/skills/dsh-doc-site-sync/SKILL.md). + +## Keep documentation content out of this tree + +`website/` owns only VitePress configuration, presentation assets, and the publication manifest. This file is the only maintained Markdown file in this subtree. + +Keep canonical prose and generated catalogs in their owning `docs/` tier, then expose selected pages through [docs.ts](docs.ts). Never add locale, route, API, or copied documentation trees such as `website/zh-CN/`, `website/en/`, or `website/api/`. + +The projector writes disposable Markdown to the ignored `website/.generated/` directory. Never edit or commit `.generated/`, `.cache/`, or `.dist/`. + +Run `pnpm docs:check` after changing this subtree; the gate rejects additional non-ignored Markdown under `website/`. diff --git a/website/docs.ts b/website/docs.ts index 528fa36110..51a8576b91 100644 --- a/website/docs.ts +++ b/website/docs.ts @@ -256,6 +256,7 @@ const reference = mirroredPages([ ['core.md', '核心数据结构', 'Core data structures'], ['scope.md', '作用域', 'Scopes'], ['session.md', '会话', 'Sessions'], + ['goal.md', '目标', 'Goals'], ['system-prompt.md', '系统提示词', 'System prompts'], ['tools.md', '工具', 'Tools'], ['llm-streaming.md', 'LLM 流式响应', 'LLM streaming'], @@ -268,6 +269,7 @@ const reference = mirroredPages([ ['skills.md', '技能', 'Skills'], ['approval.md', '审批', 'Approvals'], ['user-interaction.md', '用户交互', 'User interaction'], + ['commands.md', '命令', 'Human commands'], ['sandbox.md', '沙箱', 'Sandboxing'], ['web.md', 'Web 访问', 'Web access'], ['persistence.md', '会话持久化', 'Session persistence'], diff --git a/website/zh-CN/api/harness/commands.md b/website/zh-CN/api/harness/commands.md deleted file mode 100644 index 3b92682dac..0000000000 --- a/website/zh-CN/api/harness/commands.md +++ /dev/null @@ -1,91 +0,0 @@ - - -# ctx.commands - -`CommandService` — provided by `@deepseek-ai/dsh-commands`. - -Human-command registry. Plain-context definitions are global; definitions registered through a command-injected child of an agent context shadow globals for that agent. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/commands/src/index.ts#L207) - -### ctx.commands.register(definition) - -```ts website-api -/** - * Register a global or calling-agent-scoped command. - * @param definition - discovery metadata and direct UI handler. - * @returns the exact effect disposer that unregisters this definition. - */ -register(definition: CommandDefinition): () => void -``` - -Register a global or calling-agent-scoped command. - -- `definition` — discovery metadata and direct UI handler. - -**Returns** the exact effect disposer that unregisters this definition. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/commands/src/index.ts#L220) - -### ctx.commands.list(agent) - -```ts website-api -/** - * List the effective immutable command descriptors for one agent. - * @param agent - exact receiving agent and scoped-layer key. - * @returns name-sorted descriptors after scoped shadowing. - */ -list(agent: Agent): readonly CommandDescriptor[] -``` - -List the effective immutable command descriptors for one agent. - -- `agent` — exact receiving agent and scoped-layer key. - -**Returns** name-sorted descriptors after scoped shadowing. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/commands/src/index.ts#L247) - -### ctx.commands.find(agent, name) - -```ts website-api -/** - * Resolve one effective command definition. - * @param agent - exact receiving agent and scoped-layer key. - * @param name - command name without a slash. - * @returns the scoped shadow or global definition. - */ -find(agent: Agent, name: string): CommandDefinition | undefined -``` - -Resolve one effective command definition. - -- `agent` — exact receiving agent and scoped-layer key. -- `name` — command name without a slash. - -**Returns** the scoped shadow or global definition. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/commands/src/index.ts#L260) - -### ctx.commands.execute(agent, line, signal) - -```ts website-api -/** - * Parse and execute a known command without sending it to the model. - * @param agent - exact receiving agent. - * @param line - complete slash-command line. - * @param signal - cancellation signal owned by the UI request. - * @returns a detached result, or `undefined` when syntax or name does not resolve. - */ -async execute( agent: Agent, line: string, signal: AbortSignal, ): Promise -``` - -Parse and execute a known command without sending it to the model. - -- `agent` — exact receiving agent. -- `line` — complete slash-command line. -- `signal` — cancellation signal owned by the UI request. - -**Returns** a detached result, or `undefined` when syntax or name does not resolve. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/commands/src/index.ts#L271) diff --git a/website/zh-CN/api/harness/goals.md b/website/zh-CN/api/harness/goals.md deleted file mode 100644 index 828a5eced3..0000000000 --- a/website/zh-CN/api/harness/goals.md +++ /dev/null @@ -1,203 +0,0 @@ - - -# ctx.goals - -`GoalService` — provided by `@deepseek-ai/dsh-goal`. - -Goal service (`ctx.goals`) backed exclusively by the owning session log. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L135) - -### ctx.goals.get(agent) - -```ts website-api -/** - * Read the current goal for one exact live agent. - * @param agent - owning live agent. - * @returns a fresh view or `undefined` when no goal is current. - * @throws {@link GoalError} when the agent is not the registry's live instance. - */ -get(agent: Agent): GoalView | undefined -``` - -Read the current goal for one exact live agent. - -- `agent` — owning live agent. - -**Returns** a fresh view or `undefined` when no goal is current. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L161) - -### ctx.goals.disarm(agent) - -```ts website-api -/** - * Remove process-local continuation authority without changing durable goal - * phase or revision. Lifecycle owners use this before unloading a driver; - * a later human-authorized {@link resume} records the new activation edge. - * @param agent - owning live agent. - * @returns a fresh disarmed view, or `undefined` when no goal is current. - */ -disarm(agent: Agent): GoalView | undefined -``` - -Remove process-local continuation authority without changing durable goal phase or revision. Lifecycle owners use this before unloading a driver; a later human-authorized resume records the new activation edge. - -- `agent` — owning live agent. - -**Returns** a fresh disarmed view, or `undefined` when no goal is current. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L175) - -### ctx.goals.create(agent, request) - -```ts website-api -/** - * Create and arm a goal. A completed goal may be replaced; every other - * current phase must be cleared or resumed instead. - * @param agent - owning live agent. - * @param request - objective and optional round cap. - * @returns the created live view. - */ -create(agent: Agent, request: CreateGoalRequest): GoalView -``` - -Create and arm a goal. A completed goal may be replaced; every other current phase must be cleared or resumed instead. - -- `agent` — owning live agent. -- `request` — objective and optional round cap. - -**Returns** the created live view. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L190) - -### ctx.goals.edit(agent, ref, request) - -```ts website-api -/** - * Edit objective and/or round cap without changing phase. - * @param agent - owning live agent. - * @param ref - expected current revision. - * @param request - at least one replacement field. - * @returns the edited view. - */ -edit(agent: Agent, ref: GoalRef, request: EditGoalRequest): GoalView -``` - -Edit objective and/or round cap without changing phase. - -- `agent` — owning live agent. -- `ref` — expected current revision. -- `request` — at least one replacement field. - -**Returns** the edited view. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L215) - -### ctx.goals.pause(agent, ref) - -```ts website-api -/** - * Pause an active goal and disarm automatic continuation. - * @param agent - owning live agent. - * @param ref - expected current revision. - * @returns the paused view. - */ -pause(agent: Agent, ref: GoalRef): GoalView -``` - -Pause an active goal and disarm automatic continuation. - -- `agent` — owning live agent. -- `ref` — expected current revision. - -**Returns** the paused view. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L236) - -### ctx.goals.resume(agent, ref) - -```ts website-api -/** - * Resume and arm a stopped goal, or rearm an active goal after a - * session-start edge, while its round budget still has capacity. - * @param agent - owning live agent. - * @param ref - expected current revision. - * @returns the active view. - */ -resume(agent: Agent, ref: GoalRef): GoalView -``` - -Resume and arm a stopped goal, or rearm an active goal after a session-start edge, while its round budget still has capacity. - -- `agent` — owning live agent. -- `ref` — expected current revision. - -**Returns** the active view. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L247) - -### ctx.goals.complete(agent, ref) - -```ts website-api -/** - * Mark a current non-complete goal complete and disarm it. - * @param agent - owning live agent. - * @param ref - expected current revision. - * @returns the completed view. - */ -complete(agent: Agent, ref: GoalRef): GoalView -``` - -Mark a current non-complete goal complete and disarm it. - -- `agent` — owning live agent. -- `ref` — expected current revision. - -**Returns** the completed view. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L272) - -### ctx.goals.block(agent, ref, reason) - -```ts website-api -/** - * Mark an active goal blocked and disarm it. - * @param agent - owning live agent. - * @param ref - expected current revision. - * @param reason - policy-owned stable code and human-readable explanation. - * @returns the blocked view with its durable reason. - */ -block(agent: Agent, ref: GoalRef, reason: GoalBlockReason): GoalView -``` - -Mark an active goal blocked and disarm it. - -- `agent` — owning live agent. -- `ref` — expected current revision. -- `reason` — policy-owned stable code and human-readable explanation. - -**Returns** the blocked view with its durable reason. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L290) - -### ctx.goals.clear(agent, ref) - -```ts website-api -/** - * Clear the current goal while retaining a durable tombstone and history. - * @param agent - owning live agent. - * @param ref - expected current revision. - * @returns the tombstone ref whose revision is one past the cleared snapshot. - */ -clear(agent: Agent, ref: GoalRef): GoalRef -``` - -Clear the current goal while retaining a durable tombstone and history. - -- `agent` — owning live agent. -- `ref` — expected current revision. - -**Returns** the tombstone ref whose revision is one past the cleared snapshot. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/goal/goal/src/index.ts#L311) diff --git a/website/zh-CN/api/harness/invariants.md b/website/zh-CN/api/harness/invariants.md deleted file mode 100644 index e2f582a566..0000000000 --- a/website/zh-CN/api/harness/invariants.md +++ /dev/null @@ -1,32 +0,0 @@ - - -# ctx.invariants - -`InvariantService` — provided by `@deepseek-ai/dsh-invariants`. - -Package-owned invariant registry with global and regex-based selection. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/support/invariants/src/index.ts#L388) - -### ctx.invariants.register(packageName, installer) - -```ts website-api -/** - * Register one package's invariant installer. The package name is reserved - * even when filtering disables its checks. Enabled installers run in a child - * fiber; failure disposes that fiber and releases the reservation. - * @param packageName - full npm package name that owns the contribution. - * @param installer - listener or startup-check installer for the child context. - * @returns an effect-scoped disposer for the registration. - */ -register(packageName: string, installer: InvariantInstaller): () => void -``` - -Register one package's invariant installer. The package name is reserved even when filtering disables its checks. Enabled installers run in a child fiber; failure disposes that fiber and releases the reservation. - -- `packageName` — full npm package name that owns the contribution. -- `installer` — listener or startup-check installer for the child context. - -**Returns** an effect-scoped disposer for the registration. - -[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/support/invariants/src/index.ts#L430)