docs(notes): aggressive archive sweep of low-value decision records
Archive 21 implemented triplets whose shipped decisions are complete and whose bodies no longer guide future work (one-off UI chrome, generator applications whose scripts are self-explanatory, superseded implementation detail, process history owned by current contracts/skills). Delete 4 rejected triplets whose premises are obsolete: the DeepReadonly proposal (dev-invariants note now carries the alternative inline), the collapse tool-owned presentation proposal (superseded by the shipped render-intent union), retire-mid-turn-steering (steering is now load-bearing across plan-mode/apiproxy/TUI), and single-session-ACP (automation-only ACP resolved the question; multi-session isolation is pinned by tests). Repair every inbound link: retarget intentional historical citations to archived paths, replace decision-current citations with the surviving authority, and fix the stale example-execute-over-tsx pointer in pnpm-workspace.yaml. Re-record pairing sidecars and seal the archive manifest (append-only; existing seals unchanged).
This commit is contained in:
@@ -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
|
||||
2026-06-11-tsdown-over-dumble.md: b622fd41b262147e3a42ca590ccb192dd83d0b9f
|
||||
2026-06-11-tsdown-over-dumble.zh.md: 2200cb8c1776c5f50913ed7e3cfb9a6c305d31e0
|
||||
@@ -0,0 +1,31 @@
|
||||
# Agent Note: tsdown for JS bundling instead of dumble
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-06-11-tsdown-over-dumble.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The initial build used **dumble**, the cordiverse zero-config esbuild wrapper that upstream Cordis itself builds with — maximum alignment with the vendored packages' conventions (it reads each package.json and infers entries/formats from the `exports` field). But dumble is a liability as a load-bearing tool in this repo: v0.2.x, ~530 npm downloads/week, effectively one maintainer, and we were invoking it through a custom orchestration script (`scripts/build.ts`) because it has no workspace mode.
|
||||
|
||||
Build output currently matters only for `pnpm run build` + publint (nothing publishes yet; dev/test/demo run unbuilt via tsx), so the switching cost is at its lowest now and only grows once packages publish.
|
||||
|
||||
## Decision
|
||||
|
||||
Replace dumble with **tsdown** (rolldown-based, ~2.5M downloads/week, VoidZero-backed, actively released):
|
||||
|
||||
- Root `tsdown.config.ts` with `workspace: ['vendor/*', 'packages/*/*']` (explicit globs keep bundling to vendored Cordis and the TypeScript package tree; `workspace: true` would also discover example manifests and non-bundled workspace members).
|
||||
- Shared shape: entry `lib/types/index.js`, `outDir: 'lib'`, ESM, `platform: node`, `target: es2024`, `fixedExtension: false` (keeps `.js` for `"type": "module"` packages), `dts: false` (tsc -b owns declarations), `clean: false` (lib/ also holds TSC's `lib/types` intermediate tree). The entry was originally `src/index.ts`; the [TSC-first build Agent Note](2026-06-17-ts-build-config.md) later moved tsdown to bundling TSC-emitted JS so TypeScript transform behavior comes from one compiler.
|
||||
- Two per-package overrides in vendor/ (ours, like the regenerated tsconfigs; logged in vendor/README.md): schemastery (dual `.mjs`/`.cjs` via `outExtensions`), logger-console (two single-entry passes so the shared base class is inlined into each entry instead of a hash-named chunk, matching upstream's published shape).
|
||||
- `scripts/build.ts` deleted; `pnpm run build` = `tsc -b && tsdown` (the root solution owns the emit graph).
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **A direct esbuild script** — the most established engine and zero wrapper risk, but hand-maintains the per-package spec table tsdown's workspace mode gives us.
|
||||
- **pkgroll** — the closest drop-in philosophically, but 78k downloads/week and Rollup-based: strictly weaker maintenance story than tsdown.
|
||||
- **Keep dumble** — perfect upstream alignment, unacceptable bus factor.
|
||||
|
||||
## Consequences
|
||||
|
||||
Runtime bundle outputs still follow the dumble-era public entry shape (`lib/index.js`, plus package-specific variants such as `schemastery`'s `lib/index.mjs`/`lib/index.cjs` and `logger-console`'s `lib/browser.js`); declarations now live under `lib/types` per the [TSC-first build Agent Note](2026-06-17-ts-build-config.md). Externals still come from each package's dependencies/peerDependencies. We give up dumble's exports-field inference — new packages with non-default shapes need a per-package `tsdown.config.ts` instead of just package.json fields. Future option: tsdown could also absorb declaration bundling (isolatedDeclarations) if `tsc -b` ever becomes the bottleneck; that would be a new Agent Note.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Agent Note: 使用 tsdown 替代 dumble 进行 JS 打包
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-06-11-tsdown-over-dumble.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
最初的构建使用 **dumble**,即 cordiverse 的零配置 esbuild 包装层——上游 Cordis 自身也用它构建——与 vendor 包(package)的约定最大程度对齐(它读取每个 package.json 并从 `exports` 字段推断入口/格式)。但 dumble 作为本仓库的承重工具存在隐患:v0.2.x,每周约 530 次 npm 下载,实质上只有一位维护者,而且由于它没有 workspace 模式,我们不得不通过自定义编排脚本(`scripts/build.ts`)来调用它。
|
||||
|
||||
目前构建产物只在 `pnpm run build` + publint 中有意义(尚未发布任何包;开发/测试/演示通过 tsx 直接运行未打包的源码),因此切换成本现在最低,一旦包开始发布就只会更高。
|
||||
|
||||
## 决策
|
||||
|
||||
用 **tsdown**(基于 rolldown,每周约 250 万次下载,VoidZero 支持,活跃发布)替代 dumble:
|
||||
|
||||
- 根目录 `tsdown.config.ts`,配置 `workspace: ['vendor/*', 'packages/*/*']`(显式 glob 将打包范围限定在 vendor 的 Cordis 与 TypeScript 包目录树内;`workspace: true` 还会发现示例 manifest 和不需要打包的 workspace 成员)。
|
||||
- 共享形状:入口为 `lib/types/index.js`,`outDir: 'lib'`,ESM,`platform: node`,`target: es2024`,`fixedExtension: false`(为 `"type": "module"` 包保留 `.js`),`dts: false`(声明归 tsc -b 所有),`clean: false`(lib/ 还保存 TSC 的 `lib/types` 中间树)。入口最初是 `src/index.ts`;[TSC 优先构建 Agent Note(agent 决策记录)](2026-06-17-ts-build-config.md)随后将 tsdown 改为打包 TSC 输出的 JS,使 TypeScript 转换行为统一由一个编译器提供。
|
||||
- vendor/ 中有两个按包覆盖的配置(属于我们自己的修改,与重新生成的 tsconfig 类似;记录在 vendor/README.md 中):schemastery(通过 `outExtensions` 输出双格式 `.mjs`/`.cjs`)、logger-console(两次单入口 pass,使共享基类被内联到每个入口而非生成哈希命名的分片,与上游发布形态一致)。
|
||||
- `scripts/build.ts` 删除;`pnpm run build` = `tsc -b && tsdown`(根 solution 拥有 emit 图)。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **直接编写 esbuild 脚本**:最成熟的引擎,零包装层风险,但需要手动维护 tsdown workspace 模式自动提供的按包规格表。
|
||||
- **pkgroll**:理念上最接近的直接替代品,但每周仅 78k 下载且基于 Rollup,维护前景严格弱于 tsdown。
|
||||
- **保留 dumble**:与上游完美对齐,但巴士因子不可接受。
|
||||
|
||||
## 后果
|
||||
|
||||
运行时 bundle 输出仍沿用 dumble 时代的公开入口形状(`lib/index.js`,以及包特有的变体,例如 `schemastery` 的 `lib/index.mjs`/`lib/index.cjs` 与 `logger-console` 的 `lib/browser.js`);根据 [TSC 优先构建 Agent Note](2026-06-17-ts-build-config.md),声明现位于 `lib/types` 下。External 仍来自各包的 dependencies/peerDependencies。我们放弃了 dumble 的 exports 字段推断:采用非默认形状的新包需要逐包提供 `tsdown.config.ts`,不能只依赖 package.json 字段。未来如果 `tsc -b` 成为瓶颈,tsdown 也可以接管声明打包(isolatedDeclarations);这需要另写一份 Agent Note。
|
||||
@@ -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
|
||||
2026-07-04-cordis-jsdoc-completeness-gate.md: 4b033ab52c8bfe79d1ca9db4baf3db2a533223be
|
||||
2026-07-04-cordis-jsdoc-completeness-gate.zh.md: f1c1c9976deadaaa700a8a152e4f29a31122ca1a
|
||||
@@ -0,0 +1,42 @@
|
||||
# Agent Note: JSDoc completeness gate for the cordis surface
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-07-04-cordis-jsdoc-completeness-gate.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The generated Cordis catalog enforced event dispatch modes but not complete service and event contracts. Methods could lack descriptions, and parameters or returns could be undocumented on the cross-plugin API surface where IDE guidance matters most.
|
||||
|
||||
The AGENTS.md rule ("every export has a JSDoc explaining semantics") is prose-checkable only by review; the repo's stated preference is to encode invariants in mechanical gates. The scope "cordis service functions and events" has a precise machine definition that only the catalog generator knows: events are the `interface Events` members inside `declare module 'cordis'`, and the service surface is the public methods of the class each `interface Context` key names. An ESLint rule cannot see that mapping; the generator computes it on every run.
|
||||
|
||||
## Decision
|
||||
|
||||
Extend `scripts/gen-cordis-catalog.ts` — the same walk, the same `@mode` precedent — to enforce JSDoc COMPLETENESS on everything it catalogs. `verify-cordis-catalog` runs inside `doc-sync`, so relevant documentation changes and CI exercise the same gate without separate wiring.
|
||||
|
||||
The contract:
|
||||
|
||||
- **Events** need description prose plus a non-empty `@param` for every **payload parameter**. A payload parameter is a signature parameter that carries event data; the `this` receiver annotation and the trailing waterfall `next` are exempt — `next` is dispatch machinery whose semantics the `@mode waterfall` tag (and its structural cross-check) already owns, so restating it per event would be boilerplate. Documenting an exempt parameter anyway is allowed; only absence is checked.
|
||||
- **Service classes** need class-level JSDoc, and every public method needs description prose, a non-empty `@param` per parameter, and a non-empty `@returns` unless the annotated return type is `void`/`Promise<void>` (where `@returns` stays optional — resolution timing can be worth documenting — but is never required).
|
||||
- **Stale tags error**: an `@param` naming no real parameter is a violation, mirroring the `@mode`-contradicts-signature check. Tag descriptions must be non-empty; their semantic quality beyond that is review's job.
|
||||
- **Explicitness the walk can check**: the gate is a pure-AST pass (no type checker), so a service method must annotate its return type (an inferred return cannot be classified) and surface parameters must be simple identifiers (a binding pattern has no name for `@param` to match).
|
||||
- **Violations aggregate** into one error listing every offender — a remediation pass sees the whole list at once. The previously fail-fast `@mode` checks moved into the same aggregated report, with their message texts unchanged.
|
||||
|
||||
The generator keeps two views of the same source comment: `parseJsDoc` ends entry prose at the first block tag, while the `ts cordis-catalog` signature block includes the original JSDoc with `@param`, `@returns`, and `@mode` intact. Readers therefore see the complete source contract without block-tag text leaking into the surrounding prose.
|
||||
|
||||
Negative-path tests in `packages/core/agent/tests/gen-cordis-catalog.spec.ts` drive `collectEvents`/`collectServices` against synthetic fixtures to prove each guard fires and that the exemptions hold. The authoring rule lives in the root [AGENTS.md](../../../../AGENTS.md) conventions bullet alongside the `@mode` rule.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **An ESLint rule** — cannot see the scope's machine definition (which `interface Events` members and which `ctx.<key>` classes are the cordis surface); the catalog generator computes exactly that mapping on every run, so the gate lives there.
|
||||
- **Expanding every method into a separate prose section** — rejected: the catalog stays skimmable by keeping one service section and one signature block, while the JSDoc attached to each declaration preserves the full method contract in place.
|
||||
- **An escape-hatch tag** — none exists; the surface is small and curated (12 services, 57 methods, 27 events at adoption), and the point is that the check cannot be waved off.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A new event or service method cannot land with an undocumented parameter or result: the generator refuses to regenerate and `verify-cordis-catalog` fails `doc-sync` and CI. The ~139 gaps found at adoption were filled in the same change, so the gate landed green.
|
||||
- The service surface must annotate return types explicitly and use identifier parameters. Neither constraint bound at adoption (every method already annotated; no destructured seam parameters existed); both are now load-bearing requirements a violating change will discover mechanically.
|
||||
- The general AGENTS.md JSDoc rule ("one-liners when one line suffices") acquires a stricter carve-out on this surface: a one-line summary still suffices only when the method has no parameters and a void result.
|
||||
- `@param` on `next` or `this` stays legal but unchecked — a deliberate asymmetry: the gate enforces the payload contract and refuses to demand boilerplate.
|
||||
- Each generated event or method fragment carries its original JSDoc, while the prose summary remains tag-free. Source edits therefore refresh both the readable index and the exact contract shown beside the signature.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Agent Note: 针对 Cordis 对外服务接口的 JSDoc 完整性门禁
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-07-04-cordis-jsdoc-completeness-gate.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
生成的 Cordis 目录此前强制了事件分发模式,但未强制要求完整的服务与事件契约。方法可以缺少描述,参数或返回值可以在跨插件 API 接口上不写文档——而这恰恰是 IDE 引导最重要的地方。
|
||||
|
||||
AGENTS.md 中的规则(「每个导出都有解释语义的 JSDoc」)只能靠评审以行文形式检查;本仓库的既定偏好是将不变式编码为机械门禁。「Cordis 服务函数与事件」这一范围有精确的机器定义,只有目录生成器知道:事件是 `declare module 'cordis'` 内 `interface Events` 的成员,服务接口是每个 `interface Context` 键所指向的类的公开方法。ESLint 规则看不到这层映射;生成器在每次运行时计算它。
|
||||
|
||||
## 决策
|
||||
|
||||
扩展 `scripts/gen-cordis-catalog.ts`——复用同一次遍历和同一套 `@mode` 先例——对它编目的所有内容强制执行 JSDoc 完整性要求。`verify-cordis-catalog` 在 `doc-sync` 内运行,因此相关文档变更和 CI 会执行同一门禁,无需另行接线。
|
||||
|
||||
契约如下:
|
||||
|
||||
- **事件**需要描述性文字,以及为每个**载荷参数**提供非空的 `@param`。载荷参数是携带事件数据的签名参数;`this` 接收者注解和尾部的 waterfall(瀑布式事件) `next` 免检——`next` 是分发机制,其语义已由 `@mode waterfall` 标签(及其结构交叉检查)拥有,逐事件重述只是样板代码。为免检参数写文档是允许的;只有缺失才被检查。
|
||||
- **服务类**需要类级 JSDoc,每个公开方法需要描述性文字、为每个参数提供非空的 `@param`,以及非空的 `@returns`——除非标注的返回类型是 `void`/`Promise<void>`(此时 `@returns` 可选——resolve 时机有时值得记录——但从不强制要求)。
|
||||
- **陈旧标签报错**:`@param` 命名了一个不存在的参数即为违规,与 `@mode` 与签名矛盾的检查对称。标签描述必须非空;超出此范围的语义质量由评审负责。
|
||||
- **遍历可检查的显式性**:门禁是纯 AST 遍历(不使用类型检查器),因此服务方法必须显式标注返回类型(推断的返回类型无法分类),接口参数必须是简单标识符(解构模式没有名称供 `@param` 匹配)。
|
||||
- **违规聚合**为一条错误信息,列出所有违规项——修复时一次看到完整清单。此前快速失败的 `@mode` 检查也移入同一份聚合报告,消息文本不变。
|
||||
|
||||
生成器保留同一源码注释的两种视图:`parseJsDoc` 在第一个块标签处结束条目正文,而 `ts cordis-catalog` 签名块包含原始 JSDoc,并完整保留 `@param`、`@returns` 和 `@mode`。因此,读者可以看到完整的源码契约,而块标签文本不会泄漏到周围正文中。
|
||||
|
||||
`packages/core/agent/tests/gen-cordis-catalog.spec.ts` 中的负路径测试对合成 fixture(测试前置数据)运行 `collectEvents`/`collectServices`,验证每条守卫都会触发且免检规则成立。撰写规则写在根 [AGENTS.md](../../../../AGENTS.md) 的约定条目中,与 `@mode` 规则并列。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **ESLint 规则**:无法看到该范围的机器定义(哪些 `interface Events` 成员、哪些 `ctx.<key>` 类构成 Cordis 对外服务接口);目录生成器在每次运行时恰好计算这层映射,因此门禁放在那里。
|
||||
- **将每个方法展开为单独的正文小节**:否决。目录保留一个服务章节和一个签名块,以维持可扫读性;附着于每个声明的 JSDoc 则在原处保留完整的方法契约。
|
||||
- **逃逸标签**:不设。该接口面小且经过策展(采纳时 12 个服务、57 个方法、27 个事件),要点在于检查不可豁免。
|
||||
|
||||
## 后果
|
||||
|
||||
- 新事件或服务方法不能带着未记录的参数或结果落地:生成器会拒绝重新生成,`verify-cordis-catalog` 也会使 `doc-sync` 和 CI 失败。采纳时发现的约 139 处缺口已在同一变更中补齐,因此门禁以绿色状态落地。
|
||||
- 服务接口必须显式标注返回类型并使用标识符参数。两项约束在采纳时均未构成限制(所有方法已有标注;不存在解构的 seam 参数);但二者现在是承重要求,违反时会被机械检测到。
|
||||
- AGENTS.md 中通用的 JSDoc 规则(「一行能说清就用一行」)在此接口上获得了更严格的特例:仅当方法无参数且返回 void 时,一行摘要才足够。
|
||||
- 为 `next` 或 `this` 写 `@param` 合法但不检查——这是有意的不对称:门禁强制载荷契约,拒绝要求样板代码。
|
||||
- 每个生成的事件或方法片段都带有其原始 JSDoc,而正文摘要不含标签。因此,源码编辑会同时刷新可读索引和签名旁展示的确切契约。
|
||||
@@ -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
|
||||
2026-07-04-persistence-log-catalog.md: f000b41c24761fa4914f296d0c2c07e4a26770d6
|
||||
2026-07-04-persistence-log-catalog.zh.md: 58680e56c5b8f5959baa90318f1747303392c9aa
|
||||
@@ -0,0 +1,37 @@
|
||||
# Agent Note: Generated persistence log event catalog
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-07-04-persistence-log-catalog.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`SessionEventMap` is the on-disk vocabulary, but its declarations are split across the owning session package and declaration merges. The generated persistence catalog is the single reference for every event, its complete payload declaration and source JSDoc, and the shared `SessionEvent` envelope; hand-maintained tables drift and are removed. These records are not Cordis events—observers receive them through the single `session/event` bus event—so the Cordis catalog cannot cover them. The generator discovers all declarations and the doc-sync freshness gate rejects omissions or stale output.
|
||||
|
||||
## Decision
|
||||
|
||||
Generate `docs/persistence-catalog.md` from source, with a freshness gate, as the fourth reference surface: the *records* a persisted session log can contain, complementing the cordis catalog (wiring), core-data-structures (vocabulary), and the tool catalog (tools).
|
||||
|
||||
`gen-persistence-catalog.ts` scans every owning and declaration-merged `SessionEventMap` with the TypeScript AST. It renders each member from its leading JSDoc through the complete payload type, retaining nested property comments and removing only its containing indentation, and also pastes the owning `SessionEventType`, `SurfaceEventType`, `SurfaceOp`, and `SessionEvent` declarations that compose the persisted envelope. Derived surface badges, reference links, and source locations remain outside the declaration blocks. The doc-sync freshness check rejects a vocabulary or envelope change whose catalog was not regenerated.
|
||||
|
||||
Specific choices:
|
||||
|
||||
- **JSDoc completeness, enforced.** Every member and rendered envelope type must carry description prose, and the full source JSDoc stays attached to its declaration in the catalog. An `@mode` tag is a hard error: dispatch modes belong to cordis bus events, and persisted records have none. Violations aggregate into one error listing every offender.
|
||||
- **The surface badge is derived, not hand-listed.** `SurfaceEventType` — the subset that produces LLM messages and may carry `surfaceOp` — is parsed from its union declaration in the owning package; a union member naming no declared event is a hard error (a stale union member would otherwise silently badge nothing). Everything else renders **log-only**.
|
||||
- **A dedicated fence.** Declaration blocks use a ` ```ts persistence-catalog ` info string that `doc-typecheck` recognizes and skips, excluded from the opt-out ratio — the same treatment as `ts cordis-catalog` (the declarations reference types from their owning modules and are not standalone-compilable).
|
||||
- **Repo scope.** The catalog enumerates the packages in this repo, matching the siblings' packages-only scope; a downstream plugin can merge further event types, which are outside the catalog by construction. The walk defends its own assumptions with hard errors: the owning top-level `interface SessionEventMap` must be the single exported declaration in `@deepseek-ai/dsh-session` (an unrelated, local, or duplicate same-named interface cannot be catalogued as the on-disk vocabulary), no declaration may carry `extends` (inherited keys would join `keyof SessionEventMap` without a catalog row), every member must be a property signature with an explicit payload type (a method-form member would join `keyof` yet slip past a silent walk), and a duplicate member across declarations fails.
|
||||
|
||||
This supersedes the hand-copies: the session.md `hook/*` table, the compact README's event table, the hook-protocol README's payload bullets, and the session README's name-list now link the catalog instead of restating payloads (the surrounding semantics prose stays where it was). The two stray `@mode emit` tags on the hook-protocol merge members are removed — the new gate rejects them as the category error they were.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **A boot-based generator, like the tool catalog's** — the log vocabulary is fully static, so the AST pass reads the whole truth without booting anything.
|
||||
- **Keeping the hand-copies** — a hand-copy only checks the names someone already wrote down; the session README's merge note had already drifted when the catalog landed.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The catalog cannot drift: a vocabulary or envelope change the committed file doesn't reflect fails `verify-persistence-catalog` in `doc-sync` and CI, and a new merged event with no JSDoc fails the generator outright — a plugin can no longer add an undocumented on-disk record type.
|
||||
- Event prose has a single home, the JSDoc at the declaration; the catalog preserves that JSDoc and any nested field comments without flattening or paraphrasing them.
|
||||
- The `SurfaceEventType` union is now structurally load-bearing for docs: renaming an event without updating the union (or vice versa) fails the generator, not just the compiler.
|
||||
- The badge derivation assumes the union stays a closed set of string literals with exactly one owner; a refactor away from that shape must update the generator in the same change.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Agent Note: 生成式持久化日志事件目录
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-07-04-persistence-log-catalog.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`SessionEventMap` 是磁盘格式的词汇,但其声明分散在所属的会话包(package)和声明合并中。生成式持久化目录是所有事件、各自完整 payload 声明与源码 JSDoc,以及共享 `SessionEvent` 信封的唯一参考;手工维护的表格会发生漂移,因此被移除。这些记录不是 Cordis 事件——观察者通过唯一的 `session/event` 总线事件接收它们——所以 Cordis 目录无法覆盖。生成器会发现所有声明,文档同步新鲜度门禁会拒绝遗漏或陈旧输出。
|
||||
|
||||
## 决策
|
||||
|
||||
从源码生成 `docs/persistence-catalog.md`,配合新鲜度门禁,作为第四个参考面:持久化会话日志可以包含的*记录*,与 Cordis 目录(接线)、核心数据结构(词汇)和工具目录(工具)互补。
|
||||
|
||||
`gen-persistence-catalog.ts` 使用 TypeScript AST 扫描每个所属及声明合并的 `SessionEventMap`。它从前置 JSDoc 开始渲染每个成员,直至完整的 payload 类型,保留嵌套属性注释且只移除其容器缩进;同时粘贴构成持久化信封的所属 `SessionEventType`、`SurfaceEventType`、`SurfaceOp` 和 `SessionEvent` 声明。派生的 surface 徽章、参考链接和源码位置仍位于声明块之外。文档同步新鲜度检查会拒绝目录尚未重新生成的词汇或信封变更。
|
||||
|
||||
具体选择:
|
||||
|
||||
- **强制保证 JSDoc 完整性。** 每个成员和渲染出的信封类型都必须带有描述正文,完整的源码 JSDoc 会在目录中保持附着于其声明。`@mode` 标签是硬错误:分派模式属于 Cordis 总线事件,持久化记录没有这种模式。所有违规会汇总为一条错误,列出每个违规项。
|
||||
- **surface 徽章由派生得出,而非手工列举。** `SurfaceEventType`(产生 LLM(大语言模型)消息且可能携带 `surfaceOp` 的子集)从拥有方包中的 union 声明解析;如果 union 成员命名了一个未声明的事件,则为硬错误(否则陈旧的 union 成员会静默地不标注任何内容)。其余一律渲染为 **log-only**。
|
||||
- **专用围栏。** 声明块使用 ` ```ts persistence-catalog ` 信息字符串,`doc-typecheck` 会识别并跳过这些块,将其排除在 opt-out 比例之外——处理方式与 `ts cordis-catalog` 相同(这些声明引用所属模块中的类型,无法独立编译)。
|
||||
- **仓库范围。** 目录枚举本仓库中的包,与兄弟文档的 packages-only 范围一致;下游插件可以合并更多事件类型,它们在设计上不在目录范围内。遍历过程用硬错误保护自身假设:拥有方的顶层 `interface SessionEventMap` 必须是 `@deepseek-ai/dsh-session` 中唯一的导出声明(无关的、局部的或同名重复的接口不能被当作磁盘词汇编入目录);任何声明不得携带 `extends`(继承的键会加入 `keyof SessionEventMap` 却没有对应的目录行);每个成员必须是带有显式 payload 类型的属性签名(方法形式的成员会加入 `keyof` 却在静默遍历中被漏过);跨声明的重复成员也会失败。
|
||||
|
||||
本方案取代了手工副本:session.md 的 `hook/*` 表格、精简版 README 的事件表格、hook-protocol README 的 payload 条目列表,以及会话 README 的名称列表现在链接到目录,而不再重述 payload(周围的语义说明文字保留原位)。hook-protocol 合并成员上的两个误加的 `@mode emit` 标签已被移除——新门禁将它们作为类别错误拒绝。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **基于启动的生成器(类似工具目录)**:日志词汇完全是静态的,AST 遍历无需启动任何东西即可读取全部真相。
|
||||
- **保留手工副本**:手工副本只能检查作者已经写下的名称;目录落地时,会话 README 的合并说明已经漂移。
|
||||
|
||||
## 后果
|
||||
|
||||
- 目录不会发生漂移:提交文件未反映的词汇或信封变化会使 `doc-sync` 和 CI 中的 `verify-persistence-catalog` 失败,而没有 JSDoc 的新增合并事件会直接使生成器失败——插件不能再添加未记录的磁盘记录类型。
|
||||
- 事件正文只有一个归属,即声明处的 JSDoc;目录会保留该 JSDoc 和所有嵌套字段注释,不会将其扁平化或复述。
|
||||
- `SurfaceEventType` union 现在对文档具有结构性承载作用:重命名事件而不更新 union(或反过来)会导致生成器失败,而不仅仅是编译器失败。
|
||||
- 徽章派生假设 union 始终是一组封闭的字符串字面量且只有一个拥有方;如果重构偏离了这一形状,必须在同一个变更中更新生成器。
|
||||
@@ -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
|
||||
2026-07-06-generated-config-catalog.md: b7c69f21c0e2152235f57e90f88f670769eb9d3b
|
||||
2026-07-06-generated-config-catalog.zh.md: 59d632df0219c09d4afac9c859e2ed0a88d62550
|
||||
@@ -0,0 +1,41 @@
|
||||
# Agent Note: Generated plugin config catalog
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-07-06-generated-config-catalog.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The repository had no source-backed reference for plugin configuration. Package READMEs documented fields inconsistently, did not enumerate which packages are loadable, and did not verify that runtime schemas agree with declared config types.
|
||||
|
||||
## Decision
|
||||
|
||||
`scripts/gen-config-catalog.ts` emits [docs/config-catalog.md](../../../../docs/config-catalog.md) from each plugin's declared config type and JSDoc, with injection requirements, referenced-type links, and a source pointer. Package-local types are included transitively; workspace and external types are linked or named. Deterministic `--write` and `--check` modes make the committed page a generated artifact.
|
||||
|
||||
Pure AST generation is correct here for the same reason it is for the events/services catalog and NOT for the tool catalog: a config type is a static declaration and every schemastery schema in the repo is a static `z.object`/`z.intersect` literal, so the source is the whole truth — nothing about the config surface is runtime-composed.
|
||||
|
||||
Specific choices:
|
||||
|
||||
- **The config type is the second-parameter type.** What the catalog documents is the declared type of `apply(ctx, config)` / the service constructor's `(ctx, config)` — the value cordis actually passes — not a `Config` export located by naming convention. This is what makes the walk total: it works for interfaces named `AcpConfig` or `BasicCompactConfig`, for types declared in a sibling file, and for plugins with no validating schema at all.
|
||||
- **Classification is total.** Every `packages/<group>/<pkg>` entry resolves, mirroring the Loader's `unwrapExports` (`exports.default ?? exports`), to a configurable plugin, a config-free plugin, an abstract seam class, or a library — each rendered in its own section — and an unclassifiable entry hard-errors. A new package cannot be silently undocumented.
|
||||
- **Per-field JSDoc is enforced.** Every property of a pasted declaration (nested type literals included) needs non-empty JSDoc prose, or generation fails. The paste IS the documentation, so this is the same forcing function the events catalog applies via `@mode`: thin source docs fail the gate rather than yielding a thin catalog.
|
||||
- **Schema keys are checked against the declared type.** The generator resolves nested object and array paths through local and workspace types. Definite missing paths fail; external or dynamic shapes that cannot be enumerated are skipped. The check is intentionally one-way because declared types may contain runtime-only fields excluded from loader config.
|
||||
- **A dedicated fence.** Pasted declarations use a ` ```ts config-catalog ` info string that `doc-typecheck` skips (a lone declaration referencing imported types is not standalone-compilable), excluded from the opt-out ratio — the same treatment the `cordis-catalog` and `persistence-catalog` fences get.
|
||||
- **A single file at `docs/config-catalog.md`**, not a one-file directory: the page serves one audience (the `cordis.yml` author) with one axis, unlike `cordis-catalog/`, which holds two sibling pages.
|
||||
|
||||
The package README `## Config` sections stay. The overlap is accepted deliberately: the README is the curated per-package contract (config semantics in deployment context, alongside limitations and extension points), the catalog is the exhaustive generated enumeration. Because the catalog is generated, a disagreement between the two indicts the README, and the fix is a README edit — the catalog cannot drift.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Synthesized per-field rendering** — a bullet list, table, or annotated-YAML snippet per field, assembled from parsed JSDoc plus schema metadata. Rejected for the verbatim paste: the interface with its JSDoc is already the authored contract in its authored form, and a synthesizing renderer re-formats prose it does not own, adding a rendering layer that can misrepresent it.
|
||||
- **Runtime boot + schema introspection, as the tool catalog does** — rejected: nothing here is runtime-composed, and the schema alone under-documents the surface (prose-documented defaults, runtime-only fields, plugins with no schema at all). Booting would add fragility without adding truth.
|
||||
- **Two-directional schema/interface equality** — rejected for the subset check: the declared type legitimately carries members the schema refuses to accept from config (runtime-only seams).
|
||||
- **Retiring the README `## Config` sections in the same change** — rejected: the accepted duplication keeps the per-package contract readable in place, and a sweep would have to fold each README's extra facts into field JSDoc first — separable work the catalog does not depend on.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The catalog cannot drift: a source change the committed file does not reflect fails `verify-config-catalog` in `doc-sync` and CI. An undocumented config field, an unresolvable referenced type name, or a schema key missing from the config type fails the generator outright.
|
||||
- Config prose now has a forcing function at the declaration: writing a new config field means writing its JSDoc, which becomes the catalog entry verbatim.
|
||||
- The generator hard-errors on shapes it cannot walk statically — an aliased package-local config import, a schema built by anything other than `object`/`intersect` composition, an unlisted global type name. Introducing such a shape includes teaching the generator (or the shape stays out of the repo), which is the point: the catalog stays the whole truth.
|
||||
- `gen-cordis-catalog.ts` exports its JSDoc/pointer helpers and `LINK_MAP` for reuse, so the two catalogs cross-link types identically and a link-map addition serves both.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Agent Note: 生成式插件配置目录
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-07-06-generated-config-catalog.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
仓库此前没有以源码为后盾的插件配置参考。各包(package)的 README 对字段的记录方式不一致,未列举哪些包可被加载,也未校验运行时 schema 与声明的配置类型是否一致。
|
||||
|
||||
## 决策
|
||||
|
||||
`scripts/gen-config-catalog.ts` 根据各插件声明的 config 类型和 JSDoc 生成 [docs/config-catalog.md](../../../../docs/config-catalog.md),并包含注入要求、被引用类型的链接和源码位置。包内类型会以传递方式纳入;workspace 类型和外部类型则会链接或点名。确定性的 `--write` 和 `--check` 模式使提交页面成为生成产物。
|
||||
|
||||
此处采用纯 AST 生成是正确的,原因与事件/服务目录相同,而与工具目录不同:配置类型是静态声明,仓库中每个 schemastery schema 都是静态的 `z.object`/`z.intersect` 字面量,因此源码即全部真相——配置表面没有任何部分是运行时组合的。
|
||||
|
||||
具体选择:
|
||||
|
||||
- **配置类型是第二参数的类型。** catalog 记录的是 `apply(ctx, config)` / 服务构造函数 `(ctx, config)` 的声明参数类型——即 Cordis 实际传入的值——而非按命名约定定位的 `Config` 导出。这使得遍历是全量的:无论接口叫 `AcpConfig` 还是 `BasicCompactConfig`,无论类型声明在兄弟文件中,还是插件完全没有验证 schema,都能正常工作。
|
||||
- **分类是全量的。** 每个 `packages/<group>/<pkg>` 条目都会被解析(镜像 Loader 的 `unwrapExports`:`exports.default ?? exports`),归入可配置插件、无配置插件、抽象 seam 类或库之一——各自渲染在独立小节中——无法归类的条目直接报错。新包不可能被悄悄遗漏。
|
||||
- **逐字段 JSDoc 强制要求。** 粘贴的声明中每个属性(包括嵌套的类型字面量)都需要非空的 JSDoc 描述,否则生成失败。粘贴本身就是文档,因此这与 events catalog 通过 `@mode` 施加的强制函数相同:源码文档过于单薄时门禁报错,而非产出单薄的 catalog。
|
||||
- **Schema 键与声明类型做比对。** 生成器通过局部和 workspace 类型解析嵌套的对象与数组路径。确定缺失的路径报错;无法枚举的外部或动态形状则跳过。比对有意设计为单向的,因为声明类型可能包含被排除在 loader 配置之外的运行时专用字段。
|
||||
- **专用围栏。** 粘贴的声明使用 ` ```ts config-catalog ` 信息字符串,`doc-typecheck` 会跳过它(引用了导入类型的孤立声明无法独立编译),并将其排除在 opt-out 比例之外——与 `cordis-catalog` 和 `persistence-catalog` 围栏的处理方式相同。
|
||||
- **单文件 `docs/config-catalog.md`**,而非一个单文件目录:该页面面向单一受众(`cordis.yml` 的编写者),只有一个维度,不同于 `cordis-catalog/`(其中包含两个并列页面)。
|
||||
|
||||
各包 README 中的 `## Config` 小节保留。重叠是有意接受的:README 是经过策划的逐包契约(在部署上下文中描述配置语义,连同限制与扩展点),catalog 则是穷举式的生成枚举。由于 catalog 是生成的,二者不一致时说明 README 有误,修复方式是编辑 README——catalog 不会漂移。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **合成式逐字段渲染**:为每个字段生成项目符号列表、表格或带注释的 YAML 片段,从解析的 JSDoc 加 schema 元数据组装。否决,改用逐字粘贴:接口连同其 JSDoc 本身就是以原始形式撰写的契约,合成渲染器会重新格式化它不拥有的行文,增加一个可能歪曲原意的渲染层。
|
||||
- **运行时启动 + schema 内省(如工具目录所做的那样)**:否决。此处没有任何内容是运行时组合的,且 schema 本身对配置表面的文档化不足(以行文记录的默认值、运行时专用字段、完全没有 schema 的插件)。启动只会增加脆弱性而不增加真相。
|
||||
- **双向 schema/接口等价检查**:否决,改用子集检查。声明类型合理地包含 schema 拒绝从配置接受的成员(运行时专用 seam)。
|
||||
- **在同一变更中废除 README `## Config` 小节**:否决。保留可接受的重叠使逐包契约在原处可读,而清理工作需要先把每个 README 的额外事实折入字段 JSDoc——这是可分离的工作,catalog 不依赖它。
|
||||
|
||||
## 后果
|
||||
|
||||
- 目录不会发生漂移:提交文件未反映的源码变化会使 `doc-sync` 和 CI 中的 `verify-config-catalog` 失败。config 字段未记录、被引用类型名无法解析,或 schema 键未出现在 config 类型中,都会直接使生成器失败。
|
||||
- 配置行文现在有了声明处的强制函数:编写新配置字段意味着编写其 JSDoc,而该 JSDoc 将逐字成为 catalog 条目。
|
||||
- 生成器对无法静态遍历的形状直接报错——别名化的包内配置导入、非 `object`/`intersect` 组合构建的 schema、未列入的全局类型名。引入此类形状时必须同时教会生成器(否则该形状不能进入仓库),这正是设计意图:catalog 始终是全部真相。
|
||||
- `gen-cordis-catalog.ts` 导出其 JSDoc/指针辅助函数与 `LINK_MAP` 供复用,因此两个 catalog 以相同方式交叉链接类型,新增一条 link-map 条目同时服务于两者。
|
||||
@@ -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
|
||||
2026-07-17-run-ci-examples-from-built-lib.md: 3063bdcc8a170713151229a2a4cee22a91f1e8a9
|
||||
2026-07-17-run-ci-examples-from-built-lib.zh.md: b70367c1d25f4942043a60458b0197288c8094ba
|
||||
@@ -0,0 +1,43 @@
|
||||
# Agent Note: Run CI examples from built lib
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-07-17-run-ci-examples-from-built-lib.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
CI boots examples and Cordis-backed test projects through `node --import tsx` and the root tsconfig `paths` map. This adds TypeScript transformation cost and changes package resolution: imports resolve to workspace source instead of following package `exports` into built `lib/`.
|
||||
|
||||
These runs therefore do not test the same code or resolution behavior as an installed consumer. A package can pass CI while its built export graph is incomplete or resolves differently.
|
||||
|
||||
## Decision
|
||||
|
||||
Execution has two modes. `src` is the default local-development mode and uses tsx; `lib` is the strict CI mode and starts built bins with plain Node, without tsx or tsconfig path mapping.
|
||||
|
||||
- CI subprocesses that boot an example or a checked-in `cordis.yml` use `lib` mode.
|
||||
- TypeScript fixtures that only implement an ACP or MCP peer and do not load Cordis run directly with Node. An explicit source-path regression may remain in `src` mode.
|
||||
|
||||
### Resolution topology
|
||||
|
||||
Every test Cordis config must resolve its bare modules by walking upward from the config directory.
|
||||
|
||||
- `examples/` is one pnpm workspace member and provides the shared `examples/node_modules` resolution root.
|
||||
- Every checked-in test Cordis config, including snapshot configs and package-owned fixtures, lives under its corresponding `examples/<agent>/` tree. A config owned by `packages/<group>/<package>/` maps to `examples/<agent>/tests/fixtures/<group>/<package>/cordis.yml`; the test driver and assertions remain package-local.
|
||||
- Every package named by an example Cordis config is declared in both `examples/package.json` for `lib` resolution and the root `tsconfig.json` references for `src` mode.
|
||||
|
||||
### Launch policy
|
||||
|
||||
The shared Loader test harness selects `src` or `lib` from `DSH_EXAMPLE_MODE`. CI builds first and selects `lib`; an unset mode keeps the fast local source loop.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Keep CI on tsx** — rejected because it preserves transformation overhead and source-only resolution behavior.
|
||||
- **Use lib everywhere** — rejected because local development would require a build before every run. Dual mode keeps that cost out of the development loop.
|
||||
- **Build a private `node_modules` tree per test** — rejected because it duplicates consumer scaffolding. The `examples/` workspace root gives every Cordis config one real and declared resolution path.
|
||||
|
||||
## Consequences
|
||||
|
||||
- CI validates built package exports without tsx changing module resolution; local development retains the no-build source loop.
|
||||
- CI must build before these tests, and manual `lib` runs can observe stale local artifacts.
|
||||
- Cordis config dependencies are not visible to normal TypeScript import analysis, so `examples/package.json` and the root tsconfig references must stay synchronized with the configs.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Agent Note: 在 CI 中从构建后的 lib 运行示例
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-07-17-run-ci-examples-from-built-lib.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
CI 通过 `node --import tsx` 和根 tsconfig 的 `paths` 映射启动示例与加载 Cordis 配置的测试项目。这种方式既增加了 TypeScript 转换开销,也改变了包解析行为:import 会解析到 workspace 源码,而不是经包的 `exports` 进入构建后的 `lib/`。
|
||||
|
||||
因此,这些测试没有覆盖已安装消费方实际运行的代码和解析路径。即使包的构建导出图不完整或解析结果不同,CI 仍可能通过。
|
||||
|
||||
## 决策
|
||||
|
||||
执行机制包含两种模式。`src` 是本地开发的默认模式并使用 tsx;`lib` 是严格的 CI 模式,通过 plain Node 启动构建后的 bin,不加载 tsx,也不使用 tsconfig 路径映射。
|
||||
|
||||
- CI 中启动示例或签入仓库的 `cordis.yml` 的子进程使用 `lib` 模式。
|
||||
- 仅实现 ACP 或 MCP 对端、且不加载 Cordis 的 TypeScript fixture(测试前置数据)直接由 Node 运行。只有显式验证源码路径的回归测试可以保留 `src` 模式。
|
||||
|
||||
### 解析拓扑
|
||||
|
||||
每个测试 Cordis 配置都必须能从配置文件所在目录向上解析裸模块。
|
||||
|
||||
- `examples/` 作为一个 pnpm workspace 成员,提供统一的 `examples/node_modules` 解析根目录。
|
||||
- 所有签入仓库的测试 Cordis 配置,包括快照配置和包内测试 fixture,都放在对应的 `examples/<agent>/` 目录树下。归属 `packages/<group>/<package>/` 的配置映射到 `examples/<agent>/tests/fixtures/<group>/<package>/cordis.yml`;测试驱动和断言仍留在包内。
|
||||
- 示例 Cordis 配置中引用的每个包都同时登记在 `examples/package.json` 和根 `tsconfig.json` 的 references 中,分别支持 `lib` 与 `src` 解析。
|
||||
|
||||
### 启动策略
|
||||
|
||||
共享 Loader 测试 harness 通过 `DSH_EXAMPLE_MODE` 选择 `src` 或 `lib`。CI 先构建再选择 `lib`;未设置模式时保留快速的本地源码开发回路。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **CI 继续使用 tsx**:不予采纳,因为它会保留转换开销和仅适用于源码的解析行为。
|
||||
- **所有环境只使用 lib**:不予采纳,因为本地开发每次运行前都必须构建。双模式避免把这项成本带入开发回路。
|
||||
- **每个测试单独构造 `node_modules`**:不予采纳,因为它会重复消费方脚手架。以 `examples/` 作为 workspace 根,可让每个 Cordis 配置通过同一条真实且显式声明的路径解析模块。
|
||||
|
||||
## 后果
|
||||
|
||||
- CI 可以验证构建后的包导出,不再受 tsx 模块解析影响;本地开发仍保留免构建的源码回路。
|
||||
- CI 必须先构建再运行这些测试;手动执行 `lib` 模式时可能读取陈旧的本地产物。
|
||||
- 常规 TypeScript import 分析无法识别 Cordis 配置依赖,因此 `examples/package.json`、根 tsconfig references 与配置文件必须保持同步。
|
||||
@@ -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
|
||||
2026-07-20-generated-cordis-core-api.md: b6a66e518744354771f25d44d662e2655fa7c758
|
||||
2026-07-20-generated-cordis-core-api.zh.md: 82dbb308386b7adc678c6e4bf8ef3f06c3b2df77
|
||||
@@ -0,0 +1,32 @@
|
||||
# Agent Note: Generate the Cordis core API reference
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-07-20-generated-cordis-core-api.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
Plugin authors need the detailed Cordis APIs behind `ctx`, event dispatch, fibers, plugin registration, and services. The generated [Harness event and service catalogs](2026-06-20-generated-cordis-catalog.md) intentionally summarize inherited Cordis members, so they do not replace a method-level Cordis reference. Keeping a second hand-written copy under the website would drift from the vendored source and make the renderer an additional documentation owner.
|
||||
|
||||
## Decision
|
||||
|
||||
`scripts/cordis-core-api.ts` reads the public declarations and original JSDoc from `vendor/cordis/src` with the TypeScript compiler API. An explicit page manifest generates five files under [`docs/cordis-catalog/core/`](../../../../docs/cordis-catalog/core/context.md): Context, Events, Fiber, Registry, and Service. `scripts/gen-cordis-catalog.ts` writes these pages together with the Harness event and service catalogs, and `verify-cordis-catalog` rejects stale output.
|
||||
|
||||
The generator validates that documented classes and methods retain descriptive JSDoc, including parameter and non-void return contracts. It emits declaration-only `ts cordis-catalog` fences with the original JSDoc, then renders the same description, parameters, and return contract as readable Markdown. Source links point to the vendored files, and the five pages cross-link to one another. The Harness catalogs remain the exhaustive inventory of repository-declared events and `ctx.*` services; the core pages document how the inherited Cordis APIs operate.
|
||||
|
||||
`website/docs.ts` publishes the five canonical files under matching `/reference/cordis-api/` and `/en/reference/cordis-api/` routes. Both locales use the English generated source until the generator emits translated pages, so changing language preserves navigation structure and route identity.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Restore the old website files as canonical Markdown.** This would recover the pages quickly, but their signatures and prose could drift from the vendored implementation and the website would regain a second documentation source.
|
||||
|
||||
**Expand the inherited tier of the Harness catalogs in place.** Those catalogs answer which Harness events and services exist. Mixing full framework class references into the same pages would obscure that inventory and reverse their deliberate terse inherited tier.
|
||||
|
||||
**Publish vendored source declarations directly.** Source files are authoritative but do not provide stable topic pages, curated public ordering, or website navigation, and they expose implementation bodies that are not part of the reference contract.
|
||||
|
||||
## Consequences
|
||||
|
||||
The five Cordis API pages follow vendor updates through one deterministic generator and share the repository's documentation freshness gate. The website gains a dedicated Cordis API section without copied site content, while root and English navigation remain structurally identical.
|
||||
|
||||
The page manifest is curated, so a newly public Cordis core type needs an explicit generator entry. Generated prose is English-only, and source JSDoc quality directly limits reference quality; Chinese output requires generator-level translation rather than hand-editing the generated files.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Agent Note: 生成 Cordis 核心 API 参考文档
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-07-20-generated-cordis-core-api.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
插件作者需要了解 `ctx`、事件派发、Fiber、插件注册和 Service 背后的详细 Cordis API。已有的 [Harness 事件与服务目录](2026-06-20-generated-cordis-catalog.md)有意只简要概括继承自 Cordis 的成员,因此无法替代方法级 Cordis 参考文档。如果在网站下维护另一份手写副本,它会与 vendored 源码产生漂移,也会让渲染器成为额外的文档所有者。
|
||||
|
||||
## 决策
|
||||
|
||||
`scripts/cordis-core-api.ts` 使用 TypeScript Compiler API,从 `vendor/cordis/src` 读取公开声明和原始 JSDoc。一个显式页面清单在 [`docs/cordis-catalog/core/`](../../../../docs/cordis-catalog/core/context.md) 下生成五个文件:Context、Events、Fiber、Registry 和 Service。`scripts/gen-cordis-catalog.ts` 将这些页面与 Harness 事件和服务目录一同写入,`verify-cordis-catalog` 会拒绝过期产物。
|
||||
|
||||
生成器会验证所记录的类和方法保留描述性 JSDoc,包括参数和非 void 返回值契约。它生成包含原始 JSDoc 且仅含声明的 `ts cordis-catalog` 代码围栏,再将同一份说明、参数和返回值契约渲染为便于阅读的 Markdown。源码链接指向 vendored 文件,五个页面之间相互交叉链接。Harness 目录仍是仓库声明的事件与 `ctx.*` 服务的完整清单;核心页面负责说明继承自 Cordis 的 API 如何工作。
|
||||
|
||||
`website/docs.ts` 将五个规范源文件发布到结构对应的 `/reference/cordis-api/` 和 `/en/reference/cordis-api/` 路由。在生成器产出翻译页面之前,两个 locale 都使用英文生成源,因此切换语言时导航结构和路由标识保持不变。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**将旧网站文件恢复为规范 Markdown。** 这能快速恢复页面,但其签名和说明可能与 vendored 实现漂移,网站也会重新成为第二个文档来源。
|
||||
|
||||
**直接扩充 Harness 目录中的继承层。** 这些目录回答有哪些 Harness 事件与服务。将完整的框架类参考混入同一页面会模糊这份清单的定位,并推翻继承层保持精简的既有决定。
|
||||
|
||||
**直接发布 vendored 源码声明。** 源文件具有权威性,但不能提供稳定的主题页面、经过筛选的公开顺序或网站导航,还会暴露不属于参考契约的实现体。
|
||||
|
||||
## 影响
|
||||
|
||||
五个 Cordis API 页面通过同一个确定性生成器跟随 vendor 更新,并复用仓库的文档新鲜度检查。网站无需复制内容即可获得独立的 Cordis API 章节,中文入口和英文入口的导航结构保持一致。
|
||||
|
||||
页面清单需要人工维护,因此新增公开 Cordis 核心类型时必须显式添加生成器条目。当前生成说明只有英文,且源码 JSDoc 的质量直接决定参考文档质量;中文产物需要在生成器层实现翻译,不能手工编辑生成文件。
|
||||
@@ -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
|
||||
2026-07-22-cordis-tutorial-docs.md: a882f3f58fe4be5388bf142424e0e99e140410fc
|
||||
2026-07-22-cordis-tutorial-docs.zh.md: 4caf03ee05a120584dabef91f6ebafc78594d43a
|
||||
@@ -0,0 +1,33 @@
|
||||
# Agent Note: Tutorial-style Cordis docs under docs/cordis-tutorial
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-07-22-cordis-tutorial-docs.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The repo documents Cordis at two levels: the condensed [cordis-primer](../../../../docs/cordis-primer.md) states the concepts, and the `docs/user/develop/` pages teach harness plugin authoring against harness services. Neither serves a developer meeting Cordis itself for the first time: the primer assumes the reader already writes plugins, and the develop pages jump straight to `defineTool` without showing how contexts, fibers, services, and dispatch actually behave. There was no path where a reader runs bare Cordis, watches a fiber go PENDING, or sees a waterfall veto happen.
|
||||
|
||||
## Decision
|
||||
|
||||
`docs/cordis-tutorial/` holds a seven-chapter hands-on tutorial (first plugin → lifecycle/effects → services → events → config → composition/HMR → harness tool). Its properties, in decreasing order of load-bearing-ness:
|
||||
|
||||
- **Every transcript is real.** Each chapter's files run in the gitignored `tmp/cordis-tutorial/` scratch directory via `node --import tsx ../../vendor/cordis/bin.js`, and the shown output is what those commands print. The chapter that uses harness packages (`@deepseek-ai/dsh-tools` and `@deepseek-ai/dsh-llm`) runs keylessly.
|
||||
- **dsh-flavored, not pure Cordis**: later chapters use real harness services and events (`ctx.tools`, `tools/result`) so the tutorial lands the reader inside this repo's actual composition model, per the requesting user's choice.
|
||||
- **English-only, published to both website locales** through `mirroredPages()` in [website/docs.ts](../../../../website/docs.ts) under a `Cordis 教程` / `Cordis tutorial` section of the develop sidebar — the same pattern as the reference pages, so a Chinese pair can ratchet in later without route changes.
|
||||
- Code fences compile under `doc-typecheck` except the two fences that import scratch-relative files (`./stats.ts`) or intentionally throw, which carry `ignore-check`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Under `docs/user/develop/` as paired product docs.** That tier requires en+zh+i18n records in the same PR, roughly doubling the change and coupling every future tutorial edit to a translation. Rejected for the first landing; the mirrored projection keeps the same public visibility.
|
||||
|
||||
**Pure-Cordis tutorial with no harness packages.** Cleaner as framework documentation, but the audience is agent developers extending this harness; ending at `ctx.tools.execute` and `tools/result` teaches the composition they will actually work in. The user chose this explicitly.
|
||||
|
||||
**Extending the primer instead of a new directory.** The primer is a 600-word budgeted concept reference; a multi-chapter walkthrough inside it would break its tier's job (and its budget) rather than complement it.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A runnable introduction to Cordis exercises the loader, fiber states, effects, service injection, all five dispatch-mode contracts, Schemastery validation, and HMR. It demonstrates PENDING dependencies and validation failure; it explains the loader's logged unresolved-entry failure because that boot-time log may not reach a console exporter.
|
||||
- The tutorial's transcripts pin behavior informally but are not snapshot-gated; if loader or HMR behavior changes, the transcripts drift until a human replays the chapters. The compile gate covers only the code fences.
|
||||
- The chapters name concrete harness APIs (`ctx.tools.execute`, `CallId`, `tools/result`); renames must update the tutorial like any other doc reference (`verify-md-links` catches file moves, not API prose).
|
||||
@@ -0,0 +1,33 @@
|
||||
# Agent Note: `docs/cordis-tutorial` 下的 Cordis 实操教程文档
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-07-22-cordis-tutorial-docs.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
本仓库从两个层面介绍 Cordis:精简的 [cordis-primer](../../../../docs/cordis-primer.md) 阐述概念,`docs/user/develop/` 下的页面则讲解如何基于 harness 服务编写 harness 插件。但二者都不适合初次接触 Cordis 的开发者:primer 假定读者已经会编写插件,开发页面则直接从 `defineTool` 讲起,没有展示上下文、fiber、服务和 dispatch 的实际行为。此前没有一条学习路径让读者运行原生 Cordis、观察 fiber 进入 PENDING 状态,或看到 waterfall(瀑布式事件)否决实际发生。
|
||||
|
||||
## 决策
|
||||
|
||||
`docs/cordis-tutorial/` 包含一套七章实操教程(第一个插件 → 生命周期与 effect → 服务 → 事件 → 配置 → 组合与 HMR(热模块替换)→ harness 工具)。以下是教程的特性,按重要性从高到低排列:
|
||||
|
||||
- **每段 transcript(文本记录)都真实可复现。** 每章文件都通过 `node --import tsx ../../vendor/cordis/bin.js` 在 git 忽略的 `tmp/cordis-tutorial/` 临时目录中运行,展示的输出就是这些命令实际打印的内容。使用 harness 包(package)(`@deepseek-ai/dsh-tools` 和 `@deepseek-ai/dsh-llm`)的章节无需密钥即可运行。
|
||||
- **采用 dsh 风格,而非纯 Cordis**:后续章节使用真实的 harness 服务和事件(`ctx.tools`、`tools/result`),使读者最终进入本仓库实际采用的组合模型,这遵循了提出请求的用户所作的选择。
|
||||
- **仅提供英文版,但发布到网站的两个语言区域**:通过 [website/docs.ts](../../../../website/docs.ts) 中的 `mirroredPages()`,发布到开发侧边栏的 `Cordis 教程` / `Cordis tutorial` 分区。该方式与参考页面采用的模式相同,因此日后可以逐步纳入中文配对,而无需更改路由。
|
||||
- 除两个围栏代码块外,其余代码块均通过 `doc-typecheck` 编译;这两个例外分别导入临时目录中的相对路径文件(`./stats.ts`)或有意抛出异常,因此标有 `ignore-check`。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**作为双语产品文档放在 `docs/user/develop/` 下。** 该层级要求在同一个 PR(Pull Request)中同时提供英文、中文和 i18n 记录,这会使变更量大致翻倍,并要求未来每次修改教程时都同步翻译。首次落地不采用此方案;镜像投影仍可保持同等的公开可见性。
|
||||
|
||||
**不使用任何 harness 包的纯 Cordis 教程。** 作为框架文档会更简洁,但目标读者是扩展此 harness 的 agent(智能体)开发者;以 `ctx.tools.execute` 和 `tools/result` 收尾,能讲清他们实际使用的组合方式。用户明确选择了此方案。
|
||||
|
||||
**扩充 primer,而非新建目录。** primer 是一份预算上限为 600 词的精简概念参考;在其中加入多章演练会破坏该文档层级的职责及其篇幅预算,而非形成补充。
|
||||
|
||||
## 结果
|
||||
|
||||
- 现在有了一份可运行的 Cordis 入门教程,涵盖 loader、fiber 状态、effect、服务注入、全部五种 dispatch 模式的契约、Schemastery 校验和 HMR。教程实际展示了依赖处于 PENDING 状态和配置校验失败;对于 loader 记录的配置项解析失败,教程只作说明,因为启动阶段的日志可能无法到达控制台导出器。
|
||||
- 教程中的 transcript 以非正式方式固定了行为,但没有快照门禁;如果 loader 或 HMR 的行为发生变化,transcript 会逐渐偏离实际结果,直到有人重新运行各章。编译门禁只覆盖围栏代码块。
|
||||
- 各章写明了具体的 harness API(`ctx.tools.execute`、`CallId`、`tools/result`);这些 API 重命名时,必须像更新其他文档引用一样同步修改教程(`verify-md-links` 能发现文件移动,但无法发现 API 文字引用变化)。
|
||||
@@ -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
|
||||
2026-07-26-gui-pr-gif-evidence-and-assets-branch.md: 18e56e44190e0c44fcf2cae8bf1d66f91ef9a4c7
|
||||
2026-07-26-gui-pr-gif-evidence-and-assets-branch.zh.md: 945dd7a0242d9829334ddd79ab569c6723a64185
|
||||
@@ -0,0 +1,40 @@
|
||||
# Agent Note: GUI pull request GIF evidence and assets-branch publication
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
English | [中文](2026-07-26-gui-pr-gif-evidence-and-assets-branch.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
A pull request that changes what a product user sees in the GUI is otherwise reviewed through prose and test names, neither of which shows the rendered result. The [browser-demo GIF recording](../../../skills/record-browser-gif/SKILL.md) skill produces truthful local GIFs but deliberately stopped at the local artifact, so each pull request that wanted to show one re-derived publication on its own — and committing the GIF to the pull request branch is never acceptable, because binary media in history bloats every future clone permanently.
|
||||
|
||||
The recording procedure itself also kept being re-learned failure by failure: screenshots written outside the browser tool's allowed roots or into missing directories fail at capture time, transient UI states polled across separate tool calls are lost because the turn settles between calls, substring completion predicates match the echo of the user's own prompt, and an inline environment-variable assignment on the encoder command expands too late to take effect.
|
||||
|
||||
## Decision
|
||||
|
||||
Every pull request that changes product-user-visible GUI behavior includes a demonstration GIF recorded with the [record-browser-gif skill](../../../skills/record-browser-gif/SKILL.md), with real provenance — a real server booted from that pull request's own branch tree, a real API key, and real model rounds — stated next to the embed. Fixture provenance is acceptable only when the user explicitly asked for it.
|
||||
|
||||
The GIF is published to a dedicated orphan assets branch — no parent commit, media only — never to the pull request branch; one assets branch serves a whole pull request series (existing branches: `code-mode-ui-assets`, `pr-613-assets`). Publication works in a shallow single-branch scratch clone, commits as `assets: <what it shows> gif (#<pr>)`, and the pull request body embeds the blob URL with the required `?raw=true` suffix. Assets branches are append-only: merged pull request bodies reference their URLs forever, so an assets branch is never rewritten or deleted.
|
||||
|
||||
Recording itself stays side-effect-free; publication is a bounded final step the skill performs only when the task includes attaching the GIF to a pull request. The [record-browser-gif skill](../../../skills/record-browser-gif/SKILL.md) remains the current contract for the recording half.
|
||||
|
||||
The skill folds in the operational lessons recording earned: frames go under `.playwright-mcp/`, ignored by the repository `.gitignore` and created before capture, because the browser tool writes only under its allowed roots and resolves relative names against the repository root; each pull request stages its own built tree with a fresh scratch workspace and a new session per scenario, and servers are stopped by PID rather than a broad process-name pattern; transient states are captured by driving a slow foreground operation and polling a concrete DOM marker inside one browser-script call; completion predicates match an exact-text element rather than a substring; and the encoder runs with `GIF_SKILL_DIR` exported on its own line, per-frame durations holding the settled state longest, and both a JSON-summary check and a visual read of the encoded GIF.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Commit the GIF to the pull request branch.** Binary media merged into the default branch stays in history for every future clone and fetch; a demo GIF's value ends at review while its cost never does.
|
||||
|
||||
**Attach the GIF as a GitHub upload.** Drag-and-drop `user-attachments` uploads are not available to a command-line workflow, cannot be re-created or audited from the repository, and leave the media's lifecycle outside repository control.
|
||||
|
||||
**Store GIFs with Git LFS.** LFS still couples media to the code branch's history, adds an infrastructure dependency to every clone and CI fetch, and buys nothing over an isolated branch that ordinary git already supports.
|
||||
|
||||
**One assets branch per pull request.** A branch per pull request sprawls the ref namespace and multiplies scratch clones during a series; one branch per series keeps publication a single push while staying isolated from code history.
|
||||
|
||||
**Keep publication out of the recording skill.** That was the prior state; it preserved a clean boundary but made every pull request re-derive the same procedure. The boundary survives as an explicit gate — publication runs only when the task includes attaching the GIF to a pull request — instead of as omission.
|
||||
|
||||
**Leave the GIF optional per pull request.** Optional evidence disappears under schedule pressure exactly where it matters most; a GUI change reviewed without a recording asks reviewers to imagine the rendered result or rebuild the branch themselves.
|
||||
|
||||
## Consequences
|
||||
|
||||
Every GUI pull request carries visual evidence with stated provenance, and reviewers see the change without rebuilding the branch. Repository history stays free of media; the cost moves to append-only assets branches that grow forever, stay cheap to clone shallowly, and can never be deleted. Mandatory real-provenance recording adds a real-key, real-model round to every GUI pull request's workflow — deliberate, because that run is the evidence. The recording half remains locally reversible, and a GIF request whose task does not include attaching it to a pull request still ends at the verified local artifact.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Agent Note: GUI PR 的 GIF 证据与 assets 分支发布
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-07-27
|
||||
|
||||
[English](2026-07-26-gui-pr-gif-evidence-and-assets-branch.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
改变产品用户在 GUI 中所见行为的 PR(Pull Request),此前只能通过文字描述和测试名称接受评审,两者都无法展示渲染结果。[浏览器演示 GIF 录制](../../../skills/record-browser-gif/SKILL.md)对应的 skill(技能)能生成真实可信的本地 GIF,但刻意止步于本地产物,于是每个想展示 GIF 的 PR 都得各自重新摸索发布方式;而把 GIF 提交到 PR 分支从来不可接受:进入历史的二进制媒体会永久增大之后每一次克隆的体积。
|
||||
|
||||
录制流程本身也在靠一次次失败反复重新学习:截图写到浏览器工具允许的根目录之外或写入不存在的目录,会在截取时直接失败;跨多次工具调用轮询的瞬态 UI 状态会丢失,因为调用之间轮次已经结算;用子串匹配做完成判定会命中用户自己提示词的回显;在编码器命令上内联赋值环境变量则因参数先于赋值展开而不生效。
|
||||
|
||||
## 决策
|
||||
|
||||
每个改变产品用户可见 GUI 行为的 PR 都包含一个用 [record-browser-gif skill](../../../skills/record-browser-gif/SKILL.md) 录制的演示 GIF,其来源必须真实:从该 PR 自身分支树启动的真实服务器、真实 API 密钥、真实的模型轮次,并在嵌入处注明来源。只有当用户明确要求 fixture(测试前置数据)来源时才可使用 fixture。
|
||||
|
||||
GIF 发布到专用的孤儿(orphan)assets 分支上:该分支没有父提交、只含媒体,GIF 绝不进入 PR 自己的分支;一个 assets 分支服务整个 PR 系列(现有分支:`code-mode-ui-assets`、`pr-613-assets`)。发布在浅层单分支的临时克隆中进行,提交信息形如 `assets: <what it shows> gif (#<pr>)`,PR 正文用带必需 `?raw=true` 后缀的 blob URL 嵌入。assets 分支只允许追加:已合并的 PR 正文会永远引用其 URL,因此 assets 分支绝不重写或删除。
|
||||
|
||||
录制本身保持无副作用;发布是一个有边界的收尾步骤,仅当任务包含把 GIF 附到 PR 时才由该 skill 执行。[record-browser-gif skill](../../../skills/record-browser-gif/SKILL.md) 仍是录制部分的现行契约。
|
||||
|
||||
该 skill 还吸收了录制实践换来的操作经验:帧文件放在仓库 `.gitignore` 忽略的 `.playwright-mcp/` 目录下并在截取前先创建,因为浏览器工具只能写入其允许的根目录,相对文件名也相对仓库根目录解析;每个 PR 从自己构建的分支树启动服务,配以全新的临时工作区目录,每个录制场景新开会话,停止服务器时按 PID 精确匹配而不是用宽泛的进程名模式;瞬态状态靠驱动一个缓慢的前台操作、并在同一次浏览器脚本调用内轮询具体的 DOM 标记来截取;完成判定匹配精确文本元素而非子串;编码器在单独一行 export `GIF_SKILL_DIR` 之后运行,逐帧时长让最终稳定状态停留最久,并同时核对 JSON 摘要与目视检查编码后的 GIF。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
**把 GIF 提交到 PR 分支。**合入默认分支的二进制媒体会留在历史中,影响之后的每一次克隆和拉取;演示 GIF 的价值止于评审,代价却永不消失。
|
||||
|
||||
**作为 GitHub 附件上传。**拖拽产生的 `user-attachments` 上传对命令行工作流不可用,无法从仓库重建或审计,媒体的生命周期也脱离仓库的控制。
|
||||
|
||||
**用 Git LFS 存储 GIF。**LFS 仍把媒体耦合进代码分支的历史,给每次克隆和 CI 拉取增加一项基础设施依赖,相比普通 git 即可支持的隔离分支没有任何额外收益。
|
||||
|
||||
**每个 PR 一个 assets 分支。**按 PR 建分支会让 ref 命名空间蔓延,并在一个系列内成倍增加临时克隆;每个系列一个分支让发布只需一次推送,同时仍与代码历史隔离。
|
||||
|
||||
**把发布留在录制 skill 之外。**这是此前的状态;它保住了干净的边界,却让每个 PR 重新摸索同一套流程。这个边界如今以显式条件的形式保留:仅当任务包含把 GIF 附到 PR 时才执行发布,而不是靠省略来体现。
|
||||
|
||||
**让 GIF 在每个 PR 中保持可选。**可选的证据恰恰会在最需要它的进度压力下消失;没有录制的 GUI 变更评审,等于要求评审人自行想象渲染结果或重新构建分支。
|
||||
|
||||
## 后果
|
||||
|
||||
每个 GUI PR 都携带注明来源的可视证据,评审人无需重新构建分支即可看到变更。仓库历史保持不含媒体;代价转移到只追加的 assets 分支上:它们会持续增长、可以低成本地浅克隆、且永远不能删除。强制的真实来源录制给每个 GUI PR 的工作流增加一次真实密钥、真实模型轮次的运行,这是有意为之,因为这次运行本身就是证据。录制部分仍然在本地可撤销;任务不包含附到 PR 的 GIF 请求,仍以已验证的本地产物结束。
|
||||
Reference in New Issue
Block a user