dsh-ptc-plus 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.en.md +4 -2
  2. package/README.md +4 -2
  3. package/client.cjs +3392 -2811
  4. package/compiler-core.cjs +127 -127
  5. package/compiler-core.cjs.map +2 -2
  6. package/docs/README.md +1 -0
  7. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +4 -2
  8. package/docs/adr/0019-plugin-settings-and-kill-switch.md +2 -2
  9. package/docs/adr/0023-global-user-bindings.md +12 -0
  10. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +1 -1
  11. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +2 -2
  12. package/docs/adr/0044-coexist-with-the-official-persistent-repl.md +4 -4
  13. package/docs/adr/0046-gate-the-commit-with-the-semantic-evidence-tier.md +1 -1
  14. package/docs/adr/0048-supersede-a-rewritten-cell.md +48 -0
  15. package/docs/adr/0049-prove-a-settlement-against-its-published-record.md +53 -0
  16. package/docs/adr/0050-read-host-surfaces-through-their-published-contract.md +48 -0
  17. package/docs/adr/README.md +7 -1
  18. package/docs/architecture.md +1 -1
  19. package/docs/capability-planning-components.md +6 -6
  20. package/docs/capability-projection.md +1 -1
  21. package/docs/capability-surface.md +133 -0
  22. package/docs/client-ui.md +17 -15
  23. package/docs/client-visual-contract.md +7 -7
  24. package/docs/durability-design.md +9 -2
  25. package/docs/installation.md +17 -10
  26. package/docs/panel-concept.md +171 -0
  27. package/docs/releases/v0.5.2.md +26 -14
  28. package/docs/releases/v0.5.3.md +77 -0
  29. package/docs/releases/v0.5.4.md +75 -0
  30. package/docs/runtime-reference.md +21 -1
  31. package/docs/user-bindings.en.md +15 -0
  32. package/docs/user-bindings.md +15 -0
  33. package/docs/v0.5.3-goals.md +117 -0
  34. package/docs/verification.md +4 -4
  35. package/index.js +176 -41
  36. package/internal/binding-adoption.js +228 -0
  37. package/internal/capability-command-receipt.js +106 -8
  38. package/internal/capability-environment.js +1109 -312
  39. package/internal/capability-plan.js +18 -2
  40. package/internal/capability-policy.js +13 -0
  41. package/internal/capability-selection.js +330 -0
  42. package/internal/config-spec.js +14 -5
  43. package/internal/cordis-tools-owner.js +2 -1
  44. package/internal/direct-surface-owner.js +44 -17
  45. package/internal/edit-transport-owner.js +6 -5
  46. package/internal/execution-provider-owner.js +6 -0
  47. package/internal/execution-seam-compat.js +25 -0
  48. package/internal/host-tool-sdk.js +308 -0
  49. package/internal/message-sources.js +21 -0
  50. package/internal/official-repl-priority.js +31 -10
  51. package/internal/program-bindings.js +25 -5
  52. package/internal/repl-memory-projection.js +21 -2
  53. package/internal/runtime-bridge-owner.js +118 -9
  54. package/internal/runtime-config.js +10 -0
  55. package/internal/runtime-messages.js +5 -0
  56. package/internal/session-journal-recovery.js +210 -32
  57. package/internal/session-journal-schema.js +3 -0
  58. package/internal/session-journal.js +46 -9
  59. package/internal/session-log-view.js +69 -7
  60. package/internal/session-runtime.js +106 -39
  61. package/internal/settings-compat.js +6 -0
  62. package/internal/user-bindings-owner.js +126 -6
  63. package/package.json +59 -37
  64. package/src/client-authoring-view.js +7 -8
  65. package/src/client-capabilities-view.js +874 -909
  66. package/src/client-capability-sort.js +22 -9
  67. package/src/client-copy.js +118 -272
  68. package/src/client-indicator-view.js +2 -1
  69. package/src/client-repl-view.js +35 -16
  70. package/src/client-settings-view.js +90 -33
  71. package/src/client-styles.js +67 -19
  72. package/src/client.js +49 -22
