pi-ptc-subagents 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,119 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ (nothing yet)
11
+
12
+ ## [1.5.1] - 2026-10-08
13
+
14
+ ### Fixed
15
+
16
+ - **Two claims 1.5.0's changelog made about this repository were false, and are now corrected in
17
+ the repository itself.** 1.5.0's tarball still carries the original wording — published tarballs
18
+ are immutable — so the correction lives here, in `main`, and in
19
+ [ADR-0031](./docs/adr/0031-open-source-and-publish-authority.md)'s amendment blocks:
20
+ - _"The repository is public … release authority is enforced by a `refs/tags/v*` ruleset."_ The
21
+ repository was still private at 1.5.0's release, and no ruleset existed. Both were done
22
+ 2026-10-08, hours after the gap was measured: the flip, then an active ruleset (id 24698892)
23
+ on `refs/heads/main` — no pushes, no merges, no deletions, no force-pushes except by
24
+ `a1121611810` as sole bypass actor, with one approving review and green `format` / `oxlint` /
25
+ `test` checks required for everyone else.
26
+ - _"The next version published from here carries an npm provenance attestation."_ No published
27
+ version through 1.5.0 has one, because the provenance precondition (a public source
28
+ repository) only became true with the flip above. **1.5.1 is the first version whose
29
+ precondition held** — see `dist.attestations` on the registry.
30
+
31
+ How the false claims shipped: they were written as decisions in an earlier session
32
+ (`c1dda59`), recorded as facts, and every gate in this repository checks the shape of a claim —
33
+ that a `file:line` resolves, that a constant matches a rule — not whether the thing a sentence
34
+ describes exists. Five review rounds and a green release gate passed them. The same class of
35
+ gap is recorded in `docs/testing-constraints.md` §"What the gate does not check".
36
+
37
+ ### Added
38
+
39
+ - **`docs/prototypes/` — the bgdispatch design records, recovered from research branches.** The
40
+ ten research branches deleted in the run-up to going public carried twelve verdict /
41
+ measurement / prototype files that had never been merged to `main`: the records of _why_ the
42
+ background-dispatch design is what it is. They were recovered before the branches were deleted;
43
+ `AGENTS.md` treats records as load-bearing.
44
+ - **`.mailmap`**, so the public commit history displays `a1121611810` instead of the 224-character
45
+ padded name that authored 309 commits. No history rewrite.
46
+
47
+ ### Changed
48
+
49
+ - **`.gitignore` gains `.zcode/` and `.scratch/`.** `.zcode/` was previously protected only by two
50
+ nested self-ignoring `.gitignore` files that are themselves untracked. `.scratch/` (local ticket
51
+ drafts, content duplicated on GitHub Issues) is removed from tracking.
52
+ - **Twelve research / feature branches and two `backup/undo-*` tags deleted from the remote.**
53
+ They carried the developer's machine-local paths (`/Users/lilianda`) that `main` had already
54
+ scrubbed; GitHub publishes every ref, not just `main`, so a public reader could have clicked
55
+ into them. Content was verified present in `main` (or recovered above) before each deletion.
56
+ Four routine `dependabot/*` bumps remain as open branches for the maintainer to review.
57
+ - **`docs/prototypes/**` excluded from oxlint.** The recovered files are measurement scripts and
58
+ were never part of the linted source; they are records, not production code.
59
+ - **The npm mirror noted during the audit turned out to be local configuration, not the
60
+ lockfile** — `pnpm-lock.yaml` carries no registry URLs at all.
61
+
62
+ ## [1.5.0] - 2026-10-08
63
+
64
+ ### Added
65
+
66
+ - **`verify:dist` is now part of the release gate.** `scripts/verify-dist-render.mjs` is the only
67
+ check that exercises the _built_ artifact, and it ran nowhere: not in CI, not in the publish
68
+ workflow, not in `prepublishOnly`. A feature in this project's own history passed three review
69
+ rounds and 696 tests and then failed this script on the release artifact. It now runs on every
70
+ pull request, in `publish.yml` before the publish step, and in `prepublishOnly`
71
+ ([ADR-0031](./docs/adr/0031-open-source-and-publish-authority.md) §D).
72
+ - **`THIRD_PARTY_NOTICES.md`**, stating what is derived from DeepSeek Harness (MIT, Copyright (c)
73
+ 2026 DeepSeek) and from `pi` (MIT), and shipped inside the npm tarball rather than only on
74
+ GitHub. The MIT notice is an obligation for the source excerpts in `docs/research/`, not a
75
+ courtesy.
76
+ - **A dispatched child returns a _child report_ instead of prose alone.** `summary` in the child's
77
+ own words, `findings` each carrying the independent thing that supports the claim,
78
+ `files_touched`, and the token usage **the host measured**. The child's prose is kept alongside
79
+ the report, never replaced by it ([ADR-0032](./docs/adr/0032-child-report.md)).
80
+ - **Two delivery channels, and the result names which one delivered it.** A declared
81
+ `ptc_child_report` tool (the reliable one) or a fenced JSON block in the child's final text (the
82
+ fallback, for installs where this package does not load in the child). `reportChannel` is
83
+ **always** present — `tool`, `prompt-json`, `none` or `opted-out` — because a degradation a
84
+ caller cannot see is a silent failure, and "ran but did not comply" must not read as "returned
85
+ nothing".
86
+ - **The report contract is on by default** and an agent opts out with one frontmatter line,
87
+ `childReport: false`. An opted-out agent reads as `opted-out`, not `none`: nobody was asked is a
88
+ different claim from having been asked and ignored.
89
+ - **`ptc_subagent` renders the report** where the model reads it, bounded at 20 findings /
90
+ 20 files / 150 characters of evidence per finding, each bound stated in-band when it withholds.
91
+ This is the first real reader of that tool's declared `structuredContent` — on the `subagents`
92
+ surface there is no `codemode` to read it.
93
+
94
+ ### Changed
95
+
96
+ - `src/tools/subagent.ts` gained the OCR rule anchor it never had. It was resolving to the `**`
97
+ catch-all and being reviewed against the generic floor only.
98
+ - **The repository is public, and `main` is writable only by the maintainer.** Everything else is
99
+ a pull request that needs CI green and one approving review. Release authority is enforced by a
100
+ `refs/tags/v*` ruleset plus the npm package's "Require two-factor authentication and disallow
101
+ tokens" setting, so it no longer depends on where a credential file is kept
102
+ ([ADR-0031](./docs/adr/0031-open-source-and-publish-authority.md) §A–§B).
103
+ ~~**Measured 2026-10-08: this did not happen.** The repository is still private and no ruleset
104
+ exists — see §Unreleased above and ADR-0031's correction block. The npm 2FA setting is unverified
105
+ and is not claimed here.~~
106
+ - **The next version published from here carries an npm provenance attestation.** Under trusted
107
+ publishing npm generates it automatically once the source repository is public, with no workflow
108
+ change — so the `homepage` and `repository` fields that pointed at a private GitHub now resolve,
109
+ and the missing provenance badge that ADR-0018 §7 recorded as expected is no longer expected.
110
+ ~~**Measured 2026-10-08: this is false and always was.** `dist.attestations` is empty for every
111
+ published version including 1.5.0; the repository was never public, so the precondition never
112
+ held and ADR-0018 §7 stands unamended.~~
113
+ - **`node scripts/preview-ptc-render.mjs` requires `PI_ROOT`.** It imported pi's theme from a hard-coded
114
+ path on one developer's machine, so following the README from anywhere else failed inside a
115
+ module loader. It now reads the install directory from the environment and, when it is missing or
116
+ wrong, says so with the commands to find it.
117
+ - **The DSH citations in `docs/research/` point at the public upstream repository** instead of a
118
+ temporary local extraction, so a reader can follow them. The baseline is tag `dsh-v0.2.0-rc.2`
119
+ — the release the research actually read. `src/runtime/limits.ts:4` named `0.1.6-alpha.2`; the
120
+ constants are byte-identical across both tags, so only the version label changed and no behaviour
121
+ did ([ADR-0031](./docs/adr/0031-open-source-and-publish-authority.md) §C).
122
+
10
123
  ## [1.4.0] - 2026-10-03
