docs(agent-notes): consolidate superseded decisions
This commit is contained in:
97 files changed
+279
-1432
No files matched your search
@@ -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-custom-schema-dsl.md: 947d53555df078bfa9f3dac48eab4b8c0074007c
|
||||
2026-06-11-custom-schema-dsl.zh.md: 26ebfe2fb15a6c034e809b3f51187342fa500193
|
||||
@@ -1,23 +0,0 @@
|
||||
# Agent Note: Custom typed tool-schema DSL instead of schemastery
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-06-11-custom-schema-dsl.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
Tool parameters must reach the model as standard JSON Schema while giving tool authors typed `execute(args)` without casts. Schemastery already serves plugin config, but the tool-author API needs per-property `required: true` booleans rather than JSON Schema's separate `required` array.
|
||||
|
||||
## Decision
|
||||
|
||||
This decision is superseded by the [unified JSON-value schema DSL](2026-07-20-unified-json-value-schema-dsl.md), which retains the small authoring surface while making parameters and typed values share one vocabulary. `ParameterSchemaSpec` keeps per-property `required: true`; `InferArgs<S>` maps required keys to non-optional properties; `parameterSchemaSpecToJsonSchema()` compiles the implicit open object root; and `defineTool()` ties inference, compilation, and validation together. Raw JSON-Schema `ToolDefinition`s remain accepted by `ToolRegistry.register()` for MCP and other external tools.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Schemastery** (already vendored, used for plugin Config) was evaluated and rejected for this use: it targets validation / transformation against StandardSchema, not JSON Schema *generation*, so it would add indirection without producing the wire format cleanly.
|
||||
|
||||
## Consequences
|
||||
|
||||
- First-party tool authors get zero-cast typed args; the type gymnastics cost stays inside the core package (sanctioned by the AGENTS.md type-safety policy).
|
||||
- The owning unified note defines the current nodes, literal constraints, unions, JSON-value boundary, and object-openness rules.
|
||||
- The `InferArgs` mapping is regression-tested at the type level after an early optionality bug.
|
||||
@@ -1,23 +0,0 @@
|
||||
# Agent Note: 使用自定义类型化工具 schema DSL 替代 schemastery
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-06-11-custom-schema-dsl.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
工具参数必须以标准 JSON Schema 形式到达模型,同时让工具作者在 `execute(args)` 中获得类型化的参数而无需类型断言。Schemastery 已用于插件配置,但工具作者 API 需要逐属性的 `required: true` 布尔值,而非 JSON Schema 的独立 `required` 数组。
|
||||
|
||||
## 决策
|
||||
|
||||
该决策已由[统一 JSON 值 schema DSL](2026-07-20-unified-json-value-schema-dsl.md)取代;新设计保留小型编写接口,同时让参数与类型化值共享一套词汇。`ParameterSchemaSpec` 保留逐属性的 `required: true`;`InferArgs<S>` 将必需键映射为非可选属性;`parameterSchemaSpecToJsonSchema()` 编译隐式开放的对象根;`defineTool()` 则将类型推导、编译与校验串联起来。原始 JSON Schema 的 `ToolDefinition` 仍是 `ToolRegistry.register()` 接受的输入,供 MCP 和其他外部工具使用。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
**Schemastery**(已作为 vendor 引入,用于插件 Config)经评估后被否决:它面向的是基于 StandardSchema 的校验/转换,而非 JSON Schema *生成*,因此会增加间接层却无法干净地产出协议格式(wire format)。
|
||||
|
||||
## 后果
|
||||
|
||||
- 第一方工具作者获得零类型断言的类型化参数;类型体操的成本留在核心包内部(符合 AGENTS.md 的类型安全策略)。
|
||||
- 当前节点、字面量约束、联合类型、JSON 值边界与对象开放性规则均由上述统一说明定义。
|
||||
- `InferArgs` 映射在类型层面有回归测试,源于早期一个可选性 bug。
|
||||
@@ -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
|
||||
2026-06-20-package-hierarchy.md: 7cd07ff90225872f2a17b9a678e52fcee416b09a
|
||||
2026-06-20-package-hierarchy.zh.md: 9ef89bd56144b39bb3240a22a2bb1e9216e24115
|
||||
2026-06-20-package-hierarchy.md: 4e05e3487483ab8d710959c1888ec1f5c3b37432
|
||||
2026-06-20-package-hierarchy.zh.md: f57704ad082c4961aa48056af1b4b279d2f2c055
|
||||
@@ -4,7 +4,7 @@ Status: implemented
|
||||
|
||||
English | [中文](2026-06-20-package-hierarchy.zh.md)
|
||||
|
||||
The later [fold-stdio-helper](../simplification/2026-07-04-fold-stdio-ui-helper.md) decision superseded the original `support/ui-stdio` placement, and the [redundant-agent removal](../simplification/2026-07-20-remove-stdio-and-echo-agents.md) subsequently removed that surface entirely. The [automation-only ACP decision](../simplification/2026-07-23-acp-automation-only-protocol.md) places ACP under `packages/acp/acp` instead of the human-UI group. The uniform depth-two hierarchy remains the decision owned here.
|
||||
The [redundant-agent removal](../simplification/2026-07-20-remove-stdio-and-echo-agents.md) deletes the original `support/ui-stdio` surface instead of relocating it, and the [automation-only ACP decision](../simplification/2026-07-23-acp-automation-only-protocol.md) places ACP under `packages/acp/acp` instead of the human-UI group. The uniform depth-two hierarchy remains the decision owned here.
|
||||
|
||||
## Problem
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Status: implemented
|
||||
|
||||
[English](2026-06-20-package-hierarchy.md) | 中文
|
||||
|
||||
后续的[折叠 stdio helper](../simplification/2026-07-04-fold-stdio-ui-helper.md)决策取代了最初的 `support/ui-stdio` 放置方式,[冗余 agent 移除](../simplification/2026-07-20-remove-stdio-and-echo-agents.md)随后又彻底移除了该接口。[仅面向自动化的 ACP 决策](../simplification/2026-07-23-acp-automation-only-protocol.md)把 ACP 放在 `packages/acp/acp` 下,而不是面向人类的 UI 组。这里拥有的决策仍是统一的二层目录深度。
|
||||
[冗余 agent 移除](../simplification/2026-07-20-remove-stdio-and-echo-agents.md)直接删除最初的 `support/ui-stdio` 接口,而不是将其迁移;[仅面向自动化的 ACP 决策](../simplification/2026-07-23-acp-automation-only-protocol.md)把 ACP 放在 `packages/acp/acp` 下,而不是面向人类的 UI 组。这里拥有的决策仍是统一的二层目录深度。
|
||||
|
||||
## 问题
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
2026-07-02-tool-render-intent-union.md: 6cfd8921decbe16343f963574edd52173c2f8698
|
||||
2026-07-02-tool-render-intent-union.zh.md: d0414c5f15995192df898e968d054933f82d2ab4
|
||||
2026-07-02-tool-render-intent-union.md: 84423e9000526848a111591c1bb2ab92067bbe50
|
||||
2026-07-02-tool-render-intent-union.zh.md: 43873c622fc8483b4a7033d17b4b4fab1342a56b
|
||||
@@ -54,6 +54,8 @@ interface TerminalResultView { card: 'terminal'; title?: string; output?: string
|
||||
|
||||
`TerminalResultView` carries only `output`/`exitCode`/`signal`. A UI without the terminal capability needs a fenced ` ```console ` text fallback; that derivation moves to the **bridge** (it wraps `output` in a fenced block on the no-capability path), rather than the tool double-encoding it. This keeps the bash tool's result a single structured shape and preserves the existing capability-gated behavior byte-for-byte.
|
||||
|
||||
The terminal intent is display-only. The harness still executes the command through its bash service, preserving sandboxing, environment scrubbing, task ownership, and per-session cwd; a UI projects the completed call and never becomes a second execution backend.
|
||||
|
||||
### Purity preserved
|
||||
|
||||
`presentCall`/`presentResult` remain pure functions of `args` (+ the result for `presentResult`) — they run on live streaming AND session-log replay, so they must be replay-deterministic. Every view is derived from args alone: write's diff is new-file style (`oldText:null`) because the tool has no old content at call time; edit's diff is `old_string`→`new_string`.
|
||||
@@ -61,6 +63,7 @@ interface TerminalResultView { card: 'terminal'; title?: string; output?: string
|
||||
## Alternatives considered
|
||||
|
||||
- **Delete tool-owned presentation entirely** — [the rejected collapse proposal](../../rejected/simplification/2026-06-20-generic-tool-rendering.md); its own verdict deferred to exactly this union once two real tools and two real consumers existed, and that bar is now met.
|
||||
- **Let a UI execute terminal intents** — rejected because it would bypass the harness's bash policy and ownership contracts and fork command execution across backends. A terminal card describes harness-owned execution; it never authorizes client-side execution.
|
||||
- **A merge-extensible union** (the `ContentBlockMap` pattern) — rejected: a new render intent needs new bridge code to render it anyway, so a plugin-added variant the bridge silently drops would be worse than the compile error the closed union raises at the bridge's `assertNever` switch.
|
||||
- **Keeping the optional-field bag** — the status quo the Problem dissects: invalid states representable, undocumented field interactions, and no way to ask for a diff card at all.
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ interface TerminalResultView { card: 'terminal'; title?: string; output?: string
|
||||
|
||||
`TerminalResultView` 只携带 `output`/`exitCode`/`signal`。不具备终端能力的 UI 需要一个围栏 ` ```console ` 文本回退;该推导移至 **bridge**(在无能力路径上将 `output` 包裹在围栏代码块中),而非由工具双重编码。这使 bash 工具的结果保持单一结构化形状,并逐字节保留既有的能力门控行为。
|
||||
|
||||
terminal 意图只用于展示。harness 仍通过自身的 bash 服务执行命令,从而保留沙箱、环境清理、任务归属和每会话 cwd;UI 只呈现已完成的调用,绝不会成为第二个执行后端。
|
||||
|
||||
### 纯函数性保持不变
|
||||
|
||||
`presentCall`/`presentResult` 仍然是 `args`(`presentResult` 还有 result)的纯函数——它们在实时流式输出和会话日志回放中都会运行,因此必须具备回放确定性。每个 view 仅从 args 推导:write 的 diff 是新文件风格(`oldText:null`),因为工具在调用时没有旧内容;edit 的 diff 是 `old_string`→`new_string`。
|
||||
@@ -61,6 +63,7 @@ interface TerminalResultView { card: 'terminal'; title?: string; output?: string
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **完全删除工具自有的展示**:即[被否决的 collapse 提案](../../rejected/simplification/2026-06-20-generic-tool-rendering.md);其自身的结论正是推迟到两个真实工具和两个真实消费方存在后再做此联合类型,该条件现已满足。
|
||||
- **让 UI 执行 terminal 意图**:否决。这样会绕过 harness 的 bash 策略与归属契约,并把命令执行分裂到不同后端。terminal 卡片描述的是 harness 拥有的执行,绝不授权客户端侧执行。
|
||||
- **可合并扩展的联合类型**(`ContentBlockMap` 模式):否决。新的渲染意图无论如何需要新的 bridge 代码来渲染,因此一个被 bridge 静默丢弃的插件添加变体,比封闭联合类型在 bridge 的 `assertNever` switch 处引发的编译错误更糟糕。
|
||||
- **保留可选字段集合**:即「问题」一节所剖析的现状:无效状态可表达、字段交互无文档、且完全无法请求 diff 卡片。
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# Agent Note: Windows write-permission semantics — inherited DACLs, not mode bits
|
||||
|
||||
Status: implemented
|
||||
|
||||
The replacement-file decision in this record is superseded by [Windows DACL preservation](../bug-fix/2026-07-19-windows-atomic-write-dacl-preservation.md).
|
||||
|
||||
## Problem
|
||||
|
||||
`writeFileAtomic` in `@deepseek-ai/dsh-fs-local` protects write-in-progress content with POSIX mode bits: the staging directory is created `0o700`, the temp file is opened `0o600`, and new files default to `0o600`. On POSIX this keeps temporary content owner-only regardless of the parent directory's permissions.
|
||||
|
||||
Windows has no working equivalent behind the same API. Node's `chmod` there drives only the read-only attribute (every mode this package passes carries owner-write, so the calls are benign no-ops), and `stat().mode` reports synthetic `0o666`/`0o444` bits. The real security state is the file's DACL: a newly created file or directory inherits from its parent, while replacement needs the explicit handling owned by the superseding Agent Note.
|
||||
|
||||
## Decision
|
||||
|
||||
New Windows files use directory inheritance rather than synthetic mode bits: the staging directory is created inside the target's parent directory (`dirname(absolutePath)`), so it and the temp file inherit the destination directory's DACL. Replacement files follow the stricter [DACL preservation contract](../bug-fix/2026-07-19-windows-atomic-write-dacl-preservation.md).
|
||||
|
||||
Tests assert mode bits on POSIX only. Native Windows coverage pins the package-owned replacement behavior; new-file inheritance remains an operating-system contract rather than a machine-specific ACL allowlist.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Explicit owner-only DACLs for new files.** Rejected because they would break inheritance and surprise users whose project directories are deliberately shared. Replacement writes copy the target's existing DACL rather than inventing an owner-only policy.
|
||||
|
||||
**Test-side ACL verification.** A `Get-Acl` SID allowlist or `icacls` would verify Windows inheritance and the machine's `%TEMP%` ACL rather than package behavior; `icacls` also localizes well-known account names, making parsing locale-fragile.
|
||||
|
||||
**Skip `chmod` on Windows.** Platform-guarding benign no-op calls adds branches without changing behavior.
|
||||
|
||||
## Consequences
|
||||
|
||||
POSIX keeps owner-only temp content regardless of the parent directory. A new Windows target inside a broadly accessible directory inherits that accessibility by design; a replacement retains the target's narrower DACL when one exists.
|
||||
|
||||
Mode preservation across a replace degenerates to a no-op on Windows: a writable file probes as `0o666`, and replaying that through `chmod` leaves the read-only attribute clear. A read-only target cannot be replaced there because publication fails before the synthetic mode would matter.
|
||||
+2
-2
@@ -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
|
||||
2026-07-20-unified-json-value-schema-dsl.md: 09945c413ffe5924c74076648cdf3da60c3e18c9
|
||||
2026-07-20-unified-json-value-schema-dsl.zh.md: 00a7a199613ea857a7815f1c7794781f143a3896
|
||||
2026-07-20-unified-json-value-schema-dsl.md: 5de3523eab15a91ea32dc09e2e239146fadea6f1
|
||||
2026-07-20-unified-json-value-schema-dsl.zh.md: 321136c31a6aa6c0268150fcde2d97dcdbb0ac58
|
||||
@@ -21,6 +21,7 @@ Object-rooting is a consumer rule rather than a vocabulary restriction. Subagent
|
||||
## Alternatives considered
|
||||
|
||||
- **Keep separate parameter and structured-output schema systems:** rejected because every added output construct would require parallel inference, compilation, validation, and code-generation changes with no useful ownership boundary.
|
||||
- **Use Schemastery for tool parameters:** rejected because Schemastery targets validation and transformation through Standard Schema rather than JSON Schema generation. It would add an adapter layer without producing the model-facing wire schema or the shared output vocabulary.
|
||||
- **Adopt full JSON Schema or Ajv:** rejected because the harness must fail on every construct it cannot project into its generated SDK and validators; accepting a larger language would make enforcement and model guidance dishonest.
|
||||
- **Make every object implicitly open or closed:** rejected because either choice hides a consequential author decision. Only the legacy-shaped implicit parameter root and external raw schema retain an intentional default.
|
||||
- **Define `oneOf` as first-match:** rejected because branch ordering would change validation semantics and allow overlapping branches to hide ambiguous values.
|
||||
@@ -32,4 +33,5 @@ Object-rooting is a consumer rule rather than a vocabulary restriction. Subagent
|
||||
- Explicit object openness and type-correct literal constraints make malformed declarations fail during authoring or registration rather than during a later model call.
|
||||
- Bounded type inference retains useful exact types for ordinary declarations and degrades unusually deep tails to `JsonValue`; runtime schema enforcement remains exact at every depth.
|
||||
- Raw tools may still register broader JSON Schema directly, but unified code generation treats unsupported schemas as unknown instead of pretending to enforce them.
|
||||
- Per-property `required: true` remains the tool-author contract, and type-level regression coverage pins required keys as non-optional after the original inference path exposed an optionality bug.
|
||||
- Runtime and compile-time tests cover every root, exact-one overlap/no-match behavior, raw open defaults, explicit openness, lossy JSON values, inference, deep nesting across core and dynamic projections, JSON-invisible dynamic keys, and exotic schema arrays.
|
||||
@@ -21,6 +21,7 @@ Status: implemented
|
||||
## 备选方案
|
||||
|
||||
- **保留两套独立的参数与结构化输出 schema 系统:**不予采纳。每新增一种输出结构,都必须分别修改类型推导、编译、校验和代码生成,而这种重复并未形成有意义的职责边界。
|
||||
- **使用 Schemastery 处理工具参数:**不予采纳。Schemastery 通过 Standard Schema 面向校验与转换,而不是生成 JSON Schema。采用它会增加一层适配器,却不能产出面向模型的协议 schema 或共享的输出词汇。
|
||||
- **采用完整 JSON Schema 或 Ajv:**不予采纳。harness 必须拒绝所有无法投影到生成 SDK 和校验器中的结构;如果接受更大的语言子集,强制执行能力和模型指引就会与事实不符。
|
||||
- **让所有对象默认开放或默认封闭:**不予采纳。这两种选择都会隐藏一项影响重大的作者决策。只有保持旧有形态的隐式参数根对象和外部原始 schema 才有意保留默认值。
|
||||
- **把 `oneOf` 定义为首个匹配分支:**不予采纳。这样一来,分支顺序会改变校验语义,重叠分支也会掩盖值的歧义。
|
||||
@@ -32,4 +33,5 @@ Status: implemented
|
||||
- 显式的对象开放方式和类型正确的字面量约束会让格式错误的声明在编写或注册阶段快速失败,而不是拖到后续模型调用时才失败。
|
||||
- 有界类型推导会为常规声明保留有用的精确类型,并将异常深的尾部结构退化为 `JsonValue`;运行时 schema 强制执行在任意深度仍保持精确。
|
||||
- 原始工具仍可直接注册范围更广的 JSON Schema,但统一代码生成会把不受支持的 schema 视为未知类型,不会假装自己能够强制执行。
|
||||
- 每个属性的 `required: true` 仍是工具作者契约;原有推导路径暴露可选性缺陷后,类型级回归覆盖会锁定必填键不得为可选。
|
||||
- 运行时和编译期测试覆盖所有根类型、恰好匹配一个分支时的重叠/无匹配行为、原始 schema 的默认开放语义、显式开放方式、有损 JSON 值、类型推导、核心投影和动态投影中的深层嵌套、动态注册中 JSON 不可见的键,以及非普通 schema 数组。
|
||||
Reference in New Issue
Block a user