specpi 0.11.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/NPM_RELEASE.md +3 -1
  3. package/README.md +41 -29
  4. package/SECURITY_MODEL.md +36 -0
  5. package/THIRD_PARTY.md +18 -2
  6. package/docs/browser-testing.md +76 -0
  7. package/docs/delegation/README.md +264 -0
  8. package/docs/delegation/design-protocol.md +382 -0
  9. package/docs/delegation/design.md +525 -0
  10. package/docs/delegation/evaluation.md +307 -0
  11. package/docs/delegation/protocol.md +271 -0
  12. package/docs/delegation/research.md +216 -0
  13. package/extensions/browser/core.d.mts +64 -0
  14. package/extensions/browser/diagnostics.ts +275 -0
  15. package/extensions/browser/index.ts +349 -57
  16. package/extensions/browser/interactions.ts +118 -0
  17. package/extensions/browser/lifecycle.ts +28 -0
  18. package/extensions/command-guard/index.ts +118 -33
  19. package/extensions/delegation/core.mjs +772 -0
  20. package/extensions/delegation/errors.mjs +8 -0
  21. package/extensions/delegation/extension.mjs +475 -0
  22. package/extensions/delegation/index.ts +9 -0
  23. package/extensions/delegation/managed-files.mjs +13 -0
  24. package/extensions/delegation/native.mjs +155 -0
  25. package/extensions/delegation/presentation.mjs +315 -0
  26. package/extensions/delegation/protocol.mjs +296 -0
  27. package/extensions/delegation/provider.mjs +689 -0
  28. package/extensions/delegation/snapshot.mjs +532 -0
  29. package/extensions/delegation/worker.mjs +218 -0
  30. package/extensions/tool-wishlist/verification.mjs +1 -0
  31. package/extensions/workflow-controls/index.ts +5 -1
  32. package/package.json +17 -4
  33. package/scripts/check-package.mjs +29 -3
  34. package/scripts/check-pi-package.mjs +5 -0
  35. package/scripts/check-syntax.mjs +61 -0
  36. package/scripts/run-browser-tests.mjs +61 -0
  37. package/scripts/setup-browser-tests.mjs +38 -0
  38. package/scripts/site-browser.mjs +272 -0
  39. package/scripts/specpi.mjs +24 -0
  40. package/site/logo.svg +1 -9
  41. package/templates/AGENTS.md +1 -0
