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:
Tianyi Cui
2026-07-27 23:56:18 +08:00
parent 0dcdf188e2
commit f2c0021941
103 changed files with 203 additions and 389 deletions

View File

@@ -1,6 +0,0 @@
# 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-immutable-public-surfaces.md: c9009ad923720efaecb25e2017ceab6e3eb0dbf4
2026-06-11-immutable-public-surfaces.zh.md: 4ef67734d712e538c5858fbc05efbc6dd983c704

View File

@@ -1,29 +0,0 @@
# Agent Note: Deep-readonly public surfaces
Status: rejected — the pervasive `DeepReadonly<T>` type flip is replaced by source-owned runtime immutability in `Session` plus relational development assertions. See [source-owned session immutability and dev-mode invariants](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md).
English | [中文](2026-06-11-immutable-public-surfaces.zh.md)
## Problem
The rejected proposal targeted an ownership hole that a `readonly SessionEvent[]` type alone cannot close: its elements remain mutable at runtime, so a cast or plain JavaScript can rewrite nested history. The implemented design closes that hole in `Session` by materializing and deep-freezing every accepted event and returning frozen array snapshots. In-flight prompt waterfalls remain intentionally transformable, so immutability is an ownership boundary rather than a blanket type rule.
## Proposal
> **Implemented differently — see the Status line and [source-owned session immutability and dev-mode invariants](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md).** The `DeepReadonly<T>` design below is rejected as written: it is compile-only, noisy across consumers, and castable. `Session` instead snapshots and deep-freezes accepted events and public log snapshots in every composition; `deriveMessages()` returns detached frozen projections; the development plugin checks cross-record and cross-seam relationships.
Make immutability part of the type where mutation is corruption:
- `SessionEvent` data becomes `DeepReadonly` on the way OUT of a session (`events`, `session/event` listeners); `append()` keeps taking plain mutable input. A `DeepReadonly<T>` utility type lands in dsh-llm next to the brand/never helpers.
- `deriveMessages()` returns deep-readonly messages; the loop clones before handing a mutable request to the `agent/request` waterfall (mutation there is sanctioned — the clone makes the boundary explicit and cheap, once per step).
- `PromptAssembly` stays mutable through its waterfall (sanctioned) but the registry's internal section list is cloned per assembly (already true).
## Plan
Introduce `DeepReadonly`, flip the session read paths, and fix the resulting compile errors in consumers.
## Risks
`DeepReadonly` types can produce noisy errors at waterfall boundaries where mutation IS the API — keep the mutable/readonly boundary exactly at "logged vs in-flight" and document it in the session README.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -1,29 +0,0 @@
# Agent Note: 深度只读的公开接口
Status: rejected — 普遍采用 `DeepReadonly<T>` 的类型翻转已由 `Session` 中归属源的运行时不可变性与关系型开发断言取代。见[归属源的会话不可变性与开发模式不变式](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md)。
[English](2026-06-11-immutable-public-surfaces.md) | 中文
## 问题
被否决的提案针对的是一个所有权漏洞:仅靠 `readonly SessionEvent[]` 类型无法封堵该漏洞,因为其元素在运行时仍然可变,类型强制转换或纯 JavaScript 代码可以改写嵌套的历史记录。已实现的设计在 `Session` 中封堵了这一漏洞:对每个被接受的事件进行物化并深度冻结,返回冻结的数组快照。进行中的提示词 waterfall瀑布式事件有意保持可变换因此不可变性是一条所有权边界而非一条全局类型规则。
## 提案
> **实际采用了不同的实现方式——见 Status 行与[源拥有的会话不可变性与开发模式不变式](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md)。** 下文的 `DeepReadonly<T>` 设计已被否决:它仅在编译期生效、对消费方噪音大、且可被强制转换绕过。`Session` 改为在每次组合中对已接受的事件和公开日志快照进行快照与深度冻结;`deriveMessages()` 返回分离的冻结投影;开发插件检查跨记录与跨 seam 的关系。
在类型层面为「突变即损坏」的场景引入不可变性:
- `SessionEvent` 数据在从会话输出时(`events``session/event` 监听器)变为 `DeepReadonly``append()` 仍接受普通可变输入。一个 `DeepReadonly<T>` 工具类型放在 dsh-llm 中,与 brand/never 辅助类型相邻。
- `deriveMessages()` 返回深度只读的消息agent loop智能体循环在将可变请求交给 `agent/request` waterfall 之前先克隆(该处的突变是被允许的——克隆使边界显式且代价低廉,每个步骤仅一次)。
- `PromptAssembly` 在其 waterfall 流经期间保持可变(被允许),但注册表内部的 section 列表在每次组装时被克隆(已有此行为)。
## 计划
引入 `DeepReadonly`,翻转会话的读取路径,并修复消费方中由此产生的编译错误。
## 风险
`DeepReadonly` 类型在 waterfall 边界处(突变本身就是 API 的地方)可能产生噪音较大的错误。应将可变/只读边界精确地划在「已记录 vs 进行中」,并在会话 README 中加以说明。
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -1,6 +0,0 @@
# 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-20-generic-tool-rendering.md: a9ceb7a0e016b57295e3226e98a7fce51e49c21f
2026-06-20-generic-tool-rendering.zh.md: 553c1caa23ed34eea5f113372d12a7381dc2488a

