dsh-ptc-plus 0.3.1 → 0.4.1

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 (200) hide show
  1. package/README.en.md +66 -73
  2. package/README.md +66 -73
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-repair-zh.png +0 -0
  7. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  8. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  9. package/assets/ptc-plus-settings-en.png +0 -0
  10. package/assets/ptc-plus-settings-zh.png +0 -0
  11. package/client.js +30610 -722
  12. package/compiler-amaro.cjs +5 -0
  13. package/compiler-amaro.cjs.map +6 -0
  14. package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
  15. package/compiler-assets.json +3 -0
  16. package/compiler-core.cjs +554 -0
  17. package/compiler-core.cjs.map +6 -0
  18. package/compiler-platform.cjs +2 -0
  19. package/compiler-platform.cjs.map +6 -0
  20. package/compiler-typescript.cjs +388 -0
  21. package/compiler-typescript.cjs.map +6 -0
  22. package/docs/README.md +6 -0
  23. package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
  24. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +6 -4
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
  26. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
  27. package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
  28. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
  29. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
  30. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +9 -3
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
  32. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
  33. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  34. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
  35. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
  36. package/docs/adr/0019-plugin-settings-and-kill-switch.md +8 -6
  37. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  38. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
  39. package/docs/adr/0023-global-user-bindings.md +104 -0
  40. package/docs/adr/0024-repl-console-observation.md +33 -0
  41. package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
  42. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
  43. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
  44. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
  45. package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
  46. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
  47. package/docs/architecture.md +82 -22
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +96 -20
  51. package/docs/durability-design.md +61 -24
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/program-data-plane.md +6 -0
  55. package/docs/publishing.md +5 -3
  56. package/docs/runtime-reference.md +79 -25
  57. package/docs/semantic-validation.md +29 -0
  58. package/docs/stateful-binding-updates-proposal.md +755 -0
  59. package/docs/user-bindings.en.md +64 -0
  60. package/docs/user-bindings.md +64 -0
  61. package/docs/value-wire.md +3 -3
  62. package/docs/verification.md +57 -0
  63. package/index.js +374 -92
  64. package/internal/ast-traversal.js +5 -2
  65. package/internal/binding-descriptors.js +1 -0
  66. package/internal/binding-pattern-traversal.js +25 -0
  67. package/internal/binding-pattern.js +26 -29
  68. package/internal/binding-source-analysis.js +391 -0
  69. package/internal/binding-update-policy.js +66 -0
  70. package/internal/bounded-worker-round.js +56 -0
  71. package/internal/callable-definition-environment.js +42 -0
  72. package/internal/callable-source-catalog.js +129 -0
  73. package/internal/callable-source-encoding.js +47 -0
  74. package/internal/callable-source-facts.js +459 -0
  75. package/internal/callable-source-format.js +2 -0
  76. package/internal/callable-source-lowering.js +123 -0
  77. package/internal/cell-analysis-contract.js +25 -0
  78. package/internal/cell-analysis.js +252 -242
  79. package/internal/cell-completion.js +15 -0
  80. package/internal/cell-error.js +7 -0
  81. package/internal/cell-parser.js +66 -0
  82. package/internal/cell-rewriter.js +3 -553
  83. package/internal/commonjs-export-evidence.js +63 -0
  84. package/internal/commonjs-source-evidence.js +72 -0
  85. package/internal/compiler-amaro.cjs +2 -0
  86. package/internal/compiler-compression.js +12 -0
  87. package/internal/compiler-data.js +58 -0
  88. package/internal/compiler-descriptors.js +54 -0
  89. package/internal/compiler-dynamic-cache.js +48 -0
  90. package/internal/compiler-entry.js +35 -0
  91. package/internal/compiler-generated-names.js +17 -0
  92. package/internal/compiler-intrinsics.js +84 -0
  93. package/internal/compiler-module-links.js +34 -0
  94. package/internal/compiler-operations.js +61 -0
  95. package/internal/compiler-platform-factory.js +84 -0
  96. package/internal/compiler-platform.js +136 -0
  97. package/internal/compiler-record-roles.js +53 -0
  98. package/internal/compiler-region-output.js +259 -0
  99. package/internal/compiler-resource-lowering.js +103 -0
  100. package/internal/compiler-scope-facts.js +115 -0
  101. package/internal/compiler-service.js +109 -0
  102. package/internal/compiler-source-cache.js +32 -0
  103. package/internal/compiler-source-pieces.js +93 -0
  104. package/internal/compiler-source-regions.js +132 -0
  105. package/internal/compiler-storage-source.js +54 -0
  106. package/internal/compiler-text.js +15 -0
  107. package/internal/compiler-typescript.cjs +2 -0
  108. package/internal/config-spec.js +91 -25
  109. package/internal/cordis-tools-owner.js +130 -21
  110. package/internal/diagnostic.js +27 -1
  111. package/internal/direct-surface-owner.js +76 -23
  112. package/internal/dynamic-binding-evidence.js +16 -0
  113. package/internal/dynamic-environment-compiler.js +779 -0
  114. package/internal/dynamic-environment-integration.js +87 -0
  115. package/internal/dynamic-environment-runtime.js +362 -0
  116. package/internal/dynamic-native-calls.js +150 -0
  117. package/internal/dynamic-native-runtime.js +248 -0
  118. package/internal/dynamic-scope-analysis.js +90 -0
  119. package/internal/edit-transport-owner.js +28 -3
  120. package/internal/execution-seam-compat.js +193 -0
  121. package/internal/failure-reporting.js +118 -18
  122. package/internal/host-rpc.js +66 -0
  123. package/internal/isolated-worker.js +548 -0
  124. package/internal/kernel-child.js +139 -0
  125. package/internal/kernel-worker.js +911 -116
  126. package/internal/language-semantics.js +14 -0
  127. package/internal/legacy-binding-catalog.js +48 -0
  128. package/internal/legacy-cell-analysis.js +600 -0
  129. package/internal/legacy-cell-parser.js +50 -0
  130. package/internal/legacy-cell-rewriter.js +631 -0
  131. package/internal/legacy-repl-convenience.js +291 -0
  132. package/internal/managed-module-operations.js +170 -0
  133. package/internal/module-compilation.js +321 -0
  134. package/internal/module-runtime-platform.js +19 -0
  135. package/internal/module-transform-contract.js +12 -0
  136. package/internal/native-await-declarations.js +68 -0
  137. package/internal/native-root-compilation.js +54 -0
  138. package/internal/native-root-dynamic.js +67 -0
  139. package/internal/recovery-tips.js +27 -15
  140. package/internal/rejected-cell-editor.js +13 -8
  141. package/internal/repl-convenience.js +2 -201
  142. package/internal/repl-memory-projection.js +81 -12
  143. package/internal/repl-observation-interest.js +70 -0
  144. package/internal/repl-rewrite-contract.js +21 -0
  145. package/internal/repl-scope-normalizer.js +2091 -0
  146. package/internal/repl-value-observer.js +114 -0
  147. package/internal/rpc-contract.js +52 -0
  148. package/internal/runtime-bridge-owner.js +172 -28
  149. package/internal/runtime-config.js +48 -51
  150. package/internal/runtime-contexts.js +82 -10
  151. package/internal/runtime-messages.js +111 -0
  152. package/internal/session-cell-executor.js +455 -91
  153. package/internal/session-journal-recovery.js +684 -0
  154. package/internal/session-journal-schema.js +106 -0
  155. package/internal/session-journal.js +237 -440
  156. package/internal/session-log-view.js +42 -9
  157. package/internal/session-runtime.js +325 -80
  158. package/internal/session-state.js +249 -40
  159. package/internal/source-position-map.js +211 -71
  160. package/internal/stateful-module-compiler.js +154 -0
  161. package/internal/stateful-module-runtime.js +303 -0
  162. package/internal/stateful-root-compiler.js +598 -0
  163. package/internal/stateful-root-runtime.js +340 -0
  164. package/internal/typert-codec-compat.js +28 -0
  165. package/internal/typescript-transform.js +29 -0
  166. package/internal/user-binding-authoring.js +70 -0
  167. package/internal/user-binding-console-worker.js +184 -0
  168. package/internal/user-binding-console.js +148 -0
  169. package/internal/user-binding-draft-projection.js +335 -0
  170. package/internal/user-binding-model-context.js +31 -0
  171. package/internal/user-binding-runner.js +56 -0
  172. package/internal/user-bindings-owner.js +1065 -0
  173. package/internal/user-bindings-store.js +293 -0
  174. package/internal/user-bindings.js +353 -0
  175. package/internal/value-wire-schema.js +28 -0
  176. package/internal/value-wire.js +29 -20
  177. package/internal/worker-client.js +57 -67
  178. package/internal/worker-environment.js +45 -0
  179. package/internal/worker-output-relay.js +29 -0
  180. package/internal/worker-shutdown.js +14 -0
  181. package/package.json +81 -11
  182. package/src/client-activity.js +109 -45
  183. package/src/client-authoring-view.js +580 -0
  184. package/src/client-binding-review.js +230 -0
  185. package/src/client-bindings-data.js +29 -0
  186. package/src/client-catalog.js +127 -0
  187. package/src/client-code-editor.js +84 -0
  188. package/src/client-console.js +112 -0
  189. package/src/client-copy.js +370 -0
  190. package/src/client-feature-gates.js +62 -0
  191. package/src/client-host-compat.js +150 -0
  192. package/src/client-indicator-view.js +267 -0
  193. package/src/client-repl-view.js +292 -0
  194. package/src/client-rpc.js +20 -0
  195. package/src/client-settings-view.js +221 -0
  196. package/src/client-styles.js +106 -0
  197. package/src/client-tool-view.js +108 -0
  198. package/src/client-transport.js +159 -0
  199. package/src/client-workbench.js +521 -0
  200. package/src/client.js +233 -615