@@ -49,6 +49,20 @@ The default SDK declares `capabilities.tree/find/inspect/search` directly. `sear
49
49
 
50
50
  The compatible `find` uses case-insensitive lexical matching, prioritizing exact `namespace.member` symbols, namespace names, and member names before complete identifier or description tokens. CamelCase and punctuation separate tokens; multiple query tokens must be contiguous. A miss means no lexical match, not proof that a capability is unavailable. `tree` and item-budgeted `inspect` keep their existing unbounded-byte interfaces. With capability management enabled, the SDK section is re-rendered from the frozen session plan: Host-declared members keep their Host schemas, complete and on-demand contracts arrive through session messages, and the always-callable `edit_run_code` transport keeps its Host signature in every render. Settings changes affect new sessions; `/capabilities refresh` synchronizes an existing session. See [public integration evidence](context-public-interface-coverage.md).
51
51
 
52
+ The declaration states the delivered capability set and nothing else. That set is the step's admission: the definitions the Host renders its own `tools:sdk` block from and builds a program's `tools` members from. A registered definition this step does not admit is therefore declared nowhere, and one session record that closes a capability removes it from the declaration, the program members and the explorable metadata at once instead of waiting for the next composition. A record is durable when it is made, so the surfaces that decide what can be called read it directly; the delivered plan catches up at the next accepted step and states the rest. The panel is a different read — it lists the rows a session can decide about, closed ones included.
53
+
54
+ A switch that is off delegates the Host surface, so the record is retained but not applied. A paused capability context and a disabled management switch both compose nothing and filter nothing: the declaration is the Host's own text, the program members are the Host's, and a closure the session recorded earlier applies again once management is on. Reading the record there would enforce a closure the same switch suspended, and enforce it only halfway — the switch also lets the Host execute that member. A session the Host still holds keeps the composition it froze, so its own snapshot decides whether this environment still applies; an agent with no composition yet answers from the configuration it would compose under.
55
+
56
+ A closure is one fact whichever layer of the record states it, so a projection never asks which document recorded the decision. A saved policy rule closes a capability, and so does the legacy overrides map of a plan composed before the policy generation that the session has not refreshed; one predicate reads both. A closure also reaches the rows whose contract names the closed member as their only model path: closing the `skill` loader keeps a Skill the user can still invoke with that remaining path stated and removes one that has no path left, and a resource server row states only the operations this session still has. A row may therefore never instruct a call the same session closed. A closure written for an MCP server states the server, not any one member of it, so it resolves through that one name to everything the server owns: the `mcp__<server>__*` tool rows, the derived `mcp.<server>` row, the `mcp:<server>` instructions that row depends on, and the `mcp-resource-servers` entry that names the server as the argument of the three resource operations. A registry view that still carries those members does not keep them on a surface, and the plan composed after the closure states the decision — its row is `closed` — rather than restating a calling contract.
57
+
58
+ Stating that path is a projection rule, not a composition rule. A composition states the path it was built under, and a decision recorded afterwards — `/capabilities` writes a record, never a plan — is already the session's newest statement, so an explorable row states the intersection of three statements: what the composition delivered, what the decision left, and what this step hands the program. The third is the same admission the declaration and the program members are made of: a composition is built once and reused while a step may deliver less, and a session that decided nothing does not recompose it, so a derived row's own contract is read against the members the cell really has. Absent that observation the composition's answer stands, because a caller that published no admission is not asking to state less than the session was composed from. A Skill row only ever loses the model path there, never gains one a composition did not state; a resource server row stays explorable only while an operation remains for its contract to name, and the guidance section and `mcp-resource-servers` directory entry it owns follow that same answer. One function states the resource sentence for both the composition and the read, so an unchanged session reads the same bytes.
59
+
60
+ The assembly observes that admission instead of inferring it. The Host resolves one admission per step and publishes it with the prompt context, and the registry read takes it as its limit, so the declaration, the plan and the program describe the step that is being assembled rather than every definition registered at that scope. The admission narrows this plugin's own contract view rather than replacing it: the definitions the Host publishes are model-facing and carry no output schema, so a capability recorded from them would promise an `unknown` result and a revision no live inventory agrees with. The same set bounds the repair of a top-level call into a program call: a model that names a member this step delivers is rewritten into `tools.<name>(...)`, and a name the registry merely carries is left as a top-level call for the Host to reject as unknown. A host that publishes no admission leaves the registry view as the only evidence, and that step's declaration is then as broad as its registry.
61
+
62
+ Guidance sections follow the same admission. A host that registers instructions for a tool states them as a `tool:<name>` section, and that text is a model-visible statement about a capability: a step that does not admit the definition delivers no member for it to instruct, so the section is withheld together with the declaration, the member and the metadata. The composition is not the admission — it is composed once and reused until the session decides something else — so a composition broader than the step (built from the registry or from an earlier step's admission) may not keep the section; a later step that delivers the definition states it again, which is why this is a statement about the step rather than a section deleted once. A projection that observed no admission keeps the registry view, as for the declaration. The two call transports are exempt: `run_code` and `edit_run_code` are the channel a program is called through, not capabilities.
63
+
64
+ The `mcp-resource-servers` directory follows the same rule, and it is the one Host section whose text *is* a list of capability names: its entries are the servers a program may pass as `server`, and the three resource operations are the only members that take one. A step that delivers none of them lists no server — the section leaves instead of naming a server no member of the program can name — and it returns on the step that delivers an operation again. The rows it is rewritten from are the resource server rows this step still states, so a server's own `mcp:<server>` instructions leave with the last row that named it and stay while a tool of that server is still delivered, because that text describes the server rather than one call's signature. A session with no composition states the directory from the admission alone, and a caller that observed no admission keeps the Host's own list.
65
+
52
66
  `tree()` returns namespace entries whose `members` are strings, not a flat symbol list. Form symbols with `tree.flatMap(entry => entry.members.map(member => `${entry.namespace}.${member}`))`. Reuse an observed tree within the current capability view and inspect only relevant symbols; do not infer completeness or effect guarantees from discovery. Exact symbols in examples are conditional on the live view. The advanced `repl.state` and `code.run` schemas remain behind inspection:
53
67
 
54
68
  ```ts