View File

@@ -1,37 +0,0 @@
# Agent Note: Collapse tool-owned UI presentation
Status: rejected — TUI and the Web host/client runtime consume the tagged render-intent union, so tool-owned presentation remains current even though ACP no longer projects it.
English | [中文](2026-06-20-generic-tool-rendering.zh.md)
## Problem
The optional-field bag and ACP editor mapping below were the proposal-time context for this rejection. The current contracts live in [the tagged render-intent union](../../implemented/architecture/2026-07-02-tool-render-intent-union.md) and [automation-only ACP](../../implemented/simplification/2026-07-23-acp-automation-only-protocol.md).
Tools could define `presentCall()` and `presentResult()` callbacks that returned `ToolCallPresentation`, `ToolResultPresentation`, and optional `ToolTerminal` fields. The code itself flagged the design as muddy: title, kind, raw input, content, terminal cwd, terminal output, exit code, and signal had grown incrementally into a bag of optional fields. ACP then maintained pending call state to pair a result with the original args, created replay-only presenters on `session/load`, and mapped terminal subfields into Zed-specific `_meta`. `dsh-tool-bash` even parsed exit status back out of rendered text because the pure replay-safe presenter no longer had the structured `BashRunResult`.
The real first-party use was bash presentation for ACP. That was too little evidence to freeze a cross-package UI presentation API.
## Proposal
Remove tool-owned UI presentation callbacks for now. The canonical tool events already carry the tool name, raw argument string, result content, and error state. UIs render a generic tool card from those fields. Tool-specific rich rendering can return later as a tagged render-intent union after there are at least two real tools and two real consumers to validate the vocabulary.
## Alternatives considered
As a smaller alternative, replace the current optional-field bag with one explicit union in a single PR; but if the goal is simplification, the stronger move is to delete the callbacks and keep the generic path.
## Acceptance criteria
- `ToolDefinition` drops `presentCall` and `presentResult`.
- `ToolCallPresentation`, `ToolResultPresentation`, `ToolTerminal`, and `ToolCallKind` disappear unless a minimal generic UI type still needs one.
- ACP no longer keeps presenter pending state or calls tool callbacks during live streaming/load replay.
- `dsh-tool-bash` no longer parses rendered text to recover exit status for a UI pill.
- Snapshot expected outputs show generic tool cards and text results.
## What we give up
Under this proposal, Bash would lose its custom terminal-looking card and model-written description placement. The fallback would remain reasonable: the command would appear as tool input, and the output as text. Rich rendering would be designed when the product had enough UI/tool variety to justify a stable presentation contract.
## Related
The later [tagged render-intent union](../../implemented/architecture/2026-07-02-tool-render-intent-union.md) implements the smaller alternative once multiple producer and consumer families provide enough evidence for the vocabulary. [Automation-only ACP](../../implemented/simplification/2026-07-23-acp-automation-only-protocol.md) removes ACP's editor projection without removing tool-owned presentation from TUI or the Web host/client runtime.

View File

