docs: purge chain-of-thought leakage from prose

Delete design-session citations (decision/audit/plan ordinals, stack
positions), change narration, review choreography, and reviewer-addressed
justification from comments, JSDoc, docs, READMEs, Agent Notes, tests, and
generator templates; restate every affected fact as current-state contract
prose. Fix generated docs at their sources and regenerate the catalogs and
cordis-surface regions; re-paste type-equiv blocks; update every bilingual
counterpart and re-record the pairs. Record the citation rule in the
committed-artifact-citations Agent Note.
This commit is contained in:
Tianyi Cui
2026-08-09 15:09:19 +08:00
parent 793f6f55df
commit 25dcd7293c
763 changed files with 2705 additions and 1710 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 packages/client/ui-slash/README.md
README.md: efb289aaf6b4a442b00e7a0b26ed044b7b061070
README.zh.md: c6d3236f615642b9efc6fabaa7ec407ad896a8e2
README.md: 739e7ae93e024bdbd602aaff4267cb74406b5698
README.zh.md: 0e3cd0d4b3cb93e3d602aa0c8f30d4e5afff3af2

View File

@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
Input trigger pipeline plugin: `/` and `@` detection under the caret (word-boundary + guard-tier rules), the grouped candidate menu, and pick routing to registered sources. `ctx.slash` owns the source roster and resolves one `SlashController` per session scope (`sessionOf`); the conversation wiring layer drives `track`/`arbitrate`/`onSpace`/`adjudicate` on the controller. The same controller exposes `toggleSource` for a chrome launcher to open exactly one registered source over a synthetic selection span; the resulting candidates still use the ordinary menu, keyboard arbitration, pick callback, and scoped input mutations. Sources receive a `ClientSessionContext` projection per call — sessions are always agent-backed, so the projection is the session identity alone. A source is warmed in every session controller it can reach: the roster present at scope birth warms during controller construction, and a source registered later is warmed into every live controller by the registration itself. Sources whose `lexicon` roll changes after warm implement `subscribeLexicon(session, listener)`; the controller re-polls on each notification and publishes the aggregation through its `lexicon` snapshot store. The pipeline is command-agnostic: space/enter adjudication polls the optional `matchSpace`/`matchEnter` hooks in registration order and the first non-undefined answer wins.
Layering: `src/core/` (T2) is the pure core — `detectTrigger`, `menuReduce`/`seedGroups`/`MENU_CLOSED`, `exactMatch`, zero React/DOM/cordis; `src/client/service.ts` is the shell wiring the core to the menu snapshot store, the per-hit candidate fetch (generation-gated, `AbortSignal`-superseded, failed sources drop silently with a console record), and the three pick paths. `src/types.ts` and the two `contract.ts` files are the frozen cross-package contract (design v4 §5.1); changes require main-thread arbitration.
Layering: `src/core/` (T2) is the pure core — `detectTrigger`, `menuReduce`/`seedGroups`/`MENU_CLOSED`, `exactMatch`, zero React/DOM/cordis; `src/client/service.ts` is the shell wiring the core to the menu snapshot store, the per-hit candidate fetch (generation-gated, `AbortSignal`-superseded, failed sources drop silently with a console record), and the three pick paths. `src/types.ts` and the two `contract.ts` files are the frozen cross-package contract; changes require main-thread arbitration.
MenuView renders the menu store into the `conversation.input.overlay` slot (list kind, session scope) and renders null while closed. Typed triggers seed every source registered for that trigger; a programmatic launcher seeds only its requested source and publishes the source name through the controller's `launcher` snapshot store until the menu closes or typed tracking resumes. Groups sort by the optional `SlashSource.order` (lower first, default 0, ties keep registration order) under title rows localized through the `slash.menu` locale namespace (an unknown source shows its raw name); the list height clamps to the space above the composer, and a pointer down outside both the menu and the surrounding composer card dismisses it. The slot is owned by ui-conversation's composer entry (anchor, children declaration, lifecycle); its SlotMap type merge lives in this package's `src/client/slots.ts` because the dependency direction (ui-conversation → ui-slash) admits no reverse type import. Combobox pattern: focus stays in the textarea, rows pick on mousedown, the highlight rides `aria-activedescendant`.

View File