@@ -63,6 +77,12 @@ Discovery covers current `tools` plus the advanced `repl` and `code` namespaces.
63
77
 
64
78
  For commands, prefer project-declared scripts and use an available typed tool. When direct host process access is more appropriate, inspect the executable that is actually installed and invoke it through Node.js `child_process`. Child processes inherit the recorded cell execution cwd when `options.cwd` is omitted, while an explicit `cwd` remains authoritative; the worker preserves the host execution environment needed to resolve package runners and shells. Add a shell or package runner only when the command requires its syntax or resolution; do not assume a particular shell exists.
65
79
 
80
+ ## Host Surfaces
81
+
82
+ The reserved `run_code` definition is resolved from whichever surface publishes it: the tool registry answers `tools.get('run_code', scope)`, and a host that keeps the definition inside its tool executor publishes it through one of three contracted readers, `requirePtcTransport()`, `requireTransport()` or `transport()`. Those readers and the executor's own declaration reader `describe([])` are the only members invoked: an executor's remaining methods are host code this plugin does not own, and a probe that called them could dispose, stop or start host state during prompt assembly. An executor that declares the name in `describe([])` and publishes no definition through those readers is *declared but unreadable*: the plugin reports one warning and keeps session execution unavailable, which is a different fact from a host without an executor. See [ADR 0050](adr/0050-read-host-surfaces-through-their-published-contract.md).
83
+
84
+ The editable prompt slots (`runCode.description`, the `code` and `description` parameter texts) are written onto that definition as owned properties, and the host descriptors they replaced are restored when the attachment is disposed. A host that publishes the slots as getter-only accessors therefore receives the delivered text, and a definition whose slots cannot be replaced keeps its own text instead of half of the plugin's.
85
+
66
86
  ## Configuration
67
87
 
68
88
  Capability management is experimental and defaults to off. These inputs are frozen per session; saved changes apply to new sessions or `/capabilities refresh`:
@@ -134,7 +154,7 @@ The five legacy binding and module switches remain migration inputs. Complete en
134
154
 
135
155
  `startupProbeMs` bounds the worker startup conformance probe, which must settle before the kernel reports ready. The worker reads the value from its startup data and falls back to 5000 ms when it is absent; a healthy probe settles inside the worker's own microtasks, so the bound can only fire when startup itself yields or blocks, and the failure is reported as a startup diagnostic without executing any cell. That worker-side read is verified by inspection rather than by a timer test, because a healthy probe never reaches the bound.
136
156
 