@@ -0,0 +1,103 @@
1
+ # Attach To The Host's PTC Execution Seam Across Generations
2
+
3
+ ## Problem
4
+
5
+ PTC Plus supplies the session-bound REPL by taking over the program-execution
6
+ seam DSH uses for `run_code`. DSH registers that seam as a Cordis service and
7
+ has renamed it between releases: the preceding generation published
8
+ `ctx.codeRuntime` (`@deepseek-ai/dsh-code-runtime`) and the current one publishes
9
+ `ctx.ptcRuntime` (`@deepseek-ai/dsh-ptc-runtime`). A plugin that declares the
10
+ removed name as a required injection never applies on the current host. Cordis
11
+ does not fail such a plugin: the fiber stays pending, the plugin's entry points
12
+ are never registered, and `run_code` silently keeps executing in the host's own
13
+ runtime instead of the session REPL.
14
+
15
+ The current seam also differs in more than its name. It resolves a request before
16
+ running it (`resolve(request)` → `run(spec)`), it publishes provider descriptors
17
+ (`executionInstructions`, `sandboxMode`, `timeout`) that gate host inputs, and it
18
+ reports file-confinement facts on the result. Its provider resolves a file policy
19
+ for every request and refuses to run without one, so an override that merely
20
+ forwards a bare request to the wrapped provider fails, and an override that
21
+ accepts a confinement request it cannot enforce silently weakens DSH's policy.
22
+
23
+ ## Decision
24
+
25
+ One owner attaches the plugin to the seam: `internal/execution-seam-compat.js`.
26
+ It selects the seam from the registered service, not from a version string, and
27
+ presents the same seam-neutral contract to the runtime bridge whatever the host
28
+ generation is.
29
+
30
+ **Selection is a live capability.** The plugin's static injection names only the
31
+ services both generations provide; the seam owner waits through `inject` for
32
+ whichever execution service the host registers, in a scope that also requires the
33
+ plugin's own services, and attaches once. When both are
34
+ registered, the current generation wins, and the preceding generation attaches
35
+ only when the current one is absent. Attachment runs in the injected scope, so
36
+ the plugin is unloaded when the host unregisters that service and reattaches when
37
+ it is registered again. A service that satisfies the name but not the contract
38
+ (`run` or `resolve` missing) is reported as a load failure naming the service.
39
+
40
+ **Execution is one neutral call shape.** The owner publishes `invokeUpstream`,
41
+ which reproduces each generation's own call shape — directly for
42
+ `run(request)`, and as `run(resolve(request))` for the two-step seam — and
43
+ `takeOver(execute)`, which replaces the single execution entry and restores the
44
+ exact descriptor it replaced. The plugin's entry receives exactly `program`,
45
+ `bindings`, and `signal`: the fields PTC Plus has always consumed. A resolved
46
+ spec's directory, deadline, and authority describe the provider whose execution
47
+ was replaced; they are neither forwarded to the session kernel nor re-derived.
48
+
49
+ **The seam describes the execution the plugin performs.** While the plugin owns
50
+ the seam it also answers the descriptors with its own behavior: no
51
+ `executionInstructions` from the wrapped provider (each cell reuses the session
52
+ kernel rather than starting a fresh process, and the plugin's own guidance is
53
+ delivered through the system prompt), and neither `timeout` nor `sandboxMode`,
54
+ because the REPL applies the session's configured budgets instead of a per-call
55
+ host deadline and performs no file confinement. DSH gates `timeoutMs`, file-policy
56
+ resolution, and sandbox escalation on those descriptors, so withholding them keeps
57
+ DSH's authority decisions with DSH instead of accepting an input the plugin would
58
+ then ignore. Withdrawal is unconditional rather than generation-conditional,
59
+ because the rule belongs to the plugin's execution rather than to a generation:
60
+ the preceding generation's contract defines none of these members and nothing in
61
+ its install closure reads them, so withdrawing there is inert, while a provider
62
+ that did publish them cannot outlive the takeover. Every takeover and every
63
+ withheld descriptor is released together, and a release leaves a descriptor that
64
+ another owner replaced in the meantime.
65
+
66
+ ## Alternatives considered
67
+
68
+ **Compare a DSH version and branch on it.** A version is not the contract that
69
+ broke; the registered service is. A gate also misreports the deployments between
70
+ the two known releases and reopens the same failure at the next rename.
71
+
72
+ **Keep `ctx.codeRuntime` as a required injection and add `ptcRuntime` as a
73
+ second requirement.** Cordis injections are conjunctions, so the plugin would
74
+ require an execution service the current host no longer provides and stay pending
75
+ on it.
76
+
77
+ **Register both services from the plugin and translate between them.** The
78
+ plugin would publish a service it does not implement, making this a provider
79
+ rather than a consumer, and would have to model the wrapped provider's resolution
80
+ semantics for callers that never reach its REPL.
81
+
82
+ **Forward the resolved spec's directory, deadline, and policy to the session
83
+ kernel.** The kernel has its own directory, budgets, and journal semantics; a
84
+ per-call host deadline would have to be reconciled with the configured budget,
85
+ and confinement the plugin cannot enforce would be reported as if it applied.
86
+
87
+ **Accept a confinement request and report it without enforcement.** That is the
88
+ silent bypass this decision exists to prevent: DSH would stop offering the
89
+ escalation it thinks it is offering.
90
+
91
+ ## Consequences
92
+
93
+ The plugin activates on both the current and the preceding host generation, and
94
+ one test owner covers the selection rules, the neutral call shape, the descriptor
95
+ takeover, and the release semantics for both. A host that registers neither
96
+ execution service leaves the plugin waiting, exactly as a required injection did,
97
+ rather than loading a plugin whose entry points can never run.
98
+
99
+ `run_code` on the current host no longer advertises a per-call deadline or file
100
+ sandbox, and DSH rejects those arguments with its own message instead of the
101
+ plugin silently ignoring them. Deployments that need confinement in `run_code`
102
+ therefore need a PTC execution environment other than this plugin's session
103
+ kernel; this is a real limitation of that environment and is reported as one.
@@ -0,0 +1,27 @@
1
+ # 0029 Keep Plugin Unavailability out of the Host Agent Lifecycle
2
+
3
+ Date: 2026-09-18
4
+
5
+ ## Problem
6
+
7
+ PTC Plus publishes its optional companion tooling into agent scopes it does not own. DSH composes each scope, and a third-party profile may load this plugin while composing only part of DSH's native tool set, so a scope can expose `run_code` and still omit the Skill tool, the preset service, or the Skill service this plugin publishes through. DSH 0.1.6 announces `agent/created` through a serial dispatch whose contract is that a listener rejection rejects `agents.create()`; earlier releases dispatched that event in parallel and only logged the failure. A scope could therefore turn this plugin's own unavailability into a failed subagent creation for an unrelated plugin, and report the same diagnostic once per scope. The preceding owner deferred one surface only (`run_code`), so the same reachable condition still threw from a listener the host now awaits.
8
+
9
+ ## Decision
10
+
11
+ An agent scope decides which surfaces it exposes, so an absent companion surface is not an error. `internal/cordis-tools-owner.js` keeps an agent whose scope omits `run_code`, the Skill tool, the `agentPresets` service, or the `skills` service pending, publishes nothing there, reports nothing, and retries the mount when the scope changes: on the DSH tools-change signal and on every later prompt assembly. The scope's composition belongs to the host and its user, a session creates many scopes, and an absent surface may appear later as a tool is registered or a service is provided.
12
+
13
+ A host-driven call never rejects for this plugin's own unavailability. `agent/created` and the prepended `system-prompt/assemble` waterfall install through one contained entry point: a scope whose exposed preset, services, publication, or child-fiber activation contradicts the DSH contract withdraws from that scope, reports one warning for that scope, and stops retrying it while the owner lives. Withdrawal does not hide a defect — it reports the scope in which the plugin could not serve, at the volume one diagnostic per scope produces.
14
+
15
+ Install-time enumeration keeps one disposition per scope and adds one outcome. A scope present at installation whose exposed surfaces contradict the contract rejects `ready`, so enabling the setting fails and rolls back instead of reporting success for a mount that never happened. A scope that merely omits a surface defers there exactly as it does later, because the missing member can still appear while the plugin is loaded, so `ready` resolves and the setting stays enabled. When every scope a host enumerates omits a surface, enabling the setting therefore publishes nothing and reports nothing: an absent surface is not a defect in this plugin, and one diagnostic per scope would repeat for every scope a session creates.
16
+
17
+ ## Alternatives Considered
18
+
19
+ 1. **Contain at the `agent/created` listener only and keep install-time throwing for an absent service or tool.** Rejected: the same precondition would then have two outcomes decided by when the agent appeared, and the absent surface is a per-scope fact rather than a host-level one. One disposition per scope keeps the judgment in the place that owns it.
20
+ 2. **Treat every activation failure as a deferral.** Rejected: a scope whose exposed preset or services contradict the DSH contract is not repaired by a later tool registration, so retrying it would repeat a failing mount and its diagnostic every turn — the reporting volume the reported defect complains about.
21
+ 3. **Let the host log the rejection, as releases before 0.1.6 did with a parallel dispatch.** Rejected: the current host awaits creation listeners, which makes this plugin's unavailability the failure of an operation another plugin requested. Depending on one dispatch mode would also break again on the next host change.
22
+ 4. **Reject the agent and let the calling plugin decide what to do.** Rejected: the calling plugin enabled neither PTC mode nor its companion tooling, and cannot withdraw a contribution it does not own.
23
+ 5. **Require every agent scope to expose the companion surfaces, and report the ones that do not.** Rejected: a whitelist composition is a supported host configuration, and PTC mode must remain usable in the scopes that do expose `run_code`.
24
+
25
+ ## Consequences
26
+
27
+ A PTC agent scope that omits a companion surface keeps PTC mode and simply does not receive the official Cordis tool, owner guidance, or companion Skill; the plugin reports nothing and stays silent there, so the absence is observable through the missing model-visible surface rather than a repeated log line. `cordisToolsEnabled` therefore describes the scopes that expose every companion surface rather than every scope that exposes `run_code`; [ADR 0020](0020-optional-cordis-tools-in-ptc-mode.md) owns that setting and now states the same condition. Enabling the setting still fails and rolls back when a scope present at installation has the surfaces but cannot be mounted, so a broken host stays visible at the configuration change. Withdrawing from a created scope also stops the attempt, so an unusable scope inside a long session produces one warning rather than one per turn.
@@ -0,0 +1,27 @@
1
+ # 0030 Serve Both TYPERT Codec Generations from One Codec
2
+
3
+ Date: 2026-09-18
4
+
5
+ ## Problem
6
+
7
+ PTC Plus publishes its Remote services through DSH's TYPERT registry, and that registry validates every strict codec it is handed before any endpoint exists. The codec carries the decoder for one wire member, and DSH changed where the host reads it: the preceding generation keeps the decoder on `codec.schema` and evaluates `codec.schema.parse(value)`, while the current generation keeps a `create()` factory, rejects a codec without it, and evaluates `codec.create().parse(value)`. The plugin published only the preceding shape, so a release that requires `create` fails registration inside the plugin's own activation: the Host entry never activates and the Client entry reports a failed fiber, although every import, load check, and type-level use still succeeds.
8
+
9
+ ## Decision
10
+
11
+ `internal/typert-codec-compat.js` owns the codec shape PTC Plus publishes. `strictCodec({ typeSymbol, schema })` returns one codec that carries both members — `schema` for the preceding generation and `create()` for the current one — and both resolve to the same decoder object, which presents the maintained schema runtime as the `parse` member each generation evaluates.
12
+
13
+ Each generation validates only the member it owns and ignores the other, so one published contract serves whichever shape the installed host implements. Compatibility follows the live acceptance behavior of the registry rather than a release version, and the decoder is published once, so a host that reads both members cannot obtain two different decoders for one wire member.
14
+
15
+ CI registers the packed contract with the installed host's own registry on both the stable and alpha channels (`scripts/dsh-rpc-contract-smoke.mjs`), so a generation that changes this validation fails the packaging check instead of reaching users.
16
+
17
+ ## Alternatives Considered
18
+
19
+ 1. **Detect the installed generation and publish the matching member.** Rejected: the registry exposes no capability flag or version-free signal for this, so detection would end up as a DSH version gate — the compatibility mechanism [ADR 0017](0017-track-the-latest-dsh-public-surface.md) replaces. The two shapes are also not mutually exclusive, so nothing forces a choice.
20
+ 2. **Publish only `create()` and drop `schema`.** Rejected: the preceding generation rejects a codec without `schema.parse`, so that trades one release's compatibility for another's instead of covering both.
21
+ 3. **Keep a second descriptor builder for the current generation.** Rejected: two builders would drift, and the wire contract — type symbols, parameter names, wire fields, the shared result envelope — is one contract that must stay identical for both generations.
22
+ 4. **Rely on the CI import and load check to catch this.** Rejected: both pass with an unacceptable codec. The failure appears only when the registry validates a registration, which happens inside plugin activation in the Host and in the browser.
23
+ 5. **Publish the codec members lazily through getters that probe the host.** Rejected: a getter would have to resolve the host's validation rule per access, and a codec is read while the host builds its own records — a probe there is both fragile and unnecessary when both members can simply be correct.
24
+
25
+ ## Consequences
26
+
27
+ One plugin build serves the stable and alpha channels for this surface, and the compatibility rule lives in a single module with the decoder published once. A future generation that requires another member, or that rejects a codec carrying a member it does not know, needs this owner updated; the regression test is CI's registration against the installed registry, which fails on the channel that changed instead of on the user's machine. The decoder keeps the project's maintained schema runtime, so a host that validates payloads itself still evaluates the same semantics the plugin's own tests exercise through both members.
@@ -9,9 +9,12 @@ PTC Plus 把 DSH PTC 模式的顶层 `run_code` 变成与 session 绑定的连
9
9
  | Authority / policy | DSH / 宿主 | 不复制;每次 native tool dispatch 仍经过原流水线 |
