@gotgenes/pi-subagents 13.2.0 → 13.2.2

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 (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +21 -10
  3. package/dist/public.d.ts +34 -35
  4. package/docs/architecture/architecture.md +58 -148
  5. package/docs/architecture/history/phase-16-invert-dependencies.md +144 -0
  6. package/docs/decisions/0003-publish-bundled-type-declarations.md +3 -1
  7. package/docs/plans/0051-update-adr-0001-hard-fork.md +8 -6
  8. package/docs/plans/0257-extract-child-session-factory.md +3 -1
  9. package/docs/plans/0262-add-workspace-provider-seam.md +41 -39
  10. package/docs/plans/0264-remove-extension-lifecycle-control.md +100 -98
  11. package/docs/plans/0265-born-complete-subagent-session.md +5 -2
  12. package/docs/plans/0270-type-consumable-public-surface.md +3 -1
  13. package/docs/plans/0280-rename-agent-to-subagent.md +197 -0
  14. package/docs/retro/0051-update-adr-0001-hard-fork.md +4 -2
  15. package/docs/retro/0257-extract-child-session-factory.md +3 -1
  16. package/docs/retro/0262-add-workspace-provider-seam.md +3 -1
  17. package/docs/retro/0264-remove-extension-lifecycle-control.md +3 -1
  18. package/docs/retro/0270-type-consumable-public-surface.md +4 -2
  19. package/docs/retro/0277-encapsulate-agent-session.md +41 -0
  20. package/docs/retro/0280-rename-agent-to-subagent.md +96 -0
  21. package/package.json +1 -1
  22. package/src/index.ts +9 -9
  23. package/src/lifecycle/create-subagent-session.ts +1 -1
  24. package/src/lifecycle/{agent-manager.ts → subagent-manager.ts} +27 -27
  25. package/src/lifecycle/subagent-session.ts +2 -2
  26. package/src/lifecycle/{agent.ts → subagent.ts} +28 -28
  27. package/src/lifecycle/turn-limits.ts +1 -1
  28. package/src/lifecycle/workspace.ts +2 -2
  29. package/src/observation/notification.ts +9 -9
  30. package/src/observation/record-observer.ts +9 -9
  31. package/src/runtime.ts +1 -1
  32. package/src/service/service-adapter.ts +10 -10
  33. package/src/service/service.ts +5 -9
  34. package/src/tools/agent-tool.ts +5 -5
  35. package/src/tools/background-spawner.ts +3 -3
  36. package/src/tools/foreground-runner.ts +5 -5
  37. package/src/tools/get-result-tool.ts +2 -2
  38. package/src/tools/steer-tool.ts +2 -2
  39. package/src/types.ts +1 -1
  40. package/src/ui/agent-creation-wizard.ts +2 -2
  41. package/src/ui/agent-menu.ts +5 -5
  42. package/src/ui/agent-widget.ts +2 -2
  43. package/src/ui/conversation-viewer.ts +3 -3
@@ -0,0 +1,144 @@
1
+ # Phase 16: Invert dependencies — extensions on a minimal core
2
+
3
+ ## Summary
4
+
5
+ Phase 16 reclaimed its original intent — invert the core's outbound dependencies — and extended it: worktree isolation joined permissions as an *extension* on a minimal core, leaving pi-subagents a pure child-session orchestrator.
6
+ The decision and the full reasoning chain are recorded in [ADR-0002]; the two-surface extension model is described under [Target architecture](../architecture.md#target-architecture).
7
+
8
+ All five steps are closed: [#261], [#262], [#263], [#264], [#265].
9
+
10
+ ## Two extension surfaces
11
+
12
+ Extensions attach through exactly two surfaces, distinguished by the direction of information flow.
13
+
14
+ 1. **Lifecycle events (observational) — unlimited.**
15
+ The core emits awaited, ordered events for the child-execution lifecycle (`spawning`, `session-created` before `bindExtensions()`, `completed`, `disposed`).
16
+ Any number of extensions subscribe; handlers return nothing.
17
+ Reactive concerns live here: permission detection, telemetry, UI, notifications.
18
+ Adding a reactive concern never modifies the core.
19
+ 2. **Provider seams (generative) — rationed.**
20
+ The rare concern that must *inject* a value the core consumes synchronously registers a provider the core consults.
21
+ Today there is exactly one: the **workspace provider** (returns the child's working directory plus bracketed setup/teardown).
22
+ A provider seam is the only place the core is "open," so the list is kept as small as possible.
23
+
24
+ The discriminator when deciding how a concern attaches:
25
+
26
+ - It only needs to **know** what happened → subscribe to a lifecycle event (observational, unlimited).
27
+ - It must **return a value the core consumes** → register a provider (generative, rationed).
28
+
29
+ The governing rule — **no vacant hooks**: the architecture must *admit* a seam without *shipping* it until a concrete consumer exists.
30
+ A provider seam with no consumer is a speculative abstraction that taxes every reader and that `fallow` flags as dead.
31
+ Latent extensibility is the deliverable; a vacant hook is not.
32
+
33
+ ## Abandoned exploration: agent collaborator architecture
34
+
35
+ An earlier Phase 16 plan ("agent collaborator architecture") proposed giving `Agent` three collaborators — a session factory, a `WorktreeIsolation`, and a lifecycle observer — and dissolving the runner.
36
+ That framing was abandoned.
37
+ Pulling on a single late-bound `create(cwd?)` parameter on the planned `ChildSessionFactory` exposed deeper problems:
38
+
39
+ - `WorktreeIsolation.setup()` is a two-phase `construct-then-setup()` that violates "Construct complete" (principle 8) — the worktree is only *ready* at dequeue.
40
+ - The worktree and the child session share one lifespan, so they are one run-scoped resource, not sibling collaborators that `Agent` must sequence; the `cwd` parameter only existed because the worktree was split out and `Agent` relayed its output back in.
41
+ - Worktrees are not intrinsic to subagents — they are one *workspace strategy* and belong outside the core, exactly as Phase 14 evicted tool/extension policy.
42
+
43
+ Issue #256 (`WorktreeIsolation` as a collaborator) shipped under the abandoned plan and was superseded by #263; issue #257 (`ChildSessionFactory` extraction) was parked.
44
+ Issues #258 (Agent owns session lifecycle via factory) and #259 (dissolve runner concept) belonged to the same abandoned plan — both depended on #256/#257 and were closed as not planned; their structural goals were recovered by Step 5 (#265) via a cleaner route.
45
+ The structural win the collaborator plan chased — a born-complete child execution and the dissolution of the runner — is recovered once the workspace seam exists (Step 5).
46
+
47
+ ## Steps
48
+
49
+ ### Step 1: Child-execution lifecycle events; retire permission-bridge — [#261]
50
+
51
+ Emit ordered child-execution events (`spawning`, `session-created` before `bindExtensions()`, `completed`, `disposed`) carrying child identity (session directory, agent name, parent session id).
52
+ Migrated `@gotgenes/pi-permission-system` to subscribe to `session-created`/`disposed` for registration instead of being looked up by the core; deleted `permission-bridge.ts`.
53
+
54
+ - Cross-package: pi-subagents (emit + remove bridge) and pi-permission-system (subscribe).
55
+ - Investigation (resolved): `pi.events` is a Node `EventEmitter`, so `emit()` dispatches listeners synchronously on the same call stack — a synchronous subscriber completes before `emit()` returns.
56
+ Emitting `session-created` immediately before `bindExtensions()` therefore guarantees the registry entry lands pre-bind, with no new SDK hook.
57
+ The synchronous-handler constraint is encoded as a real-bus test in pi-permission-system.
58
+ - Outcome: the core stops reaching out to a named consumer; permission detection rides events.
59
+ - Deferred: removing the now-caller-less `registerSubagentSession`/`unregisterSubagentSession` from `PermissionsService` → #267; registry-detected resume ("executing now" → "exists" semantics) → #265.
60
+
61
+ ### Step 2: Define the `WorkspaceProvider` seam — [#262]
62
+
63
+ Added the `WorkspaceProvider` / `Workspace` interfaces (`src/lifecycle/workspace.ts`) and `SubagentsService.registerWorkspaceProvider` (single provider, throws on duplicate, returns an unregister disposer).
64
+ All five workspace types are named-re-exported from `service.ts`: `WorkspaceProvider`, `Workspace`, `WorkspacePrepareContext`, `WorkspaceDisposeOutcome`, and `WorkspaceDisposeResult` (added in #272).
65
+ At run-start `Agent.run()` consults the registered provider for the child's cwd and a disposal handle; with no provider the child runs in the parent's cwd.
66
+ On completion the core calls `Workspace.dispose({ status, description })` and appends the returned `resultAddendum` verbatim — the provider owns the wording.
67
+
68
+ - The seam is additive and non-breaking.
69
+ - Landed alongside its first consumer (Step 3) to avoid a vacant hook — the "no vacant hooks" rule.
70
+ - Outcome: a single generative seam; the core no longer knows what an "isolation strategy" is.
71
+
72
+ ### Step 3: Extract worktrees to `@gotgenes/pi-subagents-worktrees` — [#263]
73
+
74
+ New package implementing `WorkspaceProvider`: prepares a git worktree at run-start (born complete), tears it down after (saving the branch), and owns the "changes saved to branch" result.
75
+ Worktree isolation is opt-in per agent type via the package's own `worktreeAgents` config; creation failure for an opted-in agent throws (strict, no silent fallback).
76
+ Removed `worktree.ts`, `worktree-isolation.ts`, `GitWorktreeManager`, and the `isolation: "worktree"` mode from the core; dropped `isolation` from the spawn API and `SubagentsService`, and `worktreeResult` from `SubagentRecord`.
77
+
78
+ - Supersedes #256.
79
+ New package registered in `release-please-config.json` and `.pi/settings.json` (after pi-subagents); consumes the published `@gotgenes/pi-subagents` from the registry (`linkWorkspacePackages: false`).
80
+ - Outcome: git leaves the core; worktree users install one package, everyone else pays nothing.
81
+
82
+ ### Step 4: Remove `isolated` / `extensions: false` / `noSkills` — [#264]
83
+
84
+ Children always load the parent's extensions and skills; the recursion guard is now unconditional.
85
+ Deny-at-use (the in-child permission layer) covers tool restriction; prevent-load is left as a latent provider seam (not shipped).
86
+ The `skills` curation axis collapsed symmetrically with `extensions`: `AgentConfig.skills`, the skill-preload path (`skill-loader.ts`, `safe-fs.ts`, `preloadSkills`, `PromptExtras`), `SessionConfig.{extensions,noSkills,extras}`, and the `isolated:` / `extensions:` / `skills:` custom-agent frontmatter keys are all gone.
87
+
88
+ - Depended on: Step 1 (deny-at-use over events).
89
+ - Outcome: the `isolated`/`extensions`/`noSkills`/`skills` axis is gone; the guard is unconditional.
90
+
91
+ ### Step 5: Born-complete child execution; dissolve the runner — [#265]
92
+
93
+ `createSubagentSession()` is an assembly factory that returns a born-complete `SubagentSession` (session created, extensions bound, recursion guard applied).
94
+ `SubagentSession` owns turn driving (`runTurnLoop`/`resumeTurnLoop`), steering, and disposal.
95
+ `Agent.run()` is coordination, not assembly; `runAgent` / `resumeAgent` / `ConcreteAgentRunner` / `AgentRunner` / `RunOptions` / `RunResult` / `ExecutionState` dissolved.
96
+ `getAgentConversation()` relocated to `session/conversation.ts`; `normalizeMaxTurns()` to `lifecycle/turn-limits.ts`.
97
+ `disposed` now fires at true session disposal (cleanup), so resume executions are registry-detected (closing the gap deferred from #261).
98
+
99
+ - Depends on: Steps 2–4.
100
+ - Outcome: the "runner" concept is gone; `Agent.run()` is coordination, not assembly — the structural goal of the abandoned collaborator plan, reached cleanly.
101
+
102
+ ## Step dependency diagram
103
+
104
+ ```mermaid
105
+ flowchart LR
106
+ S1["Step 1<br/>Lifecycle events<br/>(retire bridge)"]
107
+ S2["Step 2<br/>WorkspaceProvider seam"]
108
+ S3["Step 3<br/>Extract worktrees pkg"]
109
+ S4["Step 4<br/>Remove isolated"]
110
+ S5["Step 5<br/>Born-complete execution"]
111
+
112
+ S2 --> S3
113
+ S1 --> S4
114
+ S2 --> S5
115
+ S3 --> S5
116
+ S4 --> S5
117
+ ```
118
+
119
+ ## Tracks
120
+
121
+ 1. **Track A — Inversion seams** (Steps 1, 2): lifecycle events and the workspace seam.
122
+ Independent of each other — proceeded in parallel.
123
+ 2. **Track B — Eviction** (Steps 3, 4): worktrees and `isolated` leave the core.
124
+ Step 3 depends on Step 2.
125
+ 3. **Track C — Consolidation** (Step 5): dissolve the runner around the new seam.
126
+ Depends on Tracks A and B.
127
+
128
+ ## Composition model
129
+
130
+ In the post-Phase-16 state, pi-subagents publishes events and a provider seam; other packages hook in:
131
+
132
+ - **pi-permission-system** (observational) subscribes to child-session lifecycle events, detects subagent execution context in the child, and gates tool calls at runtime.
133
+ - **pi-subagents-worktrees** (generative) registers a `WorkspaceProvider` that prepares a git worktree at run-start and tears it down after, supplying the child's cwd.
134
+ - **pi-subagents-ui** (future, Phase 17) subscribes to the service API, renders the widget, conversation viewer, and `/agents` menu.
135
+ - **Any future extension** (OTel, auditing, cost tracking) subscribes to the same events without pi-subagents knowing.
136
+
137
+ Composition test: install neither extension, only permissions, only workspaces, or both — the core is byte-for-byte identical in all four cases, and the two extensions never reference each other.
138
+
139
+ [#261]: https://github.com/gotgenes/pi-packages/issues/261
140
+ [#262]: https://github.com/gotgenes/pi-packages/issues/262
141
+ [#263]: https://github.com/gotgenes/pi-packages/issues/263
142
+ [#264]: https://github.com/gotgenes/pi-packages/issues/264
143
+ [#265]: https://github.com/gotgenes/pi-packages/issues/265
144
+ [ADR-0002]: ../../decisions/0002-extensions-on-a-minimal-core.md
@@ -27,7 +27,7 @@ A `tsc --traceResolution` of a sibling consuming the package surfaced two compou
27
27
  The public entry's type closure is deeply entangled: `WorkspaceProvider` (in `lifecycle/workspace.ts`) reaches `AgentStatus` in the 510-line `lifecycle/agent.ts`, plus `SubagentType`/`AgentInvocation` from `types.ts` (which itself re-exports the `Agent` class).
28
28
  A shallow alias-free entry is therefore not achievable without a substantial source restructure.
29
29
 
30
- This collides with the ship-source model (ADR 0002): every package ships raw `.ts` executed directly by Pi, with no build step.
30
+ This collides with the ship-source model ([ADR-0002]): every package ships raw `.ts` executed directly by Pi, with no build step.
31
31
 
32
32
  ## Decision
33
33
 
@@ -67,3 +67,5 @@ It is deliberately narrow: it produces type declarations only and changes nothin
67
67
  - The `types` condition points at a build-time artifact; an in-repo workspace-linked consumer that imported the package would need `dist/public.d.ts` present.
68
68
  This is acceptable because no in-repo package imports the surface yet; #263 consumes the built artifact from the published tarball.
69
69
  - Sequencing: #270 must be published (its release-please PR merged) before #263 edits `pi-subagents` core, so #263's changes do not batch into the same `pi-subagents` release.
70
+
71
+ [ADR-0002]: ./0002-extensions-on-a-minimal-core.md
@@ -3,14 +3,14 @@ issue: 51
3
3
  issue_title: "docs: update ADR 0001 to reflect hard-fork decision"
4
4
  ---
5
5
 
6
- # Update ADR 0001 to reflect hard-fork decision
6
+ # Update ADR-0001 to reflect hard-fork decision
7
7
 
8
8
  ## Problem Statement
9
9
 
10
- ADR 0001 (`docs/decisions/0001-deferred-patches.md`) was written when the fork was a thin-patch layer over `tintinweb/pi-subagents`.
10
+ [ADR-0001] was written when the fork was a thin-patch layer over `tintinweb/pi-subagents`.
11
11
  The new architecture document (`docs/architecture/architecture.md`) commits to a hard fork with material scope reduction — scheduling removal, a `SubagentsAPI` boundary, `index.ts` decomposition, and more.
12
12
 
13
- Several claims in ADR 0001 are now outdated:
13
+ Several claims in [ADR-0001] are now outdated:
14
14
 
15
15
  1. The status is "accepted" but the decision has been superseded by the architecture doc.
16
16
  2. The Upstream PRs section states "the fork's divergence reduces to package naming and tooling," which is no longer true.
@@ -18,7 +18,7 @@ Several claims in ADR 0001 are now outdated:
18
18
 
19
19
  ## Goals
20
20
 
21
- - Add a supersession note to ADR 0001 pointing to `docs/architecture/architecture.md`.
21
+ - Add a supersession note to [ADR-0001] pointing to `docs/architecture/architecture.md`.
22
22
  - Update the "Upstream PRs are open" subsection so the "divergence reduces to…" claim reflects reality.
23
23
  - Update the Consequences → Operational section to note intentional divergence per the architecture document.
24
24
  - Preserve all existing rationale — no information is removed.
@@ -31,7 +31,7 @@ Several claims in ADR 0001 are now outdated:
31
31
 
32
32
  ## Background
33
33
 
34
- ADR 0001 has YAML frontmatter (`status: accepted`, `date: 2026-05-11`) and follows a standard ADR structure: Status, Context, Decision, Consequences.
34
+ [ADR-0001] has YAML frontmatter (`status: accepted`, `date: 2026-05-11`) and follows a standard ADR structure: Status, Context, Decision, Consequences.
35
35
 
36
36
  The architecture document (`docs/architecture/architecture.md`) describes a six-phase plan that materially diverges from upstream: scheduling removal, ad-hoc RPC replacement, group-join and output-file removal, a typed `SubagentsAPI` boundary, and `index.ts` decomposition.
37
37
 
@@ -60,7 +60,7 @@ No tests are affected — this is a docs-only change.
60
60
 
61
61
  ## TDD Order
62
62
 
63
- 1. Update ADR 0001 with all four edits described above.
63
+ 1. Update [ADR-0001] with all four edits described above.
64
64
  Commit: `docs: update ADR 0001 to reflect hard-fork decision (#51)`
65
65
 
66
66
  ## Risks and Mitigations
@@ -73,3 +73,5 @@ No tests are affected — this is a docs-only change.
73
73
  ## Open Questions
74
74
 
75
75
  None — the issue's acceptance criteria are unambiguous.
76
+
77
+ [ADR-0001]: ../decisions/0001-deferred-patches.md
@@ -6,7 +6,7 @@ issue_title: "Extract ChildSessionFactory from runner"
6
6
  # Extract ChildSessionFactory from runner
7
7
 
8
8
  > Superseded — issue #257 was closed `not_planned`.
9
- > Planning this extraction exposed that worktree isolation does not belong in the core; see [ADR 0002](../decisions/0002-extensions-on-a-minimal-core.md) and the reclaimed Phase 16 roadmap in [`docs/architecture/architecture.md`](../architecture/architecture.md).
9
+ > Planning this extraction exposed that worktree isolation does not belong in the core; see [ADR-0002] and the reclaimed Phase 16 roadmap in [`docs/architecture/architecture.md`](../architecture/architecture.md).
10
10
  > The structural goal is recovered by #265.
11
11
  > This plan is retained for historical context only.
12
12
 
@@ -281,3 +281,5 @@ After all steps: `pnpm run check`, `pnpm run lint`, `pnpm -r run test`, `pnpm fa
281
281
  Deferred to Step 4, when the runner dissolves and the natural home for these creation contracts is the factory module.
282
282
  - Whether `ConcreteAgentRunner.createFactory()` lands in Step 3 (when `AgentManager` consumes it) exactly as the issue describes.
283
283
  Deferred to Step 3 per the Design Overview rationale.
284
+
285
+ [ADR-0002]: ../decisions/0002-extensions-on-a-minimal-core.md
@@ -7,50 +7,50 @@ issue_title: "Add WorkspaceProvider extension seam"
7
7
 
8
8
  ## Problem Statement
9
9
 
10
- Phase 16, Step 2 of ADR 0002 (`packages/pi-subagents/docs/decisions/0002-extensions-on-a-minimal-core.md`).
11
- The core needs only a working directory and a disposal hook for a child run; the default the parent's cwd, with no setup or teardown is always correct.
10
+ Phase 16, Step 2 of [ADR-0002].
11
+ The core needs only a working directory and a disposal hook for a child run; the default - the parent's cwd, with no setup or teardown - is always correct.
12
12
  "Where does a child run, and what brackets the run?"
13
13
  is a *strategy* (git worktree, container, tmpdir, remote sandbox), not core behavior.
14
- ADR 0002 classifies this as the single *generative* extension surface: a concern that must return a value the core consumes synchronously attaches through a rationed provider seam, not an observational event.
15
- This issue adds that seam — `WorkspaceProvider` / `Workspace` plus `SubagentsService.registerWorkspaceProvider` without the core gaining any knowledge of what an "isolation strategy" is.
14
+ [ADR-0002] classifies this as the single *generative* extension surface: a concern that must return a value the core consumes synchronously attaches through a rationed provider seam, not an observational event.
15
+ This issue adds that seam -`WorkspaceProvider` / `Workspace` plus `SubagentsService.registerWorkspaceProvider` - without the core gaining any knowledge of what an "isolation strategy" is.
16
16
 
17
17
  ## Goals
18
18
 
19
19
  - Define the `WorkspaceProvider` and `Workspace` interfaces in the core, with zero git or worktree knowledge.
20
- - Add `SubagentsService.registerWorkspaceProvider(provider): () => void` a single-provider seam (chaining is out of scope) that throws if a provider is already registered and returns an unregister disposer.
21
- - At run-start, consult the registered provider for the child's cwd and a disposal handle; with no provider, the child runs in `baseCwd` (parent cwd default behavior unchanged).
20
+ - Add `SubagentsService.registerWorkspaceProvider(provider): () => void` - a single-provider seam (chaining is out of scope) that throws if a provider is already registered and returns an unregister disposer.
21
+ - At run-start, consult the registered provider for the child's cwd and a disposal handle; with no provider, the child runs in `baseCwd` (parent cwd - default behavior unchanged).
22
22
  - Call `dispose()` after the run and append the returned `resultAddendum` to the child's result.
23
- - This change is **additive and non-breaking** the existing `isolation: "worktree"` path is left intact (its eviction is #263).
23
+ - This change is **additive and non-breaking** - the existing `isolation: "worktree"` path is left intact (its eviction is #263).
24
24
 
25
25
  ## Non-Goals
26
26
 
27
- - Removing `worktree.ts`, `worktree-isolation.ts`, `GitWorktreeManager`, or the `isolation: "worktree"` spawn mode deferred to #263.
28
- - Removing `isolated` / `extensions: false` / `noSkills` deferred to #264.
29
- - Born-complete child execution / dissolving the runner deferred to #265.
30
- - Multiple/chained providers out of scope per the issue; one provider only.
31
- - Shipping a concrete provider implementation the worktrees package (#263) is the seam's first real consumer.
27
+ - Removing `worktree.ts`, `worktree-isolation.ts`, `GitWorktreeManager`, or the `isolation: "worktree"` spawn mode - deferred to #263.
28
+ - Removing `isolated` / `extensions: false` / `noSkills` - deferred to #264.
29
+ - Born-complete child execution / dissolving the runner - deferred to #265.
30
+ - Multiple/chained providers - out of scope per the issue; one provider only.
31
+ - Shipping a concrete provider implementation - the worktrees package (#263) is the seam's first real consumer.
32
32
  Within this issue the seam is exercised only by test fakes; see Risks for the "no vacant hooks" release-coordination constraint.
33
33
 
34
34
  ## Background
35
35
 
36
36
  Relevant existing modules:
37
37
 
38
- - `src/lifecycle/agent.ts` `Agent.run()` calls `this.worktree?.setup()` at run-start to obtain a cwd, threads it into `runner.run({ context: { cwd } })`, and on completion calls `this.worktree?.cleanup(description)`, appending a "Changes saved to branch " addendum.
38
+ - `src/lifecycle/agent.ts` - `Agent.run()` calls `this.worktree?.setup()` at run-start to obtain a cwd, threads it into `runner.run({ context: { cwd } })`, and on completion calls `this.worktree?.cleanup(description)`, appending a "Changes saved to branch ..." addendum.
39
39
  This is exactly the prepare/dispose shape the seam generalizes.
40
- - `src/lifecycle/worktree-isolation.ts` `WorktreeIsolation` is the current run-scoped collaborator: `setup()` returns a path, `cleanup(description)` returns a `WorktreeCleanupResult`.
40
+ - `src/lifecycle/worktree-isolation.ts` - `WorktreeIsolation` is the current run-scoped collaborator: `setup()` returns a path, `cleanup(description)` returns a `WorktreeCleanupResult`.
41
41
  The seam is its abstraction; #263 will reimplement it as a `WorkspaceProvider` in a separate package.
42
- - `src/lifecycle/agent-manager.ts` constructs each `Agent`, owns the injected `WorktreeManager`, and threads `getRunConfig` as a getter.
42
+ - `src/lifecycle/agent-manager.ts` - constructs each `Agent`, owns the injected `WorktreeManager`, and threads `getRunConfig` as a getter.
43
43
  The same getter pattern is reused for the workspace provider.
44
- - `src/service/service.ts` the package's public API surface (`package.json` `exports` points at `./src/service.ts`).
44
+ - `src/service/service.ts` - the package's public API surface (`package.json` `exports` points at `./src/service.ts`).
45
45
  `SubagentsService`, `SpawnOptions`, and `SubagentRecord` all live here; the seam types are re-exported here so the worktrees package can implement them.
46
- - `src/service/service-adapter.ts` `SubagentsServiceAdapter implements SubagentsService`, wrapping the `AgentManagerLike` narrow interface.
47
- - `src/lifecycle/child-lifecycle.ts` the *observational* lifecycle events from #261 (`spawning`, `session-created`, `completed`, `disposed`).
46
+ - `src/service/service-adapter.ts` - `SubagentsServiceAdapter implements SubagentsService`, wrapping the `AgentManagerLike` narrow interface.
47
+ - `src/lifecycle/child-lifecycle.ts` - the *observational* lifecycle events from #261 (`spawning`, `session-created`, `completed`, `disposed`).
48
48
  The provider seam is orthogonal: events tell consumers what happened; the provider returns a value the core consumes.
49
49
 
50
50
  AGENTS.md constraints that apply:
51
51
 
52
- - Pi SDK imports stay out of library modules the seam interfaces and `AgentManager` accept the provider as a parameter; `index.ts` (the SDK edge) supplies `baseCwd: process.cwd()`.
53
- - Do not read `process.cwd()` inside library functions `baseCwd` is injected into `AgentManager` from `index.ts`.
52
+ - Pi SDK imports stay out of library modules - the seam interfaces and `AgentManager` accept the provider as a parameter; `index.ts` (the SDK edge) supplies `baseCwd: process.cwd()`.
53
+ - Do not read `process.cwd()` inside library functions - `baseCwd` is injected into `AgentManager` from `index.ts`.
54
54
  - When adding a public API pattern, follow the established convention: the repo's registration/subscription convention is an unsubscribe **function** (`() => void`, as in `SubscribableSession.subscribe` and `pi.events.on`), not a `Symbol.dispose` `Disposable`.
55
55
  The seam therefore returns `() => void`; this is a deliberate divergence from the issue's literal `Disposable` to match the codebase convention.
56
56
 
@@ -97,10 +97,10 @@ export interface WorkspaceProvider {
97
97
  ```
98
98
 
99
99
  Note the addendum-formatting boundary: the core appends `resultAddendum` *verbatim*.
100
- The provider owns its own separator and wording (the worktrees package owns the "Changes saved to branch " string in #263).
100
+ The provider owns its own separator and wording (the worktrees package owns the "Changes saved to branch ..." string in #263).
101
101
  The core never formats branch text.
102
102
 
103
- ### Registration single provider, throw on duplicate
103
+ ### Registration - single provider, throw on duplicate
104
104
 
105
105
  `AgentManager` holds an optional provider and exposes registration:
106
106
 
@@ -130,7 +130,7 @@ The disposer clears the slot only if the same provider is still active, so a sta
130
130
  Provider-first precedence: when a provider supplies a workspace, the core routes cwd and dispose through it and skips the legacy worktree collaborator; with no provider it falls back to the existing worktree path; with neither it runs in `baseCwd` (cwd undefined → SDK uses the parent cwd).
131
131
 
132
132
  ```typescript
133
- // run() replacing the worktree?.setup() block
133
+ // run() - replacing the worktree?.setup() block
134
134
  let cwd: string | undefined;
135
135
  try {
136
136
  const provider = this._getWorkspaceProvider?.();
@@ -152,7 +152,7 @@ try {
152
152
  this.observer?.onRunFinished?.(this);
153
153
  return;
154
154
  }
155
- // runner.run({ context: { cwd, parentSession }, })
155
+ // ... runner.run({ context: { cwd, parentSession }, ... })
156
156
  ```
157
157
 
158
158
  On completion (`completeRun`) the core computes the final status, then disposes:
@@ -165,7 +165,7 @@ if (this._workspace) {
165
165
  if (out?.resultAddendum) finalResult += out.resultAddendum;
166
166
  } else {
167
167
  const wt = this.worktree?.cleanup(this.description);
168
- if (wt?.hasChanges && wt.branch) finalResult += `\n\n---\nChanges saved to branch \`${wt.branch}\`…`;
168
+ if (wt?.hasChanges && wt.branch) finalResult += `\n\n---\nChanges saved to branch \`${wt.branch}\`...`;
169
169
  }
170
170
  ```
171
171
 
@@ -197,7 +197,7 @@ Provider-first precedence means the two never silently conflict, and #263 collap
197
197
  | `src/lifecycle/agent-manager.ts` | `AgentManagerOptions` gains required `baseCwd: string`. New `workspaceProvider` field, `registerWorkspaceProvider()` (throw on dup, unregister disposer). `spawn()` passes `baseCwd` and the `getWorkspaceProvider` getter into each `Agent`. |
198
198
  | `src/service/service.ts` | Re-export the five seam types and `AgentStatus`. Add `registerWorkspaceProvider(provider: WorkspaceProvider): () => void` to the `SubagentsService` interface. |
199
199
  | `src/service/service-adapter.ts` | `AgentManagerLike` gains `registerWorkspaceProvider(provider): () => void`. `SubagentsServiceAdapter` implements the method, delegating to the manager. |
200
- | `src/index.ts` | Pass `baseCwd: process.cwd()` to the `new AgentManager({})` construction (alongside the existing `GitWorktreeManager(process.cwd())`). |
200
+ | `src/index.ts` | Pass `baseCwd: process.cwd()` to the `new AgentManager({...})` construction (alongside the existing `GitWorktreeManager(process.cwd())`). |
201
201
  | `docs/architecture/architecture.md` | Mark Phase 16 Step 2 (#262) as landed in the roadmap; note the seam exists and `workspace.ts` is added to the lifecycle domain listing. |
202
202
 
203
203
  No exports are removed or renamed, so no `src/`/`test/` removed-symbol grep is required.
@@ -206,8 +206,8 @@ No file in Module-Level Changes is also claimed as unchanged in Non-Goals (the w
206
206
  ### Grep checklist before finalizing
207
207
 
208
208
  - Objects typed as `SubagentsService` in tests: `test/service/service.test.ts` casts `{ spawn: () => "id" } as unknown as SubagentsService`, so adding an interface method does **not** break it (verified).
209
- - `new AgentManager(` call sites: `src/index.ts` (one) and `test/lifecycle/agent-manager.test.ts` `createManager` (one) both updated for required `baseCwd` in the same step.
210
- - `AgentManagerLike` mocks in `test/service/service-adapter.test.ts` (`defaultManager`, inline `spawn:` stubs) add `registerWorkspaceProvider` stub in the same step.
209
+ - `new AgentManager(` call sites: `src/index.ts` (one) and `test/lifecycle/agent-manager.test.ts` `createManager` (one) - both updated for required `baseCwd` in the same step.
210
+ - `AgentManagerLike` mocks in `test/service/service-adapter.test.ts` (`defaultManager`, inline `spawn:` stubs) - add `registerWorkspaceProvider` stub in the same step.
211
211
 
212
212
  ## Test Impact Analysis
213
213
 
@@ -216,36 +216,36 @@ This is an additive seam, so the work is dominated by *new* tests; little existi
216
216
  1. New unit tests the seam enables: provider registration (throw-on-duplicate, disposer-unregisters), run-start consultation (cwd from `prepare`, `resultAddendum` appended on dispose), `prepare` returns undefined → `baseCwd`, `prepare` rejects → `markError`, and adapter delegation.
217
217
  These were impossible before because there was no provider abstraction to substitute.
218
218
  2. Redundant existing tests: none.
219
- The seam does not subsume worktree tests they exercise the legacy path, which is preserved.
220
- 3. Existing tests that must stay as-is: all `worktree.test.ts`, `worktree-isolation.test.ts`, and the AgentManager worktree-isolation tests (`calls worktrees.create` / `cleanup`) they genuinely exercise the fallback path that remains in #262.
219
+ The seam does not subsume worktree tests - they exercise the legacy path, which is preserved.
220
+ 3. Existing tests that must stay as-is: all `worktree.test.ts`, `worktree-isolation.test.ts`, and the AgentManager worktree-isolation tests (`calls worktrees.create` / `cleanup`) - they genuinely exercise the fallback path that remains in #262.
221
221
  The Agent no-provider tests assert unchanged worktree behavior.
222
222
 
223
223
  ## TDD Order
224
224
 
225
- 1. **Seam types + registration surface** `feat`.
225
+ 1. **Seam types + registration surface** - `feat`.
226
226
  New `src/lifecycle/workspace.ts`; re-export seam types + `AgentStatus` from `service.ts`; add `registerWorkspaceProvider` to `SubagentsService`, `AgentManagerLike`, and `SubagentsServiceAdapter` (delegating); add required `baseCwd` + provider field + `registerWorkspaceProvider` (throw on dup, disposer) to `AgentManager`; update `index.ts` and the `createManager` test factory for `baseCwd`.
227
227
  Tests: `agent-manager.test.ts` registration (throws on second register; disposer clears only the active provider; getter returns the registered provider) and `service-adapter.test.ts` delegation.
228
- This whole surface lands in one commit because the `SubagentsService` interface method forces the adapter to implement it and the required `baseCwd` forces both construction sites splitting would not type-check.
228
+ This whole surface lands in one commit because the `SubagentsService` interface method forces the adapter to implement it and the required `baseCwd` forces both construction sites - splitting would not type-check.
229
229
  Suggested message: `feat: add WorkspaceProvider registration seam to subagents service`.
230
230
  Run `pnpm run check` immediately after (shared-interface change).
231
231
 
232
- 2. **Run-start consumption + dispose** `feat`.
232
+ 2. **Run-start consumption + dispose** - `feat`.
233
233
  `Agent`: `AgentInit` gains `baseCwd`/`getWorkspaceProvider`; new private fields; `run()` provider-first prepare; `completeRun`/`failRun` dispose + verbatim `resultAddendum`.
234
234
  `AgentManager.spawn` passes `baseCwd` and the `getWorkspaceProvider` getter (sole extra construction site, folded in).
235
- Tests: `agent.test.ts` provider `prepare` supplies cwd to the runner; `dispose` `resultAddendum` appended to the result; `prepare` undefined → cwd falls back to `baseCwd`; `prepare` rejects → `markError` + `onRunFinished`; no-provider path still uses the worktree collaborator (regression guard).
235
+ Tests: `agent.test.ts` - provider `prepare` supplies cwd to the runner; `dispose` `resultAddendum` appended to the result; `prepare` undefined → cwd falls back to `baseCwd`; `prepare` rejects → `markError` + `onRunFinished`; no-provider path still uses the worktree collaborator (regression guard).
236
236
  Suggested message: `feat: consult workspace provider for child cwd and disposal`.
237
237
  Run `pnpm run check` after (AgentInit change).
238
238
 
239
- 3. **Architecture doc update** `docs`.
239
+ 3. **Architecture doc update** - `docs`.
240
240
  Mark Phase 16 Step 2 (#262) landed in the roadmap; add `workspace.ts` to the lifecycle domain listing; cross-link the seam.
241
241
  Suggested message: `docs: record WorkspaceProvider seam in phase 16 roadmap`.
242
242
 
243
243
  ## Risks and Mitigations
244
244
 
245
245
  - **Vacant hook (the headline risk).**
246
- ADR 0002's "no vacant hooks" rule says a provider seam with no consumer is a speculative abstraction that `fallow` flags as dead.
246
+ [ADR-0002]’s no vacant hooks rule says a provider seam with no consumer is a speculative abstraction that `fallow` flags as dead.
247
247
  Within #262 the seam is exercised only by test fakes.
248
- Mitigation: land #262 **alongside** #263 (its first real consumer, `@gotgenes/pi-subagents-worktrees`) do not cut a release that contains the seam without the worktrees package.
248
+ Mitigation: land #262 **alongside** #263 (its first real consumer, `@gotgenes/pi-subagents-worktrees`) - do not cut a release that contains the seam without the worktrees package.
249
249
  Track this as a release-coordination constraint; the architecture roadmap already pairs Steps 2 and 3.
250
250
  - **Dual cwd path confusion.**
251
251
  Provider-first precedence keeps worktree and provider from silently conflicting; the branch is documented and removed in #263.
@@ -257,6 +257,8 @@ This is an additive seam, so the work is dominated by *new* tests; little existi
257
257
  ## Open Questions
258
258
 
259
259
  - Should `baseCwd` eventually come from the parent `SessionContext.cwd` rather than `process.cwd()`?
260
- Deferred — `process.cwd()` preserves current worktree behavior; revisit during the born-complete work (#265).
260
+ Deferred -`process.cwd()` preserves current worktree behavior; revisit during the born-complete work (#265).
261
261
  - Should the `disposed` lifecycle event (#261) and `Workspace.dispose` be reconciled into one teardown notion?
262
- Deferred they serve different surfaces (observational vs generative); revisit if #265 dissolves the runner.
262
+ Deferred - they serve different surfaces (observational vs generative); revisit if #265 dissolves the runner.
263
+
264
+ [ADR-0002]: ../decisions/0002-extensions-on-a-minimal-core.md