137
- `officialReplPriority` defaults to `false` and is evaluated against the live provider capability rather than a release number: a provider reporting `programFormat: 'repl'` is an official persistent in-memory REPL. With the setting enabled and that provider live, this host attachment installs no session runtime, so `run_code` and its descriptors — `programFormat`, `executionInstructions`, `sandboxMode` and `timeout` — stay with the official provider, and the plugin's REPL guidance, session-state semantics, recovery, editor tool, capability SDK, REPL observation and binding workbench are suspended until ownership changes back. The settings card, enablement and stored global bindings remain available so the switch can be reversed. Changing the setting or the host mode re-evaluates ownership as an installation change: the retired attachment disposes its live kernels before the other shape installs, so no committed cell mixes owners. Calls executed under the official mode produce no plugin journal records and are never recovery evidence. The plugin's provider surface forwards `programFormat` to the live original and retains weak owner-to-original dispatch provenance for `release(owner)` — captured Agent cleanup reaches the original instances that served that owner even after adapter removal or source replacement, and a failed cleanup remains available for explicit retry. Owner-keyed retained environments and the executed program format belong to the provider that actually runs the program, and non-session consumers such as the workflow adapter keep executing through it — while `executionInstructions`, `sandboxMode` and `timeout` stay withheld while PTC Plus owns session execution. The plugin's own session kernel state is keyed by session and released through the plugin lifecycle, not by `release(owner)`.
157
+ `officialReplPriority` defaults to `false` and is evaluated against the live provider capability rather than a release number: a provider that declares `retainsState: true` keeps a program environment per state owner, and a provider that predates that declaration answers through the one retained source form (`programFormat: 'repl'`). With the setting enabled and such a provider live, this host attachment installs no session runtime, so `run_code` and its descriptors — `programFormat`, `retainsState`, `executionInstructions`, `sandboxMode` and `timeout` — stay with the official provider, and the plugin's REPL guidance, session-state semantics, recovery, editor tool, capability SDK, REPL observation and binding workbench are suspended until ownership changes back. The settings card, enablement and stored global bindings remain available so the switch can be reversed. Changing the setting or the host mode re-evaluates ownership as an installation change: the retired attachment disposes its live kernels before the other shape installs, so no committed cell mixes owners. Calls executed under the official mode produce no plugin journal records and are never recovery evidence. The plugin's provider surface forwards `programFormat` to the live original, answers `retainsState` for itself (an installed session kernel retains a per-session environment; otherwise the original declaration decides) and retains weak owner-to-original dispatch provenance for `release(owner)` — captured Agent cleanup reaches the original instances that served that owner even after adapter removal or source replacement, and a failed cleanup remains available for explicit retry. Owner-keyed retained environments and the executed program format belong to the provider that actually runs the program, and non-session consumers such as the workflow adapter keep executing through it — while `executionInstructions`, `sandboxMode` and `timeout` stay withheld while PTC Plus owns session execution. The plugin's own session kernel state is keyed by session and released through the plugin lifecycle, not by `release(owner)`.
138
158
 
139
159
  `cordisToolsEnabled: true` atomically mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and exactly the shipped `cordis-plugin-development` companion Skill in agent scopes that expose the `run_code` and `skill` tools plus the public preset and Skill services. PTC Plus resolves the official `cordis` preset through DSH's public preset service, lets the maintained filesystem provider read its Skill root as an immutable bundled Host resource, and filters that provider at the public registration boundary so sibling Skills are never published to the PTC scope. It does not copy the Skill or switch the agent's preset. The first request waits until the Cordis tool fiber, owner guidance, exact Skill provider, and scoped Skill load are all ready. An agent scope that omits one of those surfaces is left for later: the owner publishes nothing there, reports nothing, and retries when the tool surface changes or the next prompt is assembled. A scope whose exposed preset, Skill/tool service, declared Cordis service, inspect manifest, or activation contradicts the host contract fails the setting change at installation and removes every provisional contribution; reached through the host's own `agent/created` or assemble call it withdraws from that scope with one warning instead, so this plugin's unavailability never fails an agent another plugin created ([ADR 0029](adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md)). Disable and agent/runtime disposal remove both fibers. Native agents inherit neither the resulting `tools.*` members nor the Skill, and the code-only direct-tool projection remains `[run_code]`. Cordis can evaluate model-written plugins against the live runtime, so enabling it grants shell-equivalent trust.
140
160
 
@@ -60,6 +60,21 @@ The code console runs the entry's unsaved source draft. It supports top-level `a
60
60
 
61
61
  Bindings are stored in `$DSH_HOME/ptc-plus/bindings.json`. Relative imports in binding modules resolve from that file's directory; ordinary session REPL imports resolve from the session's project directory.
62
62
 
63
+ ## Skipping an entry in this session only
64
+
65
+ The global catalog decides what exists; whether *this* session uses an entry is a separate decision. Inside a session:
66
+
67
+ | Command | Effect |
68
+ | --- | --- |
69
+ | `/binding status` | Lists every catalog entry and says whether this session uses it |
70
+ | `/binding skip <id>` | This session does not use that entry; `id` is the identifier before the parentheses in `status` |
71
+ | `/binding use <id>` | Undoes that choice |
72
+ | `/binding reset` | Clears every choice for this session, back to "use what the catalog supplies" |
73
+
74
+ Only this session changes: the global catalog and other sessions are untouched, the next cell already follows the new choice, and the choice survives a restart because it is recorded in the session log.
75
+
76
+ An adoption decision can only withdraw a supplied entry. It cannot supply one, so an entry the catalog does not offer — or one disabled globally — stays unavailable here, and "the global switch is off but a session brought it back" cannot happen.
77
+
63
78
  ## Limits
64
79
 
65
80
  Enabled configuration allows loading but does not guarantee successful initialization. Assigning or redeclaring a same-name variable can override the global default in the current session without modifying its saved entry.
@@ -60,6 +60,21 @@
60
60
 