10
10
  | Capability view | DSH 当前 scope | 保留 native typed `tools.*`,为一个 cell 建立统一 lease |
11
11
  | Evaluation | session worker | 连续求值、顶层 binding、预算与输出编码 |
12
+ | User-global defaults | PTC Plus Host + session worker | 原子存储、源码派生快照、请求激活与 session-local shadow |
12
13
  | Journal / replay | PTC Plus + session log | 记录 call transcript、settlement、completion 和恢复边界 |
13
14
  | Presentation | DSH + PTC Plus | 保留 native guidance,并追加 REPL 指引与最小 explorer 声明 |
14
15
 
16
+ 求值层由插件自有的 helper 进程承载:Host 只持有 `IsolatedWorker` transport,真实 `node:worker_threads` session worker 与其私有 `MessagePort` 都留在 helper 内,IPC 只传输 plain data。宿主回收卡住的计算时只强杀自己的 helper process,不再在 DSH 进程内执行已测得会落入 Node 24/26 cppgc teardown fault 的 worker 强杀;`IsolatedOwner` 只在该实例的 real exit、output close 和 cleanup 都完成后释放。kernel、console、candidate 三个入口各自拥有 owner。helper 的 `init` 携带 fork 前 snapshot 的投影环境,user Worker 及其子进程保持原 env 契约,不会继承 `NODE_V8_COVERAGE` 等宿主 instrumentation;inner Worker 的 compute budget 在每次请求提交时通过 helper 的 on-demand sample 取新鲜 `eventLoopUtilization` baseline,ready handshake 另有启动样本;采样失败按 worker-exit 有界失败且不派发 prepare/run。唯一例外由 [ADR 0024](adr/0024-repl-console-observation.md) 所有:同一 worker 已确认存在未完成的 observation 时,prepare 可先于预算启动,确认 observation 完成后才开始计时。
17
+
15
18
  主入口接管模型直接发起的顶层 `run_code`,并通过 DSH tool registry 真实注册 `edit_run_code`。
16
19
  使用 code-only direct-tool projection 的 PTC request 始终按固定顺序暴露 `[run_code, edit_run_code]`;插件用 agent scope 的
17
20
  `tools.register()` 真实注册 edit transport,并用同一 scope 的 `tools.presentAs('both')` 放行这两个 provider tool call,再把其他 native tool 保留在 program SDK。首次可判定的 prompt assembly 在安装这项 presentation effect 前建立 agent composition owner;注册与 mode 记录都在所属 session、agent 或插件释放时撤销,native-mode 与无关 agent scope 不继承 `edit_run_code`。
@@ -31,32 +34,61 @@ DSH 为一个 agent composition 固定选择 `native`、`ptc` 或 `both`。首
31
34
 
32
35
  `internal/session-log-view.js` 单次前向扫描 session events,分别投影最新执行、可编辑目标、rewrite metadata、规范 journal 中的 Cordis transcript 计数和恢复 tip
33
36
  所需事实。`edit_run_code` 不产生专用 runtime context:真实 call/result 已完整表达操作身份和结果,额外 contribution
34
- 只会触发 DSH 聚合 runtime-context 全量快照并重复无关 policy 文本。只有失败或完成状态不可信的 rewrite feedback 才保留独立生命周期;成功的透明改写不产生 runtime context。
37
+ 会重复已有事实;此类信息不进入独立 PTC 消息。只有缺少有效 journal、无法确认完成状态的 rewrite feedback 才保留独立生命周期;已结算失败的恢复指引由当前结果表达;成功的透明改写不产生 runtime context。
35
38
 
36
- 插件卸载时恢复仍由自己持有的 `CodeRuntime.run` 与 `presentationMeta` 属性;若外层插件仍持有旧 wrapper,已卸载 wrapper 会透明委托原 provider,不会恢复已释放的 session 状态。
37
- 稳定 REPL 指引说明 cell 是 async function body、module 使用 dynamic import 或 require,并要求显式
38
- return 或打印需要展示的值。失败恢复先按状态分类:解析或 preflight 失败且 `state: unchanged`、无外部效果证据时,
39
- 禁止重发完整源码,优先使用 `edit_run_code` 做精确修正并重放完整 cell;`state: partially-applied` 或可能已有外部 effect 时新建短
40
- `run_code` 并复用已有 binding,完整重写只保留给结构性改动或超出编辑预算的修正。能力和命令执行依赖当前 request
39
+ 插件卸载时恢复仍由自己持有的执行缝 `run` 与 `presentationMeta` 属性;执行缝由 `internal/execution-seam-compat.js` 按宿主注册的服务选定,并同时收回该插件无法兑现的 provider 描述符(见 [ADR 0028](adr/0028-attach-to-the-host-ptc-execution-seam.md))。若外层插件仍持有旧 wrapper,已卸载 wrapper 会透明委托原 provider,不会恢复已释放的 session 状态。
40
+ 稳定 REPL 指引与执行共同从 `binding-update-policy.js` 取得语言代际。新代际说明 TypeScript、顶层 await/return、静态 import/export 与所选绑定更新策略;旧兼容态才按其独立开关说明支持的模块语法。显式结果使用 return,日志使用 console;原生表达式 completion 同样可以提供结果值。失败恢复先按状态分类:result 已证明解析或 preflight 未执行且提供符合任务意图的 validated repair 时,
41
+ 优先直接使用带目标保护的 `edit_run_code`,无需等待 recovery context;该验证只证明语法/preflight 接受。其他小型修正可用 edit 执行完整 cell;
42
+ 已执行或可能产生外部 effect 的目标,则必须依据操作 owner 的 retry/idempotence 契约和执行事实判断重跑。短 `run_code` 可以复用仍存活的 binding,但缩短源码不能证明幂等。能力和命令执行依赖当前 request
41
43
  与 execution world,模型必须先探查 live binding、实际 executable 和路径语义,不假设某个平台、shell 或 package runner。