@@ -1,37 +0,0 @@
# Agent Note: 收拢工具自有的 UI 展示逻辑
Status: rejected — 尽管 ACPAgent Client Protocol已不再投影这套契约TUI 与 Web 宿主/客户端运行时仍消费带标签 render-intent 联合类型,因此工具自有的展示仍然有效。
[English](2026-06-20-generic-tool-rendering.md) | 中文
## 问题
下文所述的可选字段集合与 ACP 的编辑器映射,是本提案遭否决时的背景。当前契约分别由[带标签 render-intent 联合类型](../../implemented/architecture/2026-07-02-tool-render-intent-union.md)与[ACP 作为仅面向自动化的协议](../../implemented/simplification/2026-07-23-acp-automation-only-protocol.md)承载。
当时,工具可以定义 `presentCall()``presentResult()` 回调,返回 `ToolCallPresentation``ToolResultPresentation` 以及可选的 `ToolTerminal` 字段。代码本身就标记了这个设计的混乱title、kind、raw input、content、terminal cwd、terminal output、exit code 和 signal 已经逐步增长为一堆可选字段。ACP 随后维护 pending call 状态以将 result 与原始 args 配对,在 `session/load` 时创建仅用于回放的 presenter并将 terminal 子字段映射为 Zed 特有的 `_meta``dsh-tool-bash` 甚至从渲染后的文本中反向解析退出状态,因为纯回放安全的 presenter 已经拿不到结构化的 `BashRunResult`
当时,真正的第一方用途是为 ACP 提供 bash 展示。这点证据不足以作为冻结一个跨包packageUI 展示 API 的依据。
## 提案
暂时移除工具自有的 UI 展示回调。规范的工具事件已经携带工具名、原始参数字符串、结果内容和错误状态。UI 从这些字段渲染一个通用的工具卡片。工具特有的富展示可以在至少有两个真实工具和两个真实消费方来验证词汇之后,以带标签的 render-intent union 形式回归。
## 曾考虑的替代方案
作为更小的替代方案,可以在一个 PRPull Request中将当前的可选字段集合替换为一个显式 union但如果目标是简化更彻底的做法是删除回调、保留通用路径。
## 验收标准
- `ToolDefinition` 移除 `presentCall``presentResult`
- `ToolCallPresentation``ToolResultPresentation``ToolTerminal``ToolCallKind` 消失,除非一个最小的通用 UI 类型仍需要其中之一。
- ACP 不再维护 presenter pending 状态,也不再在实时流式输出/加载回放期间调用工具回调。
- `dsh-tool-bash` 不再解析渲染文本来恢复退出状态以供 UI pill 使用。
- 快照预期输出展示通用工具卡片和文本结果。
## 放弃了什么
如果采用本提案Bash 会失去其自定义的终端风格卡片和模型生成描述的放置位置。届时,回退方案仍然合理:命令会作为工具输入展示,输出会作为文本展示。只有当产品拥有足够的 UI/工具多样性、足以支撑一份稳定的展示契约时,才会设计富展示。
## 相关
后续的[带标签 render-intent 联合类型](../../implemented/architecture/2026-07-02-tool-render-intent-union.md)在多类生产者与消费方为这套词汇提供充分依据后,实现了较小的替代方案。[ACP 作为仅面向自动化的协议](../../implemented/simplification/2026-07-23-acp-automation-only-protocol.md)移除了 ACP 的编辑器投影,但没有从 TUI 或 Web 宿主/客户端运行时中移除工具自有的展示。

View File

@@ -1,6 +0,0 @@
# 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-20-retire-mid-turn-steering.md: a8812b3222739244d77f4d4dab60cf7c0cd6907d
2026-06-20-retire-mid-turn-steering.zh.md: 81a211a167daeb8c57b98f2a1c1451dbc54d09e4

View File