61
61
  绑定保存在 `$DSH_HOME/ptc-plus/bindings.json`。条目源码中的相对 import 从该文件所在目录解析,普通会话 REPL 的 import 则从会话项目目录解析。
62
62
 
63
+ ## 只在本会话关闭某个绑定
64
+
65
+ 全局条目决定"目录里有什么",某个会话用不用它是另一件事。在会话里输入:
66
+
67
+ | 命令 | 作用 |
68
+ | --- | --- |
69
+ | `/binding status` | 列出目录里的每个条目,并标明本会话是否使用它 |
70
+ | `/binding skip <id>` | 本会话不使用某个条目;`id` 是 `status` 里括号前那个标识 |
71
+ | `/binding use <id>` | 撤销上一步,恢复为使用 |
72
+ | `/binding reset` | 清空本会话的全部选择,回到"目录给什么就用什么" |
73
+
74
+ 它只改变**本会话**:全局条目和其他会话都不受影响,改完立即对下一个单元格生效,会话重启后依然有效(记录在会话日志里,可重建)。
75
+
76
+ 它只能"少用一个目录里已有的条目",不能凭空增加一个。目录里没有或已停用的条目,本会话也拿不到——所以不存在"全局关掉、会话又把它复活"的情况。
77
+
63
78
  ## 使用限制
64
79
 
65
80
  已启用表示绑定配置允许加载,不保证每次初始化都成功。当前会话中同名变量的赋值或重声明可以覆盖全局默认值,不会修改保存的条目。
