fix(web): address the second review round on context provenance

- An empty replacement catalog is a real catalog: `renderCatalogUpdate()`
  publishes zero entries when the last skill disappears, and falling back
  would hide that every earlier name was retired.
- The opaque fallback keeps a `form` declaration this version cannot present.
  It is the one place a newer or foreign log's declared shape would otherwise
  vanish from the UI entirely, since the row marker is also absent there.
- An instruction change with an unrecognized `action` disqualifies the record.
  The action decides the word the row shows, so an unknown one would be
  presented as loaded or updated.
- The catalog list bounds itself and reports the withheld count. Entry count
  is unbounded and the scrollport bounds height, not node count.
- A catalog message keeps content blocks this version does not know, instead
  of dropping model-visible content the extensible union may carry.
- `core.md` defines `ContextFormed`, the interface actually carrying the
  optional field, beside `ContextForm`.
- The superseded-in-part bullet states the affected clauses as one rule rather
  than enumerating them; two rounds of enumeration each missed some, which is
  the shape being fragile rather than the list being wrong.
- The note records the one migration case that does not self-heal: an
  old-format catalog as the only one, with an empty current view, leaves a
  stale catalog nothing replaces.
This commit is contained in:
creatixchu
2026-08-05 16:51:01 +08:00
parent aeb718688e
commit ccd27f3775
16 changed files with 160 additions and 59 deletions

View File

@@ -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/feature/2026-08-05-context-form-vocabulary.md
2026-08-05-context-form-vocabulary.md: 20577b0ad0eaa9eb8dc8528cedd18d15439ad299
2026-08-05-context-form-vocabulary.zh.md: 154e8ae54fc6b27ce782533b4246d48190639128
2026-08-05-context-form-vocabulary.md: c111acfec4fa85fe9e25adeb615371d65098f20f
2026-08-05-context-form-vocabulary.zh.md: d4da218c4c6026eb875513c2acbfcd18ced8faa1

View File

@@ -27,7 +27,7 @@ The vocabulary is semantic, never visual. A value states that the content is a f
Entries record the published fact **unescaped**. The pseudo-XML escaping belongs to the `<available_skills>` frame, which exists for the model, so it is applied when rendering that frame and never stored; otherwise a consumer would have to know the frame's encoding to display a description containing `<`, and the same frame knowledge this decision removes would leak back in another shape. `escapeText` is deterministic and injective, so digesting the unescaped entries preserves republish semantics exactly, and the model-facing text stays byte-identical.
That move also relocates catalog **identity**: the republish digest now covers the durable entries rather than the rendered text, so the model-facing framing can no longer decide whether a republish is needed, and the text-slicing that recovered entries from a logged message is gone. A resumed session whose newest catalog predates this change republishes once, which the pre-release stance permits.
That move also relocates catalog **identity**: the republish digest now covers the durable entries rather than the rendered text, so the model-facing framing can no longer decide whether a republish is needed, and the text-slicing that recovered entries from a logged message is gone. A resumed session whose newest catalog predates this change republishes once, which the pre-release stance permits. One case does not self-heal: if that old-format catalog is the only one and the current view has no skills, the plugin sees no published catalog and emits no tombstone, so the model keeps a stale catalog nothing replaces. The pre-release stance ("backends reject old on-disk formats") permits it; it is recorded here rather than left to the optimistic path.
Both readers are **all-or-nothing**: one unreadable entry disqualifies the record rather than being dropped, because a body that replaces the model-facing text must not present a confident but incomplete account of what the model read. The row's form marker reports what actually rendered, not what was declared.

View File

@@ -27,7 +27,7 @@ Status: implemented
条目记录的是**未转义**的发布事实。伪 XML 转义属于 `<available_skills>` 这层为模型而设的框架,因此只在渲染该框架时施加、从不存储;否则消费方要正确展示含 `<` 的描述就得知道框架的编码方式,本决策刚移除的框架知识会换一种形式泄漏回来。`escapeText` 确定且单射,故对未转义条目取 digest 与此前完全等价,重新发布语义不变,面向模型的文本逐字节不变。
这次迁移同时挪动了目录的**身份**:重新发布用的 digest 现在覆盖持久条目而非渲染文本,于是面向模型的包装再也无法左右是否需要重新发布,那段从已记录消息里切出条目的文本切分逻辑也随之删除。若恢复的会话中最新目录早于本次改动,会重新发布一次——发布前阶段的姿态允许这样做。
这次迁移同时挪动了目录的**身份**:重新发布用的 digest 现在覆盖持久条目而非渲染文本,于是面向模型的包装再也无法左右是否需要重新发布,那段从已记录消息里切出条目的文本切分逻辑也随之删除。若恢复的会话中最新目录早于本次改动,会重新发布一次——发布前阶段的姿态允许这样做。有一种情形不会自愈:当那份旧格式目录是唯一的一份、且当前视图没有任何 skill 时,插件看不到已发布目录,也就不会发出 tombstone模型手里会留着一份无人替换的过期目录。发布前阶段的姿态「后端拒绝旧的磁盘格式」允许这一点此处如实记录而不是只写乐观路径。
两个读取器都是**全有或全无**:一条不可读的条目即判定整条记录不可用,而不是把它丢掉——会替换掉面向模型文本的内容区,不得给出自信但残缺的「模型读到了什么」。行上的形态标记报告的是实际渲染出的形态,而非声明的形态。