@@ -1,37 +0,0 @@
# Agent Note: Retire mid-turn steering
Status: rejected — mid-turn steering is an intentional agent capability for between-step user/plugin input and future goal/loop workflows. It is complexity with a product direction, not an accidental duplicate of `send()`.
English | [中文](2026-06-20-retire-mid-turn-steering.zh.md)
## Problem
The agent exposes two user-message paths that look close but have different lifecycle semantics: `send()` queues a normal user turn, while `steer()` injects a message between steps of the currently running turn and falls back to `send()` when idle. That distinction leaks through the whole stack: `Agent.steer()` is public API, the session log has a durable `steering/message` event, the agent event taxonomy has `agent/steering`, the loop maintains a steering FIFO beside the queued-message FIFO, cancellation clears both queues, and `deriveMessages()` has to render steering as a tagged synthetic user message rather than a normal prompt.
The continuation seam amplifies the cost. `agent/turn-continuation` defaults to `hadToolCalls || steeringInjected`, so a same-turn steering message can force the loop to call the model again even if the model did not ask for tools. The comments name future `/goal`, `/loop`, and budget-guard uses, but the current repo has no production listener; only tests register the waterfall. Separately, the only production UI that calls `steer()` is the stdio demo. ACP already sends prompts through the ordinary queue while a turn is running.
## Proposal
Delete mid-turn user steering for now. `Agent.send()` becomes the single public way to submit user content; when the agent is running, the content waits for the next turn. The loop continues within a turn only for tool calls, not because a user typed while a step was running. A caller that wants to interrupt the current turn uses `cancel()` and then `send()`.
Remove `Agent.steer()`, the steering FIFO, `steering/message`, `agent/steering`, steering-derived continuation, and the cancellation logic that distinguishes queued messages from steering messages. Remove `agent/turn-continuation` in the same change unless the implementing PR discovers a production listener; without steering, the current repo has no concrete continuation consumer left. If a real budget or goal plugin later needs forced continuation, it should reintroduce a narrower seam with that plugin as the concrete consumer.
## Acceptance criteria
- `Agent` exposes one user-message entry point, `send()`.
- The durable session event vocabulary no longer contains `steering/message`.
- `deriveMessages()` renders normal user messages and context injections, with no steering tag path.
- The loop has one queued-message FIFO and no same-turn user-message continuation path.
- `agent/turn-continuation` is removed or narrowed to a named production consumer.
- The stdio UI and docs describe input while running as queued next-turn input.
- The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy.
## What we give up
A user cannot add same-turn steering content while a model is between tool steps. That behavior is useful in theory for "while you are already working, also consider X", but it is not the behavior ACP exposes today and it makes the turn boundary much harder to reason about. The simpler behavior is reasonable: user input becomes the next prompt, and cancellation remains the explicit tool for replacing in-flight work.
## Related
This pairs naturally with [dropping durable step boundaries](2026-06-20-drop-durable-step-boundaries.md), because removing same-turn steering and `agent/turn-continuation` leaves tool calls as the only reason a turn contains multiple model steps.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -1,37 +0,0 @@
# Agent Note: 移除轮次中途引导
Status: rejected — 轮次中途 steering中途引导是一项有意设计的 agent智能体能力用于接收步骤之间的用户/插件输入以及未来的 goal/loop 工作流。它是面向产品方向的复杂度,而非 `send()` 的意外重复。
[English](2026-06-20-retire-mid-turn-steering.md) | 中文
## 问题
agent 暴露了两条用户消息路径,外观相近但生命周期语义不同:`send()` 将一条普通用户轮次排入队列,而 `steer()` 在当前运行轮次的步骤之间注入一条消息,空闲时则回退为 `send()`。这一区分贯穿整个栈:`Agent.steer()` 是公开 API会话日志有持久化的 `steering/message` 事件agent 事件分类体系有 `agent/steering`agent loop智能体循环在排队消息 FIFO 之外还维护一个 steering FIFO取消操作需要清空两个队列`deriveMessages()` 必须将 steering 渲染为带标签的合成用户消息,而非普通提示词。
续行 seam 进一步放大了成本。`agent/turn-continuation` 默认条件为 `hadToolCalls || steeringInjected`,因此同一轮次内的 steering 消息即使模型未请求工具调用,也会强制循环再次调用模型。注释中提到了未来 `/goal``/loop` 和预算守卫的用途,但当前仓库没有生产级监听器;只有测试注册了该 waterfall瀑布式事件。另外唯一调用 `steer()` 的生产 UI 是 stdio 演示。ACPAgent Client Protocol在轮次运行期间已经通过普通队列发送提示词。
## 提案
暂时删除轮次中途的用户 steering。`Agent.send()` 成为提交用户内容的唯一公开方式;当 agent 正在运行时,内容等待下一个轮次。循环仅因工具调用而在轮次内继续,不因用户在某个步骤运行期间输入内容而继续。调用方若要中断当前轮次,使用 `cancel()` 后再 `send()`
移除 `Agent.steer()`、steering FIFO、`steering/message``agent/steering`、由 steering 驱动的续行逻辑,以及取消操作中区分排队消息与 steering 消息的逻辑。除非实现 PRPull Request发现了生产级监听器否则在同一变更中一并移除 `agent/turn-continuation`;没有 steering 后,当前仓库不再有具体的续行消费方。如果将来真正的预算或目标插件需要强制续行,应以该插件为具体消费方重新引入一个更窄的 seam。
## 验收标准
- `Agent` 暴露唯一的用户消息入口 `send()`
- 持久化会话事件词汇不再包含 `steering/message`
- `deriveMessages()` 渲染普通用户消息和上下文注入,不存在 steering 标签路径。
- 循环只有一个排队消息 FIFO没有同轮次用户消息续行路径。
- `agent/turn-continuation` 被移除,或收窄到有具名的生产级消费方。
- stdio UI 和文档将运行期间的输入描述为「排入下一轮次的输入」。
- 会话格式版本和已录制的 fixture测试前置数据已刷新非当前版本的存储日志按预发布格式策略被拒绝。
## 放弃了什么
用户无法在模型处于工具步骤之间时添加同轮次 steering 内容。这种行为在理论上对「你已经在工作了,也考虑一下 X」的场景有用但它不是 ACP 当前暴露的行为,且使轮次边界更难推理。更简单的行为是合理的:用户输入成为下一条提示词,取消操作仍是替换进行中工作的显式手段。
## 相关
本提案与[移除持久化步骤边界](2026-06-20-drop-durable-step-boundaries.md)天然配对,因为移除同轮次 steering 和 `agent/turn-continuation` 后,工具调用成为一个轮次包含多个模型步骤的唯一原因。
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -1,6 +0,0 @@
# 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-20-single-session-acp-bridge.md: e99de76854390a0979d1b66866d1d48aacbc0036
2026-06-20-single-session-acp-bridge.zh.md: 660e4ccf6f2fba8672315bfed30872870f401554