@@ -4,7 +4,7 @@
输入触发流水线插件:光标处的 `/` 与 `@` 检测(词边界 + guard tier 规则)、分组候选菜单,以及把 pick 路由到已注册 source。`ctx.slash` 拥有 source roster,并按会话 scope(`sessionOf`)各解析一个 `SlashController`;对话接线层在 controller 上驱动 `track`/`arbitrate`/`onSpace`/`adjudicate`。同一个 controller 还暴露 `toggleSource`,供 chrome launcher 在一段合成 selection span 上只打开一个已注册 source;所得候选仍走通常的菜单、键盘仲裁、pick callback 与 scoped 输入改写。source 每次调用收到一个 `ClientSessionContext` 投影——会话始终由 agent(智能体)支撑,因此投影只含会话身份。source 在它能触达的每个会话 controller 中都会被预热:scope 出生时在场的 roster 随 controller 构造预热,晚于此注册的 source 由注册动作本身预热进每个活 controller。`lexicon` 名录在预热后仍会变化的 source 实现 `subscribeLexicon(session, listener)`;controller 每收到通知就重拉,并把聚合结果经其 `lexicon` 快照 store 发布。流水线与命令无关:空格/回车裁决按注册序轮询可选的 `matchSpace`/`matchEnter` 钩子,第一个非 undefined 的应答胜出。
分层:`src/core/`(T2)是纯内核——`detectTrigger`、`menuReduce`/`seedGroups`/`MENU_CLOSED`、`exactMatch`,零 React/DOM/cordis;`src/client/service.ts` 是壳层,把内核接到菜单快照 store、逐 hit 候选拉取(以 generation 把关、后继请求经 `AbortSignal` 取代旧请求、失败的 source 静默丢弃并留一条 console 记录)和三条 pick 路径上。`src/types.ts` 与两个 `contract.ts` 文件是冻结的跨包约定(设计 v4 §5.1);变更需经主线程仲裁。
分层:`src/core/`(T2)是纯内核——`detectTrigger`、`menuReduce`/`seedGroups`/`MENU_CLOSED`、`exactMatch`,零 React/DOM/cordis;`src/client/service.ts` 是壳层,把内核接到菜单快照 store、逐 hit 候选拉取(以 generation 把关、后继请求经 `AbortSignal` 取代旧请求、失败的 source 静默丢弃并留一条 console 记录)和三条 pick 路径上。`src/types.ts` 与两个 `contract.ts` 文件是冻结的跨包约定;变更需经主线程仲裁。
MenuView 把菜单 store 渲染进 `conversation.input.overlay` slot(列表类,会话 scope),菜单关闭期间渲染 null。键入式 trigger 会 seed 为该 trigger 注册的所有 source;程序化 launcher 只 seed 所请求的 source,并在菜单关闭或重新开始键入式 tracking 前,通过 controller 的 `launcher` 快照 store 发布该 source 名称。分组按可选的 `SlashSource.order` 排序(越小越靠前,默认 0,同值保持注册序),组标题行经 `slash.menu` locale 命名空间本地化(未知 source 显示其原名);列表高度收敛到 composer 上方的可用空间,指针落在菜单与所在 composer 卡片之外即关闭菜单。该 slot 由 ui-conversation 的组合器条目拥有(锚点、children 声明、生命周期);其 SlotMap 类型合并放在本包的 `src/client/slots.ts`,因为依赖方向(ui-conversation → ui-slash)不允许反向的类型导入。combobox 模式:焦点始终留在 textarea,行在 mousedown 时完成 pick,高亮由 `aria-activedescendant` 承载。

View File

