From 061100f6f2cc1608a2c73811d74382f9d92b27d7 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Tue, 28 Jul 2026 00:20:38 +0800 Subject: [PATCH] docs: fix four factual errors found by Codex review round 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - plan.md/.zh.md (+ plan-mode module JSDoc and README pair): the sole flush point is the prepended agent/step listener; prompt admission is pre-turn and never flushes — the pages had it backwards. - workspace.md/.zh.md: pending-mutation recovery deletes the marked table row — completing an interrupted delete but rolling back an interrupted create; 'completes exactly the marked mutation' oversold. - telemetry.md/.zh.md: delivery is best-effort with possible loss AND duplication, not at-most-once; TelemetryRecord.attributes JSDoc (source + both fences) now lists session.seed_length, which the coordinator emits for forked sessions. - Agent Note consequence bullet: nine pages cover ten services (storage owns two keys); client-modules.zh.md wire-single-source phrasing. Pairs re-recorded; plan-mode and telemetry suites green. --- .../2026-06-20-core-data-structures-catalog.i18n.yaml | 4 ++-- .../process/2026-06-20-core-data-structures-catalog.md | 2 +- .../2026-06-20-core-data-structures-catalog.zh.md | 2 +- docs/subsystems/client-modules.i18n.yaml | 2 +- docs/subsystems/client-modules.zh.md | 2 +- docs/subsystems/plan.i18n.yaml | 4 ++-- docs/subsystems/plan.md | 2 +- docs/subsystems/plan.zh.md | 2 +- docs/subsystems/telemetry.i18n.yaml | 4 ++-- docs/subsystems/telemetry.md | 10 +++++----- docs/subsystems/telemetry.zh.md | 10 +++++----- docs/subsystems/workspace.i18n.yaml | 4 ++-- docs/subsystems/workspace.md | 2 +- docs/subsystems/workspace.zh.md | 2 +- packages/session/session-telemetry/src/index.ts | 8 ++++---- 15 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.i18n.yaml b/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.i18n.yaml index 5bc51a4e7d..0883a5e9ab 100644 --- a/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.i18n.yaml +++ b/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.i18n.yaml @@ -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: 718045bf7ce511a0b766e69e64ef7657eb221189 -2026-06-20-core-data-structures-catalog.zh.md: 1e8473a373c212fa08b171719edd5803e5aa3b05 +2026-06-20-core-data-structures-catalog.md: a8de47a462da13f316236fb7da3f4bfb57edae21 +2026-06-20-core-data-structures-catalog.zh.md: d84c2befaee4c2032b358ed1a7949ac0151ae74c diff --git a/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md b/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md index 718045bf7c..a8de47a462 100644 --- a/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md +++ b/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md @@ -58,4 +58,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 sub-page tier spans every service-bearing subsystem: the nine `ctx` services without a page (permission presets, plan mode, runtime invariants, the HTTP carrier, storage, TUI extensions, workspaces, client modules, telemetry) gained lean pages, 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. +- Since 2026-07-27 the sub-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. diff --git a/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.zh.md b/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.zh.md index 1e8473a373..d84c2befae 100644 --- a/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.zh.md +++ b/.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.zh.md @@ -58,4 +58,4 @@ Status: implemented - 主干与 seam 分界线是一个可复用的范围界定工具,而非一次性的:同一条「你编写/持有/接收的东西是核心;为其提供类型推导/渲染/持久化的机制是细节」规则,后来也被用于界定事件/服务目录的 harness 层与继承层分层。 - `ts type-equiv` 围栏是继 ` ```ts `(编译)和 ` ```ts ignore-check `(草稿)之后的第三种文档块类别。后续的姊妹门禁又增加了第四种 ` ```ts cordis-catalog `(生成签名),复用了相同的跳过并排除处理。 - 添加或重塑核心类型现在附带一项文档义务,作者必须履行(门禁无法检测缺失的*新*类型),由 `dsh-code-review` 检查清单兜底。 -- 自 2026-07-27 起,子页面层级覆盖每个承载服务的子系统:原先没有页面的九个 `ctx` 服务(权限预设、计划模式、运行时不变式、HTTP 载体、存储、终端扩展、工作区、客户端模块、遥测)都获得了精简页面,于是每个 harness 服务和事件作用域都有恰好一个所属的 subsystems 页面——这是把按子系统生成的服务/事件参考写入这些页面(而非平铺目录)的前提。 +- 自 2026-07-27 起,子页面层级覆盖每个承载服务的子系统:九个精简页面(权限预设、计划模式、运行时不变式、HTTP 载体、存储——同时拥有 `ctx.storage` 与 `ctx.storageDomain`——终端扩展、工作区、客户端模块、遥测)覆盖了原先没有页面的十个 `ctx` 服务,于是每个 harness 服务和事件作用域都有恰好一个所属的 subsystems 页面——这是把按子系统生成的服务/事件参考写入这些页面(而非平铺目录)的前提。 diff --git a/docs/subsystems/client-modules.i18n.yaml b/docs/subsystems/client-modules.i18n.yaml index 9c30e276d4..8988609cdf 100644 --- a/docs/subsystems/client-modules.i18n.yaml +++ b/docs/subsystems/client-modules.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/client-modules.md client-modules.md: bd5ecdc85ea26b040e1705e373ceac4e7cb17b3c -client-modules.zh.md: e04bd8ac0c6a1566307559f02fc8075c907c0a7f +client-modules.zh.md: 5da7740e4c9272a3516f1a28712be7c95f29a6b3 diff --git a/docs/subsystems/client-modules.zh.md b/docs/subsystems/client-modules.zh.md index e04bd8ac0c..5da7740e4c 100644 --- a/docs/subsystems/client-modules.zh.md +++ b/docs/subsystems/client-modules.zh.md @@ -8,7 +8,7 @@ Web 插件表:[dsh-client-modules](../../packages/client/modules) 中 client ## wire -图是 Node 半与浏览器半之间的线上单源:宿主从扫描到的包组合出 `WebBootEntry` 行,把图作为 `` 中的第一个脚本注入(`window.__DSH_BOOT__`,其中 `<` 已转义,插件可控的字符串因此无法逃出 script 元素),壳则在启动任何东西之前先解析它。没有有效 manifest 的页面无法启动——浏览器侧的解析器在图缺失或畸形时大声抛错。 +图是 Node 半与浏览器半之间协议层的唯一事实来源:宿主从扫描到的包组合出 `WebBootEntry` 行,把图作为 `` 中的第一个脚本注入(`window.__DSH_BOOT__`,其中 `<` 已转义,插件可控的字符串因此无法逃出 script 元素),壳则在启动任何东西之前先解析它。没有有效 manifest 的页面无法启动——浏览器侧的解析器在图缺失或畸形时大声抛错。 ```ts type-equiv /** diff --git a/docs/subsystems/plan.i18n.yaml b/docs/subsystems/plan.i18n.yaml index c73912c178..7c20a42864 100644 --- a/docs/subsystems/plan.i18n.yaml +++ b/docs/subsystems/plan.i18n.yaml @@ -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 docs/subsystems/plan.md -plan.md: f67e7734ca3fd55c284d3d41227f8241de7b9f5f -plan.zh.md: 27114c3c1c2acae3868b9c5bff37485b6da85377 +plan.md: eb88047573536cd15be587b1419271bfbd93e9f1 +plan.zh.md: 5a838a045d63f2cabad80cad8af12df5da7e87f3 diff --git a/docs/subsystems/plan.md b/docs/subsystems/plan.md index f67e7734ca..eb88047573 100644 --- a/docs/subsystems/plan.md +++ b/docs/subsystems/plan.md @@ -14,7 +14,7 @@ Source: [`packages/plan/plan-mode/src/index.ts`](../../packages/plan/plan-mode/s Because every session event is turn-enclosed, a user selection is held as pending intent until a turn boundary. `set(agent, active)` records the pending selection (a no-op when the target equals the logged-or-already-pending state), and `get(agent)` returns `{ active: boolean; pending?: boolean }` — the logged state shaping the current step, plus the optimistic selection awaiting a boundary. -The service flushes one pending selection before the affected request assembly at three boundaries: prompt submission, ordinary turn continuation, and request-recovery retry. The flush runs after the downstream listener chain, so a selection arriving while an async listener awaits still shapes the request that boundary precedes. A flush failure is contained — plan policy can never block a prompt or turn — and the failed append stays pending for a later boundary. A flushed user selection also narrates the switch as one plugin-sourced `user/message` notice, but only when the last logged request header described the other state, so the model is told exactly when its context changed and never redundantly. A pending selection made while idle is process-local and lost on exit before the next boundary ([README limitation](../../packages/plan/plan-mode/README.md#known-limitations-and-deferred-work)). +The sole flush point is a prepended `agent/step` listener — the loop's in-turn interception seam that runs before every request derivation, including turn 1 step 1 and request-recovery retries. Prompt admission itself never flushes: it happens pre-turn, where a `plan/mode` append would land outside any open turn, so a selection made at the prompt is landed by the first step boundary inside the turn it starts. The prepend means the flush runs before the downstream `agent/step` listener chain. A flush failure is contained — plan policy can never block a turn — and the failed append stays pending for a later boundary. A flushed user selection also narrates the switch as one plugin-sourced `user/message` notice, but only when the last logged request header described the other state, so the model is told exactly when its context changed and never redundantly. A pending selection made while idle is process-local and lost on exit before the next boundary ([README limitation](../../packages/plan/plan-mode/README.md#known-limitations-and-deferred-work)). ## Configuration diff --git a/docs/subsystems/plan.zh.md b/docs/subsystems/plan.zh.md index 27114c3c1c..5a838a045d 100644 --- a/docs/subsystems/plan.zh.md +++ b/docs/subsystems/plan.zh.md @@ -14,7 +14,7 @@ 由于每个会话事件都位于轮次之内,用户的选择会作为待定意图保留到轮次边界。`set(agent, active)` 记录待定选择(目标值与已记录或已在待定中的状态相同时不做任何事),`get(agent)` 返回 `{ active: boolean; pending?: boolean }`,即影响当前步骤的已记录状态,加上正在等待边界的乐观选择。 -服务会在三类边界处、于受影响的请求组装之前冲刷一条待定选择:提示词提交、普通轮次延续和请求恢复重试。冲刷在下游监听器链之后运行,因此在某个异步监听器等待期间到达的选择,仍会影响紧随该边界的那个请求。冲刷失败会被收容(计划策略绝不能阻塞提示词或轮次),追加失败的选择保持待定,等待后续边界。已冲刷的用户选择还会以一条插件来源的 `user/message` 通知叙述这次切换,但仅当最后记录的请求头描述的是另一种状态时才叙述,因此模型恰好在上下文变化时被告知,且绝不重复。空闲时做出的待定选择只存在于进程内,进程在下一个边界之前退出即丢失([README 限制](../../packages/plan/plan-mode/README.md#known-limitations-and-deferred-work))。 +唯一的冲刷点是一个前置(prepend)注册的 `agent/step` 监听器——agent loop 的轮内拦截 seam,在每次请求派生之前运行,包括第 1 轮第 1 步和请求恢复重试。提示词提交本身绝不冲刷:它发生在轮次开启之前,此时追加 `plan/mode` 会落在任何开启的轮次之外,因此在提示词处做出的选择由它开启的轮次内的第一个步骤边界落地。前置注册意味着冲刷先于下游的 `agent/step` 监听器链运行。冲刷失败会被收容(计划策略绝不能阻塞轮次),追加失败的选择保持待定,等待后续边界。已冲刷的用户选择还会以一条插件来源的 `user/message` 通知叙述这次切换,但仅当最后记录的请求头描述的是另一种状态时才叙述,因此模型恰好在上下文变化时被告知,且绝不重复。空闲时做出的待定选择只存在于进程内,进程在下一个边界之前退出即丢失([README 限制](../../packages/plan/plan-mode/README.md#known-limitations-and-deferred-work))。 ## 配置 diff --git a/docs/subsystems/telemetry.i18n.yaml b/docs/subsystems/telemetry.i18n.yaml index 9f6fdb3af3..d7544c7ddf 100644 --- a/docs/subsystems/telemetry.i18n.yaml +++ b/docs/subsystems/telemetry.i18n.yaml @@ -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 docs/subsystems/telemetry.md -telemetry.md: 4942a402dacb40ed64070c2b7824bfea7a14c872 -telemetry.zh.md: c99b287fd4cb4e9bc585ffe365ca08df37f6ccd1 +telemetry.md: cf6d3556a638375d939b29cc28941bf9c9ec7ea4 +telemetry.zh.md: 95eadb26581a6a96ce208d7c3b265d89455de4ba diff --git a/docs/subsystems/telemetry.md b/docs/subsystems/telemetry.md index 4942a402da..cf6d3556a6 100644 --- a/docs/subsystems/telemetry.md +++ b/docs/subsystems/telemetry.md @@ -38,10 +38,10 @@ interface TelemetryRecord { /** * Identity attributes, deliberately minimal: ledger records carry * `session.id`, `event.type`, `event.seq`, plus `session.cwd` / - * `session.parent_id` when the header has them; ops records carry - * `telemetry.op`, `session.id`, and (for `agent-error`) `agent.id`, - * `turn`, `step`, `error.name`. Anything recoverable from the body is - * intentionally NOT duplicated here. + * `session.parent_id` / `session.seed_length` when the header has them; + * ops records carry `telemetry.op`, `session.id`, and (for `agent-error`) + * `agent.id`, `turn`, `step`, `error.name`. Anything recoverable from the + * body is intentionally NOT duplicated here. */ attributes: Record /** @@ -54,7 +54,7 @@ interface TelemetryRecord { } ``` -Only the first `assistant/chunk` of each `(turn, step)` ships — the stream-started signal; the rest drop at capture, so `seq` gaps are routine on the wire and never a loss signal. Every other [session event](session.md) type, including plugin-merged ones the seam never heard of, passes through whole. Delivery is at-most-once downstream of the handoff; receivers dedupe on `(session.id, event.seq)`. +Only the first `assistant/chunk` of each `(turn, step)` ships — the stream-started signal; the rest drop at capture, so `seq` gaps are routine on the wire and never a loss signal. Every other [session event](session.md) type, including plugin-merged ones the seam never heard of, passes through whole. Delivery is best-effort: the cursor marks handed-off, not delivered, records can be lost (crash, reload window) and duplicated (cursor-less re-adoption, SDK retries), so receivers dedupe on `(session.id, event.seq)`. ## The backend contract diff --git a/docs/subsystems/telemetry.zh.md b/docs/subsystems/telemetry.zh.md index c99b287fd4..95eadb2658 100644 --- a/docs/subsystems/telemetry.zh.md +++ b/docs/subsystems/telemetry.zh.md @@ -38,10 +38,10 @@ interface TelemetryRecord { /** * Identity attributes, deliberately minimal: ledger records carry * `session.id`, `event.type`, `event.seq`, plus `session.cwd` / - * `session.parent_id` when the header has them; ops records carry - * `telemetry.op`, `session.id`, and (for `agent-error`) `agent.id`, - * `turn`, `step`, `error.name`. Anything recoverable from the body is - * intentionally NOT duplicated here. + * `session.parent_id` / `session.seed_length` when the header has them; + * ops records carry `telemetry.op`, `session.id`, and (for `agent-error`) + * `agent.id`, `turn`, `step`, `error.name`. Anything recoverable from the + * body is intentionally NOT duplicated here. */ attributes: Record /** @@ -54,7 +54,7 @@ interface TelemetryRecord { } ``` -每个 `(turn, step)` 只发出第一条 `assistant/chunk`,即「流已开始」的信号;其余分片在捕获时丢弃,因此导出流中的 `seq` 缺口是常态,绝不是丢失信号。其他所有[会话事件](session.md)类型都会完整透传,包括该 seam 从未听说过、由插件合并进来的事件类型。交接下游的投递为至多一次(at-most-once);接收端基于 `(session.id, event.seq)` 去重。 +每个 `(turn, step)` 只发出第一条 `assistant/chunk`,即「流已开始」的信号;其余分片在捕获时丢弃,因此导出流中的 `seq` 缺口是常态,绝不是丢失信号。其他所有[会话事件](session.md)类型都会完整透传,包括该 seam 从未听说过、由插件合并进来的事件类型。投递是尽力而为的:游标标记的是「已交接」而非「已送达」,记录可能丢失(崩溃、重载窗口)也可能重复(无游标的重新接管、SDK 重试),因此接收端基于 `(session.id, event.seq)` 去重。 ## 后端契约 diff --git a/docs/subsystems/workspace.i18n.yaml b/docs/subsystems/workspace.i18n.yaml index 16ad3b95a3..a50df35f91 100644 --- a/docs/subsystems/workspace.i18n.yaml +++ b/docs/subsystems/workspace.i18n.yaml @@ -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 docs/subsystems/workspace.md -workspace.md: 2726115457905687e3c2cfc35a7a3f7251e6347c -workspace.zh.md: 3a752617a58e4c9b62ec4084d2dc518015030952 +workspace.md: bade3526964055d5bf4448265c5898f1e66a1fce +workspace.zh.md: b0437e42b644c1ce11d9a667972f7b236cf4caa1 diff --git a/docs/subsystems/workspace.md b/docs/subsystems/workspace.md index 2726115457..bade352696 100644 --- a/docs/subsystems/workspace.md +++ b/docs/subsystems/workspace.md @@ -112,7 +112,7 @@ Ownership truth is the record's ordered `sessionIds`, never derived from session ## The registry: `ctx.workspace` -`WorkspaceRegistry` ([signatures](../cordis-catalog/services.md#ctxworkspace--workspaceregistry)) owns registration and resolution. `create(path, title?)` canonicalizes the path, rejects a nonexistent path (the original `ENOENT`) or a non-directory, returns the existing entity unchanged when the canonical path is already owned, and otherwise creates a record with `title ?? basename(path)` prepended to the durable registry order — a new record cannot duplicate an existing display title (`WorkspaceNameConflictError`). `get(id)` and the ordered `list()` are synchronous cache reads; `resolveByPath(path)` applies the same realpath canon without creating. `delete(id)` removes only the registration, order entry, and session account — the directory, user files, live sessions, and persisted logs are never touched, so those sessions become Ungrouped ([decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md)); unknown ids return `false`. Create and delete persist a pending-mutation marker before their two writes (record + order) can diverge; startup completes exactly the marked mutation, and an unmarked order/table mismatch fails loud as corruption. +`WorkspaceRegistry` ([signatures](../cordis-catalog/services.md#ctxworkspace--workspaceregistry)) owns registration and resolution. `create(path, title?)` canonicalizes the path, rejects a nonexistent path (the original `ENOENT`) or a non-directory, returns the existing entity unchanged when the canonical path is already owned, and otherwise creates a record with `title ?? basename(path)` prepended to the durable registry order — a new record cannot duplicate an existing display title (`WorkspaceNameConflictError`). `get(id)` and the ordered `list()` are synchronous cache reads; `resolveByPath(path)` applies the same realpath canon without creating. `delete(id)` removes only the registration, order entry, and session account — the directory, user files, live sessions, and persisted logs are never touched, so those sessions become Ungrouped ([decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md)); unknown ids return `false`. Create and delete persist a pending-mutation marker before their two writes (record + order) can diverge; startup resolves exactly the marked mutation — by deleting the marked table row, which completes an interrupted delete and rolls back an interrupted create (the registration is re-creatable, so rollback is the safe direction) — and an unmarked order/table mismatch fails loud as corruption. Sessions get their cwd at create time from whoever creates them, not from this registry — the API gateway resolves a new session's cwd from the chosen workspace's `path` (falling back to an explicit or default cwd), creates the session so the cwd lands in its immutable [`SessionHeader`](persistence.md#sessionheader--metadata-beside-the-log), then calls `attachSession`, which re-validates that stored header cwd against the workspace path. On the first successful start, the registry bootstraps history from persisted headers alone (`id`, `cwd`, `createdAt` — never event bodies), grouping sessions with a valid canonical cwd into per-directory workspaces, newest first; the initialized marker is written last so an interrupted bootstrap resumes safely. The bootstrap is one-time: cwd-less legacy sessions stay Ungrouped, and sessions created afterwards join a workspace only through `attachSession`. diff --git a/docs/subsystems/workspace.zh.md b/docs/subsystems/workspace.zh.md index 3a752617a5..b0437e42b6 100644 --- a/docs/subsystems/workspace.zh.md +++ b/docs/subsystems/workspace.zh.md @@ -112,7 +112,7 @@ interface Workspace { ## 注册表:`ctx.workspace` -`WorkspaceRegistry`([签名](../cordis-catalog/services.md#ctxworkspace--workspaceregistry))拥有注册与解析。`create(path, title?)` 规范化路径,拒绝不存在的路径(原样传出原始 `ENOENT`)或非目录;当规范路径已被拥有时原样返回既有实体;否则创建一条标题为 `title ?? basename(path)` 的记录并前插到持久的注册表顺序中——新记录不得与既有显示标题重复(`WorkspaceNameConflictError`)。`get(id)` 与有序的 `list()` 是同步缓存读取;`resolveByPath(path)` 应用同一套 realpath 规范但不创建。`delete(id)` 只移除注册记录、顺序条目和会话账本——目录、用户文件、实时会话和已持久化日志一概不动,因此这些会话变为 Ungrouped([决策](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md));未知 id 返回 `false`。create 与 delete 会在其两次写入(记录 + 顺序)可能分叉之前先持久写入一个待定变更标记;启动时恰好补完被标记的那次变更,而没有标记的顺序/表不一致则作为损坏大声失败。 +`WorkspaceRegistry`([签名](../cordis-catalog/services.md#ctxworkspace--workspaceregistry))拥有注册与解析。`create(path, title?)` 规范化路径,拒绝不存在的路径(原样传出原始 `ENOENT`)或非目录;当规范路径已被拥有时原样返回既有实体;否则创建一条标题为 `title ?? basename(path)` 的记录并前插到持久的注册表顺序中——新记录不得与既有显示标题重复(`WorkspaceNameConflictError`)。`get(id)` 与有序的 `list()` 是同步缓存读取;`resolveByPath(path)` 应用同一套 realpath 规范但不创建。`delete(id)` 只移除注册记录、顺序条目和会话账本——目录、用户文件、实时会话和已持久化日志一概不动,因此这些会话变为 Ungrouped([决策](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md));未知 id 返回 `false`。create 与 delete 会在其两次写入(记录 + 顺序)可能分叉之前先持久写入一个待定变更标记;启动时恰好解决被标记的那次变更——通过删除被标记的表行:这会补完被中断的 delete,并回滚被中断的 create(注册可以重建,因此回滚是安全方向)——而没有标记的顺序/表不一致则作为损坏大声失败。 会话的 cwd 在创建时由创建者赋予,而不是由本注册表赋予——API 网关从所选工作区的 `path` 解析新会话的 cwd(回退到显式或默认 cwd),先创建会话使 cwd 落入其不可变的 [`SessionHeader`](persistence.md#sessionheader--metadata-beside-the-log),再调用 `attachSession`,后者会把已存储的 header cwd 与工作区路径重新校验一遍。首次成功启动时,注册表仅凭已持久化的 header(`id`、`cwd`、`createdAt`——绝不读事件正文)引导历史:把规范 cwd 有效的会话按目录分组为工作区,最新的排在最前;「已初始化」标记最后写入,因此被中断的引导可以安全续跑。引导只发生这一次:没有 cwd 的历史遗留会话保持 Ungrouped,此后创建的会话只能通过 `attachSession` 加入工作区。 diff --git a/packages/session/session-telemetry/src/index.ts b/packages/session/session-telemetry/src/index.ts index 0198df3140..6a8e9ed2ad 100644 --- a/packages/session/session-telemetry/src/index.ts +++ b/packages/session/session-telemetry/src/index.ts @@ -71,10 +71,10 @@ export interface TelemetryRecord { /** * Identity attributes, deliberately minimal: ledger records carry * `session.id`, `event.type`, `event.seq`, plus `session.cwd` / - * `session.parent_id` when the header has them; ops records carry - * `telemetry.op`, `session.id`, and (for `agent-error`) `agent.id`, - * `turn`, `step`, `error.name`. Anything recoverable from the body is - * intentionally NOT duplicated here. + * `session.parent_id` / `session.seed_length` when the header has them; + * ops records carry `telemetry.op`, `session.id`, and (for `agent-error`) + * `agent.id`, `turn`, `step`, `error.name`. Anything recoverable from the + * body is intentionally NOT duplicated here. */ attributes: Record /**