dsh-ptc-plus 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.en.md +3 -1
  2. package/README.md +3 -1
  3. package/client.cjs +165 -29
  4. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +4 -2
  5. package/docs/adr/0019-plugin-settings-and-kill-switch.md +2 -2
  6. package/docs/adr/0023-global-user-bindings.md +12 -0
  7. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +1 -1
  8. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +2 -2
  9. package/docs/adr/0044-coexist-with-the-official-persistent-repl.md +4 -4
  10. package/docs/adr/0046-gate-the-commit-with-the-semantic-evidence-tier.md +1 -1
  11. package/docs/adr/0048-supersede-a-rewritten-cell.md +48 -0
  12. package/docs/adr/0049-prove-a-settlement-against-its-published-record.md +53 -0
  13. package/docs/adr/0050-read-host-surfaces-through-their-published-contract.md +48 -0
  14. package/docs/adr/README.md +7 -1
  15. package/docs/architecture.md +1 -1
  16. package/docs/durability-design.md +9 -2
  17. package/docs/installation.md +9 -9
  18. package/docs/releases/v0.5.2.md +26 -14
  19. package/docs/releases/v0.5.3.md +77 -0
  20. package/docs/runtime-reference.md +7 -1
  21. package/docs/user-bindings.en.md +15 -0
  22. package/docs/user-bindings.md +15 -0
  23. package/docs/v0.5.3-goals.md +117 -0
  24. package/docs/verification.md +4 -4
  25. package/index.js +67 -19
  26. package/internal/binding-adoption.js +136 -0
  27. package/internal/capability-command-receipt.js +18 -4
  28. package/internal/capability-delivery-proof.js +70 -0
  29. package/internal/capability-environment.js +38 -1
  30. package/internal/config-spec.js +14 -5
  31. package/internal/cordis-tools-owner.js +2 -1
  32. package/internal/direct-surface-owner.js +27 -13
  33. package/internal/edit-transport-owner.js +6 -5
  34. package/internal/execution-provider-owner.js +6 -0
  35. package/internal/execution-seam-compat.js +25 -0
  36. package/internal/host-tool-sdk.js +308 -0
  37. package/internal/official-repl-priority.js +31 -10
  38. package/internal/program-bindings.js +12 -1
  39. package/internal/repl-memory-projection.js +21 -2
  40. package/internal/runtime-bridge-owner.js +113 -8
  41. package/internal/runtime-messages.js +5 -0
  42. package/internal/session-journal-recovery.js +210 -32
  43. package/internal/session-journal-schema.js +3 -0
  44. package/internal/session-journal.js +46 -9
  45. package/internal/session-log-view.js +22 -2
  46. package/internal/session-runtime.js +106 -39
  47. package/internal/user-bindings-owner.js +109 -6
  48. package/package.json +15 -7
  49. package/src/client-capabilities-view.js +45 -3
  50. package/src/client-copy.js +24 -0
  51. package/src/client-indicator-view.js +2 -1
  52. package/src/client-repl-view.js +35 -16
  53. package/src/client.js +3 -1
@@ -163,7 +163,7 @@ escalation it thinks it is offering.
163
163
  ## Consequences
164
164
 
165
165
  The default bundle and current official base profile use the `ptcRuntime`
166
- resolve/run shape, also exercised by the frozen Host graphs. Explicit
166
+ resolve/run shape, also exercised by the installed channel graphs. Explicit
167
167
  `codeRuntime` request-only compositions retain public Loader fixture coverage
168
168
  for request identity, replacement and bundle removal. The evidence boundary is