View File

@@ -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-07-31-web-ui-no-steer-entry-or-interjection-chrome.md
2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.md: 9dc87cd69f5d274ba5e8d66c30805cada7f1fa1a
2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.zh.md: e650b660efe0bf308a4b1a8c72e9c89a133ac769
2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.md: dfd79a1b1b44778275b4326cf5f51f6e69ce2a7c
2026-07-31-web-ui-no-steer-entry-or-interjection-chrome.zh.md: 9b470dda8de45ca754a04900ffac0535ebfabd96

View File

@@ -28,7 +28,7 @@ Keep host and runtime steering intact. Remove only the Web UI entry and chrome:
## Consequences
- **Superseded in part.** Composer steering shipped afterwards, so the first and third Decision bullets and the first consequence below no longer describe master: `InputMachine` carries the `queue`/`steer` mode union again, Web users can steer, and the steering bubble regained its user IconActions before carrying a caption again. The [context-source and steer marks decision](../feature/2026-08-04-web-context-source-and-steer-marks.md) then supplied the product decision this note's reintroduction clause required and restored the `插话` caption on steering bubbles. Host steering ownership, `ConversationService.send`'s queue-only contract, and the rejection of hiding `steering/message` from the transcript still hold.
- **Superseded in part.** Every clause below about the steer ENTRY, the `queue`/`steer` mode union, the interjection caption, its locale strings, and the goldens that pinned their absence no longer describes master — in the Decision that is bullets 1 and 3 through 5, and in the consequences bullets 1 and 3. Composer steering shipped afterwards, and the [context-source and steer marks decision](../feature/2026-08-04-web-context-source-and-steer-marks.md) then supplied the product decision this note's reintroduction clause required. What still holds: host steering ownership, `ConversationService.send`'s queue-only contract, and the rejection of hiding `steering/message` from the transcript.
- Web users cannot steer from the composer or `ctx.conversation.send`; stop/cancel and Queue remain the only mid-turn controls.
- Host-wire and non-Web clients can still steer; the Web client shows those messages without labeling them as interjections.
- Reintroducing a dedicated steer UI would need a new product decision; do not revive the mode union or badge without one.

View File

@@ -28,7 +28,7 @@ Status: implemented
## 后果
- **部分被取代。** composer steering 在此之后落地,因此 Decision 的第一条与第三条、以及下面的第一条后果都已不再描述 master`InputMachine` 重新携带 `queue``steer` mode 联合类型Web 用户可以 steersteering 气泡也早在重新带上标注之前就恢复了用户 IconActions。随后[上下文来源与 steer 标识决策](../feature/2026-08-04-web-context-source-and-steer-marks.md)提供了本 note 重新引入条款所要求的产品决策,并恢复了 steering 气泡上的 `插话` 标注。host 侧 steering 的归属、`ConversationService.send` 仅排队的契约,以及不在 transcript 中隐藏 `steering/message` 的取舍,仍然成立
- **部分被取代。** 下文中所有关于 steer **入口**`queue``steer` mode 联合类型、插话标注、其 locale 字符串、以及钉住这些「不存在」的黄金基线的子句,均已不再描述 master——在 Decision 中是第 1 条与第 3 至 5 条,在后果中是第 1 条与第 3 条。composer steering 在此之后落地,随后[上下文来源与 steer 标识决策](../feature/2026-08-04-web-context-source-and-steer-marks.md)提供了本 note 重新引入条款所要求的产品决策。仍然成立的是:host 侧 steering 的归属、`ConversationService.send` 仅排队的契约,以及不在 transcript 中隐藏 `steering/message` 的取舍。
- Web 用户无法从 composer 或 `ctx.conversation.send` steer中途控制只剩停止取消与 Queue。
- Host 线缆与非 Web 客户端仍可 steerWeb 客户端展示这些消息时不再标成插话。
- 若要重新引入专用 steer UI需要新的产品决策没有决策就不要复活 mode 联合类型或徽章。