View File

@@ -1,31 +0,0 @@
# Agent Note: Return the ACP bridge to one live session per connection
Status: rejected — Zed is the current target ACP client and its ACP implementation is explicitly multi-session: it stores live sessions in a `HashMap<SessionId, AcpSession>`, tracks `pending_sessions`, joins concurrent loads for the same id, and tests close-during-load behavior.
English | [中文](2026-06-20-single-session-acp-bridge.zh.md)
## Problem
The ACP bridge now supports multiple live sessions on one JSON-RPC connection. That capability brings multi-entry session maps, reverse session/agent lookups, per-session prompt state, loading ids, demux for every event, cross-session teardown, and isolation concerns for future permission prompts and background tasks. The older [multi-session ACP proposal](../../implemented/feature/2026-06-14-acp-multi-session.md) still tracks the unfinished permission-ownership piece; this Agent Note is the competing simplification path.
The product target has proven it needs concurrent editor conversations over one harness process: Zed's ACP connection owns multiple sessions and load states. The snapshot replay tier still avoids concurrent model streams because its replay entries are positional; that is a test-fixture limitation, not a reason to remove bridge multiplexing.
## Proposal
Scope ACP back to one live session per connection. `session/new` or `session/load` creates the only session record; a second live session request is rejected until the existing session is disposed or the connection closes. If editors need multiple chat tabs, they can launch multiple agent subprocesses until the bridge has a concrete multi-session UX and permission model.
Remove the multi-session maps and demux where a single `SessionRecord | undefined` is enough. The bridge can still keep the agent/session lifecycle seams that make disposal correct; the simplification is only about multiplexing more than one active session through the same transport.
## Acceptance criteria
- ACP has one active session record per connection.
- `session/new` and `session/load` reject while that record exists.
- Event handlers no longer demux across a `Map<sessionId, record>`.
- Multi-session tests are removed or moved under the proposal that continues to defend multiplexing.
- The existing [multi-session ACP proposal](../../implemented/feature/2026-06-14-acp-multi-session.md) is updated to link this Agent Note and remains the live direction.
## What we give up
An ACP client cannot host several concurrent conversations on one server process. That is a meaningful capability cut. The simpler model is still reasonable for an unreleased harness: one editor conversation maps to one agent process, and cross-session permission/background-task isolation stops being a live correctness burden.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -1,31 +0,0 @@
# Agent Note: 将 ACPAgent Client Protocol桥接恢复为每连接一个活跃会话
Status: rejected — Zed 是当前目标 ACP 客户端,其 ACP 实现明确支持多会话:它把活跃会话存入 `HashMap<SessionId, AcpSession>`,跟踪 `pending_sessions`,合并同一 id 的并发加载,并测试加载期间关闭的行为。
[English](2026-06-20-single-session-acp-bridge.md) | 中文
## 问题
ACP 桥接现在支持在一条 JSON-RPC 连接上承载多个活跃会话。这一能力带来了多条目会话映射、反向会话/agent智能体查找、逐会话的提示词状态、加载中 id、每条事件的解复用、跨会话拆除以及未来权限提示与后台任务的隔离问题。较早的[多会话 ACP 提案](../../implemented/feature/2026-06-14-acp-multi-session.md)仍在追踪未完成的权限归属部分;本 Agent Noteagent 决策记录)是与之竞争的简化路径。
产品目标已经证明它需要在一个 harness 进程上承载并发的编辑器对话Zed 的 ACP 连接拥有多个会话和加载状态。快照回放层仍然避免并发模型流,因为其回放条目是位置相关的;这是测试 fixture测试前置数据的局限而非移除桥接多路复用的理由。
## 提案
将 ACP 的范围收回到每连接一个活跃会话。`session/new``session/load` 创建唯一的会话记录;在现有会话被 dispose资源释放或连接关闭之前第二个活跃会话请求将被拒绝。如果编辑器需要多个聊天标签页可以启动多个 agent 子进程,直到桥接具备具体的多会话 UX 和权限模型。
移除多会话映射和解复用逻辑,改用单一的 `SessionRecord | undefined` 即可。桥接仍可保留使 dispose 正确的 agent/会话生命周期 seam简化仅针对在同一传输层上多路复用多个活跃会话这一点。
## 验收标准
- ACP 每连接只有一条活跃会话记录。
- 当该记录存在时,`session/new``session/load` 拒绝请求。
- 事件处理器不再在 `Map<sessionId, record>` 上做解复用。
- 多会话测试被移除,或移至继续支持多路复用的提案下。
- 既有的[多会话 ACP 提案](../../implemented/feature/2026-06-14-acp-multi-session.md)更新为链接本 Agent Note并继续作为当前方向。
## 放弃了什么
ACP 客户端无法在一个服务器进程上承载多个并发对话。这是一项有实质意义的能力削减。对于一个尚未发布的 harness 而言,更简单的模型仍然合理:一个编辑器对话对应一个 agent 进程,跨会话的权限/后台任务隔离不再是活跃的正确性负担。
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -1,6 +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-dependency-swaps-rejected-by-nih-audit.md: 9cdb061bf21a7b9ce4747b9022c65d60e9644e0d
2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: 9648af149c0c517eba372baa12877240f9289aac
# pnpm run verify-translation-pairing --write .agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.md
2026-07-26-dependency-swaps-rejected-by-nih-audit.md: 5ef6629500171d3de2add070b0797d2c2d9b119a
2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: a11c593cdb056477689ad6e3e86d9d5c32a206a4