@@ -0,0 +1,117 @@
1
+ # v0.5.3 目标:可证明的状态
2
+
3
+ ## 0.5.2 留下了什么
4
+
5
+ 0.5.2 解决的是**状态连续**:视图变化不再重建内核,绑定不会因为一次重渲染而丢失,编辑是追加而不是改写历史。它把"REPL 环境活着的时候算数"这件事做扎实了。
6
+
7
+ 但它留下一个未回答的问题:**插件对模型说的那些话,凭什么算数?**"这个绑定可用""这条记录没被改过""这个能力已经生效"——这些都只是**声明**,不是**证明**。0.5.2 里已经能看到三种破口:
8
+
9
+ - 恢复提示说某个名字 live,下一格却 `not defined`(`PTC-R002` 收口了一部分);
10
+ - 保存了全局绑定开关,面板只能说"已保存",说不出"已投递""已可用";
11
+ - 历史记录被事后改动,插件从记录本身看不出来,因为记录与它的副本永远自洽。
12
+
13
+ 所以 0.5.3 的主题不是再加功能,而是**把声明变成证明**:
14
+
15
+ > **0.5.3:可证明的状态。** 插件对模型陈述的每一条事实,都要有它能产出、模型能检验的证据;并且永远区分"已配置""已投递""已证明"。
16
+
17
+ ## 概念基元
18
+
19
+ 四个词贯穿本版全部目标,先定义再谈功能。
20
+
21
+ | 概念 | 回答的问题 | 谁拥有 |
22
+ | --- | --- | --- |
23
+ | **供给 supply** | 产品声明存在哪些能力、绑定、能力开关 | 全局配置与目录 |
24
+ | **取用 adoption** | 这个会话的环境选择用其中哪些 | 会话 |
25
+ | **物化 materialization** | worker 真的把它装进环境了吗 | 运行时与 worker |
26
+ | **证明 proof** | 有什么可被模型检验的证据 | 日志、探测、诊断 |
27
+
28
+ 三条不变式:
29
+
30
+ - **I1 层次不可互相代答。** 供给不能代取用,取用不能代物化,物化不能代证明。
31
+ - **I2 只有证明可以宣称"可用"。** 没有证明时如实说"不可确认",而不是默认可用。
32
+ - **I3 可证明的事实必须能从会话日志重建。** 重建不出来的,要显式标注为不可重建,而不是假装它不存在。
33
+
34
+ 0.5.2 的教训正好对应 I1:全局开关是供给,会话要不要用是取用,装了没有是物化——三层混成一个"开关",就会出现"我改了全局,为什么这个会话不变"和"配置关了但绑定还在"这类说不清的状态。
35
+
36
+ ## 目标
37
+
38
+ ### G1 绑定:从"开关"到"环境成员"
39
+
40
+ **概念纠正。** 绑定不是配置项,是**环境成员**。全局决定目录供给,会话决定取用,worker 决定可用。
41
+
42
+ - 全局关闭一个条目 = 它不在目录里,会话无从取用。**"覆盖复活已删除条目"这个概念上不存在**,不需要额外的冲突规则去禁止。
43
+ - 会话取用是二态(用 / 不用)加一个缺省(未表态即全取),不需要三态哨兵,也不借用能力侧的 `automatic`——那个词的含义是"由宿主推导",绑定没有可推导的决策。
44
+ - 取用 ≠ 物化成功:装失败的名字必须点名(接 `PTC-R002`),曾经 live 后消失的必须点名(`PTC-R006`)。
45
+
46
+ **验收**:不设会话取用时行为与 0.5.2 完全一致;A 会话不用某绑定、B 会话仍可用;全局关闭后会话不可取用且无"复活"路径;重启后取用关系随日志重建;物化失败在模型可见处点出名字。
47
+
48
+ ### G2 能力与上下文:把"已保存"升级为"已投递"
49
+
50
+ **概念。** 保存设置只改变供给,不证明模型看得到、也不证明 worker 初始化过。面板与提示必须分开说这三件事。
51
+
52
+ **验收**:保存后能报告目录是否出现该能力、新单元格能否真的取到它;三种状态(已保存 / 已投递 / 已可用)在面板与模型可见提示中区分;任一层失败都指名,而不是笼统"已生效"。
53
+
54
+ ### G3 历史证据:记录必须能自证未被改动
55
+
56
+ **概念。** 一份可以被事后编辑、且编辑后仍与自身副本一致的记录,不是证据。执行时要写入**不可重算的快照**,折叠时用它比对。
57
+
58
+ 现有的半成品:`completion` 与诊断已经互相校验(0.5.2),但 `diagnostics` 被改后再重算 `completion` 就自洽了——因为事件里没有第三份独立副本(`message.content` 为空)。
59
+
60
+ **契约**:快照字段随 journal schema 一起版本化;不一致即判该格不可信;**否决必须是重放终态**,不能进入重试循环(上一次实现因此挂死)。
61
+
62
+ **验收**:篡改 completion 或 diagnostic 都使该格不可信;恢复矩阵不退化;journal 迁移对旧记录保持兼容。
63
+
64
+ ### G4 改写历史:`editRewritesHistory`
65
+
66
+ **概念纠正。** "改写历史"不是删除,而是**取代关系**。旧单元格仍然留在日志里,只是不再参与环境构建;它声明的名字以"被取代"报告,而不是以"丢失"报告。
67
+
68
+ 这比"追加"更诚实:不假装旧执行没发生过,而是显式声明谁取代了谁(日志里记为 `supersedes`:这条记录取代了哪条)。默认关闭,开启时改写才真正替换目标单元格。
69
+
70
+ **验收**:关闭时全部既有用例不变;开启时"改写 → 后续格子读到新值"成立;被取代单元格的绑定以专门诊断点名;折叠能从日志重建取代关系。
71
+
72
+ ### G5 快照归属:REPL 面板的 generation
73
+
74
+ **概念。** 面板显示的不是"当前状态",而是**某个运行期的一次观察**。需要分开两件事:
75
+
76
+ - **新鲜度**:同一运行期内的投影刷新——清单仍然有效,不该闪成"不可确认";
77
+ - **归属**:跨运行期的快照——属于上一个进程,必须判为不可确认(既有契约,`repl-memory-projection` 的用例钉着它)。
78
+
79
+ 0.5.2 的客户端兜底只解决了新鲜度,没有表达归属。0.5.3 要在数据里表达"这份清单来自哪个运行期",而不是靠随机 UUID 与对象身份比较去猜。
80
+
81
+ **验收**:两个会话并行、其中一个结束时,另一个面板不闪;真正重建后显示不可确认并说明原因;既有契约用例保持绿。
82
+
83
+ ### G6 覆盖率:证据,不是仪式
84
+
85
+ **概念。** 覆盖率是"这段代码被执行过"的证据;阈值是策略,不是证据。0.5.2 为发版引入了"按改动文件 100% + 历史欠账白名单",那是策略例外。
86
+
87
+ **验收**:全局 `lines 100 / functions 100`,白名单清空;`review:finalize` 记录对应证明;发布流程不再需要"按改动文件"的特殊分支。
88
+
89
+ ## 非目标
90
+
91
+ - 不新增 PTC 语言特性,不改 DSH 的公共契约;
92
+ - 不做跨会话共享 worker 或跨会话共享 REPL 状态;
93
+ - 不为通过门槛而放宽断言或增加 coverage ignore(除非证明不可达并写入提交信息)。
94
+
95
+ ## 发布判据
96
+
97
+ 1. 六个目标各自的验收项全部有可执行证据;
98
+ 2. `npm run check`、`npm run coverage`(全局 100%)、`semantic:check`(unknown 0)全绿;
99
+ 3. `docs/` 中受影响的 owner 文档与 ADR 同步更新;
100
+ 4. Release 工作流 `stage` 成功,npm 进入 staged 状态;
101
+ 5. 发布说明按 [release-notes.md](release-notes.md) 的用户视角重写,不以功能清单代替变化说明。
102
+
103
+ ## 发布时机
104
+
105
+ 0.5.2 已经把"提前适配"这条路的代价演示过一遍:为一个尚未发布到 GitHub 的 alpha 改代码,等对应 rc 出来又要再改一轮,而 alpha 的接口形状在正式发布前并不稳定。
106
+
107
+ 因此 0.5.3 的发布以**通道**为准,不以"某天适配好了"为准:
108
+
109
+ | 通道 | 含义 | 对发布的作用 |
110
+ | --- | --- | --- |
111
+ | `latest` | 最新的已发布 rc | 必须通过;它决定能不能发 |
112
+ | `next` | 正在推进的下一条 rc 线,GitHub 已有发布说明 | 必须通过 |
113
+ | `alpha` | 提前适配,正式发布尚未出现 | 只报告,不阻塞 |
114
+
115
+ 规则:**插件可以提前适配 alpha(那是有价值的预警),但版本发布等待包含该形状的正式 rc。** 已经 staged 的包如果对应的是 alpha 形状的实现,就撤掉重新准备,而不是"先发出去再说"。
116
+
117
+ 通道的具体版本由 `npm run host:channels:update` 从 dist-tag 解析并写入 [compat/channels.json](../compat/channels.json),不手工编辑版本号;行为代码只做形状探测,由 `npm run host:shape-check` 守住。
@@ -13,7 +13,7 @@ Backend and browser entries accept `--list`, and selections can repeat. Omitting
13
13
 
