Merge remote-tracking branch 'origin/master' into worktree/ci-native-windows-20260808
# Conflicts: # vendor/README.md
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md
|
||||
2026-06-20-core-data-structures-catalog.md: 7ee1e0ac3df7cb37fc9797702d44f409da820a94
|
||||
2026-06-20-core-data-structures-catalog.zh.md: 7cb0ae216f5c5f429c18d097862350997a8335d3
|
||||
2026-06-20-core-data-structures-catalog.md: 9a3f80df592f28adc365d5e64cc15516d92b47b5
|
||||
2026-06-20-core-data-structures-catalog.zh.md: 96c606a3d65a67e757960c5cbc997e717f11e8d5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Agent Note: Core-data-structures catalog and the `ts type-equiv` drift gate
|
||||
# Agent Note: Subsystems catalog and the `ts type-equiv` drift gate
|
||||
|
||||
Status: implemented
|
||||
|
||||
@@ -12,20 +12,22 @@ So the work had two intertwined questions: **what belongs in such a catalog** (t
|
||||
|
||||
## Decision
|
||||
|
||||
A new `docs/core-data-structures/` folder catalogs the vocabulary, with a new `verify-type-equiv` doc-sync gate that keeps every pasted type declaration and its JSDoc synchronized with source.
|
||||
A new `docs/subsystems/` folder catalogs the vocabulary, with a new `verify-type-equiv` doc-sync gate that keeps every pasted type declaration and its JSDoc synchronized with source.
|
||||
|
||||
### What counts as "core" — the spine-vs-seam line
|
||||
|
||||
The scoping line was not picked top-down; it was discovered by testing candidate definitions against concrete borderline types until one rule survived every case. The decisive test was `BashExecRequest`/`BashExecSpec`/`BashRunResult`: bash is a capability *seam*, not part of the agent-loop spine, so if those are "core" then "core" means *all cross-package vocabulary* and the catalog is a flat dump; if they are not, "core" means *the central spine* and bash vocabulary belongs on a sub-page. The latter won, which set the whole structure: a **tiered folder**, not a flat document.
|
||||
> **Superseded as the page-scoping rule** by [package-anchored subsystem pages](2026-08-03-package-anchored-subsystem-pages.md): each page now anchors to the package group that declares its vocabulary. The `ts type-equiv` mechanism below remains current.
|
||||
|
||||
The rule that settled the remaining cases: ***the type you write, hold, or receive is core; the machinery that types it, renders it, or persists it is a sub-page detail.*** Worked through:
|
||||
The scoping line was not picked top-down; it was discovered by testing candidate definitions against concrete borderline types until one rule survived every case. The decisive test was `BashExecRequest`/`BashExecSpec`/`BashRunResult`: bash is a capability *seam*, not part of the agent-loop spine, so if those are "core" then "core" means *all cross-package vocabulary* and the catalog is a flat dump; if they are not, "core" means *the central spine* and bash vocabulary belongs on its own seam page. The latter won, which set the whole structure: a **tiered folder**, not a flat document.
|
||||
|
||||
The rule that settled the remaining cases: ***the type you write, hold, or receive is core; the machinery that types it, renders it, or persists it is a seam-page detail.*** Worked through:
|
||||
|
||||
- A data structure is **core** if it flows through the agent-loop spine — the loop holds, derives, streams, or logs it on every turn regardless of which plugins load (`Message`, `StreamChunk`, `SessionEvent`, the `Agent` handle) — **or** it is the single headline type a plugin author writes against a pipeline (`ToolDefinition`).
|
||||
- `ToolDefinition` is core (it is what every tool author writes) **even though the loop never holds one** — authoring-importance overrides the strict flows-through-spine rule for this one headline type. But its typing machinery — `ValueSchemaSpec`, `ParameterSchemaSpec`, `InferValue`, and `InferArgs` — is a sub-page detail. That is the spine-vs-seam line made sharp.
|
||||
- `ToolDefinition` is core (it is what every tool author writes) **even though the loop never holds one** — authoring-importance overrides the strict flows-through-spine rule for this one headline type. But its typing machinery — `ValueSchemaSpec`, `ParameterSchemaSpec`, `InferValue`, and `InferArgs` — is a seam-page detail. That is the spine-vs-seam line made sharp.
|
||||
- `ToolSchema` is core (it is a field of `GenerateOptions`, the model request that flows through every step) even though it is conceptually part of the tool pipeline — *flows through the spine* wins over *conceptual home* when they conflict.
|
||||
- The tool-presentation vocabulary (`ToolCallView`/`ToolResultView`, …), the `SessionPersistence` durability seam, and bash vocabulary are sub-pages.
|
||||
- The tool-presentation vocabulary (`ToolCallView`/`ToolResultView`, …), the `SessionPersistence` durability seam, and bash vocabulary are seam pages.
|
||||
|
||||
`core.md` is a **self-contained spine doc**: it states the exact type definition of each spine structure with minimal prose and links to sub-pages for the per-seam detail. The sub-pages are `llm-streaming.md`, `session.md`, `persistence.md` (split from session along the in-memory-model vs. durability-seam line), `tools.md`, and `bash.md`.
|
||||
`core.md` is a **self-contained spine doc**: it states the exact type definition of each spine structure with minimal prose and links to sibling seam pages for the per-seam detail; the folder's [README](../../../../docs/subsystems/README.md) indexes every page. The original seam pages are `llm-streaming.md`, `session.md`, `persistence.md` (split from session along the in-memory-model vs. durability-seam line), `tools.md`, and `bash.md`.
|
||||
|
||||
### The `ts type-equiv` mechanism — literal AND drift-proof
|
||||
|
||||
@@ -58,3 +60,4 @@ The spine-vs-seam rule was tested against `BashExecRequest`, tool schemas and de
|
||||
- The spine-vs-seam line is a reusable scoping tool, not a one-off: the same "the thing you write/hold/receive is core; the machinery that types/renders/persists it is a detail" rule is what later scoped the events/services catalog's harness-vs-inherited tiering.
|
||||
- The `ts type-equiv` fence is a third doc-block category alongside ` ```ts ` (compiled) and ` ```ts ignore-check ` (sketch). A later sibling added a fourth, ` ```ts cordis-catalog ` (generated signature), reusing the same skip-and-exclude treatment.
|
||||
- Adding or reshaping a core type now carries a documentation obligation the author must honor (the gate cannot detect a missing *new* type), backstopped by the `dsh-code-review` checklist.
|
||||
- Since 2026-07-27 the seam-page tier spans every service-bearing subsystem: nine lean pages (permission presets, plan mode, runtime invariants, the HTTP carrier, storage — owning both `ctx.storage` and `ctx.storageDomain` — TUI extensions, workspaces, client modules, telemetry) cover the ten `ctx` services that had none, so each harness service and event scope has exactly one owning subsystems page — the precondition for generating per-subsystem service/event reference into these pages instead of flat catalogs.
|
||||
|
||||
@@ -12,10 +12,12 @@ Status: implemented
|
||||
|
||||
## 决策
|
||||
|
||||
新增的 `docs/core-data-structures/` 目录对这些词汇编目,并配有新的 `verify-type-equiv` doc-sync(文档同步门禁),使每个粘贴的类型声明及其 JSDoc 与源码保持同步。
|
||||
新增的 `docs/subsystems/` 目录对这些词汇编目,并配有新的 `verify-type-equiv` doc-sync(文档同步门禁),使每个粘贴的类型声明及其 JSDoc 与源码保持同步。
|
||||
|
||||
### 何为「核心」——主干与 seam 的分界线
|
||||
|
||||
> **作为页面范围界定规则已被取代**,见[按包锚定的子系统页面](2026-08-03-package-anchored-subsystem-pages.md):每页现在锚定到声明其词汇的包分组。下文的 `ts type-equiv` 机制仍然有效。
|
||||
|
||||
范围界定并非自上而下拍定,而是将候选定义逐一对照具体的边界类型反复测试,直到一条规则在所有案例中都成立。决定性的测试是 `BashExecRequest`/`BashExecSpec`/`BashRunResult`:bash 是一个能力 *seam*,不属于 agent loop(智能体循环)主干;如果这些算「核心」,那么「核心」就意味着*所有跨包词汇*,目录沦为平铺罗列;如果不算,「核心」就意味着*中央主干*,bash 词汇归入子页面。后者胜出,由此确定了整体结构:一个**分层文件夹**,而非一份平铺文档。
|
||||
|
||||
确定其余案例的规则是:***你编写、持有或接收的类型是核心;为其提供类型推导、渲染或持久化的机制是子页面细节。*** 逐一验证如下:
|
||||
@@ -25,7 +27,7 @@ Status: implemented
|
||||
- `ToolSchema` 是核心(它是流经每个步骤的模型请求 `GenerateOptions` 的一个字段),即使它在概念上属于工具流水线——当*流经主干*与*概念归属*冲突时,前者胜出。
|
||||
- 工具展示词汇(`ToolCallView`/`ToolResultView` 等)、`SessionPersistence` 持久性 seam 以及 bash 词汇是子页面。
|
||||
|
||||
`core.md` 是一份**自包含的主干文档**:它给出每个主干结构的确切类型定义,辅以最少的行文,并链接到子页面获取各 seam 的细节。子页面包括 `llm-streaming.md`、`session.md`、`persistence.md`(沿内存模型与持久性 seam 的分界线从会话页面拆出)、`tools.md` 和 `bash.md`。
|
||||
`core.md` 是一份**自包含的主干文档**:它给出每个主干结构的确切类型定义,辅以最少的行文,并链接到同级 seam 页面获取各 seam 的细节;目录的 [README](../../../../docs/subsystems/README.md) 索引全部页面。最初的 seam 页面包括 `llm-streaming.md`、`session.md`、`persistence.md`(沿内存模型与持久性 seam 的分界线从会话页面拆出)、`tools.md` 和 `bash.md`。
|
||||
|
||||
### `ts type-equiv` 机制——既逐字又防漂移
|
||||
|
||||
@@ -58,3 +60,4 @@ Status: implemented
|
||||
- 主干与 seam 分界线是一个可复用的范围界定工具,而非一次性的:同一条「你编写/持有/接收的东西是核心;为其提供类型推导/渲染/持久化的机制是细节」规则,后来也被用于界定事件/服务目录的 harness 层与继承层分层。
|
||||
- `ts type-equiv` 围栏是继 ` ```ts `(编译)和 ` ```ts ignore-check `(草稿)之后的第三种文档块类别。后续的姊妹门禁又增加了第四种 ` ```ts cordis-catalog `(生成签名),复用了相同的跳过并排除处理。
|
||||
- 添加或重塑核心类型现在附带一项文档义务,作者必须履行(门禁无法检测缺失的*新*类型),由 `dsh-code-review` 检查清单兜底。
|
||||
- 自 2026-07-27 起,子页面层级覆盖每个承载服务的子系统:九个精简页面(权限预设、计划模式、运行时不变式、HTTP 载体、存储——同时拥有 `ctx.storage` 与 `ctx.storageDomain`——终端扩展、工作区、客户端模块、遥测)覆盖了原先没有页面的十个 `ctx` 服务,于是每个 harness 服务和事件作用域都有恰好一个所属的 subsystems 页面——这是把按子系统生成的服务/事件参考写入这些页面(而非平铺目录)的前提。
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-02-tool-schema-catalog.md
|
||||
2026-07-02-tool-schema-catalog.md: c8cc69df428f6eee0f66ed976865afe2a0702448
|
||||
2026-07-02-tool-schema-catalog.zh.md: a1af928cc799974b93d2c8e29792790bc1f4e9e6
|
||||
2026-07-02-tool-schema-catalog.md: 673b51831512662c4e2cc3b79cd85b149c78f585
|
||||
2026-07-02-tool-schema-catalog.zh.md: 8e47d807a2490e362bb3db6d1d634c75c60997e3
|
||||
|
||||
@@ -6,7 +6,7 @@ English | [中文](2026-07-02-tool-schema-catalog.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The repository had no single reference for the names, descriptions, and JSON Schemas actually exposed to the model. Source declarations are scattered and runtime-composed, while the existing Cordis and data-structure catalogs cover wiring and vocabulary rather than tools.
|
||||
The repository had no single reference for the names, descriptions, and JSON Schemas actually exposed to the model. Source declarations are scattered and runtime-composed, while the existing Cordis reference and subsystem pages cover wiring and vocabulary rather than tools.
|
||||
|
||||
## Decision
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Status: implemented
|
||||
|
||||
## 问题
|
||||
|
||||
仓库此前没有一份统一的参考文档来记录实际暴露给模型的工具名称、描述与 JSON Schema。源码声明分散各处且在运行时组合,而既有的 Cordis 目录和数据结构目录覆盖的是接线与词汇,而非工具。
|
||||
仓库此前没有一份统一的参考文档来记录实际暴露给模型的工具名称、描述与 JSON Schema。源码声明分散各处且在运行时组合,而既有的 Cordis 参考和子系统页面覆盖的是接线与词汇,而非工具。
|
||||
|
||||
## 决策
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-06-node-engine-floor.md
|
||||
2026-07-06-node-engine-floor.md: e42b809f83fe388526ecbd74192989e0bcdbba67
|
||||
2026-07-06-node-engine-floor.zh.md: 92fa00f7a270f4506cba6c899e9edf6a2fdd3209
|
||||
2026-07-06-node-engine-floor.md: ad21fbfe034cbf4bb7b75aaac4113f3139a1832d
|
||||
2026-07-06-node-engine-floor.zh.md: 0d1c31ed74565f9b20116cd0e666246baad9bf79
|
||||
|
||||
@@ -14,7 +14,7 @@ Set `engines.node` to `^22.19.0 || >=24.0.0` and test keyless CI on `['22.19', 2
|
||||
|
||||
Two Node features gate the source runtime:
|
||||
|
||||
- **`node:sqlite`** — `packages/session-persistence/session-persistence-sqlite` does a top-level `import { DatabaseSync } from 'node:sqlite'`. The module dropped its `--experimental-sqlite` flag requirement at **22.13** (LTS) and **23.4** (Current); before those, importing it throws at load.
|
||||
- **`node:sqlite`** — `packages/session/session-persistence-sqlite` does a top-level `import { DatabaseSync } from 'node:sqlite'`. The module dropped its `--experimental-sqlite` flag requirement at **22.13** (LTS) and **23.4** (Current); before those, importing it throws at load.
|
||||
- **Native TypeScript type-stripping** — the built-mode `examples/headless-agent/tests/keyless-smoke.e2e.ts` smoke boots its unexported `.ts` driver under plain `node` (no tsx) and loads the example's `.ts` test adapter (`cli-mock-llm.ts`). Type-stripping is the default from **22.18** (LTS) and **23.6** (Current); before those it needs `--experimental-strip-types`.
|
||||
|
||||
Those source features clear on the 22.x line at **22.18**, but the installed Pi adapter dependency raises the advertised LTS floor. `@deepseek-ai/dsh-llm-pi-ai` depends on `@earendil-works/pi-ai@0.79.3`, whose package declares `engines.node >=22.19.0`, so the LTS floor is **22.19**. The 24.x branch remains `>=24.0.0`. The disjoint range excludes Node 23 entirely: Node 23.0–23.5 still has at least one flagged source feature, and the 23 line is non-LTS/EOL, so advertising `>=23.6` would add a dead release line and a CI leg no deployment should use.
|
||||
|
||||
@@ -14,7 +14,7 @@ Status: implemented
|
||||
|
||||
两个 Node 特性决定了源码运行时的门槛:
|
||||
|
||||
- **`node:sqlite`**:`packages/session-persistence/session-persistence-sqlite` 在顶层执行 `import { DatabaseSync } from 'node:sqlite'`。该模块在 **22.13**(LTS)和 **23.4**(Current)取消了 `--experimental-sqlite` 标志要求;在此之前,导入它会在加载时抛出异常。
|
||||
- **`node:sqlite`**:`packages/session/session-persistence-sqlite` 在顶层执行 `import { DatabaseSync } from 'node:sqlite'`。该模块在 **22.13**(LTS)和 **23.4**(Current)取消了 `--experimental-sqlite` 标志要求;在此之前,导入它会在加载时抛出异常。
|
||||
- **原生 TypeScript 类型剥离**——构建模式的 `examples/headless-agent/tests/keyless-smoke.e2e.ts` 冒烟测试使用纯 `node`(无 tsx)启动该示例未导出的 `.ts` driver,并加载示例的 `.ts` 测试适配器(`cli-mock-llm.ts`)。类型剥离从 **22.18**(LTS)和 **23.6**(Current)起成为默认行为;更早版本需要 `--experimental-strip-types`。
|
||||
|
||||
这些源码特性在 22.x 线上于 **22.18** 全部就绪,但已安装的 Pi 适配器依赖将宣传的 LTS 下限进一步提高。`@deepseek-ai/dsh-llm-pi-ai` 依赖 `@earendil-works/pi-ai@0.79.3`,后者的包声明 `engines.node >=22.19.0`,因此 LTS 下限为 **22.19**。24.x 分支保持 `>=24.0.0`。该不相交范围完全排除了 Node 23:Node 23.0–23.5 至少还有一个源码特性需要标志,而 23 线是非 LTS/已 EOL 的,宣传 `>=23.6` 会增加一条已终止的发布线和一条 CI 分支,而没有任何部署应当使用它。
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-28-per-subsystem-cordis-surface-regions.md
|
||||
2026-07-28-per-subsystem-cordis-surface-regions.md: ef65bfc4c7dadd7dafe1a38f41656e6ecc61ea50
|
||||
2026-07-28-per-subsystem-cordis-surface-regions.zh.md: 1df18d7260800b89c95a9d4aeb0301adb0ee56f6
|
||||
@@ -0,0 +1,36 @@
|
||||
# Agent Note: Per-subsystem generated cordis-surface regions
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-28-per-subsystem-cordis-surface-regions.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
One subsystem's documentation was split across three homes: its hand-written subsystems page (introduction, data structures, verbs), its `ctx.<key>` slice of the flat generated `docs/cordis-catalog/services.md`, and its event scope's slice of the flat `docs/cordis-catalog/events.md`. A reader of bash.md had to open two more documents to see the service interface and events the page was describing, and nothing tied the three views together beyond hand-maintained links. The flat catalogs also sat outside the bilingual corpus (excluded from pairing because generated output is English-only), so the reference surface had no Chinese route at all.
|
||||
|
||||
The [generated-catalog decision](../../archived/process/2026-06-20-generated-cordis-catalog.md) — generate from source, `@mode` tags cross-checked, fail-closed type-link coverage, the `ts cordis-catalog` fence — is not in question; what changed is WHERE the generated output lands.
|
||||
|
||||
## Decision
|
||||
|
||||
`gen-cordis-catalog.ts` injects each subsystem's service and event reference INTO its own page, between `<!-- BEGIN GENERATED cordis-surface … -->` / `<!-- END GENERATED cordis-surface -->` markers, and the flat services/events catalogs are deleted. One page per subsystem now carries introduction, data structures, and the generated wiring surface.
|
||||
|
||||
- **Curated fail-loud partition.** `SERVICE_PAGE` maps every discovered `ctx.<key>` to exactly one page; `EVENT_SCOPE_PAGE` maps every event scope. The generator hard-errors in both directions — an unmapped discovered service/scope, and a mapped key/scope the walk no longer discovers — so the partition cannot drift from the source surface. An independent scan of EVERY `declare module 'cordis'` Context merge backstops the rendering walk's blind spot (it only sees a root `index.ts` with a same-named service class): a declared key the walk cannot render must carry a named `SERVICE_WALK_EXEMPTIONS` reason (today: the `ctx.agent` DX accessor, plus the interface-typed or non-index-declared lsp/apiProxy/appShell/tuiPrompt/tuiResumeHost), and stale exemptions hard-error; a `TODO(cordis-catalog-interface-services)` marks teaching the walk to render them.
|
||||
- **Byte-identical regions across the pair.** The generator writes the SAME English region bytes into `foo.md` and `foo.zh.md`, extending the existing rule that verbatim code fences match across a pair. `verify-translation-pairing` gained a dedicated region-identity check (`partitionGeneratedRegions` in `translation-pairing.ts` owns the marker grammar) that names a divergent or malformed region precisely; the whole-document structural signature still covers the region content a second time.
|
||||
- **Guarded pair auto-record.** A regeneration that changes region bytes would leave every touched pair out-of-sync, so the generator re-records a pair's `.i18n.yaml` itself — but ONLY when the write is region-confined: both sides' recorded blob hashes must match the pre-write bytes, and the region-STRIPPED content must be unchanged on both sides. Human-prose drift leaves the record stale so the pairing gate still forces the normal translation flow; a brand-new pair is never auto-recorded (the author's reviewed `--write` owns that). This keeps `.i18n.yaml` as plain `git hash-object` values — no stripped-hash semantics change.
|
||||
- **The inherited tier moved, not died.** The vendor `ctx` members and `internal/*`/loader/hmr/timer events render to `docs/cordis-api/inherited.md`, next to the relocated Cordis core API pages (`docs/cordis-catalog/core/` → `docs/cordis-api/`). Framework surface lives under a framework home; the harness pages stay repository-owned vocabulary.
|
||||
- **In-page links.** Signature `Types:` lines link sibling pages (`core.md`, `bash.md`); a type whose primary page is the rendering page is dropped from the line instead of self-linking. Pages reference their own region with `#cordis-surface` or a `#ctx<key>--<class>` anchor — every generated heading is preceded by an explicit `<a id>` carrying the GitHub slug (the historical flat-catalog anchor), so the fragments resolve identically on GitHub and the VitePress site, whose own slugger treats the punctuation-heavy headings differently.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Keep the flat catalogs alongside the regions, both generated** — rejected: every JSDoc edit would produce double diff noise, and the scattering (one subsystem, three documents) this change exists to remove would survive.
|
||||
- **Generator-owned whole pages with hand-written intros in fragment files** — rejected: the narrative prose is the majority of every existing page and belongs in the reviewed document itself; markers cost one grammar rule and keep authors editing the real file.
|
||||
- **Localized regions (generator emits Chinese too)** — deferred, same status as the i18n README's long-standing note for the remaining generated docs: teaching the generator zh output means translating source JSDoc, which is machinery this change does not need. English regions inside zh pages match the existing status quo of English JSDoc inside verbatim fences.
|
||||
- **Hashing region-stripped content in `.i18n.yaml`** — rejected: the record would stop being `git hash-object` of the file, breaking the recover-last-confirmed-text property and every consumer that recomputes hashes.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A subsystem's whole story is one page: `docs/subsystems/<name>.md` (and its pair) carries introduction, data structures/verbs, and the generated service/event surface; `docs/cordis-catalog/` no longer exists.
|
||||
- A new service or event scope cannot ship undocumented or unmapped: the generator fails until `SERVICE_PAGE`/`EVENT_SCOPE_PAGE` names its owning page, and the page must already exist with markers in both language sides.
|
||||
- Regeneration after a source-JSDoc change touches the affected pages in both languages plus (when region-confined) their pair records — a mechanical, reviewable diff; prose edits keep demanding the translation flow because the auto-record guard refuses them.
|
||||
- The website's subsystem nav lists every page (38 routes per locale: 35 translated pairs plus the three still-English-mirrored goal/pty/commands), replacing the two flat catalog nav entries; the Cordis API section gained `inherited.md`.
|
||||
- `packages/typert/generator/tests/cordis-catalog-contract.spec.ts` pins the region renderer (`renderPageRegion`), the same-page link-drop rule, and the fail-loud JSDoc/type-link validation; `scripts/translation-pairing.spec.ts` pins the marker grammar and blob-hash primitive; `scripts/gen-cordis-catalog-record.spec.ts` proves the auto-record guard refuses every invalid state (stale record, malformed or renamed-key sidecar, extra entries, prose drift, missing record, missing snapshot).
|
||||
@@ -0,0 +1,36 @@
|
||||
# Agent Note: 按子系统生成的 cordis-surface 区块
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-28-per-subsystem-cordis-surface-regions.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
一个子系统的文档过去分散在三个归属:手写的 subsystems 页面(介绍、数据结构、动词)、平铺生成的 `docs/cordis-catalog/services.md` 中属于它的 `ctx.<key>` 切片,以及平铺的 `docs/cordis-catalog/events.md` 中属于其事件作用域的切片。bash.md 的读者必须再打开两份文档,才能看到该页面正在描述的服务接口与事件;除了手工维护的链接,没有任何机制把这三个视图联系在一起。平铺目录还游离在双语语料之外(生成输出只有英文,故被排除在配对之外),于是这个参考面完全没有中文入口。
|
||||
|
||||
[生成式目录决策](../../archived/process/2026-06-20-generated-cordis-catalog.md)本身(从源码生成、`@mode` 标签交叉校验、失败关闭的类型链接覆盖、`ts cordis-catalog` 围栏)不在质疑之列;改变的只是生成输出「落在哪里」。
|
||||
|
||||
## 决策
|
||||
|
||||
`gen-cordis-catalog.ts` 把每个子系统的服务与事件参考注入到该子系统自己的页面内部,置于 `<!-- BEGIN GENERATED cordis-surface … -->` / `<!-- END GENERATED cordis-surface -->` 标记之间;平铺的 services/events 目录随之删除。现在每个子系统由一个页面同时承载介绍、数据结构和生成的接线表面。
|
||||
|
||||
- **人工维护、响亮失败的划分。** `SERVICE_PAGE` 把发现的每个 `ctx.<key>` 映射到恰好一个页面;`EVENT_SCOPE_PAGE` 映射每个事件作用域。生成器在两个方向上都会直接报错(既有被发现却未映射的服务或作用域,也有已映射但遍历不再发现的键或作用域),因此划分不可能相对源码表面发生漂移。一个独立扫描会读取每一处 `declare module 'cordis'` 的 Context 合并,为渲染遍历的盲区(它只看得到根 `index.ts` 中同名服务类)兜底:遍历渲染不了的已声明键必须在 `SERVICE_WALK_EXEMPTIONS` 中带着点名理由(目前是 `ctx.agent` 这一 DX 访问器字段,加上接口类型或不在 index 声明的 lsp/apiProxy/appShell/tuiPrompt/tuiResumeHost),陈旧豁免同样直接报错;教会遍历渲染它们的后续工作由 `TODO(cordis-catalog-interface-services)` 标记。
|
||||
- **区块在配对两侧按字节一致。** 生成器把同一份英文区块字节写入 `foo.md` 和 `foo.zh.md`,是对「围栏代码块在配对两侧逐字节一致」这一既有规则的延伸。`verify-translation-pairing` 新增了专门的区块一致性检查(标记语法归 `translation-pairing.ts` 中的 `partitionGeneratedRegions` 所有),能精确点名出现分歧或格式错误的区块;整篇文档的结构签名仍会把区块内容再覆盖一遍。
|
||||
- **带防护的配对自动记录。** 一次改变区块字节的重新生成会让每个被触及的配对失去同步,因此生成器会自行重新记录配对的 `.i18n.yaml`,但仅限本次写入完全限定在区块内的情况:两侧记录的 blob hash 必须与写入前的字节相符,且两侧剥离区块后的内容必须没有变化。人工行文若有漂移,记录就保持陈旧,配对门禁因此仍会强制走正常翻译流程;全新的配对绝不自动记录(那归作者经评审的 `--write` 所有)。这样 `.i18n.yaml` 保持为纯粹的 `git hash-object` 值:不引入任何「剥离后 hash」的语义变化。
|
||||
- **继承层搬了家,而非消亡。** vendor 的 `ctx` 成员与 `internal/*`/loader/hmr/timer 事件渲染到 `docs/cordis-api/inherited.md`,紧邻迁移后的 Cordis 核心 API 页面(`docs/cordis-catalog/core/` → `docs/cordis-api/`)。框架表面落在框架自己的归属之下;harness 页面仍是仓库自有的词汇。
|
||||
- **页内链接。** 签名的 `Types:` 行链接到兄弟页面(`core.md`、`bash.md`);若某个类型的主要页面就是正在渲染的页面,该类型会从该行去掉,而不是链接到自身。页面用 `#cordis-surface` 或 `#ctx<key>--<class>` 锚点引用自己的区块:每个生成标题前都有一个显式 `<a id>`,携带 GitHub slug(即平铺目录时期的历史锚点),因此这些片段在 GitHub 与 VitePress 站点上解析一致——后者自带的 slugger 对含大量标点的标题会得出不同结果。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **平铺目录与区块并存、两者都生成**:否决。每次 JSDoc 编辑都会产生双份 diff 噪音,而本次变更本要消除的分散状况(一个子系统、三份文档)也将延续。
|
||||
- **整页归生成器所有、手写介绍放进片段文件**:否决。叙述性行文占每个现有页面的大部分,应当留在被评审的文档本身;标记只花一条语法规则的成本,还让作者继续编辑真实文件。
|
||||
- **本地化区块(生成器同时输出中文)**:推迟,与 i18n README 中针对其余生成文档的长期备注同属一个状态:教会生成器输出中文意味着要翻译源码 JSDoc,而那是本次变更并不需要的机制。zh 页面里的英文区块,与「英文 JSDoc 出现在逐字节一致的围栏代码块内」这一既有现状相符。
|
||||
- **在 `.i18n.yaml` 中对剥离区块后的内容做 hash**:否决。记录将不再是文件的 `git hash-object`,这会破坏「还原上次确认文本」的性质,也会破坏每个自行重算 hash 的消费方。
|
||||
|
||||
## 后果
|
||||
|
||||
- 一个子系统的完整故事就在一个页面上:`docs/subsystems/<name>.md`(及其配对文件)承载介绍、数据结构/动词,以及生成的服务/事件表面;`docs/cordis-catalog/` 不复存在。
|
||||
- 新的服务或事件作用域无法在未记录、未映射的状态下落地:在 `SERVICE_PAGE`/`EVENT_SCOPE_PAGE` 点名其所属页面之前,生成器一直失败,而且该页面必须已经存在,并在两个语言侧都带有标记。
|
||||
- 源码 JSDoc 变更后的重新生成会触及两种语言的受影响页面,外加(当写入限定在区块内时)它们的配对记录:一份机械、可评审的 diff。行文编辑仍然要走翻译流程,因为自动记录防护会拒绝它们。
|
||||
- 网站的子系统导航列出每个页面(每个 locale 38 条路由:35 个已翻译配对,加上仍为英文镜像的 goal/pty/commands 三页),取代两个平铺目录导航项;Cordis API 一节新增 `inherited.md`。
|
||||
- `packages/typert/generator/tests/cordis-catalog-contract.spec.ts` 固定区块渲染器(`renderPageRegion`)、同页链接去除规则,以及响亮失败的 JSDoc 与类型链接校验;`scripts/translation-pairing.spec.ts` 固定标记语法与 blob hash 原语;`scripts/gen-cordis-catalog-record.spec.ts` 证明自动重录守卫拒绝每一种非法状态(陈旧记录、格式错误或键被改名的伴随记录、多余条目、行文漂移、记录缺失、快照缺失)。
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-03-package-anchored-subsystem-pages.md
|
||||
2026-08-03-package-anchored-subsystem-pages.md: 5ba7937f0c142727d343a107a3ff87f5f337931e
|
||||
2026-08-03-package-anchored-subsystem-pages.zh.md: 27d56158a69a8144b0915054032fb3b54493bd0b
|
||||
@@ -0,0 +1,34 @@
|
||||
# Agent Note: Package-anchored subsystem pages and thin group READMEs
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-08-03-package-anchored-subsystem-pages.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The [subsystems catalog](2026-06-20-core-data-structures-catalog.md) scoped its front page by the spine-vs-seam rule: a type was "core" if the loop holds, derives, streams, or logs it on every turn. That rule selected types, not packages, so as the folder grew to forty-plus pages the front page became a cross-package grab-bag: LLM conversation vocabulary sat above the agent contracts, the creation/ownership vocabulary (`AgentHandle`, `CreateAgentOptions`, `ResumeAgentOptions`, `AgentFactory`) was documented nowhere in the folder because the generator exempted it to a package README, and a reader could not predict which page documents a type from where the type lives. Package-group READMEs meanwhile had no common shape — some carried sectioned tables, stray design essays, or trailing paragraphs that belonged on a subsystem page.
|
||||
|
||||
## Decision
|
||||
|
||||
Every `docs/subsystems/` page anchors to the package or package group that declares its vocabulary, and page membership follows the repository layout: [core.md](../../../../docs/subsystems/core.md) is the `packages/core` page (creation and ownership, the `Agent` handle with its delivery/cancellation/interception contracts, pointers to the group's dedicated pages), [llm-streaming.md](../../../../docs/subsystems/llm-streaming.md) owns `packages/llm` end-to-end, and so on. Repo-wide type patterns (`…Map → derived-union`, branded ids) stay on core.md in an explicitly framed closing section rather than interleaved with the package content. This supersedes the spine-vs-seam rule *as the page-scoping rule*; the placement heuristic that survives is simpler: a type is documented where its declaring package's page is, and machinery keeps living with its machinery.
|
||||
|
||||
Every type a generated signature references must resolve somewhere in the folder: the agent ownership vocabulary moved from the generator's `TYPE_LINK_EXEMPTIONS` into `LINK_MAP → core.md`, so exemptions are reserved for genuinely service-local or vendored shapes. Each pasted declaration has one home (`SessionEvent` lives on [session.md](../../../../docs/subsystems/session.md); core.md summarizes and links).
|
||||
|
||||
Every `packages/<group>/README.md` pair is a thin front door in one shape: a why-first intro paragraph, a package table (Package / Role / ctx key), and a closing pointer to the owning subsystems page. Load-bearing prose that outgrows that shape relocates to the owning subsystems page rather than being deleted.
|
||||
|
||||
The [subsystems README](../../../../docs/subsystems/README.md) indexes every page in the folder on both language sides; `scripts/project-doc-site.spec.ts` enforces one table row per page, so a page added by a later PR (or absorbed in a merge) cannot silently miss the index.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep the spine-vs-seam scoping rule.** It answered "is this type core?" per type, which is why the front page accumulated types from four packages while missing half of `packages/core/agent`'s public surface. Predictability by repository layout won.
|
||||
|
||||
**A flat single-document catalog.** Already rejected in the [original catalog note](2026-06-20-core-data-structures-catalog.md); the growth to forty-one pages confirmed that verdict.
|
||||
|
||||
**Document ownership vocabulary only in package READMEs (the exemption status quo).** This left `AgentHandle` and the create/resume options invisible to the folder that claims to be the type reference, and the generated `Types:` footers could not link them.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Which page documents a type is predictable from `packages/<group>/`; the subsystems README is a complete index enforced by test.
|
||||
- Generated signature footers link the agent ownership vocabulary instead of silently exempting it.
|
||||
- `verify-type-equiv`'s 1:1 manifest keeps each paste single-homed; the duplicate `SessionEvent` paste is gone.
|
||||
- The [original catalog note](2026-06-20-core-data-structures-catalog.md) remains the owner of the `ts type-equiv` drift-gate mechanism; only its page-scoping rule is superseded here.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Agent Note:按包锚定的子系统页面与轻薄的分组 README
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-08-03-package-anchored-subsystem-pages.md) | 中文
|
||||
|
||||
## Problem
|
||||
|
||||
[子系统目录](2026-06-20-core-data-structures-catalog.md)最初用主干-vs-seam 规则界定首页范围:如果循环在每个轮次都持有、派生、流式传输或记录某个类型,它就是「核心」。该规则选择的是类型而非包,因此当目录增长到四十多页后,首页变成了跨包大杂烩:LLM 对话词汇排在 agent 契约之前,创建/所有权词汇(`AgentHandle`、`CreateAgentOptions`、`ResumeAgentOptions`、`AgentFactory`)在目录中无处记录(生成器把它们豁免给了某个包 README),读者无法根据类型所在位置预测哪一页记录它。与此同时,各包分组 README 没有统一形状——有的带分节表格、游离的设计短文,或本应属于子系统页面的尾部段落。
|
||||
|
||||
## Decision
|
||||
|
||||
每个 `docs/subsystems/` 页面锚定到声明其词汇的包或包分组,页面归属跟随仓库布局:[core.md](../../../../docs/subsystems/core.md) 是 `packages/core` 的页面(创建与所有权、`Agent` 句柄及其投递/取消/拦截契约、指向该组专属页面的指针),[llm-streaming.md](../../../../docs/subsystems/llm-streaming.md) 端到端拥有 `packages/llm`,依此类推。全仓通用类型模式(`…Map → 派生联合`、品牌化 id)保留在 core.md 一个明确标注的收尾小节中,而不是与包内容交错。这在*页面范围界定规则*的意义上取代了主干-vs-seam 规则;存活下来的放置启发式更简单:类型记录在其声明包对应的页面,机制仍与其机制放在一起。
|
||||
|
||||
生成签名引用的每个类型都必须能在目录中某处解析:agent 所有权词汇从生成器的 `TYPE_LINK_EXEMPTIONS` 移入 `LINK_MAP → core.md`,因此豁免只留给真正服务本地或 vendored 的形状。每个粘贴的声明只有一个家(`SessionEvent` 位于 [session.md](../../../../docs/subsystems/session.md);core.md 概括并链接)。
|
||||
|
||||
每个 `packages/<group>/README.md` 配对都是统一形状的轻薄门面:一段以「为什么」开头的介绍、一张包表格(包 / 角色 / ctx 键)、一个指向拥有方子系统页面的收尾指针。超出该形状的承重散文迁移到拥有方子系统页面,而非删除。
|
||||
|
||||
[子系统 README](../../../../docs/subsystems/README.md) 在两个语言侧索引目录中的每一页;`scripts/project-doc-site.spec.ts` 强制每页一行表格,因此后续 PR 新增(或合并吸收)的页面无法悄悄缺席索引。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**保留主干-vs-seam 界定规则。**它逐类型回答「这个类型是核心吗?」,这正是首页积累了四个包的类型、却缺失 `packages/core/agent` 一半公开 surface 的原因。按仓库布局可预测性胜出。
|
||||
|
||||
**扁平的单文档目录。**在[原目录 note](2026-06-20-core-data-structures-catalog.md) 中已被否决;增长到四十一页证实了该结论。
|
||||
|
||||
**只在包 README 中记录所有权词汇(豁免的现状)。**这让 `AgentHandle` 与 create/resume 选项在自称类型参考的目录中不可见,生成的 `Types:` 页脚也无法链接它们。
|
||||
|
||||
## Consequences
|
||||
|
||||
- 哪一页记录某类型可由 `packages/<group>/` 预测;子系统 README 是由测试强制的完整索引。
|
||||
- 生成的签名页脚链接 agent 所有权词汇,而不是静默豁免。
|
||||
- `verify-type-equiv` 的 1:1 manifest 保证每个粘贴单一归属;重复的 `SessionEvent` 粘贴已移除。
|
||||
- [原目录 note](2026-06-20-core-data-structures-catalog.md) 仍拥有 `ts type-equiv` 漂移检查机制;此处仅取代其页面范围界定规则。
|
||||
Reference in New Issue
Block a user