View File

@@ -41,7 +41,7 @@ Adopt the following dependency swaps. Rejected — per-item evidence below; a fu
- **`write-file-atomic` for fs-local/storage-json atomic writes**: the packages lack the private 0700 staging dir, Win32 DACL copy/`ReplaceFileW`, AbortSignal support, and parent-dir fsync — each the point of the hand-roll. The koffi Win32 bindings themselves are justified by the [Windows durable-publish note](../../implemented/architecture/2026-07-05-windows-jsonl-durable-publish.md).
- **`fzstd`/native zstd packages for JSONL frame scanning**: `node:zlib`'s builtin zstd already does the compression ([zstd note](../../implemented/architecture/2026-07-19-zstandard-jsonl-session-logs.md), which explicitly rejected an external native dependency); the remaining `scanZstdFrames` locates RFC 8878 frame boundaries *without decompressing* for torn-tail repair, which no package exposes.
- **`picomatch`/`tinyglobby`/`ignore` for fs search**: no glob engine exists — both discovery tools shell out to ripgrep per the [bash-backed discovery note](../../implemented/feature/2026-07-09-bash-backed-grep-glob-discovery.md).
- **`picomatch`/`tinyglobby`/`ignore` for fs search**: no glob engine exists — both discovery tools shell out to ripgrep per the [bash-backed discovery note](../../archived/feature/2026-07-09-bash-backed-grep-glob-discovery.md).
- **`istextorbinary`/`chardet` for text detection**: the hand-roll is a ~15-line NUL-sample plus fatal `TextDecoder`; heuristic packages are larger and would change which files the model can read (model-visible `FS_NOT_TEXT` drift).
- **`shell-quote` for POSIX single-quoting**: two 1-line quoting helpers with exhaustive tests versus a maintenance-mode package with a CVE history and different escaping output — a safety boundary is the wrong place to save one line.
- **`strip-ansi` for pty sanitization**: the pty sanitizer is a streaming state machine with split-sequence carry across chunks and OSC `133;D` prompt-marker extraction (the shell-readiness signal); stateless strippers replace ~20 inner lines while all state machinery stays. `stripVTControlCharacters` also demonstrably leaks unterminated-OSC payloads the session-title normalizer must strip (anti-spoofing).