14
14
  `npm run check` classifies the complete source candidate against HEAD, runs the appropriate checks below, checks source/ledger stability, and records the verified tree and verification scope for the commit hook. Run `check` alone when the ledger is absent or resolved; while findings remain unresolved, use `verify`. Do not run complete component suites around the same final command: that duplicates its work. Focused evidence is not a source-bound commit proof. Inspect a failed gate's stage/case, reproduce it with a focused entry, repair it, and rerun the final command only after the candidate is stable.
15
15
 
16
- The full branch of `check` runs `npm run verify:check`: `review:validate`, `host:baseline:verify`, `semantic:check`, `build:check`, the syntax checks and `scripts/deterministic-test-groups.mjs --check`, which selects `npm run test:client` and `npm run test:backend:fast`. The Client units run in the gate because a change can be covered only by them; the backend tier is the set of test files `semantic-obligations.json` names as evidence for at least one obligation, excluding coverage-only paths and the verification-tooling probes that fail by construction outside their harness. It targets a few minutes so the commit gate is actually run; [ADR 0046](adr/0046-gate-the-commit-with-the-semantic-evidence-tier.md) owns this split and its evidence.
16
+ The full branch of `check` runs `npm run verify:check`: `review:validate`, `host:channels:verify`, `host:shape-check`, `semantic:check`, `build:check`, the syntax checks and `scripts/deterministic-test-groups.mjs --check`, which selects `npm run test:client` and `npm run test:backend:fast`. The Client units run in the gate because a change can be covered only by them; the backend tier is the set of test files `semantic-obligations.json` names as evidence for at least one obligation, excluding coverage-only paths and the verification-tooling probes that fail by construction outside their harness. It targets a few minutes so the commit gate is actually run; [ADR 0046](adr/0046-gate-the-commit-with-the-semantic-evidence-tier.md) owns this split and its evidence.
17
17
 
18
18
  `npm run verify` keeps the complete behavioral gate: `test:client:visual` plus the whole ordinary backend inventory. CI runs `verify:ci`; release preparation runs `verify`, and coverage stays release-only. A change that touches a semantic owner missing from the graph must extend the graph, which widens the fast tier at the same time. The browser suites are not part of the commit gate: they are the complete visual contract and run in `verify`, in CI, and on demand.
19
19
 
@@ -45,11 +45,11 @@ The full local and CI behavioral gates run `test:client:visual`: one Client suit
45
45
 
46
46
  `npm run verify` and the full branch of `npm run check` validate the ledger, compare generated bundles, check syntax, run Client tests, and run all ordinary backend tests through `npm run test:backend` without coverage instrumentation. The ordinary runner selects the sorted root `test/*.test.js` inventory. Coverage-only runner/report tests and collection probes live under `test/coverage/` and are excluded from daily execution. The release coverage runner includes both inventories and sets `DSH_PTC_TEST_COVERAGE_SUITE=1` for mixed instrumented cases; the ordinary runner clears that marker. Both runners share the concurrency calculation. Backend test files run in separate Node processes. Compiler boundary protocols are divided into disjoint storage, Promise and reflection files so the bounded file pool can execute them alongside the parameter/namespace/error cases without sharing their native or PTC realms. The default process count is half the available CPUs, rounded down and bounded between one and eight; each process can also own runtime Workers. Tests within each file retain their existing ordering. The ordinary runner enables module mocks without preloading coverage hooks and removes inherited coverage and temporary bytecode settings from its test-process environment.