42
44
 
43
45
  live `PTC-C001` 若精确落在源码 EOF,独立的 bounded analyzer 只把追加单个 `}`、`)`、`]` 的三个源码分别送入该 cell 提交时的同一 preparation context。仅当唯一候选通过、无 binding collision,现有 exact editor 能从不超过固定预算的唯一尾部物化同一源码,并且 runtime 已取得该 rejected `run_code` 的持久 call sequence 时,诊断才输出一行可直接调用的 `edit_run_code({ edits: [...], expected_target_call_seq })`。edit transport 把该 sequence 与其 call event 捕获的目标比较,目标已变化时不编辑也不派生执行。这项 validated repair 优先于长度阈值,但只证明语法/preflight 接受;插件不自动 dispatch,也不改写原始 `run_code`。非 EOF、多候选、多 token、超预算、缺少目标身份或后续 preparation 拒绝均使用 length-adaptive help。
44
46
 
45
- 稳定指引保留这些跨任务不变量和失败恢复的优先动作。长 cell 失败的短提示直接作为当前 `PTC-X001` 的结构化 `help` 输出,避免一次性建议触发完整 runtime-context 快照;重复绑定失败和当前 execution world 中由诊断确认的 executable、shell 或 path 错误仍由当前 session log 派生为 `tools:ptc-plus-tip/<trigger>/<ordinal>` runtime context,分别提示能力探查或重新确认环境。投影只接受 DSH system-prompt owner 的规范快照,并按命名 section 的有效状态变化重建提示;聚合快照重复同一 section 不增加次数,正文不参与身份判断。
47
+ 稳定指引保留这些跨任务不变量和失败恢复的优先动作。长 cell 失败的短提示直接作为当前 `PTC-X001` 的结构化 `help` 输出;重复绑定失败和当前 execution world 中由诊断确认的 executable、shell 或 path 错误仍由当前 session log 派生为以 `tools:ptc-plus-tip/<trigger>/<ordinal>` 标识的独立 notice,词法失败指向可见源码与初始化,worker-owned capability 失败才指向能力探查,平台错误则提示重新确认环境。投影合并规范 PTC notice 与历史 DSH system-prompt snapshot sections,同一已投递 identity 跨两种来源只计一次,正文不参与身份判断。
46
48
  相同提示受 `tipCooldownMessages` 间隔约束,连续未解决时才升级为详细版本,成功 cell 会重置未解决计数;提示不会改变
47
49
  system sections、tool schema 或 tool order,也不假设 Windows、WSL、POSIX、shell 或 package runner。
48
50
 
49
- 当前策略位于独立的 `internal/recovery-tips.js` local provider,核心只消费其有界的 named context。另一个固定名称 `tools:ptc-plus-cordis-recovery` 只在新 agent 或 Cordis 重新启用代际观察到历史 Cordis transcript 时出现,将 replay value 限定为历史数据,并保持到 session log 出现新的成功 `cordis_inspect*` settlement;它不受疲劳阈值控制、不重放调用,也不推断 live process 是否实际丢失。外部决策插件必须提供稳定的 facts/decision contract 才能接入;缺少该契约时,核心不猜测跨插件 API,并继续使用 local provider。
51
+ 当前策略位于独立的 `internal/recovery-tips.js` local provider,核心消费其有界 named tip,并按 ADR 0010 的独立消息契约投递。另一个固定名称 `tools:ptc-plus-cordis-recovery` 只在新 agent 或 Cordis 重新启用代际观察到历史 Cordis transcript 时出现,将 replay value 限定为历史数据,并保持到 session log 出现新的成功 `cordis_inspect*` settlement;它不受疲劳阈值控制、不重放调用,也不推断 live process 是否实际丢失。外部决策插件必须提供稳定的 facts/decision contract 才能接入;缺少该契约时,核心不猜测跨插件 API,并继续使用 local provider。
50
52
 
51
53
  ## 设置与启用开关
52
54
 
53
55
  Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,优先使用 provider-owned `installSection()`,并兼容根模块的 `installSettingsSection()`;两条路径共享同一 fallback、watch、rollback 与 disposal 语义。字段清单、默认值与校验来自 `internal/config-spec.js`。
54
- Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled` 是 kill switch:关闭时只保留 settings 注册和设置卡片;`enhancedToolView` 是默认开启的正文展示开关,关闭时只交还 DSH 原生 `run_code`/`edit_run_code` generic row,
55
- 不注册 runtime、hook、tool surface 或 system prompt section;`ptc` 与兼容 `code` preset 会话在头部单独显示 `PTC Plus` 指示器。设置卡片显示“已启用/已停用”,稳定指引不包含 UI 品牌名。
56
+ Client half 通过宿主当前代的设置座位呈现全部配置:旧代际是 `settings.plugin.item`(keyed,按 namespace),当前 DSH 是 Plugins 页面的 `plugins.row.config`(keyed,键 `<包名>#<行 id>`),两者由 `src/client-host-compat.js` 同一次发布、由声明槽位的一代渲染,页面按 `summary`/`page` 视图分别取一句描述与完整表单([ADR 0019](adr/0019-plugin-settings-and-kill-switch.md))。`enabled` 是 kill switch:关闭时保留 settings 注册、设置卡片与撤销旧 PTC 声明所需的被动 presentation cleanup;`enhancedToolView` 是默认开启的正文展示开关,关闭时只交还 DSH 原生 `run_code`/`edit_run_code` generic row,
57
+ 总开关关闭时不注册 runtime、执行 hook、tool surface 或 system prompt section,也不读取 binding 存储;`ptc` 与兼容 `code` preset 会话在头部单独显示 `PTC Plus` 指示器。设置卡片显示“已启用/已停用”,稳定指引不包含 UI 品牌名。
56
58
  `autoDescribeRunCode` 默认开启且只控制请求绑定的本地执行策略。开启时,缺少外层 `run_code.description` 的调用使用派生参数通过本地 DSH 校验,备用摘要仅进入 presentation metadata;关闭时由 DSH 校验原始参数。两种状态的模型可见 tool schema、tool order 和 system sections 保持字节稳定并包含 required `description`;原始调用参数、cell 与嵌套 native 参数保持不变。
57
59
  所有字段都由 settings watch 即时交给各自 owner,且不替换已有 session-bound binding。每个已提交 cell 固定其提交时的配置代际,timer、worker 消息、program binding bridge、结果校验和诊断共同消费该快照;重配置更新随后提交 cell 的默认值。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚。settings 服务缺失时 Host 回退到 composition config,并保持相同的运行时语义。
58
60
 