169
169
  recorded in [Installation](../installation.md#compatibility-evidence).
@@ -12,7 +12,7 @@ PTC Plus publishes its Remote services through DSH's TYPERT registry, and that r
12
12
 
13
13
  Each generation validates only the member it owns and ignores the other, so one published contract serves whichever shape the installed host implements. Compatibility follows the live acceptance behavior of the registry rather than a release version, and the decoder is published once, so a host that reads both members cannot obtain two different decoders for one wire member.
14
14
 
15
- CI registers the packed contract with the installed host's own registry from the frozen official `latest` and `next` graphs (`scripts/dsh-rpc-contract-smoke.mjs`). Both frozen graphs read `create()`; they do not exercise a schema-only Host. Unit fixtures cover `schema.parse`, not a complete historical Host installation. The evidence boundary is [Installation: Compatibility Evidence](../installation.md#compatibility-evidence); passing two graphs must not be reported as packaging verification of both codec shapes.
15
+ CI registers the packed contract with the installed host's own registry from the blocking host channels it resolves on demand from `compat/channels.json` (`scripts/dsh-rpc-contract-smoke.mjs`). Both installed channels read `create()`; they do not exercise a schema-only Host. Unit fixtures cover `schema.parse`, not a complete historical Host installation. The evidence boundary is [Installation: Compatibility Evidence](../installation.md#compatibility-evidence); passing two graphs must not be reported as packaging verification of both codec shapes.
16
16
 
17
17
  ## Alternatives Considered
18
18
 
@@ -24,4 +24,4 @@ CI registers the packed contract with the installed host's own registry from the
24
24
 
25
25
  ## Consequences
26
26
 
27
- One plugin build carries both the historical schema-based and factory-based codec shapes, and the compatibility rule lives in a single module with the decoder published once. A future generation that requires another member, or that rejects a codec carrying a member it does not know, needs this owner updated; the regression test is CI's registration against the installed registry, which fails on the frozen Host graph that changed instead of on the user's machine. The decoder keeps the project's maintained schema runtime, so a host that validates payloads itself still evaluates the same semantics the plugin's own tests exercise through both members.
27
+ One plugin build carries both the historical schema-based and factory-based codec shapes, and the compatibility rule lives in a single module with the decoder published once. A future generation that requires another member, or that rejects a codec carrying a member it does not know, needs this owner updated; the regression test is CI's registration against the installed registry, which fails on the installed channel host graph that changed instead of on the user's machine. The decoder keeps the project's maintained schema runtime, so a host that validates payloads itself still evaluates the same semantics the plugin's own tests exercise through both members.
@@ -6,15 +6,15 @@ DSH added an opt-in official Node execution mode (`executionMode: persistent-in-
6
6
 
7
7
  ## Decision
8
8
 
9
- `internal/execution-seam-compat.js` and `internal/execution-provider-owner.js` forward `programFormat` to the live original generation and `release(owner)` to the originals that served that owner. These describe the provider that actually performs an execution: owner-keyed retained environments and the executed source format belong to the original, which still serves non-session consumers while the plugin owns session cells, and a bounded generation that lacks the members falls back to `'function-body'` and a resolved no-op. Execution retirement does not retire cleanup: the composition retains weak owner-to-original provenance for dispatched `stateOwner` programs, so Agent cleanup through a captured adapter still reaches a surviving original after bundle removal or source replacement. Successful cleanup removes that original from the owner's pending set; failed cleanup remains available for explicit retry, with synchronous and asynchronous failures observable. An owner without recorded dispatch delegates to the live original, or receives a resolved no-op if execution is unavailable. `executionInstructions`, `sandboxMode` and `timeout` keep their existing withholding policy while PTC Plus owns session execution, because those describe the session kernel ([ADR 0028](0028-attach-to-the-host-ptc-execution-seam.md)).
9
+ `internal/execution-seam-compat.js` and `internal/execution-provider-owner.js` forward `programFormat` and the `retainsState` declaration to the live original generation and `release(owner)` to the originals that served that owner. The composed provider answers `retainsState` as the fact about itself: while an installed adapter runs the program it retains a per-session environment, so it reports `true` whatever the original would do; `seam.originalRetainsState` carries the original declaration for the ownership decision. These describe the provider that actually performs an execution: owner-keyed retained environments and the executed source format belong to the original, which still serves non-session consumers while the plugin owns session cells, and a bounded generation that lacks the members falls back to `'function-body'` and a resolved no-op. Execution retirement does not retire cleanup: the composition retains weak owner-to-original provenance for dispatched `stateOwner` programs, so Agent cleanup through a captured adapter still reaches a surviving original after bundle removal or source replacement. Successful cleanup removes that original from the owner's pending set; failed cleanup remains available for explicit retry, with synchronous and asynchronous failures observable. An owner without recorded dispatch delegates to the live original, or receives a resolved no-op if execution is unavailable. `executionInstructions`, `sandboxMode` and `timeout` keep their existing withholding policy while PTC Plus owns session execution, because those describe the session kernel ([ADR 0028](0028-attach-to-the-host-ptc-execution-seam.md)).
10
10
 
11
- `officialReplPriority` (default `false`, State and recovery) selects who owns PTC session execution when the official persistent REPL is live. The decision reads the live capability — `provider.programFormat === 'repl'` — rather than a release number, and is re-evaluated on every configuration and host source change. Execution ownership is an attachment shape: `owned` installs the session runtime, `deferred` installs `internal/official-repl-priority.js`'s runtime record, which owns no execution resources and answers the attachment contract with empty contexts. Changing the shape retires the current attachment, disposing its live kernels, and installs the other shape before later cells; no committed cell mixes the two contracts. While deferred, `run_code` and its descriptors stay with the official provider and the plugin's REPL guidance, session-state semantics, recovery, editor tool, capability SDK, REPL observation and binding workbench are suspended with the attachment, while the settings card, enablement and stored global bindings stay available so the switch can be reversed.
11
+ `officialReplPriority` (default `false`, State and recovery) selects who owns PTC session execution when the official provider retains a program environment per state owner. The decision reads the live capability — the provider's `retainsState` declaration, or the retained source form (`programFormat === 'repl'`) on a generation that predates that declaration — rather than a release number, and is re-evaluated on every configuration and host source change. Retention, not the source form, answers the question: one host line retains state for `script` programs and another runs the same form in a fresh process. Execution ownership is an attachment shape: `owned` installs the session runtime, `deferred` installs `internal/official-repl-priority.js`'s runtime record, which owns no execution resources and answers the attachment contract with empty contexts. Changing the shape retires the current attachment, disposing its live kernels, and installs the other shape before later cells; no committed cell mixes the two contracts. While deferred, `run_code` and its descriptors stay with the official provider and the plugin's REPL guidance, session-state semantics, recovery, editor tool, capability SDK, REPL observation and binding workbench are suspended with the attachment, while the settings card, enablement and stored global bindings stay available so the switch can be reversed.
12
12
 
13
13
  Cells executed under the official mode produce no plugin journal records. They are an unrecorded execution interval: recovery never replays them, and they do not extend plugin state as recovery evidence.
14
14
 
15
15
  ## Alternatives considered
16
16
 
17
- **Gate on the DSH release or on reading the original row's `executionMode` configuration.** The mode is a live runtime capability and a configuration fact outside the plugin's public contract; `programFormat` is the provider-owned proof that a REPL actually retains state, and it degrades safely on older generations that lack it.
17
+ **Gate on the DSH release or on reading the original row's `executionMode` configuration.** The mode is a live runtime capability and a configuration fact outside the plugin's public contract; the provider's own retention declaration is proof that its environment survives between runs, and on older generations that lack the declaration the retained source form is the only evidence they publish.
18
18
 
19
19
  **Keep owning execution unconditionally and only add the missing seam members.** That repairs disposal, but it silently ignores an explicit official mode, cannot honor a user's choice to run the official semantics, and keeps two competing REPL contracts alive for the same session surface.
20
20
 
@@ -26,7 +26,7 @@ Cells executed under the official mode produce no plugin journal records. They a
26
26
 
27
27
  ## Consequences
28
28
 
29
- Agent disposal on the current official host releases cleanly, and both the format and the owner release reach the generation that owns the environment.
29
+ Agent disposal on the current official host releases cleanly, and the source form, the retention declaration and the owner release reach the generation that owns the environment.
30
30
 
31
31
  Leaving plugin ownership disposes its live kernels, and plugin-mode state contraction follows the existing disposal rules. Official in-memory state belongs to DSH's Agent/provider lifecycle and may remain available when priority is switched back to the official provider; the deferred attachment does not clear it. State built during an official interlude is never replayed or claimed by the plugin. After returning to plugin ownership, the session continues from plugin-verifiable evidence only.
32
32
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  The commit gate runs the static prerequisites plus the **semantic-evidence backend tier**; the complete behavioral inventories stay behind `npm run verify`.
10
10
 
11
- - `npm run check` records its proof from `npm run verify:check`: `review:validate`, `host:baseline:verify`, `semantic:check`, `build:check`, the syntax checks and `deterministic-test-groups.mjs --check`.
11
+ - `npm run check` records its proof from `npm run verify:check`: `review:validate`, `host:channels:verify`, `host:shape-check`, `semantic:check`, `build:check`, the syntax checks and `deterministic-test-groups.mjs --check`.
12
12
  - `--check` selects `test:client` plus `test:backend:fast`. The Client units stay in the gate because a change can be covered only by them; leaving them out let a red Client suite pass, which is how the copy-key and layout regressions were first caught. The backend tier is the test files that `semantic-obligations.json` names as evidence for at least one obligation, minus coverage-only paths and the verification-tooling probes that fail by construction outside their own harness. Each obligation keeps at least one executable owner, and the fast tier is derived from the ownership graph rather than a hand-maintained list.
13
13
  - `npm run verify` keeps the complete gate: `test:client:visual` plus the whole ordinary backend inventory. CI runs `verify:ci`, release preparation runs `verify` (and coverage), and any change that touches a semantic owner missing from the graph must extend the graph — which extends the fast tier at the same time.
14
14
  - Browser suites are no longer part of the commit gate. They remain the complete visual contract and run in `verify`, in CI, and on demand.
@@ -0,0 +1,48 @@
1
+ # Supersede a rewritten cell
2
+
3
+ ## Status
4
+
5
+ Accepted (0.5.3).
6
+
7
+ ## Problem
8
+
9
+ An accepted `edit_run_code` receipt schedules a derived execution: after the outer cell settles, the whole target source runs again in its revised form and is recorded as one more cell. The 0.5.2 fold treated both records as environment members, so "append" became the meaning: the edited cell kept building the environment and the revision stacked on top of it.
10
+
11
+ Appending is invisible in the common case, because the revision declares the same names and overwrites the old values. It leaves one state the plugin cannot state: a name the target declared and the revision no longer declares still exists in the environment, while no cell in the log declares it any more. The plugin then has no producible evidence for a name it reports to the model: the source no longer contains that declaration, and a restart removes it by a different rule.
12
+
13
+ ## Decision
14
+
15
+ The edited cell and its revision are related by **supersession**, not by deletion.
16
+
17
+ - The log does not change: the edited cell and the revision both stay recorded, each with its own journal and source.
18
+ - The fold decides the environment: with `editRewritesHistory` on, the revision folds as an ordinary recorded cell and **takes the edited cell's place** — it continues from that cell's parent, and the cells recorded after the edited cell continue from the revision. The superseded cell keeps its node but leaves the head's parent path; the head stays the latest recorded cell.
19
+ - The relation does not depend on how the revision itself settled: a revision that settled volatile is installed from its recorded values and still replaces its target; a revision the fold contracts (its recorded values cannot be installed) is absent from the environment, and the relation the record states still holds, so the target's provable names are still reported as superseded. A contracted revision has no node to continue from, so its successors continue from the superseded cell's parent. A `discarded` settlement is no evidence that the revision ran, so it states no relation and keeps append semantics; a `noop` derived settlement never becomes a record and states none either.
20
+ - The relation is reconstructable from the log: the fold reports `supersessions` (`supersededCallSeq` / `supersedingCallSeq`) and `supersededBindings`, attached to the recovery result like `deletedBindings` and without changing the enumerable shape of §history.
21
+ - Reporting: one relation states `PTC-R007` once per runtime, naming which cell replaced which, together with the names the log can prove and the revision no longer supplies.
22
+ - Off by default. When it is off, the fold keeps 0.5.2's append semantics exactly, so neither existing cases nor the reading of earlier session logs changes.
23
+
24
+ ## What it claims and what it refuses to claim
25
+
26
+ `supersededBindings` contains only names the log can prove: the `values` a volatile settlement recorded, `unrecordedBindings`, and `binding` save operations. A durable cell's declared names are not in its journal — replay produces them by executing the source — so the fold never guesses a name for the model, and `PTC-R007` states only the relation when there is no name to report.
27
+
28
+ The live worker and the durable fold keep their existing division of labour: the environment recovery replays is the environment the supersession applies to, while the live cache keeps the values it executed. This matches how `PTC-R005` (a recorded rewind ended these bindings) is handled: the record states the ending, the notice names it, and the live cache is not described as already emptied.
29
+
30
+ ## Alternatives considered
31
+
32
+ - **Keep append semantics and report the leftover names as lost.** Rejected: the log does contain a cell that declared them, so "lost" would be false, and the model would be told to redeclare a name whose replacement it never saw.
33
+ - **Delete the superseded cell from the log.** Rejected: the session log is append-only evidence, and history replacement belongs to DSH. Supersession is a fold-time relation, and the record has to keep proving what happened.
34
+ - **Refuse the edit unless the revision redeclares every name the target declared.** Rejected: an accepted edit is the user's decision about the cell's source, and a revision that intentionally drops a name is legal JavaScript. Refusing it would make the transport decide the source.
35
+ - **Report the relation on every later fold instead of once per runtime.** Rejected: the relation is a durable fact of the log, not a per-cell event; repeating it on every recovery is noise the model cannot act on twice, and the names it withdraws are reported by their own diagnostics.
36
+ - **Adopt the relation for a `discarded` settlement too.** Rejected: a discarded settlement is not evidence that the revision ran, so claiming it replaced the target would state an execution the log denies.
37
+
38
+ ## Consequences
39
+
40
+ - With the switch on, the set of cells in the environment no longer equals the set of cells in the log; the difference is the supersession relation, not a loss.
41
+ - Later cells read the revised value, and so do they after recovery.
42
+ - A session log written while the switch was off folds under the same relation once it is on, because the relation lives in the log rather than in the configuration.
43
+ - A runtime that supersedes a cell reports `PTC-R007` beside any restore notice the same recovery produces, and each consecutive recovery states the facts it replayed.
44
+
45
+ ## Related
46
+
47
+ - ADR 0025 owns logical binding identity and language generations.
48
+ - [durability-design.md](../durability-design.md) §「日志折叠」 and §「恢复通知」 describe the fold order and the diagnostics.
@@ -0,0 +1,53 @@
1
+ # Prove a settlement against the record it published
2
+
3
+ ## Status
4
+
5
+ Accepted (0.5.3).
6
+
7
+ ## Problem
8
+
9
+ Recovery reads a journal record as evidence for the bindings its cell declared, and the record is the only account of its own cell. Nothing inside it distinguishes "this is what happened" from "this is what someone wrote afterwards".
10
+
11
+ 0.5.2 already checks two things against each other: `completion.error.message` is rendered from the first diagnostic, so a completion that no longer renders from its diagnostic is refused, and `dshPtcPlusDiagnosticWording.digest` covers the facts those diagnostics state. Together they show why internal consistency is not enough. Edit a diagnostic and re-render the completion and the record is consistent again; and the completion's kind and value, and the cell's status, have no second statement anywhere in the record.
12
+
13
+ ## Decision
14
+
15
+ A settlement leaves a digest of its own statement at publication, and the fold recomputes it before treating the record as evidence:
16
+
17
+ - `dshPtcPlusSettlementProof = { version, recordedMessage }`, where `recordedMessage` covers the status, the completion (kind, `hasValue`, the value digest, the error kind and message) and the facts each diagnostic states.
18
+ - `help` and `source` stay out of it because they are presentation: a historical record may be read through an older generation's help wording, and a caret position is re-derived when the diagnostic is rendered.
19
+ - A mismatch means the cell cannot prove its bindings. It establishes a contraction point (`PTC-R002`) beside every other damaged-metadata case. It is not a throw and not a retry: the refusal is the fold's terminal state.
20
+ - A record without the field predates this proof and keeps the 0.5.2 evidence rules. Migration is by absence, and the journal schema version does not change, because the proof is a sibling key rather than a journal field.
21
+
22
+ Alongside it, the Host's `isError` on the same `tool/result` event is a copy this plugin cannot recompute: it comes from what the tool returned, not from the journal. When it is present, the fold requires
23
+
24
+ ```text
25
+ isError === (status === "discarded" || completion.kind === "throw")
26
+ ```
27
+
28
+ which is the relation a nested edit already proves before it publishes a derived settlement. An editor who repairs the proof from their own edited journal therefore still has to agree with the Host's copy of the outcome.
29
+
30
+ ## What the proof does not claim
31
+
32
+ - It is not an unforgeable boundary. Someone who rewrites both the facts and the proof is equivalent to a re-publication; what the proof establishes is that the record was not partially rewritten.
33
+ - The plugin does not own the Host's model-visible text, so it does not compare against it. That text is a projection this plugin cannot reproduce, and treating it as evidence would call a presentation difference tampering, a far more common event than the damage the proof guards against.
34
+ - `dshPtcPlusDiagnosticWording` remains the reading path for records that carry it; a new publication writes one proof rather than two names for one relation.
35
+
36
+ ## Alternatives considered
37
+
38
+ - **Compare against the retained model-visible message text.** Rejected: the plugin cannot recompute the Host's projection, so a renderer or locale change would read as tampering, and every failing cell in a host that projects differently would lose its bindings.
39
+ - **Bump the journal schema version and migrate old records.** Rejected: absence already carries the same information without rewriting historical metadata, and a version bump would make every pre-0.5.3 record unreadable to a generation that has no better evidence to offer.
40
+ - **Keep only the diagnostic wording digest and add a separate completion digest.** Rejected: two digests over one settlement state the same relation twice, and the fold would have to decide which of them wins when they disagree.
41
+ - **Reject a mismatched record by throwing from the fold.** Rejected: a throw turns every retry into the same throw, which is the loop that hung an earlier attempt. The fold registers an unavailable result instead.
42
+
43
+ ## Consequences
44
+
45
+ - An edited record no longer proves its cell's bindings. The fold contracts at that cell, the model reads `PTC-R002` naming the skipped cell, and the source stays in the session log.
46
+ - Both publication paths write the proof: `withJournal` for a `run_code` settlement and the derived edit settlement's metadata.
47
+ - Session logs need no migration: old records lack the field, new records carry it, and the reader branches on its presence.
48
+ - The `isError` agreement only applies where the Host records it. A log without that copy keeps the weaker evidence rather than inventing a claim.
49
+
50
+ ## Related
51
+
52
+ - ADR 0025 owns logical binding identity and language generations.
53
+ - [durability-design.md](../durability-design.md) §「Completion 校验」 describes how the fold uses the proof.
@@ -0,0 +1,48 @@
1
+ # Read and write the host's surface only through the contract it publishes
2
+
3
+ ## Status
4
+
5
+ Accepted (0.5.3).
6
+
7
+ ## Problem
8
+
9
+ Three of this plugin's host interactions cross a boundary the plugin does not own, and each had settled on a proof that is really an assumption about one host implementation:
10
+
11
+ 1. A transport-owning executor keeps the reserved `run_code` definition inside itself. Reading it by probing every zero-argument member identifies the surface by shape, but it *invokes* host code: an executor with `cancel()` and `flush()` had both called during prompt assembly, and an executor that publishes the definition through a reader taking an argument was judged "no executor" with no diagnostic, which silently removed every feature that needs execution.
12
+ 2. A derived edit dispatched as one model batch is handed to a host that assigns the execution identity itself. The relation was proved by comparing the parent token the plugin holds with the token on the execution the host runs — a token the host mints for itself and the batch contract never carries, so the accepted edit was admitted as its own root, the nested result arrived without an execution journal, and the model read "failed after acceptance" while the revision never applied.
13
+ 3. The user's editable `run_code` description and parameter texts were written onto the host's live definition by assignment. On a getter-only definition that write throws, so the text never reached the model and nothing reported it; on a writable one it survived disposal, leaving PTC Plus prose on a host object the plugin no longer owned.
14
+
15
+ ## Decision
16
+
17
+ Each interaction names the fact the plugin owns and the fact the host owns, and proves the relation with the former:
18
+
19
+ - **Reading an executor.** Which member may be invoked is not a shape question. Prompt assembly asks an executor exactly one declaration reader — `describe([])`, which says which reserved names it owns — and then the plugin's three contracted definition readers, `requirePtcTransport`, `requireTransport` and `transport`. No other member of a host executor is ever called. A host that declares the reserved name in `describe()` and publishes no definition through those readers is reported as *declared but unreadable* (an operator warning) instead of being folded into "this host has no executor"; the answer is recorded per executor as a property of the executor, and the readers are asked again on a later assembly because a scope can mount their service afterwards.
20
+ - **A batch-dispatched derived execution.** The dispatch records the submission on the execution object the plugin built, before the host runs it. The relation is then proved by the call id the plugin chose (which the host matches its batch result back to) together with the derived source; the parent token and root call are compared only on the registry contract, where the host runs the very object the plugin built. A batch host's own identity is never required to reproduce a token it never received.
21
+ - **Writing the editable slots.** The plugin installs an owned property definition and records the descriptor it replaced, once per definition. Both the writable and the accessor-only shape therefore carry the delivered text; a definition whose slot cannot be replaced keeps all of its own text rather than half of the plugin's; and disposal restores the recorded descriptors, so the host's own model-facing definition is exactly what it published before the attachment.
22
+
23
+ ## What this does not claim
24
+
25
+ - The reader names are the plugin's published contract with a transport-owning executor. A host that publishes the definition under another name is unreadable to this plugin until that name is added here — reported, not guessed at.
26
+ - Identifying a batch execution by its call id trusts the host to run the call it was given. The plugin already refuses a batch result that does not name the submitted id, so the alternative is refusing to relate the derived edit at all.
27
+ - Restoring a descriptor cannot succeed against a host that made the slot non-configurable after the plugin replaced it. That case keeps the last written text and is not reported as a host fault.
28
+
29
+ ## Alternatives considered
30
+
31
+ - **Probe every zero-argument member, with a denylist of lifecycle names.** Rejected: the denylist is a list of words, not a property of the members actually invoked, so any executor with an unlisted side-effecting member is executed during assembly.
32
+ - **Infer the parent token from the batch payload.** Rejected: it requires the host to adopt a field the batch contract does not define, and a host that ignores it leaves the relation unprovable — the defect returns whenever the optional executor package is a different revision.
33
+ - **Assign the slot and swallow the failure.** Rejected: silently not delivering the text the user edited is worse than a documented limitation, and it is the failure this decision exists to remove.
34
+ - **Restore the slot by writing the host's old text back.** Rejected: a host that published an accessor keeps reading its own state, so writing text back leaves a data property where an accessor was, and the model-visible definition changes shape because the plugin was unloaded.
35
+
36
+ ## Consequences
37
+
38
+ - Prompt assembly no longer invokes any host executor member beyond the declaration reader `describe([])` and the contracted definition readers, and a declared-but-unreadable transport is a visible operator warning rather than a set of features that quietly disappear.
39
+ - A derived edit settles as the root cell's derived execution on both dispatch contracts, so the deferred settlement, the pre-policy projection and the root cell's own durability decision are exercised on a batch host too.
40
+ - Disabling or unloading the plugin leaves the host's `run_code` definition with the text its owner published.
41
+ - `compat/channels.json` records every host package an install receives: a package outside the release train is pinned at the version the release's own dependency range resolves to, with that tarball's integrity, and a package the release neither publishes nor depends on is recorded as absent. A publish inside a declared range is therefore a recorded drift that the channel proof reports.
42
+
43
+ ## Related
44
+
45
+ - ADR 0017 owns tracking the newest published host surface.
46
+ - ADR 0049 owns proving a settlement against the record it published.
47
+ - [docs/runtime-reference.md](../runtime-reference.md) describes the executor reader contract.
48
+ - [docs/installation.md](../installation.md) describes the channel manifest a consumer install is proved against.
@@ -23,4 +23,10 @@ This directory records stable design constraints for PTC Plus. An ADR states a d
23
23
 
24
24
  - [0043: Compose Capability and Context Policies](0043-compose-capability-and-context-policies.md) — independent availability, disclosure, allocation, collection, recovery and presentation inputs with historical replay generations.
25
25
 
26
- - [0044: Coexist With The Official Persistent REPL](0044-coexist-with-the-official-persistent-repl.md) — forwarded owner release and program format, plus live-capability ownership selection between PTC Plus and the official persistent REPL.
26
+ - [0044: Coexist With The Official Persistent REPL](0044-coexist-with-the-official-persistent-repl.md) — forwarded owner release, program format and state retention, plus live-capability ownership selection between PTC Plus and the official persistent REPL.
27
+ - [0045: Customize Model-Visible Prompt Text](0045-customize-model-visible-prompt-text.md) — fixed plugin text becomes a document the user can edit per slot, with read-only segments and drafts.
28
+ - [0046: Gate The Commit With The Semantic Evidence Tier](0046-gate-the-commit-with-the-semantic-evidence-tier.md) — the change gate runs the semantic tier rather than a hand-picked file list.
29
+ - [0047: Store Journal Argument Digests In The Projection](0047-store-journal-argument-digests-in-the-projection.md) — the projection keeps argument digests so admission can reject a rewritten call.
30
+ - [0048: Supersede A Rewritten Cell](0048-supersede-a-rewritten-cell.md) — an edit with the rewrite setting on supersedes its target; the target stays in the log and its names are reported as replaced.
31
+ - [0049: Prove A Settlement Against Its Published Record](0049-prove-a-settlement-against-its-published-record.md) — a settlement carries the digest of what it asserts, so an edited record is not evidence.
32
+ - [0050: Read And Write The Host Surface Only Through Its Published Contract](0050-read-host-surfaces-through-their-published-contract.md) — contracted executor readers, the call id a batch host echoes back, and owned slot descriptors that disposal restores.
@@ -68,7 +68,7 @@ Skill 根在当前 generation 发布绝对 composition path 时取其 sibling;
68
68
 
69
69
  `internal/user-bindings-store.js` 独占 `$DSH_HOME/ptc-plus/bindings.json`。单一 JSON 文档避免源码与 metadata 双源;进程内队列、file lock、磁盘文本比较、expected revision 和 atomic replacement 共同防止并发覆盖。每次 mutation 在替换前验证完整 enabled snapshot 的声明预算;外部写入的超限文档按损坏输入处理。损坏输入保持显式 error,不会被空文档静默覆盖。候选与正式激活的相对 import 都以该文件所在目录为基准,因此 session cwd 不会改变 helper 的依赖解析。
70
70
 
71
- prompt assembly 为每个 PTC request 取得一次当前启用条目快照,作为随后 dispatch 的期望集合。`tools:ptc-plus-user-binding-defaults` 从首轮通过追加的 PTC API catalog 展示条目提示词及按开关选择的源码派生接口,按稳定 ID 排序,只描述配置 API 与执行时初始化契约;组装不执行源码,也不读取激活状态或加入 revision。`/binding` 保存并启用、会话中途启停、删除或修改模型上下文后,下一次宿主允许且接受的步骤追加当前说明或撤销旧说明,无需等待激活。条目变化不改写 system、tool schema、顺序或旧消息;未变说明按已提交日志与公共 ordered surface 去重,重启后继续使用相同证据。停用条目不贡献内容,关闭声明且提示词为空的条目同样省略。
71
+ prompt assembly 为每个 PTC request 取得一次当前启用条目快照,作为随后 dispatch 的期望集合。快照先经该会话的 adoption 决策过滤,再作为期望集合:全局启停拥有 supply(目录里有哪些条目),会话拥有 adoption(这个会话用其中哪些),worker 拥有 materialization(实际装上了哪些),诊断与 journal 拥有 proof。adoption 是每条目的二态决定加一个缺省——未表态即使用目录提供的一切,因此“显式使用”与“从未表态”产生同一环境,是一个状态而不是两个,也不需要借用能力侧的 `automatic`(那个词表示“由宿主推导”)。adoption 只能撤回已供给条目:全局停用的条目不在目录里,会话无从命名,`/binding skip` 直接拒绝,因此“覆盖复活已删除条目”没有编码,也无须规则禁止。决策记录为一条 `binding-adoption` PTC 消息,由 session log projection 读回最新一条,重启即从日志重建,不引入第二存储或进程内缓存。`tools:ptc-plus-user-binding-defaults` 从首轮通过追加的 PTC API catalog 展示条目提示词及按开关选择的源码派生接口,按稳定 ID 排序,只描述配置 API 与执行时初始化契约;组装不执行源码,也不读取激活状态或加入 revision。`/binding` 保存并启用、会话中途启停、删除或修改模型上下文后,下一次宿主允许且接受的步骤追加当前说明或撤销旧说明,无需等待激活。条目变化不改写 system、tool schema、顺序或旧消息;未变说明按已提交日志与公共 ordered surface 去重,重启后继续使用相同证据。停用条目不贡献内容,关闭声明且提示词为空的条目同样省略。
72
72
 
73
73
  配置段是全局绑定提示词与接口的唯一注入来源,不包含实现源码;成功初始化、session-local shadow 和 worker 重建本身不生成活动公告,也不触发相同配置重发。执行结果与诊断拥有实际行为证据;需要验证可用性时只对已知名称做无副作用观察。历史 `tools:ptc-plus-user-bindings` 活动段保留读取支持,下一次允许且接受的快照撤销旧活动声明一次。`tools:sdk` 与 direct-tool schema 不随条目内容改变。配置 API 不证明当前值,也不延长被 compaction 遮蔽的 session-local 状态。runtime 在 cell preflight 前把期望集合映射到 BindingCatalog,再由 worker 激活:当前覆盖粒度为 `per-name`,同名 session-local binding 只覆盖对应全局名称,同条目的其他名称继续激活;v1-v7 历史 cell 保留整条 entry 退出的旧粒度。条目级失败产生诊断并从该 cell 排除,不阻塞独立代码。失败 initializer 一旦发起 program call,整个 cell 进入 volatile,因此只包含成功条目的结果 snapshot 不会被误作该调用的 cold replay source。
74
74
 
@@ -193,7 +193,8 @@ pre-execute -> tools/execute -> post-execute
193
193
  9. durable node 保存 parent link,命名状态保存 node index;
194
194
  10. `meta.dshPtcPlusRecoveryBoundaries` 在 `tool/result` event sequence 参与排序前完成规范化;损坏 boundary 被拒绝为状态证据并建立保守收缩点,合法 boundary 通过失败 call seq 选择 node,并要求记录的 frontier 恰好是该 node 的 parent;
195
195
  11. 折叠器剪除不可证明的 node、binding 与依赖后代,按剩余记录重算 checkpoints;runtime 重置 worker 并验证候选 frontier,逐级收缩到最大可重放 frontier,必要时到空 REPL;
196
- 12. 当前 cell 只在 worker 实际拥有该 frontier 后执行,成为它的新 child 或空 REPL 的新 root;当前结果持久记录本次收缩并投影一次恢复诊断。
196
+ 12. 当前 cell 只在 worker 实际拥有该 frontier 后执行,成为它的新 child 或空 REPL 的新 root;当前结果持久记录本次收缩并投影一次恢复诊断;
197
+ 13. `editRewritesHistory` 打开时,派生 cell 仍按实际派生 start 序号折叠,同时折叠器应用它记录的取代关系:修订接在被取代 node 的位置(接它的 parent),原本跟在被取代 node 之后的 node 改接修订,head 仍是最晚记录的那一格。被取代的 node 保留在记录里但离开 head 的 parent 路径,因此编辑前读到它声明的 cell 重放时看到的是修订后的环境,而不是一个已撤销的声明。收缩的修订没有 node 可接,它的后继直接改接被取代 node 的 parent。关系与日志能证明的被撤销名字随恢复结果给出(`supersessions` / `supersededBindings`),不改变 `§history` 的可枚举形状。开关关闭时折叠保持追加语义,见 [ADR 0048](adr/0048-supersede-a-rewritten-cell.md)。
197
198
 
198
199
  当前 journal 没有完整的逐 binding 读写与传递依赖图。源码存在、重放未抛错或 completion 相同,都不足以证明闭包捕获、属性 mutation、动态名称访问或运行时值与原执行一致。因此恢复不能任意挑选 unknown boundary 后看似独立的 binding;缺少机械证明时以 cell 为原子并丢弃受影响后缀。将来若要保留更细粒度状态,必须通过版本化、owner-owned 的依赖证据扩展契约,不能从名称或成功重放猜测。
199
200
 
@@ -207,6 +208,8 @@ surface replacement 是否删除状态取决于模型失去了什么,而不取
207
208
 
208
209
  该规则也约束 live worker。structured host session 的 runtime 在每个后续 cell 前都重新读取 DSH public ordered surface,并验证所有现存 live call 的精确 assistant provenance;`replaceGeneration` 没有变化不构成继续复用的证明。generation 改变、缺失或不可读取也进入同一验证。若现存 binding 超出模型可知 frontier,先重置 worker、重建交集 frontier,再执行当前 cell。只有不带 host session 的内部 primitive session id 明确使用 live-only 连续性。Client header card 的 `dshPtcPlusBindings` 只帮助用户检查,不进入模型上下文,也不延长 binding 生命周期;相同 contraction 应让 UI inventory 进入不可确认状态。未来若新增模型可见状态投影,它必须由用户、模型或明确配置选择,有固定 schema、硬预算、append-only runtime-context 证据和明确清除条件,不能仅为延长隐藏状态而默认注入,也不能污染稳定 prompt prefix。
209
210
 
211
+ 面板显示的不是“当前状态”,而是某个运行期的一次观察;投影的 wire view 因此携带产生它的运行期(`{ runtime, memory }`,`internal/repl-memory-projection.js` 的 `normalizeReplMemoryView`),让读者按数据判断而不是按对象身份猜。两件事分开:同一运行期内的投影刷新只是新鲜度问题,清单仍然有效,不该闪成“不可确认”;跨运行期的快照属于上一个进程,必须判为不可确认并说明原因(`memory.previousRuntime`)。这同时是契约用例 `repl-memory-projection` 的客户端对应面:服务端仍按 UUID generation 校验写入的快照,客户端不再用兜底把上一个运行期的清单复活。
212
+
210
213
  ## Completion 校验
211
214
 
212
215
  普通异常可能已经建立或修改 binding,所以 durable `throw` 属于语言历史。重放接受的语义结果只有:
@@ -225,7 +228,9 @@ surface replacement 是否删除状态取决于模型失去了什么,而不取
225
228
 
226
229
  基础设施失败会终止当前 worker,并把 log-only recovery boundary 放入当前已结算 `tool/result` 的私有 metadata。kernel 从失败 node 的 parent 重新重放;若该 frontier 仍失败则继续向 parent 收缩,直到最大可验证 frontier 或空 REPL。触发恢复的当前 `run_code` 在候选 frontier 验证成功前不执行,验证后在同一次请求中继续。boundary 的失败身份可为顶层 call,或已经由共享 timeline 证明两组 dispatch pairs 和根最终关联的真实派生 start 序号;原始 nested start 不足以证明该身份。收缩保留失败 node 的已证明 parent,之后独立的有效 carrier 仍可进入新 ancestry。boundary 无效或历史本身无法完整折叠时,相关记录和可能依赖它的状态不得进入 frontier;这类历史 PTC metadata 损坏本身不返回永久 recovery error,而是继续收缩,最差用空 worker 执行当前 cell,并在其最终 result 持久表达新 ancestry。已经写入旧版自定义 boundary event 的日志必须先通过显式、非破坏迁移转换为 DSH 可加载的 metadata;迁移失败时不得覆盖原始日志。
227
230
 
228
- 记录的 diagnostics 与 completion 是同一 settlement 的两份表述:`completion.error.message` 由第一条 diagnostic 渲染得到,因此两者一致只证明这份记录内部自洽,不能证明文字未被改写(编辑 diagnostic 后重新渲染 completion 仍一致)。发布 `tool/result` 私有 metadata 时,runtime 同时写入这些 diagnostic 所述事实的摘要 `dshPtcPlusDiagnosticWording.digest`,覆盖 `message` 以及 code、severity、phase、stateEffect、dispatchState、cause、collisions;`help` 与 `source` 属于呈现(历史记录的 help 措辞和 caret 位置可来自更早 generation),不进入摘要。恢复折叠在把该记录当作证据前重算并比较摘要:摘要缺失表示记录发布于该字段引入之前,仍按旧证据规则处理;摘要存在而不再匹配时,该 cell 不能证明自己的 binding,与其它损坏 metadata 一样建立收缩点。该摘要证明记录未被部分改写,不是不可伪造的边界。
231
+ 记录的 diagnostics 与 completion 是同一 settlement 的两份表述:`completion.error.message` 由第一条 diagnostic 渲染得到,因此两者一致只证明这份记录内部自洽,不能证明文字未被改写(编辑 diagnostic 后重新渲染 completion 仍一致)。0.5.2 在发布 `tool/result` 私有 metadata 时写入这些 diagnostic 所述事实的摘要 `dshPtcPlusDiagnosticWording.digest`,覆盖 `message` 以及 code、severity、phase、stateEffect、dispatchState、cause、collisions;`help` 与 `source` 属于呈现(历史记录的 help 措辞和 caret 位置可来自更早 generation),不进入摘要。恢复折叠在把该记录当作证据前重算并比较摘要:摘要缺失表示记录发布于该字段引入之前,仍按旧证据规则处理;摘要存在而不再匹配时,该 cell 不能证明自己的 binding,与其它损坏 metadata 一样建立收缩点。该摘要证明记录未被部分改写,不是不可伪造的边界。0.5.3 起新记录不再写这个摘要,但仍读取带它的记录。
232
+
233
+ 0.5.3 起,发布的不是只覆盖 diagnostics 的摘要,而是一条 settlement 对自己陈述的完整证明:`dshPtcPlusSettlementProof = { version, recordedMessage }` 覆盖 status、completion(kind、`hasValue`、value 摘要、error kind 与 message)与 diagnostics 所述事实,`help`/`source` 仍属呈现。折叠重算并比较它,不一致即在该 cell 上收缩(`PTC-R002`);字段缺失表示记录发布于该证明之前,按 0.5.2 的互校规则处理,因此日志不需要迁移,两个 adjunct 可以共存于同一份历史。Host 对同一 `tool/result` 记录的 `isError` 是插件不能重算的第二份副本,折叠在它存在时要求它等于 `status === "discarded" || completion.kind === "throw"`:即使改写者按新摘要重算证明,也仍要与 Host 记下的事实一致。见 [ADR 0049](adr/0049-prove-a-settlement-against-its-published-record.md)。
229
234
 
230
235
  ## State Operations
231
236
 
@@ -280,6 +285,8 @@ help: ...
280
285
 
281
286
  live kernel 首次进入 volatile 只更新 journal 的 `status` / `volatileReason` 和 `repl.state(list)`,不向模型投影 warning/note。该转换不要求当前任务采取行动;只有 cold recovery 已实际跳过 volatile、unknown 或 replay-abandoned 历史时才发送 `PTC-R002`。worker 在首次观察到直接 Node/OS 边界时立即通知主线程,因此后续 hard abort、timeout 或 worker exit 仍能把原因写入 discarded journal。
282
287
 
288
+ 记录中的 rewind 与取代关系是另外两种“按记录结束”,各自有自己的诊断:`PTC-R005` 说明 rewind 结束了哪些 binding,`PTC-R007` 说明哪一格取代了哪一格,并在日志能证明时点名修订不再提供、重放也没有重新装上的名字。两者都不是恢复失败,也不把 live 缓存说成已被清空;取代关系在每个 runtime 内报告一次。同一次恢复可以既装入了记录值又取代了单元格,两个事实各自成一条诊断,不互相取舍:被取代的名字不在 missing 与 rewind 列表里,只陈述恢复会把它报告到无处。帮助对说明的是这类通知的含义,因此同一 runtime 内再次报告同一组事实时,事实照旧重复、帮助只出现一次。
289
+
283
290
  ## 失败状态语义
284
291
 
285
292
  诊断的 `stateEffect` 描述当前 cell 的 live/冷恢复状态事实,不由 severity 推断,也不表示当前 binding 是否仍可用:
@@ -2,7 +2,7 @@
2
2
 
3
3
  PTC Plus requires Node.js `^22.19.0 || >=24.0.0` and targets the latest available DSH release with TypeScript PTC mode. Historical interface branches are retained through capability detection, without promising a fully verified earlier Host installation; when both generations expose competing presentation evidence, the current DSH contract wins. Compatibility follows those live extension surfaces rather than a version allowlist.
4
4
 
5
- The package declares `engines.dsh: "*"` for DSH's plugin compatibility preflight. This keeps release-number gating open while public capability checks and packed Host contracts establish compatibility; it does not claim that every historical or future Host has been tested. Host-owned service packages remain required peers.
5
+ The package declares `engines.dsh: "*"` for DSH's plugin compatibility preflight. This keeps release-number gating open while public capability checks and packed Host contracts establish compatibility; it does not claim that every historical or future Host has been tested. Host-owned service packages are peers of the plugin because the host supplies them; the PTC tool executor is an optional peer, because a host that keeps tool presentation in its registry does not publish that package at all.
6
6
 
7
7
  The worker uses the Host's Node executable. Startup verifies native REPL framing, imports, syntax failures, synchronous throws, awaited rejections, and original return values. The adapter combines public eval callbacks with domain error events before REPL formatting; a source-end witness distinguishes empty completion from falsy rejection. A failed or timed-out probe reports a runtime prerequisite failure before user code executes. The engine range follows DSH; CI exercises Node 22.19, 24, and 26, without claiming that untested future releases have already passed.
8
8
 
@@ -20,7 +20,7 @@ Install the plugin into the profile that actually runs the target DSH surface. D
20
20
 
21
21
  `danger-full-access` is the primary supported experience. The worker isolates the REPL lifecycle; it is not a malicious-code sandbox. DSH continues to own native-tool scope, policy, approval, cancellation, sandboxing, and scheduling. Narrower profiles expose only their available capabilities; PTC Plus does not simulate missing authority or add another permission system.
22
22
 
23
- The optional `cordisToolsEnabled` integration requires the current DSH installation to provide its shipped `cordis` preset plus the public preset, Skill, Cordis, settings, and tool-runtime services. PTC Plus declares the shared service packages as unrestricted required peers instead of installing private runtime copies. For a pathless preset it asks the Host's `pluginPackages.packageOf` service for the active generation's official preset package and requires the exact `cordis-plugin-development/SKILL.md` before mounting it; an older generation with no package service may resolve only its historical plural preset package relative to the Host's `ctx.baseUrl`. This compatibility path is disabled whenever `pluginPackages` exists, so packages merely reachable from the plugin profile cannot override an active Host result. Runtime capability validation owns compatibility, and CI checks the packed plugin against frozen official `latest`, `next`, and newest published release graphs. Each result applies only to the tested cohort. That load step mounts the pathless Cordis companion from a separated active-host resource while a resolvable stale package is present beside the packed plugin, runs `scripts/dsh-execution-seam-smoke.mjs` to exercise public provider composition around the execution service the installed tool runtime uses, and runs `scripts/dsh-rpc-contract-smoke.mjs` to register the packed Remote descriptors with the installed TYPERT registry and evaluate the decoder that generation reads ([ADR 0030](adr/0030-serve-both-typert-codec-generations-from-one-codec.md)); an import alone cannot distinguish a loaded plugin from one whose injection is unsatisfied, because Cordis leaves the latter pending without an error, and it cannot show that the Host accepts a wire contract it validates only at registration. The same suite round-trips argument diagnostics through the selected Host's compressed JSONL Session persistence. Service packages retain the dependency graph selected by their Host distribution rather than independently selecting each service's release tag. DSH's profile module fallback must resolve the peers, while `pluginPackages` identifies the official preset resource from an active installation that publishes the service. Do not copy `SKILL.md` or add the Cordis preset's Skill directory to global roots. If the host surface is incomplete, plugin activation or enabling the setting fails instead of loading a second DSH core.
23
+ The optional `cordisToolsEnabled` integration requires the current DSH installation to provide its shipped `cordis` preset plus the public preset, Skill, Cordis, settings, and tool-runtime services. PTC Plus declares the shared service packages as unrestricted required peers instead of installing private runtime copies. For a pathless preset it asks the Host's `pluginPackages.packageOf` service for the active generation's official preset package and requires the exact `cordis-plugin-development/SKILL.md` before mounting it; an older generation with no package service may resolve only its historical plural preset package relative to the Host's `ctx.baseUrl`. This compatibility path is disabled whenever `pluginPackages` exists, so packages merely reachable from the plugin profile cannot override an active Host result. Runtime capability validation owns compatibility, and CI checks the packed plugin against the blocking host channels resolved from `compat/channels.json`. Each result applies only to the tested cohort. That load step mounts the pathless Cordis companion from a separated active-host resource while a resolvable stale package is present beside the packed plugin, runs `scripts/dsh-execution-seam-smoke.mjs` to exercise public provider composition around the execution service the installed tool runtime uses, and runs `scripts/dsh-rpc-contract-smoke.mjs` to register the packed Remote descriptors with the installed TYPERT registry and evaluate the decoder that generation reads ([ADR 0030](adr/0030-serve-both-typert-codec-generations-from-one-codec.md)); an import alone cannot distinguish a loaded plugin from one whose injection is unsatisfied, because Cordis leaves the latter pending without an error, and it cannot show that the Host accepts a wire contract it validates only at registration. The same suite round-trips argument diagnostics through the selected Host's compressed JSONL Session persistence. Service packages retain the dependency graph selected by their Host distribution rather than independently selecting each service's release tag. DSH's profile module fallback must resolve the peers, while `pluginPackages` identifies the official preset resource from an active installation that publishes the service. Do not copy `SKILL.md` or add the Cordis preset's Skill directory to global roots. If the host surface is incomplete, plugin activation or enabling the setting fails instead of loading a second DSH core.
24
24
 
25
25
  The plugin attaches to whichever program-execution service the installed DSH registers, and it presents the capabilities of the execution it performs ([ADR 0028](adr/0028-attach-to-the-host-ptc-execution-seam.md)). Cells run in the session kernel under the session's configured budgets, so `run_code` advertises neither a per-call deadline nor a file sandbox, and DSH rejects `timeoutMs` and `sandbox_permissions` with its own message instead of the plugin accepting an input it would then ignore.
26
26
 
@@ -32,11 +32,11 @@ The Client root requires `slots`, `locale`, `connection`, and `remote`; it adopt
32
32
 
33
33
  ### Compatibility Evidence
34
34
 
35
- The frozen dependency graphs are installation cohorts, not proof of distinct capability generations. `compat/latest/package-lock.json`, the root `package-lock.json`, and `compat/current/package-lock.json` record the latest-tag, next-tag, and newest published-release cohorts respectively; their recorded provenance does not prove live registry availability or untested compatibility. The latest/next cohorts exercise `ptcRuntime`, `configForms`, `plugins.row.config`, volatile configuration and `codec.create()`. `npm run host:baseline:verify` checks frozen graph consistency, not every historical fallback or current registry availability.
35
+ Support is expressed as channels, not versions. `compat/channels.json` records, per channel, the host release each official dist-tag resolves to and the tarball integrity of every host package whose published surface the plugin probes. `latest` and `next` are the blocking channels; `alpha` is a pre-adaptation that reports without blocking. A probed package the release does not publish is recorded at the version and integrity its own declared dependency range resolves to, so a publish inside that range is a recorded drift; a probed package the release neither publishes nor depends on is recorded as absent, and the install proof rejects a graph that contains one. The recorded versions do not prove live registry availability or untested compatibility, and behaviour never branches on them: presentation, transport ownership and state retention are detected from the host's published shapes. `npm run host:channels:verify` checks the tracked manifest offline; `npm run host:channels:update` is the only command that resolves it from the registry.
36
36
 
37
37
  | Retained interface | Evidence and consequence of removal |
38
38
  | --- | --- |
39
- | `retainedBy.mainView`, `plugins.row.config`, volatile updates, `codec.create()`, composer props carrying their own `sessionId` | Required by the frozen graphs; these are current paths, not optional historical fallbacks. |
39
+ | `retainedBy.mainView`, `plugins.row.config`, volatile updates, `codec.create()`, composer props carrying their own `sessionId` | Required by the installed channel graphs; these are current paths, not optional historical fallbacks. |
40
40
  | `settingsScope`, `settings.plugin.item`, section-installer variants | Historical, unfrozen Host shapes, covered with fixtures. Removal loses their live settings, card seat or reconfiguration path. |
41
41
  | `snapshot.current`, bare summary `agentPreset` | Historical, unfrozen selection and preset shapes. Removal can strand their session-specific views or PTC eligibility. Binding and draft evidence never use the preset fallback. |
42
42
  | Historical icon names and missing primitives | Fixtures establish component fallback, not a complete earlier Client. Removal of icon candidates loses native glyphs; usable text/component fallbacks must remain. |
@@ -47,14 +47,14 @@ The frozen dependency graphs are installation cohorts, not proof of distinct cap
47
47
  | `sessionQuery.observeSession` and host-only `sessionProjections` | Current history and presentation owners; missing services are diagnosed, not treated as empty recovery evidence. The projection publishes no Client wire view. |
48
48
  | Legacy session event arrays and V0 boundary migration | Read-only fixtures preserve historical ancestry and file conversion through the shared event transition. Removal can discard reconstructable older sessions; malformed arrays are diagnosed, and format conversion does not prove a complete older Host's live behavior. |
49
49
 
50
- Capabilities select behavior, never a release number. Historical fixture success is not packed-Host evidence. Full Host smoke is bounded to its selected graph, and a clean build is not live browser activation. Public provider composition supports frozen original execution instances without changing their descriptors. The result bridge requires mutable registered tool definitions ([ADR 0028](adr/0028-attach-to-the-host-ptc-execution-seam.md)); frozen tool definitions remain unsupported. The public admission boundary uses DSH's published `ptc` mode: `run_code` is the sole top-level transport, and the agent-scoped editor is invoked through `tools.edit_run_code` inside a program ([ADR 0005](adr/0005-temporary-rejected-cell-edit-transport.md#public-integration-status)). Packed lifecycle evidence covers actual nested admission, derived execution, persisted recovery and provider handback on its frozen official Host graph; an internal fixture or provider seam probe alone does not establish that compatibility. Proven native miscalls are normalized before Host dispatch; an unnormalized root call can reach approval before the plugin rejects it.
50
+ Capabilities select behavior, never a release number. Historical fixture success is not packed-Host evidence. Full Host smoke is bounded to its selected graph, and a clean build is not live browser activation. Public provider composition supports frozen original execution instances without changing their descriptors. The result bridge requires mutable registered tool definitions ([ADR 0028](adr/0028-attach-to-the-host-ptc-execution-seam.md)); frozen tool definitions remain unsupported. The public admission boundary uses DSH's published `ptc` mode: `run_code` is the sole top-level transport, and the agent-scoped editor is invoked through `tools.edit_run_code` inside a program ([ADR 0005](adr/0005-temporary-rejected-cell-edit-transport.md#public-integration-status)). Packed lifecycle evidence covers actual nested admission, derived execution, persisted recovery and provider handback on its installed channel host graph; an internal fixture or provider seam probe alone does not establish that compatibility. Proven native miscalls are normalized before Host dispatch; an unnormalized root call can reach approval before the plugin rejects it.
51
51
 
52
52
  ## npm Release
53
53
 
54
54
  Use this form after the selected version is available from the npm registry:
55
55
 
56
56
  ```sh
57
- dsh plugin --profile <profile> add dsh-ptc-plus@0.5.1
57
+ dsh plugin --profile <profile> add dsh-ptc-plus@0.5.3
58
58
  dsh --profile <profile> --dump-config
59
59
  ```
60
60
 
@@ -91,7 +91,7 @@ pnpm dsh --profile <profile> --dump-config
91
91
 
92
92
  ```sh
93
93
  npm pack
94
- dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.5.1.tgz
94
+ dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.5.3.tgz
95
95
  dsh --profile <profile> --dump-config
96
96
  ```
97
97
 
@@ -159,7 +159,7 @@ dsh plugin add github:muyuanjin/dsh-ptc-plus#main
159
159
  dsh --dump-config
160
160
  ```
161
161
 
162
- After an npm release, the package spec may instead be `dsh-ptc-plus@0.5.1`. For a local package, use its absolute tarball path. Restart DSH Desktop after installation. Linux Desktop is not a current DSH Desktop release target; use DSH CLI/Web on Linux.
162
+ After an npm release, the package spec may instead be `dsh-ptc-plus@0.5.3`. For a local package, use its absolute tarball path. Restart DSH Desktop after installation. Linux Desktop is not a current DSH Desktop release target; use DSH CLI/Web on Linux.
163
163
 
164
164
  ## Upgrades
165
165
 
@@ -172,7 +172,7 @@ dsh --profile <profile> --dump-config
172
172
 
173
173
  For a tarball or a different pinned Git revision, replace the `add` argument with the absolute tarball path or `github:muyuanjin/dsh-ptc-plus#COMMIT_SHA`. This updates the profile dependency spec; the Loader module name remains `dsh-ptc-plus`. `dsh plugin --profile <profile> update dsh-ptc-plus` updates within its saved dependency constraint, so it cannot move an exact pinned revision to a different revision. Bare commands in Desktop's **Open DSH Terminal** target its active profile.
174
174
 
175
- Restart the selected Host after the package transaction and verify its selected package and full bundle in the plugin page and `--dump-config`; installing dependencies alone does not replace already loaded JavaScript. Preserve the bundle's existing enabled/disabled choice. A source-checkout `npm ci` only prepares developer dependencies, and Windows build-cache preparation only prepares a tarball: neither upgrades a profile. Avoid `npm install` in an unrelated shell directory and do not edit Loader module names to encode revisions. The plugin never patches DSH or requires a custom Host build. Compatibility claims require the current published cohort's packed acceptance evidence.
175
+ Restart the selected Host after the package transaction and verify its selected package and full bundle in the plugin page and `--dump-config`; installing dependencies alone does not replace already loaded JavaScript. Preserve the bundle's existing enabled/disabled choice. A source-checkout `npm ci` only prepares developer dependencies, and Windows build-cache preparation only prepares a tarball: neither upgrades a profile. Avoid `npm install` in an unrelated shell directory and do not edit Loader module names to encode revisions. The plugin never patches DSH or requires a custom Host build. Compatibility claims require the resolved channel release's packed acceptance evidence.
176
176
 
177
177
  Your settings document and session logs are not rewritten by an upgrade. A release that adds a configuration field applies its documented default until you choose a value. A release that raises the Session format generation requires the migration below before an older log is opened by the new Host; the plugin reports that condition instead of rewriting history on its own.
178
178
 
@@ -12,6 +12,13 @@
12
12
  * 检查点的保留窗口在设置里可调:保留最近单元格数(8–512,默认 8)、保留载荷预算(1 MiB–16 MiB,默认 8 MiB,最坏情况约 32 MB)、单元格簇上限(默认与保留单元格数一致,`0` 表示只由载荷预算约束)
13
13
  * 计算基线采样、worker 回收、启动探针与宿主执行入口的等待上限,以及连续未测量 cell 的丢弃阈值,都可以按机器情况调整
14
14
 
15
+ <h3>稳定性</h3>
16
+
17
+ * 表面重发布(例如切换工作目录、能力观测、宿主重渲染)不再重建内核:只要 worker 还活着,绑定就在原地保留,视图变化不影响计算状态
18
+ * 恢复提示只在真的需要时出现:完整恢复不再发通知,只有确实无法恢复的绑定才被点名(`PTC-R002`),曾被宣布可用之后消失的绑定另有 `PTC-R006` 点名
19
+ * 编辑命令是追加语义:被接受的编辑新增一次调用,原有单元格与其绑定保持不变;改写历史是可选的未来能力,默认关闭
20
+ * 长会话实测:163 个 run_code 单元格的会话中恢复类诊断数为 0
21
+
15
22
  <h3>修复</h3>
16
23
 
17
24
  * 会话日志检查点的体积由窗口与预算决定,不再随会话累计写入量无限增长:窗口按条数与字节双重上限把旧结算折成摘要记录,超出预算的单个单元格整簇移除。以每格 20 KB 代码加 20 KB 写入的会话实测,450 格为 0.62 MB、900 格为 0.83 MB
@@ -19,6 +26,8 @@
19
26
  * 单个单元格本身就超过载荷预算时,它会连同参数原文一起移出检查点并提示「最新单元格超出保留预算,已从检查点移除」,而不是让检查点无限变大
20
27
  * 提示文本面板里的原因码与槽位种类按界面语言显示,中文界面不再出现内部代码;只读槽位的说明与按钮样式与设置页其余部分一致
21
28
  * 选中提示文本的某一行后,编辑区会滚动到可见位置,不再出现「点了行却没看到编辑器」
29
+ * REPL 绑定面板在会话投影刷新时保留最后一次确认的清单,不再短暂显示后变成「当前绑定状态尚不可确认」
30
+ * 依赖安全:`zod` 解析到 4.6.7(GHSA-mrhx-c7xc-m3w5,high)
22
31
 
23
32
  <h3>升级</h3>
24
33
 
@@ -29,6 +38,10 @@
29
38
  dsh plugin --profile <profile> add dsh-ptc-plus@0.5.2
30
39
  ```
31
40
 
41
+ <h3>致谢</h3>
42
+
43
+ * 感谢 @smter 的 #11:定位了「宿主事件循环停顿后,已经到达的采样回复被当成超时丢弃」的竞态,并贡献了随本版本合入的回归用例
44
+
32
45
  ---
33
46
 
34
47
  <h3 id="en-v052">Host Compatibility</h3>
@@ -43,6 +56,13 @@ dsh plugin --profile <profile> add dsh-ptc-plus@0.5.2
43
56
  * The checkpoint retention window is configurable: retained recent cells (8–512, default 8), retained payload budget (1 MiB–16 MiB, default 8 MiB, worst case about 32 MB) and the retained cluster limit (defaults to the retained cells; `0` means the payload budget is the only bound)
44
57
  * The bounds for compute-baseline sampling, worker reclamation, the startup probe and the host execution seam, plus the threshold for consecutive unmeasured cells, can be tuned for the machine
45
58
 
59
+ <h3>Stability</h3>
60
+
61
+ * A surface republish (working-directory change, capability observation, host re-render) no longer rebuilds the kernel: while the worker is alive the bindings stay where they are, and a change of view does not change computation state
62
+ * Recovery notices appear only when they are needed: a complete restore is silent, a binding that truly cannot be restored is named (`PTC-R002`), and one that was announced live and later vanished is named separately (`PTC-R006`)
63
+ * An accepted edit appends a call: the original cell and its bindings stay unchanged; rewriting history remains a future opt-in, off by default
64
+ * Measured on a long session: 163 run_code cells produced zero recovery diagnostics
65
+
46
66
  <h3>Fixes</h3>
47
67
 
48
68
  * The session-log checkpoint is bounded by the window and the budget instead of growing without limit with what the session writes: a count and byte window folds older settlements into summary records and removes a single cell that exceeds the budget on its own. Measured on sessions writing 20 KB of code plus 20 KB per cell: 0.62 MB at 450 cells and 0.83 MB at 900
@@ -50,6 +70,8 @@ dsh plugin --profile <profile> add dsh-ptc-plus@0.5.2
50
70
  * A single cell larger than the payload budget is removed with its argument text and reported as "The newest cell exceeded the retention budget and was removed from the checkpoint", instead of letting the checkpoint grow without bound
51
71
  * The reason codes and slot kinds in the prompt text surface follow the interface language, so Chinese users no longer see internal identifiers; read-only slot descriptions and buttons match the rest of the settings page
52
72
  * Selecting a prompt text row scrolls its editor into view, so a selected row always shows the editor it expands
73
+ * The REPL binding panel keeps the last confirmed inventory when the session projection refreshes, instead of briefly showing it and then falling back to "Current binding state cannot be confirmed"
74
+ * Dependency security: `zod` resolves to 4.6.7 (GHSA-mrhx-c7xc-m3w5, high)
53
75
 
54
76
  <h3>Upgrade</h3>
55
77
 
@@ -60,20 +82,10 @@ dsh plugin --profile <profile> add dsh-ptc-plus@0.5.2
60
82
  dsh plugin --profile <profile> add dsh-ptc-plus@0.5.2
61
83
  ```
62
84
 
63
- ---
64
-
65
- Full Changelog: https://github.com/muyuanjin/dsh-ptc-plus/compare/v0.5.1...v0.5.2
85
+ <h3>Thanks</h3>
66
86
 
67
- <h3>稳定性</h3>
87
+ * Thanks to @smter for #11: they located the race where a buffered sampling reply was discarded as a timeout after a host event-loop stall, and contributed the regression case that ships with this release
68
88
 
69
- * 表面重发布(例如切换工作目录、能力观测、宿主重渲染)不再重建内核:只要 worker 还活着,绑定就在原地保留,视图变化不影响计算状态
70
- * 恢复提示只在真的需要时出现:完整恢复不再发通知,只有确实无法恢复的绑定才被点名(`PTC-R002`),曾被宣布可用之后消失的绑定另有 `PTC-R006` 点名
71
- * 编辑命令是追加语义:被接受的编辑新增一次调用,原有单元格与其绑定保持不变;改写历史是可选的未来能力,默认关闭
72
- * 长会话实测:163 个 run_code 单元格的会话中恢复类诊断数为 0
73
-
74
- <h3 id="en-stability">Stability</h3>
89
+ ---
75
90
 
76
- * A surface republish (working-directory change, capability observation, host re-render) no longer rebuilds the kernel: while the worker is alive the bindings stay where they are, and a change of view does not change computation state
77
- * Recovery notices appear only when they are needed: a complete restore is silent, a binding that truly cannot be restored is named (`PTC-R002`), and one that was announced live and later vanished is named separately (`PTC-R006`)
78
- * An accepted edit appends a call: the original cell and its bindings stay unchanged; rewriting history remains a future opt-in, off by default
79
- * Measured on a long session: 163 run_code cells produced zero recovery diagnostics
91
+ Full Changelog: https://github.com/muyuanjin/dsh-ptc-plus/compare/v0.5.1...v0.5.2