@@ -0,0 +1,264 @@
1
+ # Bounded delegation
2
+
3
+ Status: experimental in SpecPi 0.13.0. Disabled by default.
4
+ The package remains `specpi`; no separate npm package or background service is required.
5
+
6
+ SpecPi keeps one agent responsible for changes and acceptance. This extension adds
7
+ bounded, read-only workers for independent questions. It does not add a second writer,
8
+ an automatic planner, or a permanent team. The [research](research.md) supports testing
9
+ selective delegation; it does not establish that this implementation improves outcomes.
10
+
11
+ ## Use normal Pi startup
12
+
13
+ ```sh
14
+ pi
15
+ ```
16
+
17
+ The native extension is discovered through the ordinary Pi package and SpecPi
18
+ install/update lifecycle. Existing Pi startup, UI, resources, trust decisions and
19
+ proxy configuration remain Pi-owned. Delegation needs no alternate launcher, extra
20
+ SDK host, separate runtime process or new setup path.
21
+
22
+ Delegation checks **SDK capabilities, not an exact Pi version list**. New Pi versions
23
+ can activate when the required public session, runtime, settings and thinking APIs are
24
+ available. Missing APIs produce an error naming the unavailable capability; session
25
+ construction and each request still enforce the tool, model and resource policy.
26
+ The [compatibility record](research.md#pi-compatibility-evidence) records tested versions
27
+ separately; API presence does not prove every future SDK behavior. Normal SpecPi installation
28
+ keeps its separately documented host floor and 0.84.4 bootstrap pin.
29
+ Restart Pi after updating SpecPi to load a changed delegation
30
+ runtime version or change its working root. The broker uses the canonical working
31
+ directory captured for this Pi process.
32
+
33
+ Workers are actual SDK `createAgentSession` instances with in-memory session storage.
34
+ Pi runs their model/tool loop. SpecPi supplies admission, selected-source tools and
35
+ result checks; it does not implement a second conversation loop. Children load no
36
+ ambient extensions, skills, AGENTS files or parent session history.
37
+
38
+ ```mermaid
39
+ flowchart LR
40
+ parent["Parent Pi agent · sole writer"] -->|bounded question| controller["SpecPi admission and receipts"]
41
+ controller --> child["Pi AgentSession · memory only"]
42
+ child -->|admitted SDK invocation| runtime["Pi ModelRuntime"]
43
+ child -->|selected list/read/search| broker["Snapshot broker"]
44
+ child -->|claims and evidence| parent
45
+ parent --> checks["Verification and final decision"]
46
+ ```
47
+
48
+ The child uses a fresh Pi `ModelRuntime` with standard authentication, environment
49
+ and `models.json` resolution. Its settings take transport and thinking budgets from
50
+ Pi's configured global settings; project settings are not loaded. The parent model and
51
+ thinking level are explicit. Pi's public thinking-level clamp determines the effective
52
+ child level, which the adapter verifies. Pi handles authentication
53
+ and OAuth; SpecPi does not copy credentials or inspect private runtime fields.
54
+ Preflight rejects runtime-only authentication, selected extension-registered provider
55
+ overrides, model-specific headers, startup proxy configuration and mismatched safe
56
+ model descriptors because the fresh runtime cannot faithfully reproduce those parent
57
+ routes. Parent configuration is left unchanged; an unsupported route disables delegation.
58
+
59
+ This is **not full parent inference parity**. Parent request hooks, ephemeral runtime
60
+ settings and session affinity are not automatically transferred. A workflow requiring
61
+ those inherited controls for every request must keep delegation disabled. Receipts
62
+ bind supported model and source descriptors; they cannot certify an unchanged remote
63
+ service or every configuration change behind a stable provider identity.
64
+
65
+ ## Enable deliberately
66
+
67
+ In the interactive session:
68
+
69
+ ```text
70
+ /delegate on
71
+ /delegate status
72
+ /delegate limits
73
+ /delegate cancel <batchId>
74
+ /delegate off
75
+ ```
76
+
77
+ In Pi's terminal UI, a small panel above the editor shows each worker's ID, role,
78
+ state, elapsed time, model calls and source-tool calls. `1/2 workers` means one of
79
+ two slots is occupied; it is not a completion percentage. Model calls are SDK
80
+ invocations, not tokens. Completed jobs remain visible as **ready for review** until
81
+ the parent resolves them. A cancelled worker shows **stopping** while its SDK request
82
+ still occupies a slot. The panel disappears when no work needs attention.
83
+
84
+ Tool output uses compact summaries. Expand it with Pi's normal tool-output shortcut
85
+ to read answers, findings, evidence references and missing context. These remain
86
+ advisory worker reports. `/delegate status` shows the selected model, process budgets
87
+ and batch IDs; use an ID with `/delegate cancel <batchId>` to cancel that batch.
88
+
89
+ The panel follows Pi's theme and adapts to terminal width. It refreshes at most once
90
+ per second between lifecycle changes, stops its timer when workers settle and is
91
+ removed on shutdown or reload. It reads counters without checking files or providers;
92
+ it does not retain or display live child reasoning. RPC and print mode keep the same
93
+ structured tool responses and do not mount terminal widgets. The UI uses Pi's public
94
+ [widget and tool-rendering APIs](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md).
95
+
96
+ `on` grants the displayed experimental calls/time envelope. There is no model-call
97
+ permission toggle in the model-facing tool. `limits` is read-only; the shipped ceilings
98
+ cannot be raised by prompts. Turning delegation off, changing guard policy, switching
99
+ sessions or models, navigating branches, and changing task/scope bindings revoke the
100
+ current generation. Off/on, `/reload` and session switches do not reset the Pi process's
101
+ counters or free requests that are still settling. The same in-memory controller remains
102
+ in use; restart Pi to load changed runtime code. Normal conversation leaf advancement
103
+ does not invalidate workers.
104
+
105
+ Once enabled, delegation follows changes to the parent's provider, model and thinking
106
+ level without another `/delegate on`. Each change revokes old worker results, retains
107
+ unsettled slots and consumed quotas, and checks the new host before resuming dispatch.
108
+ Old jobs are not retried. An unsupported selection pauses delegation with a reason;
109
+ selecting a compatible model resumes it automatically. `/delegate off` remains off
110
+ through later model changes. Guard, task/scope and session lifecycle changes still
111
+ revoke activation. Status separates the user's `requested` choice from `enabled`
112
+ dispatch, with `updating` and `pauseReason` for model setup.
113
+
114
+ While delegation is off, its tool is removed from the parent's active tool list.
115
+ The command remains available, but the delegation tool schema is included in model
116
+ requests only after activation. Other active tools are preserved.
117
+
118
+ Command Guard is optional: delegation can run when Guard is absent or Off. When
119
+ active, Command Guard continues to intercept the parent `delegate` tool. Strict mode presents
120
+ the effective capability envelope and binds approval to its policy fingerprint and
121
+ the exact call. A locked, unready or ambiguous installed Guard still blocks activation;
122
+ the error identifies that state. `/delegate status` reports the observed Guard state.
123
+ Worker tool restrictions and resource limits are enforced independently of Guard. A worker result
124
+ cannot authorize a write, a commit, a deployment, or an improvement.
125
+
126
+ ## Admit a specific purpose
127
+
128
+ | Mode | Required structure | Context and tools |
129
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
130
+ | `review` | A frozen artifact, original requirements, relevant constraints and actual validation facts. The parent checks findings before acting. | Nonempty inline context or selected files; selected-source tools when files are supplied. |
131
+ | `scout` | A bounded evidence question with an independently checkable answer and a reason to separate the analysis. | At least one selected file; list/read/literal-search only, with no live web access. |
132
+
133
+ The packet declares one benefit: `independent_review` for review jobs, or
134
+ `parallel_analysis` / `context_isolation` for scout jobs. It includes a nonempty `why`.
135
+ `parallel_analysis` also requires useful `parentWork`; this may be empty for the other
136
+ benefits. A final review can be useful even when the parent waits. The controller checks
137
+ this structure, not whether the claimed benefit will materialize. Duplicate questions
138
+ after trimming and case normalization are rejected; distinct text is not proof of
139
+ independent work.
140
+
141
+ Each job names its assigned global requirement IDs and receives only those requirements,
142
+ plus the fixed decisions and non-goals. Transfer original constraints and evidence,
143
+ not the parent's reasoning or verdict. Do not delegate routine lookups, small understood
144
+ edits, coupled mutable work, generic second opinions or repeated role-based answers.
145
+ Use parallel parent tool calls when retrieval alone answers the question.
146
+
147
+ Workers have no shell, writes, arbitrary plugin tools or recursive delegation.
148
+ The parent obtains and selects source material. Model routing, automatic retries,
149
+ live-web access, monetary admission and automatic policy tuning remain unimplemented.
150
+
151
+ `/task` remains optional. Changes to an active task contract invalidate delegation,
152
+ but the parent is still responsible for faithfully transferring the task into the packet.
153
+
154
+ Use `delegate run`, continue useful parent work, then `collect`. Collection can wait
155
+ up to 30 seconds without another model request. Do not poll on a fixed schedule.
156
+ Check the referenced evidence and `resolve` each report, including per-finding
157
+ dispositions. One changed-input `follow_up` is available under the original deadline
158
+ and counters. [Protocol and executable examples](protocol.md) define the exact fields.
159
+
160
+ ## Enforced resource envelope
161
+
162
+ | Resource | Ceiling |
163
+ | ------------------------- | ---------------------------------------------------------------------------- |
164
+ | Active worker requests | 2 per Pi process, including cancelled requests still settling |
165
+ | Batches / jobs | 4 batches per Pi process; one unresolved batch; 2 jobs per batch |
166
+ | SDK model invocations | 32 per Pi process, 8 per batch; 4 per logical job including follow-up |
167
+ | Follow-ups / retries | 1 changed-input follow-up per job; provider and session retries disabled |
168
+ | Time | 120 seconds per logical job; 300 seconds per batch, including follow-up time |
169
+ | Packet / child context | 256 KiB, checked before dispatch |
170
+ | Selected sources | 200 files and 8 MiB per batch |
171
+ | Tools | 12 calls and 64 KiB total returned JSON per logical job |
172
+ | Tool response | Bounded reads/search; 16 KiB per snapshot read/search response |
173
+ | Final report | 16 KiB; 8 findings; coverage for each assigned requirement |
174
+ | Requested provider output | 8,192 tokens, clamped to the model maximum |
175
+ | SDK-visible response | 256 KiB acceptance limit on observed response content |
176
+
177
+ These are conservative experiment limits, not empirically optimal values. Each SDK
178
+ invocation is admitted before dispatch. Automatic provider/session retries and
179
+ compaction are disabled, so they cannot silently create another SDK request.
180
+ Pi authentication preflight occurs before the model-invocation counter; these quotas
181
+ do not count or bound Pi's authentication/OAuth preparation.
182
+
183
+ The native SDK stream is observed while the child runs. Its response checks are not
184
+ hard bounds on raw transport, hidden provider attempts, billing or process memory.
185
+ Bytes may already be buffered before an SDK event becomes visible. Stream checks count
186
+ recognized deltas incrementally; full response validation occurs at content/terminal
187
+ boundaries, before tool execution and before publication. This relies on Pi's parsed
188
+ stream contract, not arbitrary inconsistent partial objects. Cheap lease checks run
189
+ per event; full root, model and provider-policy checks run at protected boundaries.
190
+ The parsed stream allows at most 64 content blocks, 512 structural nodes per partial,
191
+ 65,536 events and 130 non-delta boundaries per invocation. These are implementation
192
+ ceilings, not empirically optimal values.
193
+ Cost is unavailable; available token fields are retained even when others are missing.
194
+ Never-reported fields are `null`, and per-field `usageReportedCalls` distinguishes
195
+ partial totals from complete accounting. This version cannot satisfy a policy
196
+ requiring those unsupported guarantees.
197
+
198
+ Cancellation revokes broker access and requests SDK abort. Slots remain held through
199
+ SDK-visible stream/result and prompt settlement; that does not prove physical remote
200
+ execution has ended. Late content is discarded. A non-cooperative SDK/provider can
201
+ require ending Pi; a timeout does not launch a replacement behind its back. Completed
202
+ reports keep their original source bindings after the deadline, but child sessions are
203
+ released at the deadline and later follow-up is rejected.
204
+
205
+ ## Evidence and retention
206
+
207
+ Snapshots contain only exact selected regular text files under the fixed canonical
208
+ working root of the Pi process. The broker rejects traversal, symlinks/junctions,
209
+ hardlinks, binary content, private path
210
+ names, unknown source IDs, and oversized reads. Each worker can search only its own
211
+ selection. Capture verifies content digests. Each tool call checks canonical paths,
212
+ file identity and change metadata against the immutable capture, without rereading
213
+ all selected bytes. Publication, collection, follow-up and disposition also recheck
214
+ content digests. Changed source bindings require a fresh batch. A content change that
215
+ evades filesystem metadata is detected at the next digest check, not by each tool call.
216
+
217
+ This is a trusted-local-filesystem contract, not an operating-system sandbox or an
218
+ atomic filesystem snapshot. Filename restrictions cannot detect secrets embedded in
219
+ an ordinary source file. The parent must select appropriate material for the configured
220
+ model provider. Trusted Pi extensions remain privileged in the shared process despite
221
+ being absent from the child's resource loader. Only the submitted packet is inherited
222
+ automatically, not the parent transcript.
223
+
224
+ Each report separates worker claims from a host receipt: job/attempt identity, packet
225
+ digest, generation, result revision, route, state, counters and usage completeness.
226
+ Source references are validated for identity and line range; their truth is still a
227
+ verification question for the parent. `accept` records a parent assessment, not human
228
+ approval or verified task completion.
229
+
230
+ Child conversations use in-memory sessions. A final disposition, cancellation, exhausted
231
+ follow-up or original deadline releases the child; active SDK work retains its slot until
232
+ settlement. Teardown failures do not escape into Pi's event loop. Shared snapshot text
233
+ is destroyed once no job can continue, including failed jobs at their original deadline.
234
+ Packet and job-input references are dropped when owned workers settle. Source metadata
235
+ and digests still validate completed reports after text is destroyed. Starting the next
236
+ accepted batch retires the previous batch's reports; invalidation retires old generations
237
+ after their workers settle. Retired batches cannot be collected or followed up.
238
+ Only bounded state summaries, quota counters and the idempotency journal remain for
239
+ the Pi process lifetime, including `/reload` and session switches. They cannot recreate
240
+ retired work. SDK setup errors are replaced with generic diagnostics before reaching
241
+ status, command notices or model-facing errors; code-owned policy errors stay specific.
242
+ There is no child session database, raw metrics log,
243
+ credential copy, automatic resume, or secure memory-erasure claim. Normal Pi parent
244
+ tool results may be retained in its ordinary session. Turning delegation off does not
245
+ remove results already retained by Pi; review the session before sharing it.
246
+
247
+ ## Implementation and evaluation
248
+
249
+ The modules under `extensions/delegation/` integrate Pi AgentSession with admission,
250
+ snapshot tools and closed result validation. There are no additional runtime dependencies
251
+ or separate host process. Compatibility requires verification against the supported
252
+ Pi SDK and ordinary package discovery, not merely a passing mock provider.
253
+
254
+ The evaluation plan separates deterministic runtime/security fixtures from comparative
255
+ task outcomes. Use isolated state and synthetic providers for contract tests; live
256
+ inference needs separate authorization. Such fixtures do not establish parity with
257
+ the parent's full inference pipeline or every production provider.
258
+
259
+ The [evaluation plan](evaluation.md) compares selective delegation with strong single
260
+ agents, serial workflows and always-delegate baselines using matched resource budgets.
261
+ No production quality, speed or cost improvement is claimed until those experiments run.
262
+ The [original design](design.md) and [target protocol](design-protocol.md) preserve the
263
+ broader proposal and its currently unimplemented proof obligations. The implemented
264
+ [calls/time protocol](protocol.md) is the source of truth for this release's behavior.
@@ -0,0 +1,382 @@
1
+ # Delegation protocol: archived version 1 target proposal
2
+
3
+ This is the original target design contract, not the implemented API. The normative
4
+ words **must** and **must not** specify requirements for that stronger future contract.
5
+ Use the [implemented calls/time protocol](protocol.md) and [guide](README.md) for the
6
+ current experimental interface. It does not supply this proposal's hard raw-transport,
7
+ per-provider-attempt or monetary admission guarantees, live web adapter, or alternative
8
+ model routes. The schemas and command examples below must not be submitted as if they
9
+ were the implemented protocol.
10
+
11
+ The current `bounded-pi-sessions-v1` implementation checks public SDK capabilities and uses actual Pi AgentSession workers,
12
+ fresh standard ModelRuntime configuration, explicit parent model/thinking and SDK-visible
13
+ streaming. It does not claim full parent-hook/ephemeral-setting inheritance or hard
14
+ raw-transport, hidden-attempt or invoice limits. It admits only `review` and `scout`,
15
+ with assigned requirement subsets. Its controller survives reloads; the implemented
16
+ guide specifies restart, fixed-root and SDK-settlement boundaries. The custom loop,
17
+ four-profile schema and stronger port contracts below remain historical proposals.
18
+
19
+ See the [archived architecture](design.md) for target policy and
20
+ [evaluation](evaluation.md) for the distinction between current fixtures and unmet
21
+ proof obligations.
22
+
23
+ ## 1. Authority and ownership
24
+
25
+ Separate three objects:
26
+
27
+ 1. **Request:** a parent-model proposal. All fields are untrusted and can only request
28
+ capabilities already granted by the human.
29
+ 2. **Envelope:** a host-created record binding a validated request to identities,
30
+ permissions, sources and resource reservations. A model cannot replace its fields.
31
+ 3. **Result:** a worker's claims plus separately generated host observations. Worker
32
+ prose cannot manufacture host observations or acceptance.
33
+
34
+ Use closed schemas, explicit version numbers, bounded strings and arrays, and rejection
35
+ of unknown fields. Validate input before dispatch and output before collection. This
36
+ is an in-process protocol with structured objects; no HTTP listener, RPC server, or
37
+ message serialization framework is required.
38
+
39
+ ## 2. Parent request
40
+
41
+ One tool named `delegate` accepts this discriminated union:
42
+
43
+ | Operation | Required input | Outcome |
44
+ | ----------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
45
+ | `run` | Objective, job proposals, selected input references, requested limits | Admit one batch; return IDs and effective limits, or a deterministic rejection |
46
+ | `status` | Batch ID | Compact states, coverage counts, usage and accounting availability |
47
+ | `collect` | Batch ID, optional job IDs, acknowledged revision cursor and bounded wait | Return applicable payloads after the caller's acknowledged cursor; no destructive dequeue |
48
+ | `follow_up` | Existing job/result binding, correction or new evidence, idempotency key | Admit one additional attempt under the original job limits and deadline |
49
+ | `resolve` | Exact result binding, disposition and per-finding decisions, idempotency key | Record parent adjudication; never authorize a new action |
50
+ | `cancel` | Batch or job ID | Revoke admission and request cancellation; report requests still settling |
51
+
52
+ For stage 1, the single review call may finish within `run`; it uses the same envelope
53
+ and result schema. Stage 2 adds asynchronous return without changing the result contract.
54
+ `collect` can wait at most 30 seconds; it does not create polling timers or new model
55
+ turns. Collect immediately before consuming the relevant evidence, not on a fixed loop.
56
+
57
+ `collect` returns a revision cursor without advancing the caller's acknowledgment.
58
+ Only the next request's explicit cursor acknowledges receipt. Repeating the old cursor
59
+ returns the same applicable payload, so a response lost before consumption can be
60
+ recovered. Results remain available within bounded session retention until resolved,
61
+ discarded or explicitly expired; advertise any expiration rather than silently
62
+ returning an empty successful result.
63
+
64
+ `follow_up` requires `batchId`, `jobId`, `attemptId`, `packetDigest`, `resultRevision`,
65
+ `taskGeneration`, and an idempotency key. It is admitted only from `incomplete` with an
66
+ explicit context gap, or `invalid` with a correctable format error. The controller
67
+ validates that the correction addresses that outcome, that prior provider resources
68
+ have settled, and that the original task/policy bindings, deadline and counters permit
69
+ another attempt. Authority and new source grants are rechecked. An atomic transition
70
+ records the new immutable attempt and debits the **existing** job, batch and session
71
+ ledgers. It does not create another batch or refresh the deadline. The same idempotency
72
+ key and payload return the existing outcome; reuse with a different payload is rejected.
73
+
74
+ Transient transport retries are controller-owned attempts under the same job, not a
75
+ parent `follow_up`. Cancelled, expired, stale, accepted and discarded jobs cannot be
76
+ revived through this operation. A materially new task requires a new admission and
77
+ still consumes the existing session allowance. A renamed request does not authorize
78
+ automatic recovery of a cancelled job.
79
+
80
+ `resolve` binds to those same exact result identity fields and includes a disposition
81
+ (`accept`, `discard`, or `needs_check`) plus decisions for every presented finding
82
+ (`confirmed`, `rejected`, or `needs_check`, with reasons and evidence IDs). The host
83
+ rejects stale revisions and mismatched task generations. An accepted result must have
84
+ required coverage and no unresolved material finding. Accepting a review report means
85
+ its evidence has been adjudicated; it does not assert that the code is ready or that
86
+ confirmed defects have been fixed. Incomplete, invalid and failed results can be
87
+ discarded or held for investigation but cannot be accepted as complete. Apply each
88
+ idempotent resolution atomically. Host state does not infer acceptance from prose,
89
+ collection, silence, or a worker verdict. Finding decisions remain separate from the
90
+ overall disposition. Ordinary task completion still follows SpecPi's existing gates.
91
+
92
+ Proposed request example:
93
+
94
+ ```json
95
+ {
96
+ "version": 1,
97
+ "op": "run",
98
+ "objective": "Resolve two independent causes of the observed regression",
99
+ "jobs": [
100
+ {
101
+ "key": "cache-invalidation",
102
+ "mode": "investigate",
103
+ "question": "Can the cache retain a value after its source changes?",
104
+ "requirements": ["R1"],
105
+ "sourceIds": ["src-cache", "src-cache-callers", "src-cache-tests"],
106
+ "acceptance": "Identify the relevant path and evidence, or state what prevents a conclusion.",
107
+ "decisions": ["Public cache API behavior must remain compatible."],
108
+ "nonGoals": ["Implementing a fix", "Changing public API behavior"],
109
+ "independence": "The parent is separately investigating event ordering.",
110
+ "parentAlternative": "Read these same sources sequentially in the parent.",
111
+ "reason": "A substantial independent source investigation can run while event ordering is inspected."
112
+ }
113
+ ],
114
+ "limits": {
115
+ "maxRequests": 4,
116
+ "deadlineMs": 120000
117
+ }
118
+ }
119
+ ```
120
+
121
+ Source IDs must already refer to a host-validated manifest prepared under the session
122
+ grant. The model does not create that manifest by inventing IDs. A repository capture
123
+ interface validates requested paths before issuing IDs. User-provided attachments or
124
+ parent-selected excerpts get explicit provenance distinct from broker observations.
125
+
126
+ The example's acceptance criteria permit an honest unknown. An instruction to prove a
127
+ preferred conclusion is not an acceptable evidence task.
128
+
129
+ ## 3. Host envelope
130
+
131
+ | Field | Meaning |
132
+ | ------------------------------------------------- | ----------------------------------------------------------------------------------------- |
133
+ | `version` | Protocol version; reject unsupported versions |
134
+ | `batchId`, `jobId`, `attemptId` | Host-generated opaque identities |
135
+ | `policyGeneration` | Immutable human policy and capability selection |
136
+ | `taskGeneration` | Active task identity, requirement revision, scope grant and session epoch |
137
+ | `taskBinding` | Exact card digest and requirement IDs, or an explicit ephemeral requirement set |
138
+ | `packetDigest` | SHA-256 of canonical packet content and selected source manifest |
139
+ | `modelRoute` | Bound model and effective configuration identity; no secrets or raw authorization headers |
140
+ | `capabilities` | Closed broker operations and source/network grants |
141
+ | `limits` | Effective job and batch reservations, deadlines and output limits |
142
+ | `question`, `acceptance`, `decisions`, `nonGoals` | Validated task content |
143
+ | `sources` | Opaque IDs, hashes, media type, coverage, captured location and provenance |
144
+ | `priorAttempt` | Optional parent-approved correction or additional evidence from the same logical job |
145
+
146
+ Canonical serialization must define UTF-8 encoding, key ordering, numeric handling and
147
+ array order. Hash the exact bytes supplied to a worker, including explicit omitted or
148
+ unavailable inputs. A digest detects accidental drift; it is not a signature or proof
149
+ against a compromised host extension. Do not expose reversible configuration secrets
150
+ through fingerprints; use a host-owned opaque generation for sensitive configuration.
151
+
152
+ Fresh context contains the task contract, mode instructions, admitted tools and source
153
+ references. Tool outputs are appended as data. A webpage or source comment cannot
154
+ alter the envelope by resembling its JSON syntax.
155
+
156
+ ## 4. Evidence references
157
+
158
+ Every accepted reference resolves through a host-managed evidence table:
159
+
160
+ | Kind | Required binding | Limit of the evidence |
161
+ | ------------------ | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
162
+ | Snapshot source | Source ID, exact hash, one-based line or byte range | Supports claims about captured bytes only |
163
+ | Public source | Fetch receipt, final URL, retrieval time, source date when available, extracted range and hash | Retrieval does not establish factual correctness |
164
+ | Parent observation | Receipt ID, origin, observed result, bound source generation when relevant | A parent-authored description is not an independently executed check |
165
+ | Host check receipt | Fixed validator identity, actual outcome, environment/source binding | Valid only for the recorded check and inputs |
166
+
167
+ The worker can cite a receipt ID it received; it cannot register an observation as if
168
+ the host executed it. The host rejects nonexistent IDs, impossible ranges, wrong
169
+ hashes and references outside the grant. Mark exact quotations separately from
170
+ paraphrase or inference. Preserve relevant source-version distinctions.
171
+
172
+ Source text may include malicious instructions. The broker supplies it as bounded tool
173
+ data. The model-facing worker has no tool for shell execution, filesystem mutation,
174
+ credential lookup, delegation, persistent memory, or capability expansion. This is a
175
+ closed tool interface under a trusted process, not a claim of perfect prompt-injection
176
+ resistance or OS containment.
177
+
178
+ ## 5. Worker result and host receipt
179
+
180
+ The worker returns:
181
+
182
+ ```text
183
+ status: complete | partial | needs_context
184
+ answer: bounded answer to the assigned question
185
+ coverage[]:
186
+ requirementId, covered | uncovered | not_applicable, explanation, evidenceIds[]
187
+ findings[]:
188
+ findingId, claim, trigger, consequence,
189
+ observed | inferred | unverified,
190
+ evidenceIds[], contraryEvidenceIds[], limitations
191
+ missing[]:
192
+ neededEvidence, affectedConclusion, reason
193
+ suggestedNextStep: optional bounded text, never an executable action
194
+ ```
195
+
196
+ For review, add severity based on impact and the affected source location. No confidence
197
+ percentage or aggregate self-score is required. For research, distinguish publication
198
+ status and source date, and include evidence against the proposed conclusion. For
199
+ investigation, report failed hypotheses that affect the answer. For consultation,
200
+ prefer a discriminating next experiment to unsupported certainty.
201
+
202
+ The host separately attaches:
203
+
204
+ ```text
205
+ hostStatus: ready | incomplete | invalid | failed | cancelled | expired | stale
206
+ identity: batchId, jobId, attemptId, packetDigest, taskGeneration, resultRevision
207
+ observations: sourceReceipts[], toolOutcomes[], stopReason
208
+ usage: perRequestRecords[], totals, availabilityByCategory
209
+ responseLimits: rawBytesReceived, parsedBytesRetained, overflow, bufferingBoundVerified
210
+ timing: queuedAt, startedAt, settledAt, elapsedMs
211
+ limits: admitted, consumed, reserved, stillSettling
212
+ validation: schema, references, coverage, generation, truncation
213
+ ```
214
+
215
+ The collection response separately supplies `collectionCursor`, a batch event position
216
+ used to acknowledge delivery. It is not the job's `resultRevision`: follow-up and
217
+ resolution bind the exact result revision even if a batch cursor advances because a
218
+ different job produces an event.
219
+
220
+ Persist original assistant/tool message structures in the worker's bounded in-memory
221
+ loop as required by the provider, including opaque signatures. Do not fabricate,
222
+ rewrite or transplant provider reasoning blocks. The parent sees the bounded result,
223
+ not hidden chain-of-thought or the entire worker transcript.
224
+
225
+ For usage, separate input, output, cache reads/writes, tool charges and cost. Preserve
226
+ provider category semantics: Pi's output accounting can already include reasoning,
227
+ so adding reasoning a second time would inflate totals. Distinguish known zero from
228
+ missing data and estimated cost from reported cost. A returned error can still have
229
+ billable usage. Include unsuccessful and cancelled attempts.
230
+
231
+ ## 6. Lifecycle
232
+
233
+ ```mermaid
234
+ stateDiagram-v2
235
+ [*] --> proposed
236
+ proposed --> rejected: admission fails
237
+ proposed --> queued: envelope and reservation created
238
+ queued --> running: capacity available; preconditions rechecked
239
+ queued --> cancelled: grant revoked or user cancels
240
+ running --> ready: complete payload and host validation
241
+ running --> incomplete: partial answer or missing context
242
+ running --> invalid: schema, evidence or truncation error
243
+ running --> failed: provider or broker failure
244
+ running --> cancelled: cancellation
245
+ running --> expired: deadline
246
+ ready --> stale: task or required source binding changes
247
+ ready --> accepted: parent adjudicates
248
+ ready --> discarded: parent rejects result
249
+ incomplete --> discarded
250
+ invalid --> discarded
251
+ failed --> discarded
252
+ ```
253
+
254
+ The diagram shows logical result state. Resource settlement is tracked separately:
255
+ `reserved`, `in_flight`, `settled`, or `uncertain`. `cancelled` does not assert that the
256
+ provider stopped billing. A cancelled or expired request cannot publish a late result.
257
+ An adapter that ignores cancellation retains its slot as still settling; do not start
258
+ new work under the fiction that no request is running.
259
+
260
+ Review output is accepted against its frozen target. On any relevant source change,
261
+ revalidate or mark it stale before use as completion evidence. Research citations may
262
+ remain informative after an unrelated code edit, but requirement/policy drift always
263
+ requires explicit parent reassessment. Scope determines relevance; a changed unrelated
264
+ file must not invalidate every independent research result.
265
+
266
+ Ordinary advancement of a session leaf is not branch navigation. Use a controller
267
+ epoch changed on actual task/policy navigation, not a rule that invalidates jobs after
268
+ each normal parent tool result. Pi 0.84.4 exposes `session_before_switch`,
269
+ `session_before_fork`, `session_before_tree`, `session_tree`, `session_start`, and
270
+ `session_shutdown`; there is no assumed `session_switch` or `session_fork` after-event.
271
+ Before-events may conservatively cancel work even if navigation is later declined.
272
+ Restarting after that is explicit, never a hidden retry.
273
+ [Pinned lifecycle contracts](https://github.com/earendil-works/pi/blob/v0.84.4/packages/coding-agent/src/core/extensions/types.ts#L522).
274
+
275
+ ## 7. Retries, follow-ups, cancellation and deduplication
276
+
277
+ Retry only a classified failure, with its reservation intact. Honor provider backoff
278
+ inside the original deadline. A transport retry after uncertain dispatch can duplicate
279
+ billing; label it accordingly. Do not promise exactly-once provider execution.
280
+
281
+ A `needs_context` follow-up creates a new immutable attempt under the same job ID. It
282
+ includes what changed and why that addresses the gap. The old result remains marked
283
+ incomplete. Call, tool, byte and deadline counters remain attached to the logical job
284
+ and batch; a follow-up cannot reset them. Limit it to one generation initially.
285
+
286
+ Deduplicate only exact active equivalents: same task and policy generation, mode,
287
+ question/acceptance contract, source hashes, model route and relevant settings. Return
288
+ the existing job identity for a repeated request. Do not infer that semantically similar
289
+ questions are equivalent. Cross-session result reuse is out of scope.
290
+
291
+ Cancellation must revoke broker access before awaiting provider settlement. Combine
292
+ the parent signal with job and batch deadlines. Recheck revocation and counters before
293
+ every complete tool invocation and model request. Partial streamed tool arguments are
294
+ never executable. Check terminal `stopReason` as well as rejected promises: Pi streams
295
+ can resolve an assistant message with `error` or `aborted` status. The scheduler's own
296
+ abort state remains authoritative when setup errors obscure cancellation.
297
+
298
+ ## 8. Scheduling and provider interface
299
+
300
+ The following pseudocode expresses ordering, not an available Pi API:
301
+
302
+ ```text
303
+ admit(request):
304
+ validate closed request and current human policy
305
+ bind immutable task, source and model generation
306
+ check exact deduplication key
307
+ reserve batch capacity atomically
308
+ enqueue only ready independent jobs
309
+
310
+ run(job):
311
+ acquire concurrency slot
312
+ recheck authority, generation, deadline and source binding
313
+ build explicit child context and broker schemas
314
+ while another request is allowed:
315
+ reserve next request before dispatch
316
+ response = InferencePort.request(context, admittedOptions, signal,
317
+ admissionBeforeEveryUnderlyingAttempt, rawByteLimit)
318
+ account for response, terminal status and uncertainty
319
+ reject incomplete tool calls or oversized messages
320
+ if final result: validate and stop
321
+ for each complete tool call in this response:
322
+ recheck grant, counters and cancellation
323
+ execute through EvidencePort; append bounded tool result
324
+ settle or quarantine outstanding resources
325
+ publish only if the job generation remains valid
326
+ ```
327
+
328
+ `InferencePort` and `EvidencePort` are proposed injected interfaces. The former must
329
+ be host-owned and preserve request-policy behavior; the latter is the closed broker.
330
+ The Pi 0.84.4 public registry completion facade is insufficient to claim full host
331
+ pipeline parity. [Target architecture compatibility gate](design.md#8-pi-runtime-and-package-boundary).
332
+
333
+ Each underlying inference attempt, including an internal SDK retry, must obtain a
334
+ controller reservation immediately before dispatch. Disable opaque retries when the
335
+ adapter cannot call this admission hook. Initial dispatch and each admitted retry get
336
+ separate attempt receipts; admission must not debit the initial attempt twice. The
337
+ same retry classifier, job retry count, deadlines, provider backoff and batch/session
338
+ ledgers apply. If the adapter cannot enforce this boundary, it cannot claim hard
339
+ attempt limits and must be rejected for the default policy. Silent provider fallback
340
+ to another model is also forbidden.
341
+
342
+ The port's raw-byte ceiling covers the entire decoded application response, including
343
+ stream framing, tool arguments, thinking, text and metadata, across bounded chunks.
344
+ Also bound decompression and transport buffering before handing bytes to a parser;
345
+ reject adapters with opaque unbounded buffering. Stop accumulating and signal abort
346
+ as soon as the next chunk would exceed the allowance. Do not parse or append the
347
+ oversized content, and do not truncate it into an apparently valid final object.
348
+ Record overflow and keep uncertain billing and non-cooperative requests in settlement
349
+ tracking. The byte bound is not a guarantee about provider-side generation or billing.
350
+
351
+ For asynchronous work, the host issues a job-lifetime lease containing immutable
352
+ admitted route data and cancellation hooks. Do not retain a stale invocation-scoped
353
+ extension context after `run` returns. The lease is revoked by actual task/policy
354
+ invalidation, user cancellation, shutdown or deadline; a normal parent tool return
355
+ must not accidentally expire it. Its lifetime and hook behavior are part of the
356
+ proposed host bridge, to be proven by integration fixtures.
357
+
358
+ Serialize tool calls within one worker initially. Parallelism is across admitted jobs.
359
+ If `pi-agent-core` is used to preserve message protocol correctness, explicitly configure
360
+ its tool execution policy; its default is parallel. Limits require pre-call enforcement,
361
+ not only `shouldStopAfterTurn`, which is evaluated after work already occurred.
362
+
363
+ No automatic sibling steering is necessary. If a finding invalidates another job's
364
+ premise, the parent cancels that job and submits an explicitly revised contract, charging
365
+ all abandoned work to the same evaluated task.
366
+
367
+ All batches also debit the session allowance. Starting another batch, changing an
368
+ ephemeral requirement set, or toggling the tool cannot reset consumed usage. A human
369
+ policy change must explicitly renew an exhausted allowance; it cannot erase the
370
+ reported cost of prior work.
371
+
372
+ ## 9. Protocol acceptance tests
373
+
374
+ Before a real provider is enabled, fixtures must demonstrate rejection of forged host
375
+ fields, unsupported versions, oversized or truncated output, nonexistent source IDs,
376
+ stale revisions, duplicate results, recursive delegation, denied tools and exhausted
377
+ reservations. Race tests must cover simultaneous admission, cancellation between a tool
378
+ decision and execution, and late results after task navigation.
379
+
380
+ A valid schema is only the beginning. An answer with valid references can still be
381
+ wrong. Parent adjudication and actual task checks are required for acceptance; a
382
+ protocol receipt must never be displayed as proof that the task itself is correct.