59
- `cordisToolsEnabled` 默认关闭且即时生效。Host 只在可见 `run_code` 的 agent scope 中挂载官方 `@deepseek-ai/dsh-tool-cordis`,并通过公共 `agentPresets` service 定位 shipped `cordis` preset,再用维护中的 Skill filesystem plugin 把其 companion Skill 目录发布到同一 scope。两个 child fiber、tool guidance 与 `cordis-plugin-development` Skill 是一个 mount;首轮 request 等待完整发布和 scoped Skill load 验证,关闭、agent/runtime 释放或任一激活失败时逆序卸载。工具名、数量、schema 和 guidance 直接来自 official tool fiber,Skill 内容直接来自 shipped preset,Host 均不复制。官方插件同时向 process-global `cordisInspect` 注册 Host provider;owner 将 manifest 相同的 per-agent 注册合并为引用计数 lease,查询委托给当前仍存活的官方 registration,最后一份 lease 释放后才注销 provider。manifest 不一致时启用失败。该开关不切换 preset,也不改变 code-only direct-tool projection。
61
+ `bindingUpdates` 是新配置的统一入口,默认 `stateful`,也可选择 `protected`。Host schema 保留该字段原始缺省状态,再由 `runtime-config.js` 把完整旧策略映射到相应新策略;混合旧开关保留显式 `legacyBindingSettings` 兼容态。Client 复用同一 resolver,仅在兼容态展示旧开关;用户选择统一开关时,一次 settings update 同时写入新策略并清除兼容标记。
62
+
63
+ `cordisToolsEnabled` 默认关闭且即时生效。Host 只在同时暴露 `run_code`、`skill` 工具与 `agentPresets`/`skills` service 的 agent scope 中挂载官方 `@deepseek-ai/dsh-tool-cordis`,并通过公共 `agentPresets` service 定位 shipped `cordis` preset,再用维护中的 Skill filesystem plugin 把其 companion Skill 目录发布到同一 scope。两个 child fiber、tool guidance 与 `cordis-plugin-development` Skill 是一个 mount;首轮 request 等待完整发布和 scoped Skill load 验证,关闭、agent/runtime 释放或任一激活失败时逆序卸载。缺少其中任一 surface 的 scope 不是错误:owner 保持 pending、不在该 scope 发布内容,并在工具 surface 变化或下一次 prompt assembly 时重试;由宿主自身调用触达的不可用 scope 只从该 scope 撤出并记一条 warning,不使 `agents.create()` 或 prompt assembly 失败([ADR 0029](adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md))。工具名、数量、schema 和 guidance 直接来自 official tool fiber,Skill 内容直接来自 shipped preset,Host 均不复制。官方插件同时向 process-global `cordisInspect` 注册 Host provider;owner 将 manifest 相同的 per-agent 注册合并为引用计数 lease,查询委托给当前仍存活的官方 registration,最后一份 lease 释放后才注销 provider。manifest 不一致时启用失败。该开关不切换 preset,也不改变 code-only direct-tool projection。
64
+
65
+ ## Global User Bindings
66
+
67
+ `internal/user-bindings.js` 是条目、文档、请求快照和有界声明的严格规范化 owner。它从命名 value export 的 TypeScript 源码派生 symbols、binding kind、body-free declaration、durability 与 fingerprint,并封闭校验所有衍生字段;调用方不能提交一份与源码不一致的声明或 snapshot。`namespace` scope 产生一个条目名对象,`top-level` scope 产生选定导出;保留名称和启用集合中的调用标识符冲突在持久化或请求激活前拒绝。
68
+
69
+ 可选 `modelContext` 与源码一起持久化,包含默认开启的接口注入开关 `includeDeclaration` 和独立注入的提示词 `instructions`。声明只有源码派生这一份,不另行编写。非空 metadata 参与条目 fingerprint;旧条目省略该字段时保持原序列化与 fingerprint,存储文档无需迁移。此前保存的 `enabled` / `declaration` 字段保留规范化形式以验证旧 fingerprint,但旧自定义声明不用于展示或注入;旧关闭状态映射为声明关闭、提示词为空,下一次编辑保存使用新字段。Client 安全的结构校验和偏好映射由 `internal/user-binding-model-context.js` 复用,预算和完整 schema 见 [ADR 0023](adr/0023-global-user-bindings.md)。
70
+
71
+ `internal/user-bindings-store.js` 独占 `$DSH_HOME/ptc-plus/bindings.json`。单一 JSON 文档避免源码与 metadata 双源;进程内队列、file lock、磁盘文本比较、expected revision 和 atomic replacement 共同防止并发覆盖。每次 mutation 在替换前验证完整 enabled snapshot 的声明预算;外部写入的超限文档按损坏输入处理。损坏输入保持显式 error,不会被空文档静默覆盖。候选与正式激活的相对 import 都以该文件所在目录为基准,因此 session cwd 不会改变 helper 的依赖解析。
72
+
73
+ prompt assembly 为每个 PTC request 取得一次当前启用条目快照,作为随后 dispatch 的期望集合。`tools:ptc-plus-user-binding-defaults` 从首轮通过追加的 PTC API catalog 展示条目提示词及按开关选择的源码派生接口,按稳定 ID 排序,只描述配置 API 与执行时初始化契约;组装不执行源码,也不读取激活状态或加入 revision。`/binding` 保存并启用、会话中途启停、删除或修改模型上下文后,下一次宿主允许且接受的步骤追加当前说明或撤销旧说明,无需等待激活。条目变化不改写 system、tool schema、顺序或旧消息;未变说明按已提交日志与公共 ordered surface 去重,重启后继续使用相同证据。停用条目不贡献内容,关闭声明且提示词为空的条目同样省略。
74
+
75
+ 配置段是全局绑定提示词与接口的唯一注入来源,不包含实现源码;成功初始化、session-local shadow 和 worker 重建本身不生成活动公告,也不触发相同配置重发。执行结果与诊断拥有实际行为证据;需要验证可用性时只对已知名称做无副作用观察。历史 `tools:ptc-plus-user-bindings` 活动段保留读取支持,下一次允许且接受的快照撤销旧活动声明一次。`tools:sdk` 与 direct-tool schema 不随条目内容改变。配置 API 不证明当前值,也不延长被 compaction 遮蔽的 session-local 状态。runtime 在 cell preflight 前把期望集合映射到 BindingCatalog,再由 worker 激活:当前覆盖粒度为 `per-name`,同名 session-local binding 只覆盖对应全局名称,同条目的其他名称继续激活;v1-v7 历史 cell 保留整条 entry 退出的旧粒度。条目级失败产生诊断并从该 cell 排除,不阻塞独立代码。失败 initializer 一旦发起 program call,整个 cell 进入 volatile,因此只包含成功条目的结果 snapshot 不会被误作该调用的 cold replay source。
76
+
77
+ worker 对 namespace 成员和 top-level 导出保留 ECMAScript module live read;对顶层名称的赋值或重声明将该名称转换为 session-local binding,并使该名称在后续全局激活中保持 session-local 覆盖,同一条目的其他名称不受影响;v1-v7 历史 cell 仍按整条来源条目退出。binding module 使用稳定 worker-global proxy 访问当前 request 的全部 program namespace;proxy 在调用时读取 AsyncLocalStorage 中的原 cell lease,因此旧 continuation 即使恰逢下一 cell 运行也不能借用其 authority。新 namespace 可在后续 request 安装,已消失 namespace 的 proxy 不再提供 member;与任何既有 worker global 冲突时,bridge 在写入 global 前拒绝整个安装,避免覆盖 Node intrinsic 或留下部分 namespace。一个导出闭包一旦可能被普通 session binding 保存,worker 就无法证明其不可达,因此相应 synthetic-module 解析基准与已安装 proxy 保守保留到 worker 结束。worker 从未成功暴露过条目时,disabled 或 empty activation 不安装 bridge;若本次尝试全部失败,则在执行 cell 前移除本次安装的 bridge。
78
+
79
+ 配置段以消息文本直接投递,不进入 system renderer;提示词与声明中的 `{{...}}` 保持字面量,不被解释为宿主变量。投递遵守 runtime-context suppression、取消和步骤准入,解除屏蔽后的下一请求同步当前说明。worker 对同一条目 ID 只比较源码、scope、namespace 调用名和有序导出列表来决定模块复用;模型上下文、purpose 或 top-level 显示名称变化不重置模块状态,也不重复初始化。
80
+
81
+ 每个结算 cell 把实际激活的完整 snapshot 放入私有 `meta.dshPtcPlusUserBindings`。包含模型上下文的完整 fingerprint 继续拥有快照校验与 provenance。snapshot v2 的 `transform` 由固定版本的 `internal/typescript-transform.js` 编译器 owner 提供,并参与快照 fingerprint;恢复先验证转换代际,不能用当前编译器替代未知历史转换器。未记录该代际的非空 v1 snapshot 按 unknown-boundary 规则收缩,空 v1 snapshot 保持原 fingerprint 和恢复资格。journal 的 `userBindingsReusePolicy` 为新 cell 记录 `implementation-v1`;v1-v5 则迁移为 `fingerprint-v1`,v6 保留其记录值。覆盖粒度与复用代际相互独立:`userBindingsShadowPolicy` 为新 cell 记录 `per-name`,v1-v7 迁移为 `whole-entry`;`userBindingNames` 按名称记录该 cell 结算时的 provider/local/absent/unknown 事实,provider 事实另带 `entryId`,`whole-entry`、noop 与 discarded cell 为显式 `null`,并与并行快照共同校验名称证据的完整性与来源。对仍可证明的历史,cold replay 重新验证源码及其所有派生字段,逐 cell 应用记录的复用策略,保留因 purpose 或 top-level 显示名称变化而发生的模块重置;接续的 live cell 使用新策略,不因策略切换本身重置模块。恢复不读取当前文件来替换过去状态,也不重新派发初始化中的宿主调用。缺失或未知策略与其他无法证明的 metadata 一样,按 session recovery 的 unknown-boundary 规则收缩。
82
+
83
+ `internal/user-bindings-owner.js` 只在 `userBindingsEnabled` 开启时经 `internal/host-rpc.js` 注册由宿主 Gateway、Host/Origin fence 与浏览器认证保护的管理 Remote。它从精确 agent scope 的公共 `run_code` tool view 判断资格,在 agent 创建和 `tools/change` 时协调 `/binding`,不依赖 preset 名称。命令 registration 由该 agent 的注入 fiber 持有;命令服务迟到时保留 pending fiber,回调若已失去资格则失败并清理占位。生命周期监听的安装代际独立于请求与 projection 代际,不能因 projection 就绪而跳过注册。Agent disposal 只清理该精确 Agent 的资源,session disposal 才按 session ID 清空。Client 从 Host command directory 判断首轮前的 composer 编写入口,已有文本不被覆盖。REPL 全局绑定区和 Settings 管理弹窗复用完整工作台,通过 RPC 管理持久条目,不依赖当前存在 PTC 会话;会话观察及其独立 UI metadata 边界见 [ADR 0024](adr/0024-repl-console-observation.md)。
84
+
85
+ `/binding new` 与 `/binding edit` 经 `agent.steer` 追加完整编写指引并启动普通 turn 后完成 admission;该成功不宣称草稿已生成。`internal/user-binding-authoring.js` 拥有稳定 `code.submitBindingDraft({requestId, entry})` SDK、发现 schema 与字段说明,候选校验仍由 `user-bindings.js` normalizer 拥有。runtime bridge 在 cell 创建时捕获请求,提交时核验精确 agent、requestId、代际、lease 与单次提交资格,并在异步验证后重查。SDK 存在性由配置决定,瞬时资格不通过挂卸 tool 或 Skill 表达。program transcript 的 cold replay 只返回旧交接结果,不再提交或恢复草稿。
86
+
87
+ 接受结果的私有 metadata 同时保留 opaque locator 与完整 source-owned candidate,后者携带 requestId 和 commandId。`ptcPlusBindingDraft` projection 从真实 command、turn 和接受结果派生当前编写状态,并独立保存只读历史;历史可跨重挂载、seed 和 owner 代际重建,旧 locator 不恢复。管理 Remote 不提供 caller/session identity,因此草稿 RPC 只接受 capability。用户保存或丢弃完成后,Host 消费可写 locator,并用公共 `Session.append` 追加引用 accepting result 的结构化 action notice;正文只含身份与保存/启停事实,不含源码或 locator,也不宣称激活。回执 append 失败不能撤销已完成写入或恢复保存资格。`draft-review` 提供当前生命周期内的只读结果,持久关系由 projection 的精确 source relation 验证;不能用后来修改的同 ID 存储条目替换历史源码。完整格式由 [ADR 0023](adr/0023-global-user-bindings.md) 拥有。
88
+
89
+ Client 遇到冲突后重读 catalog、draft 和 review;只有权威空 draft 才撤销操作,仍有效的候选保留供用户决定重试。new draft 使用 create-only write,edit 保持原 ID;用户保存为停用或保存并启用都原子认领草稿并校验 revision,并发 discard 返回 busy。Agent、session、功能或 owner 结束撤销相关 locator 与内存 review,迟到的存储结算不能复活它们。仅撤销未保存草稿不追加持久化回执。
90
+
91
+ 全局绑定代码控制台通过 `internal/user-binding-console.js` 按需创建 Node REPL worker,直接运行当前源码草稿并跨命令保留临时变量;普通错误保留此前状态。Host 生成 opaque handle,限制同时存在的环境和单环境并发,沿用执行时间、输出与内存预算;连续 10 分钟未执行、源码替换、离开工作台、停止或 owner 释放时终止环境。旧候选 `run` RPC 保持兼容。两条路径都不连接 Agent kernel 或写入 session journal;源码和 import 仍拥有 DSH 进程的 Node/OS 权限,外部 effect 不能回滚。`replViewEnabled` 与 `bindingAuthorButtonVisible` 仅控制各自 UI 入口,`userBindingsEnabled` 仍拥有整个全局绑定能力。模型没有常驻 binding 管理 API,插件也不从 session binding 反推模块源码:缺少完整源码 provenance 时,session-to-global promotion 无法无损实现。
60
92
 