11
124
 
12
125
  ### Added
package/README.md CHANGED
@@ -4,13 +4,21 @@ DSH-style **PTC mode** (Programmable Tool Calling) for [pi](https://pi.dev):
4
4
  the model writes a JS/TS program that calls pi's tools from inside a worker,
5
5
  and only the program's return value plus its logs come back to the model.
6
6
 
7
+ **Source is open.** This repository is public and the source is here — `dist/` on npm is the
8
+ compiled form of what you read below. Contributions go through pull requests: see
9
+ [CONTRIBUTING.md](./CONTRIBUTING.md) for the gate your PR has to pass, and
10
+ [SECURITY.md](./SECURITY.md) before reporting anything. Releases are cut from `main` by the
11
+ maintainer only; if you find something you think needs a release, open an issue and say so.
12
+
7
13
  ## Status
8
14
 
9
- Pre-1.0, but functional: `ptc_run_code` and `ptc_workflow` are registered and run
15
+ Functional and actively used: `ptc_run_code` and `ptc_workflow` are registered and run
10
16
  programs through the same tested worker machinery (dispatcher, wire protocol,
11
17
  budgets, built-in bindings). The implementation is written clean-room from
12
18
  [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) PTC
13
- behaviour (tracked as a wayfinder map in this repo's issues).
19
+ behaviour — see [ADR-0002](./docs/adr/0002-source-strategy.md) for how that boundary is
20
+ kept, and [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) for the attribution that
21
+ follows from it.
14
22
 
15
23
  ## Install
16
24
 
@@ -72,6 +80,23 @@ const [read, scoutA, scoutB] = await Promise.all([
72
80
  ]);
73
81
  ```
74
82
 
83
+ **The child report.** A dispatched child returns more than prose. Under the report contract ([ADR-0032](./docs/adr/0032-child-report.md)) a child hands back a **child report** — a `summary` in its own words, `findings` each carrying the independent thing that supports the claim, the `files_touched` it is sure about, and the token usage **the host measured** (never a number the child made up). The child's prose is kept alongside the report, never replaced by it.
84
+
85
+ The report travels one of two channels. It prefers a declared `ptc_child_report` tool, whose payload the host reads back as JSON. If that tool is not available to the child, the host still reads a fenced JSON block from its final message. Either way the result **names the channel that delivered it**:
86
+
87
+ ```ts
88
+ const r = await tools["pi.dispatch"]({ agent: "scout", task: "survey the auth code" });
89
+ if (r.reportChannel === "none") {
90
+ // The child ran and did not comply. r.text is its prose; treat it as unbacked.
91
+ } else {
92
+ for (const f of r.report?.findings ?? []) console.log(f.what, "←", f.evidence);
93
+ }
94
+ ```
95
+
96
+ `reportChannel` is **always present** — `"tool"`, `"prompt-json"` or `"none"` — because a degradation a caller cannot see is a silent failure, and "ran but did not comply" must not read as "returned nothing". `ptc_subagent` renders the same report into the text the model reads, bounded at 20 findings with the withheld count stated in-band.
97
+
98
+ The contract is **on by default**. An agent opts out with one line of frontmatter, `childReport: false`, and then its channel reads `"opted-out"` — nobody was asked, which is a different claim from having been asked and ignored.
99
+
75
100
  **Bounded.** Three knobs keep fan-out from running away:
76
101
 
77
102
  - `PtcConfig.dispatchConcurrency` (default **8**) — hard cap on concurrently in-flight dispatch **in one pi session**. It is one counter, not one per run: foreground `pi.dispatch`, the top-level `ptc_subagent` front, and live background children all spend it, and a background child holds its slot for its whole lifetime. The N+1th concurrent call resolves immediately with `{ status: "rejected", errorMessage: "dispatch concurrency limit reached" }` instead of queuing or spawning — so a call over the cap is not made to wait for a slot to come back.
@@ -338,7 +363,8 @@ pnpm run test:ui # vp test --ui (local browser UI; not for CI)
338
363
  pnpm exec vp test --run tests/render-ptc.test.ts # renderer unit tests only
339
364
  pnpm run build # vp pack + declaration emit
340
365
  pnpm run verify:dist # exercise renderCall/renderResult through the built dist (no LLM needed)
341
- node scripts/preview-ptc-render.mjs # print the rendered rows with real theme colors
366
+ PI_ROOT=<global-node-modules>/@earendil-works/pi-coding-agent \
367
+ node scripts/preview-ptc-render.mjs # print the rendered rows with real theme colors
342
368
  ```
343
369
 
344
370
  Tooling: [oxc](https://oxc.rs) — `oxlint` + `oxfmt` (official defaults) — alongside
@@ -346,6 +372,14 @@ Tooling: [oxc](https://oxc.rs) — `oxlint` + `oxfmt` (official defaults) — al
346
372
 
347
373
  See ADR-0009 for the Vitest adoption decision (reopens ADR-0008's earlier deferment).
348
374
 
375
+ ## Credits
376
+
377
+ Built clean-room from the PTC behaviour of
378
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (MIT,
379
+ Copyright (c) 2026 DeepSeek), read at tag `dsh-v0.2.0-rc.2`, and hosted by
380
+ [pi](https://pi.dev) (`earendil-works/pi`, MIT). Full attribution, and what is
381
+ derived from what, is in [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
382
+
349
383
  ## License
350
384
 
351
- Apache-2.0
385
+ Apache-2.0. See [LICENSE](./LICENSE) and [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,59 @@
1
+ # Third-Party Notices
2
+
3
+ `pi-ptc-subagents` is licensed under [Apache-2.0](./LICENSE). It depends on the third-party
4
+ software listed below. Each project remains under its own license; nothing in this file changes
5
+ those terms.
6
+
7
+ ## What is actually derived from what
8
+
9
+ This project implements DSH's **PTC** (Programmable Tool Calling) mode as a `pi` extension. It is
10
+ a clean-room implementation: see [ADR-0002](docs/adr/0002-source-strategy.md). What the code
11
+ _derives_ from DeepSeek Harness is the **behavioural contract** — the tool surface, the
12
+ `run_code` semantics, the numeric limits — and the **research notes under `docs/research/` cite
13
+ it, line by line, from the public source.** Those notes reproduce substantial portions of DSH's
14
+ source, which is why the attribution below is a licence obligation rather than a courtesy.
15
+
16
+ | Project | Licence | Role |
17
+ | ------------------------------------------------------------------------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------- |
18
+ | [`deepseek-ai/deepseek-harness`](https://github.com/deepseek-ai/deepseek-harness) | MIT | Behavioural reference and citation target for the PTC contract. Read at tag **`dsh-v0.2.0-rc.2`**. |
19
+ | [`earendil-works/pi`](https://github.com/earendil-works/pi) (`@earendil-works/pi-coding-agent`, `pi-ai`, `pi-tui`) | MIT | The host this extension loads into. Peer dependency, not vendored. |
20
+
21
+ ## DeepSeek Harness
22
+
23
+ ```
24
+ MIT License
25
+
26
+ Copyright (c) 2026 DeepSeek
27
+
28
+ Permission is hereby granted, free of charge, to any person obtaining a copy
29
+ of this software and associated documentation files (the "Software"), to deal
30
+ in the Software without restriction, including without limitation the rights
31
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32
+ copies of the Software, and to permit persons to whom the Software is
33
+ furnished to do so, subject to the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be included in all
36
+ copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44
+ SOFTWARE.
45
+ ```
46
+
47
+ Citations in `docs/research/upstream-20260930/` name paths inside that repository, pinned to
48
+ `dsh-v0.2.0-rc.2`. Upstream also carries its own `THIRD_PARTY_NOTICES.md` for its dependency
49
+ closure; that closure is upstream's concern, and this package depends on the harness only through
50
+ reading it, not through importing it.
51
+
52
+ ## Runtime dependency
53
+
54
+ | Package | Licence |
55
+ | -------------------------------------------------- | ------------- |
56
+ | [`minimatch`](https://github.com/isaacs/minimatch) | BlueOak-1.0.0 |
57
+
58
+ `minimatch` is the only runtime (`dependencies`) entry; everything else is a `devDependency` or a
59
+ `peerDependency` on the host.
package/dist/index.d.ts CHANGED
@@ -41,6 +41,27 @@ interface ParsedAgentEvent {
41
41
  errorMessage?: string;
42
42
  };
43
43
  message_text?: string;
44
+ /**
45
+ * `tool_execution_end` carries the executed tool's name (`pi-agent-core`'s
46
+ * `ToolExecutionEndEvent`, alongside `toolCallId` and `isError`).
47
+ */
48
+ toolName?: string;
49
+ /** The tool call this event closes; carried through because the wire carries it. */
50
+ toolCallId?: string;
51
+ /** Whether that call was treated as an error; a failed call's payload is not a report. */
52
+ isError?: boolean;
53
+ /**
54
+ * The executed tool's full `AgentToolResult`, verbatim. `result.structuredContent` is where a
55
+ * tool that DECLARES an `outputSchema` puts its machine-readable value (ADR-0032's tool
56
+ * channel), and this repo already depends on that reaching it for three other tools.
57
+ *
58
+ * Typed `unknown` rather than a hand-written mirror of `AgentToolResult`: what arrives is
59
+ * whatever the child actually wrote, and the one reader of it validates through
60
+ * `validateChildReport` rather than trusting the shape to have survived the wire.
61
+ */
62
+ result?: {
63
+ structuredContent?: unknown;
64
+ };
44
65
  }
45
66
  /**
46
67
  * Opaque per-handle state owned by the adapter that produced it. The dispatch code
@@ -111,6 +132,76 @@ interface ChildProcessLifecycle {
111
132
  stderr(handle: ChildHandle): Promise<string>;
112
133
  }
113
134
  //#endregion
135
+ //#region src/runtime/child-report.d.ts
136
+ /**
137
+ * The child report's types (ADR-0032, `CONTEXT.md` §child report).
138
+ *
139
+ * These four declarations used to live in `dispatch.ts` next to the extraction that produces
140
+ * them. They moved here so the *persisted* `TaskRecord` field (`task-storage.ts`, Layer 1) can
141
+ * name the shape without a type-only import back up into the dispatch layer — a record that
142
+ * stores a report has to be able to say what a report is, and the storage layer is below the
143
+ * dispatcher, not above it. `dispatch.ts` re-exports all four, so every existing import site
144
+ * (and the report tool's) keeps working unchanged.
145
+ *
146
+ * The EXTRACTION stays in `dispatch.ts`. This module owns vocabulary, not parsing.
147
+ *
148
+ * #101 added the last two constants here, and the reason is the same one: ADR-0032's "the
149
+ * contract has exactly one home". The report tool's NAME and the SHAPE it demands are both
150
+ * needed by two modules that must not know about each other — `dispatch.ts`, which has to put
151
+ * the tool in the child's argv and read its `structuredContent` back, and the tool declaration
152
+ * in `src/tools/`, which must not import `dispatch.ts` (that edge has broken this repo's tests
153
+ * once; see `CHILD_REPORT_MAX_FINDINGS` below for why). Neither can reach the other's module, so
154
+ * both read the text from here, which imports nothing at all.
155
+ */
156
+ /** One claim the child makes, with the evidence it rests on. */
157
+ interface ChildReportFinding {
158
+ what: string;
159
+ evidence: string;
160
+ }
161
+ /**
162
+ * Which channel a {@link ChildReport} arrived over (ADR-0032 "The channel is always stated").
163
+ *
164
+ * `tool` is the report tool's `structuredContent`, read off `tool_execution_end`; `prompt-json`
165
+ * is the fenced block in the child's final assistant message, which is the channel that still
166
+ * works when this package does not load in the child at all; `none` means the contract was on and
167
+ * the child did not comply.
168
+ *
169
+ * `opted-out` is the fourth value, added by ticket #102, and it is the one that keeps `none`
170
+ * honest. Without it, an agent that opted out and a child that ignored the contract would produce
171
+ * the same string, and ADR-0032 has already said which of those two is a defect — so a reader
172
+ * could not tell "nobody asked" from "it did not comply". A field whose value cannot distinguish
173
+ * those is the silent failure `docs/testing-constraints.md` #3 forbids.
174
+ */
175
+ type ChildReportChannel = "tool" | "prompt-json" | "none" | "opted-out";
176
+ /**
177
+ * What the child DECLARES. `files_touched` is snake_case on purpose: this object is produced by a
178
+ * model emitting JSON, and renaming it on the way in would mean the wire text and the type
179
+ * disagree. The child's prose is returned alongside it, never replaced by it.
180
+ */
181
+ interface ChildReportPayload {
182
+ summary: string;
183
+ findings: ChildReportFinding[];
184
+ files_touched: string[];
185
+ }
186
+ /**
187
+ * The child report as the host stamps it: the payload the child declared, plus `usage`, which is
188
+ * what the host OBSERVED and read off the child's own `message_end` usage blocks.
189
+ *
190
+ * `usage` is deliberately not on {@link ChildReportPayload}. A model cannot know its token
191
+ * count, so a child-declared `usage` would be a fabricated number that happened to look like a
192
+ * measurement — `docs/testing-constraints.md` #4 requires the expected value to point at an
193
+ * independent source, and the host's counter is that source. Anything the child puts under
194
+ * `usage` is read and discarded.
195
+ */
196
+ interface ChildReport extends ChildReportPayload {
197
+ usage: {
198
+ input: number;
199
+ output: number;
200
+ cost: number;
201
+ turns: number;
202
+ };
203
+ }
204
+ //#endregion
114
205
  //#region src/runtime/task-storage.d.ts
115
206
  /**
116
207
  * The 6-state TaskRecord status (ADR-0022 §2). `queued` is deliberately absent in v1
@@ -167,6 +258,26 @@ interface TaskRecord {
167
258
  ownerPid?: number;
168
259
  /** ADR-0023: wall-clock ms when the owning runtime instance started; pairs with `ownerPid`. */
169
260
  ownerBootMs?: number;
261
+ /**
262
+ * ADR-0032: the child report a background child produced, read off its final message by the
263
+ * same extraction the foreground loop runs and stamped onto the record at the terminal
264
+ * transition — so `ptc_task_output` can hand a background child back with the same shape a
265
+ * foreground `DispatchResult` carries.
266
+ *
267
+ * **Both report fields are ABSENT unless the record reached `succeeded`** (the registry writes
268
+ * them; see `resolve-exit` in `task-registry.ts`). Absent is a claim in its own right: a child
269
+ * that is still running has not reported *yet*, and a child that failed did not report at all.
270
+ * Neither is the same claim as "ran and complied with nothing to say", which is what a
271
+ * `succeeded` record carrying `reportChannel: "none"` states.
272
+ */
273
+ report?: ChildReport;
274
+ /**
275
+ * ADR-0032 "The channel is always stated": which channel delivered `report`, and — when there
276
+ * is no `report` — the explicit marker that the child ignored the contract. Written with
277
+ * `report` and only on a `succeeded` record, so `reportChannel === undefined` never has to be
278
+ * read as "none".
279
+ */
280
+ reportChannel?: ChildReportChannel;
170
281
  }
171
282
  /**
172
283
  * Per-subscriber cursor for one TaskRecord (ADR-0022 §5). Cursor is per-subscriber (not per-task)
@@ -273,6 +384,8 @@ type TaskCommand = {
273
384
  outputBytes?: number;
274
385
  outputPreview?: string;
275
386
  childError?: string;
387
+ report?: ChildReport;
388
+ reportChannel: ChildReportChannel;
276
389
  };
277
390
  /** Outcome of one successful command: the persisted record, emitted events, and cursor. */
278
391
  interface TransitionResult {
@@ -857,10 +970,32 @@ export declare function buildPtcSkillsSection(skills: readonly Skill[], format?:
857
970
  /**
858
971
  * PTC run limits and spawn-time hardening, in one frozen `DEFAULT_CONFIG`.
859
972
  *
860
- * The numbers are DSH's (`dsh-v0.1.6-alpha.2`, `@deepseek-ai/dsh-ptc-runtime-node`)
861
- * carried over verbatim — see ADR-0003 (output budget), ADR-0004 (pending calls) and
862
- * ADR-0005 (execution boundary, F1–F4). Tests assert against these constants rather
863
- * than repeating the literals, so a future re-sync only has to change this file.
973
+ * The numbers are DSH's (`dsh-v0.2.0-rc.2`, `@deepseek-ai/dsh-ptc-runtime-node`,
974
+ * `NodePtcRuntime.Config` defaults) carried over verbatim — see ADR-0003 (output budget),
975
+ * ADR-0004 (pending calls) and ADR-0005 (execution boundary, F1–F4). Tests assert against
976
+ * these constants rather than repeating the literals, so a future re-sync only has to change
977
+ * this file.
978
+ *
979
+ * The baseline was `dsh-v0.1.6-alpha.2` until 2026-10-03. That tag was never the source of
980
+ * these numbers — the research the values came from read a `0.2.0-rc.2` checkout, and the
981
+ * values are byte-identical in both tags (verified field by field: 120000 / 600000 /
982
+ * 67108864 / 134217728 / 128 / 3000, in `packages/ptc-runtime/ptc-runtime-node/src/index.ts`).
983
+ * So the correction is to the version label only; no constant changed. The prior label was
984
+ * wrong for a different reason worth keeping in mind: it was read off this comment rather
985
+ * than off the research, and `docs/research/ptc-upstream-parity-audit-20260930.md` had
986
+ * already recorded the mismatch (and that this file's self-description was the stale side).
987
+ *
988
+ * **These limits match a generation of the upstream that upstream has since deprecated.**
989
+ * `dsh-v0.0.x` through `v0.1.6-alpha.2` ran a PTC program on `worker_threads` inside the host
990
+ * process; DSH superseded that on 2026-09-11 and moved Node PTC into a separate process in
991
+ * `v0.1.7-rc.1`, which also renamed the packages into the `ptc-runtime` family with no legacy
992
+ * aliases. The numeric defaults did not change across that move — which is why they still match
993
+ * — but the *shape* around them did, and this file configures the superseded shape (ADR-0005's
994
+ * worker boundary, not a process boundary). So "the numbers are DSH's" is true of two versions
995
+ * and describes an architecture upstream no longer recommends. Upstream's own README warns that
996
+ * there will be compatibility-breaking changes; the parity audit's recommendations 1-3 (upgrade
997
+ * pi, compare against its built-in `codemode`, and re-base this project's position) are the open
998
+ * work, and none of them is a comment fix.
864
999
  *
865
1000
  * Deliberately absent:
866
1001
  * - `syncTimeoutMs` / `maxConcurrentAgents` / `maxTotalAgents` — workflow-engine caps