@@ -47,7 +47,8 @@ export class SlashController {
*/
readonly launcher: SnapshotStore<string | null> = createSnapshotStore<string | null>(null)
/**
* Aggregated hot reference lexicon, grouped by trigger (decision 21):
* Aggregated hot reference lexicon, grouped by trigger (plain-text-reference decision;
* see .agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md):
* sources implementing the lexicon hook are polled with the session
* projection; undefined answers (roll not hot yet) are skipped; multiple
* sources on one trigger concatenate in registration order. A snapshot
@@ -225,7 +226,7 @@ export class SlashController {
/**
* Serialize one reference occurrence to its model form via the owning
* source's codec (design §9.1 prompt serialization: registry → explicit
* source's codec (prompt serialization: registry → explicit
* call → await). Owner missing or codec-less rejects — the submit attempt
* blocks instead of silently downgrading to the clipboard text.
* @param source - owning source name.

View File

@@ -4,7 +4,7 @@
* entry (declaring is claiming: anchor, children declaration, lifecycle),
* but the SlotMap type merge lives here: the owner package depends on this
* one, so the dependency direction admits no reverse type import, and a
* type-erased registration is ruled out (PR #632 review). The owner's
* type-erased registration is ruled out. The owner's
* program picks this merge up transitively through its ui-slash imports.
*/
// Type-only edge: the SlotMap augmentation below merges into this package's interface.

View File

@@ -1,8 +1,8 @@
/**
* Frozen pure-core contract (design v4, plan §1.2): trigger detection and
* menu reduction, zero React / DOM / cordis. Types only — T2 implements
* these signatures in sibling modules (annotate implementations with these
* aliases); the service shell (T4) wires them to ctx.
* Frozen pure-core contract: trigger detection and
* menu reduction, zero React / DOM / cordis. Types only — implementations
* live in sibling modules annotated with these
* aliases; the service shell wires them to ctx.
*/
import type { SlashCandidate, TokenSpan, TriggerChar, TriggerGuard, TriggerPosition } from '../types.ts'

View File

@@ -1,5 +1,5 @@
/**
* Trigger detection pure core (design §5.1, plan §1.2). Scans backward from
* Trigger detection pure core. Scans backward from
* the caret for a live trigger char under the guard tier and applies the
* word-boundary rules. Zero React / DOM / cordis.
*/

View File

@@ -1,5 +1,5 @@
/**
* Menu reduction pure core (design §5.1, plan §1.2). One group per source;
* Menu reduction pure core. One group per source;
* generation-gated settlement; empty ready groups auto-close. Zero React /
* DOM / cordis. Stale or no-op events return the same state reference so
* store subscribers skip re-renders.

View File

@@ -80,7 +80,9 @@ export interface SubmitOutcome {
/**
* Unified pick return. `undefined` = miss → default sink; `'handled'` = the
* source dealt with it internally (e.g. opened its popup shell). The `text`
* arm is the plain-text reference path (decision 21): the token span is
* arm is the plain-text reference path (decision recorded in
* .agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md):
* the token span is
* replaced with literal text — no occurrence identity, no placeholder; any
* chip visual is derived downstream by scanning the draft against the
* source lexicons.
@@ -159,8 +161,8 @@ export interface SlashSource {
*/
warm?(session: ClientSessionContext): void
/**
* Synchronous hot-snapshot name roll for plain-text reference decoration
* (decision 21). Implementing IS the participation claim: the render side
* Synchronous hot-snapshot name roll for plain-text reference decoration.
* Implementing IS the participation claim: the render side
* scans the draft for `<trigger><name>` tokens and decorates exact matches.
* `undefined` = backing data not warm yet — no decoration, never a fetch
* (the render path must stay synchronous and side-effect free).
@@ -211,7 +213,7 @@ export interface ConsumeTokenRequest {
| { readonly kind: 'bare-token'; readonly token: string }
}
/** Request payload of the scoped insert-text input event (decision 21). */
/** Request payload of the scoped insert-text input event (the plain-text reference path). */
export interface InsertTextRequest {
/** Literal replacement for the trigger token span (e.g. `/name `). */
readonly text: string
@@ -245,7 +247,7 @@ declare module 'cordis' {
'slash/input-consume-token'(request: ConsumeTokenRequest): true | undefined
/**
* Replaces the trigger token span with literal text — the plain-text
* reference path (decision 21). Same carrier routing and applied-truth
* reference path. Same carrier routing and applied-truth
* contract; the draft gains ordinary characters, no occurrence entry.
* @param request - Replacement text and menu-time span CAS.
* @mode bail

View File

@@ -1,5 +1,5 @@
// detectTrigger word-boundary, position, guard-tier, and span behavior
// (design §5.1). URL rule pinned here: '/' is dead when its predecessor is
// detectTrigger word-boundary, position, guard-tier, and span behavior.
// URL rule pinned here: '/' is dead when its predecessor is
// another '/' (second slash of '//') or a ':' itself preceded by a
// non-whitespace char (scheme separator) — this is the concrete rule chosen
// to honor "no trigger inside URLs".

View File

@@ -1,6 +1,5 @@
// menuReduce generation gating, auto-close, silent group removal, cyclic
// highlight movement, stale/no-op reference identity; exactMatch lookup
// (design §5.1, plan §1.2).
// highlight movement, stale/no-op reference identity; exactMatch lookup.
import { describe, expect, it } from 'vitest'
import type { MenuState, TriggerHit } from '../src/core/contract.ts'
import { exactMatch, MENU_CLOSED, menuReduce, seedGroups } from '../src/core/menu.ts'

View File

@@ -1,6 +1,6 @@
// @vitest-environment jsdom
/**
* MenuView rendering spec, props-direct (slot-parity doctrine): closed store
* MenuView rendering spec, props-direct: closed store
* renders null, groups render in roster order under localized title rows
* (unknown sources fall back to the raw name) with pending rows as loading,
* pointer picks route (source, index) back without stealing focus, the

View File

@@ -483,7 +483,7 @@ describe('pick / scoped input events', () => {
expect(inserts).toEqual([{ reference: insert, span: { start: 0, end: 2, draftRev: 3 } }])
})
it('routes a text outcome through the scoped insert-text event (decision 21) and closes the menu', async () => {
it('routes a text outcome through the scoped insert-text event and closes the menu', async () => {
const { controller, actx } = pickBench(() => ({ text: '/goal ' }))
const texts: Array<{ text: string; span: unknown }> = []
actx.on('slash/input-insert-text', (req) => {