47
47
 
48
- After the shared ledger, baseline, semantic, build and syntax prerequisites pass, `scripts/deterministic-test-groups.mjs` selects its groups by gate: `--check` (the commit gate) runs only the semantic-evidence backend tier, the default `verify` runs the complete Client visual and backend groups concurrently on machines with at least 12 available CPUs, and `--ci` keeps the focused runtime group. Smaller machines retain serial phases. Every selected group must settle, and any nonzero exit, signal or startup failure rejects verification. Each browser pool retains its own CPU bound and process isolation.
48
+ After the shared ledger, channel, semantic, build and syntax prerequisites pass, `scripts/deterministic-test-groups.mjs` selects its groups by gate: `--check` (the commit gate) runs only the semantic-evidence backend tier, the default `verify` runs the complete Client visual and backend groups concurrently on machines with at least 12 available CPUs, and `--ci` keeps the focused runtime group. Smaller machines retain serial phases. Every selected group must settle, and any nonzero exit, signal or startup failure rejects verification. Each browser pool retains its own CPU bound and process isolation.
49
49
 
50
- Both verification scopes produce a review-bound proof. Coverage runs only before a new version is released, as specified by [ADR 0035](adr/0035-run-coverage-only-before-a-new-release.md). CI starts from a clean lockfile install and runs `verify:ci` on every supported Node and operating-system entry: frozen-baseline validation, semantic ownership, generated builds, Client tests, and focused runtime contracts covering compiler startup, helper-process lifecycle, plugin takeover, native-call transport, and durable argument diagnostics. Ubuntu Node 24 additionally exercises the packed plugin against both frozen official `latest` and `next` DSH graphs. Normal CI therefore proves portable installation and runtime integration without running coverage-specific suites, collecting coverage, or running the exhaustive semantic suite on hosted runners.
50
+ Both verification scopes produce a review-bound proof. Coverage runs only before a new version is released, as specified by [ADR 0035](adr/0035-run-coverage-only-before-a-new-release.md). CI starts from a clean lockfile install and runs `verify:ci` on every supported Node and operating-system entry: channel-manifest validation, semantic ownership, generated builds, Client tests, and focused runtime contracts covering compiler startup, helper-process lifecycle, plugin takeover, native-call transport, and durable argument diagnostics. A separate job per host channel packs the plugin and runs the host contracts against that channel's installed release. Normal CI therefore proves portable installation and runtime integration without running coverage-specific suites, collecting coverage, or running the exhaustive semantic suite on hosted runners.
51
51
 
52
- `host:baseline:verify`, which is part of deterministic verification, is offline. It requires the installed root DSH `next` cohort to match `package-lock.json` and validates the tracked `compat/latest` declaration and lock without installing it. CI separately runs `test:host-contract`: the root graph exercises `next`, while a temporary consumer created from `compat/latest/package-lock.json` exercises `latest` against the packed plugin. The tracked `compat/current` graph additionally captures the newest published official release, even when its distribution is on another npm channel; its isolated packed consumer is part of `test:host-contract`. Registry and official release lookup and lock regeneration belong only to `host:baseline:update` and the scheduled baseline-refresh workflow. That workflow publishes a changed frozen candidate as a pull request and explicitly dispatches this CI workflow once because its `GITHUB_TOKEN`-authored events cannot start another run; ordinary gates never install a floating channel.
52
+ `host:channels:verify`, which is part of deterministic verification, is offline: it validates the tracked channel manifest — schema, per-channel dist-tag and blocking role, full coverage of the probed host packages, one release per pinned cohort, and a recorded integrity for each — without resolving a tag or installing a host. It also reports which channel the development graph declares and whether the lock materializes that record, so the checkout's own graph is read against the manifest the CI jobs install from. CI runs `test:host-contract` as one job per channel. Each job installs its channel on demand from the manifest into an ignored cache, proves the installed versions against the manifest, packs the plugin, and runs the host contracts through a repository-external consumer; the `alpha` job reports without failing the workflow. Registry resolution belongs only to `host:channels:update` and the scheduled channel-refresh workflow. That workflow publishes a changed manifest as a pull request and explicitly dispatches this CI workflow once because its `GITHUB_TOKEN`-authored events cannot start another run; ordinary gates never install a floating channel.
53
53
 
54
54
  ## Release coverage
55
55