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
@@ -2,32 +2,40 @@
2
2
 
3
3
  ## Cell Semantics
4
4
 
5
- Each top-level `run_code` input is parsed as an async function body using modern JavaScript and erasable TypeScript syntax. Top-level bindings survive across cells, block scope and top-level `await` work normally, and only explicit `return` or printed values appear in the result. Static `import` and `export` declarations are adapted when their corresponding `auto*` options are enabled; named and default imports remain live, read-only bindings across later cells. Otherwise use dynamic `import()` or `require()` and omit module declarations.
5
+ Each top-level `run_code` executes a JavaScript/TypeScript cell with top-level `await`, ordinary control flow and a session root that continues across cells. Static import/export syntax is part of the language. TypeScript runtime forms such as enums and namespaces are transformed before execution. Returned and printed values form the result; binding state is independent of its presentation.
6
6
 
7
- The default loose binding mode lets a complete top-level `const` or `let` declarator replace existing names. New declarators create bindings. With `autoSplitRedeclarations` enabled, mixed new/existing destructuring is split while preserving its assignment semantics; disabling that option, function/class redeclaration, or any redeclaration in strict binding mode is rejected before execution.
7
+ With the default `bindingUpdates: 'stateful'`, declarations and assignments update one logical identity in the same scope and activation, including local const bindings. An existing closure observes later updates, while a saved function value keeps its own identity. Separate block, function and iteration scopes stay separate. A bare `let x`, `var x` or `const x` preserves an existing value and creates undefined when new. Each declarator publishes its complete candidate only after its initializer and binding pattern succeed. Earlier declarators and ordinary assignment effects remain real if a later operation fails. Unreached lexical declarations do not reserve a name for future cells.
8
8
 
9
- Every capability namespace is leased to one cell. Captured `tools`, `capabilities`, `repl`, `code`, or member functions expire when that cell ends.
9
+ An import alias follows its module until an actual successful assignment or declaration supplies a local value. Re-importing switches that same identity back to the module, so existing new-generation closures observe the change. Named default declarations associate `__default` with the local identity until independently overwritten. Static dependencies link before the body and keep their effects even if a later dependency fails.
10
10
 
11
- For PTC mode requests using the code-only direct-tool projection, PTC Plus presents two tools in stable order: `run_code` and `edit_run_code`. It recognizes the current Host's `tools:ptc-only` owner section and the preceding Host's `tools:code-only` alias, with the current section authoritative if both appear. The latter tool edits the most recent eligible cell visible when the edit call is dispatched, including a cell that completed successfully but needs a small adjustment. That target remains fixed while the call is in flight; later tool settlements cannot retarget it. The edit accepts exactly one atomic `edits` or `regex_edits` array and an optional `expected_target_call_seq` precondition. Invalid, unavailable, or mismatched-target edits return `{ edited: false, reason }` without executing source or consuming the target.
11
+ `bindingUpdates: 'protected'` retains native local restrictions, rejects cross-cell redeclarations, and protects newly created const/import bindings. Switching policy neither resets the worker nor freezes earlier writable identities. Historical cells replay under their recorded language generation; earlier closures retain that generation's behavior. See [ADR 0025](adr/0025-use-versioned-logical-binding-identities.md) for the ownership and compatibility contract.
12
+
13
+ New protected modules retain these native protections and the current callable source reflection contract. Their compilation generation is distinct from historical modules, including when a policy change imports the same source again.
14
+
15
+ Every program namespace is leased to one cell. Namespace objects or member functions captured in the REPL expire when that cell ends. A reusable REPL helper must look up the current namespace inside its body. Global User Binding modules use a dynamic bridge: even a module-captured member resolves the current implementation when invoked by a new cell; a removed member fails until it is available again. Continuations retain their originating cell identity and cannot borrow a later cell's lease.
16
+
17
+ For PTC mode requests using the code-only direct-tool projection, PTC Plus presents two tools in stable order: `run_code` and `edit_run_code`. It recognizes the current Host's `tools:ptc-only` owner section and the preceding Host's `tools:code-only` alias, with the current section authoritative if both appear. The latter tool edits the most recent eligible cell visible when the edit call is dispatched, including a cell that completed successfully but needs a small adjustment. That target remains fixed while the call is in flight; later tool settlements cannot retarget it. The edit accepts exactly one atomic `edits` or `regex_edits` array and an optional `expected_target_call_seq` precondition. Invalid, unavailable, or mismatched-target edits return `{ edited: false, reason }` without executing source or consuming the target. `regex_edits[].replacement` is a JavaScript replacement template: captures are referenced as `$1`, `$2`, ... or `$<name>`, a literal dollar sign is written `$$`, and backslash forms such as `\1` are emitted literally rather than resolved as captures. `pattern` values are JavaScript regular-expression sources without delimiters.
12
18
 
13
19
  `edit_run_code` is a real registered tool call. Session history and later model requests retain its original name and delta arguments. The plugin executes the materialized source as a host-derived `run_code`, returns only the edit status, value, and logs to the model, and keeps the complete source plus journal in private replay metadata. It does not rewrite assistant history or emit edit-specific runtime context.
14
20
 
