mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
Merge remote-tracking branch 'origin/master' into feat/code-runtime-python-protocol
Resolutions: - docs/module-graph.md, docs/config-catalog.md: both are generated files. Regenerated with gen-module-graph and gen-config-catalog on the merged tree instead of hand-merging the conflict hunks. - scripts/verify-package-readme-model-experience.ts: both sides appended registry entries; kept all four. Adapted this package to conventions master introduced while the branch was open: version 0.0.1-rc.1 with publishConfig and repository metadata, the workspace: protocol for peer dependencies, and the @deepseek-ai/cordis rescope in package.json and src/invariant.ts.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# AGENTS.md — Agent Notes
|
||||
|
||||
Agent Notes are effectively RFCs written by agents: durable proposals and decision records that preserve rationale, alternatives, consequences, and verification contracts. Follow the [documentation standard](../../docs/AGENTS.md) and the [Agent Note contract](README.md).
|
||||
Agent Notes are effectively RFCs written by agents: durable proposals and decision records that preserve rationale, alternatives, consequences, and required verification. Follow the [documentation standard](../../docs/AGENTS.md) and the [Agent Note rules](README.md).
|
||||
|
||||
**Every new Agent Note triggers a supersession check.** Search the active tree for older notes covering the same decision or mechanism, classify any full or partial supersession with [`dsh-archive-agent-notes`](../skills/dsh-archive-agent-notes/SKILL.md), and archive every qualifying implemented triplet in the same PR. Keep partial supersessions active and cross-linked.
|
||||
|
||||
|
||||
@@ -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/README.md
|
||||
README.md: 3cfbb5154713046846a3bfcb2ccea62c0e4cb6c0
|
||||
README.zh.md: 61887320e9e54b35154c79e33e9a9525aceeef50
|
||||
README.md: ae8e4724d610c97d74910d7dec5c95af69e93281
|
||||
README.zh.md: d9989dd1d722185145099b706169dfce19559b52
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
One kind of design doc lives here. An **Agent Note** records a decision or proposal that shapes this codebase — the *why* and *what we gave up*, the parts code and docs can't carry. This file is the front door and contract: where Agent Notes live, when to write one, and [the in-file format](#the-file-format).
|
||||
One kind of design doc lives here. An **Agent Note** records a decision or proposal that affects this codebase — the *why* and *what we gave up*, the parts code and docs can't carry. This file defines where Agent Notes live, when to write one, and [the in-file format](#the-file-format).
|
||||
|
||||
## Layout and naming
|
||||
|
||||
@@ -43,13 +43,13 @@ Once sealed, every archived triplet is permanently frozen. Do not edit, translat
|
||||
|
||||
## When to write one
|
||||
|
||||
Every non-trivial change MUST add or update at least one Agent Note in the same PR. A change is non-trivial when it alters behavior, architecture, a cross-file or cross-package contract, process or tooling, testing strategy, an on-disk, wire, or configuration format, or another decision a maintainer may reasonably revisit. A proposal for substantial future work starts in `proposed/`; a decision already made starts in `implemented/`. Pick the class folder that matches the decision (see [Classification](#classification)).
|
||||
Every non-trivial change MUST add or update at least one Agent Note in the same PR. A change is non-trivial when it alters behavior, architecture, a contract shared across files or packages, process or tooling, testing strategy, an on-disk, wire, or configuration format, or another decision a maintainer may reasonably revisit. A proposal for substantial future work starts in `proposed/`; a decision already made starts in `implemented/`. Pick the class folder that matches the decision (see [Classification](#classification)).
|
||||
|
||||
Updating the Agent Note that already owns the decision satisfies the rule; do not create a duplicate. Only a purely mechanical or local edit with no behavioral, contractual, structural, process, or rationale change is exempt. An Agent Note is never edited into a *different decision*: supersede it with a new one, and keep both notes cross-linked unless the old note is later fully consolidated under the rule below. Editing an `implemented/` Agent Note to track where its existing decision lives is required, not forbidden; see [implemented/AGENTS.md](implemented/AGENTS.md).
|
||||
Updating the Agent Note that already owns the decision satisfies the rule; do not create a duplicate. Only a purely mechanical or local edit with no change to behavior, contracts, structure, process, or rationale is exempt. An Agent Note is never edited into a *different decision*: supersede it with a new one, and keep both notes cross-linked unless the old note is later fully consolidated under the rule below. Editing an `implemented/` Agent Note to track where its existing decision lives is required, not forbidden; see [implemented/AGENTS.md](implemented/AGENTS.md).
|
||||
|
||||
An implemented Agent Note that is fully superseded may be consolidated into the current owning note and deleted. Before deletion, the owner must preserve every unique rationale, alternative, consequence, verification contract, and named coverage gap; repair every inbound link; and delete the Chinese counterpart and consistency record in the same change. Partial supersession does not qualify: keep both notes cross-linked and update every fact that remains current. Consolidation must not rewrite the old file into its opposite or rely on git history as the only copy of rationale.
|
||||
An implemented Agent Note that is fully superseded may be consolidated into the current owning note and deleted. Before deletion, the owner must preserve every unique rationale, alternative, consequence, required verification, and named coverage gap; repair every inbound link; and delete the Chinese counterpart and consistency record in the same change. Partial supersession does not qualify: keep both notes cross-linked and update every fact that remains current. Consolidation must not rewrite the old file into its opposite or rely on git history as the only copy of rationale.
|
||||
|
||||
A feature-addition note may be consolidated into the later removal note only when the feature is absent from production code, configuration, schemas, durable or wire formats, migration, and compatibility behavior; no current documentation presents it as available; and no test exercises it as supported behavior. Removal rationale and tests that verify absence may remain. The removal owner preserves the original motivation, why it no longer justified the feature, alternatives to full removal, the capability given up, conditions for reintroduction, and verification of complete absence. Obsolete implementation inventories and tests that only verified the deleted behavior are not current verification contracts. Removing one transport, default, implementation, or presentation is partial supersession, as is any surviving durable data or compatibility handling.
|
||||
A feature-addition note may be consolidated into the later removal note only when the feature is absent from production code, configuration, schemas, durable or wire formats, migration, and compatibility behavior; no current documentation presents it as available; and no test exercises it as supported behavior. Removal rationale and tests that verify absence may remain. The removal owner preserves the original motivation, why it no longer justified the feature, alternatives to full removal, the capability given up, conditions for reintroduction, and verification of complete absence. Obsolete implementation inventories and tests that only verified the deleted behavior are not current verification evidence. Removing one transport, default, implementation, or presentation is partial supersession, as is any surviving durable data or compatibility handling.
|
||||
|
||||
## The file format
|
||||
|
||||
@@ -122,4 +122,4 @@ Moving a file between lifecycle folders means updating the `Status:` line and re
|
||||
|
||||
### Chinese counterparts
|
||||
|
||||
A `.zh.md` counterpart mirrors its English sibling's structure section-for-section under the [i18n contract](../../docs/i18n/README.md); the machine-checked header tokens (`# Agent Note: ` and the `Status:` line) stay in English verbatim. The format gate skips `.zh.md` files — the pairing gate owns their consistency.
|
||||
A `.zh.md` counterpart mirrors its English sibling's structure section-for-section under the [i18n contract](../../docs/i18n/README.md); the machine-checked header tokens (`# Agent Note: ` and the `Status:` line) stay in English verbatim. The format gate skips `.zh.md` files — the pairing gate checks their consistency.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
这里存放一类设计文档。**Agent Note** 记录塑造本代码库的决策或提案:代码和文档无法承载的*为什么*以及*放弃了什么*。本文件是入口和契约:Agent Note 存放在哪里、何时需要写一份,以及[文件内格式](#the-file-format)。
|
||||
这里存放一类设计文档。**Agent Note** 记录影响本代码库的决策或提案:代码和文档无法承载的*为什么*以及*放弃了什么*。本文件规定 Agent Note 存放在哪里、何时需要写一份,以及[文件内格式](#the-file-format)。
|
||||
|
||||
## 布局与命名
|
||||
|
||||
@@ -45,13 +45,13 @@
|
||||
|
||||
## 何时需要写一份
|
||||
|
||||
每个非平凡变更都必须在同一 PR(Pull Request)中新增或更新至少一份 Agent Note。如果变更修改了行为、架构、跨文件或跨包契约、流程或工具、测试策略、磁盘存储格式、协议格式(wire format)或配置格式,或者其他维护者可能合理重新审视的决策,就属于非平凡变更。对未来重大工作的提案从 `proposed/` 开始;已经做出的决策从 `implemented/` 开始。选择与决策匹配的类别文件夹(见[分类](#classification))。
|
||||
每个非平凡变更都必须在同一 PR(Pull Request)中新增或更新至少一份 Agent Note。如果变更修改了行为、架构、跨文件或跨包约定、流程或工具、测试策略、磁盘存储格式、协议格式(wire format)或配置格式,或者其他维护者可能合理重新审视的决策,就属于非平凡变更。对未来重大工作的提案从 `proposed/` 开始;已经做出的决策从 `implemented/` 开始。选择与决策匹配的类别文件夹(见[分类](#classification))。
|
||||
|
||||
更新已经拥有该决策的 Agent Note 即可满足规则;不要创建重复记录。只有不涉及行为、契约、结构、流程或理由变化的纯机械性或局部编辑才可豁免。Agent Note 永远不会被编辑为一个*不同的决策*:用新 Agent Note 取代旧记录,并让两个记录保持互相链接,除非后续依据下方规则完全合并旧记录。编辑 `implemented/` Agent Note 以跟踪其现有决策的所在位置是必需的,而非禁止的;见 [implemented/AGENTS.md](implemented/AGENTS.md)。
|
||||
更新已经拥有该决策的 Agent Note 即可满足规则;不要创建重复记录。只有不涉及行为、约定、结构、流程或理由变化的纯机械性或局部编辑才可豁免。Agent Note 永远不会被编辑为一个*不同的决策*:用新 Agent Note 取代旧记录,并让两个记录保持互相链接,除非后续依据下方规则完全合并旧记录。编辑 `implemented/` Agent Note 以跟踪其现有决策的所在位置是必需的,而非禁止的;见 [implemented/AGENTS.md](implemented/AGENTS.md)。
|
||||
|
||||
被完全取代的 implemented Agent Note 可以合并到当前持有该决策的记录中,并删除原文件。删除前,当前记录必须保存所有独有的决策依据、备选方案、影响、验证契约和明确指出的覆盖缺口;修复所有入站链接;并在同一变更中删除中文对侧文件和一致性记录。仅部分被取代的记录不符合此条件:保留两个记录并让它们互相链接,同时更新所有仍然适用的事实。合并不得将旧文件改写成与其相反的决策,也不得让 git 历史成为决策依据的唯一副本。
|
||||
被完全取代的 implemented Agent Note 可以合并到当前持有该决策的记录中,并删除原文件。删除前,当前记录必须保存所有独有的决策依据、备选方案、影响、必需的验证和明确指出的覆盖缺口;修复所有入站链接;并在同一变更中删除中文对侧文件和一致性记录。仅部分被取代的记录不符合此条件:保留两个记录并让它们互相链接,同时更新所有仍然适用的事实。合并不得将旧文件改写成与其相反的决策,也不得让 git 历史成为决策依据的唯一副本。
|
||||
|
||||
只有当一项功能已从生产代码、配置、schema、持久化格式或协议格式、迁移和兼容行为中完全消失,当前文档不再将其描述为可用,且没有测试把它作为受支持行为来执行时,新增该功能的 Agent Note 才可合并进后续的移除记录。移除决策的依据和验证该功能已不存在的测试可以保留。移除决策的持有记录必须保留最初动机、为什么该动机已不足以证明保留该功能的合理性、完全移除之外的备选方案、放弃的能力、重新引入的条件,以及证明已彻底移除的验证。过时的实现清单和只验证已删除行为的测试不属于当前验证契约。仅移除一种传输、默认值、实现或展示属于部分取代;仍有任何持久数据或兼容处理也同样如此。
|
||||
只有当一项功能已从生产代码、配置、schema、持久化格式或协议格式、迁移和兼容行为中完全消失,当前文档不再将其描述为可用,且没有测试把它作为受支持行为来执行时,新增该功能的 Agent Note 才可合并进后续的移除记录。移除决策的依据和验证该功能已不存在的测试可以保留。移除决策的持有记录必须保留最初动机、为什么该动机已不足以证明保留该功能的合理性、完全移除之外的备选方案、放弃的能力、重新引入的条件,以及证明已彻底移除的验证。过时的实现清单和只验证已删除行为的测试不属于当前验证证据。仅移除一种传输、默认值、实现或展示属于部分取代;仍有任何持久数据或兼容处理也同样如此。
|
||||
|
||||
<a id="the-file-format"></a>
|
||||
|
||||
@@ -79,7 +79,7 @@ Status: <status>
|
||||
|
||||
### 正文骨架
|
||||
|
||||
每份 Agent Note 的正文以 `## Problem` 开头:动机,写法上不依赖解决方案即可独立成文。后续内容取决于生命周期;固定章节使用以下规范名称且仅限这些名称,而真正独特的技术章节(包拓扑、协议契约、schema 等)在必需章节之间可自由组织。
|
||||
每份 Agent Note 的正文以 `## Problem` 开头:动机,写法上不依赖解决方案即可独立成文。后续内容取决于生命周期;固定章节使用以下规范名称且仅限这些名称,而真正独特的技术章节(包拓扑、协议约定、schema 等)在必需章节之间可自由组织。
|
||||
|
||||
#### `proposed/`
|
||||
|
||||
@@ -126,4 +126,4 @@ Status: <status>
|
||||
|
||||
### 中文对侧文件
|
||||
|
||||
`.zh.md` 对侧文件按 [i18n 契约](../../docs/i18n/README.md)逐章节镜像其英文对侧文件的结构;机器检查的头部标记(`# Agent Note: ` 和 `Status:` 行)保持英文原样不翻译。格式门禁跳过 `.zh.md` 文件——配对门禁负责它们的一致性。
|
||||
`.zh.md` 对侧文件按 [i18n 约定](../../docs/i18n/README.md)逐章节与其英文对侧文件保持相同结构;机器检查的头部标记(`# Agent Note: ` 和 `Status:` 行)保持英文原样不翻译。格式门禁跳过 `.zh.md` 文件;配对门禁检查它们的一致性。
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/architecture/2026-07-28-dsh-native-typescript-source-launch.md
|
||||
2026-07-28-dsh-native-typescript-source-launch.md: b5e8ed2a18bb0cbb3ab54cf5ed4a427efaa9dfeb
|
||||
2026-07-28-dsh-native-typescript-source-launch.zh.md: 05a4f92d2d3be553bb9ca363bf08e3ebf8e3f5b8
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Native TypeScript source launch for dsh
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-28-dsh-native-typescript-source-launch.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: dsh 原生 TypeScript 源码启动
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-28-dsh-native-typescript-source-launch.md) | 中文
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/bug-fix/2026-07-27-question-composer-rows-do-not-shrink.md
|
||||
2026-07-27-question-composer-rows-do-not-shrink.md: 47e581a23caaeb9368b075fa84a01d2bc945ab46
|
||||
2026-07-27-question-composer-rows-do-not-shrink.zh.md: a1b72cab41c51c268540dcec60e85a0edf2fe9bc
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Question-composer option rows are scroll content, not the slack absorber
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-27-question-composer-rows-do-not-shrink.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 提问 composer 的选项行是滚动内容,而非空间不足时的吸收方
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-27-question-composer-rows-do-not-shrink.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/bug-fix/2026-07-28-web-conversation-polish-sweep.md
|
||||
2026-07-28-web-conversation-polish-sweep.md: 338b687c041c585c1d490fdad9b8bbcf88fc3912
|
||||
2026-07-28-web-conversation-polish-sweep.zh.md: 166662070b67837b9f4755e6457578f74af771ed
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web conversation UI polish sweep
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-28-web-conversation-polish-sweep.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web 对话 UI 视觉优化
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-28-web-conversation-polish-sweep.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/bug-fix/2026-07-30-web-details-default-closed.md
|
||||
2026-07-30-web-details-default-closed.md: e4271917b998c9a916d8c67b30627022587547b8
|
||||
2026-07-30-web-details-default-closed.zh.md: 3b9432067c944ea604e039ee1b305a927a1c206e
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web details default closed
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-30-web-details-default-closed.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web 详情栏默认关闭
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-30-web-details-default-closed.md) | 中文
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/bug-fix/2026-07-31-hero-visible-while-blank-session-opens.md
|
||||
2026-07-31-hero-visible-while-blank-session-opens.md: 1b1f35d82731675978585d718e4ef837f0c78aa5
|
||||
2026-07-31-hero-visible-while-blank-session-opens.zh.md: 91c1a372ebc6341632820450d9e54d58c9d36916
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Hero stays visible while a blank session opens
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-31-hero-visible-while-blank-session-opens.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 空白会话打开期间保持 hero 可见
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-31-hero-visible-while-blank-session-opens.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/bug-fix/2026-08-10-web-favicon-dark-mode.md
|
||||
2026-08-10-web-favicon-dark-mode.md: 22e1d063a72b177e0e5c11f4bcfcbc233a81fdfc
|
||||
2026-08-10-web-favicon-dark-mode.zh.md: dff90567337c6968220091c16c6cd55cf7dad2d1
|
||||
@@ -0,0 +1,26 @@
|
||||
# Agent Note: Web favicon follows the color scheme
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
English | [中文](2026-08-10-web-favicon-dark-mode.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`apps/web/public/favicon.svg` paints the DeepSeek mark solid black (`fill="#000"`), and `index.html` declares only that single SVG icon. Under an OS or browser dark color scheme the tab strip is dark too, so the black mark is effectively invisible. Safari versions before 26 do not render SVG favicons, so their users get no tab icon in any scheme.
|
||||
|
||||
## Decision
|
||||
|
||||
The favicon stays one file and adapts through the browser's own color-scheme signal: `favicon.svg` embeds `@media (prefers-color-scheme: dark) { path { fill: #fff } }`, switching the mark to white under a dark scheme while the light scheme keeps black. `index.html` and `manifest.webmanifest` also declare a 32×32 PNG fallback (`favicon-32x32.png`, DeepSeek brand blue `#4D6BFE`) that Safari versions before 26 render and that stays visible on both light and dark tab strips, extending the [web-install-manifest decision](../feature/2026-08-06-web-install-manifest.md).
|
||||
|
||||
The theme signal is the OS/browser scheme, not the GUI's in-app `dsh.theme` toggle: the favicon lives in browser chrome, whose background follows the browser scheme, so `prefers-color-scheme` is the correct semantic and needs no JavaScript. Known browser quirks — Chromium may not repaint the tab icon until reload after a scheme switch, and Safari versions before 26 ignore the SVG variant — are accepted and the PNG fallback covers the older-Safari case.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **A second `<link rel="icon" media="(prefers-color-scheme: dark)">` pointing at a separate dark SVG.** Rejected: the same scheme semantics with two files to keep in sync, and no benefit over the in-file media query.
|
||||
- **A theme-presenter that swaps the icon href on `theme/change`.** Rejected: it would follow the in-app toggle rather than the browser scheme that actually colors the tab strip, and it adds client code and a presenter for a chrome asset.
|
||||
- **No PNG fallback.** Rejected: Safari versions before 26 never render SVG favicons, so the fallback is the only way those versions get a tab icon at all.
|
||||
|
||||
## Consequences
|
||||
|
||||
Light scheme still shows the black mark, dark scheme shows white, and Safari versions before 26 show the blue PNG in both. `apps/web/tests/pwa-manifest.e2e.ts` pins the PNG link and its order before the SVG, both manifest icons, the shipped PNG's format and dimensions, and the dark media query inside the shipped SVG. The Chromium repaint quirk remains a browser behavior the app cannot fix.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Agent Note: 网页图标随配色方案切换
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
[English](2026-08-10-web-favicon-dark-mode.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`apps/web/public/favicon.svg` 把 DeepSeek 图标绘制为纯黑色(`fill="#000"`),而 `index.html` 只声明了这一个 SVG 图标。当操作系统或浏览器处于暗色配色方案时,标签栏同样是深色,黑色图标实际上不可见。Safari 26 之前的版本不渲染 SVG favicon,因此这些版本的 Safari 用户无论何种配色方案都看不到标签页图标。
|
||||
|
||||
## 决策
|
||||
|
||||
favicon 保持单一文件,并通过浏览器自身的配色方案信号自适应:`favicon.svg` 内嵌 `@media (prefers-color-scheme: dark) { path { fill: #fff } }`,在暗色方案下把图标切换为白色,浅色方案保持黑色。`index.html` 与 `manifest.webmanifest` 同时声明 32×32 PNG 兜底(`favicon-32x32.png`,DeepSeek 品牌蓝 `#4D6BFE`),Safari 26 之前的版本会渲染该 PNG,且它在浅色与深色标签栏上都清晰可见;这是对 [Web 安装 manifest 决策](../feature/2026-08-06-web-install-manifest.md) 的扩展。
|
||||
|
||||
主题信号取操作系统/浏览器方案,而不是 GUI 应用内 `dsh.theme` 开关:favicon 位于浏览器 chrome 中,其背景跟随浏览器方案,因此 `prefers-color-scheme` 是正确语义,无需任何 JavaScript。已知的浏览器怪癖——Chromium 在切换方案后可能要到刷新页面才重绘标签图标,Safari 26 之前的版本忽略 SVG 变体——均被接受,旧版 Safari 场景由 PNG 兜底覆盖。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **新增指向独立暗色 SVG 的第二个 `<link rel="icon" media="(prefers-color-scheme: dark)">`。** 不予采纳:语义相同却要多维护一个文件,相比文件内媒体查询没有任何收益。
|
||||
- **由主题 presenter 在 `theme/change` 时替换图标 href。** 不予采纳:它会跟随应用内开关,而不是真正决定标签栏颜色的浏览器方案,并且为一个 chrome 资源引入客户端代码和 presenter。
|
||||
- **不提供 PNG 兜底。** 不予采纳:Safari 26 之前的版本从不渲染 SVG favicon,兜底是这些版本获得标签图标的唯一途径。
|
||||
|
||||
## 后果
|
||||
|
||||
浅色方案仍显示黑色图标,暗色方案显示白色,Safari 26 之前的版本两种方案都显示蓝色 PNG。`apps/web/tests/pwa-manifest.e2e.ts` 固定断言 PNG 链接及其位于 SVG 之前的顺序、manifest 中的两个图标、交付 PNG 的格式与尺寸,以及交付 SVG 内部的暗色媒体查询。Chromium 的重绘怪癖仍是浏览器行为,应用无法修复。
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-07-22-docked-web-goal-bar.md
|
||||
2026-07-22-docked-web-goal-bar.md: decf40996b51a0f2358a943bbb928f00d4db2026
|
||||
2026-07-22-docked-web-goal-bar.zh.md: 8ebb106b260a6107084c07b51b1763adb1df2da8
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Docked web goal bar
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-22-docked-web-goal-bar.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 停靠式 Web 目标条
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-22-docked-web-goal-bar.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-07-29-web-message-icon-actions-and-clock.md
|
||||
2026-07-29-web-message-icon-actions-and-clock.md: a95c7d33a917026c882f17d30264cf9ec743dee5
|
||||
2026-07-29-web-message-icon-actions-and-clock.zh.md: b64c14aaa7056e19ccc4d512db3d24714e11a309
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web message IconActions and clocks
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-29-web-message-icon-actions-and-clock.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web 消息 IconActions 与时钟
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-29-web-message-icon-actions-and-clock.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-07-30-dsh-dump-config.md
|
||||
2026-07-30-dsh-dump-config.md: cc16f11d79b536a661d67811c6fd50705f6009e3
|
||||
2026-07-30-dsh-dump-config.zh.md: 185a2b8f37cef102b48d4dea1b3ed0a96958d220
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: dsh --dump-config prints the composed config tree
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-30-dsh-dump-config.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: dsh --dump-config 打印合成后的配置树
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-30-dsh-dump-config.md) | 中文
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-07-30-web-composer-stats-and-input-polish.md
|
||||
2026-07-30-web-composer-stats-and-input-polish.md: 3c80b74c564a1779c69ef525f8d1f194e8915f6b
|
||||
2026-07-30-web-composer-stats-and-input-polish.zh.md: eabb174e78f3171997b9103650c7fe326793ce1b
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web composer stats detail and input-zone polish
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-30-web-composer-stats-and-input-polish.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web composer stats detail and input-zone polish
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-30-web-composer-stats-and-input-polish.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-07-30-web-context-injection-disclosure.md
|
||||
2026-07-30-web-context-injection-disclosure.md: e9551cacdcd5b3e45ba35eeb76db6c70e5bbe368
|
||||
2026-07-30-web-context-injection-disclosure.zh.md: a4937ba880df30b911f7c8a1eceaab7e2bb74026
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web context injection disclosure
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-30-web-context-injection-disclosure.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web 上下文注入展开项
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-30-web-context-injection-disclosure.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-07-31-hover-card-click-copy.md
|
||||
2026-07-31-hover-card-click-copy.md: 906f64129ee9ba767859260a3288faada21ea4de
|
||||
2026-07-31-hover-card-click-copy.zh.md: 0359b2edacde9e048db900d315b6a7befd361075
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Hover cards copy their primary value on activation
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-31-hover-card-click-copy.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 悬浮卡片激活时复制主要值
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-31-hover-card-click-copy.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-07-31-web-cards-toolrow.md
|
||||
2026-07-31-web-cards-toolrow.md: 9bdf5d4e8917178ec27ea5f5d24af753c0d10eab
|
||||
2026-07-31-web-cards-toolrow.zh.md: ce473a8a34ba8b1bb022d681244508c56a1084f3
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Card tool rows collapse through one ToolRow
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-31-web-cards-toolrow.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 卡片工具行通过同一个 ToolRow 折叠
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-31-web-cards-toolrow.md) | 中文
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/feature/2026-08-08-dsh-run-headless-command.md
|
||||
2026-08-08-dsh-run-headless-command.md: ce9cff965192357022c49655983fe6ff8d554b9f
|
||||
2026-08-08-dsh-run-headless-command.zh.md: 0484c069ed6365235e4616542a4fb3d5ceb2d880
|
||||
@@ -0,0 +1,44 @@
|
||||
# Agent Note: `dsh run` owns one-shot headless execution
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
English | [中文](2026-08-08-dsh-run-headless-command.zh.md)
|
||||
|
||||
> **Superseded command grammar.** [Apps now own their command lines](../architecture/2026-08-06-app-owned-command-line.md): the headless startup row parses the task from `dsh --profile headless <task...>`, and the launcher no longer has a `run` invocation or patches task text into rows. This note remains the rejected launcher-owned design context; the direct execution and completion contract it selected remains current in [headless is a direct core entry point](../architecture/2026-08-09-headless-direct-core-entry-point.md).
|
||||
|
||||
## Problem
|
||||
|
||||
Generic profile boot and one-shot task execution have different lifecycle contracts. A root grammar that accepts optional task text makes one argv shape mean either a long-lived process or a terminating task according to a plugin row discovered only after composition. It also exposes a profile implementation detail as the primary user command and gives custom profiles no explicit one-shot entry.
|
||||
|
||||
The `run` verb must have one top-level meaning. Sharing it with application-file execution or inferring its meaning from positional shape creates the same ambiguity.
|
||||
|
||||
## Decision
|
||||
|
||||
One-shot execution owns this grammar:
|
||||
|
||||
```text
|
||||
dsh run [--profile <name>] [--patch <path>...] <task...>
|
||||
```
|
||||
|
||||
`--profile` defaults to `headless` and supports custom one-shot compositions. `--patch` is repeatable and occupies the normal overlay layer. Commander joins the variadic task arguments with spaces and rejects a missing or blank task before boot.
|
||||
|
||||
`RunInvocation` is a distinct `DshInvocation` member. The generic profile invocation carries no task state and accepts no positional arguments. Both dispatch paths use `runProfile`: profile boot omits `task`, while `run` supplies it. A one-shot profile without `headless-runner` fails through the composed-row check, and profile boot containing that row without a task points to `dsh run --profile <name> "<task>"`.
|
||||
|
||||
The [profile plugin bundle decision](../architecture/2026-08-05-profile-plugin-bundles.md) owns composition. [Headless is a direct core entry point](../architecture/2026-08-09-headless-direct-core-entry-point.md) owns the execution contract: one fresh persisted Session, final assistant text on stdout, completed/non-completed exit mapping, empty stderr on success, no listening port, and bounded signal shutdown after Agent quiescence and Session flush.
|
||||
|
||||
The `run` verb belongs only to one-shot task execution. Application-file launch requires a distinct command name.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
| Alternative | Contract mismatch |
|
||||
|---|---|
|
||||
| Put task text on root profile boot | Lifecycle meaning depends on a plugin row discovered after parsing. |
|
||||
| Accept root aliases such as `dsh -p` | The pre-release grammar acquires compatibility branches with no current command ownership. |
|
||||
| Require `--profile headless` | The shipped one-shot surface loses its shortest canonical spelling. |
|
||||
| Use `dsh run` for application files | One top-level verb has two meanings and the primary task command becomes indirect. |
|
||||
| Add a shallow `apps/cli/src/run.ts` forwarder | Command ownership splits without hiding any complexity. |
|
||||
|
||||
## Consequences
|
||||
|
||||
Help, documentation, parser tests, built-bin acceptance, PTY shutdown coverage, and the assembled keyless snapshot use `dsh run`. Custom one-shot profiles use `--profile`; long-lived profile boot and config dumps keep the root profile grammar. Application-file execution is a separate command concern.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Agent Note: `dsh run` 负责一次性 headless 执行
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
[English](2026-08-08-dsh-run-headless-command.md) | 中文
|
||||
|
||||
> **命令语法已被取代。** [应用现在持有自己的命令行](../architecture/2026-08-06-app-owned-command-line.md):headless 启动行从 `dsh --profile headless <task...>` 解析任务,启动器不再包含 `run` 调用,也不再把任务文本 patch 进配置行。本笔记保留被否决的启动器持有设计背景;它选定的直接执行与完成约定仍由 [headless 是直接 core 入口](../architecture/2026-08-09-headless-direct-core-entry-point.md)持有。
|
||||
|
||||
## 问题
|
||||
|
||||
通用 profile 启动与一次性任务执行具有不同的生命周期约定。若根语法接受可选任务文本,同一种 argv 形态会表示常驻进程或终止式任务,具体含义取决于组合完成后才发现的插件配置行。它还会把 profile 实现细节暴露成主要用户命令,并使自定义 profile 缺少明确的一次性入口。
|
||||
|
||||
`run` 动词必须只有一种顶层含义。与应用文件执行共用该动词,或根据位置参数形态推断含义,都会产生相同的歧义。
|
||||
|
||||
## 决策
|
||||
|
||||
一次性执行采用以下语法:
|
||||
|
||||
```text
|
||||
dsh run [--profile <name>] [--patch <path>...] <task...>
|
||||
```
|
||||
|
||||
`--profile` 默认为 `headless`,并支持自定义一次性组合。`--patch` 可重复使用,并占据正常的 overlay 层。Commander 用空格拼接可变数量的任务参数,并在启动前拒绝缺失或空白任务。
|
||||
|
||||
`RunInvocation` 是单独的 `DshInvocation` 成员。通用 profile 调用不携带任务状态,也不接受位置参数。两条分派路径都使用 `runProfile`:profile 启动省略 `task`,而 `run` 提供该字段。缺少 `headless-runner` 的一次性 profile 会触发组合行检查;如果启动的 profile 包含该行却未提供任务,错误会指向 `dsh run --profile <name> "<task>"`。
|
||||
|
||||
[profile 插件组合包决策](../architecture/2026-08-05-profile-plugin-bundles.md)负责组合。[Headless 是直接 core 入口](../architecture/2026-08-09-headless-direct-core-entry-point.md)负责执行约定:一个新的持久化会话、stdout 上的最终 assistant 文本、completed/非 completed 的退出状态映射、成功时为空的 stderr、无监听端口,以及 Agent 完全停稳且会话 flush 后的有界信号关闭。
|
||||
|
||||
`run` 动词只负责一次性任务执行。应用文件启动需要不同的命令名。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
| 替代方案 | 约定不匹配之处 |
|
||||
|---|---|
|
||||
| 把任务文本放在根 profile 启动命令上 | 生命周期含义依赖解析后才发现的插件配置行。 |
|
||||
| 接受 `dsh -p` 等根命令别名 | 预发布语法获得不属于任何当前命令的兼容分支。 |
|
||||
| 要求指定 `--profile headless` | 随附的一次性 surface 失去最短的规范写法。 |
|
||||
| 将 `dsh run` 用于应用文件 | 一个顶层动词具有两种含义,主要任务命令也变得间接。 |
|
||||
| 添加仅转发的 `apps/cli/src/run.ts` | 命令归属被拆分,却没有隐藏任何复杂度。 |
|
||||
|
||||
## 后果
|
||||
|
||||
帮助信息、文档、解析器测试、构建后二进制验收、PTY 关闭覆盖和组装应用的无密钥快照均使用 `dsh run`。自定义一次性 profile 使用 `--profile`;常驻 profile 启动与配置 dump 使用根 profile 语法。应用文件执行是独立的命令关注点。
|
||||
@@ -40,6 +40,9 @@
|
||||
"architecture/2026-07-28-consolidated-tui-presentation.i18n.yaml": "sha256:1eb43c420a21b7a3adf0aa5274d9aa597187630a29d7e535c5e266f82e803665",
|
||||
"architecture/2026-07-28-consolidated-tui-presentation.md": "sha256:e6fa4ea0c9d1d94942ab98de47c554f4e8aa3b639a1cce52113107b1dbb0f4b0",
|
||||
"architecture/2026-07-28-consolidated-tui-presentation.zh.md": "sha256:01814434482a84ebd7f672eb5c26fc468b568e773452563bf39ba52ad25d054a",
|
||||
"architecture/2026-07-28-dsh-native-typescript-source-launch.i18n.yaml": "sha256:af071e07bce5d9bc8f3df65fed9dcd9b3779a98c5864badbd530363bda021b55",
|
||||
"architecture/2026-07-28-dsh-native-typescript-source-launch.md": "sha256:1b56e3454277ace713e2a01c4da538c756c45bf633fd24d7b16443d584afac5d",
|
||||
"architecture/2026-07-28-dsh-native-typescript-source-launch.zh.md": "sha256:8c0f97472c2c89d2c19ae5cfa68c6e67f32b50960b08b60b46496f78ea6ffad1",
|
||||
"bug-fix/2026-07-20-code-mode-result-card-completeness.i18n.yaml": "sha256:1035dae11d049d32ab09fd7d4f950eceae44bf46ba498b3cfaf3c75102b9fb64",
|
||||
"bug-fix/2026-07-20-code-mode-result-card-completeness.md": "sha256:6ca2c9d4df98be18813ef38b7462db880900b5bcd6944fbcd1b8f2258006b93e",
|
||||
"bug-fix/2026-07-20-code-mode-result-card-completeness.zh.md": "sha256:ed85fa7f935e5f525d566bc37a92014614983e649c75de9a9f244939097a7991",
|
||||
@@ -61,6 +64,9 @@
|
||||
"bug-fix/2026-07-26-intent-draft-same-tick-echo.i18n.yaml": "sha256:c623947c4fa00e6d4b51792c7972ba09582bbcb7605beb373725c0dd666f2c81",
|
||||
"bug-fix/2026-07-26-intent-draft-same-tick-echo.md": "sha256:fa8b1417b2cdd3deecbf8e55bdddd73dd3a8c6e3486fd399b0b8bdf317e56373",
|
||||
"bug-fix/2026-07-26-intent-draft-same-tick-echo.zh.md": "sha256:00ce72552dbaa11562fbc541343a5d33f9449edabbe6dd354eb879a7d4d530f8",
|
||||
"bug-fix/2026-07-27-question-composer-rows-do-not-shrink.i18n.yaml": "sha256:9b8fd6c3fc5f6527890d74a70372de90ade6db5fa957246d4bbdc06ee06e072c",
|
||||
"bug-fix/2026-07-27-question-composer-rows-do-not-shrink.md": "sha256:0411033becc2835ce53cd268c9fa149830274c9f61016514087bea89562cfbb8",
|
||||
"bug-fix/2026-07-27-question-composer-rows-do-not-shrink.zh.md": "sha256:41f024f8b7a4587a92026b4d36d77879086bc86716b0b28bf2131b85db35ee75",
|
||||
"bug-fix/2026-07-27-tool-card-single-row-fields-inline.i18n.yaml": "sha256:4b94aded16c60628d22414dce524e8a98a8af4fff298805ee7efc63cae02c90d",
|
||||
"bug-fix/2026-07-27-tool-card-single-row-fields-inline.md": "sha256:40adcd522a9a2eeacc6f2b0196d1f24888a4f57830b7490a3be3d78c86c4e968",
|
||||
"bug-fix/2026-07-27-tool-card-single-row-fields-inline.zh.md": "sha256:a79d56c9b781442ee596b47707d1a8c80abcd6466094b01802189c8e55f16da7",
|
||||
@@ -70,15 +76,27 @@
|
||||
"bug-fix/2026-07-27-tui-step-timing-trails-tool-cards.i18n.yaml": "sha256:280b93ece72662501f65edd58a00cdafb5b5941e4ef1314d7198fab18950cb03",
|
||||
"bug-fix/2026-07-27-tui-step-timing-trails-tool-cards.md": "sha256:112bdbde16b6023eeb5b8a79cd2a711385e7198d51bbfc0520e9612acaa95c8a",
|
||||
"bug-fix/2026-07-27-tui-step-timing-trails-tool-cards.zh.md": "sha256:fc4e7f778ea63c4583cf81132c264cf6c4b9cc3e1818778061b0497ff16b8ef6",
|
||||
"bug-fix/2026-07-28-web-conversation-polish-sweep.i18n.yaml": "sha256:27e60a7d822b201dee65eebc8335da8edd416ee19b9b89c056302bae69c830fd",
|
||||
"bug-fix/2026-07-28-web-conversation-polish-sweep.md": "sha256:92647f06202f8711107918da9d9947767385a8b6307e772b1e868b0b1cb07412",
|
||||
"bug-fix/2026-07-28-web-conversation-polish-sweep.zh.md": "sha256:95ccc18f4b35396bdcabee8b8745e75008f6c07f829fd8719bbb35d77ac0fd95",
|
||||
"bug-fix/2026-07-30-tui-adapter-registration-race.i18n.yaml": "sha256:50b7a32e11591719c249258ecc2ec0f45e58f1a04050d2e53f6e2650f58ba137",
|
||||
"bug-fix/2026-07-30-tui-adapter-registration-race.md": "sha256:7e17eb1dd8f92e1efb7a18477df277b13580840b473ffe8a5309fc70ec3cfa3e",
|
||||
"bug-fix/2026-07-30-tui-adapter-registration-race.zh.md": "sha256:efcbd3d82af6a58677efe1a0580edd715945b6a93418fde47badac9c01a29866",
|
||||
"bug-fix/2026-07-30-web-details-default-closed.i18n.yaml": "sha256:2af5559d727f3e4afdd4946eaf89ac212c81db611db78dbd9bfabb1c4661db17",
|
||||
"bug-fix/2026-07-30-web-details-default-closed.md": "sha256:27a280a817c8048718bb22927e7d9572cf99ffd0c044631e99e0fd6ea236876f",
|
||||
"bug-fix/2026-07-30-web-details-default-closed.zh.md": "sha256:e047c7d02cf4b95b0c7f78f4b79af254091294b05cc75e98a8bb860ae2074189",
|
||||
"bug-fix/2026-07-31-hero-visible-while-blank-session-opens.i18n.yaml": "sha256:42218a762ce0141d3cb43deb6c688d3705cdc4405e03851d486c78f3d25b70ef",
|
||||
"bug-fix/2026-07-31-hero-visible-while-blank-session-opens.md": "sha256:a40992e89736131f5c487e5357848f14accd06e135dbec9ce242c968a5b11d43",
|
||||
"bug-fix/2026-07-31-hero-visible-while-blank-session-opens.zh.md": "sha256:e0cc576bc1c196affc9220ddabf15d735c347029c530c56454f0e585979101e1",
|
||||
"bug-fix/2026-07-31-tui-diff-context-line-accounting.i18n.yaml": "sha256:cd39ae2646fdc6827bf29a63953b5463faa37d5b404ae8cc3c0913c47bc92d0c",
|
||||
"bug-fix/2026-07-31-tui-diff-context-line-accounting.md": "sha256:57066bccd22c2dc2c3546b363de73d13b55ff8683ee12b17a81ed2bcf536645b",
|
||||
"bug-fix/2026-07-31-tui-diff-context-line-accounting.zh.md": "sha256:a658d886c5eb203f5f30a6fac70ad18e4a24cf756746254723d8f1d144653c04",
|
||||
"bug-fix/2026-08-03-tui-long-session-render-costs.i18n.yaml": "sha256:f65f7bf8fc84c7a1f022ee393c8d969c06d9bde8bed3a0206de86fb35b246ac6",
|
||||
"bug-fix/2026-08-03-tui-long-session-render-costs.md": "sha256:6ecf2ef831f527f361ade18a882d79bc6eccf15cc676d05728e7753f41cde051",
|
||||
"bug-fix/2026-08-03-tui-long-session-render-costs.zh.md": "sha256:5f44e707b332e13fa06d625212173ea055c1c3c0aee60888435a0ff099ec6037",
|
||||
"bug-fix/2026-08-10-web-favicon-dark-mode.i18n.yaml": "sha256:859c4399f9a017a68ba89552fdafa05e73c0599d94cee9551c84ea5b749a14f3",
|
||||
"bug-fix/2026-08-10-web-favicon-dark-mode.md": "sha256:4d17e247abd76ae3aed5fb4e075fd66a2838292f89f7021c82a79fe37ed905e6",
|
||||
"bug-fix/2026-08-10-web-favicon-dark-mode.zh.md": "sha256:7bbff8a3b7061c127afcc75cd2a8043b02a999b78c0180edd8f7e4807fcfe71d",
|
||||
"feature/2026-06-14-acp-agent-client-protocol.i18n.yaml": "sha256:006795baa43ae962a8d125cc0f1e9f134bc2ee9fb758b6e7669e3fa0126e1918",
|
||||
"feature/2026-06-14-acp-agent-client-protocol.md": "sha256:6828c0af74bb3fb96206ca6b21c0e56a000b50e4744aad4bc2c05092f3a5a31b",
|
||||
"feature/2026-06-14-acp-agent-client-protocol.zh.md": "sha256:ba104e841a1fb84edbd3b6c8119d50445b7785255a7a8d13bb9ac8a2cb4d2e69",
|
||||
@@ -157,6 +175,9 @@
|
||||
"feature/2026-07-21-tui-verbose-status-line.i18n.yaml": "sha256:4371b9a46d713d4180aa5d0b1ecde1ff3cae948380a8f56474c895e6113d7824",
|
||||
"feature/2026-07-21-tui-verbose-status-line.md": "sha256:9dcba19ee725b1593e9413a1da5398c205a258aff2e384acd406bb618e86c7f0",
|
||||
"feature/2026-07-21-tui-verbose-status-line.zh.md": "sha256:203c2abac99cedf7afa2540c925367ba66f00b61b926d1cc86472a603ad2bb07",
|
||||
"feature/2026-07-22-docked-web-goal-bar.i18n.yaml": "sha256:4fae22f5b921ae37feda632addace14bab8d1578c861228dd1df89a7b579f057",
|
||||
"feature/2026-07-22-docked-web-goal-bar.md": "sha256:94a4b00afc231eddd0fdcf6a494157d12a12b3ae40e8d732c3c05c6811b1c1f8",
|
||||
"feature/2026-07-22-docked-web-goal-bar.zh.md": "sha256:90dfa4f855a810bce6d57157049eacd6b0d9aa45e99fc296d9024adae4ebac7a",
|
||||
"feature/2026-07-23-trajectory-step-cell.i18n.yaml": "sha256:fe2e935a0affdef877902a40d9861ef5f55b30f40650469f6a52a4d45a92793f",
|
||||
"feature/2026-07-23-trajectory-step-cell.md": "sha256:185e3b87174cb6d2f2d2271fd2a74b1517d03e8570be602570d027bf6002d106",
|
||||
"feature/2026-07-23-trajectory-step-cell.zh.md": "sha256:51f46be43d2f5c4f78a05ed9aeec92d1f33ac988f45cf24d35528e9c43828ef3",
|
||||
@@ -205,24 +226,48 @@
|
||||
"feature/2026-07-29-tui-hidden-mode-assistant-fold.i18n.yaml": "sha256:0865835802348b730542adbe6b7db613750f3786993c6a14dbb2f47686c13c70",
|
||||
"feature/2026-07-29-tui-hidden-mode-assistant-fold.md": "sha256:a5fefebd802e2d9c3c79c7852c1c34c7bbef3f2ac2150d224608b9ec44e966ad",
|
||||
"feature/2026-07-29-tui-hidden-mode-assistant-fold.zh.md": "sha256:21bccd1e07ec8dc73b618f428461848bb90b6235afe0b842afb0afab2d5cc575",
|
||||
"feature/2026-07-29-web-message-icon-actions-and-clock.i18n.yaml": "sha256:59b09ed3c94f9b706b5ba8b567e264d9a6f4ff396245a8f4435ddfe5f4af3620",
|
||||
"feature/2026-07-29-web-message-icon-actions-and-clock.md": "sha256:c4f56f6681f7fa5fdef5354cdd1d0d00ae7e77f4ebcfae6015579b8a4e9ad712",
|
||||
"feature/2026-07-29-web-message-icon-actions-and-clock.zh.md": "sha256:6f8522382f644467ba39c7c0f15583de30a79ac17305cdba94c4195169cb0dc3",
|
||||
"feature/2026-07-30-compaction-progress-visibility.i18n.yaml": "sha256:4c2267054ad5d73aecc8d39d138a0cb532981b33175252e67252d07c3314b0f5",
|
||||
"feature/2026-07-30-compaction-progress-visibility.md": "sha256:2dfe07244cd784f27a9e5850801d40e96eae21a20ba10aaf56f9a793cdf5b505",
|
||||
"feature/2026-07-30-compaction-progress-visibility.zh.md": "sha256:6180b8aff0536147ab6ed6a78ecdbe1448fd12d89407746ecb1c7c05c73d4d60",
|
||||
"feature/2026-07-30-dsh-dump-config.i18n.yaml": "sha256:b400c8cce902328989e5493301f66451de7a36e966d69c43b20935fc635aca0f",
|
||||
"feature/2026-07-30-dsh-dump-config.md": "sha256:85b81dd517aaa6bb7510780acd961739c6da223bf2ddf6747d62f7a74e652d1f",
|
||||
"feature/2026-07-30-dsh-dump-config.zh.md": "sha256:d0d55947bcb0ef53d534844c15928ae26eebe3fb6d43f98ab176ec36a8eac640",
|
||||
"feature/2026-07-30-tui-details-command.i18n.yaml": "sha256:033cea6df0a16fc68cbdb435babdc6e75c1199a8e70e1a71d87c800c40f5a044",
|
||||
"feature/2026-07-30-tui-details-command.md": "sha256:a13478d4e55ec6d358209b51b541413ec75d0e20dfc22196ace28020f03f0c2d",
|
||||
"feature/2026-07-30-tui-details-command.zh.md": "sha256:de9c449b98468cef34ce4f9a9d2a854a5d8905eecd61f80e27a9a0e4495e9901",
|
||||
"feature/2026-07-30-versioned-tui-first-run-welcome.i18n.yaml": "sha256:4c3fc380b0512ad7c00baacd0ac610e1a78ae45374311d9bd43bab6b5e29e630",
|
||||
"feature/2026-07-30-versioned-tui-first-run-welcome.md": "sha256:296f153e6c839f3743078e4f5aab3b2befc211c934835238668c57bdeae52231",
|
||||
"feature/2026-07-30-versioned-tui-first-run-welcome.zh.md": "sha256:82871a9cca1fec46bb08a5b39daad28a44bb2419dea367b4ae41af3cf07bfa65",
|
||||
"feature/2026-07-30-web-composer-stats-and-input-polish.i18n.yaml": "sha256:eb0f02b1e15cb127618c82871fcea1b3ba4a9a5db670615072f15c71864729ea",
|
||||
"feature/2026-07-30-web-composer-stats-and-input-polish.md": "sha256:68d0c0486219d8e886db07c4cde2285e515adf9756c1e4bc94e4bbd5cbcc2c93",
|
||||
"feature/2026-07-30-web-composer-stats-and-input-polish.zh.md": "sha256:679565f7a2e183ac71132bd1cbd9d2418d77fe0e5472224b15bd310dd3931f4b",
|
||||
"feature/2026-07-30-web-context-injection-disclosure.i18n.yaml": "sha256:83a8b78b0140afc8f18034674b594b998099c162ed9b9d939cb99e49cea2272b",
|
||||
"feature/2026-07-30-web-context-injection-disclosure.md": "sha256:00f869b29861ff8f30e64ea3ca65aa9f8a06b33be205453ea3b679e5dfe05c4d",
|
||||
"feature/2026-07-30-web-context-injection-disclosure.zh.md": "sha256:9bca10469e4d77b20c3785ce7ab6cbc628a03b8919de8a47c14466c1dbc94d36",
|
||||
"feature/2026-07-31-experimental-subcommand-gate.i18n.yaml": "sha256:d223669bebbf6ea65b4ec636e8e7ed618eff389117335946be713897151c6968",
|
||||
"feature/2026-07-31-experimental-subcommand-gate.md": "sha256:8fdee37340f7e72397cf2f440a2ca70639a987d07f0c2102e02e79c0fec4bfeb",
|
||||
"feature/2026-07-31-experimental-subcommand-gate.zh.md": "sha256:bcdec0f82319670a1d1de54a27b103b5e2d86306b884a5f9f415b89cd5a373f4",
|
||||
"feature/2026-07-31-hover-card-click-copy.i18n.yaml": "sha256:2b95987c23e13a4499f5f3851770e8f97aa6f6df457a36b6aa818a8db08785c9",
|
||||
"feature/2026-07-31-hover-card-click-copy.md": "sha256:f9a85c1603dcbdd36d26f730bf2a1f7bfaaa2267c7c30bee08cb9a94a7ce774e",
|
||||
"feature/2026-07-31-hover-card-click-copy.zh.md": "sha256:b01e6edda5c6b031b5265ca0d868583fecce04cbc817fa7e8cc4433d10056e64",
|
||||
"feature/2026-07-31-web-cards-toolrow.i18n.yaml": "sha256:f9a6ab72a77934cdcc02167c7313f08d7e9925362017b34bed7ad56c8c70fbaa",
|
||||
"feature/2026-07-31-web-cards-toolrow.md": "sha256:5058f7cec4497d1cb0a5c8e77b88fddacac6eead034f3edec88e8514919b8a3e",
|
||||
"feature/2026-07-31-web-cards-toolrow.zh.md": "sha256:ba84ef2e1be61211ab5ba6950b78ede3d3a979f252bc068d3e04e2c025f7bc03",
|
||||
"feature/2026-08-08-dsh-run-headless-command.i18n.yaml": "sha256:1c2b4c5b61b9263b6267275d6fc69faeaad3cc887f0728a7ed4172d817af812b",
|
||||
"feature/2026-08-08-dsh-run-headless-command.md": "sha256:7695fe7fd322377d5986f14e35f13337f4cd376405c758218a81230f6d182d1c",
|
||||
"feature/2026-08-08-dsh-run-headless-command.zh.md": "sha256:113c14a36c64d2facc8ae46f37c7aa76359d8cacb9c18fcba26a723f15d036fb",
|
||||
"process/2026-06-11-doc-sync-enforcement.i18n.yaml": "sha256:33b6d5874427bd7a2bd82e7e2f4f482b12448b2464aef15a9c57975edb48554d",
|
||||
"process/2026-06-11-doc-sync-enforcement.md": "sha256:aa2fe83d519fc30d48dff19e596e83c8922aacc9e063e14fe2cc35b769b9100e",
|
||||
"process/2026-06-11-doc-sync-enforcement.zh.md": "sha256:698017bd35f030fdea3eac51df9e43138c48140f504739d687b7251d13fced2b",
|
||||
"process/2026-06-11-tsdown-over-dumble.i18n.yaml": "sha256:22791adb84a4b6c545173d4f1708eea51151d57e426d875e0e5423be9b6e0212",
|
||||
"process/2026-06-11-tsdown-over-dumble.md": "sha256:8d3c35dddd8869cc3361059dfe4b7b8ab6716d29dda232c97c2f37e92c841dc0",
|
||||
"process/2026-06-11-tsdown-over-dumble.zh.md": "sha256:cf11c651c13f5ffef5474e7795006ba3653c5eb08eae75a879be6499353455dc",
|
||||
"process/2026-06-20-generated-cordis-catalog.i18n.yaml": "sha256:5250aaec698b25bdf5e3a02f67e531793fc968f9c38348f0c8bd11418571967f",
|
||||
"process/2026-06-20-generated-cordis-catalog.md": "sha256:1f3190b759bf1445b35f25f2f18ac1c8b16d7f2e9263e2627bf3c94fac54d275",
|
||||
"process/2026-06-20-generated-cordis-catalog.zh.md": "sha256:5d46da71bd73bba62ba15a9f11b21da422dbecc8ed68b6f530ceb302e1935ddb",
|
||||
"process/2026-07-03-documentation-graph-atlas.i18n.yaml": "sha256:b1e1ed4b7865d87f939dbf8c94c0ea1069fdf7af6fa68f695e6c9d6eccbeb123",
|
||||
"process/2026-07-03-documentation-graph-atlas.md": "sha256:b62e92bb12123bfa4c4dac806f584aabb6b60af4c5a6a4ab88f84bb9153e766d",
|
||||
"process/2026-07-03-documentation-graph-atlas.zh.md": "sha256:3485ede4a5e695643bcf9e744a62f8914cff788ae35717dac5eb6bf77e0d65cf",
|
||||
@@ -256,9 +301,24 @@
|
||||
"process/2026-07-23-browser-demo-gif-recording.i18n.yaml": "sha256:808ccdda39e540645b440e40a2124baed737b98636265d8f6d8cf036a70f0d50",
|
||||
"process/2026-07-23-browser-demo-gif-recording.md": "sha256:4d3a3dc829c75b66f4f57a6a763b4b9562ce10efb90a19308142f598c5ea8524",
|
||||
"process/2026-07-23-browser-demo-gif-recording.zh.md": "sha256:409e5d31ea87f35c5227fcd1bd105167f580a8a24d29a223ce390b29eb31639a",
|
||||
"process/2026-07-23-personal-staging-maintenance-skills.i18n.yaml": "sha256:cbd5c32f2997713339699f4c12876b1950895fcb21594927c7509e3cb4c5b8d5",
|
||||
"process/2026-07-23-personal-staging-maintenance-skills.md": "sha256:a1ef960ddc47c8bc14578d8432ed0ac4272e31178937780989e4e3b3e3f4d49f",
|
||||
"process/2026-07-23-personal-staging-maintenance-skills.zh.md": "sha256:c0f3fcf0914af88cafcc7e094a2131bd1f764003defc6eaa446b743319e73ec1",
|
||||
"process/2026-07-26-gui-pr-gif-evidence-and-assets-branch.i18n.yaml": "sha256:89b6dc255cb0dd9d97ae6f34f37d185412d4260d7bf2f87d6b25e340f01dc26e",
|
||||
"process/2026-07-26-gui-pr-gif-evidence-and-assets-branch.md": "sha256:1742e09435ade4a09349c8843eb381e870a2be2d51b74449f08260422c8096b3",
|
||||
"process/2026-07-26-gui-pr-gif-evidence-and-assets-branch.zh.md": "sha256:220bd53a88617b09ee8970627c46540fa8951724b102acfe296a7ed7d0f7b5fe",
|
||||
"process/2026-07-27-wine-windows-gates-experiment.i18n.yaml": "sha256:6f4cbc12ee9cddbb297bf7e138ccabcd204f66898a0f7411b1633f03d5a9eab5",
|
||||
"process/2026-07-27-wine-windows-gates-experiment.md": "sha256:8d37dcdab058098c7de3da1de00ce61bef92bbc8d6ee71add959474c6fb3e936",
|
||||
"process/2026-07-27-wine-windows-gates-experiment.zh.md": "sha256:77fbf04df36af09e55007a93bd6b22d08ff99869efe8de3e97dac5b4701e0a9e",
|
||||
"process/2026-07-31-installer-adopts-existing-checkout.i18n.yaml": "sha256:e7d072856cd3df3b717d94647be4ea05db087b114df4b11d68a171677b94c970",
|
||||
"process/2026-07-31-installer-adopts-existing-checkout.md": "sha256:d97e39d07bde30d534417a406fc6c56ee6765dbc7b671888d17ef2cac9a7c6dc",
|
||||
"process/2026-07-31-installer-adopts-existing-checkout.zh.md": "sha256:ab445493c1aacf3f8dc091bdbd765ad56c551ae613e0d8156610f0c4614a7431",
|
||||
"process/2026-08-04-forward-only-pr-issue-status.i18n.yaml": "sha256:af23e203a66a95674154899410e2f420d1d0685dbf856c24cfccdaa547a17925",
|
||||
"process/2026-08-04-forward-only-pr-issue-status.md": "sha256:2d31077da47d95ab3ddf64d5efc6b1b8fb7c7709d39aca4a825ef9e9d382d501",
|
||||
"process/2026-08-04-forward-only-pr-issue-status.zh.md": "sha256:b61f865b7a8a0ac901250a3edbb92ea73177067c4c25448c7088925c2caeccd7",
|
||||
"process/2026-08-08-review-driven-issue-lifecycle-triggers.i18n.yaml": "sha256:4c28c59d3fc323e7cd01eff31f1fe759834719c5bede1e82b39f868970bf856d",
|
||||
"process/2026-08-08-review-driven-issue-lifecycle-triggers.md": "sha256:1b0514de5d030170e91e12e4d6ba788a9247f840e82700faa385a1c0c76ab857",
|
||||
"process/2026-08-08-review-driven-issue-lifecycle-triggers.zh.md": "sha256:028d78d61f603d8bac64c4cce20b393a78f8e029d3bb4976e79a47ecaefa6032",
|
||||
"simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.i18n.yaml": "sha256:ad3d1263cb0051b885173bf064de62065e2c646ccaae2d7250723da3b4eab90c",
|
||||
"simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.md": "sha256:8fb061d51c8c23b47d2367814bab3623c6d5b972f38d207a273caa9030b579bd",
|
||||
"simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.zh.md": "sha256:2ffeaca91f82844a5616d6dcce6b4af514bb8a7c46f78e47f668b204ac6edc04",
|
||||
@@ -322,9 +382,24 @@
|
||||
"simplification/2026-07-26-consolidate-gate-scripts-on-existing-deps.i18n.yaml": "sha256:7acf002ea8c1533f052c7bfc0c4e3da013ecf43c5872866a3ee4a8c2691c5e33",
|
||||
"simplification/2026-07-26-consolidate-gate-scripts-on-existing-deps.md": "sha256:f18a913096b7defd2192c4bac888a33f68075c3662703a0e28a6146897d17777",
|
||||
"simplification/2026-07-26-consolidate-gate-scripts-on-existing-deps.zh.md": "sha256:ff48a37673c97059536fe5b61aff746133eac682145550badb049eb5c83b097c",
|
||||
"simplification/2026-07-26-eventsource-parser-for-deepseek-sse.i18n.yaml": "sha256:534abd90ddde9ccd35ab7e595de4242d8fa30a75908a638e5b3290f749553e5a",
|
||||
"simplification/2026-07-26-eventsource-parser-for-deepseek-sse.md": "sha256:ce449c72ed09238ba5dbe6068689db13bd33225694b2ad2c7696541f32dc0eec",
|
||||
"simplification/2026-07-26-eventsource-parser-for-deepseek-sse.zh.md": "sha256:2f66b407b626f1f8c661d849b3b359c959e690715e0ac3f61f7e3d57d59e89c3",
|
||||
"simplification/2026-07-26-turndown-for-tool-web-html-markdown.i18n.yaml": "sha256:6df56f5f5639847f0fac445abb5fea8a07f3cf9a0703d9022e50728c8f5055ca",
|
||||
"simplification/2026-07-26-turndown-for-tool-web-html-markdown.md": "sha256:344c5cc2a1e79287eeda6996ae417dd2e02a7545987df0f2c9016b20ae094d93",
|
||||
"simplification/2026-07-26-turndown-for-tool-web-html-markdown.zh.md": "sha256:8c4f2ac12ccd23f7ada90694502f5da689344e6bba72080a89672aa9b4f1903a",
|
||||
"simplification/2026-07-27-copyable-transcript-no-gutter-bar.i18n.yaml": "sha256:821f96f3e203e03b80553c07b10a511926bb5014be95c7df6bffb30c8e226d31",
|
||||
"simplification/2026-07-27-copyable-transcript-no-gutter-bar.md": "sha256:4b6aa150bbc8a4da0acac4d20f5fb8c2b77fef7e9c4c4dba8fd8e84dec36d619",
|
||||
"simplification/2026-07-27-copyable-transcript-no-gutter-bar.zh.md": "sha256:5225e627ff301be171434a5b9f18905fe1578f50eb2d4bf9998e126aba6cc3e3",
|
||||
"simplification/2026-07-30-sidebar-resize-without-visible-pill.i18n.yaml": "sha256:ad42430fef4a5db610f8c56acfcda79b40396418694f664a5b0fb093bff1f114",
|
||||
"simplification/2026-07-30-sidebar-resize-without-visible-pill.md": "sha256:6f2cfc5121371ec19c7178b31777c223e16bebdc9b63b7654a61cadc4a765b63",
|
||||
"simplification/2026-07-30-sidebar-resize-without-visible-pill.zh.md": "sha256:ab859eeb12c6a74da3c37d411af52fce37bed3195941570b2f23ccd9a00d55fe",
|
||||
"simplification/2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.i18n.yaml": "sha256:531c446f0e95054f8ced17be9a180f8b0a823f7e9d5ce466c94c2f9cff90a111",
|
||||
"simplification/2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.md": "sha256:a35a6372aabdf7cbc211f1bd5820d85d3467c9ed50f84e05caa3339382379ce7",
|
||||
"simplification/2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.zh.md": "sha256:a6ed9530289a783c3d7a1ddb038fba6b7daf7feb773298a57e811791e354d438",
|
||||
"simplification/2026-08-03-explicit-config-dsh-entrypoint.i18n.yaml": "sha256:5466161f3fb8f2e8117fe8ff242675cc9fe9ef264d1e29b9bc586891c73c051a",
|
||||
"simplification/2026-08-03-explicit-config-dsh-entrypoint.md": "sha256:f23accae7d05c2e75cb73ec69b492307f1ce7526ecfa9f6b12a621e02fd1a0c3",
|
||||
"simplification/2026-08-03-explicit-config-dsh-entrypoint.zh.md": "sha256:a32d2c6ecf748a16a2c35b59cd2da2fda75769e3ab24be6a2e026d8655466db4",
|
||||
"testing/2026-06-20-remove-redundant-snapshot-log-expected-output.i18n.yaml": "sha256:4177012c0821a8c22499852ecdf096af56d7263cb91c5d9d1bcd552cc26a3e00",
|
||||
"testing/2026-06-20-remove-redundant-snapshot-log-expected-output.md": "sha256:45234e7cc04b6010c6141f8d5924c04547300098f96262d423c50108e7c7011a",
|
||||
"testing/2026-06-20-remove-redundant-snapshot-log-expected-output.zh.md": "sha256:15e5a4ad3dee0bb711480cabe45cd97ec37bbdba19c2c2b47d1e9c203b07a48b",
|
||||
@@ -345,6 +420,9 @@
|
||||
"testing/2026-07-08-shared-acp-snapshot-package.zh.md": "sha256:02da3f910c2060f70038a0d86a7ddae4a8890905600440e1373412f54fbdcea8",
|
||||
"testing/2026-07-18-tui-terminal-state-snapshots.i18n.yaml": "sha256:c1a22174274b9f34ef4368039b3547f221507b040bd51b87af73a2722ee6b4d2",
|
||||
"testing/2026-07-18-tui-terminal-state-snapshots.md": "sha256:9a7fdcbeafc34376cb049b9668e0f4e9e541f523116fb11c3af9d35c2963e908",
|
||||
"testing/2026-07-18-tui-terminal-state-snapshots.zh.md": "sha256:26750f240f6c8a7b28746f62fe161b357e9c5dd52867cc7037399f1ed6ff37fa"
|
||||
"testing/2026-07-18-tui-terminal-state-snapshots.zh.md": "sha256:26750f240f6c8a7b28746f62fe161b357e9c5dd52867cc7037399f1ed6ff37fa",
|
||||
"testing/2026-07-26-execa-for-test-subprocess-plumbing.i18n.yaml": "sha256:dd45cddb591b892739b75b0c180bde7f14008f4769227b863571475be295e1e0",
|
||||
"testing/2026-07-26-execa-for-test-subprocess-plumbing.md": "sha256:1f45a69d0a7367ec5afbf112a77b355339b35270af8ff52696bee879cdf770d3",
|
||||
"testing/2026-07-26-execa-for-test-subprocess-plumbing.zh.md": "sha256:8a24bdc8376373d7a97f65cefc07078824bf918d6a9934056a025ecfafe8634b"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/process/2026-06-20-generated-cordis-catalog.md
|
||||
2026-06-20-generated-cordis-catalog.md: 8d013a5b0c7e1b8df9f607215384f6c26a83b5b8
|
||||
2026-06-20-generated-cordis-catalog.zh.md: 2550bc805db7bea95106d444ecf9b0ad75ef91cc
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Generated cordis events + services catalog
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-06-20-generated-cordis-catalog.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 生成的 Cordis 事件与服务目录
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-06-20-generated-cordis-catalog.md) | 中文
|
||||
|
||||
+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 .agents/notes/implemented/process/2026-07-23-personal-staging-maintenance-skills.md
|
||||
2026-07-23-personal-staging-maintenance-skills.md: a7ccc5b1e0f13e880c58a93d2e4c2cd4f06e2a93
|
||||
2026-07-23-personal-staging-maintenance-skills.zh.md: d988eae0b4746f14b893b8f1f32f8396e6548232
|
||||
2026-07-23-personal-staging-maintenance-skills.md: 86654be1afe986107e7dae4440b497a266636012
|
||||
2026-07-23-personal-staging-maintenance-skills.zh.md: 7593a2e8cafc93df02c9418c94f84bbd3f154a0c
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Personal staging maintenance skills
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
English | [中文](2026-07-23-personal-staging-maintenance-skills.zh.md)
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 个人集成分支维护 skill
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
[English](2026-07-23-personal-staging-maintenance-skills.md) | 中文
|
||||
|
||||
@@ -12,7 +13,7 @@ Status: implemented
|
||||
|
||||
仓库从其根 `skills/` 目录分发 [`dsh-customize`](../../../../skills/dsh-customize/SKILL.md)、[`dsh-upgrade`](../../../../skills/dsh-upgrade/SKILL.md) 和 [`dsh-upstream-customization`](../../../../skills/dsh-upstream-customization/SKILL.md)。它们的描述同时说明操作内容和选择该 skill(技能)的用户请求。分发的 TUI 在启动时将该目录提供给本地 skill 提供方,在发现优先级上位于项目根目录和用户根目录之后。这些 skill 根据已安装的启动器而非个人路径或分支名称定位当前生效的检出和集成分支,遵从仓库内指令,要求使用任务 worktree,并利用集成分支所在 worktree 的既有 `.agents/merge.lock`,串行执行每一次个人集成分支修改。
|
||||
|
||||
升级流程在变基前检查 Git 日志和提交范围,以识别将进入升级的上游变更、个人提交、重复内容和可能发生冲突的区域。它会丢弃上游已经提供的定制;如果这类定制在本地只剩文档差异,也会一并丢弃,除非该说明包含上游缺失且可独立使用的当前契约。每次升级尝试使用同一个 UTC 基本格式时间戳,用于其独立的 `dsh-staging-<timestamp>` 同级克隆、本地 `dsh-upgrade/prepare-<timestamp>` 分支、新的 `dsh-staging/<timestamp>` 分支、私有的上游引用与恢复引用,以及启动器备份。同级克隆的名称不派生自当前目录名,名称冲突会直接失败,而不是追加临时后缀。流程根据进程命令和运行时环境而非 shell 工作目录推导当前 DSH 进程的源码位置,随后将已安装启动器所指向的仓库和检出视为不可变,唯一例外是持有其既有合并锁。
|
||||
升级流程在变基前检查 Git 日志和提交范围,以识别将进入升级的上游变更、个人提交、重复内容和可能发生冲突的区域。它会丢弃上游已经提供的定制;如果这类定制在本地只剩文档差异,也会一并丢弃,除非该说明包含上游缺失且可独立使用的当前约定。每次升级尝试使用同一个 UTC 基本格式时间戳,用于其独立的 `dsh-staging-<timestamp>` 同级克隆、本地 `dsh-upgrade/prepare-<timestamp>` 分支、新的 `dsh-staging/<timestamp>` 分支、私有的上游引用与恢复引用,以及启动器备份。同级克隆的名称不派生自当前目录名,名称冲突会直接失败,而不是追加临时后缀。流程根据进程命令和运行时环境而非 shell 工作目录推导当前 DSH 进程的源码位置,随后将已安装启动器所指向的仓库和检出视为不可变,唯一例外是持有其既有合并锁。
|
||||
|
||||
在独立克隆中验证通过后,工作流会创建并验证带时间戳的集成分支,然后以原子方式将启动器从保持不变的旧集成分支检出一次性切换到新集成分支检出。启动器绝不会指向准备、功能、评审、发布或处于分离状态的检出。切换前的失败会让已安装的检出和启动器保持不变;切换后的失败则恢复并验证启动器备份。旧的集成分支检出、其分支、恢复引用和启动器备份会一直保留,直到重启后的进程证明 DSH 运行于新的集成分支,且用户明确批准回滚清理为止。
|
||||
|
||||
+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 .agents/notes/implemented/process/2026-07-27-wine-windows-gates-experiment.md
|
||||
2026-07-27-wine-windows-gates-experiment.md: 640c8e455b1a35ea4ac83454227147b9979316dc
|
||||
2026-07-27-wine-windows-gates-experiment.zh.md: 67f59a93d1b1fad98e36e6f9c51bc77abb9e3d07
|
||||
2026-07-27-wine-windows-gates-experiment.md: ce2ad1ad4506f59083de5731677d052161849e3d
|
||||
2026-07-27-wine-windows-gates-experiment.zh.md: a0a5e8e7cce078adf2c961fe4d8cc50edc28355c
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Wine-run Windows blocking gates on Linux runners
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-08
|
||||
|
||||
English | [中文](2026-07-27-wine-windows-gates-experiment.zh.md)
|
||||
|
||||
@@ -14,7 +15,7 @@ The question the experiment answered: can a plain Linux runner produce an equiva
|
||||
|
||||
The required pull-request `windows` job in [ci.yml](../../../../.github/workflows/ci.yml) (`windows node 24 / wine blocking`) runs the blocking gate commands on `ubuntu-latest` under Wine with real Windows binaries: a checksum-verified win-x64 Node.js executes `tsc -b`, `tsdown`, and the VitePress production build, so the win32 branches of the toolchain — backslash path handling, `CreateProcess` spawn semantics, PE loading of `@esbuild/win32-x64`, and the rolldown/rollup MSVC `.node` addons — actually execute. The master `serial-windows` job is untouched: the complete native-kernel inventory, including the observational portability gates this lane does not run, still executes on real `windows-2025` on every master push.
|
||||
|
||||
Dependencies install natively on Linux with `supportedArchitectures` extended to win32-x64, which materializes the Windows platform packages in the same store; the cmd-shim layer is bypassed by invoking each tool's JavaScript entrypoint directly, the same processes `run-gates` ultimately spawns. `nodeLinker: hoisted` is load-bearing, not stylistic: the independent prototype in [PR #689](https://github.com/deepseek-harness/deepseek-harness/pull/689) kept pnpm's default isolated layout — including a faithful offline Windows-pnpm re-install over a Linux-prefetched store — and Windows Node under Wine still could not resolve `@esbuild/win32-x64` or load the koffi prebuild through the isolated symlink chain, failing before any repository gate ran. A flat layout with real files is what makes the gates reachable at all; #689's checksum pinning is adopted, while its Windows-pnpm-installs-the-tree goal is explicitly given up (the install contract stays Linux-tested here).
|
||||
Dependencies install natively on Linux with `supportedArchitectures` extended to win32-x64, which materializes the Windows platform packages in the same store; the cmd-shim layer is bypassed by invoking each tool's JavaScript entrypoint directly, the same processes `run-gates` ultimately spawns. `nodeLinker: hoisted` is load-bearing, not stylistic: an independent prototype kept pnpm's default isolated layout — including a faithful offline Windows-pnpm re-install over a Linux-prefetched store — and Windows Node under Wine still could not resolve `@esbuild/win32-x64` or load the koffi prebuild through the isolated symlink chain, failing before any repository gate ran. A flat layout with real files is what makes the gates reachable at all; the prototype's checksum pinning is adopted, while its Windows-pnpm-installs-the-tree goal is explicitly given up (the install contract stays Linux-tested here).
|
||||
|
||||
The lane holds the wall clock of the Linux CI jobs through four levers: the master-refreshed pnpm store cache (restore-only, same key as the Linux jobs), Wine provisioning (apt install, Windows Node download, `wineboot`) running concurrently with `pnpm install`, the two blocking surfaces running concurrently — the same shape `run-gates` gives them on native Windows — and an apt-archive cache keyed on the runner image, seeded from master by the `wine apt cache` job so every pull request restores from the default-branch scope.
|
||||
|
||||
@@ -32,7 +33,7 @@ Measured on 2026-07-27, warm caches, pull-request trigger, standard 2-core `ubun
|
||||
|
||||
**A full Windows guest under QEMU/KVM inside the Linux runner.** Real NT kernel, so full fidelity including case-insensitive NTFS and ConPTY — but tens of minutes of image download and unattended install before the first gate runs (40m19s measured end-to-end on the sibling experiment branch `exp/kvm-windows-ci`). Promotable only with disk-image caching that pressures the Actions cache budget.
|
||||
|
||||
**Windows pnpm performing the install under Wine ([PR #689](https://github.com/deepseek-harness/deepseek-harness/pull/689)).** The higher-fidelity variant of this same idea: MinGit and pnpm staged into the prefix, a Linux prefetch filling the store, then `pnpm install --offline` run by Windows Node so the install contract itself executes as win32. It reached the install but not the gates — Wine's networking could not reach the registry directly, and the isolated `node_modules` layout defeated resolution of the Windows platform packages even after a clean offline install. This lane trades that fidelity away (hoisted layout, Linux-side install) to reach the gates; the two records are complementary halves of the same verdict.
|
||||
**Windows pnpm performing the install under Wine.** The higher-fidelity variant of this same idea: MinGit and pnpm staged into the prefix, a Linux prefetch filling the store, then `pnpm install --offline` run by Windows Node so the install contract itself executes as win32. It reached the install but not the gates — Wine's networking could not reach the registry directly, and the isolated `node_modules` layout defeated resolution of the Windows platform packages even after a clean offline install. This lane trades that fidelity away (hoisted layout, Linux-side install) to reach the gates; the two records are complementary halves of the same verdict.
|
||||
|
||||
**Filesystem-semantics lanes on Linux (casefolded ext4, filename lint).** Catches the highest-frequency Windows breakage class for near-zero cost but proves nothing about win32 binaries. Explored as the sibling experiment branch `exp/casefold-windows-ci`; complementary to, not competitive with, this lane.
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 在 Linux runner 上用 Wine 运行 Windows 阻断门禁
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-08
|
||||
|
||||
[English](2026-07-27-wine-windows-gates-experiment.md) | 中文
|
||||
|
||||
@@ -14,7 +15,7 @@ Pull request 的 Windows 通道旨在验证两个阻断性 win32 表面,即 wo
|
||||
|
||||
[ci.yml](../../../../.github/workflows/ci.yml) 中必需的 pull request `windows` 作业(`windows node 24 / wine blocking`)在 `ubuntu-latest` 上通过 Wine 用真实 Windows 二进制运行阻断门禁命令:校验和验证过的 win-x64 Node.js 执行 `tsc -b`、`tsdown` 与 VitePress 生产构建,因此工具链的 win32 分支——反斜杠路径处理、`CreateProcess` 派生语义、`@esbuild/win32-x64` 的 PE 加载、以及 rolldown/rollup 的 MSVC `.node` 插件——都真正执行。master 的 `serial-windows` 作业原封不动:完整的原生内核清单,包括本通道不运行的观察性可移植性门禁,仍在每次 master push 时于真实 `windows-2025` 上执行。
|
||||
|
||||
依赖在 Linux 上原生安装,`supportedArchitectures` 扩展到 win32-x64,使 Windows 平台包物化进同一个 store;通过直接调用各工具的 JavaScript 入口绕开 cmd-shim 层,这正是 `run-gates` 最终派生的那些进程。`nodeLinker: hoisted` 是承重的,不是风格问题:[PR #689](https://github.com/deepseek-harness/deepseek-harness/pull/689) 的独立原型保留了 pnpm 默认的 isolated 布局——包括在 Linux 预取的 store 上忠实地用 Windows pnpm 离线重装——而 Wine 下的 Windows Node 依然无法穿过 isolated 符号链接链解析 `@esbuild/win32-x64` 或加载 koffi 预编译产物,在任何仓库门禁运行前就失败了。扁平的真实文件布局才让门禁变得可达;本通道采纳了 #689 的校验和固定,同时明确放弃其「Windows pnpm 安装依赖树」的目标(安装契约在此仍由 Linux 侧验证)。
|
||||
依赖在 Linux 上原生安装,`supportedArchitectures` 扩展到 win32-x64,使 Windows 平台包物化进同一个 store;通过直接调用各工具的 JavaScript 入口绕开 cmd-shim 层,这正是 `run-gates` 最终派生的那些进程。`nodeLinker: hoisted` 是承重的,不是风格问题:一个独立原型保留了 pnpm 默认的 isolated 布局——包括在 Linux 预取的 store 上忠实地用 Windows pnpm 离线重装——而 Wine 下的 Windows Node 依然无法穿过 isolated 符号链接链解析 `@esbuild/win32-x64` 或加载 koffi 预编译产物,在任何仓库门禁运行前就失败了。扁平的真实文件布局才让门禁变得可达;本通道采纳了该原型的校验和固定,同时明确放弃其「Windows pnpm 安装依赖树」的目标(安装契约在此仍由 Linux 侧验证)。
|
||||
|
||||
该通道靠四个杠杆把墙钟时间保持在与 Linux CI 作业相当的水平:master 刷新的 pnpm store 缓存(只恢复,与 Linux 作业同键)、Wine 供给(apt 安装、Windows Node 下载、`wineboot`)与 `pnpm install` 并发运行、两个阻断表面并发运行——与 `run-gates` 在原生 Windows 上给它们的形状相同——以及按 runner 镜像为键的 apt 归档缓存,由 master 的 `wine apt cache` 作业播种,使每个 pull request 都能从默认分支作用域恢复。
|
||||
|
||||
@@ -32,7 +33,7 @@ Pull request 的 Windows 通道旨在验证两个阻断性 win32 表面,即 wo
|
||||
|
||||
**在 Linux runner 内用 QEMU/KVM 跑完整 Windows 客户机。** 真实 NT 内核,保真度完整,包括大小写不敏感的 NTFS 与 ConPTY——但首个门禁运行前要花数十分钟下载镜像并做无人值守安装(兄弟实验分支 `exp/kvm-windows-ci` 实测端到端 40 分 19 秒)。只有配上会挤压 Actions 缓存预算的磁盘镜像缓存才可投入使用。
|
||||
|
||||
**在 Wine 下由 Windows pnpm 执行安装([PR #689](https://github.com/deepseek-harness/deepseek-harness/pull/689))。** 同一想法的更高保真度变体:把 MinGit 与 pnpm 放进 prefix,用 Linux 预取填充 store,再由 Windows Node 运行 `pnpm install --offline`,让安装契约本身以 win32 身份执行。它到达了安装但没到达门禁——Wine 的网络无法直接访问 registry,且 isolated 的 `node_modules` 布局即便在干净的离线安装后也挫败了 Windows 平台包的解析。本通道牺牲这份保真度(hoisted 布局、Linux 侧安装)来换取门禁可达;两份记录是同一裁决互补的两半。
|
||||
**在 Wine 下由 Windows pnpm 执行安装。** 同一想法的更高保真度变体:把 MinGit 与 pnpm 放进 prefix,用 Linux 预取填充 store,再由 Windows Node 运行 `pnpm install --offline`,让安装契约本身以 win32 身份执行。它到达了安装但没到达门禁——Wine 的网络无法直接访问 registry,且 isolated 的 `node_modules` 布局即便在干净的离线安装后也挫败了 Windows 平台包的解析。本通道牺牲这份保真度(hoisted 布局、Linux 侧安装)来换取门禁可达;两份记录是同一裁决互补的两半。
|
||||
|
||||
**Linux 上的文件系统语义通道(casefold ext4、文件名 lint)。** 以近零成本捕获最高频的 Windows 故障类别,但对 win32 二进制什么也证明不了。作为兄弟实验分支 `exp/casefold-windows-ci` 探索;与本通道互补而非竞争。
|
||||
|
||||
+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 .agents/notes/implemented/process/2026-07-31-installer-adopts-existing-checkout.md
|
||||
2026-07-31-installer-adopts-existing-checkout.md: de3cd052f94a0d5256c7687e9a1a38ee69fd2caf
|
||||
2026-07-31-installer-adopts-existing-checkout.zh.md: 2e8be804b4af6151e77e36f8b109616aab3a18e9
|
||||
2026-07-31-installer-adopts-existing-checkout.md: 08eec47a1cf1254409df93ab83ddf503b4d7d171
|
||||
2026-07-31-installer-adopts-existing-checkout.zh.md: dc3cc5ee8dbbe287860ad02f4bdba66afb4fbeaf
|
||||
+9
-8
@@ -1,14 +1,15 @@
|
||||
# Agent Note: the installer adopts an existing checkout into the managed layout
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
English | [中文](2026-07-31-installer-adopts-existing-checkout.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`scripts/install.sh` produced two incompatible install shapes. A `curl … | sh` install built the managed layout — a master clone at `~/.dsh/source/master`, a staging worktree on `dsh-staging/<timestamp>`, and the stable `current` symlink the PATH launcher resolves through. Running the same script from a checkout instead linked `dsh` straight at that checkout's `bin/dsh`, per the earlier [in-repo skip-clone decision](../../archived/process/2026-07-22-installer-in-repo-skip-clone.md).
|
||||
`scripts/install.sh` produced two incompatible installation layouts. A `curl … | sh` install built the managed layout — a master clone at `~/.dsh/source/master`, a staging worktree on `dsh-staging/<timestamp>`, and the stable `current` symlink the PATH launcher resolves through. Running the same script from a checkout instead linked `dsh` straight at that checkout's `bin/dsh`, per the earlier [in-repo skip-clone decision](../../archived/process/2026-07-22-installer-in-repo-skip-clone.md).
|
||||
|
||||
The direct link is a terminal state. `current` is what an upgrade repoints, so an install without it is not upgradable by [`dsh-upgrade`](../../../../skills/dsh-upgrade/SKILL.md); the PATH symlink dangles if the checkout moves; and the launcher resolves to whatever branch the contributor happened to have checked out, which the upgrade contract forbids as a launcher target. The upgrade skill already described this shape as a legacy install needing a one-time migration, so the layouts diverged at install time and were reconciled only later, if ever.
|
||||
The direct link cannot be upgraded. `current` is what an upgrade repoints, so an install without it is not upgradable by [`dsh-upgrade`](../../../../skills/dsh-upgrade/SKILL.md); the PATH symlink dangles if the checkout moves; and the launcher resolves to whatever branch the contributor happened to have checked out, which the upgrade contract forbids as a launcher target. The upgrade skill already described this layout as a legacy install needing a one-time migration, so the layouts diverged at install time and were reconciled only later, if ever.
|
||||
|
||||
## Decision
|
||||
|
||||
@@ -20,7 +21,7 @@ The installer records nothing about where that repository lives. A container who
|
||||
|
||||
Adoption branches from `HEAD`, so committed work is what runs and uncommitted changes stay in the checkout. This is not prompted or warned about: the installer builds the layout and gets out of the way. Setting `DSH_SOURCE` to a different directory remains the one documented way to opt back into cloning a separate tree.
|
||||
|
||||
Every path comparison runs on physical paths through a `resolve_dir` helper, and every compared value is resolved at assignment rather than at the comparison. Git always reports resolved paths, so comparing one against an unresolved path disagrees whenever a symlink sits anywhere above the checkout — a symlinked home directory is enough, and macOS reaches every `mktemp` path that way through `/var` -> `private/var`. The mismatch misclassified an existing managed install as a foreign clone and would have built a second container beside the real one. The same defect recurred twice more during review, both times as one side of a comparison left unresolved: a curl install's `REPO_ROOT`, and the container path it was compared against. `resolve_dir` therefore echoes a missing path back rather than failing, so a not-yet-created container needs no per-call fallback and no site can compare against an empty path by forgetting one; callers that need "does not exist" test the directory explicitly. `git rev-parse --path-format=absolute` would do the same job but requires git 2.31+.
|
||||
Every path comparison runs on physical paths through a `resolve_dir` helper, and every compared value is resolved at assignment rather than at the comparison. Git always reports resolved paths, so comparing one against an unresolved path disagrees whenever a symlink sits anywhere above the checkout — a symlinked home directory is enough, and macOS reaches every `mktemp` path that way through `/var` -> `private/var`. The mismatch misclassified an existing managed install as a foreign clone and would have built a second container beside the real one. The same defect class recurs whenever one side of a comparison is left unresolved — as it did with a curl install's `REPO_ROOT` and with the container path it was compared against. `resolve_dir` therefore echoes a missing path back rather than failing, so a not-yet-created container needs no per-call fallback and no site can compare against an empty path by forgetting one; callers that need "does not exist" test the directory explicitly. `git rev-parse --path-format=absolute` would do the same job but requires git 2.31+.
|
||||
|
||||
Before `current` is repointed, the installer rejects a staging path that resolves to the repository itself, enforcing the upgrade contract that the launcher never resolves to the master clone.
|
||||
|
||||
@@ -28,9 +29,9 @@ Before `current` is repointed, the installer rejects a staging path that resolve
|
||||
|
||||
**Make `~/.dsh/source/master` a symlink to the arbitrary clone.** Rejected. Git resolves the symlink and records the *real* path: a worktree created through it stores `gitdir: …/<clone>/.git/worktrees/<name>`, and `git worktree list` reports the clone. The symlink is therefore decorative — nothing reads it — while implying the container owns the repository. It also fails silently: moving the clone leaves `master` present but dangling and every staging worktree dead with `fatal: not a git repository`. Worst, it aliases two names onto one tree, so the "current must never be the master clone" check passes by string comparison while being false. `~/.dsh/source/master` is a location, not a name, and only the location is authoritative.
|
||||
|
||||
**Promote the checkout itself to the `current` target.** Rejected: the upgrade contract requires `current` to be a clean staging worktree on a staging branch, never a feature, review, or detached checkout. It would also make every upgrade rewrite the tree the contributor is editing.
|
||||
**Promote the checkout itself to the `current` target.** Rejected: the upgrade contract requires `current` to point to a clean staging worktree on a staging branch, never a feature, review, or detached checkout. It would also make every upgrade rewrite the tree the contributor is editing.
|
||||
|
||||
**Keep link-in-place behind a prompt or a `DSH_ADOPT` flag.** Rejected, and an earlier revision of this change shipped exactly that before it was removed. The divergent shape was the defect itself, so retaining it as an option preserves the problem and doubles the states every later change must reason about — the prompt, the flag, the dirty-tree warning, and a second linking path all existed only to keep a shape nothing should produce. The original motivation for link-in-place, keeping the script testable against local source, survives adoption: a staging worktree branched from the checkout's `HEAD` runs the same code. `DSH_SOURCE` remains the escape hatch for installing a separate tree.
|
||||
**Keep link-in-place behind a prompt or a `DSH_ADOPT` flag.** Rejected, and an earlier revision of this change shipped exactly that before it was removed. The second layout was the defect itself, so retaining it as an option preserves the problem and doubles the states every later change must handle — the prompt, the flag, the dirty-tree warning, and a second linking path all existed only to keep a layout nothing should produce. The original motivation for link-in-place, keeping the script testable against local source, survives adoption: a staging worktree branched from the checkout's `HEAD` runs the same code. `DSH_SOURCE` remains available for installing a separate tree.
|
||||
|
||||
**Warn or prompt when the tree is dirty.** Rejected: `worktree add` from `HEAD` cannot carry uncommitted work, so the behavior is determined and a prompt only adds a decision the user cannot act on differently. The contract is documented instead.
|
||||
|
||||
@@ -38,7 +39,7 @@ Before `current` is repointed, the installer rejects a staging path that resolve
|
||||
|
||||
## Consequences
|
||||
|
||||
One layout now serves every install, so an adopted clone is upgradable by `dsh-upgrade` without the one-time migration that skill described, and the installer has no branch that produces an unupgradable shape. In-repo runs still never mutate the working tree.
|
||||
One layout now serves every install, so an adopted clone is upgradable by `dsh-upgrade` without the one-time migration that skill described, and the installer has no branch that produces an unupgradable layout. In-repo runs still never mutate the working tree.
|
||||
|
||||
The cost is that a contributor can no longer point PATH at a checkout and have `dsh` follow that working tree as they switch branches: the launcher now resolves to a staging worktree pinned to the `HEAD` adopted at install time. Re-running the installer adopts the current `HEAD` again.
|
||||
|
||||
@@ -46,6 +47,6 @@ A container adopting an outside clone is also no longer self-contained: deleting
|
||||
|
||||
## Testing
|
||||
|
||||
`scripts/install.sh` has no automated test, and this change does not add one: the user directed that `install.spec.ts` be left out of scope. That is a known gap on a shipped user-facing path, and the `/var` resolution defect above is exactly the class of bug a test would have caught first. The standing [`FIXME(install-ts)`](../../../../scripts/install.sh) asking for this workflow to move into a tested TypeScript entrypoint is correspondingly more pressing.
|
||||
`scripts/install.sh` now has a real-shell PTY regression suite in `apps/cli/tests/install-script.spec.ts`, covering adoption and curl-style paths with stubbed dependencies. Curl-style installs default to the public `deepseek-ai/deepseek-harness-sdk` source, while replacing the installer with pnpm/npx remains separate work.
|
||||
|
||||
Verification was manual, through a throwaway harness driving the real script with a stubbed `pnpm`: adopting a standalone clone; adopting from a linked worktree into its existing container; an explicit `DSH_SOURCE` still opting back into cloning; a dirty tree adopting silently with no prompt or warning while its uncommitted file stays behind; a non-git checkout failing with guidance; and a `curl`-style clone install asserting the built layout, which is the regression that caught the unresolved-`REPO_ROOT` defect. The interactive path was exercised under tmux from a dirty checkout, confirming the run reaches the launcher with no adoption prompt and ends with `dsh` running from the new staging worktree while the original checkout keeps its branch and its uncommitted file.
|
||||
Verification was manual, through a throwaway harness driving the real script with a stubbed `pnpm`: adopting a standalone clone; adopting from a linked worktree into its existing container; an explicit `DSH_SOURCE` still opting back into cloning; a dirty tree adopting silently with no prompt or warning while its uncommitted file stays behind; a non-git checkout failing with guidance; and a `curl`-style clone install asserting the built layout, the check that catches an unresolved `REPO_ROOT`. The interactive path was exercised under tmux from a dirty checkout, confirming the run reaches the launcher with no adoption prompt and ends with `dsh` running from the new staging worktree while the original checkout keeps its branch and its uncommitted file.
|
||||
+11
-10
@@ -1,14 +1,15 @@
|
||||
# Agent Note: 安装器把已有检出接管进受管布局
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
[English](2026-07-31-installer-adopts-existing-checkout.md) | 中文
|
||||
|
||||
## Problem
|
||||
|
||||
`scripts/install.sh`会产生两种互不兼容的安装形态。`curl … | sh`安装会构建受管布局——`~/.dsh/source/master`处的 master 克隆、位于`dsh-staging/<时间戳>`分支上的 staging worktree,以及 PATH 启动器据以解析的稳定`current`符号链接。而从检出中运行同一脚本时,则依据此前的[检出内跳过克隆决策](../../archived/process/2026-07-22-installer-in-repo-skip-clone.md),把`dsh`直接链接到该检出的`bin/dsh`。
|
||||
`scripts/install.sh`会产生两种互不兼容的安装布局。`curl … | sh`安装会构建受管布局——`~/.dsh/source/master`处的 master 克隆、位于`dsh-staging/<时间戳>`分支上的 staging worktree,以及 PATH 启动器据以解析的稳定`current`符号链接。而从检出中运行同一脚本时,则依据此前的[检出内跳过克隆决策](../../archived/process/2026-07-22-installer-in-repo-skip-clone.md),把`dsh`直接链接到该检出的`bin/dsh`。
|
||||
|
||||
这种直接链接是一种终态。升级重指的正是`current`,因此缺少它的安装无法通过[`dsh-upgrade`](../../../../skills/dsh-upgrade/SKILL.md)升级;检出一旦移动,PATH 符号链接就会失效;而且启动器会解析到贡献者恰好检出的任意分支,这正是升级契约禁止作为启动器目标的情形。升级技能早已把这种形态描述为需要一次性迁移的旧式安装,于是两种布局在安装时就已分叉,并且要到很久以后才会被调和——甚至永远不会。
|
||||
这种直接链接无法升级。升级重指的正是`current`,因此缺少它的安装无法通过[`dsh-upgrade`](../../../../skills/dsh-upgrade/SKILL.md)升级;检出一旦移动,PATH 符号链接就会失效;而且启动器会解析到贡献者恰好检出的任意分支,这正是升级约定禁止作为启动器目标的情形。升级技能早已把这种布局描述为需要一次性迁移的旧式安装,因此两种布局从安装时起便不相同,只有以后执行迁移才会一致,而迁移也可能永远不执行。
|
||||
|
||||
## Decision
|
||||
|
||||
@@ -20,25 +21,25 @@ Status: implemented
|
||||
|
||||
接管以`HEAD`为分支起点,因此运行的是已提交的内容,未提交的更改仍留在检出中。这一点既不提示也不警告:安装器构建好布局后便不再打扰。把`DSH_SOURCE`设为其他目录,仍是唯一有文档记载的、回到克隆另一棵树的方式。
|
||||
|
||||
所有路径比较都通过`resolve_dir`辅助函数在物理路径上进行,且每个参与比较的值都在赋值时解析,而非在比较时解析。git 报告的始终是已解析的路径,因此只要检出之上任意一层存在符号链接,拿它与未解析的路径相比较就会不相等——家目录本身是符号链接即已足够,而 macOS 通过`/var` -> `private/var`使每个`mktemp`路径都如此。这种不匹配会把已有的受管安装误判为外来克隆,并在真正的容器旁再建一个容器。同一缺陷在评审过程中又出现了两次,两次都是比较的一侧未经解析:一次是 curl 安装的`REPO_ROOT`,一次是与之比较的容器路径。因此`resolve_dir`在路径不存在时原样回显该路径而非失败,这样尚未创建的容器无需在每个调用点单独兜底,也就没有调用点会因遗漏兜底而与空路径比较;需要判断"不存在"的调用方则显式检测该目录。`git rev-parse --path-format=absolute`能完成同样的工作,但要求 git 2.31 及以上版本。
|
||||
所有路径比较都通过`resolve_dir`辅助函数在物理路径上进行,且每个参与比较的值都在赋值时解析,而非在比较时解析。git 报告的始终是已解析的路径,因此只要检出之上任意一层存在符号链接,拿它与未解析的路径相比较就会不相等——家目录本身是符号链接即已足够,而 macOS 通过`/var` -> `private/var`使每个`mktemp`路径都如此。这种不匹配会把已有的受管安装误判为外来克隆,并在真正的容器旁再建一个容器。只要比较的一侧未经解析,同类缺陷就会重现——curl 安装的`REPO_ROOT`以及与之比较的容器路径都曾如此。因此`resolve_dir`在路径不存在时原样回显该路径而非失败,这样尚未创建的容器无需在每个调用点单独兜底,也就没有调用点会因遗漏兜底而与空路径比较;需要判断"不存在"的调用方则显式检测该目录。`git rev-parse --path-format=absolute`能完成同样的工作,但要求 git 2.31 及以上版本。
|
||||
|
||||
在重指`current`之前,安装器会拒绝解析结果等于仓库自身的 staging 路径,以此落实"启动器绝不解析到 master 克隆"这一升级契约。
|
||||
在重指`current`之前,安装器会拒绝解析结果等于仓库自身的 staging 路径,以此落实"启动器绝不解析到 master 克隆"这一升级约定。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**把`~/.dsh/source/master`做成指向该任意克隆的符号链接。** 已否决。Git 会解析该符号链接并记录*真实*路径:经由它创建的 worktree 会存储`gitdir: …/<克隆>/.git/worktrees/<名称>`,而`git worktree list`报告的是该克隆。因此这个符号链接纯属装饰——没有任何代码读取它——却又暗示容器拥有该仓库。它还会静默失效:移动克隆后,`master`看似仍在却已悬空,而每个 staging worktree 都会以`fatal: not a git repository`失败。最糟的是,它把两个名称别名到同一棵树上,于是"current 绝不能是 master 克隆"这项检查会在字符串比较下通过,实则为假。`~/.dsh/source/master`是位置而非名称,且只有位置具有权威性。
|
||||
|
||||
**把检出自身提升为`current`的目标。** 已否决:升级契约要求`current`必须是位于 staging 分支上的干净 staging worktree,绝不能是 feature、review 或 detached 检出。这还会使每次升级都改写贡献者正在编辑的那棵树。
|
||||
**把检出自身提升为`current`的目标。** 已否决:升级约定要求`current`指向 staging 分支上的干净 staging worktree,绝不能指向 feature、review 或 detached 检出。这还会使每次升级都改写贡献者正在编辑的那棵树。
|
||||
|
||||
**把就地链接保留在提示或`DSH_ADOPT`开关之后。** 已否决;本次变更的早期修订版本正是如此实现,之后被移除。分叉的形态本身就是缺陷,因此把它保留为一个选项等于保留了问题,并使此后每次改动需要推敲的状态翻倍——提示、开关、工作树不干净的警告,以及第二条链接路径,全都只为维持一种本不该产生的形态而存在。就地链接最初的动机——让脚本能针对本地源码进行测试——在接管方案下依然成立:以检出的`HEAD`为起点创建的 staging worktree 运行的是同一份代码。`DSH_SOURCE`仍是安装另一棵树的退路。
|
||||
**把就地链接保留在提示或`DSH_ADOPT`开关之后。** 已否决;本次变更的早期修订版本正是如此实现,之后被移除。第二种布局本身就是缺陷,因此把它保留为一个选项等于保留了问题,并使此后每次改动必须处理的状态翻倍——提示、开关、工作树不干净的警告,以及第二条链接路径,全都只为维持一种本不该产生的布局而存在。就地链接最初的动机——让脚本能针对本地源码进行测试——在接管方案下依然成立:以检出的`HEAD`为起点创建的 staging worktree 运行的是同一份代码。`DSH_SOURCE`仍可用于安装另一棵树。
|
||||
|
||||
**在工作树不干净时发出警告或提示。** 已否决:以`HEAD`为起点的`worktree add`本就无法带上未提交的内容,因此该行为是确定的,提示只会增加一个用户无法做出不同选择的决策点。改为在文档中说明该契约。
|
||||
**在工作树不干净时发出警告或提示。** 已否决:以`HEAD`为起点的`worktree add`本就无法带上未提交的内容,因此该行为是确定的,提示只会增加一个用户无法做出不同选择的决策点。改为在文档中说明该约定。
|
||||
|
||||
**把被接管克隆的 staging worktree 放在该克隆旁边**(`~/src/staging-*`),而非放进`~/.dsh/source`。已否决:`current`和 PATH 启动器都是每用户唯一的,因此把 worktree 散落到各个克隆的父目录中,会重新引入 source 容器本就为之而设、意在杜绝的同级克隆蔓延问题。
|
||||
|
||||
## Consequences
|
||||
|
||||
现在一套布局服务于所有安装,因此被接管的克隆无需该技能所述的一次性迁移,即可由`dsh-upgrade`升级,而且安装器不再有任何一条分支会产生无法升级的形态。检出内运行仍然绝不改动工作树。
|
||||
现在一套布局服务于所有安装,因此被接管的克隆无需该技能所述的一次性迁移,即可由`dsh-upgrade`升级,而且安装器不再有任何一条分支会产生无法升级的布局。检出内运行仍然绝不改动工作树。
|
||||
|
||||
代价是:贡献者不能再把 PATH 指向某个检出、并让`dsh`随其切换分支而跟随该工作树;启动器现在解析到的是一个固定在安装时所接管`HEAD`上的 staging worktree。重新运行安装器会再次接管当前的`HEAD`。
|
||||
|
||||
@@ -46,6 +47,6 @@ Status: implemented
|
||||
|
||||
## Testing
|
||||
|
||||
`scripts/install.sh`没有自动化测试,本次变更也未添加:用户明确要求把`install.spec.ts`排除在范围之外。这是一条已交付的、面向用户的安装路径上的已知缺口,而上文那个`/var`解析缺陷,恰恰属于测试本应最先捕获的那类 bug。相应地,要求把这套流程迁移到有测试覆盖的 TypeScript 入口的既有[`FIXME(install-ts)`](../../../../scripts/install.sh)也变得更为紧迫。
|
||||
`scripts/install.sh` 现有一套位于 `apps/cli/tests/install-script.spec.ts` 的真实 shell PTY 回归测试,使用 stub 依赖覆盖接管路径和 curl 风格路径。curl 风格安装默认使用公开的 `deepseek-ai/deepseek-harness-sdk` 源,而以 pnpm/npx 替换安装器仍是另一项工作。
|
||||
|
||||
验证是手工完成的,通过一个一次性测试装置以打桩的`pnpm`驱动真实脚本:接管独立克隆;从 linked worktree 接管进其已有容器;显式`DSH_SOURCE`仍回到克隆路径;工作树不干净时静默接管、既不提示也不警告,且其未提交文件留在原处;非 git 检出失败并给出指引;以及`curl`式克隆安装断言所构建的布局——正是这项回归测试捕获了`REPO_ROOT`未解析的缺陷。交互路径在 tmux 下从一个不干净的检出走通,确认整个过程不出现接管提示即可到达启动器,最终`dsh`从新的 staging worktree 运行,而原检出保持其分支不变、未提交文件仍在。
|
||||
验证是手工完成的,通过一个一次性测试装置以打桩的`pnpm`驱动真实脚本:接管独立克隆;从 linked worktree 接管进其已有容器;显式`DSH_SOURCE`仍回到克隆路径;工作树不干净时静默接管、既不提示也不警告,且其未提交文件留在原处;非 git 检出失败并给出指引;以及`curl`式克隆安装断言所构建的布局——该检查能捕获未解析的`REPO_ROOT`。交互路径在 tmux 下从一个不干净的检出走通,确认整个过程不出现接管提示即可到达启动器,最终`dsh`从新的 staging worktree 运行,而原检出保持其分支不变、未提交文件仍在。
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-04-forward-only-pr-issue-status.md
|
||||
2026-08-04-forward-only-pr-issue-status.md: 56004a39ce52c77429574f481d9945cdc4936d30
|
||||
2026-08-04-forward-only-pr-issue-status.zh.md: ee85319842d3245bdfab9668de0a42ab29597fac
|
||||
@@ -0,0 +1,40 @@
|
||||
# Agent Note: Forward-only PR-to-Issue status projection
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
English | [中文](2026-08-04-forward-only-pr-issue-status.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The Issue Project status represents the phase of the work, while an exact same-repository resolving keyword establishes the authoritative PR-to-Issue relationship. Restricting lifecycle advancement to Issues already in `Ready` leaves an Issue in `Inbox` or `Backlog` after implementation has demonstrably started. Requiring otherwise valid PR metadata before projecting the phase also conflates policy compliance with the work's observable state.
|
||||
|
||||
## Decision
|
||||
|
||||
PR and PR-review events project the current PR phase to every exact same-repository resolving Issue. A draft PR, or a non-draft PR without a review request or submitted review, targets `In progress`. A non-draft PR with either form of review activity targets `In review`.
|
||||
|
||||
The active statuses have the order `Inbox`, `Backlog`, `Ready`, `In progress`, and `In review`. Projection writes only when the target is later in that order. It does not move an Issue backward, alter `Done` or `No action`, or add an Issue that has no Project status. The lifecycle path is independent of PR metadata validation; the separate required PR policy check continues to enforce labels, references, and priority consistency.
|
||||
|
||||
This projection is intentionally one-way. It does not query from an Issue to related PRs, and it does not add a scheduled reconciler. PR events are the source of lifecycle advancement. The pure transition decision is exercised by the Issue-management test and that test runs in the `check-all`, `ci-primary`, and `ci-static` gates.
|
||||
|
||||
## Verification
|
||||
|
||||
`.github/issue-management/policy.test.mjs` covers advancement from every earlier active status, the draft and review distinctions, metadata-policy independence, and protection against backward or terminal transitions. `scripts/run-gates.ts` owns execution of that focused policy test in top-level local and CI gate modes.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Require `Ready` as the only source status.** This preserves a manual prerequisite but leaves stale `Inbox` and `Backlog` items even though the resolving PR proves implementation has begun.
|
||||
|
||||
**Add bidirectional or scheduled reconciliation.** Looking up PRs from Issue events or sweeping the Project could repair more histories, but it adds another authority direction and recurring API work beyond the required PR-driven lifecycle.
|
||||
|
||||
**Gate projection on complete PR metadata.** Labels, references, and priority still require enforcement, but a metadata defect does not make the implementation or review phase untrue.
|
||||
|
||||
**Move statuses backward when a PR becomes a draft or loses reviewers.** That would make transient PR state overwrite a later observed work phase and complicate status ownership. Projection therefore remains monotonic.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A PR event self-corrects a resolving Issue left in `Inbox`, `Backlog`, or `Ready`.
|
||||
- An Issue created after the last relevant PR event waits for a later PR event or a manual status update because there is no reverse lookup or scheduled sweep.
|
||||
- A draft PR remains `In progress` even if it has historical review activity; only a non-draft PR targets `In review`.
|
||||
- Terminal statuses and later active statuses remain protected from regression.
|
||||
- PR metadata failures remain visible through the required policy check without suppressing lifecycle projection.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Agent Note: PR 到 Issue 的状态仅向前投射
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
[English](2026-08-04-forward-only-pr-issue-status.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
Issue Project 状态表示工作所处阶段,同仓库内精确匹配的解决型关键字引用则建立权威的 PR(Pull Request)到 Issue 关系。若仅允许已处于 `Ready` 的 Issue 推进生命周期,即使实现已经明确开始,处于 `Inbox` 或 `Backlog` 的 Issue 仍会停留在原状态。只有 PR 元数据在其他方面均有效时才投射工作阶段,也会把政策合规性与可观察到的工作状态混为一谈。
|
||||
|
||||
## 决策
|
||||
|
||||
PR 事件和 PR 评审事件会把当前 PR 阶段投射到同仓库内由解决型关键字精确引用的每个 Issue。草稿 PR,或既没有评审请求也没有已提交评审的非草稿 PR,目标状态为 `In progress`。具备上述任一类评审活动的非草稿 PR,目标状态为 `In review`。
|
||||
|
||||
活跃状态依次为 `Inbox`、`Backlog`、`Ready`、`In progress` 和 `In review`。只有目标状态在该顺序中位于当前状态之后时,投射才会写入。投射不会把 Issue 状态向后移动,不会改动 `Done` 或 `No action`,也不会把没有 Project 状态的 Issue 加入 Project。生命周期路径独立于 PR 元数据校验;另行执行的必需 PR 政策检查继续强制落实标签、引用和优先级一致性。
|
||||
|
||||
这项投射刻意保持单向。它不会从 Issue 反查关联 PR,也不会添加定时对账任务。PR 事件是推进生命周期的来源。Issue 管理测试会验证纯函数实现的状态转换决策,并且该测试会在 `check-all`、`ci-primary` 和 `ci-static` 门禁中运行。
|
||||
|
||||
## 验证
|
||||
|
||||
`.github/issue-management/policy.test.mjs` 覆盖从所有更早活跃状态推进、区分草稿与评审状态、独立于元数据政策,以及防止状态倒退或改动终态。`scripts/run-gates.ts` 负责在顶层本地门禁模式和 CI 门禁模式中执行这项专项政策测试。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**仅允许从 `Ready` 状态推进。** 这种方案保留了人工前置条件,但解决型 PR 已经证明实现开始后,仍会让处于 `Inbox` 和 `Backlog` 的条目保持陈旧状态。
|
||||
|
||||
**增加双向或定时对账。** 由 Issue 事件反查 PR,或定期扫描 Project,可以修复更多历史遗留状态;但这会新增一条反向的权威状态更新路径,并增加周期性 API 工作量,超出所需的 PR 驱动生命周期范围。
|
||||
|
||||
**以完整的 PR 元数据作为投射前提。** 标签、引用和优先级仍须强制落实,但元数据缺陷并不能否定工作实际处于实现或评审阶段。
|
||||
|
||||
**PR 转为草稿或失去评审人时将状态向后移动。** 这会让临时的 PR 状态覆盖已经观察到的更靠后工作阶段,也会使状态所有权更复杂。因此,投射保持单调。
|
||||
|
||||
## 后果
|
||||
|
||||
- PR 事件会自动纠正由该 PR 解决但仍停留在 `Inbox`、`Backlog` 或 `Ready` 的 Issue。
|
||||
- 若 Issue 创建于最后一个相关 PR 事件之后,则必须等待后续 PR 事件或人工更新状态,因为系统不会反向查找或定时扫描。
|
||||
- 即使存在历史评审活动,草稿 PR 仍保持 `In progress`;只有非草稿 PR 才会以 `In review` 为目标状态。
|
||||
- 终态以及顺序中更靠后的活跃状态不会倒退。
|
||||
- 必需的政策检查仍会暴露 PR 元数据错误,而不会因此阻止生命周期投射。
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-review-driven-issue-lifecycle-triggers.md
|
||||
2026-08-08-review-driven-issue-lifecycle-triggers.md: 444927968912d93f473e27ae8576e8371b9c287c
|
||||
2026-08-08-review-driven-issue-lifecycle-triggers.zh.md: 6e00e2a936b6421824743e779756011fcd4a1c9e
|
||||
@@ -0,0 +1,32 @@
|
||||
# Agent Note: Review-driven Issue lifecycle triggers
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
English | [中文](2026-08-08-review-driven-issue-lifecycle-triggers.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The Issue lifecycle workflow reads the current pull request after each subscribed repository event and projects resolving Issues forward to `In progress` or `In review`. A resolving draft already reaches `In progress` from its `opened` event. Changing that draft to ready creates no new lifecycle outcome until a reviewer is requested or submits a review, yet subscribing to `ready_for_review` launches another hosted job and creates another GitHub App token.
|
||||
|
||||
Draft-to-ready automation commonly submits a review moments later. In that sequence the ready job cannot advance the Issue, while the review job is still required to observe the `In review` phase.
|
||||
|
||||
## Decision
|
||||
|
||||
[Issue lifecycle](../../../../.github/workflows/issue-lifecycle.yml) does not subscribe to `pull_request.ready_for_review`. It retains `pull_request.review_requested` and `pull_request_review.submitted`, so either a requested reviewer or a submitted review can advance a resolving Issue to `In review`. The handler continues to fetch the live pull request instead of deriving phase from the triggering payload.
|
||||
|
||||
[Issue policy](../../../../.github/workflows/issue-policy.yml) still subscribes to `ready_for_review`. That workflow owns the required check when a human pull request enters review; removing a lifecycle trigger does not weaken policy enforcement.
|
||||
|
||||
The workflow test parses both files and pins this split. The lifecycle policy tests separately pin that draft and open resolving pull requests reach `In progress`, while a review request or submitted review reaches `In review`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Keep both events and cancel an in-progress run** - rejected because concurrency can discard a pending run but cannot combine two webhook payloads into one execution. Cancelling the earlier mutation also makes correctness depend on arrival order, while a completed ready job still consumes the full runner setup.
|
||||
- **Remove the submitted-review event** - rejected because a review may arrive without an explicit review request. In that path `pull_request_review.submitted` is the only repository event that exposes the transition to `In review`.
|
||||
- **Delay every pull request event behind a debounce dispatcher** - rejected because another queue or scheduled workflow adds latency and control-plane state to eliminate a trigger that carries no lifecycle information.
|
||||
|
||||
## Consequences
|
||||
|
||||
A draft becoming ready no longer launches Issue lifecycle work. The resolving Issue remains `In progress` from an earlier pull request event until a review is requested or submitted, at which point one review-driven run can advance it to `In review`. The required Issue policy check still runs at the ready boundary.
|
||||
|
||||
If a future lifecycle phase depends on ready status itself, that change must restore the trigger and update the workflow test and this decision. Until then, omitting `ready_for_review` saves one hosted run from the common ready-then-review sequence without dropping a status transition.
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Agent Note: 由评审驱动的 Issue 生命周期触发器
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-10
|
||||
|
||||
[English](2026-08-08-review-driven-issue-lifecycle-triggers.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
Issue 生命周期工作流会在每个已订阅的仓库事件发生后读取当前 PR(Pull Request),并将解决型 Issue 的状态向前推进到 `In progress` 或 `In review`。解决型草稿 PR 已通过其 `opened` 事件进入 `In progress`。在请求评审人或评审人提交评审之前,把该草稿转为可评审状态不会产生新的生命周期结果;但订阅 `ready_for_review` 仍会启动另一个托管作业,并创建另一个 GitHub App token。
|
||||
|
||||
草稿转为可评审状态的自动化通常会在片刻后提交评审。在这一事件序列中,转为可评审状态的作业无法推进 Issue,而要观察到 `In review` 阶段,仍必须运行评审作业。
|
||||
|
||||
## 决策
|
||||
|
||||
[Issue 生命周期](../../../../.github/workflows/issue-lifecycle.yml)不订阅 `pull_request.ready_for_review`。它保留 `pull_request.review_requested` 和 `pull_request_review.submitted`,因此无论是请求评审人还是提交评审,都可以将解决型 Issue 推进至 `In review`。处理程序仍会获取实时 PR,而不是根据触发事件的载荷推导阶段。
|
||||
|
||||
[Issue 政策](../../../../.github/workflows/issue-policy.yml)仍订阅 `ready_for_review`。该工作流负责在由人类发起的 PR 进入评审时执行必需检查;移除生命周期触发器不会削弱政策执行。
|
||||
|
||||
工作流测试会解析这两个文件,并固定这种划分。生命周期政策测试另行固定以下行为:草稿及开放状态的解决型 PR 会进入 `In progress`,评审请求或已提交评审则会使其进入 `In review`。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
- **保留两个事件并取消正在进行的工作流运行**:不予采纳,因为并发控制可以丢弃待处理的工作流运行,却无法把两个 webhook 载荷合并为一次执行。取消较早的状态变更操作也会使正确性依赖事件到达顺序;而已经完成的转为可评审状态作业仍会产生完整的运行器初始化开销。
|
||||
- **移除已提交评审事件**:不予采纳,因为评审可能在没有明确评审请求的情况下直接提交。在这条路径中,`pull_request_review.submitted` 是唯一能让系统观察到进入 `In review` 这一状态转换的仓库事件。
|
||||
- **让每个 PR 事件都先经过防抖分派器再处理**:不予采纳,因为新增一条队列或一个定时工作流会引入延迟和控制平面状态,只为消除一个不携带生命周期信息的触发器。
|
||||
|
||||
## 后果
|
||||
|
||||
草稿转为可评审状态后,不再启动 Issue 生命周期工作。解决型 Issue 会保持在更早的 PR 事件所设定的 `In progress`,直到请求或提交评审;届时,一次由评审驱动的工作流运行即可将其推进至 `In review`。必需的 Issue 政策检查仍会在转为可评审状态的边界运行。
|
||||
|
||||
如果未来某个生命周期阶段依赖可评审状态本身,相关变更必须恢复该触发器,并更新工作流测试和本决策。在此之前,省略 `ready_for_review` 可使常见的先转为可评审状态、再提交评审这一序列少启动一次托管工作流运行,而不会遗漏状态转换。
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/simplification/2026-07-26-eventsource-parser-for-deepseek-sse.md
|
||||
2026-07-26-eventsource-parser-for-deepseek-sse.md: 9e716cf9556c9c2d8cdf6cb85c6908d45a127a0f
|
||||
2026-07-26-eventsource-parser-for-deepseek-sse.zh.md: 16c63ddc9646f6309654910bd80801bd20005545
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Replace the hand-rolled SSE parser in llm-deepseek with eventsource-parser
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-26-eventsource-parser-for-deepseek-sse.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 用 eventsource-parser 替换 llm-deepseek 中手写的 SSE 解析器
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-26-eventsource-parser-for-deepseek-sse.md) | 中文
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/simplification/2026-07-26-turndown-for-tool-web-html-markdown.md
|
||||
2026-07-26-turndown-for-tool-web-html-markdown.md: 46c4eba12c782146aa32df245c5f69567723935a
|
||||
2026-07-26-turndown-for-tool-web-html-markdown.zh.md: 077ea89c54c63e41bec2aabd15e744b7b8a764da
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Replace tool-web's regex HTML-to-markdown converter with turndown
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-26-turndown-for-tool-web-html-markdown.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 用 turndown 替换 tool-web 的正则 HTML 转 markdown 转换器
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-26-turndown-for-tool-web-html-markdown.md) | 中文
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/simplification/2026-07-30-sidebar-resize-without-visible-pill.md
|
||||
2026-07-30-sidebar-resize-without-visible-pill.md: 50bf43564675540df2db8e88530a82177f551407
|
||||
2026-07-30-sidebar-resize-without-visible-pill.zh.md: 41a33e039aaa29813d4232e5a6d36141b55ce947
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Sidebar resize without a visible pill
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-30-sidebar-resize-without-visible-pill.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 侧边栏缩放不显示胶囊
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-30-sidebar-resize-without-visible-pill.md) | 中文
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/simplification/2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.md
|
||||
2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.md: 8ef2b7deb103f2e2a9147b4b50c7d39936ed2381
|
||||
2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.zh.md: 875e44a06e1cebf4f2b1731909c479b2f06fb06a
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web UI drops steer entry and interjection chrome
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Web UI 去掉 steer 入口与插话 chrome
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.md) | 中文
|
||||
|
||||
+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 .agents/notes/implemented/simplification/2026-08-03-explicit-config-dsh-entrypoint.md
|
||||
2026-08-03-explicit-config-dsh-entrypoint.md: bbb40babf8abca726126678f4bccb40a63160568
|
||||
2026-08-03-explicit-config-dsh-entrypoint.zh.md: a97221a73bab0b181562ddfb7ddab1211f87f168
|
||||
2026-08-03-explicit-config-dsh-entrypoint.md: 4474e786b3a99ff0ee81ac54fcb6eb5aaff5ee04
|
||||
2026-08-03-explicit-config-dsh-entrypoint.zh.md: 11b7b27c7560aaa43a9fdcfb12a9a17f9433e619
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Explicit-config dsh entrypoint
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-08
|
||||
|
||||
English | [中文](2026-08-03-explicit-config-dsh-entrypoint.zh.md)
|
||||
|
||||
@@ -20,7 +21,7 @@ The CLI no longer ships a TUI application. Its TUI overlay, launcher, first-run
|
||||
|
||||
`dsh web` retains the shared base plus Web overlay and personal-or-explicit user layer. `dsh -p` retains the one-shot Web/headless composition. The reusable TUI package initially remained after this entrypoint change, then [the package-wide removal decision](2026-08-04-remove-tui-package.md) deleted it and its SDK interface.
|
||||
|
||||
This decision supersedes the `dsh`-specific parts of the [dedicated TUI front door](../../archived/feature/2026-07-17-dedicated-full-screen-tui-front-door.md), [personal config](../feature/2026-07-20-dsh-cli-personal-config.md), [guided skill commands](../../archived/feature/2026-07-28-dsh-guided-skill-session-commands.md), [meta workspace](../../archived/feature/2026-07-28-dsh-meta-source-workspace.md), [shared config overlays](2026-07-29-shared-base-config-overlays.md), [config dump](../feature/2026-07-30-dsh-dump-config.md), [first-run welcome](../../archived/feature/2026-07-30-versioned-tui-first-run-welcome.md), and [experimental subcommand gate](../../archived/feature/2026-07-31-experimental-subcommand-gate.md) notes. The later [package-wide removal decision](2026-08-04-remove-tui-package.md) supersedes their reusable-package decisions and consolidates the deleted launcher-identity record.
|
||||
This decision supersedes the `dsh`-specific parts of the [dedicated TUI front door](../../archived/feature/2026-07-17-dedicated-full-screen-tui-front-door.md), [personal config](../feature/2026-07-20-dsh-cli-personal-config.md), [guided skill commands](../../archived/feature/2026-07-28-dsh-guided-skill-session-commands.md), [meta workspace](../../archived/feature/2026-07-28-dsh-meta-source-workspace.md), [shared config overlays](2026-07-29-shared-base-config-overlays.md), [config dump](../../archived/feature/2026-07-30-dsh-dump-config.md), [first-run welcome](../../archived/feature/2026-07-30-versioned-tui-first-run-welcome.md), and [experimental subcommand gate](../../archived/feature/2026-07-31-experimental-subcommand-gate.md) notes. The later [package-wide removal decision](2026-08-04-remove-tui-package.md) supersedes their reusable-package decisions and consolidates the deleted launcher-identity record.
|
||||
|
||||
## Verification
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
# Agent Note:显式配置的 dsh 入口
|
||||
# Agent Note: 显式配置的 dsh 入口
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-08
|
||||
|
||||
[English](2026-08-03-explicit-config-dsh-entrypoint.md) | 中文
|
||||
|
||||
@@ -20,7 +21,7 @@ CLI 不再交付 TUI 应用。TUI overlay、启动器、首次运行 onboarding
|
||||
|
||||
`dsh web` 保留共享 base、Web overlay 与个人或显式用户层。`dsh -p` 保留一次性 Web/headless 组合。可复用 TUI 包(package)在本入口变更后起初保留,随后[全包移除决策](2026-08-04-remove-tui-package.md)将其及 SDK 接口删除。
|
||||
|
||||
本决策取代以下记录中专用于 `dsh` 的部分:[独立 TUI 入口](../../archived/feature/2026-07-17-dedicated-full-screen-tui-front-door.md)、[个人配置](../feature/2026-07-20-dsh-cli-personal-config.md)、[引导式 skill 命令](../../archived/feature/2026-07-28-dsh-guided-skill-session-commands.md)、[meta workspace](../../archived/feature/2026-07-28-dsh-meta-source-workspace.md)、[共享配置 overlay](2026-07-29-shared-base-config-overlays.md)、[配置转储](../feature/2026-07-30-dsh-dump-config.md)、[首次运行欢迎页](../../archived/feature/2026-07-30-versioned-tui-first-run-welcome.md)和[实验性子命令门禁](../../archived/feature/2026-07-31-experimental-subcommand-gate.md)。后续的[全包移除决策](2026-08-04-remove-tui-package.md)取代了其中关于可复用包的决策,并整合了已删除的启动器身份记录。
|
||||
本决策取代以下记录中专用于 `dsh` 的部分:[独立 TUI 入口](../../archived/feature/2026-07-17-dedicated-full-screen-tui-front-door.md)、[个人配置](../feature/2026-07-20-dsh-cli-personal-config.md)、[引导式 skill 命令](../../archived/feature/2026-07-28-dsh-guided-skill-session-commands.md)、[meta workspace](../../archived/feature/2026-07-28-dsh-meta-source-workspace.md)、[共享配置 overlay](2026-07-29-shared-base-config-overlays.md)、[配置转储](../../archived/feature/2026-07-30-dsh-dump-config.md)、[首次运行欢迎页](../../archived/feature/2026-07-30-versioned-tui-first-run-welcome.md)和[实验性子命令门禁](../../archived/feature/2026-07-31-experimental-subcommand-gate.md)。后续的[全包移除决策](2026-08-04-remove-tui-package.md)取代了其中关于可复用包的决策,并整合了已删除的启动器身份记录。
|
||||
|
||||
## 验证
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/archived/testing/2026-07-26-execa-for-test-subprocess-plumbing.md
|
||||
2026-07-26-execa-for-test-subprocess-plumbing.md: ca5edc50bf17a34809037462f8e9603b8ed28e74
|
||||
2026-07-26-execa-for-test-subprocess-plumbing.zh.md: 186e5cd560b6500364c855438d6c3ff18206b52b
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: Adopt execa for hand-rolled test subprocess plumbing
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
English | [中文](2026-07-26-execa-for-test-subprocess-plumbing.zh.md)
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
# Agent Note: 采用 execa 替换手写的测试子进程管道代码
|
||||
|
||||
Status: implemented
|
||||
Archived: 2026-08-07
|
||||
|
||||
[English](2026-07-26-execa-for-test-subprocess-plumbing.md) | 中文
|
||||
|
||||
@@ -10,4 +10,4 @@ When a shipped note is unlikely to guide future work, archive its complete tripl
|
||||
|
||||
### This is not a license to rewrite the *decision*
|
||||
|
||||
Update factual realization in place. A reversal of the decision or its rationale requires a new Agent Note and cross-link; a fully superseded old note may be deleted only through the consolidation rule in the [Agent Note contract](../README.md).
|
||||
Update factual realization in place. A reversal of the decision or its rationale requires a new Agent Note and cross-link; a fully superseded old note may be deleted only through the consolidation rule in the [Agent Note rules](../README.md).
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.md
|
||||
2026-06-11-content-block-vocabulary.md: 5228724bb9101307db9929aaf7831b477c2a6022
|
||||
2026-06-11-content-block-vocabulary.zh.md: ffcbbc13dfe9176941f4838b0078d3850403a16c
|
||||
2026-06-11-content-block-vocabulary.zh.md: b43de335917c6b5304a94b296fcb8253827b0600
|
||||
|
||||
@@ -12,7 +12,7 @@ harness 需要一套统一的内部消息语言,供 agent loop(智能体循
|
||||
|
||||
自主拥有词汇:消息是类型化内容块的数组(`text`、`reasoning`、`tool-call`、`tool-result`),其联合类型派生自可合并扩展的 `ContentBlockMap`,插件通过声明合并添加新的块类型。同一可合并扩展映射模式为所有「字符串化」字段提供类型(`MessageSource`、`FinishReason`、`TurnTrigger`、`TurnEndReason`)。流式输出采用原始分片协议;`BlockAssembler` 是唯一的共享组装实现。适配器负责转换为提供方的协议格式(wire format)——映射成本留在适配器中,正是它该在的地方。
|
||||
|
||||
会话内上下文注入(`context/message`)和轮次中途 steering 最初渲染为带标签的 user-role 信封(system-reminder 模式),而非引入新角色,因此适配器无需承担额外负担。如今两者都投影为无包装的普通用户内容;见[注入内容信封 Agent Note](../simplification/2026-07-20-unwrap-injected-content-envelopes.md)。实际适配器验证已确认此渲染方式符合当前 DeepSeek 的行为;如果未来某提供方出现不兼容,应在该适配器内处理,而非引入新的规范角色。
|
||||
会话内上下文注入(`context/message`)和轮次中途 steering(中途引导)最初渲染为带标签的 user-role 信封(system-reminder 模式),而非引入新角色,因此适配器无需承担额外负担。如今两者都投影为无包装的普通用户内容;见[注入内容信封 Agent Note](../simplification/2026-07-20-unwrap-injected-content-envelopes.md)。实际适配器验证已确认此渲染方式符合当前 DeepSeek 的行为;如果未来某提供方出现不兼容,应在该适配器内处理,而非引入新的规范角色。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
|
||||
+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 .agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md
|
||||
2026-06-11-dev-invariants-over-deep-readonly.md: f1a741927cf63b43b1aaf558efc148ff80d2d881
|
||||
2026-06-11-dev-invariants-over-deep-readonly.zh.md: 9aadde4f4452a53acce8a3bcf58560ee9722ecfb
|
||||
2026-06-11-dev-invariants-over-deep-readonly.md: 66980f1ee09c6112f72786d6c3a147aadbc57f6c
|
||||
2026-06-11-dev-invariants-over-deep-readonly.zh.md: 38a19ee7dd63823b5a5e103908410786c20435c6
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ English | [中文](2026-06-11-dev-invariants-over-deep-readonly.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The session log needs two different protections: immutable ownership of each stored fact, and checks for relationships among facts across time and service seams. Conflating them in an optional development plugin would leave production history vulnerable; trying to express both through TypeScript readonly types would not create a runtime boundary or describe relational rules.
|
||||
The session log needs two different protections: immutable ownership of each stored fact, and checks for relationships among facts across time and service contracts. Conflating them in an optional development plugin would leave production history vulnerable; trying to express both through TypeScript readonly types would not create a runtime boundary or describe relational rules.
|
||||
|
||||
The session log is the durable source of truth for replay, request reconstruction, persistence, and user-visible history. Code outside the session package must be able to inspect that history without retaining a reference that can rewrite it later, and inputs accepted from callers must not remain connected to caller-owned mutable objects.
|
||||
|
||||
|
||||
+2
-2
@@ -6,11 +6,11 @@ Status: implemented
|
||||
|
||||
## 问题
|
||||
|
||||
会话日志需要两种不同的保护:对每条已存储事实的不可变所有权,以及对跨时间和服务 seam 的事实之间关系的检查。如果将二者混为一个可选的开发插件,生产环境的历史记录将失去保护;如果试图通过 TypeScript readonly 类型同时表达两者,既无法建立运行时边界,也无法描述关系规则。
|
||||
会话日志需要两种不同的保护:对每条已存储事实的不可变所有权,以及对跨时间和服务约定的事实之间关系的检查。如果将二者混为一个可选的开发插件,生产环境的历史记录将失去保护;如果试图通过 TypeScript readonly 类型同时表达两者,既无法建立运行时边界,也无法描述关系规则。
|
||||
|
||||
会话日志是回放、请求重建、持久化与用户可见历史的持久真源。会话包外部的代码必须能检视历史,但不能保留一个可在之后改写历史的引用;从调用方接受的输入也不能继续连接到调用方拥有的可变对象。
|
||||
|
||||
单个值的不可变性只是契约的一半。一份日志可以包含完全不可变的记录,但其序列、轮次/步骤嵌套、工具调用配对、作用域分发或重建的模型请求是错误的。这些规则涉及多条记录或多个服务,无法通过冻结单个对象来建立。
|
||||
单个值的不可变性只是约定的一半。一份日志可以包含完全不可变的记录,但其序列、轮次/步骤嵌套、工具调用配对、作用域分发或重建的模型请求是错误的。这些规则涉及多条记录或多个服务,无法通过冻结单个对象来建立。
|
||||
|
||||
TypeScript readonly 类型不是充分的运行时边界。它们在程序运行时消失,类型转换可以绕过它们,而递归的 `DeepReadonly<T>` 会扩散到每个日志和消息消费方,尽管某些下游请求处理 API 有意使用可变值。
|
||||
|
||||
|
||||
@@ -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/architecture/2026-06-11-event-sourced-sessions.md
|
||||
2026-06-11-event-sourced-sessions.md: 01f9628c1cfc000aca8654caf5edeff09411fdcc
|
||||
2026-06-11-event-sourced-sessions.zh.md: ec5c3e766dfa97c5612827023c5cc66bf01a8e6c
|
||||
2026-06-11-event-sourced-sessions.md: 1303ee843cd5f9e02eae7a1f9bc51594be4bb407
|
||||
2026-06-11-event-sourced-sessions.zh.md: d1800ab3820ba5fc60723d6851fe71eded38134f
|
||||
|
||||
@@ -25,4 +25,4 @@ Ordering contract: the loop claims inbox messages before `agent/pre-step`, opens
|
||||
- Replay, trace, and telemetry are structurally guaranteed, not bolted on.
|
||||
- Persistence stays a plugin concern; the in-memory store ships in dsh-session.
|
||||
- The event vocabulary is merge-extensible (plugins add e.g. compaction events); [session persistence](2026-06-14-session-persistence.md) froze its shape once the log became durable.
|
||||
- Derivation cost grows with log length — compaction (future plugin) is the intended mitigation, not log mutation.
|
||||
- Derivation cost grows with log length — compaction (dsh-compact) is the intended mitigation, not log mutation.
|
||||
|
||||
@@ -14,7 +14,7 @@ MVP 要求严格的基于事件的追踪,以及完全可回放的会话(严
|
||||
|
||||
追加操作是同步的(热路径从不阻塞于 I/O);`session/event` 是同步通知;持久化插件在后台缓冲写入,并在每个轮次结束时触发的 `session/flush` 检查点处等待排空。
|
||||
|
||||
顺序契约:agent loop(智能体循环)先领取 inbox 消息,再运行 `agent/pre-step`;只有 enter 决策才打开 `step/start`,随后在请求派生前追加返回的 `user/message` 批次。提供方输出组装并以 `assistant/message` 追加后才分派工具,因此持久日志记录工具实际遵循的确切消息。回归测试固定了这一顺序。
|
||||
顺序约定:agent loop(智能体循环)先领取 inbox 消息,再运行 `agent/pre-step`;只有 enter 决策才打开 `step/start`,随后在请求派生前追加返回的 `user/message` 批次。提供方输出组装并以 `assistant/message` 追加后才分派工具,因此持久日志记录工具实际遵循的确切消息。回归测试固定了这一顺序。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
@@ -25,4 +25,4 @@ MVP 要求严格的基于事件的追踪,以及完全可回放的会话(严
|
||||
- 回放、追踪与遥测在结构上得到保证,而非事后附加。
|
||||
- 持久化仍是插件关注点;内存存储随 dsh-session 一起提供。
|
||||
- 事件词汇可通过合并扩展(插件可添加如压缩(compaction)事件);[会话持久化](2026-06-14-session-persistence.md)在日志具备持久性后固定了其结构。
|
||||
- 派生成本随日志长度增长,压缩(未来插件)是预期的缓解手段,而非日志变更。
|
||||
- 派生成本随日志长度增长,压缩(dsh-compact)是预期的缓解手段,而非日志变更。
|
||||
|
||||
+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 .agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.md
|
||||
2026-06-11-microkernel-event-taxonomy.md: 202595fed125966a5d77920536e7f4ee88f875fe
|
||||
2026-06-11-microkernel-event-taxonomy.zh.md: 899c96d86cb7e37d90df349ce5f3f932e0a72f95
|
||||
2026-06-11-microkernel-event-taxonomy.md: 6cacbc30f29c863de0f8ddfa4df7251a64bc80ca
|
||||
2026-06-11-microkernel-event-taxonomy.zh.md: 40edfa1ada0c11395e8ff29e0abfcb0c38de096a
|
||||
|
||||
@@ -10,14 +10,14 @@ The product principle is "everything is a plugin": hooks, /goal, /loop, dynamic
|
||||
|
||||
## Decision
|
||||
|
||||
Pure Cordis event taxonomy. The loop's extension seams are typed events with deliberate dispatch modes:
|
||||
Pure Cordis event taxonomy. The loop's extension points are typed events with deliberate dispatch modes:
|
||||
|
||||
- **waterfall** (around-middleware) where plugins transform, veto, recover, or wrap: `agent/pre-step`, `agent/request`, `agent/request-error`, `tools/pre-execute`, `tools/execute`, `tools/post-execute`, `llm/stream`, `system-prompt/assemble`.
|
||||
- **waterfall** (around-middleware) where plugins transform, short-circuit, recover, or wrap: `agent/pre-step`, `agent/request`, `agent/request-error`, `tools/pre-execute`, `tools/execute`, `tools/post-execute`, `llm/stream`, `system-prompt/assemble`.
|
||||
- **serial** (awaited in listener order) for ordered checkpoints such as `agent/turn-stopping`.
|
||||
- **parallel** (awaited fan-out) where every listener must get an independent chance: the `session/flush` durability checkpoint.
|
||||
- **emit** (synchronous fire-and-forget) for notifications: inbox transitions, lifecycle, errors, and the contained immutable `tools/result` observation. Durable session events own turn and step boundaries.
|
||||
|
||||
The event vocabulary lives in interface packages (dsh-agent declares the agent/* events); `@deepseek-ai/dsh-agent-loop` is the only concrete loop plugin and is itself swappable — nothing outside it may depend on it.
|
||||
The event vocabulary lives in contract packages (`dsh-agent` declares the `agent/*` events); `@deepseek-ai/dsh-agent-loop` is the only concrete loop plugin and is itself swappable — nothing outside it may depend on it.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
@@ -28,4 +28,4 @@ The event vocabulary lives in interface packages (dsh-agent declares the agent/*
|
||||
- Every MVP feature maps to a listener (the [feature → mechanism map](../../../../docs/cookbook/extension-cookbook.md#the-feature--mechanism-map) is the proof obligation, kept current).
|
||||
- HMR and disposal come free: listeners and registrations are Cordis effects.
|
||||
- Waterfall semantics (call `next()` or short-circuit) are non-obvious and must be taught — documented in AGENTS.md and covered by composition tests.
|
||||
- The loop must be defensive: plugin exceptions are contained at turn level, steering from any seam is never stranded (regression-tested).
|
||||
- The loop must be defensive: plugin exceptions are contained at turn level, steering from any extension point is never stranded (regression-tested).
|
||||
|
||||
@@ -10,14 +10,14 @@ Status: implemented
|
||||
|
||||
## 决策
|
||||
|
||||
纯 Cordis 事件分类体系。agent loop(智能体循环)的扩展 seam 是带类型的事件,具有明确的分发模式:
|
||||
纯 Cordis 事件分类体系。agent loop(智能体循环)的扩展点是带类型的事件,具有明确的分发模式:
|
||||
|
||||
- **waterfall(瀑布式事件)**(around-middleware):插件可变换、否决、恢复或包装:`agent/pre-step`、`agent/request`、`agent/request-error`、`tools/pre-execute`、`tools/execute`、`tools/post-execute`、`llm/stream`、`system-prompt/assemble`。
|
||||
- **waterfall(瀑布式事件)**(around-middleware):插件可变换、短路、恢复或包装:`agent/pre-step`、`agent/request`、`agent/request-error`、`tools/pre-execute`、`tools/execute`、`tools/post-execute`、`llm/stream`、`system-prompt/assemble`。
|
||||
- **serial**(按监听器顺序依次 await):用于 `agent/turn-stopping` 等有序检查点。
|
||||
- **parallel**(await 扇出):每个监听器都必须获得独立执行的机会:`session/flush` 持久性检查点。
|
||||
- **emit**(同步 fire-and-forget):用于 inbox 转换、生命周期、错误,以及包含不可变 `tools/result` 观测的事件。轮次与步骤边界由持久会话事件拥有。
|
||||
- **emit**(同步 fire-and-forget):用于通知:inbox 转换、生命周期、错误,以及受错误隔离的 `tools/result` 观测;该观测接收不可变的最终结果。轮次与步骤边界由持久会话事件拥有。
|
||||
|
||||
事件词汇定义在接口包中(dsh-agent 声明 agent/* 事件);`@deepseek-ai/dsh-agent-loop` 是唯一的具体循环插件,且自身可替换——外部不得依赖它。
|
||||
事件词汇定义在约定包中(`dsh-agent` 声明 `agent/*` 事件);`@deepseek-ai/dsh-agent-loop` 是唯一的具体循环插件,且自身可替换——外部不得依赖它。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
@@ -28,4 +28,4 @@ Status: implemented
|
||||
- 每个 MVP 功能都映射到一个监听器([功能→机制映射](../../../../docs/cookbook/extension-cookbook.md#the-feature--mechanism-map)是证明义务,保持更新)。
|
||||
- HMR 与 dispose 无需额外工作:监听器和注册均为 Cordis effect。
|
||||
- waterfall 语义(调用 `next()` 或短路)不直观,需要教学——在 AGENTS.md 中记录,并由组合测试覆盖。
|
||||
- 循环必须具备防御性:插件异常在轮次级别被隔离,任何 seam 发出的 steering(中途引导)永远不会被搁置(有回归测试保障)。
|
||||
- 循环必须具备防御性:插件异常在轮次级别被隔离,任何扩展点发出的 steering(中途引导)永远不会被搁置(有回归测试保障)。
|
||||
|
||||
@@ -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-06-11-runtime-arg-validation.md: e0bca0ff24c5adc7ca58007932dff6580694b01d
|
||||
2026-06-11-runtime-arg-validation.zh.md: 09958147766b4015d6bebf786c4947b9d2941f74
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-06-11-runtime-arg-validation.md
|
||||
2026-06-11-runtime-arg-validation.md: 520823c76f147205805d306e97b5461f38b84c35
|
||||
2026-06-11-runtime-arg-validation.zh.md: ccb9b50d8b8a932d17516e579a5364321b64577b
|
||||
|
||||
@@ -18,7 +18,7 @@ The validator and compiler therefore share exact semantics: the implicit paramet
|
||||
|
||||
- The model gets actionable feedback on its own malformed calls instead of an opaque crash, closing the gap between `InferArgs`'s promise and runtime reality.
|
||||
- The validator and `InferArgs` must stay in agreement; [a property test](../testing/2026-06-11-property-based-testing.md) generates args satisfying a spec and asserts they pass `validateArgs` (with targeted corruptions rejected), closing that drift risk mechanically.
|
||||
- `ToolArgsError` is a plain `Error` with a `code` field for now; if a harness-wide error taxonomy lands it becomes a subclass without changing callers that read `.message`.
|
||||
- `ToolArgsError` subclasses `HarnessError` from the [structured error taxonomy](2026-06-11-structured-error-taxonomy.md), keeping its `code` field; callers that read `.message` are unaffected by the hierarchy.
|
||||
- Validation cost is negligible next to a model call.
|
||||
|
||||
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->
|
||||
|
||||
@@ -6,19 +6,19 @@ Status: implemented
|
||||
|
||||
## 问题
|
||||
|
||||
`defineTool`([统一 schema DSL](2026-07-20-unified-json-value-schema-dsl.md))为工具作者的 `execute(args)` 提供了经 `InferArgs<S>` 映射的类型化参数。但该类型只是对运行时值的编译期声明,而这个值实际上是模型生成的 JSON:没有任何机制强制模型遵守 schema,因此畸形调用(缺少必需键、声明为数字的位置传入字符串,或字面量超出声明的集合)会以「仅名义类型化」的状态到达 `execute`。工具函数体随后要么在错误形状上崩溃,要么静默地行为异常。
|
||||
`defineTool`([统一 schema DSL](2026-07-20-unified-json-value-schema-dsl.md))为工具作者的 `execute(args)` 提供了经 `InferArgs<S>` 映射的类型化参数。但该类型只是对运行时值的编译期声明,而这个值实际上是模型生成的 JSON:没有任何机制强制模型遵守 schema,因此畸形调用(缺少必需键、声明为数字的位置传入字符串,或字面量超出声明的集合)会以「仅在名义上类型化」的状态到达 `execute`。工具函数体随后要么在错误形状上崩溃,要么静默地行为异常。
|
||||
|
||||
## 决策
|
||||
|
||||
`validateArgs(spec, args): string[]` 编译 `ParameterSchemaSpec`,并委托共享的 `validateJsonSchemaValue()` 遍历器,对格式正确的声明返回可读的违规列表。`defineTool` 在定义时对编译后的参数 schema 创建快照,并在调用类型化函数体之前执行校验;存在违规时会抛出 `ToolArgsError`(`INVALID_ARGS`),注册表将其作为模型可据以修正的错误结果返回。
|
||||
|
||||
校验器与编译器因此共享完全一致的语义:隐式参数根是开放对象;必需键仅来自 `required: true`;默认值仍是注解;显式嵌套对象遵循其声明的开放性;数组通过 `items` 递归;标量字面量约束保证类型正确;`oneOf` 仅在恰好一个分支匹配时才接受。原始注册的工具自行负责输入校验。
|
||||
校验器与编译器因此共享完全一致的语义:隐式参数根是开放对象;必需键仅来自 `required: true`;默认值仍是注解;显式嵌套对象遵循其声明的开放性;数组通过 `items` 递归;标量字面量约束保证类型正确;`oneOf` 仅在恰好一个分支匹配时才接受。直接注册的工具自行负责输入校验。
|
||||
|
||||
## 后果
|
||||
|
||||
- 模型在自身畸形调用上获得可操作的反馈,而非不透明的崩溃,弥合了 `InferArgs` 的承诺与运行时现实之间的鸿沟。
|
||||
- 校验器与 `InferArgs` 必须保持一致;一项[属性测试](../testing/2026-06-11-property-based-testing.md)生成满足 spec 的参数并断言它们通过 `validateArgs`(同时断言定向破坏的参数被拒绝),以机械方式封堵漂移风险。
|
||||
- `ToolArgsError` 目前是带 `code` 字段的普通 `Error`;如果日后引入 harness 级别的错误分类体系,它将变为子类,但不影响读取 `.message` 的调用方。
|
||||
- `ToolArgsError` 是[结构化错误分类体系](2026-06-11-structured-error-taxonomy.md)中 `HarnessError` 的子类,保留其 `code` 字段;读取 `.message` 的调用方不受该层级结构影响。
|
||||
- 校验开销相对于一次模型调用可忽略不计。
|
||||
|
||||
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->
|
||||
|
||||
@@ -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/architecture/2026-06-13-capability-seams.md
|
||||
2026-06-13-capability-seams.md: 7c755dced7825d2831acc0901f6412b8e5afe95a
|
||||
2026-06-13-capability-seams.zh.md: 3d0fe2b842766838d247aca75972509bf6c7b951
|
||||
2026-06-13-capability-seams.md: b2dc124f7bf0b56598466e6521e0764af06075ab
|
||||
2026-06-13-capability-seams.zh.md: 7790124b980e0525705c2db398802ea6a418685a
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Agent Note: Capability seams — interface / implementation / consumer split
|
||||
# Agent Note: Capability seams — Service Definition / Service provider / Consumer roles
|
||||
|
||||
Status: implemented
|
||||
|
||||
@@ -6,27 +6,31 @@ English | [中文](2026-06-13-capability-seams.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The harness has swappable capabilities — bash execution today, sandboxed/remote executors and alternative model providers tomorrow. A capability has three concerns that change at different rates and for different reasons: the *contract* (what the capability is), the *implementation* (how it runs), and the *consumer surface* (what the model and other plugins program against). Bundling them in one package couples those rates of change — swapping a local executor for a sandboxed one would churn the tool schemas the model sees, even though the model-facing contract never changed.
|
||||
The harness has swappable capabilities — bash execution today, sandboxed/remote executors and alternative model providers tomorrow. A capability has three concerns that change at different rates and for different reasons: the *contract* (what the capability is), the *implementation* (how it runs), and the *consumer API* (what the model and other plugins program against). Bundling them in one package couples those rates of change — swapping a local executor for a sandboxed one would churn the tool schemas the model sees, even though the model-facing contract never changed.
|
||||
|
||||
This is distinct from "who provides vs. needs a capability at runtime", which Cordis already answers with services + `inject` (a provider registers `ctx.bash`; a consumer declares `inject: ['bash']` and its fiber pends until the service exists). That mechanism is necessary but doesn't dictate package boundaries; this Agent Note does.
|
||||
|
||||
## Decision
|
||||
|
||||
A swappable capability is **three packages**:
|
||||
A swappable capability has **three roles**:
|
||||
|
||||
1. **Interface** — an abstract service + the vocabulary types, owning the `ctx.<key>` and depending only on its vocabulary dependencies (e.g. `dsh-bash`: `BashExecutor`, `BashRunResult`, `BashProcess`).
|
||||
2. **Implementation** — a concrete subclass loaded as a plugin (e.g. `dsh-bash-local`: subprocesses, process-group kills, spill-file truncation). Sandboxed/remote backends are sibling packages implementing the same interface.
|
||||
3. **Consumer** — what the model and plugins see (e.g. `dsh-tool-bash`: the `bash` schema, with background handles registered into the generic task runtime). Consumers `inject` the interface key and never import implementation types.
|
||||
1. **Service Definition** — the Cordis `Service` and vocabulary types owning `ctx.<key>` and depending only on the vocabulary the contract needs (e.g. `dsh-bash`: `BashExecutor`, `BashRunResult`, `BashProcess`). A definition may be an abstract class or a concrete registry service; it is never a TypeScript `interface`.
|
||||
2. **Service provider** — a plugin that supplies or registers an implementation (e.g. `dsh-bash-local`: subprocesses, process-group kills, spill-file truncation). Sandboxed and remote providers are sibling packages implementing or registering against the same Service Definition.
|
||||
3. **Consumer** — what the model and plugins program against (e.g. `dsh-tool-bash`: the `bash` schema, with background handles registered into the generic task runtime). Consumers inject the service key and never import provider-specific types.
|
||||
|
||||
Implementation and consumer then evolve independently: a sandboxed executor replaces `dsh-bash-local` without touching a tool schema.
|
||||
Service providers and Consumers then evolve independently: a sandboxed executor replaces `dsh-bash-local` without touching a tool schema.
|
||||
|
||||
The split is not mandatory when the parts are genuinely one concern: the LLM seam folds interface + consumer into `dsh-llm` (the consumer is the loop itself, not a swappable schema surface) with adapters as the implementation packages. Don't split preemptively — a capability with one conceivable implementation and one consumer stays one package until a second appears.
|
||||
Roles normally use separate packages when they evolve independently, but the split is not mandatory when the roles are genuinely one concern: the LLM seam folds Service Definition and Consumer into `dsh-llm` (the Consumer is the loop itself, not a swappable schema surface) with adapters as Service provider packages. Don't split preemptively — a capability with one conceivable provider and one Consumer stays one package until a second appears.
|
||||
|
||||
## Terminology: "seam" names the trio, not the interface
|
||||
|
||||
A **seam** is the whole capability — the three roles together: a **Service Definition** (the Cordis `Service` that owns `ctx.<key>` and the vocabulary), one or more **Service providers**, and one or more **Consumers**. `packages/bash` is the canonical example — `dsh-bash` / `dsh-bash-local`+`dsh-bash-sandbox` / `dsh-tool-bash`. A package may own multiple roles, but one role alone is not the seam. The term "seam" is reserved for this complete capability; name a constituent by its role, class, service, contract, or extension point. The [glossary](../../../../docs/glossary.md#capability-seam) is the canonical entry.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **One combined package** — rejected because it recouples the three rates of change the split exists to separate (the whole point).
|
||||
- **`@cordisjs/plugin-capability`** — a different axis entirely: it is a permission/capability-*security* service (named permissions with inheritance, tested against a session via `ctx.capability.test`), a candidate for the deferred permissions/sandbox work on the `tools/pre-execute` deny/ask seam, NOT a mechanism for swapping implementations. Confusing the two ("capability") is the trap this Agent Note names.
|
||||
- **Always combine the roles** — rejected because it recouples independently changing Service Definitions, providers, and Consumers.
|
||||
- **`@cordisjs/plugin-capability`** — a different axis entirely: it is a permission/capability-*security* service (named permissions with inheritance, tested against a session via `ctx.capability.test`), a candidate for the deferred permissions/sandbox work on the `tools/pre-execute` deny/ask gate, NOT a mechanism for swapping implementations. Confusing the two ("capability") is the trap this Agent Note names.
|
||||
|
||||
## Consequences
|
||||
|
||||
More packages and more boilerplate per capability (a `package.json`/`tsconfig`/README trio, the inject wiring). Bought: implementations and consumers ship and version independently, and a new backend never risks the model-facing contract. The rule is documented in [AGENTS.md](../../../../AGENTS.md) § Conventions ("Capability seams are three packages") and [architecture.md](../../../../docs/architecture.md) § "Capability seams"; the bash trio is the reference template. When to fold vs. split is a judgment call the architecture doc spells out — this Agent Note records *why* the default is to split.
|
||||
Separating roles adds packages and boilerplate (`package.json`, `tsconfig`, README, and injection wiring). In return, Service providers and Consumers ship and version independently, and a new backend never risks the model-facing contract. [AGENTS.md](../../../../AGENTS.md) and [architecture.md](../../../../docs/architecture.md) carry the rule; the bash trio is the reference template. This Agent Note records why independently changing roles normally split while genuinely shared concerns may remain folded.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user