61
93
  ## Prompt 前缀稳定性
62
94
 
@@ -69,27 +101,37 @@ Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled`
69
101
  | Replacement | 只有 DSH 拥有的 compaction 或其他显式 surface replacement 可以替换已保留历史;PTC Plus 不用 replacement 表达瞬时状态。 |
70
102
  | Independent request | 新的辅助模型调用必须单独说明 route、prefix 和 token 影响,不能用它的缓存表现证明主会话前缀稳定。当前插件不发起辅助模型请求。 |
71
103
 
72
- 一次性或会变化的 session 状态必须作为 `PromptAssembly.contexts` 的命名贡献交给 DSH。DSH 将完整快照记录为带来源的 `user/message` 并追加到历史尾部;值未变时不重复,值变化或全部消失时追加更新或 clearance。完整快照携带当时所有 owner 的命名 context,因此任一贡献变化都会使未变化贡献在新的尾部消息中再次出现;这会增加 append-only history token,但不会改写已有前缀。PTC Plus 不通过绕过 prompt assembly 的私有消息通道规避这项宿主聚合成本。Cordis 恢复 context 使用规范 journal 的历史 transcript 与当前 agent/enable generation 作为事实源,不能从进程内 Plugin registry 直接生成未记录输入。此类状态不得进入 `PromptAssembly.sections`,也不得通过增删 tool、改变 schema 字段或调整 tool order 传递。
104
+ PTC 动态状态通过 DSH 公共 `agent/pre-step` 追加为 `source.plugin: ptc-plus` 的独立 `user/message`。未知完成状态的 rewrite feedback 与 Cordis recovery 构成有界 `snapshot`;全局绑定 API 说明使用独立 `catalog`;恢复 tip 是以 trigger/ordinal 为 identity 的 `notice`。恢复快照只替代 PTC 旧状态,空快照撤销旧状态,不替代绑定目录、authoring task、Skill、tool result 或其他 producer。PTC 更新不重发其他 owner 文本,其他 owner 的 aggregate 更新也不重发未变 PTC 信息。`systemPrompt.context` 中的空 witness 受 `includeRuntimeContext` 与 scoped suppression 管理且不渲染 aggregate 文本;assembly 捕获精确请求事实,pre-step 委托宿主 waterfall 后只对匹配且未取消的 accepted step 提议消息。已提交记录与公共 ordered surface 共同决定去重和状态重申;缺少公共 surface 时不推断可见状态,不投递。raw history 只可用于 tip identity/cooldown,不能让隐藏 binding 变成模型知识。关闭 PTC 后仅保留撤销旧声明所需的被动 presentation cleanup,不安装 runtime 或读取 binding 存储。Cordis 恢复使用规范 journal 与当前 agent/enable generation;绑定目录只描述当前启用配置,不要求激活证明。目录和恢复快照分别去重与撤销,Host aggregate 的撤销不适用于目录;暂停只限制新投递。历史快照中的绑定段由一次恢复快照退役。此类状态不得进入 `PromptAssembly.sections`,也不得通过 tool/schema/order 变化传递。来源、边界与历史兼容由 [ADR 0010](adr/0010-session-log-derived-recovery-tips.md) 拥有。
73
105
 
74
106
  所有模型可见输入都必须能从 session log 重建。静态 system/schema 由 `request/header` 保存,动态 context 由带来源的 `user/message` 保存;进程内临时状态不能直接成为未记录的模型输入。在同一插件版本与配置下,普通执行结果、可编辑目标、诊断和其他插件拥有的运行期变化都不得改变 header;`edit_run_code` 的身份与结果由真实 call/result 表达,不生成 edit feedback context。插件升级、显式配置变化、provider/model route 变化、真实 native capability schema 变化,以及 DSH 拥有的 history replacement 可以使缓存从首个变化 token 起失效。
75
107
 
108
+ 全局绑定条目属于可变的用户计算输入;修改其提示词、接口、源码或启用集合通过上述追加通道同步,不能套用静态插件配置变化的例外来重写 header。配置说明可从已提交 PTC catalog 重建,且不为保留隐藏的 session-local 状态提供额外证明。
109
+
76
110
  缓存稳定性必须有 keyless contract test:跨每个相关生命周期状态序列化并比较完整 system text 与有序 tool schemas,同时断言 edit 不增加 runtime context。真实模型的 `cacheReadTokens` 只作为端到端补充;“不破坏缓存”表示插件保留已有可复用前缀,不承诺 provider 建立、保留或命中缓存。
77
111
 
78
112
  ## 能力表面
79
113
 
80
- cell 直接使用 DSH 为当前 request 提供的 `tools.*`,不按工具名过滤,不翻译已提供的 program-call 参数或 canonical result。只有完全省略参数且 DSH 的 live object schema 验证 `{}` 合法时,worker 才在 encoding 前把 omission 规范为 `{}`;显式 `undefined`、需要输入的 member 与其他 namespace 不变,调用方携带的内部 metadata 不能扩大这项集合。模型 direct-call 边界与这个 data-plane contract 分离:声明的顶层 transport 是 `run_code` 和 `edit_run_code`;可证明的误发 native call 可先规范为 `run_code`,native member 随后在 cell 内 nested dispatch。所有 native member、`capabilities.*`、`repl.state` 和 `code.run` 共享 cell lease;cell 结束后,捕获的函数统一失效。调用时仍由 DSH 检查 scope、policy、取消和 scheduler。
114
+ cell 直接使用 DSH 为当前 request 提供的 `tools.*`,不按工具名过滤,不翻译已提供的 program-call 参数或 canonical result。只有完全省略参数且 DSH 的 live object schema 验证 `{}` 合法时,worker 才在 encoding 前把 omission 规范为 `{}`;显式 `undefined`、需要输入的 member 与其他 namespace 不变,调用方携带的内部 metadata 不能扩大这项集合。模型 direct-call 边界与这个 data-plane contract 分离:声明的顶层 transport 是 `run_code` 和 `edit_run_code`;可证明的误发 native call 可先规范为 `run_code`,native member 随后在 cell 内 nested dispatch。所有 native member、`capabilities.*`、`repl.state` 和 `code.*` 共享 cell lease;cell 结束后,捕获的函数统一失效。调用时仍由 DSH 检查 scope、policy、取消和 scheduler。
81
115
 
82
- `capabilities.tree/find/inspect` 是描述 API,不是反射调用入口。默认 SDK 只展开这个导航器;`repl.state` 与 `code.run` 保持可调用,但其完整契约只在 explorer 中按需返回。explorer 合并 live tool schema 与插件自有 program-binding 描述;可证明的 metadata 包括名称、描述、输入/输出 schema、authority 和 replay。effect 与 result completeness 没有 owner 证据时保持 `unknown`。探索不会授予权限或触发额外模型调用。
116
+ `capabilities.tree/find/inspect` 是描述 API,不是反射调用入口。默认 SDK 展开这个导航器,Global User Bindings 开启时另声明稳定的 `code.submitBindingDraft`;`repl.state` 与 `code.run` 保持可调用,但其完整契约只在 explorer 中按需返回。explorer 合并 live tool schema 与插件自有 program-binding 描述;可证明的 metadata 包括名称、描述、输入/输出 schema、authority 和 replay。effect 与 result completeness 没有 owner 证据时保持 `unknown`。探索不会授予权限或触发额外模型调用。
83
117
 
84
118
  当前公共扩展面没有跨 prompt assembly 与 cell dispatch 的冻结 view token。PTC Plus 使用同一 agent scope 分别读取 prompt 和 runtime view,并让实际 request binding 成为执行事实;能力在两阶段之间变化时,不伪造原子快照保证。
85
119
 
86
- CodeRuntime request 已携带的 owner-provided program namespace 会被原样保留并共享 cell lease,PTC Plus 不翻译其参数或结果。与插件保留的 `capabilities`、`code` 或 `repl` 同名时 request fail-fast,避免主线程与 worker 绑定分叉;普通 cell 局部变量允许自然 shadow 这三个低频 namespace,必要时可通过 `globalThis` 访问。`tools` 仍是保留名称,因为 shadow 它会切断主要能力面。当前公共扩展面没有用于发现额外服务的 program-binding registry;插件不提供名称分发总线或私有 provider registry。若 DSH 以后提供统一 registry,PTC Plus 只消费实际 request 中的 live binding,不复制 authority 或 discovery。
120
+ 执行缝 request 已携带的 owner-provided program namespace 会被原样保留并共享 cell lease,PTC Plus 不翻译其参数或结果。与插件保留的 `capabilities`、`code` 或 `repl` 同名时 request fail-fast,避免主线程与 worker 绑定分叉;普通 cell 局部变量允许自然 shadow 这三个低频 namespace,必要时可通过 `globalThis` 访问。`tools` 仍是保留名称,因为 shadow 它会切断主要能力面。当前公共扩展面没有用于发现额外服务的 program-binding registry;插件不提供名称分发总线或私有 provider registry。若 DSH 以后提供统一 registry,PTC Plus 只消费实际 request 中的 live binding,不复制 authority 或 discovery。
87
121
 
88
122
  ## REPL 生命周期
89
123
 
90
- 每个顶层 `run_code` 是同一 session kernel 的下一格。顶层 binding 跨 cell 保留;默认宽松模式允许一个完整 declarator 全部替换已有变量,严格模式拒绝重声明,关闭 `autoSplitRedeclarations` 时混合新旧名称的解构也在执行前拒绝。cell 始终作为 async function body 求值,支持 block scope、top-level `await` 和普通控制流 return;return lowering 通过顶层 `this` receiver 访问每个 cell 独立生成并在 settlement 清理的 worker-global signal property,因此 lexical `globalThis` 与 `with` object environment 都不能重定向控制流。worker 启动时验证该 receiver 指向 evaluator 的 global context。跨 cell 重声明是 REPL 便利策略,由 `internal/repl-convenience.js` 独立实现;它不是 cell 语言或原生 JavaScript 语义的组成部分。
124
+ 每个顶层 `run_code` 在独立 native frame 中执行,session root 的逻辑身份跨 cell 保留。`repl-scope-normalizer.js` 用正式作用域归属归一化局部 activation;`stateful-root-compiler.js` 生成 root 读写、候选提交、模块依赖和源码映射,`stateful-root-runtime.js` 持有实际来源和值。`stateful` 下同 scope 的声明与赋值更新同一身份,包括 const、function、class 和 import;不同 block、function 与 iteration 保留各自身份。完整 declarator 成功后才发布,普通赋值保留真实部分写入。旧闭包观察同一逻辑身份的新值,保存的函数值保持自身身份。`protected` 保留原生局部限制并检测跨 cell 覆盖。详细语言与代际契约由 [ADR 0025](adr/0025-use-versioned-logical-binding-identities.md) 拥有。
125
+
126
+ `compiler-service.js` 是 cell、模块、动态源码、工作台 completion 与 binding-source metadata 的同步准备入口。编译器及其维护中的 JavaScript 依赖在私有 VM realm 中运行;`compiler-data.js` 复制数据集合与紧凑 source map,服务重建调用方诊断,`compiler-platform.js` 和 realm 内的 factory 只提供编译所需的捕获操作。平台原语在用户执行前固定,路径和压缩由编译器私有依赖处理,转换不发现外部配置。该 realm 不持有用户程序值、模块图、journal 或权限,源码反射查询只允许同步的字符串回调;编译仍受既有 worker 预算和生命周期约束。
91
127
 
92
- 包裹前的 AST 重写适配模块语法(cell 是 async function body,模块声明在函数体内非法):全部默认开启且可用 config 关闭(`autoRewriteImports` / `autoStripExports` / `autoSplitRedeclarations`)。原始 Babel Program 在任何 lowering 或 preload 前完成 scope validation;同一 cell 的 value import 与 lexical、hoisted `var`、function 或其他 import 重名会按模块 binding 规则拒绝,block shadow 和 type-only 名称仍合法。静态 `import` 声明产生有序 module preload record;worker 通过短生命周期 ESM adapter 让 Node 从 session cwd 完成解析和 export linkage,再在 cell 编译前通过生成的临时 global identifier 把 namespace 交给另一个持久 lexical identifier。两者都避开当前源码、持久 binding、program binding 与活跃 private namespace,生成的 prologue 直接引用临时 identifier,因此 lexical `globalThis` shadowing 不参与 host capture;临时 global 在 cell settlement 清理。session 未记录 cwd 时,解析基准回退到 worker process cwd;目标模块的依赖保持各自的自然 parent。scope-aware alias lowering 保留 named/default binding 的 live read、只读、shadowing 和跨 cell 重放语义。value import alias 活跃时,原始 Program 上的 direct `eval` 与 `WithStatement` 在任何 preload record 生效前拒绝,因为生成的 namespace property read 无法复现它们的动态 lexical resolution;没有 value alias 的非严格 `with`、type-only import 和局部 shadow 的 `eval` 仍使用普通 JavaScript 语义。顶层 `export` 修饰符剥离(声明保留、`export default` 转为 `__default` 绑定、re-export 转为预加载的副作用模块、type-only export 擦除);REPL 便利层在 `autoSplitRedeclarations` 开启时按插件定义的兼容规则 lowering 混合新旧名称的顶层解构,关闭时保持原文走既有解析失败路径。无法精确适配的形态在执行前拒绝。改写来源以 `meta.dshPtcPlusRewrites` 平行记录(journal schema 封闭)。成功改写不改变下一步决策,因此不产生 runtime context;改写后失败或缺少有效 journal 时,`tools:ptc-plus-rewrite-info` 才在下一轮说明恢复边界。
128
+ 纯模块转换由 `module-compilation.js` 返回代码、映射、导出描述和静态 link/source-registration 事实;`stateful-module-compiler.js` 在编译器外消费这些事实,负责 Node 公共 hooks、文件格式判定和运行时注册。`compiler-operations.js` 每次编译建立一次绑定索引,各 AST 分区独立记录新增名称、私有传输操作、生成调用外壳与用户值边界。静态和动态调用适配消费同一 planner;helper 参数中的用户表达式仍按源码处理,生成操作不会被重复当作用户调用适配。运行时的模块图、逻辑 root 和 legacy adapter 通过 `compiler-intrinsics.js` 消费内部事实,不重新进入用户的集合协议。可选 TypeScript 编译依赖由私有平台按需加载到同一 realm;平台成员、产物一致性和原预算下的实际执行分别由构建契约与行为测试核验,详细生命周期见 ADR 0025。
129
+
130
+ cell 的静态 import 与 remote re-export 产生有序 preload,Node 继续拥有解析、链接、attributes、缓存和模块效果。import 为逻辑身份提供 live 来源;成功写入形成局部覆盖,重新导入恢复模块来源。模块、绑定候选、正式激活、工作台和隔离 `code.run` 接入共同编译契约,各自保留独立状态与生命周期。模块公开接口选择 PTC 管理的稳定 namespace:读取沿正式来源关系取得逻辑值,原生导出仅承担所需传输,不把 provider 的动态来源复制成值镜像。传给外部函数的 namespace 同样保持实时读取;外部代码独立原生导入 compiled URL 的传输接口不承诺同一可写语义。实例化、TDZ、反射、全部入口与原生获取边界由 [ADR 0025](adr/0025-use-versioned-logical-binding-identities.md#managed-module-interface) 拥有。
131
+
132
+ 用户主动动态编译保留原生语法目标与结果;调用环境仍须与源码一致。直接 eval 解析到调用处逻辑作用域,间接 eval 和 Function 家族使用各自 realm 的 root。`dynamic-scope-analysis.js` 为归一化和编译提供共同的声明归属、初始化查找与 eval activation 路径事实;`dynamic-environment-compiler.js`、`dynamic-environment-runtime.js` 与入口适配负责实际环境,`dynamic-binding-evidence.js` 核验动态声明的来源。这些职责不能用“外部编译”豁免,尚未通过的动态反例仍是实现缺陷。
133
+
134
+ `dynamic-native-runtime.js` 为 eval、四种 Function 构造器和 Function.prototype.toString 持有每个 owned realm 与语言代际的稳定接口。`dynamic-native-calls.js` 在源码的取值与调用边界选取接口,包括属性读取、原生调用结果和异步恢复结果;源码传出的接口在原生或不透明回调中继续使用所属环境和源码事实。`native-root-dynamic.js` 在冻结的 legacy 声明转换之后准备动态入口,通过既有 import namespace owner 保留 helper ancestry:legacy 动态源码使用原生 REPL 环境,并为缺少已初始化原生 lexical 存储的名称桥接目录证明的逻辑绑定;新代使用逻辑 root,两者均消费 managed module namespace。legacy 的实际初始化与结算同样回传逻辑来源和写入证据。全部全局与原型属性保持原生,kernel ambient getter 也只返回原值并记录 volatile 读取,因此旧闭包跨模式、异步与模块继续执行时仍保留原来的环境和已保存的接口身份。direct eval 以原始 intrinsic 执行显式调用环境;普通用户函数保持 canonical identity,用户改写不被接口准备覆盖。`callable-source-facts.js` 的编译器源码事实和已拥有 realm 的 intrinsic 关联跨 realm 共享;另外创建的用户 realm 保持独立。未编译 VM 源码自行读取原生 toString 时仍由原生引擎返回源码;要由不透明 observer 观察原始源码,可以向其传入所属代际的源码观察接口。
93
135
 
94
136
  可确定的计算和 recorded-value capability call 可以推进 durable head。未进入 journal 的 Node/OS 能力、环境输入、时钟、随机数和 timer 进入 sticky `volatile`;live worker 继续可用,cold replay 回到最后 durable frontier。`require(...)` 与动态导入按同一白名单分类:白名单内置模块(assert/buffer/querystring/string_decoder/stream/util/url/zlib)保持 durable,其余 volatile,`worker_threads`/`cluster` 等内核控制模块在执行前以 PTC-C002 拒绝。durable 只描述冷恢复对已结算历史的重放能力;失败 cell 仍可能已经修改 binding 或产生外部 effect,不能由 durability 推导出安全重试。worker thread 是生命周期隔离,不是安全沙箱。
95
137
 
@@ -101,9 +143,21 @@ CodeRuntime request 已携带的 owner-provided program namespace 会被原样
101
143
 
102
144
  ```ts