15
21
  When a live parse error occurs exactly at the cell EOF, PTC Plus checks the three single-closing-token corrections `}`, `)`, and `]` with the same preparation context. If exactly one correction succeeds, the existing editor can express it from a bounded unique suffix, and the rejected call has a persistent event identity, `PTC-C001` includes a directly callable `edit_run_code({ edits: [...], expected_target_call_seq })` invocation. The guard must match the target captured when the edit call is persisted, so a later editable cell suppresses execution even when the same literal replacement would match it. The plugin does not apply or execute the correction automatically; ambiguous, broader, and unbound repairs keep the ordinary length-adaptive guidance.
16
22
 
17
- Only values explicitly returned or printed appear in a cell result:
23
+ Use `return` for an explicit result and `console` for logs. Native expression completion can also supply a result value:
18
24
 
19
25
  ```ts
20
26
  const records = [{ id: 1 }, { id: 2 }]
21
27
  return records.map(record => record.id)
22
28
  ```
23
29
 
24
- Parse, filter, and aggregate large results in the cell before returning them; oversized output is truncated by the host, so expose only what the next step needs.
30
+ Parse, filter, and aggregate large results before returning them. Aggregate log/return overflow terminates the worker and discards the cell; its bindings are not available for a later reduction. An encoding-budget rejection (`PTC-O001`, `invalid-output`) instead keeps the worker live, so an already initialized binding can be reduced in a later cell. Neither path undoes external effects.
25
31
 
26
32
  Prefer executing work directly in the current cell; reserve `code.run` for source already held as data. When nested source must be written inline, escape quotes, backslashes, and newlines exactly — its parse errors point only at the generated text, not at a position in the outer cell.
27
33
 
28
34
  ## Capability Discovery
29
35
 
30
- The default SDK keeps navigation compact. Use `capabilities.tree/find/inspect` when the required member is not already visible:
36
+ The default SDK declares `capabilities.tree/find/inspect` directly, so exploring the explorer's own contract is unnecessary. `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. Prefer a short query such as `read` over a prose request such as `read file content`. A miss means no lexical match, not proof that a capability is unavailable.
37
+
38
+ `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:
31
39
 
32
40
  ```ts
33
41
  const matches = await capabilities.find('session')
@@ -57,13 +65,11 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
57
65
  enhancedToolView: true
58
66
  canonicalizeToolCalls: true
59
67
  autoDescribeRunCode: true
60
- looseTopLevelRedeclarations: true
61
- autoRewriteImports: true
62
- autoStripExports: true
63
- autoSplitRedeclarations: true
68
+ bindingUpdates: 'stateful'
64
69
  durableReplay: true
65
70
  tipsEnabled: true
66
71
  cordisToolsEnabled: false
72
+ userBindingsEnabled: false
67
73
  computeMs: 60000
68
74
  maxWallMs: 600000
69
75
  maxOldGenerationSizeMb: 512
@@ -77,25 +83,63 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
77
83
  tipEscalationFailures: 2
78
84
  ```
79
85
 
80
- `enabled: false` is the settings-based kill switch: the Host keeps only the settings namespace and client card, and removes every runtime hook, prompt section, and tool surface. Every setting is applied live when its owner can reconcile it. A submitted cell retains one configuration snapshot through preflight, execution, binding calls, completion validation, and diagnostics; an update accepted while it runs applies to cells submitted afterward. Node fixes a worker's V8 old-generation limit at creation, so changing `maxOldGenerationSizeMb` while a session worker is active is rejected and rolled back rather than reported as applied. Other non-`enabled` updates reconfigure existing owners without replacing session-bound bindings. The settings card is available under Settings → Plugin configuration, shows “已启用” or “已停用”, and disables every control except `enabled` while the plugin is off. If a live enable or reconfiguration cannot install a complete runtime, PTC Plus unwinds or restores every owner and persists the last applied settings; a failed compensating settings write is surfaced as an activation diagnostic. The TypeScript language check is deferred until runtime activation, so a non-TypeScript host can load the plugin in disabled mode but cannot enable it.
86
+ The five legacy binding and module switches remain migration inputs. Complete enabled/protected choices map to the corresponding new policy; mixed old choices retain their effective controls until the user explicitly selects the new switch. The Host schema preserves an omitted `bindingUpdates` value until migration, and the settings card resolves the same policy. Selecting the switch also clears the compatibility marker.
81
87
 
82
- `cordisToolsEnabled: true` atomically mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and exactly the shipped `cordis-plugin-development` companion Skill in PTC agent scopes. PTC Plus resolves the official `cordis` preset through DSH's public preset service, lets the maintained filesystem provider own its Skill root, 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. A missing or broken preset, Skill/tool service, declared Cordis service, inconsistent inspect manifest, or rejected activation fails the setting change and removes every provisional contribution. 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, edit_run_code]`. Agents whose `run_code` surface appears after creation are retried on the DSH tools-change signal. Cordis can evaluate model-written plugins against the live runtime, so enabling it grants shell-equivalent trust.
88
+ `enabled: false` is the settings-based kill switch: the Host keeps the settings namespace, client card, and passive cleanup that can withdraw old PTC declarations at the next permitted accepted step. It removes execution hooks, prompt sections, and tool surfaces without reading binding storage. Every setting is applied live when its owner can reconcile it. A submitted cell retains one configuration snapshot through preflight, execution, binding calls, completion validation, and diagnostics; an update accepted while it runs applies to cells submitted afterward. Node fixes a worker's V8 old-generation limit at creation, so changing `maxOldGenerationSizeMb` while a session worker is active is rejected and rolled back rather than reported as applied. Other non-`enabled` updates reconfigure existing owners without replacing session-bound bindings. The settings card renders in whichever settings seat the installed DSH declares — the side-bar **Plugins** page's per-bundle configuration on the current release, Settings → Plugin configuration on the preceding one ([ADR 0019](adr/0019-plugin-settings-and-kill-switch.md)) — shows “已启用” or “已停用”, and disables every control except `enabled` while the plugin is off. If a live enable or reconfiguration cannot install a complete runtime, PTC Plus unwinds or restores every owner and persists the last applied settings; a failed compensating settings write is surfaced as an activation diagnostic. The TypeScript language check is deferred until runtime activation, so a non-TypeScript host can load the plugin in disabled mode but cannot enable it.
83
89
 
