dsh-ptc-plus 0.4.1 → 0.4.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.
- package/README.en.md +164 -78
- package/README.md +165 -79
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
- package/assets/ptc-plus-icon.svg +5 -0
- package/client.js +854 -336
- package/compiler-core.cjs +161 -157
- package/compiler-core.cjs.map +3 -3
- package/docs/README.md +4 -1
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +8 -7
- package/docs/adr/0008-define-agent-operating-contract.md +2 -2
- package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
- package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -0
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
- package/docs/adr/0023-global-user-bindings.md +7 -7
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +9 -5
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
- package/docs/adr/README.md +4 -4
- package/docs/architecture.md +15 -10
- package/docs/binding-review-placement-design.md +1 -1
- package/docs/client-ui.md +9 -5
- package/docs/durability-design.md +12 -9
- package/docs/installation.md +27 -9
- package/docs/runtime-reference.md +23 -12
- package/docs/semantic-validation.md +7 -3
- package/docs/stateful-binding-updates-proposal.md +8 -4
- package/docs/user-bindings.en.md +10 -7
- package/docs/user-bindings.md +9 -6
- package/docs/value-wire.md +1 -1
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +53 -2
- package/index.js +39 -34
- package/internal/async-local-storage-intrinsics.js +43 -0
- package/internal/binding-declaration-contract.js +8 -0
- package/internal/binding-source-analysis.js +381 -64
- package/internal/binding-update-policy.js +7 -1
- package/internal/callable-source-facts.js +37 -1
- package/internal/cell-analysis.js +85 -17
- package/internal/compiler-intrinsics.js +9 -43
- package/internal/compiler-module-links.js +7 -4
- package/internal/config-spec.js +1 -1
- package/internal/cordis-tools-owner.js +20 -10
- package/internal/dynamic-environment-integration.js +8 -4
- package/internal/dynamic-environment-runtime.js +24 -3
- package/internal/dynamic-native-calls.js +4 -1
- package/internal/dynamic-native-runtime.js +15 -1
- package/internal/edit-transport-owner.js +80 -20
- package/internal/execution-seam-compat.js +192 -30
- package/internal/failure-reporting.js +38 -30
- package/internal/isolated-worker.js +17 -10
- package/internal/kernel-worker.js +645 -393
- package/internal/message-sources.js +14 -0
- package/internal/module-compilation.js +7 -5
- package/internal/module-transform-contract.js +32 -2
- package/internal/record-utils.js +24 -15
- package/internal/repl-memory-projection.js +10 -0
- package/internal/repl-scope-normalizer.js +72 -35
- package/internal/repl-value-observer.js +145 -46
- package/internal/runtime-config.js +62 -2
- package/internal/runtime-intrinsics.js +104 -0
- package/internal/runtime-messages.js +6 -4
- package/internal/session-cell-executor.js +26 -2
- package/internal/session-journal-recovery.js +267 -67
- package/internal/session-journal-schema.js +30 -13
- package/internal/session-journal.js +485 -44
- package/internal/session-log-view.js +5 -4
- package/internal/session-runtime.js +46 -21
- package/internal/session-state.js +7 -29
- package/internal/settings-compat.js +66 -1
- package/internal/source-position-map.js +38 -0
- package/internal/stateful-root-compiler.js +3 -1
- package/internal/typescript-transform.js +4 -2
- package/internal/user-binding-console-worker.js +115 -41
- package/internal/user-binding-draft-projection.js +3 -2
- package/internal/user-bindings-owner.js +2 -1
- package/internal/user-bindings.js +16 -3
- package/internal/validated-parse-repair.js +6 -2
- package/internal/value-wire.js +189 -94
- package/internal/worker-client.js +99 -8
- package/internal/worker-cwd-virtualization.js +82 -54
- package/internal/worker-environment.js +20 -0
- package/internal/worker-output-capture.js +187 -0
- package/internal/worker-output-fence.js +26 -0
- package/internal/worker-realm-surfaces.js +197 -0
- package/internal/worker-repl-realm.js +140 -0
- package/package.json +37 -4
- package/src/client-activity.js +12 -2
- package/src/client-authoring-view.js +121 -62
- package/src/client-console.js +4 -2
- package/src/client-copy.js +4 -2
- package/src/client-feature-gates.js +7 -9
- package/src/client-host-compat.js +104 -27
- package/src/client-repl-view.js +3 -1
- package/src/client-styles.js +12 -1
- package/src/client-tool-view.js +3 -1
- package/src/client-transport.js +2 -2
- package/src/client-workbench.js +4 -3
- package/src/client.js +56 -17
package/docs/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Getting Started
|
|
4
4
|
|
|
5
|
-
- [Installation](installation.md): npm, Git, source, tarball, development snapshot, and Desktop installation.
|
|
5
|
+
- [Installation](installation.md): npm, Git, source, tarball, development snapshot, and Desktop installation, plus upgrades, troubleshooting, and Session format migration.
|
|
6
6
|
- [Global binding guide](user-bindings.en.md) ([中文](user-bindings.md)): author, review, save, configure, and test reusable helpers.
|
|
7
7
|
- [Runtime Reference](runtime-reference.md): cell behavior, capability discovery, configuration, diagnostics, and limits.
|
|
8
8
|
- [Evaluation](evaluation.md): paired ordinary-task protocol, metrics, configuration, and limitations.
|
|
@@ -17,11 +17,14 @@
|
|
|
17
17
|
- [Global User Bindings](adr/0023-global-user-bindings.md): persistent helper ownership, model projection, runtime activation, recovery, and Agent-assisted authoring.
|
|
18
18
|
- [Binding Review Placement](binding-review-placement-design.md): the implemented full draft panel above the current composer, using the published input dock and one review controller.
|
|
19
19
|
- [Logical Binding Identities](adr/0025-use-versioned-logical-binding-identities.md): stateful declarations, scope, candidate publication and language generations.
|
|
20
|
+
- [Stateful Binding Updates](stateful-binding-updates-proposal.md): target stateful-computation semantics and their acceptance boundaries.
|
|
20
21
|
- [Architecture Decisions](adr/): stable design constraints and their consequences.
|
|
21
22
|
|
|
22
23
|
## Maintenance
|
|
23
24
|
|
|
24
25
|
- [Deterministic Verification](verification.md): complete checks, backend test concurrency, coverage, and focused diagnostics.
|
|
26
|
+
- [Incremental Review Verdict](adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md): predeclared parallel lanes, dependency-scoped invalidation, and the composite commit verdict.
|
|
27
|
+
- [Focused Verification](verification-optimization-work.md): bounded coverage diagnostics and their relationship to the final verification gate.
|
|
25
28
|
- [Semantic Validation](semantic-validation.md): pinned Test262 cases, dialect contracts, native differential matrices and their limits.
|
|
26
29
|
- [Publishing](publishing.md): release checks, package contents, permissions, and platform validation.
|
|
27
30
|
- [Client UI](client-ui.md): current UI boundary and reevaluation criteria.
|
|
@@ -23,9 +23,10 @@ edit_run_code({
|
|
|
23
23
|
|
|
24
24
|
PTC mode requests using the code-only direct-tool projection expose `[run_code, edit_run_code]` in
|
|
25
25
|
that order. The first conclusive prompt assembly creates one per-agent composition owner before any
|
|
26
|
-
plugin-owned presentation effect changes the nearest DSH scope. A non-empty `tools:
|
|
27
|
-
section proves `
|
|
28
|
-
|
|
26
|
+
plugin-owned presentation effect changes the nearest DSH scope. A non-empty `tools:ptc-only`
|
|
27
|
+
section proves `ptc`, an empty section proves `both`, and the preceding generation's
|
|
28
|
+
`tools:code-only` alias proves the same composition; the current section wins when both appear, and
|
|
29
|
+
only a missing owner signal permits a tool-shape inference. For a code composition, the same owner
|
|
29
30
|
registers `edit_run_code` through `agent.ctx.tools.register()` and uses the same scope's
|
|
30
31
|
`tools.presentAs('both')` to admit both direct calls. It retains both exact disposers and releases
|
|
31
32
|
registration and presentation together with the owning session, agent, or plugin. Native-mode and unrelated agent
|
|
@@ -105,10 +106,10 @@ would repeat unrelated policy context. Rewrite feedback and unrelated recovery t
|
|
|
105
106
|
own independently justified lifecycles.
|
|
106
107
|
|
|
107
108
|
A live `PTC-C001` at the exact source EOF may carry a validated one-call repair. The analyzer tries
|
|
108
|
-
only the
|
|
109
|
-
bindings, redeclaration mode, rewrite policy, and import state as the
|
|
110
|
-
candidate must complete preparation without a binding
|
|
111
|
-
must be able to express it from a bounded unique source suffix. The rejected call must also have a
|
|
109
|
+
only the 12 suffixes of one or two closing tokens drawn from `}`, `)`, and `]` against the same
|
|
110
|
+
binding catalog, reserved bindings, redeclaration mode, rewrite policy, and import state as the
|
|
111
|
+
rejected cell. Exactly one candidate across both lengths must complete preparation without a binding
|
|
112
|
+
collision, and the existing exact editor must be able to express it from a bounded unique source suffix. The rejected call must also have a
|
|
112
113
|
persistent event sequence. The diagnostic then renders those already-validated
|
|
113
114
|
`edit_run_code({ edits: [...], expected_target_call_seq })` arguments on one help line. At dispatch,
|
|
114
115
|
the precondition binds the proof to that rejected cell even if another matching source has become
|
|
@@ -6,11 +6,11 @@ PTC Plus needs a tracked instruction surface for product boundaries, source owne
|
|
|
6
6
|
|
|
7
7
|
## Decision
|
|
8
8
|
|
|
9
|
-
`AGENTS.md` contains only obligations whose subject is PTC Plus: DSH authority, runtime and journal invariants, public source ownership,
|
|
9
|
+
`AGENTS.md` contains only obligations whose subject is PTC Plus: DSH authority, runtime and journal invariants, public source ownership, checkout-local review state, deterministic verification, documentation parity, generated-material exclusions, and delivery safety. It links the canonical product and architecture documents rather than duplicating their detailed behavior.
|
|
10
10
|
|
|
11
11
|
Reusable maintenance methods remain outside tracked project sources. Local editor state and branch topology also remain checkout-owned. They may guide an individual maintainer, but project code, tests, hooks, and acceptance do not require their names, installation, or presence.
|
|
12
12
|
|
|
13
|
-
The tracked ledger
|
|
13
|
+
The tracked findings-ledger and review-plan templates and their lifecycle script remain because they enforce repository-specific commit invariants: unresolved findings cannot be committed, changed paths need declared semantic coverage, lane evidence follows dependency-scoped invalidation, and a verified source tree must match the prospective Git tree. The tracked hook is limited to those invariants and is installed explicitly.
|
|
14
14
|
|
|
15
15
|
## Alternatives considered
|
|
16
16
|
|
|
@@ -7,6 +7,13 @@ The persistent REPL contract must stay short enough for every request, while som
|
|
|
7
7
|
## Decision
|
|
8
8
|
|
|
9
9
|
PTC-owned dynamic information uses independently sourced, accepted DSH messages.
|
|
10
|
+
All newly emitted PTC messages, including authoring instructions and binding-action
|
|
11
|
+
receipts, use `source.kind: 'plugin:ptc-plus'`, the producer identity assigned by
|
|
12
|
+
DSH's official source migration. They never emit the retired `kind: 'plugin'`
|
|
13
|
+
wrapper. Readers retain that historical wrapper and recognize current Host
|
|
14
|
+
`runtime-context` sources as well as historical system-prompt sources. Source
|
|
15
|
+
admission is tested through the official persistence codec; an in-memory Session
|
|
16
|
+
alone does not establish that a message can be saved or resumed.
|
|
10
17
|
Unproved rewrite completion and Cordis recovery form a bounded `ptc-plus`
|
|
11
18
|
`snapshot` with named sections. Configured Global User Binding documentation uses
|
|
12
19
|
DSH's existing `catalog` form, independently of transient recovery state. A recovery tip is a `ptc-plus` `notice` whose
|
|
@@ -6,11 +6,11 @@ An ignored root `REVIEW_FINDINGS.md` prevents unresolved review findings from be
|
|
|
6
6
|
|
|
7
7
|
## Decision
|
|
8
8
|
|
|
9
|
-
`.agents/templates/REVIEW_FINDINGS.md` is the tracked schema owner for local review ledgers. Schema `dsh-review-findings/v3` separates each finding's stable required outcome, current implementation plan, accumulated resolution evidence, and optional accepted-disposition owner. A finding can be captured before diagnosis produces a plan; once a concrete correction is selected, the plan is recorded before implementation and kept synchronized when evidence changes the diagnosis, ownership, affected artifacts, or verification path. `resolutionEvidence` records completed facts rather than a chronological work transcript. A finding marked `resolved` retains its implemented plan. A finding marked `accepted` names a tracked document or HTTPS issue in `dispositionRef`; the local ledger cannot be the only copy of an intentionally retained limitation.
|
|
9
|
+
`.agents/templates/REVIEW_FINDINGS.md` is the tracked schema owner for local review ledgers. Schema `dsh-review-findings/v3` separates each finding's stable required outcome, current implementation plan, accumulated resolution evidence, and optional accepted-disposition owner. A finding can be captured before diagnosis produces a plan; once a concrete correction is selected, the plan is recorded before implementation and kept synchronized when evidence changes the diagnosis, ownership, affected artifacts, or verification path. `resolutionEvidence` records completed facts rather than a chronological work transcript. A finding marked `resolved` retains its implemented plan. A finding marked `accepted` names a tracked document or HTTPS issue in `dispositionRef`; a local reference must be one exact tracked file that remains present in the source candidate, not a directory, pathspec match, or index entry deleted from the checkout. The local ledger cannot be the only copy of an intentionally retained limitation. The separate `.agents/templates/REVIEW_PLAN.json` owns the independent-review partition described by ADR 0026; a review plan routes verification work and invalidation, while the findings ledger owns discovered defects and their resolution.
|
|
10
10
|
|
|
11
11
|
Review output and ledger mutation follow task authority. A read-only review reports records in the ledger's semantic fields without writing checkout state. Every implementation preserves an active ledger, but an ordinary change with no finding does not create one. `npm run review:new` creates the ignored root ledger only when a task authorizes capture or resolution of at least one finding; implementation imports those findings before editing their fixes. Archives move under Git's local `review-findings/archive` metadata path, so they cannot enter a commit and remain associated with the checkout.
|
|
12
12
|
|
|
13
|
-
Ledger validation, readiness, deterministic verification, archive, and commit proof are distinct states with one implementation. `npm run review:validate` checks schema and tracked-file safety while accepting a valid open ledger. `npm run review:gate` is read-only: it blocks an open ledger, validates durable owners for accepted findings, and never archives. `npm run verify` performs schema validation before syntax and coverage checks, so open-ledger work can collect evidence. `npm run check` applies the readiness gate first, then materializes the current tracked and non-ignored checkout through Git's clean and index semantics in a temporary Git administrative directory. Git selects that directory's default index through `--git-dir` and reads the target checkout through `--work-tree`; it never receives an alternate-index environment path that could fall back to the real repository index. The temporary object database reads the repository's objects through a relative alternates entry and stores new fingerprint objects locally. The resulting tree object ID includes canonical blobs, symlink identity, and file modes while remaining independent of staged content. Sparse checkouts retain skip-worktree paths from the `HEAD` baseline and apply clean conversion only to materialized paths, so an absent out-of-cone file is neither lost nor mistaken for a deletion. Every path discovered by Git is reapplied through NUL-delimited literal input. The command separately snapshots the ignored ledger, runs `verify`, rejects changed source or ledger state, and refreshes the proof before archiving a resolved ledger. Repository scripts that launch npm as a child use one command resolver: an invoking npm process supplies its CLI path and Node executable, while direct script invocation falls back to the platform npm executable. A successful later check without an active ledger also refreshes an older proof. The pre-commit hook rejects open and unverified resolved ledgers, requires a verification proof for the current `HEAD`, requires equal source and index tree fingerprints, and requires
|
|
13
|
+
Ledger validation, readiness, deterministic verification, archive, and commit proof are distinct states with one implementation. `npm run review:validate` checks schema and tracked-file safety while accepting a valid open ledger. `npm run review:gate` is read-only: it blocks an open ledger, validates durable owners for accepted findings, and never archives. `npm run verify` performs schema validation before syntax and coverage checks, so open-ledger work can collect evidence. `npm run check` applies the readiness gate first, then materializes the current tracked and non-ignored checkout through Git's clean and index semantics in a temporary Git administrative directory. Git selects that directory's default index through `--git-dir` and reads the target checkout through `--work-tree`; it never receives an alternate-index environment path that could fall back to the real repository index. The temporary object database reads the repository's objects through a relative alternates entry and stores new fingerprint objects locally. The resulting tree object ID includes canonical blobs, symlink identity, and file modes while remaining independent of staged content. Sparse checkouts retain skip-worktree paths from the `HEAD` baseline and apply clean conversion only to materialized paths, so an absent out-of-cone file is neither lost nor mistaken for a deletion. Every path discovered by Git is reapplied through NUL-delimited literal input. The command separately snapshots the ignored ledger, runs `verify`, rejects changed source or ledger state, and refreshes the proof before archiving a resolved ledger. Repository scripts that launch npm as a child use one command resolver: an invoking npm process supplies its CLI path and Node executable, while direct script invocation falls back to the platform npm executable. A successful later check without an active ledger also refreshes an older proof. The pre-commit hook rejects open and unverified resolved ledgers, requires a verification proof for the current `HEAD`, requires equal source and index tree fingerprints, and requires the composite independent-review plan, lane verdicts, and final verdict defined by ADR 0026. Staging the complete verified tree permits repeated commit attempts; partial staging, additional staged content, mode-only changes, later checkout changes, uncovered paths, or invalidated lane dependencies require reconciliation before commit.
|
|
14
14
|
|
|
15
15
|
Hook installation is explicit. `npm run hooks:install` selects the tracked hook path only when the effective `core.hooksPath` is unset or already `.githooks`. Before the first switch from Git's default hooks directory, installation rejects any non-sample hook file there; it also rejects an incompatible configured path instead of disabling another hook owner. Dependency installation has no Git-configuration side effect.
|
|
16
16
|
|
|
@@ -40,4 +40,4 @@ Hook installation is explicit. `npm run hooks:install` selects the tracked hook
|
|
|
40
40
|
|
|
41
41
|
## Consequences
|
|
42
42
|
|
|
43
|
-
Review findings have an explicit `reported -> authorized capture -> planned -> implemented and verified -> proof and archive -> matching commit -> proof retirement` lifecycle while retaining `open` and `resolved` as file-level states. An open ledger uses `verify` for deterministic evidence collection and fails `check` before its full gate runs; it also blocks commits. A resolved ledger remains active until verification succeeds against an unchanged source fingerprint, and commit readiness additionally requires the prospective index to match that fingerprint.
|
|
43
|
+
Review findings have an explicit `reported -> authorized capture -> planned -> implemented and verified -> proof and archive -> matching commit -> proof retirement` lifecycle while retaining `open` and `resolved` as file-level states. An open ledger uses `verify` for deterministic evidence collection and fails `check` before its full gate runs; it also blocks commits. A resolved ledger remains active until verification succeeds against an unchanged source fingerprint, and commit readiness additionally requires the prospective index to match that fingerprint. Independent review lanes have their own `plan -> parallel review -> dependency-scoped retry -> deterministic proof -> composite verdict` lifecycle and do not replace finding disposition. Both local records stay outside source history; accepted limitations and durable architecture remain in tracked owners. Hook installation is an explicit checkout setup decision rather than a dependency-install side effect.
|
|
@@ -8,17 +8,17 @@ Accepted
|
|
|
8
8
|
|
|
9
9
|
## Context
|
|
10
10
|
|
|
11
|
-
PTC Plus 0.1.0 deliberately had no Client UI. The runtime surface was the DSH PTC mode `run_code`/`edit_run_code` transport, and adding a settings bundle was judged not to improve the core REPL path. The plugin also lacked a user-facing way to disable its runtime without removing the Cordis entry, and enabling/disabling state was invisible outside the session log. PTC Plus requires a plugin settings UI containing every Host Config field, a kill switch, and an explicit enabled marker. DSH's public settings service registers a namespace
|
|
11
|
+
PTC Plus 0.1.0 deliberately had no Client UI. The runtime surface was the DSH PTC mode `run_code`/`edit_run_code` transport, and adding a settings bundle was judged not to improve the core REPL path. The plugin also lacked a user-facing way to disable its runtime without removing the Cordis entry, and enabling/disabling state was invisible outside the session log. PTC Plus requires a plugin settings UI containing every Host Config field, a kill switch, and an explicit enabled marker. DSH's public settings service registers a namespace; the browser half contributes a card through the generation's settings seat, and the client reads the installed generation's settings transport (`configForms` on the current alpha, `settingsScope` on the preceding RC).
|
|
12
12
|
|
|
13
13
|
## Decision
|
|
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 belongs to plugin configuration. Current DSH renders a bundle's own configuration on the sidebar Plugins → Installed 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. Preceding Hosts use `settings.plugin.item` (keyed, root) in the Settings panel, keyed by the served namespace. `src/client-host-compat.js` publishes to these plugin configuration seats through `ctx.slots.inject`, which waits until the owning page declares its slot. General settings may coexist with plugin configuration and is not a seat for this 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 names the observed native plugin configuration seat. The package's top-level `icon` field points to a packaged SVG; DSH owns loading and displaying it on the installed bundle and row pages.
|
|
18
18
|
|
|
19
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
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.
|
|
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. On the generations that expose a section installer, 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. The newest settings generation exposes no section installer and serves only fields marked volatile in the plugin's exported `Config`; the Host provides the form and commits volatile-only changes into the live references, notifying the owning fiber through the public `loader/volatile-update` event. The plugin subscribes and reconciles through its existing update path instead of running a per-field settings watch. 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.
|
|
22
22
|
|
|
23
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.
|
|
24
24
|
|
|
@@ -24,6 +24,8 @@ Cordis tools remain native program bindings under `tools.*`. The PTC code-only d
|
|
|
24
24
|
|
|
25
25
|
Cordis definitions and Runs are process-local, while PTC journals replay every settled program binding as a recorded value without redispatch. On the first prompt for an agent or a new Cordis enable generation, PTC Plus derives whether normalized journals contain a Cordis transcript. If they do, it emits the fixed `tools:ptc-plus-cordis-recovery` runtime context: recorded values remain journal data, but the model must perform a live read-only Cordis Inspect before relying on prior IDs, state, approvals, or capability observations. The context remains until a newly settled successful `cordis_inspect*` call appears in the session log. It neither asserts that live state was lost nor reruns a mutating call.
|
|
26
26
|
|
|
27
|
+
Companion Skill resolution is capability-based. The generations that discover presets from their composition directories publish an absolute composition path for the shipped `cordis` preset, and PTC Plus reads the sibling `skills` root. A generation that declares presets without publishing any composition path publishes no companion Skill root through `agentPresets` at all: PTC Plus reports that missing publication as an unsupported surface, naming the surface and its consequence rather than a shape failure of a healthy host, and keeps the existing rollback or single-warning withdrawal instead of assuming a directory. An unusable preset (`broken`) is reported as unavailable before any path shape is considered.
|
|
28
|
+
|
|
27
29
|
## Alternatives Considered
|
|
28
30
|
|
|
29
31
|
1. Add the Cordis row to the PTC preset. Rejected: PTC Plus is installed as a host plugin and does not own or fork DSH's shipped preset files; a plugin setting keeps the optional mount within the plugin's ownership boundary.
|
|
@@ -28,12 +28,23 @@ artifact rewrite API when one exists. Until then, PTC Plus must not rewrite
|
|
|
28
28
|
files obtained from private storage paths or race the persistence provider's
|
|
29
29
|
append/flush queue. Legacy `ptc-plus/recovery-boundary` artifacts therefore
|
|
30
30
|
remain an explicit, startup-before-restore migration concern rather than a
|
|
31
|
-
runtime event handler concern.
|
|
31
|
+
runtime event handler concern. The dedicated retired-event converter accepts
|
|
32
|
+
only a Session format 0 artifact with a contiguous zero-based source identity graph, removes the retired events
|
|
33
|
+
and rewrites every supported Host and PTC sequence relation through one closed
|
|
34
|
+
old-to-new map. Before writing, it validates current-surface replacement and
|
|
35
|
+
compaction ranges, provenance, command and title sources, and complete
|
|
36
|
+
recovery-boundary application; invalid or unknown relations fail closed. If a
|
|
37
|
+
header records a seeded session, the converter also maps its exact inherited-
|
|
38
|
+
event cut across removed boundary events so child-owned events cannot become
|
|
39
|
+
inherited.
|
|
32
40
|
|
|
33
41
|
Host format upgrades are also explicit pre-restore work. The migration CLI may
|
|
34
42
|
resolve the selected Host's public Session format catalog and use its strict
|
|
35
|
-
conversion and current validation.
|
|
36
|
-
|
|
43
|
+
conversion and current validation. When the selected catalog requires
|
|
44
|
+
parent-specific child evidence, the caller supplies the complete direct-child
|
|
45
|
+
facts or explicitly declares that the artifact has no children; the CLI does
|
|
46
|
+
not infer an empty child set. PTC result metadata remains opaque to that catalog.
|
|
47
|
+
The CLI therefore proves an ordered one-to-one relation between the
|
|
37
48
|
unchanged tool records, remaps only PTC-owned sequence references, and validates
|
|
38
49
|
the resulting timeline. Original call arguments and recorded effects are never
|
|
39
50
|
rewritten or executed. Ambiguous records, invalid PTC evidence and mixed retired
|
|
@@ -6,15 +6,15 @@ Repeated project helpers are useful across sessions, but ordinary REPL bindings
|
|
|
6
6
|
|
|
7
7
|
## Decision
|
|
8
8
|
|
|
9
|
-
Global User Bindings is
|
|
9
|
+
Global User Bindings is a PTC Plus capability controlled by `userBindingsEnabled`, which defaults to `true` so its management and authoring entry points are available on installation. An explicit `false` remains authoritative, including across upgrades; omission uses the current default. This feature switch does not change any saved entry's enabled state, create helpers, or save Agent drafts automatically. 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
10
|
|
|
11
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
12
|
|
|
13
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
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.
|
|
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. The projection preserves unshadowed environment type names and emits the referenced closure of local interfaces, type aliases, enums and classes in an entry-private declaration namespace. Public class instance fields, constructor parameter properties, methods and accessors are part of the projected class shape. Imported aliases that cannot produce a self-contained declaration are rejected during normalization; qualified import types retain their module reference. 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
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
|
|
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 historical declaration string limit of 8192 code units; custom declarations are excluded from model and 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
18
|
|
|
19
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
20
|
|
|
@@ -30,7 +30,7 @@ The workbench keeps a save revision with the loaded source. Catalog-only changes
|
|
|
30
30
|
|
|
31
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
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
|
|
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 one of `amaro@1.1.11`, `stateful-module-v1+amaro@1.1.11`, or `stateful-module-v2+amaro@1.1.11` and includes that exact identity in the snapshot fingerprint. Both stateful transforms apply shared logical scope rules before TypeScript transformation; v2 additionally resolves program namespaces through compiled logical roots. The legacy transform retains its historical lowering, and v1 retains its worker-global program bridge. 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
34
|
|
|
35
35
|
Version 1 snapshots did not record the native compiler. A matching source,
|
|
36
36
|
declaration, durability classification or cell completion does not prove matching
|
|
@@ -53,13 +53,13 @@ Configured prompts and interfaces never enter `PromptAssembly.sections` or its v
|
|
|
53
53
|
|
|
54
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
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.
|
|
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. Current v2 binding modules resolve request namespaces through their compiled logical root; each cell receives a namespace view bound to that cell's lease. Historical v1 and legacy modules use their recorded worker-global bridge, whose calls resolve the AsyncLocalStorage execution lease that reached them. Neither path lets a continuation from a completed cell borrow a later cell's capabilities. A historical bridge that collides with an existing worker global fails before 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
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
|
|
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 until that worker ends; historical modules also retain their dynamic program-namespace bridge. Failed modules that expose no value release their temporary mapping. When no historical binding has ever been exposed, disabled or empty activation installs no bridge; if every attempted historical activation fails, any bridge installed for that attempt is removed before the cell executes.
|
|
59
59
|
|
|
60
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
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.
|
|
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. Its commands and binding modules share that worker's native Node realm, matching the session kernel's intrinsic and `globalThis` contract without sharing session state. 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
63
|
|
|
64
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
65
|
|
|
@@ -36,11 +36,11 @@ New-generation cell returns leave source scopes through a private labelled break
|
|
|
36
36
|
|
|
37
37
|
## Language and recovery generations
|
|
38
38
|
|
|
39
|
-
Journal version
|
|
39
|
+
Journal version 10 requires the closed `languageSemantics` and `moduleTransform` fields. The language field records `legacy-v1`, `stateful-v1` or `protected-v1`; the transform field records the exact parser, compiler and lowering generation, and only compatible pairs are accepted. Versions 1 through 8 migrate to `legacy-v1` with the frozen legacy transform while preserving their independently recorded binding, rewrite, module, reuse and shadow policies. Version 9 keeps its recorded language and maps it to the transform shipped with that journal generation: stateful uses `stateful-module-v1`, protected uses `protected-module-v1`, and legacy uses the legacy transform. Replay consumes that exact transform; live requests record the transform selected by 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
40
|
|
|
41
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
42
|
|
|
43
|
-
Binding snapshots
|
|
43
|
+
Binding snapshots distinguish `amaro@1.1.11`, `stateful-module-v1+amaro@1.1.11`, and `stateful-module-v2+amaro@1.1.11`. Source metadata and fingerprints remain canonical; an unsupported transform cannot be silently replaced with the current transform. The v2 generation resolves program namespaces through compiled logical roots, while v1 and legacy snapshots retain their historical worker-global bridge. 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
44
|
|
|
45
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
46
|
|
|
@@ -58,7 +58,7 @@ Eval checks lexical conflicts before initializing its ordered declaration plan.
|
|
|
58
58
|
|
|
59
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
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.
|
|
61
|
+
All global and prototype properties retain their original native identities, including Function.prototype.toString. Each session kernel and binding workbench uses its worker's native Node realm for REPL evaluation and Node module execution. Cell code, managed imports, provided require and binding modules therefore share intrinsic identities and `globalThis`; user-created VM contexts and independent runtimes keep their own realms. 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. Compiler and source analysis record ambient reads for durability without interposing getters on user-visible globals. `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
62
|
|
|
63
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
64
|
|
|
@@ -1,21 +1,37 @@
|
|
|
1
|
-
# Persist
|
|
1
|
+
# Persist incremental review lanes against the verified candidate
|
|
2
2
|
|
|
3
3
|
## Problem
|
|
4
4
|
|
|
5
|
-
A complete-tree independent review
|
|
5
|
+
A single complete-tree independent review bound a clean conclusion to one tree, but every correction invalidated the whole conclusion. Earlier parallel reviews remained diagnostic transcripts even when a fix could not affect their owners, inputs, assumptions, or consumers. This forced repeated complete review, discouraged early parallelism, and allowed review boundaries to drift during execution without a machine-visible plan. Retaining every earlier conclusion without dependency information would be unsound for the opposite reason: a change to a shared owner can invalidate consumers whose own files did not change.
|
|
6
6
|
|
|
7
7
|
## Decision
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Independent review starts from a checkout-local `REVIEW_PLAN.json`, created from `.agents/templates/REVIEW_PLAN.json` and recorded under Git metadata before reviewers run. This root file is the sole active plan source. Schema `dsh-review-plan/v1` declares obligations and semantic lanes. The five standard obligation IDs remain mandatory; a plan may add change-specific obligations but cannot replace the standard review surface. Each lane has a stable ID, scope, exact file or directory-prefix inputs, upstream lane dependencies, assigned obligations, owners, consumers, and discriminating counterexamples. Obligations are either covered by at least one lane or explicitly excluded with a non-applicability reason. Every path changed between the plan base and the current source tree must match at least one lane; the implementation never silently expands a lane to absorb an unplanned path.
|
|
10
|
+
|
|
11
|
+
The recorded plan resolves its base to a commit that is an ancestor of current `HEAD`. Before review state can use or record a clean conclusion, it parses the active file and compares its canonical hash with the recorded plan. Missing, malformed, or semantically different active content fails closed before an old clean conclusion can authorize delivery; JSON formatting alone is immaterial. Negative review disposition is fail-closed in the other direction: it can retire recorded authorization even while the active plan or current source has drifted. A revision reuses the recorded base unless a base is explicitly supplied. A lane fingerprint hashes the lane declaration, its persistent invalidation `{epoch, revision}`, the canonical Git mode and blob identity including raw pathname bytes of every current source-tree entry selected by that lane's path rules, and the fingerprints of all declared upstream lanes. Plan path rules match those raw bytes as UTF-8 encoded exact names or directory prefixes; diagnostics encode undecodable paths reversibly rather than confusing their identities. The dependency graph must be acyclic. A change to one lane's declaration, generation, or selected content changes that lane fingerprint and recursively changes every dependent fingerprint, while unrelated lanes retain their identity. Re-recording a revised plan preserves only lane verdicts whose base, lane identity, evidence identity, and resulting fingerprint still match; this makes a boundary revision explicit without discarding unrelated evidence.
|
|
12
|
+
|
|
13
|
+
Reviewers may run concurrently from the fingerprints printed by `review:plan` or `review:status`. `recordReviewLaneVerdict` retains the captured `HEAD` as provenance and accepts a clean report when the captured lane fingerprint still matches. An unrelated concurrent edit or `HEAD` advance therefore does not reject the report. A changed direct or upstream input does reject it. A report ending in findings or `VERDICT: INCOMPLETE` is inspected before current-candidate equality and retires that lane and its declared dependent closure from the recorded plan, not unrelated lanes. Temporary source or active-plan drift therefore cannot make rejected evidence effective again after restoring the earlier candidate. Each accepted report is copied under `review-findings/evidence`, hashed, and referenced by a `dsh-review-lane-verdict/v1` record whose internal lane ID and evidence name must match its declared lane and fingerprint.
|
|
14
|
+
|
|
15
|
+
Plan recording, lane verdict recording or retirement, aggregate finalization, clearing, and pre-commit authorization are serialized by one atomic repository-local lock under Git metadata. The lock records the owner PID and a unique release token and bounds acquisition; it never automatically reclaims an owner because no portable pathname operation can prove and remove that exact generation without an ABA race. Crash recovery is an explicit operator action after confirming no review-state command is active, scoped to the exact `review-findings/state.lock` path resolved by Git. A negative report ordered after an older clean recorder advances a persistent per-lane invalidation revision within a random epoch even if the lane is temporarily absent from the current plan, before retiring its current closure. Reaching `Number.MAX_SAFE_INTEGER` rotates to a different random epoch at revision zero, preserving both invalidation and future progress. The absent lane must already have a retained tuple; an unknown ID cannot create one, and every active lane requires an explicit tuple. The tuple is part of that lane's fingerprint and therefore every dependent fingerprint; reports captured before retirement cannot overwrite it after the lane is re-added. Generations survive normal clearing and lane removal. Missing, partial, or malformed generation state blocks authorization; clear preserves valid state but removes damaged state, and explicit plan reconstruction creates a new epoch so a damaged lifecycle cannot restore an older authorization identity. A new clean review captured after retirement remains valid. Status is diagnostic and need not hold the mutation lock. Because the active plan, source tree, index, proof, and evidence can also be changed outside these commands, finalization and pre-commit obtain a stable final observation of their relevant identities before persisting or returning authorization.
|
|
16
|
+
|
|
17
|
+
After all lanes are effective, `npm run check` produces the existing whole-source verification proof for current `{HEAD, fingerprint}`. `review:finalize` writes `dsh-review-verdict/v2`, containing that whole-tree identity, the base, the complete plan hash, and every effective lane fingerprint and evidence hash. The pre-commit hook separately requires an absent or terminal findings ledger, a proof for current `HEAD`, equal source and index fingerprints, complete changed-path coverage, an effective verdict for every lane, intact evidence, and a composite verdict exactly matching that plan and proof. Advancing `HEAD` retires the proof and aggregate verdict. Source or index drift makes both records ineffective while the mismatch remains; restoring their exact candidate identity makes them effective again, while accepting a changed candidate requires another deterministic proof and aggregate finalization. Lane evidence remains reusable when its semantic declaration, direct inputs, upstream fingerprints, base ancestry, and evidence hash still hold.
|
|
18
|
+
|
|
19
|
+
`REVIEW_FINDINGS.md`, `REVIEW_PLAN.json`, reports, lane records, plans, proofs, and aggregate verdicts remain checkout-local. The two root working files are installed into `.git/info/exclude`; reports must stay outside the checkout or be ignored. The old `dsh-review-verdict/v1` single-report format and `verdict` command are rejected rather than treated as composite evidence.
|
|
10
20
|
|
|
11
21
|
## Alternatives considered
|
|
12
22
|
|
|
13
|
-
**
|
|
23
|
+
**Repeat one complete-tree review after every fix.** This binds review to exact bytes but discards unaffected independent evidence and serializes work that has explicit semantic boundaries.
|
|
24
|
+
|
|
25
|
+
**Partition only by changed file paths.** File ownership is mechanically convenient, but intrinsic handling, declaration publication, callable reconstruction, module contracts, and recovery cross file boundaries. Paths select fingerprint inputs; semantic scope, dependencies, owners, consumers, and counterexamples define what the reviewer must establish.
|
|
26
|
+
|
|
27
|
+
**Keep every clean lane unless one of its own files changes.** This misses shared-owner and contract changes. Upstream lane fingerprints enter dependent fingerprints so invalidation follows the declared semantic graph.
|
|
28
|
+
|
|
29
|
+
**Never permit plan changes after review starts.** A fixed incorrect boundary would preserve a known coverage gap. Plan revisions are allowed but versioned by content, must cover every changed path, and invalidate only lanes whose declarations or dependency inputs changed.
|
|
14
30
|
|
|
15
|
-
**
|
|
31
|
+
**Automatically add uncovered paths to a nearby lane.** This avoids stopping the workflow, but a file path does not prove semantic ownership. The gate reports uncovered paths and requires an explicit plan revision.
|
|
16
32
|
|
|
17
|
-
**Store
|
|
33
|
+
**Store lane evidence without a final whole-tree proof.** Lane fingerprints establish review inputs, not deterministic test success or exact prospective commit contents. The composite verdict therefore remains paired with `npm run check` and the index-tree gate.
|
|
18
34
|
|
|
19
35
|
## Consequences
|
|
20
36
|
|
|
21
|
-
|
|
37
|
+
Independent review can begin in parallel after focused checks, and a correction normally reruns only the failed lane and its dependency closure. A small change may use one lane, so partition overhead remains proportional to the work. The plan author must identify semantic dependencies accurately; automation proves declared coverage and evidence identity, not that the declarations are intellectually complete. A newly discovered dependency is a plan defect that must be recorded before review continues. Finalization still requires one stable-tree `npm run check`, but it no longer requires another reviewer to repeat every already effective lane.
|
|
@@ -2,20 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Problem
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Running the session kernel in a `node:worker_threads` worker owned by the DSH process makes reclamation of a stuck or unresponsive kernel depend on `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` and ends the DSH process. The available Windows reproductions and stacks identify the defect on those Node lines. Reclamation therefore cannot rely on downgrading Node, retrying CI, or treating an in-host kill as safe; the process boundary must contain the failure without requiring proof about other Node lines.
|
|
6
6
|
|
|
7
7
|
## Decision
|
|
8
8
|
|
|
9
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
10
|
|
|
11
|
-
`internal/isolated-worker.js` exposes the worker-shaped transport
|
|
11
|
+
`internal/isolated-worker.js` exposes the worker-shaped transport expected by callers: `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
12
|
|
|
13
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
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: ... })`.
|
|
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: ... })`. When the Host executable is Electron, `IsolatedWorker` overlays `ELECTRON_RUN_AS_NODE=1` only on the fork environment so the packaged executable starts the helper module instead of reopening the desktop application. The inner user Worker and the child processes it creates still receive the original projected snapshot, without that helper-only override. 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
16
|
|
|
17
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
18
|
|
|
19
|
+
Helper stdout and stderr are part of cell settlement, not unowned process noise. For every run, the Host chooses a nonce and arms one output round before posting source. The kernel writes nonce-bearing start markers to both native descriptors and waits for the Host to observe both before executing the cell. It writes matching end markers after evaluation, pending program calls and value encoding settle, but before posting `done`. The Host releases `done` only after both end markers arrive. Bytes between those fences, including direct fd 1/2 writes and inherited child stdio, belong to that physical run and share its aggregate output budget. Structured console logs retain their own order; stdout and stderr each retain pipe order, but the merged presentation does not claim a total chronology across the independent control and descriptor channels.
|
|
20
|
+
|
|
21
|
+
Bytes before a new start fence prove that a settled run still produced descriptor output, so the new run is rejected and the worker is reset rather than attaching those bytes to it. A structured console or stream callback keeps its scheduling execution identity through the worker's private async-local store; output after that execution closes sends a `late-output` control event and likewise rejects any affected run. Bare descriptor bytes carry no scheduling identity, so bytes that actually arrive between a later run's acknowledged start and end fences follow the physical-arrival rule instead of being attributed to an unknowable callback. Fence mismatch, overlap, excess output or out-of-round output is an explicit output-attribution failure and resets the transport. The first idle out-of-round chunk closes capture and starts reset immediately; when no request is active, the Host retains one failure for the next execution instead of continuing to drain an unbounded producer without a diagnostic.
|
|
22
|
+
|
|
19
23
|
## Alternatives considered
|
|
20
24
|
|
|
21
25
|
**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.
|
|
@@ -30,8 +34,8 @@ The helper answers an on-demand utilization-sample request from its own loop, an
|
|
|
30
34
|
|
|
31
35
|
## Consequences
|
|
32
36
|
|
|
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
|
|
37
|
+
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 keeps session-worker teardown out of DSH, but it does not repair, characterize, or claim the same cause for every upstream Node teardown path.
|
|
34
38
|
|
|
35
39
|
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
40
|
|
|
37
|
-
Environment provenance is
|
|
41
|
+
Environment provenance is explicit in code: the user Worker receives the caller's projected environment, while coverage instrumentation and an Electron Host's Node startup override stay on the helper side. A reclamation refusal stays owned and observable instead of being reported as success or surfacing as an unhandled rejection.
|
|
@@ -30,12 +30,23 @@ generation is.
|
|
|
30
30
|
**Selection is a live capability.** The plugin's static injection names only the
|
|
31
31
|
services both generations provide; the seam owner waits through `inject` for
|
|
32
32
|
whichever execution service the host registers, in a scope that also requires the
|
|
33
|
-
plugin's own services, and
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
plugin's own services, and owns one live attachment at a time. When both are registered, the current
|
|
34
|
+
generation wins, including when it appears after a preceding-generation attachment.
|
|
35
|
+
That transition first disposes the legacy injected fiber and its complete effect
|
|
36
|
+
tree, then attaches in the current service's scope; cleanup from the retired scope
|
|
37
|
+
cannot clear the new owner. If that attach fails, the owner marks the current
|
|
38
|
+
service object unusable, re-arms the preceding-generation attachment
|
|
39
|
+
immediately, and reports the failure instead of leaving the plugin with no
|
|
40
|
+
execution seam; the failed object is never retried, while a replacement current
|
|
41
|
+
service supersedes the restored attachment. The preceding generation therefore
|
|
42
|
+
attaches only when the current one is absent or is the recorded unusable object.
|
|
43
|
+
Attachment runs in the injected scope, so
|
|
36
44
|
the plugin is unloaded when the host unregisters that service and reattaches when
|
|
37
45
|
it is registered again. A service that satisfies the name but not the contract
|
|
38
|
-
(`run` or `resolve` missing) is reported as a load
|
|
46
|
+
(`run` or `resolve` missing) follows the same path and is reported as a load
|
|
47
|
+
failure naming the service; when a preceding-generation attachment exists, the
|
|
48
|
+
report accompanies its restoration so `run_code` never silently returns to the
|
|
49
|
+
host provider.
|
|
39
50
|
|
|
40
51
|
**Execution is one neutral call shape.** The owner publishes `invokeUpstream`,
|
|
41
52
|
which reproduces each generation's own call shape — directly for
|
|
@@ -93,8 +104,17 @@ escalation it thinks it is offering.
|
|
|
93
104
|
The plugin activates on both the current and the preceding host generation, and
|
|
94
105
|
one test owner covers the selection rules, the neutral call shape, the descriptor
|
|
95
106
|
takeover, and the release semantics for both. A host that registers neither
|
|
96
|
-
execution service
|
|
97
|
-
|
|
107
|
+
execution service cannot be served, and its activation settles with a bounded
|
|
108
|
+
diagnostic naming the missing seam — `no host execution seam (ptcRuntime or
|
|
109
|
+
codeRuntime) is registered` — after the attachment window instead of staying
|
|
110
|
+
pending: the host awaits loader settlement before it becomes usable, so an
|
|
111
|
+
unbounded wait would park every agent, native tool, and unrelated plugin rather
|
|
112
|
+
than failing this entry.
|
|
113
|
+
|
|
114
|
+
A current-generation service that fails to attach cannot leave the plugin
|
|
115
|
+
unattached while the preceding service is still registered: the preceding
|
|
116
|
+
attachment is restored and the failure is reported through the plugin's own
|
|
117
|
+
logger in addition to the injected fiber's activation result.
|
|
98
118
|
|
|
99
119
|
`run_code` on the current host no longer advertises a per-call deadline or file
|
|
100
120
|
sandbox, and DSH rejects those arguments with its own message instead of the
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Close semantic obligations by owner
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
Focused tests and coverage can prove their selected examples executed, but they cannot show that every plugin-owned semantic transformation or execution boundary has an owner, contract and independent oracle. Adding more examples to a single compiler or realm matrix leaves module, transport, recovery, host and client boundaries outside that matrix, while treating all Node behavior as one fidelity surface erases intentional PTC differences and bounded external assumptions. An unregistered source boundary therefore needs a deterministic failure before review, not a reviewer remembering to extend a list.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
`semantic-obligations.json` is the tracked graph of plugin-owned semantic boundaries. Each obligation records one semantic owner, exact implementation files, disposition, owning contract, preserved observables or declared differences, execution entries, lifecycle states, language or data generations, platform bounds, consumers, oracle, executable evidence and upstream obligations. Dispositions distinguish behavior preserved from an external owner, intentional PTC differences and bounded external surfaces; an omitted or uncertain obligation is unknown and fails validation rather than being relabeled as preserved.
|
|
10
|
+
|
|
11
|
+
`scripts/semantic-obligations.mjs` validates the graph before build and coverage in `npm run verify`. The exact inventory covers every JavaScript implementation file in `internal/` and `src/`, the public `index.js` and `client.js` entries, and the journal migration entry, with exactly one semantic owner per file. Adding, removing or assigning a source twice fails closed until the graph changes. Contract and evidence files must exist, every obligation needs executable test evidence, dependencies must resolve without cycles, journal versions must remain a contiguous recorded range, and declared language and module generations must equal their executable owners. The command emits counts for preserved, intentional-difference, bounded-external and unknown obligations; zero unknown means closure over the declared plugin source boundary, not equivalence with all current or future Node or DSH behavior.
|
|
12
|
+
|
|
13
|
+
The graph complements, rather than replaces, focused tests, coverage and independent review lanes. A boundary-specific registry can enforce a stronger relation where the implementation supplies one: worker realm mutations use their classified installation entry and AST check, journal generations use their closed schema, and language/module generations use exported sets. Other records bind exact owner files and evidence so a new boundary cannot enter the tracked source inventory silently; review still evaluates whether an edit inside an existing owner changes its obligation or dependencies.
|
|
14
|
+
|
|
15
|
+
`docs/semantic-validation.md` explains how to choose an oracle and extend evidence. `REVIEW_PLAN.json` remains the checkout-local snapshot partition: its lanes may split or combine tracked obligations for review cost, but must preserve the graph's owner dependencies and changed-path coverage. A clean graph report establishes declared coverage only; deterministic verification and clean independent lane verdicts remain separate delivery requirements.
|
|
16
|
+
|
|
17
|
+
## Alternatives considered
|
|
18
|
+
|
|
19
|
+
**Expand the Test262 or compiler matrix until it appears comprehensive.** Those matrices are strong independent oracles for selected language behavior, but they do not own worker transport, program bindings, journals, host integration or client projection and cannot detect an omitted entry outside their axes.
|
|
20
|
+
|
|
21
|
+
**Use line and function coverage as the global model.** Coverage shows code execution, not which observable must be preserved, whether a difference is contractual, or whether the expected value came from an independent source.
|
|
22
|
+
|
|
23
|
+
**Make the worker realm inventory the global registry.** Realm mutation is only one boundary. Folding compiler, module, wire, recovery and host behavior into Node surface rows would obscure their owners and recreate a manually sampled API matrix.
|
|
24
|
+
|
|
25
|
+
**Derive ownership only from each temporary review plan.** Review plans are candidate-specific and checkout-local. They cannot serve as the durable source inventory or contract graph required before a plan exists.
|
|
26
|
+
|
|
27
|
+
## Consequences
|
|
28
|
+
|
|
29
|
+
New implementation files and generation changes require an explicit obligation update, and stale evidence blocks the normal gate early. The graph is intentionally bounded to plugin-owned JavaScript sources and declared platform contracts; dependency internals, arbitrary Node APIs and future DSH behavior remain outside the equivalence claim. Exact file assignment adds maintenance cost, but makes source growth and ownership movement reviewable instead of silent.
|
package/docs/adr/README.md
CHANGED
|
@@ -5,14 +5,14 @@ This directory records stable design constraints for PTC Plus. An ADR states a d
|
|
|
5
5
|
## Rules
|
|
6
6
|
|
|
7
7
|
- **A new or changed durable decision updates or adds an ADR** in the same change. Record architecture, public or cross-module contracts, persisted or wire formats, and process or tooling choices that a maintainer may reasonably revisit. A difficult or non-trivial implementation of an existing decision does not by itself require an ADR.
|
|
8
|
-
- **`## Alternatives considered` is mandatory.** Name each genuine alternative and why it lost, one bold-led paragraph per alternative. A decision recorded without what it beat invites re-litigation.
|
|
8
|
+
- **`## Alternatives considered` is mandatory for a new or changed ADR.** Name each genuine alternative and why it lost, one bold-led paragraph per alternative. A decision recorded without what it beat invites re-litigation. Entries written before this rule may use the shorter `## Alternatives` heading; repair them when they are next touched.
|
|
9
9
|
- **Naming.** Files follow `NNNN-kebab-case-title.md`. Keep the number sequence monotonic; never reuse a number.
|
|
10
|
-
- **Format.** One physical line per paragraph:
|
|
10
|
+
- **Format.** One physical line per paragraph in new entries, or the entry's own consistent wrapping when it already wraps; the headings and their order are what make an ADR readable:
|
|
11
11
|
- `# <title>` — the decision in one line
|
|
12
|
-
- `## Problem` — the motivation, written to stand without the solution
|
|
12
|
+
- `## Problem` or `## Context` — the motivation, written to stand without the solution
|
|
13
13
|
- `## Decision` — shipped reality in the present tense; keep it current with what actually shipped
|
|
14
14
|
- bespoke technical sections as needed
|
|
15
15
|
- `## Alternatives considered` — mandatory
|
|
16
|
-
- `## Consequences` — what the trade-off cost and bought
|
|
16
|
+
- `## Consequences` — what the trade-off cost and bought; older entries may carry `## Verification` instead or omit it
|
|
17
17
|
- **Supersession.** When a later decision replaces an ADR, fold every unique rationale, alternative, and consequence into the current owner and repair inbound links before deleting the old file. Partial supersession keeps both files cross-linked. Git history is not the only copy of rationale.
|
|
18
18
|
- **Kept current.** When the code later moves a file, renames a surface, or changes a default, update the owning ADR in the same change (facts only, not the decision itself).
|