103
145
  {
104
- version: 3,
105
- bindingMode: "loose" | "strict",
146
+ version: 9,
147
+ languageSemantics: "legacy-v1" | "stateful-v1" | "protected-v1",
148
+ bindingPolicy: {
149
+ variableRedeclarations: boolean,
150
+ functionClassRedeclarations: boolean
151
+ },
106
152
  rewritePolicy: { autoRewriteImports, autoStripExports, autoSplitRedeclarations },
153
+ moduleSemantics: {
154
+ defaultExportBinding: "legacy-variable" | "live-readonly",
155
+ importExpressionBoundary: "legacy" | "statement-safe"
156
+ },
157
+ userBindingsFingerprint: string | null,
158
+ userBindingsReusePolicy: "fingerprint-v1" | "implementation-v1",
159
+ userBindingsShadowPolicy: "whole-entry" | "per-name",
160
+ userBindingNames: { name, state, entryId? }[] | null,
107
161
  status: "durable" | "volatile" | "discarded" | "noop",
108
162
  calls: CallTranscript[],
109
163
  operations: StateOperation[],
@@ -114,11 +168,15 @@ CodeRuntime request 已携带的 owner-provided program namespace 会被原样
114
168
  }
115
169
  ```
116
170
 
117
- `calls` 只保存 global、member、PTC Value Graph 编码的 args/result 或 error,以及 settlement 序号。cold replay 校验调用名称、参数、数量和提交顺序,并按 recorded settlement order 释放 recorded result;不会重新 dispatch program binding 或重做外部 effect。该规则同样适用于 native tools、owner-provided namespace 和 `code.run`,不按名称分支。Cordis 的进程内对象不会因此被宣称已恢复;presentation 可以从已验证 transcript 派生重新检查要求,但不能改变 replay。若基础设施终止时仍有未结算 binding,heap 回滚到 durable frontier,discarded journal 以最先观察到的 `global.member` 保留 possible-effect boundary。effect、completeness 和 source metadata 属于 capability explorer,不伪装成 journal 字段。
171
+ `languageSemantics` 选择完整编译代际;v1–v8 固定迁移为 `legacy-v1`,不能按当前新语言重新解释。`bindingPolicy`、`rewritePolicy` 与 `moduleSemantics` 保留历史细分契约;新代际从统一策略派生它们,旧代际由冻结编译器消费。`userBindingsFingerprint` 以 `null` 证明不存在 Global User Binding 输入,或以 SHA-256 绑定并行私有快照;声明存在但快照缺失或不一致时,node 形成 unknown boundary。`userBindingsShadowPolicy` 固化该 cell 的 session-local 覆盖粒度,`userBindingNames` 以按名称排序的 `{name, state}` 记录结算时的 provider/local/absent/unknown 事实,因此 cold replay 按历史粒度重放覆盖关系,不从当前实现推断 v1-v7 cell 的整条 entry 语义;provider 事实必须对应并行快照的条目 ID,并覆盖该快照公开的每个名称,否则 node 形成 unknown boundary。`calls` 只保存 global、member、PTC Value Graph 编码的 args/result 或 error,以及 settlement 序号。cold replay 校验调用名称、参数、数量和提交顺序,并按 recorded settlement order 释放 recorded result;不会重新 dispatch program binding 或重做外部 effect。该规则同样适用于 native tools、owner-provided namespace 和 `code.run`,不按名称分支。Cordis 的进程内对象不会因此被宣称已恢复;presentation 可以从已验证 transcript 派生重新检查要求,但不能改变 replay。若基础设施终止时仍有未结算 binding,heap 回滚到 durable frontier,discarded journal 以最先观察到的 `global.member` 保留 possible-effect boundary。effect、completeness 和 source metadata 属于 capability explorer,不伪装成 journal 字段。
118
172
 
119
- journal 通过 `run_code.output.presentationMeta` 附着到最终 result,再由 `tools/result` 做两阶段确认。缺失、损坏或被替换的 journal 形成 unknown/volatile 边界;未进入 runtime 的 call 由后续 `confirms` 以对应 `tool/call.seq` 证明为 no-op。volatile 源码保留在原 session log,但不参与 cold replay。
173
+ journal 通过 `run_code.output.presentationMeta` 附着到最终 result,再由 `tools/result` 做两阶段确认。live binding catalog 在 worker 结算时随实际状态更新,下一条排队 cell 使用这份目录准备;journal 确认只推进持久历史,迟到或逆序的确认不能覆盖较新的 live 目录,也不能恢复已重置的状态。缺失、损坏或被替换的 journal 形成 unknown/volatile 边界;未进入 runtime 的 call 由后续 `confirms` 以对应 `tool/call.seq` 证明为 no-op。volatile 源码保留在原 session log,但不参与 cold replay。
120
174
 
121
- durable replay 无法验证某个 node 时,当前已结算 `tool/result` 的私有 `meta.dshPtcPlusRecoveryBoundaries` 记录失败 call 与其 parent frontier,不修改冻结的历史 result。boundary 在进入排序前必须完成 schema 与 event-sequence validation;损坏 metadata 使恢复 fail closed,不能从 fold 中静默消失。折叠器剪除该 node 及依赖后代、重算可重建 checkpoints;kernel 重置 worker 并逐级验证更早 frontier,随后执行当前 live cell。新 durable node 的 parent 始终是 worker 实际拥有的 frontier。自定义 boundary event 不由运行时生成,迁移工具必须在 DSH restore 前显式转换它。
175
+ 恢复从当前请求对应的 head 沿 parent 关系向更早状态收缩,而不是越过损坏记录继续假定原 heap 存在。候选 frontier 同时受两种证据约束:append-only session log 中的 journal 证明状态可重建;生成当前调用的 DSH ordered surface/derived request history 证明模型可以知道其精确 binding provenance。raw event 未删除不等于模型仍可见,Client 的 `dshPtcPlusBindings` inventory 也只是 UI presentation。只裁剪 tool result、仍把携带源码的 assistant call 留在模型 surface 中时不必删除对应状态;assistant provenance 被遮蔽且没有显式、有界、模型可见的结构化状态投影时,live worker 与 cold replay 都必须收缩。插件不从自然语言 compaction summary 推断 binding。
176
+
177
+ durable replay 无法验证某个 node 时,当前已结算 `tool/result` 的私有 `meta.dshPtcPlusRecoveryBoundaries` 记录失败 call 与其 parent frontier,不修改冻结的历史 result。boundary 在进入排序前必须完成 schema 与 event-sequence validation;损坏 metadata 不能从 fold 中静默消失,也不能证明任何旧状态。折叠器剪除结构不可证明或模型不可知的 node、binding 与依赖后代,重算可重建 checkpoints;只有 ancestry、独立性和模型可知 provenance 都有 owner evidence 的部分可以保留。现有 journal 没有完整的逐 binding 依赖图,因此不能证明细粒度剪枝时按 cell 与受影响后缀收缩。已经存在的 live kernel 在执行下一 cell 前检查 surface replacement generation;若模型可见历史已经收缩,先重置并物化对齐后的 frontier。
178
+
179
+ kernel 重置 worker 并逐级验证更早 frontier;最大非空 frontier 仍不可证明时使用空 REPL。这个最差情况会丢失全部旧 binding,但历史 PTC Plus metadata 损坏本身不得阻止当前合法 cell 执行。当前 cell 在实际重建的 frontier 上建立新分支,并通过一次 `PTC-R002` 说明哪些历史状态未恢复;收缩不重新派发或撤销历史 program binding effect,也不把未知状态说成已恢复。DSH 对当前请求的 validation、policy、authority、approval 与 cancellation 错误不属于该可用性降级。新 durable node 的 parent 始终是 worker 实际拥有的 frontier。自定义 boundary event 不由运行时生成,迁移工具必须在 DSH restore 前显式转换它。
122
180
 
123
181
  诊断由封闭结构确定性渲染,包括语法、preflight、绑定冲突、输出、运行异常和恢复边界。普通成功与首次进入 volatile 不投影 warning/note;恢复分类保留在 journal 和 `repl.state(list)` 中。
124
182
 
@@ -135,3 +193,5 @@ durable replay 无法验证某个 node 时,当前已结算 `tool/result` 的
135
193
  - [Plugin Settings UI and Enabled Kill Switch](adr/0019-plugin-settings-and-kill-switch.md)
136
194
  - [Optional Cordis Tools in PTC Mode](adr/0020-optional-cordis-tools-in-ptc-mode.md)
137
195
  - [Separate Host Bootstrap From Session Runtime](adr/0021-separate-host-bootstrap-from-session-runtime.md)
196
+
197
+ - [Use Versioned Logical Binding Identities](adr/0025-use-versioned-logical-binding-identities.md)