84
- Keep large Cordis host or client source in a top-level binding before calling a Cordis tool. A parse or validation rejection is a runtime failure, so bindings assigned before it remain live for a short retry cell; reuse the binding instead of resending the source.
90
+ `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 own its Skill root, 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, edit_run_code]`. Cordis can evaluate model-written plugins against the live runtime, so enabling it grants shell-equivalent trust.
91
+
92
+ Keep large Cordis host or client source in a top-level binding before calling a Cordis tool. If that call rejects while the worker stays live, an initialized binding can be reused in a short continuation. Decide whether to retry from the operation owner's retry/idempotence contract and available execution facts; a thrown call or a smaller cell does not prove that no effect occurred.
85
93
 
86
94
  Cordis Plugins and Runs are process-local. On a resumed agent or after Cordis is re-enabled, normalized journal calls can restore their recorded return values but do not prove that prior IDs, approvals, Runs, or Inspect observations are live. The fixed `tools:ptc-plus-cordis-recovery` context remains present until a new successful `cordis_inspect*` call is settled. Use that live read-only inspection before a stateful Cordis decision; do not rerun a recorded mutating call merely to rebuild state.
87
95
 
96
+ ## Global User Bindings
97
+
98
+ `userBindingsEnabled` defaults to `false`. While disabled, PTC Plus does not read binding storage, add new declarations, inject values, register the management RPC or `/binding` command, or render Global User Binding controls. The next permitted accepted step can withdraw previously delivered declarations. Enabling it loads the single document at `$DSH_HOME/ptc-plus/bindings.json`; writes use a file lock, atomic replacement, owner-only file permissions, and an expected process-local revision. A stale revision or an external file change rejects the mutation and requires reload rather than overwriting newer content. A damaged document produces an explicit catalog error and remains unwritable until the user repairs it and reloads.
99
+
100
+ Each entry has a stable `id`, display `name`, `namespace` or `top-level` scope, selected symbols, one-line purpose, enabled state, and TypeScript source. A namespace name must be a JavaScript identifier; a top-level entry uses its exported symbols as call identifiers and keeps `name` for display. Source must provide named value exports; default exports and re-exports are rejected. An omitted symbol list derives every named value export, while an explicit list limits the exposed API; clearing the Settings symbol field requests fresh derivation from the edited source. The parser derives declaration kinds, parameter and return annotations, bounded inferred types, and model-visible declarations from the selected source exports. A `namespace` entry contributes one object binding under its name, while a `top-level` entry contributes each selected export. Reserved REPL names and duplicate active identifiers fail validation. The document accepts at most 64 entries, 65,536 UTF-16 code units per source, 262,144 across all sources, and 16,384 across active declarations. Mutations validate the complete enabled declaration set before atomic replacement; an externally written document that exceeds the limit is reported as damaged storage and contributes no active entries.
101
+
102
+ The current enabled snapshot is the desired input for the next cell. The independent binding API catalog represents `tools:ptc-plus-user-binding-defaults` and supplies configured prompts and selected source-derived interfaces from the first permitted request. One header identifies use inside `run_code` and replacement of the previous global binding API reference, followed only by entry labels and selected documentation. It does not prove successful activation. Successful activation, session-local shadowing and worker reconstruction do not themselves append a new binding context. Unchanged documentation is deduplicated against retained model-visible history; changed or compacted-away configuration is supplied again. Historical configuration and `tools:ptc-plus-user-bindings` snapshot sections remain readable and are withdrawn once by the next permitted accepted recovery snapshot; the current API catalog is delivered separately. The stable `tools:sdk` prefix and direct-tool schemas do not change with binding contents. A request-owned program namespace or error class takes precedence over a same-name user entry; that entry produces an activation diagnostic and is omitted from the settled snapshot without blocking independent code. If a failed initializer issued a program call, the cell becomes volatile because the successful-entry snapshot is not complete replay source for that call transcript.
103
+
104
+ Namespace members and top-level exports preserve ECMAScript module live reads until assignment or a permitted redeclaration creates a session-local shadow. A shadow is not written to the global document and suppresses only the shadowed name from subsequent global activation while the entry's other names keep activating; journals migrated from versions 1 through 7 keep their recorded whole-entry suppression. Updates, disablement, and removal affect unshadowed names from the next submitted cell. Entry imports resolve from the directory containing `bindings.json` during both candidate evaluation and session activation, independently of the session cwd. Binding modules can use program namespaces present in the current request through a stable bridge; new namespace names are installed as they appear, absent namespaces expose no members, and each invocation uses its originating cell lease. A namespace that has the same name as any existing worker global fails explicitly before bridge installation, so Node intrinsics cannot be replaced and a failed installation cannot leave a partial bridge. Once an exported value may have escaped into an ordinary session binding, its synthetic-module resolution base and installed bridges remain available for that worker lifetime. When no user binding has ever been exposed, disabled or empty activation installs no bridge; if every attempted activation fails, any bridge installed for that attempt is removed before the cell executes.
105
+
106
+ The REPL tab and Settings management modal share a workbench for source editing, validation, declaration preview, revision-checked persistence, import of a local `.ts` file as a disabled entry, enablement, removal, and code execution. The code console evaluates the current unsaved source draft in a separate bounded Node REPL worker hosted by the plugin-owned helper process. Named exports can be called directly from TypeScript commands, with top-level `await` and variables retained across executions. It does not access the Agent session kernel or enter its journal. The compatible `run` RPC separately provides single-use candidate evaluation with an optional exported-function name and JSON argument array. Both execution paths retain DSH process authority: source and imports may create irreversible Node/OS effects before cancellation, timeout, or failure. Console controls and resource limits are documented in [Client UI](client-ui.md#全局用户-binding-工作台).
107
+
108
+ Session value previews use a separate bounded observation path. A visible inventory without observations can request one read from the existing settled worker through the authenticated Host observation Remote. The read shares the cell queue, verifies the catalog and surface generation, and waits at most 250 ms without creating a worker, replaying history or changing the journal. Only worker-confirmed observation may defer the next cell's execution budgets; sending a request does not exempt background blocking from timeout recovery. The Client discards responses for replaced inventories, including an assignment that changes a binding's recorded source without changing its name. Committed ordinary values in the logical root can be read directly without invoking source getters. The legacy native-storage path additionally requires an actual evaluator probe for await lexical previews; function-local await does not suppress outer declarations. Primitive values and five fixed array slots can be displayed without getters, Proxy traps or complete object enumeration. Arrays always retain an incomplete marker because additional own properties remain uninspected. BigInt conversion is capped at 128 digits. Missing observations remain distinct from unreadable values; details belong to [ADR 0024](adr/0024-repl-console-observation.md).
109
+
110
+ In an enabled PTC session, `/binding new <requirement>` and `/binding edit <id> <requirement>` append a complete authoring task and start a normal Agent turn. The task gives the current `requestId`, field definitions, namespace invocation examples, and the dependency resolution base. Edit also supplies the exact stored entry and preserves its stable ID. The Agent submits from `run_code` through `code.submitBindingDraft({requestId, entry})`, a stable internal SDK member under enabled configuration. The first valid submission becomes one memory draft with `enabled: false`. The Host checks the captured request, exact Agent, generation, and cell lease; duplicate, concurrent, late, or mismatched submissions fail. Acceptance, turn stop, error, cancellation, replacement, disablement, or disposal revokes eligibility. Starting and ending a request does not register or unregister tools or Skills. The Agent cannot persist or execute candidates through this API.
111
+
112
+ The authoring task permits small REPL cells with in-memory fixtures and assertions, including temporary variables and correction from observed results. It starts with a focused core check, uses `node:assert/strict`, and summarizes bulk checks with totals and representative failures. Tests must not modify external files or services. File and network integrations use in-memory substitutes to verify forwarding and error propagation; unknown-effect imports and initializers are reviewed without execution. Before submission, the final source, exports, types and usage prompt are checked against the requirement and tested behavior. The answer distinguishes helper failures from test-harness failures and reports untested integration. A usage prompt can be empty when the source-derived interface suffices. These instructions neither enforce an effect sandbox nor expose management RPC to the model. See the [user guide](user-bindings.en.md) for the interactive workflow.
113
+
114
+ The accepted outer result carries an opaque draft locator plus complete accepted source and command identity in private metadata. Authenticated Client RPC uses that locator, never a payload session ID. Saving atomically claims the draft before revision-checked persistence; concurrent discard reports busy, and new drafts use create-only storage. Save and enable writes `enabled: true` in the same mutation. Catalog conflicts refresh authoritative state and keep still-valid drafts available for manual retry. Saving or discarding consumes writable access, while `draft-review` and the log projection retain the exact historical source. A public, structured action notice references the accepting result and exposes only the completed persistence fact to later model requests; notice failure does not repeat or undo storage. Agent/session disposal, feature disablement, and owner disposal revoke live locators and reviews, while historical source never restores save eligibility. Cold replay returns recorded submission values without another handoff.
115
+
116
+ Stored, enabled, and successfully activated are distinct facts. Global UI state reports the catalog's enabled flag. Configured model documentation and a save notice prove no activation; results and diagnostics report actual execution. `repl.state` is a checkpoint function and its `list` names are checkpoint names, not bindings. Availability questions use execution results or a side-effect-free observation of a known name, without write/delete smoke tests. Effectful verification must be authorized by the task and use exclusively created resources whose ownership is established before cleanup.
117
+
118
+ Every completed cell records the exact source-derived active snapshot in private result metadata, and its normalized journal records the shadow granularity plus the per-name `provider`/`local`/`absent`/`unknown` evidence of that settlement. Cold recovery validates that snapshot again, checks the name evidence against it, and replays the cell with its recorded granularity; it never substitutes the current `bindings.json` or the current per-name rule for a historical cell. Malformed or incomplete binding metadata invalidates the affected historical node and contracts recovery under the ordinary fail-closed frontier rules. An entry that fails activation is excluded from that cell's active snapshot and produces a bounded diagnostic without preventing independent `run_code` work.
119
+
88
120
  `durableReplay: false` starts new kernels without recorded REPL state while preserving live bindings in the current process. It does not delete session logs.
89
121
 
90
- Recovery tips are disabled with `tipsEnabled: false`. When enabled, a bounded runtime context in the reserved `tools:ptc-plus-tip/<trigger>/<ordinal>` name family may appear after a repeated binding failure or a diagnostic that identifies an executable, shell, or path problem in the current execution world. The trigger and per-trigger ordinal are reconstructed from canonical named system-prompt snapshots; repeated aggregate copies and visible wording do not advance the history. A tip is subject to `tipCooldownMessages` and becomes detailed only after `tipEscalationFailures` unresolved matches; it never changes the code-only direct-tool list or schema. `edit_run_code` does not emit a runtime context because its real call and result already carry the relevant fact.
122
+ With `durableReplay: true`, recovery claims only bindings backed by both verified journal ancestry and exact provenance available to the model that produced the current call. DSH's append-only event log proves reconstructability; its ordered model-visible surface proves awareness. Raw events shadowed from that surface and UI-only `dshPtcPlusBindings` metadata do not make a binding model-knowable. A result-only replacement may retain a cell when the assistant call containing its source remains visible. If compaction shadows that call, the live worker and cold recovery contract the affected state unless an explicitly selected bounded structured projection exposes the exact bindings in model context. Such a projection is not injected by default merely to retain hidden state. Natural-language summaries are not parsed as state evidence.
123
+
124
+ An invalid historical association, prune replacement, or recovery boundary is also rejected as evidence for its bindings; it is not by itself a reason to reject every later valid `run_code`. Recovery takes the intersection of the model-knowable and structurally verified frontiers, then discards cells and bindings that may depend on an unknown boundary. Because the current journal does not contain a complete per-binding dependency graph, the conservative unit is an affected cell and suffix unless stronger owned evidence exists. If no non-empty frontier is provable, the worker starts from an empty REPL and executes the current cell as a new root.
125
+
126
+ The first continued cell reports `PTC-R002` once so the model can redeclare any missing bindings, and its result persists the contraction for later cold starts. Recovery never redispatches, reverses, or certifies effects from discarded history. This availability fallback applies only to historical PTC Plus recovery data; validation, policy, authority, approval, cancellation, and other failures of the current request retain their normal DSH behavior.
127
+
128
+ PTC dynamic messages carry `source: { kind: 'plugin', plugin: 'ptc-plus', form: 'snapshot' | 'catalog' | 'notice' }`. Current-state snapshots replace only earlier PTC state, including historical PTC aggregate sections; they do not replace the binding API catalog, tasks, Skill instructions, tool results, or other producers. Only a later binding catalog replaces an earlier catalog, and an explicit empty catalog withdraws its API documentation. Host runtime-context clearance does not invalidate the catalog; suppression gates new delivery. Empty snapshots withdraw earlier state claims. Delivery uses the public accepted pre-step decision and honors `includeRuntimeContext`, scoped suppression, and cancellation through an empty assembly witness. Committed messages and the public ordered surface drive deduplication and reconciliation after compaction or resume. Proposals do not count as delivery; raw history does not establish model knowledge.
129
+
130
+ Recovery tips are disabled with `tipsEnabled: false`. When enabled, a bounded independent PTC notice identified by `tools:ptc-plus-tip/<trigger>/<ordinal>` may appear after a repeated binding failure or a diagnostic that identifies an executable, shell, or path problem in the current execution world. The trigger and per-trigger ordinal are reconstructed from committed PTC notices and canonical historical DSH snapshot sections; each identity counts once across both sources, and visible wording does not advance history. A tip is subject to `tipCooldownMessages` and becomes detailed only after `tipEscalationFailures` unresolved matches; it never changes the code-only direct-tool list or schema. `edit_run_code` does not emit a runtime context because its real call and result already carry the relevant fact.
131
+
132
+ Static and dynamic imports retain Node's resolver from the session cwd, falling back to the worker cwd when none was recorded. Relative files, bare packages, conditional exports, URLs and attributes retain their Node meaning. A missing requested export fails at static linking before that target module evaluates. Namespace property writes still follow the namespace object's own contract; updating an alias does not mutate the namespace. A managed namespace's members are readonly: a member write is refused and leaves the member's live value unchanged, while reflection continues to report that live value. `Reflect.set` returns `false`, and `Object.defineProperty` with a different value throws `TypeError`; a direct assignment from a non-strict cell body is silently discarded, exactly as for any other readonly property.
91
133
 
92
- The three `auto*` toggles control text-level AST rewriting applied before cell wrapping, all on by default:
134
+ Cell export modifiers retain local declarations; remote re-exports retain dependency loading, type-only exports are erased, and default values use the ordinary `__default` identity. Real modules expose their actual export names through a stable PTC-managed namespace. Member reads follow live sources until actual local override and return original function/object values. A namespace retained by an external function continues to provide live reads. Its readonly properties do not make it a native module namespace object. Opaque code independently using native import on the raw compiled URL, or its own native createRequire, can see the transport representation and does not receive PTC's writable-export interface. PTC-provided imports and require, binding modules, workbench commands and `code.run` children share the managed interface with independent state and lifecycle. The complete contract and acceptance conditions belong to [ADR 0025](adr/0025-use-versioned-logical-binding-identities.md#managed-module-interface).
93
135
 
94
- - `autoRewriteImports` adapts static `import` declarations (`import x from 'm'`, named, namespace, side-effect, mixed, and type-only forms) through worker-preloaded module namespaces. Static and dynamic imports use Node's ESM resolver from the session cwd, or the worker process cwd when the session has no recorded cwd; relative project files, bare packages, conditional exports, URL imports, and import attributes retain Node semantics. Modules resolve in source order before the cell body is compiled, and a missing requested export fails during linking before that target module evaluates. Named and default aliases keep live reads and reject assignment; a later explicit loose-mode declaration may replace an alias for future cells while closures compiled earlier retain the import. Unsupported destructuring, loop, and delete writes are rejected before execution. Direct `eval` and `with` are unavailable while value import aliases are active because their dynamic name resolution cannot be preserved by alias rewriting; rejection occurs before module preload. Indirect or locally shadowed eval, `with` without active value aliases, and cells containing only type imports retain ordinary JavaScript behavior.
95
- - `autoStripExports` strips top-level `export` modifiers: declarations survive without the keyword, `export default` becomes a local `__default` binding, re-exports become side-effect imports, and type-only exports are erased. A cell that separately declares the fixed public name `__default` and also has a default export is rejected before execution; a default function or class already named `__default` needs no alias.
96
- - `autoSplitRedeclarations` allows a top-level destructuring declaration to mix existing and new names. The original pattern runs under native JavaScript initialization semantics, then existing names are committed and fresh names remain available to later cells.
136
+ User-selected dynamic compilers retain their native syntax goals and results. Their environments remain part of the source contract: direct eval resolves the caller's logical scope, while indirect eval and Function constructors use their owning realm's root. Environment adaptation must preserve those relationships, ordinary shadowed functions and actual effects. A valid call failing because it sees generated storage names is an implementation defect.
97
137
 
98
- Rewrites are recorded as `meta.dshPtcPlusRewrites` on the tool result (parallel to the journal, a closed schema). Successful rewrites do not emit runtime context. If the rewritten cell fails or has no valid journal, `tools:ptc-plus-rewrite-info` describes the recovery boundary in the next model request.
138
+ This also applies when native or opaque code invokes a supplied eval or Function constructor: after `const value=41`, `["typeof value"].map(eval)` returns `["number"]`. Function.prototype.toString, including a bound callback or module observer, returns compiler-proved original JavaScript source for compiled user functions. Recompilation preserves the source's own dependencies and function values keep their canonical identity. User overrides and separately created realms retain their own behavior.
139
+
140
+ Intrinsic interfaces belong to their source generation. Source selects the owning realm's interface when reading intrinsic values, including property reads and results from native or asynchronous operations. Actual global and prototype properties stay native, including Function.prototype.toString. Legacy root source receives a stable interface after its frozen declaration lowering; its dynamic code uses the native REPL environment, while module results use the same managed namespace interface as other PTC entries. Existing closures retain their saved identities and environments across mode changes and asynchronous or module continuations. Uncompiled VM source continues to observe native engine source when it independently fetches native toString; an opaque observer can receive the PTC source interface as a callable argument.
141
+
142
+ Rewrites are recorded as `meta.dshPtcPlusRewrites` on the tool result (parallel to the journal, a closed schema). Settled rewrites do not emit redundant runtime context: failures already carry execution and retry guidance in their result. Only when the rewritten cell has no valid journal does `tools:ptc-plus-rewrite-info` describe unknown completion in the next model request.
99
143
 
100
144
  `require(...)` is classified exactly like a dynamic import: allowlisted builtins (`node:assert`, `node:buffer`, `node:querystring`, `node:string_decoder`, `node:stream`, `node:util`, `node:url`, `node:zlib`) stay durable, other modules are volatile, `node:worker_threads`/`node:cluster` are rejected before execution (`PTC-C002`), and non-literal arguments are dynamic module resolution.
101
145
 
@@ -108,19 +152,29 @@ Rewrites are recorded as `meta.dshPtcPlusRewrites` on the tool result (parallel
108
152
  | `PTC-N001` | Top-level binding conflict | Not executed; REPL unchanged |
109
153
  | `PTC-O001` | Unsupported or over-budget output | Cell executed; earlier mutations may exist |
110
154
  | `PTC-X001` | Uncaught runtime exception, located at the cell source line | Mutations before the throw may exist |
111
- | `PTC-R002` | Cold recovery skipped volatile, unconfirmed, or replay-abandoned history | Restored the last reconstructable durable frontier |
112
- | `PTC-W001` | The same cell failed 3 consecutive times with an identical binding error | One-shot binding-recovery warning appended to the failing cell's logs; no state change |
113
- | `PTC-W002` | The same cell failed 3 consecutive times with another identical error | One-shot cause-specific warning appended to the failing cell's logs; no state change |
155
+ | `PTC-R002` | Cold recovery discarded volatile, unconfirmed, damaged, or replay-abandoned history | Continued from the greatest verified frontier, possibly an empty REPL; missing bindings may need redeclaration |
156
+ | `PTC-W001` | The same cell failed 3 consecutive times with an identical binding error | Supplemental warning carries the primary failure's conservative state effect, or `unknown` when unavailable |
157
+ | `PTC-W002` | The same cell failed 3 consecutive times with another identical error | Supplemental warning carries the primary failure's conservative state effect, or `unknown` when unavailable |
158
+
159
+ Durability describes recovery after a kernel restart; it does not certify that a failed cell is safe to execute again. A failing cell can mutate bindings or produce an external effect before the exception. Continuation follows the operation owner's retry/idempotence contract and available execution facts; there is no universal status-query prerequisite. Host tool calls restore the DSH initiator boundary, so tools that require the exact live calling agent work through `tools.*`.
160
+
161
+ Repeated lexical errors direct the model to visible source, scope, and initialization. Worker-owned lease failures direct continuation to the current namespace; proved missing members recommend capability discovery. Error text cannot impersonate that provenance. The warning threshold is three consecutive identical failures, independently of tip cooldown and escalation. Platform tips consume bounded multiline message/stderr facts when no structured owner cause exists; a generic command exit alone does not establish a platform error.
114
162
 
115
- Durability describes how the session can recover after a kernel restart; it does not certify that a failed cell is side-effect-free or safe to execute again. A failing cell can mutate bindings or produce an external effect before the exception, so inspect the current state and retry only work whose execution status is known. Host tool calls from inside a cell restore the DSH initiator boundary, so tools that require the exact live calling agent (for example goal tracking) work through `tools.*`.
163
+ Cold replay reuses validated recorded `PTC-X001` and `PTC-O001` diagnostic wording for the same actual failure before comparing the complete completion and transcript. Historical help text therefore cannot discard otherwise verified bindings merely because current guidance changed. A real failure-message, completion, or transcript mismatch still contracts recovery; no historical call is redispatched.
116
164
 
117
165
  Known top-level native calls outside the declared direct surface may be normalized into `run_code` when the live schema proves the intended tool. The canonicalizer parses arguments once: ordinary JSON is embedded as JavaScript, while values with own `__proto__` keys use safe literals, so the derived cell needs no `JSON.parse` and keeps execution semantics. The normalization adds no provenance or correction text visible to the model. Unknown, malformed, or internally inconsistent calls remain on the DSH host diagnostic path. The two declared direct tools, `run_code` and `edit_run_code`, are never renamed by this recovery path.
118
166
 
119
167
  Inside a cell, omitting the JavaScript argument of a native `tools` member is accepted only when DSH's live object schema validates `{}`. The worker canonicalizes that omission to `{}` before encoding, so dispatch, the durable call transcript, and cold replay observe the same arguments. This supports natural calls such as `tools.cordis_inspect_list()` without a tool-name exception. Passing `undefined` explicitly, omitting required input, or calling another program namespace retains its existing behavior.
120
168
 
169
+ ## Named State
170
+
171
+ `capabilities.inspect({ symbols: ['repl.state'] })` exposes the optional state API and its timing contract. A save/restore/delete return value acknowledges an accepted operation. Save checkpoints the final settled cell frontier, not the statement where `save` was called. Restore takes effect after settlement for the next cell; reads and writes after awaiting restore still use the current cell's state. Tentative saves can be discarded when the cell becomes volatile or discarded. These operations neither reverse external effects nor certify retry safety.
172
+
121
173
  ## Limits
122
174
 
123
- - The runtime is identified as `typescript`. Type annotations, interfaces, and type aliases are erased before executable JavaScript analysis. TypeScript syntax that requires JavaScript generation, including enums, namespaces, and parameter properties, remains unsupported; JSX and decorators are also rejected.
175
+ `computeMs` measures worker event-loop active time, including synchronous blocking operations such as `execSync`; it is not a measurement proving CPU consumption. Asynchronous waits are instead bounded by `maxWallMs`. Either timeout terminates the worker and resumes later work from verified recovery state. It does not establish child-process cancellation or external-effect rollback; use an asynchronous process API when waiting on external work.
176
+
177
+ - The runtime is identified as `typescript`. Stateful and protected cells erase type-only syntax and lower enums, namespaces, parameter properties, and decorators through maintained TypeScript and decorator transforms. JSX is outside the cell syntax. Historical cells retain their recorded language generation.
124
178
  - `tools.read` is a bounded inspection window in the current DSH tool contract. Whole-file computation in `danger-full-access` should use `node:fs/promises.readFile` or streams and becomes `volatile`.
125
179
  - Relative Node filesystem entry points, including `node:fs/promises` helpers and globbing, resolve from the recorded session cwd. Absolute paths and explicit filesystem options remain unchanged; sessions without a recorded cwd use the worker's native cwd and are classified as volatile when the access is ambient.
126
180
  - The durable import allowlist is `node:assert`, `node:buffer`, `node:querystring`, `node:string_decoder`, `node:stream`, `node:url`, `node:util`, and `node:zlib`. Other Node imports remain usable but make the cell volatile.
@@ -0,0 +1,29 @@
1
+ # 编译语义验证
2
+
3
+ `npm run test:semantics` 运行三个互补的确定性测试层。它们也由 `npm run verify` / `npm run check` 的常规测试发现机制执行,不需要网络或模型调用。定向命令用于快速诊断,不能替代完整检查、资源验收和独立审查。
4
+
5
+ | 层 | Oracle 与执行方式 | 代码 owner |
6
+ | --- | --- | --- |
7
+ | 保留的 JavaScript 语义 | 固定版本的 Test262 原文与官方 assertion harness;先在新的 Node Script realm 执行,再在新的 stateful/protected cell 执行 | `test/semantic-test262.test.js` |
8
+ | PTC 方言 | 声明来源 × 后续操作的明确契约;检查旧闭包、保存的原值、完整 declarator 发布、失败后的状态与真实效果 | `test/semantic-dialect.test.js` |
9
+ | 编译器边界 | 协议操作、参数环境或动态错误 × root/function/eval/module 入口;同一源码先在原生 Node 执行,再比较 PTC 的值与显式源码效果 | `test/semantic-boundaries.test.js` |
10
+
11
+ Test262 子集位于 `test/fixtures/test262/`。`manifest.json` 记录上游仓库、精确 revision、每份原始文件的 SHA-256、语义主题和适用策略。测试验证源码及 harness 的校验值,遵守 strict/noStrict 运行要求;未知 flags、缺少 includes 和未适配的 negative 测试直接使测试配置失败。harness canary 验证错误断言确实会在两个执行路径中失败。
12
+
13
+ 此子集覆盖 lexical 初始化、函数参数/函数体环境、引用求值顺序、迭代器关闭、调用 receiver 与异常完成值。它不代表完整 Test262 合规:PTC cell 有自己的顶层执行契约,尚未接入全套 module/async/realm/agent harness。TypeScript、装饰器、资源管理、真实模块图和历史代际继续由现有专项测试验证。
14
+
15
+ 方言差异必须有规范依据和配对断言。例如提案 §5.1 允许 stateful 赋值先建立值,后续裸声明保留它,因此原生 TDZ 写入拒绝用例只适用于 protected;manifest 指向相应方言断言。不得为了通过而修改上游源码、悄悄跳过失败或把实现限制登记成方言差异。
16
+
17
+ 扩充子集时,从 manifest 的 revision 检出 Test262,原样复制所选用例、依赖的 harness 和 LICENSE,更新校验值及语义主题;升级 revision 时核对全部已选文件。普通测试始终读取仓库内固定输入。优先补齐未覆盖的语义规则与入口组合,而非堆积同一反例的拼写变体。
18
+
19
+ 矩阵是有限笛卡尔积,失败名称会给出协议、操作和入口,可用 Node 的 `--test-name-pattern` 单独复现。原生比较不执行 lowering 产物来生成预期值。涉及对象身份或副作用顺序的规则还需要直接契约断言;相同 JSON 不能证明任意对象等价。扩大生成范围时保留稳定输入和可独立复现的最小案例,禁止无记录的随机种子或无限生成。
20
+
21
+ 当前边界矩阵包括参数默认值闭包与函数体 eval 的组合、六种描述符继承字段与 namespace 反射,以及替换错误构造器后的 eval / Function 失败。装饰器测试用维护中的 TypeScript 标准装饰器 lowering 作独立对照,验证继承属性不成为私有控制状态,同时保留公开 context/access 对象的原型和显式属性读取。资源专项测试把私有栈、函数绑定和 Promise 协议同时替换,并检查 getter/disposer 的原生 caller、receiver、GetMethod 校验顺序、异步间隔和异常身份;原生 Node 24 与强制降级路径比较,Node 22 执行降级路径。真实模块直接持有资源声明,源码重建另经共享语言 lowerer 验证。连续控制台测试跨 cell 保留协议修改,再验证后续计算与恢复,沿用原有资源限额。
22
+
23
+ 失败先写入 checkout-local `REVIEW_FINDINGS.md`,明确规范 owner、共同根因和受影响消费者,再修改实现。源码作用域可见性由 `dynamic-scope-analysis.js` 共享;私有描述符的读写与 Proxy 传输由 `compiler-descriptors.js` 统一处理;`compiler-storage-source.js` 为运行时和独立源码重建提供同一组存储操作;编译失败分类由 `compiler-service.js` 使用编译 realm 的捕获构造器。测试不会使错误抽象自动正确,同类错误跨入口复发时必须纠正其共同边界。
24
+
25
+ ## 发布验证边界
26
+
27
+ 完整确定性检查与独立代码审查是提交门槛,不代表发布稳定性证明。发布前仍需补齐 Node 22/24 下连续会话、反复创建与释放 worker、覆盖插桩和资源压力组合的稳定性验证。进程级失败必须保留原始退出码、signal 与 stderr;仅有测试文件级 `test failed` 无法区分运行时缺陷、引擎退出或外部终止。
28
+
29
+ Windows Node 24/26 的 worker teardown native 崩溃已由插件自有 helper 进程边界限制在可回收子进程内(见 [ADR 0027](adr/0027-run-the-session-kernel-in-a-killable-helper-process.md));`npm run check` 现在执行一次确定性运行,不再根据 TAP 文件级 abort 自动重试。发布前仍须在受支持 Node 版本上保留原始退出码、signal 与 stderr,并覆盖连续创建/释放 helper、覆盖插桩和资源压力组合。文件级失败仍按实际 owner 修复,不得用重试掩盖。