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.
- package/README.en.md +66 -73
- package/README.md +66 -73
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +30610 -722
- package/compiler-amaro.cjs +5 -0
- package/compiler-amaro.cjs.map +6 -0
- package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
- package/compiler-assets.json +3 -0
- package/compiler-core.cjs +554 -0
- package/compiler-core.cjs.map +6 -0
- package/compiler-platform.cjs +2 -0
- package/compiler-platform.cjs.map +6 -0
- package/compiler-typescript.cjs +388 -0
- package/compiler-typescript.cjs.map +6 -0
- package/docs/README.md +6 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +6 -4
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +9 -3
- package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +8 -6
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
- package/docs/adr/0023-global-user-bindings.md +104 -0
- package/docs/adr/0024-repl-console-observation.md +33 -0
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
- package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
- package/docs/architecture.md +82 -22
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +96 -20
- package/docs/durability-design.md +61 -24
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/program-data-plane.md +6 -0
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +79 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +755 -0
- package/docs/user-bindings.en.md +64 -0
- package/docs/user-bindings.md +64 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification.md +57 -0
- package/index.js +374 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/binding-descriptors.js +1 -0
- package/internal/binding-pattern-traversal.js +25 -0
- package/internal/binding-pattern.js +26 -29
- package/internal/binding-source-analysis.js +391 -0
- package/internal/binding-update-policy.js +66 -0
- package/internal/bounded-worker-round.js +56 -0
- package/internal/callable-definition-environment.js +42 -0
- package/internal/callable-source-catalog.js +129 -0
- package/internal/callable-source-encoding.js +47 -0
- package/internal/callable-source-facts.js +459 -0
- package/internal/callable-source-format.js +2 -0
- package/internal/callable-source-lowering.js +123 -0
- package/internal/cell-analysis-contract.js +25 -0
- package/internal/cell-analysis.js +252 -242
- package/internal/cell-completion.js +15 -0
- package/internal/cell-error.js +7 -0
- package/internal/cell-parser.js +66 -0
- package/internal/cell-rewriter.js +3 -553
- package/internal/commonjs-export-evidence.js +63 -0
- package/internal/commonjs-source-evidence.js +72 -0
- package/internal/compiler-amaro.cjs +2 -0
- package/internal/compiler-compression.js +12 -0
- package/internal/compiler-data.js +58 -0
- package/internal/compiler-descriptors.js +54 -0
- package/internal/compiler-dynamic-cache.js +48 -0
- package/internal/compiler-entry.js +35 -0
- package/internal/compiler-generated-names.js +17 -0
- package/internal/compiler-intrinsics.js +84 -0
- package/internal/compiler-module-links.js +34 -0
- package/internal/compiler-operations.js +61 -0
- package/internal/compiler-platform-factory.js +84 -0
- package/internal/compiler-platform.js +136 -0
- package/internal/compiler-record-roles.js +53 -0
- package/internal/compiler-region-output.js +259 -0
- package/internal/compiler-resource-lowering.js +103 -0
- package/internal/compiler-scope-facts.js +115 -0
- package/internal/compiler-service.js +109 -0
- package/internal/compiler-source-cache.js +32 -0
- package/internal/compiler-source-pieces.js +93 -0
- package/internal/compiler-source-regions.js +132 -0
- package/internal/compiler-storage-source.js +54 -0
- package/internal/compiler-text.js +15 -0
- package/internal/compiler-typescript.cjs +2 -0
- package/internal/config-spec.js +91 -25
- package/internal/cordis-tools-owner.js +130 -21
- package/internal/diagnostic.js +27 -1
- package/internal/direct-surface-owner.js +76 -23
- package/internal/dynamic-binding-evidence.js +16 -0
- package/internal/dynamic-environment-compiler.js +779 -0
- package/internal/dynamic-environment-integration.js +87 -0
- package/internal/dynamic-environment-runtime.js +362 -0
- package/internal/dynamic-native-calls.js +150 -0
- package/internal/dynamic-native-runtime.js +248 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +28 -3
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +118 -18
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +548 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +911 -116
- package/internal/language-semantics.js +14 -0
- package/internal/legacy-binding-catalog.js +48 -0
- package/internal/legacy-cell-analysis.js +600 -0
- package/internal/legacy-cell-parser.js +50 -0
- package/internal/legacy-cell-rewriter.js +631 -0
- package/internal/legacy-repl-convenience.js +291 -0
- package/internal/managed-module-operations.js +170 -0
- package/internal/module-compilation.js +321 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +12 -0
- package/internal/native-await-declarations.js +68 -0
- package/internal/native-root-compilation.js +54 -0
- package/internal/native-root-dynamic.js +67 -0
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -201
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +21 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +114 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -51
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +455 -91
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +106 -0
- package/internal/session-journal.js +237 -440
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +325 -80
- package/internal/session-state.js +249 -40
- package/internal/source-position-map.js +211 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +598 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +29 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +184 -0
- package/internal/user-binding-console.js +148 -0
- package/internal/user-binding-draft-projection.js +335 -0
- package/internal/user-binding-model-context.js +31 -0
- package/internal/user-binding-runner.js +56 -0
- package/internal/user-bindings-owner.js +1065 -0
- package/internal/user-bindings-store.js +293 -0
- package/internal/user-bindings.js +353 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +29 -20
- package/internal/worker-client.js +57 -67
- package/internal/worker-environment.js +45 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +81 -11
- package/src/client-activity.js +109 -45
- package/src/client-authoring-view.js +580 -0
- package/src/client-binding-review.js +230 -0
- package/src/client-bindings-data.js +29 -0
- package/src/client-catalog.js +127 -0
- package/src/client-code-editor.js +84 -0
- package/src/client-console.js +112 -0
- package/src/client-copy.js +370 -0
- package/src/client-feature-gates.js +62 -0
- package/src/client-host-compat.js +150 -0
- package/src/client-indicator-view.js +267 -0
- package/src/client-repl-view.js +292 -0
- package/src/client-rpc.js +20 -0
- package/src/client-settings-view.js +221 -0
- package/src/client-styles.js +106 -0
- package/src/client-tool-view.js +108 -0
- package/src/client-transport.js +159 -0
- package/src/client-workbench.js +521 -0
- package/src/client.js +233 -615
|
@@ -14,19 +14,21 @@ PTC Plus 0.1.0 deliberately had no Client UI. The runtime surface was the DSH PT
|
|
|
14
14
|
|
|
15
15
|
PTC Plus registers one DSH settings namespace `ptc-plus` with the same field list, defaults and validation as the Host Config schema. The field definitions live once in `internal/config-spec.js`; `index.js` builds the Cordis Config schema from it, and the esbuild client bundle imports the same module so the UI does not copy defaults or validation.
|
|
16
16
|
|
|
17
|
-
The settings card
|
|
17
|
+
The settings card's seat is generation-owned. The preceding DSH declares `settings.plugin.item` (keyed, root) in its Settings panel and renders one card per served namespace; the current one renders a bundle's own configuration on its side-bar Plugins page through `plugins.row.config` (keyed, root) as `<bundle package name>#<row id>` and reserves `plugins.item` for the host-plane pages DSH itself ships. `src/client-host-compat.js` owns both seats and publishes the card to both, because `ctx.slots.inject` waits while a key is undeclared and neither generation declares the other's key; the declaring generation therefore decides which single seat renders the card. The composer shortcut opens an application-modal rendering of the same complete form, so direct access does not depend on a host page's component-local navigation state; its hover hint still names the native seat available on that generation.
|
|
18
18
|
|
|
19
|
-
The
|
|
19
|
+
The settings card places the plugin switch first, then groups controls by purpose: tool call tolerance, REPL syntax, state and recovery, tool extensions, interface display, and resource limits. Recovery tips and their thresholds stay together, and global binding management appears directly below its enable switch. `CONFIG_GROUPS` owns this presentation order independently of Host schema field order and defaults. `autoDescribeRunCode` defaults to true and controls only a request-bound local execution policy. When enabled, a call that omits the outer `description` uses derived arguments for local DSH validation, and the fallback summary enters presentation metadata only; when disabled, DSH validates the original arguments. Both states produce byte-stable model-visible tool schemas, order, and system sections with the required `description` declaration. Original call arguments, explicit summaries, cell source, and nested native-tool arguments remain unchanged.
|
|
20
|
+
|
|
21
|
+
The configurable set is the Host runtime configuration plus `enabled: boolean` (default true). `enabled` is the kill switch: when false the Host keeps the settings namespace, client card, and passive message cleanup defined in [ADR 0010](0010-session-log-derived-recovery-tips.md). The next permitted accepted step may clear earlier PTC declarations. It does not install the runtime bridge, edit transport, direct-surface owner, system prompt section, tool registrations, or execution hooks, and does not read binding storage. When true the same runtime as pre-settings PTC Plus is installed. Every field is live when its owner can reconcile it: the Host observes the settings scope and installs, uninstalls, or reconfigures owners without replacing existing session-bound bindings. Node fixes a worker's V8 `maxOldGenerationSizeMb` limit at construction, so changing that field while a session worker is active is rejected and the settings provider is restored to the previous value; other reconfiguration failures use the same compensation path. The TypeScript language check occurs at activation, allowing disabled settings to load on a host using another code runtime while rejecting and rolling back an enable attempt. If the settings service is absent, the Host falls back to composition config and enables the runtime.
|
|
20
22
|
|
|
21
23
|
Every settings-card and PTC Plus tool-row string is registered into the DSH client locale namespace `settings.ptcPlus` and follows the active DSH client locale. Field copy exists in both locales from the single `internal/config-spec.js` source (`label`/`labelEn`, `description`/`descriptionEn`); display chrome copy is owned by the Client half. The stable REPL guidance remains plugin-owned protocol text without UI branding. The transport names remain `run_code`/`edit_run_code`; DSH owns their identity.
|
|
22
24
|
|
|
23
|
-
The existing `conversation.session.header.actions` entry with id `ptc-plus-active` remains the lightweight enabled indicator. Hovering it, or focusing it from the keyboard, opens a compact reusable-binding card. The card lists the top-level bindings the current runtime owner has proved reusable by the agent, classified as variable, function, class, or import. Each entry includes its original declaration line and column, a one-line preview, and an accessible inline action that expands the declaration source. Preparation owns declaration spans before execution and `BindingCatalog` retains that provenance as bindings are replaced or replayed; the presentation path never reads, serializes, previews, or invokes the bound runtime values or getters. The runtime emits a complete, value-independent `meta.dshPtcPlusBindings` presentation envelope after each settled cell. The enclosed inventory is bounded to 128 entries, 1024 UTF-16 code units per declaration source, and 16384 code units of declaration source in aggregate, and reports its exact valid total and omitted count. Volatile is a cold-recovery classification, not a live-availability classification, so a live volatile worker continues to publish
|
|
25
|
+
The existing `conversation.session.header.actions` entry with id `ptc-plus-active` remains the lightweight enabled indicator. Hovering it, or focusing it from the keyboard, opens a compact reusable-binding card. The card lists the top-level bindings the current runtime owner has proved reusable by the agent, classified as variable, function, class, or import. Each entry includes its original declaration line and column, a one-line preview, and an accessible inline action that expands the declaration source. Preparation owns declaration spans before execution and `BindingCatalog` retains that provenance as bindings are replaced or replayed; the presentation path never reads, serializes, previews, or invokes the bound runtime values or getters. The runtime emits a complete, value-independent `meta.dshPtcPlusBindings` presentation envelope after each settled cell. The enclosed inventory is bounded to 128 entries, 1024 UTF-16 code units per declaration source, and 16384 code units of declaration source in aggregate, and reports its exact valid total and omitted count. Volatile is a cold-recovery classification, not a live-availability classification, so a live volatile worker continues to publish retained bindings while their exact provenance remains in the model-visible state frontier. A restore-bearing or discarded settlement, or a DSH surface contraction that hides binding provenance, contracts the worker and reports the binding surface as unconfirmed until a later cell materializes and proves the effective state.
|
|
24
26
|
|
|
25
27
|
The inventory preserves `BindingCatalog`'s last-declared-first stack order instead of sorting by name. Declarations later in one cell appear above earlier declarations, and redeclaring an existing binding moves it back to the top. The Client aligns every entry through a fixed-width type column followed by its name and definition preview.
|
|
26
28
|
|
|
27
|
-
The Host optionally registers `ptcPlusRepl` through DSH's public `sessionProjections` seam. Each enabled runtime activation owns an opaque presentation generation shared by its metadata producer and projection definition. The projection's pure fold pairs public `run_code` and `edit_run_code` events through the formal `tool/call.data.callId` and `tool/result.data.message.source.callId` identity; optional `sourceEventSeqs` provenance is not required. It accepts only complete snapshots from its own generation, clears live proof at `session/end-seed`, and serves only the inventory to the Client. A paired result with absent, malformed, or foreign-generation metadata cannot overwrite an already proved inventory
|
|
29
|
+
The Host optionally registers `ptcPlusRepl` through DSH's public `sessionProjections` seam. Each enabled runtime activation owns an opaque presentation generation shared by its metadata producer and projection definition. The projection's pure fold pairs public `run_code` and `edit_run_code` events through the formal `tool/call.data.callId` and `tool/result.data.message.source.callId` identity; optional `sourceEventSeqs` provenance is not required. It accepts only complete snapshots from its own generation, clears live proof at `session/end-seed`, and serves only the inventory to the Client. A paired result with absent, malformed, or foreign-generation metadata cannot overwrite an already proved inventory. Unrelated tool results and result-only surface replacements that leave binding provenance model-visible are also no-ops for this UI fold; a replacement that contracts the runtime's model-visible state frontier invalidates the corresponding inventory through the same owner decision, so presentation cannot claim a binding the next cell will not own. The bounded call-correlation state is checkpointed separately from the wire value, carries the generation, and has its own invalidation version. A cached state from another generation normalizes to an unconfirmed inventory without replaying stale live claims. DSH deployments without the service, hosts exposing an incompatible registration contract, and unsupported versioned envelopes retain the indicator without binding data while the core runtime stays enabled. Existing logs require no migration. The DSH header-action kit exposes no public navigation command for opening a tool call or source location, so the card uses inline inspection and does not reach through private stores or query Host DOM. Source navigation requires a public Host capability. The projection generation and private metadata are presentation-only: neither enters model messages nor changes the closed journal schema, canonical result value, runtime context, tool schema, replay reader, authority, or compatibility rules. In particular, the UI inventory does not prove model awareness or keep a binding alive after the runtime's model-visible frontier contracts.
|
|
28
30
|
|
|
29
|
-
While the setting is ready and enabled, the Client registers `run_code` and `edit_run_code` in the public keyed `tool.call.toolview` slot; disabling disposes both entries and restores DSH's owner fallback. Each replacement row preserves expandable source, result, visible lifecycle state, and Inspect access, then derives optional feature notices from existing PTC Plus presentation metadata. A non-PTC session uses a neutral Code title unless a settled result carries a valid PTC journal. The view never folds a conversation window or displays cumulative counts. Unknown or malformed metadata
|
|
31
|
+
While the setting is ready and enabled, the Client registers `run_code` and `edit_run_code` in the public keyed `tool.call.toolview` slot; disabling disposes both entries and restores DSH's owner fallback. Each replacement row preserves expandable source, result, visible lifecycle state, and Inspect access, then derives optional feature notices from existing PTC Plus presentation metadata. A non-PTC session uses a neutral Code title unless a settled result carries a valid PTC journal. The view never folds a conversation window or displays cumulative counts. Unknown or malformed metadata is ignored as feature evidence without hiding the base row; v1 journals with non-empty confirms receive the same treatment because the Client deliberately does not load the session identity needed to resolve them.
|
|
30
32
|
|
|
31
33
|
Feature notices name actual per-result events rather than configuration defaults: validated import/export/mixed-redeclaration provenance, a complete safe-edit target/derived-run/non-noop/optional-recovery-boundary relation consistent with any target precondition in the outer call, successful `code.run` calls, a completed durable recovery proven by the stable `PTC-R002` warning/recover/rolled-back tuple, and `repl.state` operations. Volatile-retained and discarded state remain recovery classifications, not user-facing feature notices. Recovery boundaries are validated when present but deliberately not displayed. Features without an existing Client-only fact, including ordinary loose redeclarations, canonicalizer provenance, official ownership of a `cordis_*` member, and whether a model recovery tip was selected, are not guessed from code shapes, name prefixes, or settings. The body feature row remains read-only and adds no Host owner. The REPL-memory card adds only the public session projection and parallel presentation snapshot described above; it adds no tool, prompt section, runtime context, session event type, journal field, value inspection, or migration behavior.
|
|
32
34
|
|
|
@@ -35,7 +37,7 @@ Runtime activation is transactional. If a live enable cannot complete, every own
|
|
|
35
37
|
## Alternatives Considered
|
|
36
38
|
|
|
37
39
|
1. Keep no Client UI. Rejected: PTC Plus requires a settings surface and a kill switch. Away from settings, users cannot tell whether PTC Plus is actively installed because the only marker is application behavior.
|
|
38
|
-
2. Rely on DSH generic schema rendering without a
|
|
40
|
+
2. Rely on DSH generic schema rendering without a settings-card seat. Rejected: the generic tab renders only namespace cards supplied through the slot; shipping no card would leave the namespace invisible even though the schema is registered.
|
|
39
41
|
3. Rename the `run_code` tool or use a schema/name-level UI alias. Rejected: PTC Plus must preserve the DSH host tool names and their public pipeline semantics; presentation labels belong in Client-owned UI, not in transport identity or model-visible stable text.
|
|
40
42
|
4. Keep non-`enabled` fields restart-applied. Rejected: a settings UI that persists values without changing the active runtime is misleading. Runtime owners now accept live configuration updates; session kernels keep their bindings while applying new limits and policies to subsequent cells.
|
|
41
43
|
5. Keep the card copy Chinese-only, or let the card branch on the browser locale itself. Rejected: the plugin's own copy must follow the same Host-owned locale preference every other settings surface follows, so it registers both dictionaries under `settings.ptcPlus` and uses the `t` seat composed by the DSH slot renderer; duplicating per-locale text outside `internal/config-spec.js` would break the single-source rule for field copy.
|
|
@@ -14,9 +14,9 @@ Tool registration must finish before the prompt assembly returned to the agent;
|
|
|
14
14
|
|
|
15
15
|
## Decision
|
|
16
16
|
|
|
17
|
-
Add `cordisToolsEnabled: boolean`, default `false`, as a live PTC Plus setting. When enabled, PTC Plus mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and a maintained `@deepseek-ai/dsh-skill-filesystem` provider in each agent scope that exposes `run_code
|
|
17
|
+
Add `cordisToolsEnabled: boolean`, default `false`, as a live PTC Plus setting. When enabled, PTC Plus mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and a maintained `@deepseek-ai/dsh-skill-filesystem` provider in each agent scope that exposes every surface the mount publishes through: `run_code`, the `skill` tool, and the public `agentPresets` and `skills` services; disabling it disposes both fibers immediately. The provider resolves the shipped `cordis` preset through the public `agentPresets` service and reads its sibling `skills` root without including default roots. At the public `skills.registerProvider` seam, PTC Plus transparently filters discovery and loading to exactly `cordis-plugin-development`; the maintained filesystem provider still owns parsing, resource bases, watch, invalidation, incomplete observations, and body loading. This prevents unrelated or obsolete sibling Skills from silently enlarging the PTC surface without copying the companion content or switching the current preset. A scope that omits one of those surfaces is not an error: the owner keeps the agent pending, publishes nothing there, and retries on the DSH tools-change signal or the next prompt assembly, because a later registration or provided service can supply the missing member ([ADR 0029](0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md)).
|
|
18
18
|
|
|
19
|
-
The Skill provider and official Cordis child fiber form one reversible mount. Both must settle, and the scoped `skills` service must prove that `cordis-plugin-development` resolves from the added provider as model-invocable and loadable, before the first prompt assembly is returned. A prepended public `system-prompt/assemble` waterfall listener is the final race barrier: if collection began before the mount settled, it waits for readiness, discards that stale assembly, and invokes public assembly again so DSH recollects the official providers. It does not synthesize or mutate Cordis schemas or guidance. The Cordis fiber's resolved `inject` declaration is the authoritative required-service list, and its live registrations are the authoritative tool names, schemas, and guidance. A
|
|
19
|
+
The Skill provider and official Cordis child fiber form one reversible mount. Both must settle, and the scoped `skills` service must prove that `cordis-plugin-development` resolves from the added provider as model-invocable and loadable, before the first prompt assembly is returned. A prepended public `system-prompt/assemble` waterfall listener is the final race barrier: if collection began before the mount settled, it waits for readiness, discards that stale assembly, and invokes public assembly again so DSH recollects the official providers. It does not synthesize or mutate Cordis schemas or guidance. The Cordis fiber's resolved `inject` declaration is the authoritative required-service list, and its live registrations are the authoritative tool names, schemas, and guidance. A scope whose exposed preset, Skill service/tool, declared service, publication, or child fiber contradicts the DSH contract is a configuration error and rolls back every contribution; an absent surface only defers the mount. An unusable scope reached through the host's own calls — `agent/created` and the prepended waterfall — withdraws and reports one warning instead of failing the host operation that created the scope ([ADR 0029](0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md)).
|
|
20
20
|
|
|
21
21
|
The official plugin also registers Host inspect providers in the process-global `cordisInspect` service. Per-agent fibers therefore acquire owner-scoped leases for identical provider manifests instead of registering the same provider ID independently. Queries delegate to a registration from a still-live official fiber, and the process-global provider is removed when the final lease is released. A manifest mismatch remains a configuration error rather than silently choosing one contract.
|
|
22
22
|
|
|
@@ -30,6 +30,18 @@ append/flush queue. Legacy `ptc-plus/recovery-boundary` artifacts therefore
|
|
|
30
30
|
remain an explicit, startup-before-restore migration concern rather than a
|
|
31
31
|
runtime event handler concern.
|
|
32
32
|
|
|
33
|
+
Host format upgrades are also explicit pre-restore work. The migration CLI may
|
|
34
|
+
resolve the selected Host's public Session format catalog and use its strict
|
|
35
|
+
conversion and current validation. PTC result metadata remains opaque to that
|
|
36
|
+
catalog. The CLI therefore proves an ordered one-to-one relation between the
|
|
37
|
+
unchanged tool records, remaps only PTC-owned sequence references, and validates
|
|
38
|
+
the resulting timeline. Original call arguments and recorded effects are never
|
|
39
|
+
rewritten or executed. Ambiguous records, invalid PTC evidence and mixed retired
|
|
40
|
+
event conversions are refused. The source artifact remains intact and output
|
|
41
|
+
uses the target Host's encoder; already-renumbered metadata requires a retained
|
|
42
|
+
predecessor rather than inference from numeric values. See the
|
|
43
|
+
[upgrade procedure](../installation.md#session-format-upgrades).
|
|
44
|
+
|
|
33
45
|
The EasyRewrite host/client split is an architectural reference only; its
|
|
34
46
|
session and workspace UI APIs are not a persistence recovery mechanism.
|
|
35
47
|
|
|
@@ -58,11 +70,13 @@ assembly pattern remains available for future UI or host features. A plugin
|
|
|
58
70
|
update alone cannot repair an already-running DSH process that has failed while
|
|
59
71
|
loading legacy artifacts; the process must be stopped and the artifacts
|
|
60
72
|
migrated before restore. Existing valid sessions are unaffected by the
|
|
61
|
-
migration, while malformed legacy artifacts must
|
|
62
|
-
diagnosable.
|
|
73
|
+
migration, while malformed legacy artifacts must remain unchanged and
|
|
74
|
+
diagnosable. This migration rule rejects an unsafe overwrite of the source
|
|
75
|
+
artifact; it does not authorize the session runtime to reject later valid cells
|
|
76
|
+
once DSH has supplied a loadable session. Runtime contraction follows
|
|
77
|
+
[ADR 0007](0007-separate-worker-transport-from-session-semantics.md).
|
|
63
78
|
|
|
64
79
|
The long-term upstream requirement is one of: a restore-preflight migration
|
|
65
80
|
hook, an atomic raw-artifact rewrite API, or a contract that isolates one bad
|
|
66
81
|
session from workspace registry initialization. None of these is emulated by
|
|
67
82
|
PTC Plus in the absence of a public DSH extension point.
|
|
68
|
-
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Keep Global User Bindings Source-Owned and User-Controlled
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
Repeated project helpers are useful across sessions, but ordinary REPL bindings belong to one session and cannot be promoted losslessly from a runtime value back into TypeScript module source. A persistent helper feature must keep executable values, model-visible declarations, storage, and cold recovery aligned without turning helpers into DSH tools or granting the model a second authority path for persistent changes and candidate execution.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
Global User Bindings is an optional PTC Plus capability controlled by `userBindingsEnabled`, which defaults to `false`. Disabled mode does not read storage, inject values or new declarations, register management RPC or commands, or render management UI. The next permitted accepted step can withdraw a previously delivered declaration through the PTC binding catalog contract in [ADR 0010](0010-session-log-derived-recovery-tips.md). Enabling the capability does not change DSH authority, approval, scheduling, cancellation, sandbox, or the code-only direct-tool projection.
|
|
10
|
+
|
|
11
|
+
Each entry is a TypeScript module with named value exports, a stable ID, display/call name, `namespace` or `top-level` scope, selected symbols, purpose, enabled state, and optional `modelContext`. Omitted symbol selection derives every named value export; an explicit selection limits the exposed API. One strict normalizer derives body-free declarations, binding kinds and durability from source, fingerprints the complete stored entry, and rejects any supplied derivative that disagrees. Activated entries are ordinary writable REPL bindings; a model redeclaration follows the configured binding policy and shadows the default only for that session.
|
|
12
|
+
|
|
13
|
+
New-generation export descriptors consume the shared TypeScript value normalizer used by module compilation, including enum and namespace values and erased type-only declarations. Original type-name evidence remains available for bounded signature projection. Validation derives this evidence without executing module source; historical snapshot transforms retain their recorded descriptor rules.
|
|
14
|
+
|
|
15
|
+
`modelContext` contains `includeDeclaration` (default `true`) and `instructions` (default empty, at most 4096 UTF-16 code units). The checkbox controls injection of the existing source-derived interface; a nonempty prompt is injected independently. There is no separately authored interface. Unchecking the declaration and clearing the prompt omits both without disabling execution. Source-derived declarations remain canonical for conflict checks and recovery. The enabled set's selected declarations and prompts have an aggregate 16384-code-unit budget in addition to the existing source-derived declaration budget. The shared Client-safe metadata normalizer preserves these fields in accepted draft history.
|
|
16
|
+
|
|
17
|
+
Omitting `modelContext` preserves the existing entry serialization and fingerprint; stored documents need no migration. Historical snapshot recovery additionally requires the transform evidence described below. When present, normalized metadata participates in the existing fingerprint. Previously saved `enabled` and `declaration` fields retain their normalized representation for fingerprint verification, including the old declaration string limit of 8192 code units, but custom declarations no longer enter model or UI presentation. A legacy disabled prompt maps to `includeDeclaration: false` and empty instructions; otherwise its prompt is retained and the source-derived interface is included. An explicit `includeDeclaration` takes precedence. Editing and saving through the workbench writes current fields. Existing entries without metadata gain first-turn discovery through the default declaration setting.
|
|
18
|
+
|
|
19
|
+
For new cells, the complete snapshot fingerprint is an integrity check, not a module reuse key. For the same active entry ID, the worker reuses the module when its exact source, scope, namespace call name, and ordered selected exports match. Changes to model context, purpose, or a top-level entry's display name preserve module state and do not repeat initialization. Execution-input changes, removal, and disablement retain their activation and shadow rules. Each cell still records the complete current snapshot and fingerprint.
|
|
20
|
+
|
|
21
|
+
Journal version 6 introduced the required `userBindingsReusePolicy`: new cells write `implementation-v1`, while closed versions 1 through 5 normalize to `fingerprint-v1`. Later closed generations keep the field required: version 6 retains its recorded policy, and version 7 adds the import-expression boundary documented in [ADR 0014](0014-persist-cell-rewrite-policy.md). Version 8 adds the required `userBindingsShadowPolicy` and the per-name evidence a settled new cell records under it, without changing the reuse policy's meaning; the two generations stay independent, so a cell can replay per-name attachment under either reuse policy. The historical policy reuses a module only when the complete entry fingerprint matches, preserving the resets caused by purpose and top-level display-name changes. Version 5 and later retain their snapshot fingerprint; versions 1 through 4 retain their explicit no-snapshot migration. Replay passes each cell's recorded policy to the worker, including in mixed histories; subsequent live cells use the current policy without resetting a matching implementation merely because the policy changed. Normalizing a journal again preserves that policy. Missing or unknown policies invalidate historical evidence and contract the recoverable frontier without blocking the current valid cell. Replayed initializer calls return recorded values without redispatching host effects. Void completions do not prove internal state equivalence, so they cannot substitute for the recorded policy.
|
|
22
|
+
|
|
23
|
+
Namespace call names and selected export names must parse as exactly one Identifier whose AST name equals the complete supplied string. Whitespace, comments, binding patterns, extra declarations and escaped spellings cannot change identifier identity between validation, declarations, conflict checks, activation and recovery. Supported Unicode identifiers keep their exact spelling. Top-level entry display names remain separate from callable export names.
|
|
24
|
+
|
|
25
|
+
## Storage and Source Ownership
|
|
26
|
+
|
|
27
|
+
The Host owns one document at `$DSH_HOME/ptc-plus/bindings.json`. The store serializes mutations, holds a file lock, compares the last-read disk bytes, requires an expected revision, and atomically replaces the file with owner-only permissions. The complete enabled declaration budget is validated before replacement; an externally written document that exceeds the same budget is treated as damaged storage. A malformed document remains an explicit error, and stale or external changes require reload instead of blind overwrite. A new Agent draft uses create-only persistence so a concurrent entry with the same ID cannot be replaced.
|
|
28
|
+
|
|
29
|
+
The workbench keeps a save revision with the loaded source. Catalog-only changes cannot advance it unless a successful compare-and-swap operation proves the source unchanged. Reload synchronizes a read-only selection's source, declaration and baseline; a removed entry clears the selection. While editing, explicit reload preserves the draft and updates its save revision and cancel baseline. Catalog and entry reads must agree on revision before applying the reload. Stale responses cannot change a newer workbench. Reload neither saves nor executes code; changing the selected source releases its temporary console environment, while unchanged source retains that environment.
|
|
30
|
+
|
|
31
|
+
Source is the only implementation authority. Both candidate execution and session activation resolve relative imports from the directory containing `bindings.json`; a session cwd therefore cannot change a persistent helper's dependency graph. The parser accepts named value exports and rejects default exports, re-exports, reserved bindings, invalid selected symbols, and conflicting enabled call names. Model-visible declarations retain useful annotations and bounded inferred shapes but omit implementation bodies and private helpers.
|
|
32
|
+
|
|
33
|
+
`internal/stateful-module-compiler.js` owns binding-module compilation, shared by validation, live/cold activation, candidate execution and the draft console. Its maintained TypeScript transform owner remains `internal/typescript-transform.js`. Snapshot version 2 records either the historical `amaro@1.1.11` transform or `stateful-module-v1+amaro@1.1.11`, and includes that exact identity in the snapshot fingerprint. The latter applies the shared logical scope rules before TypeScript transformation; the former retains its historical lowering. Entry fingerprints and stored source remain source-owned. Unsupported identities cannot be substituted with the current compiler, and a transform change participates in runtime reuse identity. The complete language and journal-generation decision is [ADR 0025](0025-use-versioned-logical-binding-identities.md).
|
|
34
|
+
|
|
35
|
+
Version 1 snapshots did not record the native compiler. A matching source,
|
|
36
|
+
declaration, durability classification or cell completion does not prove matching
|
|
37
|
+
module values: an enum initializer can change a saved value while its cell returns
|
|
38
|
+
`undefined`. Nonempty version 1 snapshots therefore cannot prove a recovery
|
|
39
|
+
frontier. Existing journal recovery contracts at that cell and its dependent
|
|
40
|
+
suffix, persists and reports the contraction once, and executes the current
|
|
41
|
+
valid cell. It does not replay discarded effects, rewrite the historical log or
|
|
42
|
+
modify stored binding entries. Empty version 1 snapshots retain their original
|
|
43
|
+
fingerprint and remain usable as recovery evidence; subset selection preserves
|
|
44
|
+
the snapshot version and transform identity. Reconstructable cells still obey
|
|
45
|
+
their recorded module-reuse policy independently of the transform identity.
|
|
46
|
+
Ordinary REPL cells use the shared scope normalization and source-position contract with their recorded cell language generation.
|
|
47
|
+
|
|
48
|
+
## Runtime, Prompt, and Recovery
|
|
49
|
+
|
|
50
|
+
Prompt assembly captures the current enabled snapshot as the desired input later passed through dispatch. Its `tools:ptc-plus-user-binding-defaults` context describes configured bindings from the first model turn, using each entry's nonempty prompt and its source-derived declaration when selected. Entries with neither are omitted. It never evaluates a module or claims successful activation. The catalog wrapper identifies use inside `run_code` and replacement of the previous global binding API reference. Entry labels, selected prompts and declarations form the entire body; initialization failures belong to execution results. Entries are ordered by stable ID; revision counters and activation facts do not enter this context. It belongs to the independently sourced PTC binding API catalog, appended at the matching accepted `agent/pre-step` under the Host's runtime-context permissions. Saving and enabling a `/binding` draft, enabling or removing an entry mid-session, and changing prompt settings update the next permitted request without waiting for activation or a new session. Merely accepting a disabled draft does not publish it.
|
|
51
|
+
|
|
52
|
+
Configured prompts and interfaces never enter `PromptAssembly.sections` or its variables. Entry changes preserve the complete system text and ordered tool schemas; they append a new binding catalog that supersedes the earlier catalog, including an explicit empty catalog when no configured documentation remains. Committed messages and the public ordered surface own deduplication, restart reconstruction and reassertion after compaction. Rejected, cancelled or runtime-suppressed steps do not mark delivery; the next permitted request reconciles current configuration. Host runtime-context clearances do not withdraw a retained catalog, and recovery snapshot updates do not resend it. Text is delivered literally, so `{{...}}` in prompts or source-derived declarations neither substitutes Host variables nor invokes template validation. No per-session frozen configuration cache or second persistence store is introduced.
|
|
53
|
+
|
|
54
|
+
Configured documentation is the only new model-visible source of binding prompts and interfaces. Successful activation, session-local shadows and worker reconstruction do not append an activation announcement or resend an unchanged configuration. Execution results and diagnostics own actual execution facts; an availability check observes a known name without external effects. A separate active-declaration channel would duplicate the configured API and make every first successful cell repeat the whole binding context. The reader still accepts historical binding configuration and `tools:ptc-plus-user-bindings` sections: the next permitted accepted recovery snapshot omits them and explicitly supersedes the earlier claims once, including when bindings are disabled. Current documentation is supplied independently as a catalog. Existing messages and journals are not rewritten. Binding contents do not modify `tools:sdk`, direct-tool schemas, or tool order. Ordinary requests receive neither module source nor management operations. Configured API documentation remains a statement of defaults and does not preserve session-local state across compaction or recovery.
|
|
55
|
+
|
|
56
|
+
The worker activates entries before the cell and reports entry-level failures without blocking independent code. A request-owned program namespace or error class takes precedence over a same-name user entry, which is excluded from that request's activated snapshot. Every current request namespace is also exposed to binding modules through a stable worker-global bridge, but each call resolves the AsyncLocalStorage execution lease that reached it; a continuation from a completed cell cannot borrow a later cell's capabilities. A request namespace that collides with any existing worker global fails before bridge installation because replacing a Node intrinsic cannot preserve both environments. BindingCatalog records `user-global` provenance and attaches current entries per call identifier: a session-local shadow suppresses only that name from subsequent global activation while the entry's remaining names keep activating, so one local override neither erases a sibling helper nor resets the module state that provides it. Journals migrated from versions 1 through 7 keep the historical whole-entry suppression, and `userBindingsShadowPolicy` fixes which rule each cell replays under. Namespace members and top-level exports read their live module values until assignment or a permitted redeclaration creates that shadow. Each settled cell persists the exact successfully activated snapshot in private result metadata, while journal versions 5 and later record its fingerprint or an explicit `null` when no snapshot exists. Per-name cells additionally record the `provider`/`local`/`absent`/`unknown` attachment facts the worker reported at settlement, validated against that snapshot. Replay re-activates the recorded snapshot and re-executes the recorded source, then requires the facts reported during that replay to equal the recorded ones: the record verifies replay instead of installing names, and a divergence is rejected rather than resolved from the current store. If a failed initializer issued a program call, the cell becomes volatile because that snapshot cannot supply its replay source. Cold recovery validates the journal-to-snapshot relation and executes recorded source rather than consulting the current store; missing, malformed, or source-inconsistent evidence contracts the recoverable frontier under the existing fail-closed journal rules.
|
|
57
|
+
|
|
58
|
+
An exported function may escape into an ordinary session binding before its global entry is updated, disabled, or removed. Reachability cannot be proved inside the worker, so every successfully exposed binding module retains its relative-import parent mapping and dynamic program-namespace bridge until that worker ends. Failed modules that expose no value release their temporary mapping. 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.
|
|
59
|
+
|
|
60
|
+
Installed provider setters can likewise remain callable after their entries disappear. BindingCatalog retains their name eligibility along the accepted execution ancestry, separately from current entries and the visible binding inventory. A later setter write may therefore establish a local value without reactivating its provider. Provider facts still require the current activated snapshot and must not conflict with request-owned names; failed activation or preflight does not establish historical eligibility. Worker reset clears this evidence, and cold replay rebuilds it only from the retained verified ancestry, including historical whole-entry cells. Recovery contraction cannot carry eligibility across a discarded boundary.
|
|
61
|
+
|
|
62
|
+
The workbench code console runs the current unsaved source draft in a separate bounded Node REPL worker and never accesses the Agent session kernel or its journal. Named exports are available directly; commands accept TypeScript and top-level await, return inspected expression values and logs, and preserve declarations across executions. Ordinary syntax and runtime errors preserve the environment and prior partial effects. The existing one-shot candidate RPC remains compatible. Both paths retain DSH process authority and can produce irreversible Node/OS effects before failure or cancellation; worker state isolation is not a security sandbox.
|
|
63
|
+
|
|
64
|
+
An authenticated user request lazily creates an opaque Host-generated environment handle. Each workbench owns its handle, one command at a time, with at most four environments per owner. Worker wall, output and memory limits use the existing configuration. Source and command inputs are each limited to 1 MiB. Ten minutes without code execution releases the worker without erasing displayed history. Source replacement at the next execution, switching entries, saving changed source, leaving the workbench, explicit reset, cancellation, timeout, output failure, worker failure, disablement or owner disposal also releases it. No action replays earlier commands automatically. Folding, ordinary editing and clearing displayed history do not execute code. The user may explicitly start again from the current draft after release.
|
|
65
|
+
|
|
66
|
+
## Agent-Assisted Authoring
|
|
67
|
+
|
|
68
|
+
An enabled agent whose effective public tool view contains `run_code` receives `/binding new <requirement>` and `/binding edit <id> <requirement>` through DSH's public command service. Eligibility is available before prompt assembly, follows `tools/change`, and does not infer capability from a preset label. The command registration belongs to an injected child fiber of the exact agent scope. A pending fiber installs when the command service becomes available; a callback whose eligibility expired fails and removes its placeholder. Lifecycle listener installation has its own generation, independent of request and projection changes. The Client uses DSH's session command directory and a compact `conversation.input.left` action, including before the first model turn.
|
|
69
|
+
|
|
70
|
+
Either command appends complete authoring instructions through `agent.steer` and completes admission once the normal Agent turn has been requested. Admission means authoring started, not that a candidate exists. Under enabled configuration, `code.submitBindingDraft({requestId, entry})` is a stable program SDK member, including its discovery schema. It uses the ordinary cell lease and program-call transcript. The Host captures the exact active request at cell creation and checks agent identity, request ID, generation, and single-submission eligibility before and after asynchronous validation. Acceptance, turn stop, error, cancellation, replacement, disablement, or disposal revokes submission eligibility without changing the declaration. No request-specific tool or Skill registration changes the system prefix. Cold replay returns a recorded receipt without submitting again or restoring writable access.
|
|
71
|
+
|
|
72
|
+
The schema, SDK and authoring instructions include both `modelContext` fields. Authoring asks for a declaration-injection choice and a usage prompt containing only information missing from the interface; when types and API comments suffice, the prompt is empty. Edits preserve existing prompt preferences unless the requirement changes them. The workbench exposes both controls directly, without requiring source-edit mode; changes enter an unsaved draft with save and cancel actions. The input-dock review exposes accepted prompt metadata before a user saves it; the command card retains read-only history. Saving as disabled does not add configured API text to ordinary requests.
|
|
73
|
+
|
|
74
|
+
Authoring uses ordinary `run_code` cells to develop and test pure logic with in-memory inputs, assertions and substitutes. Temporary session variables are permitted; the task prohibits test effects on external files and services. Imports and initializers with unknown effects are reviewed without execution. Tests can guide corrections and comparisons before the final source, exports, types and usage prompt are checked together. The answer reports verified behavior and untested integration, without requiring reasoning transcripts or a fixed number of calls. This is task guidance under DSH authority, not a sandbox or an additional permission mechanism. It does not expose the workbench's candidate runner to the Agent.
|
|
75
|
+
|
|
76
|
+
The existing REPL supplies execution feedback; the handoff accepts final source for user review. Each tool's instructions describe its own task and required inputs. Deterministic Host scenarios verify the execution and submission contracts; explicitly configured model runs evaluate actual authoring behavior. See [binding workflow evaluation](../evaluation.md#binding-workflow).
|
|
77
|
+
|
|
78
|
+
The first validated candidate becomes one disabled memory draft. Private `dshPtcPlusBindingDraft` metadata version 2 contains the generation, opaque locator, and the accepted candidate with request ID, command ID, version, mode, and complete source-owned entry. Later results carry `candidate: null`; version 1 remains readable. The `ptcPlusBindingDraft` projection, state version 4, pairs command and turn lifecycle with acceptance, retains read-only history across seeds and owner generations, and never revives a historical locator. Malformed historical presentation cannot block subsequent valid execution. The management Remote uses the authenticated Host Gateway and provides no caller/session identity, so draft operations require the locator rather than a payload session ID. The `draft-review` endpoint serves the accepted source and any completed action while its owning lifecycle remains available.
|
|
79
|
+
|
|
80
|
+
After a user save or discard completes, the Host consumes the writable locator and appends a structured plugin notice through public `Session.append('user/message', ..., {surfaceOp: 'append', sourceEventSeqs: [acceptingResultSeq]})`. The notice contains only request ID, entry ID, saved/discarded state, and enabled state. It asserts persistence, never session activation, and contains no source or locator. The projection accepts it only when identity and the accepting source relation match. Card remounts use this historical source and action, never a later store entry with the same ID. A failed notice append cannot undo the write or restore save eligibility; missing durable evidence remains unknown after restart. In-flight storage settlement cannot resurrect a locator already revoked by lifecycle cleanup. Clearing an unsaved draft alone does not append a persistence notice.
|
|
81
|
+
|
|
82
|
+
Catalog revision conflicts refresh the catalog, draft, and review independently. A still-valid draft stays inspectable and retryable by the user; an authoritative empty draft revokes the actions. No conflict automatically repeats a write. Agent disposal invalidates only that exact Agent's locators and in-memory reviews; session disposal, feature disablement, and owner disposal revoke all affected access. Durable history proves a past action, not current activation or model knowledge.
|
|
83
|
+
|
|
84
|
+
The Agent's handoff API cannot persist, execute, enable, remove or import stored candidates, or inspect unrelated entries. Session-local tests use ordinary REPL execution as described above. Client actions remain separate: the conversation command card preserves the request and read-only accepted source; one current review controller in the published input dock offers explicit save-as-disabled, save-and-enable, or discard actions, while the shared REPL/Settings-modal workbench owns complete editing, validation, candidate execution, activation, import, and removal ([ADR 0024](0024-repl-console-observation.md)). Both save paths atomically claim the draft before checking the current catalog revision; save-and-enable writes the validated entry enabled in that same mutation. A concurrent discard receives a busy conflict instead of reporting success for a draft that can still reach storage.
|
|
85
|
+
|
|
86
|
+
## Alternatives considered
|
|
87
|
+
|
|
88
|
+
**Register each helper as a DSH tool.** Rejected because it changes dispatch and approval semantics and duplicates DSH's capability ownership instead of extending the REPL value environment.
|
|
89
|
+
|
|
90
|
+
**Register and unregister a handoff tool per request.** Rejected because transient transaction eligibility changes the complete SDK prefix on both entry and exit. A stable internal API with Host transaction prerequisites preserves the same authority and one-submission contract without those changes.
|
|
91
|
+
|
|
92
|
+
**Expose persistent management operations to the model.** Rejected because an ordinary model error could change every future session. A request-scoped draft handoff preserves assisted authoring while leaving durable and effectful actions under explicit user control.
|
|
93
|
+
|
|
94
|
+
**Promote a session binding into global storage.** Rejected because runtime values and bounded declaration provenance do not reconstruct complete module source, imports, comments, and types. Persisting a guessed representation would claim losslessness that the system cannot prove.
|
|
95
|
+
|
|
96
|
+
**Store source and metadata in separate files.** Rejected because source, symbol selection, purpose, and activation state would gain independent write and recovery histories. One atomically replaced document keeps identity and implementation under one owner while `.ts` import supports file-based authoring.
|
|
97
|
+
|
|
98
|
+
**Resolve helper imports from each session cwd.** Rejected because the same persistent entry could load different dependencies in candidate validation, live activation, and replay. The storage directory is stable across those paths.
|
|
99
|
+
|
|
100
|
+
## Consequences
|
|
101
|
+
|
|
102
|
+
Users can maintain typed helpers once and reuse them across sessions while models receive compact callable declarations and ordinary REPL values. Storage, prompt projection, execution, and replay share one source-derived identity, so recovery fails closed instead of substituting current external state. Session-local experimentation remains cheap and cannot silently overwrite the global default.
|
|
103
|
+
|
|
104
|
+
The cost is an opt-in management surface, a bounded configured declaration/context addition when entries are enabled, source evaluation at activation and replay, revision conflicts that require explicit reload, and user console execution whose external effects cannot be undone. The REPL view and Settings modal share full editing, code execution, import and removal; the input-dock review provides explicit draft save choices and does not expose Agent authority or unrelated entry management.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Keep REPL Observation Separate From Execution Evidence
|
|
2
|
+
|
|
3
|
+
## Decision
|
|
4
|
+
|
|
5
|
+
The REPL console contributes a public `conversation.view` entry alongside Conversation and Trajectory. Its registration follows the browser Client's current session selection and public `agentPreset` projection, appearing only while the plugin and `replViewEnabled` are enabled and the current preset is `ptc` or `code`. `replViewEnabled` defaults to true and does not gate Settings management. The separate `bindingAuthorButtonVisible`, also true by default, hides only the composer shortcut and leaves `/binding` commands available; that shortcut owns the global-binding actions and opens the plugin's complete settings form in an application modal, while its hover hint names the native seat for that host generation. The existing `userBindingsEnabled` remains the global capability switch. The current session is read from whichever selection evidence the installed release publishes: the preceding one projects the selection on the session list, and the current one keeps view selection outside the controller and retains the session its main view shows. DSH owns selection fallback when an entry disappears. Each contribution consumes its own public slot inputs. REPL registration additionally needs the public sessions selection and projection face; the header and command card use the supplied useProjection hook, and authoring needs the public input hooks and remote command capability. A missing uiSession service name does not block contributions whose public inputs exist. Missing projection inputs suppress dependent session views and leave command cards without draft actions. The view does not access private stores, host DOM, or a second session coordinator.
|
|
6
|
+
|
|
7
|
+
Session observation and global management appear as consecutive sections of one page, without nested navigation tabs. A compact session table presents neutral binding names, declaration kinds and optional value previews, with name and kind filters over the existing inventory. Empty inventories use a compact full-width state without an empty inspector. Selecting a row shows its recorded definition and value in an adjacent inspector; selection and filtering never request additional observation. The public TypeScript `CodeBlock` highlights and copies only the recorded bounded source, with plain-text fallback when the primitive is absent. Observation time belongs to the section header; truncation and unreadable states accompany each value. The global section and a large Settings-launched public `Modal` reuse one workbench, with separate component lifetimes. Content is ordered as interface declaration, collapsible implementation source, code console and entry configuration. Source is read-only until Edit mounts CodeMirror with TypeScript highlighting, line numbers and document-local history. Editing never executes code. The independent draft console's execution and lifecycle contract belongs to [ADR 0023](0023-global-user-bindings.md). Ordinary global management requires no PTC session. Authenticated Host Gateway Remote, revision checks, feature enablement, user execution authority, and persistence-versus-activation semantics remain with their existing owners. Agent authoring retains its session command capability requirement.
|
|
8
|
+
|
|
9
|
+
The REPL root opts into the same `data-conversation-composer-overlay` layout as the official trajectory view, letting the conversation shell supply a full-height view and disable transcript width handles. While mounted, it registers an empty, session-matched `conversation.composer` chain entry through Cordis-owned effects. The selector declines pending Host interactions and follows existing takeover priority. The public overlay chain keeps the ordinary composer and draft mounted but hidden; view unmount, ineligibility, and provider disposal release the entry and restore the composer. The plugin neither traverses Host DOM nor overrides Host styles.
|
|
10
|
+
|
|
11
|
+
## Observation Boundary
|
|
12
|
+
|
|
13
|
+
Only a visible session observation region requests previews. The Client combines IntersectionObserver and document visibility, holding a cancellable `watch` operation through the public Remote `ptcPlusRepl.invoke`. The Host bounds active watchers to 64; abort, connection lifecycle, setting disablement and owner disposal release them. Unexpected request completion clears that request and permits up to three retries after 1, 2 and 4 seconds while visible. Exhaustion leaves no timer; visibility or connection lifecycle changes reset the retry allowance. Cleanup cancels both the active request and pending retry, and stale completions cannot replace a newer request. `watch` records interest only. At live-cell dispatch the runtime checks the current interest and tab setting before generating observation names. Replay never collects previews.
|
|
14
|
+
|
|
15
|
+
When the visible inventory has no observation, the Client also requests a one-shot `observe` through the same authenticated Host Remote. The payload contains the bounded inventory, never an expression. The Host validates it and matches it against the existing worker's committed catalog; missing workers, unsettled cells, disabled settings and changed or unreadable surface generations cannot supply observations. The read shares the cell queue and private readiness handshake, with a 250 ms total presentation deadline including queue waiting. It never creates or recovers a worker, runs a cell, or appends an event. Cancellation, owner disposal and worker failure end the pending presentation request without changing execution state. Host concurrency is bounded to 64 requests, and an active inspection excludes another inspection of the same worker. The Client retains the response only for the exact requesting inventory and active visibility request. Projection replacement invalidates it even if names remain equal. No polling or persistent preview cache is introduced. Missing observations say “Not observed yet”, separately from values that cannot be read safely.
|
|
16
|
+
|
|
17
|
+
The logical root introduced by [ADR 0025](0025-use-versioned-logical-binding-identities.md) supplies committed local values directly through `stateful-root-runtime.js`. Observation does not invoke source getters for imports, linked aliases or ambient accessors. Successful declarations and actual writes before a later cell error may still supply a value when the final catalog proves them. The legacy storage path remains separate: Acorn parses successfully executed lowered programs to prove top-level storage, `var` uses own data descriptors, and proved `let`/`const` identifiers use bounded `vm.runInContext` reads. Lexical storage takes precedence over same-name global properties, including when uninitialized. Before user execution, a real evaluator probe determines whether await lexical storage is readable; failure disables that legacy observation capability without blocking execution. No Node version controls this decision. Failed cells add no legacy storage proof, and unproved names cannot expose unrelated same-name global properties. Value observation does not use the REPL evaluator, update its last-result binding, invoke imports, or accept expressions from the Client.
|
|
18
|
+
|
|
19
|
+
`node:util.types.isProxy` rejects proxies, including revoked proxies, before reflection. Own-property descriptors supply shallow descriptions without getters, `toJSON`, custom inspection, prototype traversal, or formatter callbacks. Root functions, symbols, proxies and all non-array objects are unreadable. This includes plain objects, typed arrays (including Buffer), boxed strings and module namespaces. No preview enumerates a complete property set. Primitive strings retain their bounded text previews. BigInts are compared against a fixed magnitude bound before conversion: at most 128 digits are printed, and larger values receive a truncated size-limit marker. Arrays provide a finite index range: previews read only the first five own slots, including explicit hole markers. Every array preview remains marked incomplete because additional own properties are uninspected, even when all indexed slots fit. Nested objects remain type markers and accessors remain unreadable markers. The preview omits prototype/internal state. Text is limited to 512 UTF-16 code units, and at most 128 catalog names are inspected. The observation records its start time and is not an atomic heap snapshot. It neither follows later asynchronous mutations nor claims that the model knows these values.
|
|
20
|
+
|
|
21
|
+
The worker sends normal `done` before optional `observation`. The kernel first performs ordinary execution and journal settlement and detaches execution timers and cancellation listeners. It then waits at most 250 ms for the UI supplement, within the existing cell queue. The worker stops lexical reads after 100 ms and limits each identifier read to 25 ms. Inspection uses fixed array slots and bounded primitive text; it never enumerates arbitrary heap objects. Missing, late, failed, or malformed observations cannot change the settled result or restart a worker. Unsupported inspection degrades to explicit unavailable values. The observer opens no debugging connection and retains only names, never value references.
|
|
22
|
+
|
|
23
|
+
`dshPtcPlusBindings` metadata version 5 and `ptcPlusRepl` projection state version 5 retain the existing inventory and observation data and add per-entry `reuseCount` and a top-level `reuseTotal`. Version 4 added the optional `{ at, entries }` observation data; versions 3 and 4 remain readable, and their missing reuse counts recover as 0. Observation candidates first pass through `createReplMemorySnapshot`, sharing the display inventory's name and definition validation, entry limit, and total definition-source budget. Omitted bindings neither consume observation slots nor invalidate previews for visible bindings. Entries carry `{ name, status, text, truncated }`; strict validation and final-catalog filtering prevent foreign names or over-budget text from being published. A preview is private UI presentation, never a canonical program value, model context, journal entry, recovery proof, or reason to retain hidden state. Existing runtime-generation, seed, restore/discard, and model-visible frontier invalidation governs both the inventory and its attached observation. Cold replay does not collect observations.
|
|
24
|
+
|
|
25
|
+
Result settlement does not establish worker readiness. Before each cell, the executor sends a private `prepare` message with the cell ID. The worker acknowledges `ready` through the existing private port after preceding synchronous observation and cleanup. Only the matching active cell's first acknowledgement dispatches `run`. The existing compute and wall timers start before `prepare` unless the same worker has confirmed that an unfinished observation started. Sending `observe` alone provides no such proof: the worker sends `observation-started` when it handles that request and then observes synchronously. Post-cell `done.observing` provides the equivalent proof for synchronous observation in the same worker turn. Observation identity remains separate from the 250 ms presentation deadline; its completion message or a matching readiness acknowledgement starts the deferred timers. A late start acknowledgement cannot suspend or restart an existing budget. A replaced worker cannot inherit the previous worker's observation exemption. Waiting retains the submitted configuration and remains subject to request cancellation, session disposal, and worker failure. Stale, malformed, and duplicate acknowledgements do not dispatch code. Confirmed observation may defer the next cell's budgets beyond the supplement deadline. Other blocking work, including previous cells' background callbacks before an observation starts, retains timeout and worker-recovery protection. Readiness uses the existing cell queue and leaves journal records unchanged.
|
|
26
|
+
|
|
27
|
+
## Alternatives
|
|
28
|
+
|
|
29
|
+
Ordinary property traversal and serialization can invoke getters, Proxy traps, or custom formatters. General expression evaluation can mutate the session. Inspector-backed previews were rejected because local Inspector sessions produced native Node crashes during Windows worker lifecycle tests, including with explicit disconnection. A separate REPL or cloned state cannot preserve arbitrary closures, modules, handles, and external effects. These are outside the observation contract. The console therefore provides no session code input, object-to-Agent action, or cloned experimental environment.
|
|
30
|
+
|
|
31
|
+
## Verification
|
|
32
|
+
|
|
33
|
+
Contract tests exercise lexical and own-data reads, accessors, custom formatters, Proxy traps, revoked proxies, text and structure limits, malformed metadata, missing and late observations, continued binding reuse, and existing recovery invalidation. Readiness tests cover delayed, stale, malformed, and duplicate acknowledgements, cancellation, disposal, worker failure, transport failure, background-callback timeouts, and budget activation after a late observation completes. An isolated process regression compares observation enabled and disabled for a four-million-key plain object in a 128 MiB worker, then verifies later cells retain their 100 ms execution budgets and existing bindings. Public DSH renderer tests exercise current-session registration, setting/provider disposal, observation retry/backoff cleanup, localized observation, shared management, conflict recovery with retained edits, and access through Settings without a session. Playwright checks desktop and narrow layouts and the packed Host integration.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Use Versioned Logical Binding Identities
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
Continuous computation cannot depend on whether a previous cell happened to use an import, a persistent native lexical declaration, or a particular destructuring form. Reusing one native declaration environment couples source names to immutable storage choices. Renaming later occurrences alone also changes what existing closures observe, and recording every parsed declaration publishes names whose initializers never ran. Reinterpreting old journal source under repaired rules can silently change state even when recorded completions still match.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
PTC Plus compiles each new cell into a private native frame and retains session state through logical identities. `internal/repl-scope-normalizer.js` owns formal scope and activation analysis; `internal/stateful-root-compiler.js` owns root declaration and reference plans; `internal/stateful-root-runtime.js` owns their live values and sources. The existing kernel, queue, leases, cancellation, budgets and DSH authority remain authoritative. This partially supersedes the new-cell behavior in [ADR 0006](0006-rewrite-module-syntax-with-an-ast.md), [ADR 0012](0012-preserve-mixed-destructuring-initialization.md) and [ADR 0013](0013-isolate-repl-redeclaration-convenience.md). Their historical language behavior remains executable for recorded legacy cells.
|
|
10
|
+
|
|
11
|
+
The public `bindingUpdates` policy selects `stateful` or `protected`. Stateful declarations and assignments update the same identity within a scope and activation. Local `const` is writable under this selected dialect. `var` retains its function/root owner; blocks, parameter environments, catch, switch and iterations retain their own activation rules. Existing closures read the current identity; saving a function or object value preserves that value. A bare identifier declaration ensures existence without clearing an existing value. Each declarator publishes all candidate values only after its initializer and binding pattern complete. Separate declarators commit in order; ordinary assignments retain their actual partial-write effects. Hoisted values publish at scope instantiation, while unreached lexical declarations remain unproved.
|
|
12
|
+
|
|
13
|
+
Import aliases use the same identity with a live module source. An actual successful write changes only that alias to a local value; a later static import reinstalls a module source. Static dependencies link before body execution and retain Node resolution, attributes, caches and effects. Named default declarations associate `__default` with their logical identity until it is independently overwritten. Real modules expose a PTC-managed namespace with their actual export names, including `default`; the cell-only `__default` convention does not apply. `internal/stateful-module-compiler.js` is the common module entry for binding validation, candidates, activation, workbench sources and the user module graph. `code.run` executes the selected language in an independent state owner while preserving the native tool dispatch and authority boundary.
|
|
14
|
+
|
|
15
|
+
Protected mode retains native local declaration restrictions, rejects replacement of an existing root identity before execution, and protects newly created const/import bindings. It does not freeze identities already created writable or reset the worker. An explicit later stateful cell can revise earlier protected state. Generated helper identities are private implementation details and never become user catalog entries.
|
|
16
|
+
|
|
17
|
+
The shared TypeScript value normalizer handles enum, const enum and namespace declarations before value ownership, export descriptors and callable reflection are derived. Runtime enums retain their objects and member reads instead of depending on constant inlining across compilation units. Ambient and type-only declarations remain erased. Binding descriptors retain original source type-name evidence while consuming the same normalized runtime declarations as modules.
|
|
18
|
+
|
|
19
|
+
`compiler-intrinsics.js` captures emitted helper and runtime bookkeeping operations before an owned realm executes user code. Cell preparation connects recorded helper declarations to the existing root runtime; module preparation uses the module owner. CommonJS bootstrap dependencies are declared by the compiler-link owner. Conflicting source bindings and their declaration peers use the shared logical cells, including protected and post-transform legacy entries, while retaining native declaration validation, protection, initialization order and wrapper activation. Internal argument and metadata transport does not inherit user array species behavior. This ownership continues through runtime consumers: module graph records, logical roots, legacy dynamic adapters and their publication callbacks use captured collection and reflection operations. Source getters and callbacks retain their actual effects and identities.
|
|
20
|
+
|
|
21
|
+
`compiler-service.js` is the synchronous preparation boundary for cells, modules, dynamic source, workbench completion and binding-source metadata. The compiler and its maintained JavaScript dependencies execute in a private VM realm. This realm owns no user program values, module graph, journal or authority. `compiler-platform.js` captures text codecs, hashing, URL, source-map and VM operations before user execution, independently of later prototype or synchronized Node ESM export changes; `compiler-platform-factory.js` creates the dependency interfaces in the compiler realm. Compiler-local dependencies own path operations and bounded raw-DEFLATE compression. Compiler transforms disable external configuration discovery. `compiler-data.js` reconstructs request and result collections and packed source maps; diagnostics regain their caller-realm error classes. Original callable-source lookup is a synchronous string-only callback. Executable cell parsing removes TypeScript syntax with the pinned Amaro eraser whenever the host's experimental stripper refuses an input, so erasure and its source positions do not depend on the host Node release.
|
|
22
|
+
|
|
23
|
+
`module-compilation.js` returns code, source maps, export descriptions and static-link/source-registration facts. `stateful-module-compiler.js` consumes those facts and owns the existing public Node hooks, filesystem format selection and runtime registration. Compilation does not install hooks or reserve live module identities. Generated compiler bundles and maps must match their build sources; verification must account for private-realm execution against the original compiler files. Build and package procedures belong to [Publishing](../publishing.md). The VM and compiler work remain inside the existing worker budget and lifetime.
|
|
24
|
+
|
|
25
|
+
The private platform loads optional Amaro and TypeScript compilers only when a transform requires them, from fixed packaged artifacts into the same compiler realm. Each operation holds its dependency exports during use; weak caches between operations allow unused code and WASM data to be reclaimed. Dependency artifacts do not install global exports, so their lifetime is independent of the core compiler. Packaged code uses compact emission with original-source maps. The build extracts large dependency string arguments into content-addressed text assets, preserving their exact UTF-16 values and source mappings. The platform reads only entries in the generated asset manifest and does not cache asset contents; binary payload text need not remain pinned by script source or literal pools after consumption. Build validation derives required Node members from compiler-owner imports; platform tests exercise maintained dependencies' supported bootstrap, reclamation, lazy and fallback paths, including native type erasure. Disabled configuration-discovery paths do not grant compiler access to user files or configuration.
|
|
26
|
+
|
|
27
|
+
Runtime ownership also covers implicit native protocols. `compiler-data.js` serializes internal keys from own data fields with null prototypes, so inherited source `toJSON` hooks cannot select graph identity. `compiler-descriptors.js` supplies own-field descriptor construction, reads and writes, including internal Proxy descriptor transport to the service, platform and emitted realms; source reflection keeps native inherited-field semantics. `module-runtime-platform.js` captures the public hook registration/disposal lifecycle and URL operations for static, dynamic, require and CommonJS evidence adapters. `compiler-storage-source.js` supplies one emitted construction contract for internal weak collections, arrays and records, including captured iterator advancement, stack operations and private Promise observation. Runtime capture and standalone source reconstruction consume the same contract. `compiler-record-roles.js` proves generated plain-data record privacy from binding consumers, aliases and bound helper parameters before operation emission. Unknown calls, publication, own accessors or operations that can expose the record as a receiver deny the proof. Public decorator contexts, access objects and accessor values retain ordinary source-facing prototypes and property effects. Late dynamic environments derive ToObject and error operations from their captured realm. Compiler-service classifies compilation errors using its captured constructor before reconstructing the selected execution-realm error. Exception provenance uses the same captured bookkeeping contract and never replaces the thrown value. Root storage attaches declaration metadata to the entry it actually creates or updates; request overlays do not imply an owned logical entry.
|
|
28
|
+
|
|
29
|
+
`dynamic-scope-analysis.js` owns source-environment visibility, including parameter expressions and nested closures that cannot see function-body declarations. Native binding lookup, logical binding lookup and dynamic capture consume that same ancestry rule. [Semantic validation](../semantic-validation.md) separates preserved native behavior, intentional dialect contracts and generated boundary comparisons.
|
|
30
|
+
|
|
31
|
+
Hosts without native explicit resource management use the maintained Babel resource transform after logical declaration ownership is established. Babel owns lexical lifetime and abrupt-completion routing. `compiler-resource-lowering.js` consumes its context binding identities and emits acquisition GetMethod, original disposer invocation and Await in the existing source activation. Its context holds only private stack, error and suppression state; no source getter or disposer runs inside a bookkeeping helper. Per-acquisition temporaries retain the original value through method lookup before declaration initialization. Disposal preserves async-from-sync result suppression and await gaps without source-function wrappers or helper Promise layers. The shared lowering retains disposal lifetime, captured intrinsics, binding policy and source maps across cells, modules and reconstructed callable source; resource syntax never changes a protected lexical declaration into a writable root variable.
|
|
32
|
+
|
|
33
|
+
Module resource acquisition always uses an unexported lifetime binding, while exported values use the shared logical lexical cells. This separation also applies in protected mode: native engines can omit disposal for an exported using binding, and fallback transforms move declarations into a try region. Both paths retain readonly exports, TDZ and disposal at module completion.
|
|
34
|
+
|
|
35
|
+
New-generation cell returns leave source scopes through a private labelled break with an inert completion value. Resource disposal and finally execute with native non-exceptional completion; the worker's existing completion signal is delivered only after those scopes exit. Normal fallthrough cancels a return overridden by finally, while declaration-only bookkeeping retains expression completions. Execution preparation owns the native async-arrow frame for cells that suspend, including async resources anywhere in that activation. Kernel and workbench consume its explicitly private Promise through the shared captured observer; Node's cross-realm REPL continuation cannot acquire source then/constructor/species protocols. Source return values retain owning-realm Await and their original identities. Synchronous evaluation and historical exceptional return lowering remain on their existing paths; the legacy generation keeps native REPL declaration instantiation.
|
|
36
|
+
|
|
37
|
+
## Language and recovery generations
|
|
38
|
+
|
|
39
|
+
Journal version 9 requires the closed `languageSemantics` field: `legacy-v1`, `stateful-v1` or `protected-v1`. Versions 1 through 8 migrate to `legacy-v1`, preserving their independently recorded binding, rewrite, module, reuse and shadow policies. Replay selects the recorded compiler; live requests select the current configuration. Frozen `legacy-*` preparation, parser, module rewrite, convenience and catalog owners preserve historical semantics, including bare-declaration clearing and older import closures. A void completion does not justify substituting a newer compiler. This extends [ADR 0014](0014-persist-cell-rewrite-policy.md).
|
|
40
|
+
|
|
41
|
+
The post-transform native adapter records physical lexical declarations independently of logical declaration kind. The binding catalog preserves those facts across revisions and supplies them to migration, dynamic selection and publication. Only a successful current storage probe proves initialization; static declaration facts cannot establish recoverability. An unrelated same-named global property cannot override proven initialized lexical storage or confer writability on absent or readonly storage.
|
|
42
|
+
|
|
43
|
+
Binding snapshots likewise distinguish `amaro@1.1.11` from `stateful-module-v1+amaro@1.1.11`. Source metadata and fingerprints remain canonical; an unsupported transform cannot be silently replaced with the current transform. A generation change participates in module reuse identity. Actual worker initialization and source facts advance the catalog; parsing a declaration alone never proves that its value exists. Model-visible provenance, verified journal ancestry and the existing recovery contraction rule still bound every retained identity.
|
|
44
|
+
|
|
45
|
+
Current protected module graphs use the distinct `protected-module-v1+amaro@1.1.11` compilation identity. They retain native declaration and import protection while sharing callable source facts, dynamic interfaces and module loading with current computations. This live module identity does not add a persisted binding snapshot transform or reinterpret historical modules.
|
|
46
|
+
|
|
47
|
+
The catalog retains evidence of native legacy declarations separately from provider and logical-root sources. Migration uses that evidence and actual available storage to bridge writable native lexical, var and function bindings. Scalar and whole-pattern publication use the same storage operation, preserving old read/write closures; readonly captures retain their original storage. Ambient properties and provider activation do not become native declaration evidence merely because a property exists.
|
|
48
|
+
|
|
49
|
+
Newly prepared legacy cells also bridge catalog-proved logical roots through their existing native dynamic environment. Initialized native lexical storage retains precedence; names without that storage use their established logical identity. Existing native closures and saved intrinsic interfaces keep their captured environments. Native declarations replacing import aliases or restoring deleted names publish after actual initialization is proved, and legacy settlement reconciles logical source and write facts even after failure. Switching policy therefore does not create a disconnected store for names established only in the logical root.
|
|
50
|
+
|
|
51
|
+
Configuration migration runs before a schema default can erase whether `bindingUpdates` was omitted. A complete enabled old policy maps to stateful, a complete protected policy maps to protected, and mixed old choices retain explicit compatibility state. The settings card uses the same resolver and exposes old controls only for that compatibility state. Selecting a unified policy atomically records it and clears its compatibility marker. Stable model guidance derives from the same resolved language generation; changing session values never changes the prompt prefix.
|
|
52
|
+
|
|
53
|
+
## Dynamic compilation environments
|
|
54
|
+
|
|
55
|
+
User-selected eval and Function compilation retain their native syntax goals, completion and effect behavior. The logical environment adapter must preserve direct eval's caller scope, indirect eval's realm root, Function constructors' owning realm and `with` object lookup. `dynamic-scope-analysis.js` owns the shared source facts for declaration ownership, initializer lookup and intervening eval activations; normalization and compilation consume those facts. `dynamic-environment-compiler.js` and `dynamic-environment-runtime.js` own the actual environments; entry integration and `dynamic-binding-evidence.js` connect executed declarations to the cell's source and catalog. Native syntax does not exempt logical environment continuity, and generated names must not become a new reason for valid source to fail.
|
|
56
|
+
|
|
57
|
+
Eval checks lexical conflicts before initializing its ordered declaration plan. Global function declarations also retain native property eligibility checks before source initializers or body execution. A later global declaration failure preserves earlier declarations already established by Node's global environment; it does not execute the remaining body or erase those established values. Local and logical declarations retain their own environment rules.
|
|
58
|
+
|
|
59
|
+
`dynamic-native-runtime.js` owns stable interfaces for exactly six native intrinsics: eval, the four Function constructors, and Function.prototype.toString. Owned entry points prepare these interfaces before source can capture them. `dynamic-native-calls.js` selects the owning realm and generation's interface at source value and invocation boundaries, including property reads, reflected native results and asynchronous resumption. Cells that only save an intrinsic value use the same boundary. Native and opaque consumers of these values therefore use the same interface as compiled calls. Direct eval recognizes the exposed identity but executes the captured original intrinsic with its explicit caller environment. Constructors delegate coercion, validation and newTarget prototype selection to their original native operation before adapting the resulting source. Canonical user functions and module namespaces are never wrapped by this interface. Separately created user realms retain their own intrinsics.
|
|
60
|
+
|
|
61
|
+
All global and prototype properties retain their original native identities, including Function.prototype.toString. Replacing global eval would invalidate native direct eval in escaped legacy closures; replacing toString would invalidate their saved intrinsic identity observations. Temporary restoration around synchronous calls cannot preserve asynchronous continuations. Kernel ambient getters return the original values while retaining volatile-read accounting. `native-root-compilation.js` adapts legacy cells after frozen declaration lowering; `native-root-dynamic.js` supplies their stable legacy interface and retains helper ancestry through the existing import namespace owner. Legacy dynamic source uses the native REPL environment; new-generation source uses its logical root. Both consume managed module namespaces. Legacy compilation units keep their native declaration behavior, environments and saved interface identities before, during and after new-generation execution, including newly created legacy cells and modules after a mode change. Interface preparation never writes user-visible properties. Module runtime creation shares interfaces, intrinsic identity associations and source facts. Invocation context carries the selected runtime and source provenance through native and asynchronous callbacks; callbacks outside such an invocation use their interface's realm owner. Interfaces remain live while escaped values can use them and end with the owning realm.
|
|
62
|
+
|
|
63
|
+
For legacy top-level await, `native-await-declarations.js` retains source declarations for Node's persistent root instantiation and function hoisting. Direct eval uses a separate cell activation inside that root and outside nested lexical scopes. Initializer writes retain native ordering, including partial destructuring writes on failure; indirect eval and Function continue to use the native realm root.
|
|
64
|
+
|
|
65
|
+
Uncompiled VM source that independently fetches a native intrinsic retains its native engine behavior, including physical source reflection. An opaque observer supplied with the new-generation toString interface observes the compiler-proved original source. The adapter does not rewrite arbitrary returned strings, replace native prototype properties, or wrap ordinary user functions to simulate a different engine result.
|
|
66
|
+
|
|
67
|
+
Ordinary source invocations execute through a captured native continuation in a compiler-owned realm. Its fixed eval body enters the invocation context, calls the original target through native reflection, and restores the context in `finally`. Native eval adds no callable activation between source caller and callee. This preserves native caller observations and cleans up exceptions thrown in parameter initializers, including when opaque code catches them. A private invocation record passes synchronously from source argument preparation to that continuation; it is consumed before any user code runs. Source parameters, declaration environments, function identities and user-visible realm properties remain intact. Tagged templates retain their native cached template object and evaluate substitutions before invocation validation.
|
|
68
|
+
|
|
69
|
+
Member reads remain native expressions in the source caller. Preparation saves the receiver as its first argument and reads the member in its second, clearing its compiler temporary before any key or getter executes. Reentrant and suspended reads therefore retain the original receiver without retaining the last object in compiler storage. Object and primitive accessors, private members, Proxy traps and nullish errors keep their native property semantics. Internal argument lists and binding-descriptor transport use indexed compiler-owned operations instead of user array methods, species or iterators.
|
|
70
|
+
|
|
71
|
+
`callable-source-facts.js` carries compiler-owned source marks through normalization and associates exact emitted callable text with original JavaScript source. The class owns its constructor's source identity. Type erasure preserves source text where possible; TypeScript runtime constructs require equivalent JavaScript initialization in the reflected source. Owned realms share these facts so module observers can receive canonical function values from another owned realm. Native toString validates its receiver first and consults those facts without invoking user code. A function reconstructed from that source retains its actual source dependencies; hidden compiler helpers do not become dependencies and unavailable user closure bindings do not become available.
|
|
72
|
+
|
|
73
|
+
Decorator source reflection reuses the shared language lowerer. All callable identities and their enclosing private syntax contexts are recorded before reflected products are lowered. `callable-definition-environment.js` identifies parameters and computed keys that evaluate outside a callable's body scope, and emits definition frames that preserve suspension and lexical context. The normalizer establishes class names and per-evaluation initializer state before lowering; computed object-property names carry their natively coerced key into that evaluation without a shared cache. Reflection assigns each body the transitive helper dependencies proved by lexical bindings. Exact nested source aliases preserve this ownership through repeated reconstruction. Class fragments with contextual `await` or `yield` retain their native compilation-context requirement. Temporary expression containers supply that grammar without acquiring logical declaration identities, and are removed before emission. Execution retains native static blocks and source callable activations.
|
|
74
|
+
|
|
75
|
+
Compiler source regions divide static native-call expansion and module emission by parser-proved output slots. A region introduces no runtime scope, activation or completion boundary. Complete declaration ownership remains with the shared scope facts; a partial grammar wrapper cannot establish lexical binding ownership. Lexical dynamic compilation retains its complete scope graph. Normalization releases declaration trees before materializing its numeric output pieces, and copied source ranges compose directly with their known mapping segments. Private module names use the shared allocator's compact, collision-safe representation; semantic consumers use explicit binding descriptors.
|
|
76
|
+
|
|
77
|
+
Source maps use packed numeric runs for large outputs. Emitters' raw mapping points stream directly into those runs when available; encoded maps remain supported for other maintained transforms. Composition reuses known segments and endpoint lookups while retaining unmapped spans and EOF anchors.
|
|
78
|
+
|
|
79
|
+
Native-interface adaptation consumes the normalizer's transport descriptors: private cell objects and operations remain internal, while their value properties retain user-value adaptation. A successful native-JavaScript parse permits reusing that proof through normalization and retaining the native generator's output when no resource lowering is needed; other inputs retain maintained TypeScript lowering. Module callable catalogs store shared compressed UTF-16 source blocks and ranges. Digests select candidates only; exact source comparison proves reflection identity without retaining overlapping whole-callable strings.
|
|
80
|
+
|
|
81
|
+
`compiler-operations.js` indexes logical binding descriptors once per compilation and owns a separate operation plan for each AST region. Immutable descriptor indexes are shared; newly generated binding names, operation shells and explicit source-value boundaries belong to the region. Static and dynamic native-call adaptation consume the same planner. An internal operation's arguments can still contain source expressions that require adaptation; owning the operation never implies skipping those source subtrees. Generated helper calls and values do not recursively acquire source-call instrumentation.
|
|
82
|
+
|
|
83
|
+
Dynamic frames expose one binding lookup/update contract. Logical roots implement that contract as a live view over continuous identities; map-backed activations use captured native operations through a stable adapter. Consumers preserve frame identity and do not assume every binding store is a native Map.
|
|
84
|
+
|
|
85
|
+
The final module emitter may erase comments. Exact source-map segments relate emitted callable starts to their marked input owners; preparation restores each proven mark inside its final native source span before registering source facts. Identical emitted function bodies therefore retain distinct original sources. The final position map includes these insertions and CommonJS wrapper extraction. Callable catalogs cross the compiler boundary as data; the module runtime entry registers them before Node can expose hoisted exports to cyclic importers. Catalog registration does not add executable text or companion modules to the source.
|
|
86
|
+
|
|
87
|
+
## Managed module interface
|
|
88
|
+
|
|
89
|
+
PTC owns a stable managed namespace for each compiled module identity. Reading a member resolves the current logical source and returns its canonical value. An import remains a live source until an actual successful write selects a local value. Saving a function or object saves that original value; passing a namespace to opaque code preserves its live member reads. Neither operation substitutes a forwarding function for the source value.
|
|
90
|
+
|
|
91
|
+
Native exports may carry hoisted binding accessors as transport. Compiler and loader facts identify accessor, native, forwarding and namespace exports; ordinary function names or shapes are never transport evidence. Every PTC-owned import entry consumes this same interface, including static and dynamic imports, provided REPL require, compiled CommonJS require, bindings, candidates, the workbench and isolated children. Each compilation unit owns its import operation even when import syntax occurs only in runtime-created source; eval and Function retain that unit's resolution base. Node continues to own actual resolution, attributes, URL identity, native missing/ambiguous export checks, caches, top-level await and one evaluation. Accessors must be available at module instantiation, preserve lexical TDZ and retain early function identity without executing user initializers.
|
|
92
|
+
|
|
93
|
+
Session transforms apply only to session-owned module sources. Files resolved beneath `node_modules` remain under Node's native compiler and loader ownership; PTC wraps their result at the managed namespace boundary when a PTC entry consumes it. External package code therefore keeps native evaluation and identity while PTC-owned callers still receive the documented live namespace behavior.
|
|
94
|
+
|
|
95
|
+
CommonJS compilation retains original JavaScript export evidence separately from executable lowering. At actual module loading, an unreachable reexport points to a private source companion for Node's own CommonJS analysis. Literal require arguments in that analysis source preserve their raw spelling and receive private routing prefixes; detected reexports resolve through the complete public hook chain from the original CommonJS parent. The private resolution request ends before loading, and the original linker performs subsequent loading and value extraction. No companion body executes, no alternative export detector is maintained, and pure compilation allocates no native module identities. Prepared CommonJS modules carry the evidence through their loader metadata; source changes before import replace the current companion association. Named-export snapshots, default values and actual execution retain native CommonJS behavior.
|
|
96
|
+
|
|
97
|
+
The managed namespace provides stable membership, readonly properties and live values; it does not claim Node's native module namespace brand or identity. Opaque code independently calling native import on a compiled raw URL, or acquiring a native require through createRequire, uses a separate native interface and can observe the transport representation. This boundary does not exempt any PTC-owned entry, any ordinary exported value, or a managed namespace supplied to an opaque function. Native-only providers keep their native namespace when no adaptation is needed.
|
|
98
|
+
|
|
99
|
+
Namespace reflection, `then` exports, cycles, re-export chains and builtin updates before local override are acceptance conditions. The managed module entry and worker suites exercise the `node:fs` / `syncBuiltinESMExports` regression through actual imports and opaque namespace consumers. Private import adapters reuse native module identities while preserving each call's native option validation, source resolution and thenable assimilation; repeated imports do not allocate a new native module per call.
|
|
100
|
+
|
|
101
|
+
## Alternatives considered
|
|
102
|
+
|
|
103
|
+
**Mirror values into native export slots.** An uncompiled provider has no general publication callback. After syncBuiltinESMExports changes a builtin export, a local mirror can be stale before any PTC code executes. Direct native forwarding follows that update but cannot later switch to a local override. Native namespace transparency therefore cannot own this dialect's writable-export semantics using current public Node APIs.
|
|
104
|
+
|
|
105
|
+
**Continue adding exceptions around the shared native REPL.** Fixes for import-to-function, lexical-to-import, failed patterns and nested var would each need separate storage rules. Independent frames remove the representation conflict at its owner.
|
|
106
|
+
|
|
107
|
+
**Rename each declaration to a fresh native variable.** This removes parser collisions but makes old closures observe old values and can publish half-initialized patterns. Formal logical identities and candidate publication express those contracts directly.
|
|
108
|
+
|
|
109
|
+
**Make every write transactional.** Ordinary JavaScript assignments can perform effects before throwing. Rolling those back would misrepresent executed computation; only the declared candidate publication boundary is atomic.
|
|
110
|
+
|
|
111
|
+
**Reset all state when policy changes.** This discards valid, model-known work and makes a presentation or configuration change an implicit state operation. Versioned code and identity access preserve existing values without reinterpreting historical closures.
|
|
112
|
+
|
|
113
|
+
**Replay every history with the latest compiler.** Matching visible results cannot establish hidden state equivalence. Frozen generations retain the evidence contract; unsupported historical evidence contracts the frontier instead of blocking the current valid cell.
|
|
114
|
+
|
|
115
|
+
**Replace Node with an independent interpreter.** A separate interpreter could own more compilation surfaces, but would duplicate Node module, object and capability behavior. PTC retains Node execution and adapts the compilation units it owns; user-selected external engines keep their native results.
|
|
116
|
+
|
|
117
|
+
## Consequences
|
|
118
|
+
|
|
119
|
+
The dialect deliberately changes declaration and const-update behavior; it is not a claim of complete ECMAScript equivalence. AST-derived source mappings, candidate state, runtime publication facts and versioned history are maintained together. New syntax must enter the shared owners rather than acquire entry-specific exceptions. Tests discriminate values, closures, effects, scope lifetime and replay behavior, including failures with void completions. DSH tool names, arguments, approval, sandboxing, canonical values and model-visible state boundaries remain unchanged.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Persist the delivery verdict against the verified candidate
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
A complete-tree independent review returns a sampled conclusion for one tree state. Nothing bound that conclusion to the candidate, so the same source bytes could be reported clean by one review and expose three reproducible defects to the next, and a clean verdict never stopped the following review from starting. The verification proof already bound `npm run check` to `{HEAD, fingerprint}`, but the review stayed a transcript outside the commit gate, and the gate allowed a commit whenever no proof, no ledger or only a historical clean review existed.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
The delivery gate is a persisted verdict in checkout-local Git metadata (`review-findings/verdict`) holding `{schema, status, head, fingerprint, base, evidence}`. `status` is `clean`; `head` and `fingerprint` are the frozen candidate the independent complete review examined; `base` is the scope base commit and must be an ancestor of `HEAD`; `evidence` names the retained copy of the original review report and its SHA-256. `recordReviewVerdict` is the only writer: it requires both the `HEAD` and the fingerprint captured before the review, refuses a candidate where either changed, refuses any report whose last non-empty line is not `VERDICT: NO FINDINGS`, and copies the report into `review-findings/evidence` before hashing it. A report that is not clean retires the standing proof and verdict instead of leaving an earlier clean verdict in force. `preCommitReviewLedger` is fail-closed and requires a proof for the current `HEAD`, equal source and index fingerprints, and a clean verdict whose head, fingerprint and scope cover that candidate and whose evidence still hashes to the recorded value. An absent ledger, an absent proof or documentation-only work is not an exemption. A proof whose `HEAD` moved is retired together with its verdict, and a verdict that no longer matches the verified candidate is discarded without touching the proof; either case blocks the commit. The recording entry resolves the scope base to a full commit id, binds the current `HEAD`, and refuses a base that is not an ancestor of it, so a stored record cannot name a candidate other than the one under review. The bound values come from the `verified <head> <fingerprint>` line that `npm run check` prints and stores in the proof, and the independent report must stay outside the checkout or be ignored because the fingerprint covers untracked non-ignored paths.
|
|
10
|
+
|
|
11
|
+
## Alternatives considered
|
|
12
|
+
|
|
13
|
+
**Keep the review as an unpinned transcript.** Recording only that a clean review happened leaves the gate bypassable by editing after the review, by reusing a verdict for another candidate, or by deleting the metadata; the observed clean-then-three-defects sequence is exactly the failure it hides.
|
|
14
|
+
|
|
15
|
+
**Let the gate pass when no proof or ledger exists.** This preserves the previous behaviour for small or documentation-only changes, but it also preserves the bypass: removing or archiving the metadata becomes a way to commit without any review.
|
|
16
|
+
|
|
17
|
+
**Store the verdict inside the verification proof.** A single file is simpler, but `npm run check` runs before the review and must not require a verdict to exist; the two records have different producers and lifetimes, so the proof gains a verdict-identity check instead of the verdict's storage.
|
|
18
|
+
|
|
19
|
+
## Consequences
|
|
20
|
+
|
|
21
|
+
A clean verdict now binds one candidate and one report hash, so editing after the review invalidates the commit instead of silently passing. The cost is that every commit, including a documentation-only slice, needs a fresh `npm run check` and one complete independent review; the alternative is a gate that any metadata deletion bypasses. The hook cannot judge review quality: it proves that a clean report for these exact bytes was recorded, not that the review was complete, which is why the coverage matrix and the `VERDICT: INCOMPLETE` outcome live in AGENTS.md as review obligations.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Run the session kernel in a killable helper process
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
The session kernel previously executed user cells in a `node:worker_threads` worker owned by the DSH process. Reclaiming a stuck or unresponsive kernel required `worker.terminate()` inside that process; on Node 24 and 26 that teardown can reach an upstream cppgc finalizer defect (`node::contextify::ContextifyScript` or `ContextifyContext`) that writes through a destroyed `node::Realm`. Because the worker lived in the DSH process, the fault ended the whole process. The available Windows reproductions and stacks identify the defect on Node 24 and 26; the fix below does not depend on proving which other Node lines can reach it, so reclamation cannot rely on downgrading Node, retrying CI, or claiming that an in-host kill is safe.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
The session kernel runs inside a plugin-owned helper process that the host may kill without killing DSH. `internal/kernel-child.js` receives one `init` message over process IPC, creates the existing kernel worker with the supplied `workerData` and `resourceLimits`, keeps that worker's private `MessagePort` inside the helper, and relays only plain data in both directions. Kernel output reaches the host through the helper's own platform stdio pipes, so no output byte is duplicated into IPC messages or counted by two owners.
|
|
10
|
+
|
|
11
|
+
`internal/isolated-worker.js` exposes the worker-shaped transport that existing callers already use: `message`, `error`, `exit`, `close`, `stdout`, `stderr`, `postMessage` to the worker's parent port, and `terminate()`. The handshake exposes a process-local port adapter for the private kernel channel; the real `MessagePort` never crosses the process boundary. The transport records the real child exit immediately for stop deadlines, then publishes its worker-shaped `exit` event only after child `close` proves the platform output pipes drained; outward `exit` still precedes outward `close`. Every close has cutoffs anchored at its start: a release grace, a kill of the helper process, then a positive window for the real child exit. A shutdown acknowledgement proves that the helper released its own machinery, but it does not reclaim the instance.
|
|
12
|
+
|
|
13
|
+
`internal/isolated-worker.js` also owns every started transport until its real exit, cleanup, and output close. The kernel client, the workbench console, and the candidate runner each create one owner. `releaseAll()` frees current instances while the owner remains usable for reconfiguration; `dispose()` is terminal and refuses new starts. Reclamation failures remain visible to every aggregation and to later disposal instead of becoming unhandled rejections.
|
|
14
|
+
|
|
15
|
+
Caller and helper environment boundaries are explicit. Each creation site projects the host environment with `normalizeWorkerEnvironment`, which keeps the user-visible contract and strips host-only instrumentation such as `NODE_V8_COVERAGE`. `IsolatedWorker` snapshots that projected object before `child_process.fork`; the fork call may mutate its own env object by copying `NODE_V8_COVERAGE` into the helper, so the pre-fork snapshot is what `kernel-child.js` passes to `new Worker({ env: ... })`. The helper itself may retain the coverage variable, but the inner user Worker and the child processes it creates receive only the projected environment. An explicit transport/coverage test can opt into instrumentation to verify worker branches; production paths do not change the user environment to collect coverage.
|
|
16
|
+
|
|
17
|
+
The helper answers an on-demand utilization-sample request from its own loop, and `IsolatedWorker` exposes `sampleUtilization()`. `SessionCellExecutor` starts the wall bound at request submission, awaits that fresh baseline, then starts the compute interval before it posts the prepare or run frame, so work completed before submission is excluded while prepare blocking remains counted. The exception owned by [ADR 0024](0024-repl-console-observation.md) is a confirmed unfinished observation on the same worker: the executor may post prepare before starting budgets so the observation can finish, and the matching observation-completion/readiness acknowledgement starts them. A sample that times out, cannot be sent, disconnects, or exits is a bounded worker-exit failure: pending requests are cleared and no prepare/run frame is dispatched. The ready handshake still contributes an additional startup sample, but it is not the per-cell baseline and it does not reset or defer budgets.
|
|
18
|
+
|
|
19
|
+
## Alternatives considered
|
|
20
|
+
|
|
21
|
+
**Keep the worker in the DSH process and only improve cooperative shutdown.** Asking user code to release its own realms cannot bound a wedged kernel, and any fallback in-host kill reaches the same upstream teardown path that ends DSH.
|
|
22
|
+
|
|
23
|
+
**Kill the in-host worker through an inspector or another thread-level mechanism.** Those paths still tear down the same isolate and cppgc graph inside the DSH process, so they do not remove the host-fatal fault.
|
|
24
|
+
|
|
25
|
+
**Create a child process per cell instead of one long-lived helper per session.** This gives up continuous bindings and the existing private-channel protocol, and it turns every cell into a new process-startup and environment-reconstruction problem. It also expands the change into scheduling and process governance that DSH owns.
|
|
26
|
+
|
|
27
|
+
**Downgrade Node or retry the native crash in CI.** This leaves installed users on supported Node lines exposed and treats a reproducible host crash as a test flake.
|
|
28
|
+
|
|
29
|
+
**Move all user computation into a general child-process sandbox or permission layer.** That would replace DSH authority and sandbox decisions with a second system, which the project explicitly does not own.
|
|
30
|
+
|
|
31
|
+
## Consequences
|
|
32
|
+
|
|
33
|
+
A synchronously stuck kernel is reclaimed by killing a plugin-owned helper process; DSH survives and observes a real child exit, output close, and cleanup outcome. This containment removes the plugin's in-host kill path, but it does not repair, characterize, or claim the same cause for every upstream Node teardown path.
|
|
34
|
+
|
|
35
|
+
The transport gains one process and an IPC hop. Ports, shared memory, and transferred buffers cannot cross the process boundary, so they stay inside the helper or become plain data; output continues over the helper's stdio pipes. The helper's own `execArgv` is emptied so it does not inherit host startup arguments.
|
|
36
|
+
|
|
37
|
+
Environment provenance is now explicit in code: the user Worker receives the caller's projected environment, while coverage instrumentation stays on the helper side except in the explicit transport test. A reclamation refusal stays owned and observable instead of being reported as success or surfacing as an unhandled rejection.
|