@gotgenes/pi-subagents 11.5.0 → 12.0.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.
- package/CHANGELOG.md +20 -0
- package/dist/public.d.ts +170 -0
- package/docs/architecture/architecture.md +14 -16
- package/docs/decisions/0003-publish-bundled-type-declarations.md +69 -0
- package/docs/plans/0270-type-consumable-public-surface.md +202 -0
- package/docs/retro/0262-add-workspace-provider-seam.md +43 -0
- package/docs/retro/0270-type-consumable-public-surface.md +106 -0
- package/package.json +18 -2
- package/src/config/custom-agents.ts +0 -1
- package/src/config/invocation-config.ts +1 -4
- package/src/index.ts +0 -2
- package/src/lifecycle/agent-manager.ts +1 -14
- package/src/lifecycle/agent.ts +10 -24
- package/src/service/service-adapter.ts +0 -3
- package/src/service/service.ts +3 -4
- package/src/tools/agent-tool.ts +1 -7
- package/src/tools/background-spawner.ts +0 -1
- package/src/tools/foreground-runner.ts +0 -1
- package/src/tools/spawn-config.ts +1 -5
- package/src/types.ts +0 -6
- package/src/ui/agent-config-editor.ts +0 -1
- package/src/ui/agent-creation-wizard.ts +0 -1
- package/src/ui/display.ts +0 -1
- package/src/lifecycle/worktree-isolation.ts +0 -59
- package/src/lifecycle/worktree.ts +0 -194
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [12.0.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v11.6.0...pi-subagents-v12.0.0) (2026-05-29)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### ⚠ BREAKING CHANGES
|
|
12
|
+
|
|
13
|
+
* SubagentRecord no longer carries worktreeResult, and the core no longer creates git worktrees. Worktree isolation moves to @gotgenes/pi-subagents-worktrees.
|
|
14
|
+
* the Agent tool no longer accepts isolation: "worktree", and SubagentsService.SpawnOptions no longer has an isolation field. Install @gotgenes/pi-subagents-worktrees and list the agent in worktreeAgents instead.
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* drop the isolation spawn axis from the subagents API ([2ff8970](https://github.com/gotgenes/pi-packages/commit/2ff897059feec67a49af7e3f54e0e4828faa2521))
|
|
19
|
+
* remove git worktree isolation from the subagents core ([2e81044](https://github.com/gotgenes/pi-packages/commit/2e81044221562c528d1fb296f356f60d81af0661))
|
|
20
|
+
|
|
21
|
+
## [11.6.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v11.5.0...pi-subagents-v11.6.0) (2026-05-29)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* **pi-subagents:** publish bundled type declarations and fix stale exports path ([8eda6f6](https://github.com/gotgenes/pi-packages/commit/8eda6f6611a12c60d99a5069f352abd634997e67))
|
|
27
|
+
|
|
8
28
|
## [11.5.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v11.4.0...pi-subagents-v11.5.0) (2026-05-29)
|
|
9
29
|
|
|
10
30
|
|
package/dist/public.d.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { ThinkingLevel } from '@earendil-works/pi-ai';
|
|
2
|
+
|
|
3
|
+
/** usage.ts — Token usage: shapes, accumulator operators, session-stats readers. */
|
|
4
|
+
/**
|
|
5
|
+
* Lifetime usage components, accumulated via `message_end` events. Survives
|
|
6
|
+
* compaction (which replaces session.state.messages and would reset any
|
|
7
|
+
* stats-derived sum). cacheRead is excluded because each turn's cacheRead is
|
|
8
|
+
* the cumulative cached prefix re-read on that one call — summing across
|
|
9
|
+
* turns counts the prefix N times. See issue #38.
|
|
10
|
+
*/
|
|
11
|
+
type LifetimeUsage = {
|
|
12
|
+
input: number;
|
|
13
|
+
output: number;
|
|
14
|
+
cacheWrite: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* types.ts — Type definitions for the subagent system.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** Agent type: any string name (built-in defaults or user-defined). */
|
|
22
|
+
type SubagentType = string;
|
|
23
|
+
interface AgentInvocation {
|
|
24
|
+
/** Short display name, e.g. "haiku" — only set when different from parent. */
|
|
25
|
+
modelName?: string;
|
|
26
|
+
thinking?: ThinkingLevel;
|
|
27
|
+
maxTurns?: number;
|
|
28
|
+
isolated?: boolean;
|
|
29
|
+
inheritContext?: boolean;
|
|
30
|
+
runInBackground?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* agent.ts — Agent class with encapsulated status-transition logic and per-agent behavior.
|
|
35
|
+
*
|
|
36
|
+
* Status transitions (status, result, error, startedAt, completedAt) are owned
|
|
37
|
+
* by the class and exposed via transition methods. External code reads these
|
|
38
|
+
* fields through public properties but cannot write them directly.
|
|
39
|
+
*
|
|
40
|
+
* Stats (toolUses, lifetimeUsage, compactionCount) are owned by the class and
|
|
41
|
+
* accumulated via mutation methods (incrementToolUses, addUsage, incrementCompactions).
|
|
42
|
+
*
|
|
43
|
+
* Behavior (abort, steer buffering) lives on the agent rather than on
|
|
44
|
+
* AgentManager — each agent manages its own lifecycle concerns.
|
|
45
|
+
*
|
|
46
|
+
* The child's working directory is supplied by a registered WorkspaceProvider
|
|
47
|
+
* (the workspace seam); with no provider the child runs in the parent cwd.
|
|
48
|
+
*
|
|
49
|
+
* Phase-specific collaborators (execution, notification) are attached
|
|
50
|
+
* after construction as lifecycle information becomes available.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
type AgentStatus = "queued" | "running" | "completed" | "steered" | "aborted" | "stopped" | "error";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* workspace.ts — The single generative extension seam (ADR 0002, Phase 16 Step 2).
|
|
57
|
+
*
|
|
58
|
+
* "Where does a child run, and what brackets the run?" is a strategy (git
|
|
59
|
+
* worktree, container, tmpdir, remote sandbox), not core behavior. The core
|
|
60
|
+
* needs only a working directory plus a disposal hook; the default — the
|
|
61
|
+
* parent's cwd, with no setup/teardown — is always correct.
|
|
62
|
+
*
|
|
63
|
+
* Unlike the observational lifecycle events in child-lifecycle.ts, this is a
|
|
64
|
+
* *generative* seam: a registered provider returns a value the core consumes
|
|
65
|
+
* synchronously at run-start. The core has no knowledge of git or worktrees.
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/** Context the core hands a provider when a child run starts. */
|
|
69
|
+
interface WorkspacePrepareContext {
|
|
70
|
+
agentId: string;
|
|
71
|
+
agentType: SubagentType;
|
|
72
|
+
baseCwd: string;
|
|
73
|
+
invocation?: AgentInvocation;
|
|
74
|
+
}
|
|
75
|
+
/** Outcome the core reports to a workspace when the run ends. */
|
|
76
|
+
interface WorkspaceDisposeOutcome {
|
|
77
|
+
status: AgentStatus;
|
|
78
|
+
description: string;
|
|
79
|
+
}
|
|
80
|
+
/** What dispose may hand back for the core to fold into the child result. */
|
|
81
|
+
interface WorkspaceDisposeResult {
|
|
82
|
+
/** Appended verbatim to the child's result text — the provider owns the wording. */
|
|
83
|
+
resultAddendum?: string;
|
|
84
|
+
}
|
|
85
|
+
/** A prepared working directory plus its bracketed teardown. Born complete. */
|
|
86
|
+
interface Workspace {
|
|
87
|
+
/** The working directory — already exists when the workspace is handed back. */
|
|
88
|
+
readonly cwd: string;
|
|
89
|
+
dispose(outcome: WorkspaceDisposeOutcome): WorkspaceDisposeResult | undefined;
|
|
90
|
+
}
|
|
91
|
+
/** The single generative seam: supplies a child's workspace. */
|
|
92
|
+
interface WorkspaceProvider {
|
|
93
|
+
prepare(ctx: WorkspacePrepareContext): Promise<Workspace | undefined>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* service.ts — Public API surface for cross-extension access to subagents.
|
|
98
|
+
*
|
|
99
|
+
* Consumers declare this package as an optional peer dependency and use
|
|
100
|
+
* dynamic import to access the accessor functions:
|
|
101
|
+
*
|
|
102
|
+
* const { getSubagentsService } = await import("@gotgenes/pi-subagents");
|
|
103
|
+
* const svc = getSubagentsService();
|
|
104
|
+
* svc?.spawn("Explore", "Check for stale TODOs");
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
type SubagentStatus = "queued" | "running" | "completed" | "steered" | "aborted" | "stopped" | "error";
|
|
108
|
+
/** Serializable snapshot of an agent's state — no live session objects. */
|
|
109
|
+
interface SubagentRecord {
|
|
110
|
+
id: string;
|
|
111
|
+
type: string;
|
|
112
|
+
description: string;
|
|
113
|
+
status: SubagentStatus;
|
|
114
|
+
result?: string;
|
|
115
|
+
error?: string;
|
|
116
|
+
toolUses: number;
|
|
117
|
+
startedAt: number;
|
|
118
|
+
completedAt?: number;
|
|
119
|
+
lifetimeUsage: LifetimeUsage;
|
|
120
|
+
compactionCount: number;
|
|
121
|
+
}
|
|
122
|
+
/** Options for spawning an agent via the service. */
|
|
123
|
+
interface SpawnOptions {
|
|
124
|
+
description?: string;
|
|
125
|
+
model?: string;
|
|
126
|
+
maxTurns?: number;
|
|
127
|
+
thinkingLevel?: string;
|
|
128
|
+
isolated?: boolean;
|
|
129
|
+
inheritContext?: boolean;
|
|
130
|
+
foreground?: boolean;
|
|
131
|
+
bypassQueue?: boolean;
|
|
132
|
+
}
|
|
133
|
+
/** The public service contract for cross-extension subagent access. */
|
|
134
|
+
interface SubagentsService {
|
|
135
|
+
/** Spawn an agent. Returns the agent ID immediately. */
|
|
136
|
+
spawn(type: string, prompt: string, options?: SpawnOptions): string;
|
|
137
|
+
/** Get a snapshot of an agent's current state. */
|
|
138
|
+
getRecord(id: string): SubagentRecord | undefined;
|
|
139
|
+
/** List all tracked agents, most recent first. */
|
|
140
|
+
listAgents(): SubagentRecord[];
|
|
141
|
+
/** Abort a running or queued agent. Returns false if not found. */
|
|
142
|
+
abort(id: string): boolean;
|
|
143
|
+
/** Send a steering message to a running agent. */
|
|
144
|
+
steer(id: string, message: string): Promise<boolean>;
|
|
145
|
+
/** Wait for all running and queued agents to complete. */
|
|
146
|
+
waitForAll(): Promise<void>;
|
|
147
|
+
/** Whether any agents are running or queued. */
|
|
148
|
+
hasRunning(): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Register the single workspace provider that supplies a child's working
|
|
151
|
+
* directory plus bracketed setup/teardown. Throws if one is already
|
|
152
|
+
* registered. Returns a disposer that unregisters the provider.
|
|
153
|
+
*/
|
|
154
|
+
registerWorkspaceProvider(provider: WorkspaceProvider): () => void;
|
|
155
|
+
}
|
|
156
|
+
/** Event channel constants for pi.events subscriptions. */
|
|
157
|
+
declare const SUBAGENT_EVENTS: {
|
|
158
|
+
readonly STARTED: "subagents:started";
|
|
159
|
+
readonly COMPLETED: "subagents:completed";
|
|
160
|
+
readonly ACTIVITY: "subagents:activity";
|
|
161
|
+
};
|
|
162
|
+
/** Publish the SubagentsService on globalThis for cross-extension access. */
|
|
163
|
+
declare function publishSubagentsService(service: SubagentsService): void;
|
|
164
|
+
/** Retrieve the published SubagentsService, or undefined if not yet published. */
|
|
165
|
+
declare function getSubagentsService(): SubagentsService | undefined;
|
|
166
|
+
/** Remove the SubagentsService from globalThis (call on shutdown/reload). */
|
|
167
|
+
declare function unpublishSubagentsService(): void;
|
|
168
|
+
|
|
169
|
+
export { SUBAGENT_EVENTS, getSubagentsService, publishSubagentsService, unpublishSubagentsService };
|
|
170
|
+
export type { LifetimeUsage, SpawnOptions, SubagentRecord, SubagentStatus, SubagentsService, WorkspaceProvider };
|
|
@@ -56,9 +56,9 @@ flowchart TB
|
|
|
56
56
|
AgentManager["AgentManager<br/>(spawn, abort, collection)"]
|
|
57
57
|
ConcurrencyQueue["ConcurrencyQueue<br/>(scheduling, drain)"]
|
|
58
58
|
AgentRunner["agent-runner<br/>(session, turns, results)"]
|
|
59
|
-
Agent["Agent<br/>(status, behavior: abort/steer/
|
|
59
|
+
Agent["Agent<br/>(status, behavior: abort/steer/run lifecycle)"]
|
|
60
60
|
ParentSnapshot["ParentSnapshot<br/>(frozen parent state)"]
|
|
61
|
-
|
|
61
|
+
Workspace["workspace<br/>(provider seam: child cwd + teardown)"]
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
subgraph observation["Observation domain"]
|
|
@@ -112,7 +112,6 @@ classDiagram
|
|
|
112
112
|
+toolUses: number
|
|
113
113
|
+lifetimeUsage: LifetimeUsage
|
|
114
114
|
+execution?: ExecutionState
|
|
115
|
-
+worktree?: WorktreeIsolation
|
|
116
115
|
+notification?: NotificationState
|
|
117
116
|
+markRunning()
|
|
118
117
|
+markCompleted()
|
|
@@ -270,14 +269,12 @@ src/
|
|
|
270
269
|
├── lifecycle/ agent execution and state tracking
|
|
271
270
|
│ ├── agent-manager.ts collection manager + observer wiring
|
|
272
271
|
│ ├── agent-runner.ts session creation, turn loop, tool filtering
|
|
273
|
-
│ ├── agent.ts owns full execution lifecycle (run, abort, steer,
|
|
272
|
+
│ ├── agent.ts owns full execution lifecycle (run, abort, steer, workspace)
|
|
274
273
|
│ ├── concurrency-queue.ts background agent scheduling with configurable concurrency limit
|
|
275
274
|
│ ├── parent-snapshot.ts immutable spawn-time parent state
|
|
276
275
|
│ ├── execution-state.ts session/output phase state
|
|
277
276
|
│ ├── child-lifecycle.ts child-execution lifecycle event publisher
|
|
278
277
|
│ ├── workspace.ts workspace provider seam (generative extension surface)
|
|
279
|
-
│ ├── worktree.ts git worktree isolation
|
|
280
|
-
│ ├── worktree-isolation.ts worktree lifecycle collaborator
|
|
281
278
|
│ └── usage.ts token usage tracking
|
|
282
279
|
│
|
|
283
280
|
├── observation/ progress tracking and notification
|
|
@@ -357,14 +354,14 @@ They declare this package as an optional peer dependency and use dynamic import
|
|
|
357
354
|
Reactive consumers subscribe: `@gotgenes/pi-permission-system` registers each child session on `session-created` and unregisters it on `disposed`.
|
|
358
355
|
This replaced the former outbound `permission-bridge` (#261, ADR 0002) — the core no longer looks up a named consumer.
|
|
359
356
|
- `workspace` — the single generative seam (#262, ADR 0002): a registered `WorkspaceProvider` supplies a child's cwd plus bracketed `dispose()` at run-start.
|
|
360
|
-
With no provider, children run in the parent cwd (default unchanged); the git worktree strategy
|
|
357
|
+
With no provider, children run in the parent cwd (default unchanged); the git worktree strategy lives behind this seam in `@gotgenes/pi-subagents-worktrees` (#263, the seam's first consumer).
|
|
361
358
|
- `session-config` — pure configuration assembler (extracted from `agent-runner`).
|
|
362
359
|
- `SubagentRuntime` — session-scoped state bag with methods.
|
|
363
360
|
- `ParentSnapshot` — immutable snapshot of parent session state, captured once at spawn time.
|
|
364
361
|
- `record-observer` — session-event observer that updates record statistics without callback threading.
|
|
365
362
|
- Agent type registry — default agents, custom `.md` file loading.
|
|
366
363
|
- Prompt assembly, context extraction, skills, environment.
|
|
367
|
-
- Worktree isolation —
|
|
364
|
+
- Worktree isolation — evicted to `@gotgenes/pi-subagents-worktrees` via the workspace provider seam in Phase 16 (#263, ADR 0002); `git` no longer appears in the core.
|
|
368
365
|
- Token usage tracking.
|
|
369
366
|
- Session directory derivation and persisted `SessionManager` for subagent transcripts.
|
|
370
367
|
- Settings persistence.
|
|
@@ -418,7 +415,7 @@ Key types:
|
|
|
418
415
|
|
|
419
416
|
- `SubagentsService` — `spawn`, `getRecord`, `listAgents`, `abort`, `steer`, `waitForAll`, `hasRunning`.
|
|
420
417
|
- `SubagentRecord` — serializable agent snapshot (no live session objects).
|
|
421
|
-
- `SpawnOptions` — `description`, `model`, `maxTurns`, `thinkingLevel`, `isolated`, `inheritContext`, `foreground`, `bypassQueue
|
|
418
|
+
- `SpawnOptions` — `description`, `model`, `maxTurns`, `thinkingLevel`, `isolated`, `inheritContext`, `foreground`, `bypassQueue`.
|
|
422
419
|
- `SUBAGENT_EVENTS` — channel constants for `pi.events` subscriptions.
|
|
423
420
|
|
|
424
421
|
### Accessor pattern
|
|
@@ -501,7 +498,7 @@ Latent extensibility is the deliverable; a vacant hook is not.
|
|
|
501
498
|
- **Extension filtering** (`extensions: string[]` allowlist) — tool visibility is pi-permission-system's job.
|
|
502
499
|
- **Worktree isolation** (`worktree.ts`, `worktree-isolation.ts`, `GitWorktreeManager`, the `isolation: "worktree"` spawn mode) — environment policy, not core.
|
|
503
500
|
Git worktrees are one *strategy* for choosing the child's working directory; containers, throwaway tmpdirs, and remote sandboxes are others.
|
|
504
|
-
|
|
501
|
+
Evicted to `@gotgenes/pi-subagents-worktrees` (#263), the first consumer of the workspace provider seam.
|
|
505
502
|
- **Extension lifecycle control** (`extensions: false`, `isolated`, `noSkills`) — deny-at-use (the in-child permission layer blocking disallowed tool calls) covers what `isolated` pretended to do for tools.
|
|
506
503
|
Prevent-load (refusing to bind an extension because of load-time side effects, cost, or true sandboxing) is genuinely generative and is left as a *latent* (un-built) provider seam, added only if a real consumer needs it.
|
|
507
504
|
|
|
@@ -608,7 +605,6 @@ interface SpawnExecution {
|
|
|
608
605
|
inheritContext: boolean;
|
|
609
606
|
runInBackground: boolean;
|
|
610
607
|
isolated: boolean;
|
|
611
|
-
isolation: IsolationMode | undefined;
|
|
612
608
|
agentInvocation: AgentInvocation;
|
|
613
609
|
}
|
|
614
610
|
|
|
@@ -765,8 +761,8 @@ Migrate `@gotgenes/pi-permission-system` to subscribe to `session-created`/`disp
|
|
|
765
761
|
#### Step 2: Define the `WorkspaceProvider` seam — [#262] ✅ Delivered
|
|
766
762
|
|
|
767
763
|
Added the `WorkspaceProvider` / `Workspace` interfaces (`src/lifecycle/workspace.ts`) and `SubagentsService.registerWorkspaceProvider` (single provider, throws on duplicate, returns an unregister disposer).
|
|
768
|
-
Only `WorkspaceProvider` is named-re-exported from `service.ts`; `Workspace` and the context types resolve via inference when a consumer assigns to `WorkspaceProvider` (
|
|
769
|
-
At run-start `Agent.run()` consults the registered provider
|
|
764
|
+
Only `WorkspaceProvider` is named-re-exported from `service.ts`; `Workspace` and the context types resolve via inference when a consumer assigns to `WorkspaceProvider` (named re-exports of the collaborator types are tracked in #272).
|
|
765
|
+
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 (the legacy worktree-collaborator fallback was removed when worktrees left the core in #263).
|
|
770
766
|
On completion the core calls `Workspace.dispose({ status, description })` and appends the returned `resultAddendum` verbatim — the provider owns the wording.
|
|
771
767
|
|
|
772
768
|
- The seam is additive and non-breaking: the existing `isolation: "worktree"` path is untouched (its eviction is Step 3).
|
|
@@ -774,13 +770,15 @@ On completion the core calls `Workspace.dispose({ status, description })` and ap
|
|
|
774
770
|
Within #262 the seam is exercised only by test fakes; do not cut a release containing the seam without `@gotgenes/pi-subagents-worktrees`.
|
|
775
771
|
- Outcome: a single generative seam; the core no longer knows what an "isolation strategy" is.
|
|
776
772
|
|
|
777
|
-
#### Step 3: Extract worktrees to `@gotgenes/pi-subagents-worktrees` — [#263]
|
|
773
|
+
#### Step 3: Extract worktrees to `@gotgenes/pi-subagents-worktrees` — [#263] ✅ Delivered
|
|
778
774
|
|
|
779
775
|
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.
|
|
780
|
-
|
|
776
|
+
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).
|
|
777
|
+
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`.
|
|
781
778
|
|
|
782
779
|
- Supersedes #256.
|
|
783
|
-
New package registered in `release-please-config.json
|
|
780
|
+
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`), since `exports.types` resolves to the shipped declaration bundle.
|
|
781
|
+
Only `WorkspaceProvider` is importable by name from 11.6.0; the collaborator types are recovered via inference until #272 adds named re-exports.
|
|
784
782
|
- Outcome: git leaves the core; worktree users install one package, everyone else pays nothing.
|
|
785
783
|
|
|
786
784
|
#### Step 4: Remove `isolated` / `extensions: false` / `noSkills` — [#264]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: accepted
|
|
3
|
+
date: 2026-05-29
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0003 — Publish a bundled `.d.ts` for the public surface
|
|
7
|
+
|
|
8
|
+
## Status
|
|
9
|
+
|
|
10
|
+
Accepted.
|
|
11
|
+
Introduces the repository's first build step, scoped to type declarations only.
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
`@gotgenes/pi-subagents` could not be imported by another TypeScript package in this workspace.
|
|
16
|
+
Issue #263 (extract worktree isolation to `@gotgenes/pi-subagents-worktrees`) is the first intra-repo consumer: it must `implements WorkspaceProvider` and call `getSubagentsService().registerWorkspaceProvider(...)`, both of which require importing the package by name.
|
|
17
|
+
|
|
18
|
+
A `tsc --traceResolution` of a sibling consuming the package surfaced two compounding failures.
|
|
19
|
+
|
|
20
|
+
1. `package.json` `exports["."]` pointed at `./src/service.ts`, which does not exist — the real module is `./src/service/service.ts`.
|
|
21
|
+
A latent bug, unnoticed because nothing in-repo imported the package by name.
|
|
22
|
+
2. Once corrected, the public entry's internal alias imports cascade.
|
|
23
|
+
`service/service.ts` imports `type LifetimeUsage` and `type WorkspaceProvider` via the `#src/*` alias.
|
|
24
|
+
When a sibling's `tsc` follows the symlink, the consumer's own `paths` (`#src/*` → `./src/*`) intercept first and resolve into the *consumer's* `src/` — a global-`paths` collision, since both packages define `#src/*`.
|
|
25
|
+
The fallback to the publisher's `package.json` `imports` field also fails: `tsc` cannot resolve the extensionless `.ts` target under Node `imports` semantics ("Import specifier '#src/lifecycle/usage' does not exist in package.json scope").
|
|
26
|
+
|
|
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
|
+
A shallow alias-free entry is therefore not achievable without a substantial source restructure.
|
|
29
|
+
|
|
30
|
+
This collides with the ship-source model (ADR 0002): every package ships raw `.ts` executed directly by Pi, with no build step.
|
|
31
|
+
|
|
32
|
+
## Decision
|
|
33
|
+
|
|
34
|
+
Emit a single, self-contained `dist/public.d.ts` for the public surface and advertise it through a `types` export condition, while the runtime entry continues to serve `.ts` source.
|
|
35
|
+
|
|
36
|
+
```jsonc
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/public.d.ts",
|
|
40
|
+
"default": "./src/service/service.ts"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- `rollup-plugin-dts` rolls the declaration graph rooted at `src/service/service.ts` into one file, inlining the internal `#src/*` types and keeping peer-dependency types (`@earendil-works/*`, `@sinclair/typebox`) external.
|
|
46
|
+
We ship `.ts` source, so only the declaration bundle is emitted — no JS.
|
|
47
|
+
- The bundle is generated at `prepack` time and shipped via a `files` allowlist; it is gitignored and never committed.
|
|
48
|
+
- `default` → `./src/service/service.ts` fixes the stale path and serves runtime consumers; its `import type` lines erase, so no runtime `#src/*` resolution is needed.
|
|
49
|
+
- A `pnpm pack` → throwaway-consumer → `tsc` harness proves external consumability with no publish round-trip and no workspace privileges.
|
|
50
|
+
|
|
51
|
+
This is the repository's first build step.
|
|
52
|
+
It is deliberately narrow: it produces type declarations only and changes nothing about how Pi loads the extension from source (`pi.extensions: ["./src/index.ts"]` is untouched).
|
|
53
|
+
|
|
54
|
+
## Alternatives considered
|
|
55
|
+
|
|
56
|
+
- Alias-free public entry (restructure the source so the entry's full type closure resolves via same-directory `./` imports).
|
|
57
|
+
Mechanically possible, but it requires moving the `AgentStatus`/`SubagentType`/`AgentInvocation`/`WorkspaceProvider` definitions and untangling the `agent.ts`/`types.ts` graph, with care that inner layers do not import the outer service layer.
|
|
58
|
+
`eslint`'s `no-parent-relative-imports` rule (which forbids `../`) narrows the options further.
|
|
59
|
+
Larger blast radius than emitting a `.d.ts`, and it churns the domain model to serve a packaging concern.
|
|
60
|
+
- A self-contained entry that re-declares the public types inline, guarded by a conformance test.
|
|
61
|
+
Avoids a build step but duplicates the seam/usage/status type definitions, which drift over time.
|
|
62
|
+
|
|
63
|
+
## Consequences
|
|
64
|
+
|
|
65
|
+
- The repository now has a build step, but it is type-only and isolated to this package; the ship-source model is otherwise intact.
|
|
66
|
+
- Consumers (including `@gotgenes/pi-subagents-worktrees` in #263) consume the packaged public interface like any external developer — no `workspace:*` privileges.
|
|
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
|
+
This is acceptable because no in-repo package imports the surface yet; #263 consumes the built artifact from the published tarball.
|
|
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.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 270
|
|
3
|
+
issue_title: "Make @gotgenes/pi-subagents type-consumable by sibling workspace packages"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Publish a bundled `.d.ts` for the pi-subagents public surface
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`@gotgenes/pi-subagents` cannot be imported by another TypeScript package in this workspace.
|
|
11
|
+
A sibling that writes `import { getSubagentsService, type WorkspaceProvider } from "@gotgenes/pi-subagents"` fails its `tsc` run.
|
|
12
|
+
This blocks Issue #263 (extract worktree isolation to `@gotgenes/pi-subagents-worktrees`), the first package that needs to import the subagents service and the `WorkspaceProvider` seam by name.
|
|
13
|
+
|
|
14
|
+
Two compounding causes were confirmed empirically with `tsc --traceResolution`:
|
|
15
|
+
|
|
16
|
+
1. `package.json` `exports["."]` points at `./src/service.ts`, which does not exist — the real module is `./src/service/service.ts`.
|
|
17
|
+
This is a latent bug; nothing in-repo imports the package by name today.
|
|
18
|
+
2. Once the path is corrected, the public entry's internal alias imports cascade.
|
|
19
|
+
`service/service.ts` imports `type LifetimeUsage` and `type WorkspaceProvider` via `#src/*`.
|
|
20
|
+
When a sibling's `tsc` follows the symlink and resolves those specifiers, the consumer's own `paths` (`#src/*` → `./src/*`) intercept first and point into the *consumer's* `src/` (a global-`paths` collision — both packages even define `#src/*`).
|
|
21
|
+
The fallback to the publisher's `package.json` `imports` field then fails too: tsc cannot resolve the extensionless `.ts` target under Node `imports` semantics ("Import specifier '#src/lifecycle/usage' does not exist in package.json scope").
|
|
22
|
+
|
|
23
|
+
The public entry's type closure is also deeply entangled: `WorkspaceProvider` (in `lifecycle/workspace.ts`) pulls in `AgentStatus` from the 510-line `lifecycle/agent.ts`, plus `SubagentType`/`AgentInvocation` from `types.ts` (which itself re-exports the `Agent` class).
|
|
24
|
+
A shallow alias-free entry is therefore not achievable without a substantial source restructure.
|
|
25
|
+
|
|
26
|
+
## Goals
|
|
27
|
+
|
|
28
|
+
- A consumer using the **published/packaged** `@gotgenes/pi-subagents` can `import { getSubagentsService, type WorkspaceProvider } from "@gotgenes/pi-subagents"` and have `tsc` pass.
|
|
29
|
+
- Fix the stale `exports["."]` path so it resolves to a real file.
|
|
30
|
+
- Emit a self-contained (alias-free) `.d.ts` for the public surface via `rollup-plugin-dts`, generated at pack/publish time and shipped in the npm tarball.
|
|
31
|
+
- Add a verification harness that proves external consumability via `pnpm pack` → throwaway consumer → `tsc`, with no publish round-trip.
|
|
32
|
+
- No regression to how Pi loads the extension from source (`pi.extensions: ["./src/index.ts"]` is untouched).
|
|
33
|
+
- `feat:` — adds a publishable capability (a typed public API surface) to `pi-subagents`.
|
|
34
|
+
|
|
35
|
+
## Non-Goals
|
|
36
|
+
|
|
37
|
+
- No source restructuring to make the entry alias-free (the rejected alternative — see Background).
|
|
38
|
+
`src/` modules and `#src/*` internal imports are left exactly as they are.
|
|
39
|
+
- No removal of worktree code from the core (`worktree.ts`, `worktree-isolation.ts`, `IsolationMode`, `isolation` spawn mode) — that is Issue #263.
|
|
40
|
+
- No change to `pi-subagents-worktrees`' dependency wiring: it stays on `workspace:*` and does not yet import `@gotgenes/pi-subagents`.
|
|
41
|
+
Flipping it to registry consumption (drop `workspace:*`, set `link-workspace-packages: false`, point at the fixed version, wire the real import) is deferred to Issue #263, because the registry version carrying this fix does not exist until #270 is published.
|
|
42
|
+
- No registration of `pi-subagents-worktrees` into `release-please-config.json` — that belongs to Issue #263 when it is ready to publish.
|
|
43
|
+
|
|
44
|
+
## Background
|
|
45
|
+
|
|
46
|
+
Relevant modules and facts:
|
|
47
|
+
|
|
48
|
+
- `packages/pi-subagents/src/service/service.ts` — the real public entry.
|
|
49
|
+
Locally declares the public service contract (`SubagentsService`, `SubagentRecord`, `SpawnOptions`, `SubagentStatus`, `SUBAGENT_EVENTS`) and the `Symbol.for()` accessor functions (`publishSubagentsService`, `getSubagentsService`, `unpublishSubagentsService`).
|
|
50
|
+
Its only internal imports are `import type { LifetimeUsage }` and `import type { WorkspaceProvider }` — both type-only, so they erase at runtime.
|
|
51
|
+
- `src/lifecycle/workspace.ts` — defines `WorkspaceProvider`, `Workspace`, and the prepare/dispose context types.
|
|
52
|
+
Imports `AgentStatus` from `#src/lifecycle/agent` and `AgentInvocation`/`SubagentType` from `#src/types`.
|
|
53
|
+
- `src/lifecycle/usage.ts` — defines `LifetimeUsage` plus internal runtime helpers.
|
|
54
|
+
- `src/lifecycle/agent.ts` (510 LOC) — defines `AgentStatus` near the top, alongside the `Agent` class and a wide `#src/*` import graph.
|
|
55
|
+
- `src/index.ts` — the Pi extension entry; imports `publishSubagentsService`/`unpublishSubagentsService` from `#src/service/service` (internal use, unaffected).
|
|
56
|
+
|
|
57
|
+
Constraints from `AGENTS.md` and the package skill:
|
|
58
|
+
|
|
59
|
+
- Ship-source model: every package ships raw `.ts` executed directly by Pi; there is no build step today.
|
|
60
|
+
ADR 0002 frames pi-subagents as a minimal core.
|
|
61
|
+
Introducing the repo's **first build step** is a deliberate decision and warrants an ADR.
|
|
62
|
+
- `eslint`'s `no-parent-relative-imports` rule forbids `../` imports inside `packages/*/src`; same-directory `./` is allowed.
|
|
63
|
+
This (plus the deep type entanglement above) is why the alias-free-entry alternative was rejected.
|
|
64
|
+
- New packages and internal docs subdirectories must be added to `release-please-config.json` `exclude-paths` where appropriate.
|
|
65
|
+
|
|
66
|
+
Rejected alternative (recorded for the ADR): restructure the source so the entry's full type closure is alias-free (leaf types module imported via `./`).
|
|
67
|
+
It is mechanically possible but requires moving `AgentStatus`/`SubagentType`/`AgentInvocation`/`WorkspaceProvider` definitions and reworking the `agent.ts`/`types.ts` entanglement, with care around dependency direction (inner layers must not import the outer service layer).
|
|
68
|
+
Larger blast radius than emitting a `.d.ts`.
|
|
69
|
+
|
|
70
|
+
Release/CI mechanics relevant to sequencing:
|
|
71
|
+
|
|
72
|
+
- CI publishes only when the `release-please` PR is merged (`publish` job gated on `releases_created == true`), not on every push to `main`.
|
|
73
|
+
- `release-please` batches all releasable commits per component.
|
|
74
|
+
The `#263` scaffold commits on `main` touch only the `pi-subagents-worktrees` component (which is **not** registered in `release-please-config.json`), so they neither trigger a release nor batch into `pi-subagents`.
|
|
75
|
+
|
|
76
|
+
## Design Overview
|
|
77
|
+
|
|
78
|
+
### Conditional exports
|
|
79
|
+
|
|
80
|
+
Point the `types` condition at the bundled declaration and the runtime condition at the real source module:
|
|
81
|
+
|
|
82
|
+
```jsonc
|
|
83
|
+
"exports": {
|
|
84
|
+
".": {
|
|
85
|
+
"types": "./dist/public.d.ts",
|
|
86
|
+
"default": "./src/service/service.ts"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- `default` → `./src/service/service.ts` fixes the stale path and serves runtime `await import("@gotgenes/pi-subagents")` (the accessor functions; its `import type` lines erase, so no runtime `#src/*` resolution is needed).
|
|
92
|
+
- `types` → `./dist/public.d.ts` gives a consumer's `tsc` a self-contained declaration that never references `#src/*`, sidestepping both the `paths` collision and the `imports`-field resolution failure.
|
|
93
|
+
|
|
94
|
+
### Bundled declaration emit
|
|
95
|
+
|
|
96
|
+
`rollup-plugin-dts` rolls the declaration graph rooted at `src/service/service.ts` into a single `dist/public.d.ts`.
|
|
97
|
+
It tree-shakes to only the types reachable from the entry's exports — `SubagentsService`, `SubagentRecord`, `SpawnOptions`, `SubagentStatus`, `SUBAGENT_EVENTS`, the accessor signatures, plus the seam closure (`WorkspaceProvider`, `Workspace`, the context types, `AgentStatus`, `AgentInvocation`, `SubagentType`) and `LifetimeUsage`, all inlined.
|
|
98
|
+
Imports from `@earendil-works/*` and `@sinclair/typebox` remain **external** in the output (the consumer has them as peers), so they are not inlined.
|
|
99
|
+
|
|
100
|
+
We ship `.ts` source, so we want **only** the `.d.ts` — no JS bundle.
|
|
101
|
+
`rollup-plugin-dts` does exactly that.
|
|
102
|
+
|
|
103
|
+
Resolution note (primary feasibility risk): the roll-up must resolve the publisher's `#src/*` specifiers while parsing the type graph.
|
|
104
|
+
`rollup-plugin-dts` drives the TypeScript compiler, which reads `compilerOptions.paths`; the build config references a tsconfig carrying the existing `#src/*` → `./src/*` paths.
|
|
105
|
+
If `#src/*` does not resolve out of the box, add a tsconfig-paths/alias resolver to the rollup config.
|
|
106
|
+
The first build step (below) is the checkpoint that proves this.
|
|
107
|
+
|
|
108
|
+
### Generation timing and packaging
|
|
109
|
+
|
|
110
|
+
- `prepack` runs `build:types` before both `pnpm pack` and `pnpm publish` (publish packs internally), so the tarball always contains a freshly generated `dist/public.d.ts`.
|
|
111
|
+
- `dist/` is gitignored, so the artifact is **never committed**.
|
|
112
|
+
- A `files` allowlist is added so the gitignored `dist/` is included in the published tarball.
|
|
113
|
+
The allowlist must preserve everything currently published (the whole `src/` tree, `docs/`, `README.md`, `LICENSE`, `CHANGELOG.md`, `AGENTS.md`, `.prettierignore`) plus `dist/`.
|
|
114
|
+
Validate parity with `pnpm pack --dry-run` before/after.
|
|
115
|
+
|
|
116
|
+
### Verification harness (the proof)
|
|
117
|
+
|
|
118
|
+
A script (run locally and in CI) proves external consumability without a publish:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# pseudocode — scripts/verify-public-types.sh
|
|
122
|
+
pnpm --filter @gotgenes/pi-subagents pack --pack-destination "$TMP" # triggers prepack → build:types
|
|
123
|
+
cd "$TMP/consumer" # minimal package.json + tsconfig
|
|
124
|
+
pnpm add "$TMP/gotgenes-pi-subagents-*.tgz" \
|
|
125
|
+
@earendil-works/pi-ai @earendil-works/pi-coding-agent @earendil-works/pi-tui typescript
|
|
126
|
+
# probe.ts: import { getSubagentsService, type WorkspaceProvider } from "@gotgenes/pi-subagents"
|
|
127
|
+
pnpm exec tsc --noEmit # must pass
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Plus a cheap self-containment guard: assert `dist/public.d.ts` exists, exports the expected symbols, and contains no `#src/` substring (proving it is alias-free).
|
|
131
|
+
|
|
132
|
+
## Module-Level Changes
|
|
133
|
+
|
|
134
|
+
- `packages/pi-subagents/package.json`
|
|
135
|
+
- `exports["."]` → `{ "types": "./dist/public.d.ts", "default": "./src/service/service.ts" }`.
|
|
136
|
+
- Add `devDependencies`: `rollup`, `rollup-plugin-dts` (package-specific, not catalog — only this package builds types).
|
|
137
|
+
- Add scripts: `"build:types"` (runs rollup with the dts config) and `"prepack": "pnpm run build:types"`; add `"verify:public-types"` invoking the harness script.
|
|
138
|
+
- Add a `files` allowlist including `src`, `dist`, `docs`, `README.md`, `LICENSE`, `CHANGELOG.md`, `AGENTS.md`, `.prettierignore` (validated against `pnpm pack --dry-run`).
|
|
139
|
+
- `packages/pi-subagents/rollup.dts.config.mjs` — **new**.
|
|
140
|
+
Entry `src/service/service.ts` → output `dist/public.d.ts`; `rollup-plugin-dts` with the package tsconfig (for `#src/*` paths); externals = peer deps + `@sinclair/typebox`.
|
|
141
|
+
- `packages/pi-subagents/scripts/verify-public-types.sh` (or repo-level `scripts/`) — **new**.
|
|
142
|
+
Pack → throwaway consumer → `tsc`, plus the self-containment grep guard.
|
|
143
|
+
- `.github/workflows/ci.yml`
|
|
144
|
+
- Add a "Verify public types" step in the `check` job (runs on PR and `main`) invoking `pnpm --filter @gotgenes/pi-subagents run verify:public-types`.
|
|
145
|
+
- `packages/pi-subagents/docs/decisions/0003-publish-bundled-type-declarations.md` — **new** ADR.
|
|
146
|
+
Records the first-build-step decision, the rejected alias-free alternative, and the ship-source tradeoff (docs path; `exclude-paths` already covers `docs/decisions`, so it does not trigger a release).
|
|
147
|
+
|
|
148
|
+
No `src/` module is added, renamed, or removed; no exported symbol is removed.
|
|
149
|
+
No `docs/architecture/` layout/complexity tables reference `dist/` or the build config, so no architecture-doc edits are required beyond the ADR (optionally cross-link a one-line "build step" note — defer to build stage).
|
|
150
|
+
|
|
151
|
+
## Test Impact Analysis
|
|
152
|
+
|
|
153
|
+
This is a build/packaging change; `src/` is untouched, so the existing vitest suite (362 tests) is unaffected and stays as-is.
|
|
154
|
+
|
|
155
|
+
1. New verification this enables: a packaged-artifact consumability check (`pnpm pack` → consumer `tsc`) that did not exist and was previously impossible (the package was not consumable at all).
|
|
156
|
+
2. No existing tests become redundant — none currently exercise packaging or the public `exports`.
|
|
157
|
+
3. No existing tests must change; the public service contract types in `service.ts` are unchanged.
|
|
158
|
+
|
|
159
|
+
## Build Order
|
|
160
|
+
|
|
161
|
+
This is a tooling/config change with a verification harness (not red→green→refactor), so it proceeds as build steps that each leave the repo valid.
|
|
162
|
+
|
|
163
|
+
1. **Emit checkpoint.**
|
|
164
|
+
Add `rollup` + `rollup-plugin-dts` devDeps, write `rollup.dts.config.mjs`, add the `build:types` script.
|
|
165
|
+
Run `build:types`; confirm `dist/public.d.ts` is generated, exports the expected symbols, and contains no `#src/` substring (resolve the `#src/*` resolution risk here — add a paths/alias resolver if needed).
|
|
166
|
+
Commit: `build(pi-subagents): bundle public .d.ts with rollup-plugin-dts`.
|
|
167
|
+
2. **Wire exports + packaging.**
|
|
168
|
+
Set the conditional `exports` (`types` + `default`, fixing the stale path), add `prepack`, add the `files` allowlist; validate `pnpm pack --dry-run` parity (no currently-shipped file dropped; `dist/public.d.ts` present).
|
|
169
|
+
Commit: `feat(pi-subagents): publish bundled type declarations and fix stale exports path`.
|
|
170
|
+
3. **Verification harness + CI.**
|
|
171
|
+
Add `scripts/verify-public-types.sh` (pack → throwaway consumer → `tsc`, plus self-containment guard), the `verify:public-types` script, and the CI step.
|
|
172
|
+
Commit: `test(pi-subagents): verify the public surface is type-consumable from the packaged tarball`.
|
|
173
|
+
4. **ADR.**
|
|
174
|
+
Add `docs/decisions/0003-publish-bundled-type-declarations.md`.
|
|
175
|
+
Commit: `docs(pi-subagents): record decision to publish bundled type declarations (ADR 0003)`.
|
|
176
|
+
|
|
177
|
+
## Risks and Mitigations
|
|
178
|
+
|
|
179
|
+
- **`rollup-plugin-dts` cannot resolve `#src/*`** (primary risk).
|
|
180
|
+
Mitigation: drive it with the package tsconfig (which carries `#src/*` paths); if needed, add a tsconfig-paths/alias resolver.
|
|
181
|
+
Step 1 is the explicit checkpoint — if it cannot produce an alias-free `dist/public.d.ts`, stop and reassess before wiring exports.
|
|
182
|
+
- **Deep type graph via `agent.ts`.**
|
|
183
|
+
The seam closure reaches the 510-LOC `agent.ts`.
|
|
184
|
+
Mitigation: `rollup-plugin-dts` tree-shakes to only reachable types; the self-containment guard asserts the output is minimal and alias-free.
|
|
185
|
+
- **`files` allowlist drops currently-published files.**
|
|
186
|
+
Mitigation: diff `pnpm pack --dry-run` before/after; the allowlist must reproduce the existing tarball plus `dist/`.
|
|
187
|
+
- **`types` condition points at a gitignored, build-time artifact.**
|
|
188
|
+
An in-repo workspace-linked consumer that imported the package would need `dist/public.d.ts` present.
|
|
189
|
+
Mitigation: tight scope — `pi-subagents-worktrees` does not import the package yet; #263 consumes the built artifact from the published tarball.
|
|
190
|
+
- **Release batching / ordering with #263.**
|
|
191
|
+
Once #263 resumes and edits `pi-subagents` core, its commits batch into the same `pi-subagents` release.
|
|
192
|
+
Mitigation: publish #270 first (merge its release-please PR → `pi-subagents` publishes), then resume #263 against the published version.
|
|
193
|
+
The current `#263` scaffold commits touch only the unregistered `pi-subagents-worktrees` component, so they do not batch into `pi-subagents` today.
|
|
194
|
+
- **`prepack` must fire under the publish path.**
|
|
195
|
+
`scripts/publish-released.sh` runs `pnpm --filter @gotgenes/pi-subagents publish`, which packs and therefore runs `prepack`.
|
|
196
|
+
Mitigation: the verification harness exercises the same `pnpm pack` path that publish uses.
|
|
197
|
+
|
|
198
|
+
## Open Questions
|
|
199
|
+
|
|
200
|
+
- Whether to add a fast vitest self-containment assertion in addition to the shell harness, or keep the guard inside the script only — defer to the build stage.
|
|
201
|
+
- Whether the ADR should add a short "build process" subsection to `docs/architecture/architecture.md` — defer; the ADR is sufficient.
|
|
202
|
+
- Exact `files` allowlist entries — finalize against `pnpm pack --dry-run` in Step 2.
|
|
@@ -42,3 +42,46 @@ All deterministic gates green: `check`, `lint`, `test`, and `fallow dead-code` (
|
|
|
42
42
|
- Used `git commit --fixup` + `--autosquash` rebase twice (unpushed history) to fold the fallow trim into the Step 1 `feat` commit and the reviewer's doc-wording fix into the Step 3 `docs` commit, keeping each commit self-consistent.
|
|
43
43
|
- Pre-completion reviewer: WARN — all blocking checks pass; one non-blocking doc finding (architecture.md overstated that `Workspace` is re-exported).
|
|
44
44
|
Addressed before finishing.
|
|
45
|
+
|
|
46
|
+
## Stage: Final Retrospective (2026-05-29T15:40:54Z)
|
|
47
|
+
|
|
48
|
+
### Session summary
|
|
49
|
+
|
|
50
|
+
Shipped #262: pushed, CI green, closed the issue, and merged release-please PR #269 → `pi-subagents-v11.5.0`.
|
|
51
|
+
Mid-session the user asked what `model: claude-sonnet-4-6-20260526` in `.pi/agents/pre-completion-reviewer.md` resolved to; investigation found it had no entry in Pi's model registry and was silently falling back to the parent session model, and the fix (`anthropic/claude-sonnet-4-6`) landed in `1e46c5f4`.
|
|
52
|
+
Across all stages the plan's risk predictions held and TDD verification was incremental.
|
|
53
|
+
|
|
54
|
+
### Observations
|
|
55
|
+
|
|
56
|
+
#### What went well
|
|
57
|
+
|
|
58
|
+
- The planning-stage "vacant hook" risk prediction manifested exactly as predicted at the `fallow dead-code` gate: the plan named the failure (speculative re-exports flagged dead) before it happened, and the fix (re-export only `WorkspaceProvider`) was already reasoned out — a clean closed loop from planning risk to implementation gate.
|
|
59
|
+
- Incremental verification during TDD: `check` / `test` / `lint` ran after each step plus per-file vitest red→green, not just at the end.
|
|
60
|
+
- The model-spec question surfaced and fixed a latent silent bug — the reviewer's configured `model:` had no registry entry and was inheriting the parent model, so the misconfiguration produced no error.
|
|
61
|
+
- Cost discipline: the session model was switched to `opencode-go/deepseek-v4-flash` for the mechanical shipping stage — appropriate model-to-task matching.
|
|
62
|
+
|
|
63
|
+
#### What caused friction (agent side)
|
|
64
|
+
|
|
65
|
+
- `rabbit-hole` — the model-resolution investigation (≈30 tool calls, turns 119–160) grepped the Pi core monorepo (`~/development/pi/pi/packages/coding-agent`) for `.pi/agents/` frontmatter handling that does not live there, before landing on `pi-prompt-template-model/model-selection.ts` (a `pi-packages` dependency) plus the `models.generated.ts` registry.
|
|
66
|
+
Impact: long detour on a user tangent; no rework, but the answer was reachable far sooner.
|
|
67
|
+
- `other` (minor, recurring) — the pre-commit `trim trailing whitespace` hook reformatted files on the first `git commit`, failing it; the immediate retry succeeded (turns 55→56 and 95→96).
|
|
68
|
+
Impact: one wasted commit attempt per occurrence, no rework.
|
|
69
|
+
|
|
70
|
+
#### What caused friction (user side)
|
|
71
|
+
|
|
72
|
+
- The broken model spec was pre-existing config; because the failure mode is a silent fallback, such typos never surface on their own.
|
|
73
|
+
Opportunity (not criticism): a short convention note so future `.pi/agents/*.md` authoring uses the registry-resolvable `provider/id` form.
|
|
74
|
+
|
|
75
|
+
### Diagnostic details
|
|
76
|
+
|
|
77
|
+
- **Model-performance correlation** — the `pre-completion-reviewer` subagent was dispatched (turn 90) while the parent model was `anthropic/claude-opus-4-8`; because its `model:` frontmatter did not resolve, it ran on opus-4-8 (strong reasoning, appropriate for judgment-heavy review) rather than the configured Sonnet.
|
|
78
|
+
No quality mismatch in outcome, but the risk was latent: a weaker parent model would have silently degraded the reviewer.
|
|
79
|
+
The shipping stage ran on `deepseek-v4-flash` (mechanical git/CI/release ops) — appropriate.
|
|
80
|
+
- **Escalation-delay tracking** — the model-resolution `rabbit-hole` ran ≈30 consecutive search calls on the same goal, far past the 5-call threshold; consulting the `prompt-template-authoring` skill or reading `model-selection.ts` first would have shortcut it.
|
|
81
|
+
- **Unused-tool detection** — for that investigation the `prompt-template-authoring` skill (documents template/agent `model:` format) and `code_search` were available but not used; grep over two monorepos was used instead.
|
|
82
|
+
- **Feedback-loop gap analysis** — none; verification ran incrementally after each TDD step, not only at the end.
|
|
83
|
+
|
|
84
|
+
### Changes made
|
|
85
|
+
|
|
86
|
+
1. `AGENTS.md` (Pre-completion reviewer subsection) — added a one-line guardrail: agent `model:` frontmatter must use the `provider/id` alias form the Pi CLI/UI accepts, because an ID absent from the model registry silently falls back to the parent session model.
|
|
87
|
+
2. `packages/pi-subagents/docs/retro/0262-add-workspace-provider-seam.md` — this Final Retrospective stage entry.
|