View File

@@ -41,7 +41,7 @@ Status: rejected — 下列每一项替换在证据上都未达到净简化门
- **以 `write-file-atomic` 承担 fs-local/storage-json 的原子写**:这些包缺少私有 0700 暂存目录、Win32 DACL 复制/`ReplaceFileW`、AbortSignal 支持和父目录 fsync——每一项都正是手写实现的意义所在。koffi Win32 绑定本身由 [Windows 持久发布决策](../../implemented/architecture/2026-07-05-windows-jsonl-durable-publish.md)提供依据。
- **以 `fzstd`/原生 zstd 包承担 JSONL 帧扫描**`node:zlib` 内置的 zstd 已经负责压缩([zstd 决策](../../implemented/architecture/2026-07-19-zstandard-jsonl-session-logs.md),其中明确否决了外部原生依赖);剩下的 `scanZstdFrames` 为撕裂尾部修复*不做解压*地定位 RFC 8878 帧边界,没有任何包公开这项能力。
- **以 `picomatch`/`tinyglobby`/`ignore` 承担 fs 搜索**:根本不存在 glob 引擎——依照 [bash 承载的发现工具决策](../../implemented/feature/2026-07-09-bash-backed-grep-glob-discovery.md),两个发现类工具都通过 shell 调用 ripgrep。
- **以 `picomatch`/`tinyglobby`/`ignore` 承担 fs 搜索**:根本不存在 glob 引擎——依照 [bash 承载的发现工具决策](../../archived/feature/2026-07-09-bash-backed-grep-glob-discovery.md),两个发现类工具都通过 shell 调用 ripgrep。
- **以 `istextorbinary`/`chardet` 承担文本检测**:手写实现是约 15 行的 NUL 采样加 fatal 模式的 `TextDecoder`;启发式包体量更大,还会改变模型能读到哪些文件(模型可见的 `FS_NOT_TEXT` 漂移)。
- **以 `shell-quote` 承担 POSIX 单引号包裹**:两个各 1 行、测试详尽的引号辅助函数,对上一个处于维护模式、有 CVE 历史、转义输出还不一样的包——安全边界不是省一行代码的地方。
- **以 `strip-ansi` 承担 pty 净化**pty 净化器是一台流式状态机,带跨分片的断裂序列续接和 OSC `133;D` 提示符标记提取shell 就绪信号);无状态的剥离器只能替掉约 20 行内层代码,全部状态机构件原样保留。`stripVTControlCharacters` 还被实证会泄漏未终止的 OSC 载荷,会话标题归一化器必须剥除它们(反欺骗)。