pi-herdr-agents 1.7.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -3
- package/CHANGELOG.md +29 -1
- package/CONTEXT.md +61 -0
- package/README.md +111 -23
- package/RELEASING.md +1 -1
- package/agents/adversarial-reviewer.md +3 -1
- package/docs/README.md +1 -0
- package/docs/adr/0002-agent-workflow-skill-runtime-taxonomy.md +1 -1
- package/docs/adr/0003-installable-role-packs.md +2 -2
- package/docs/adr/0011-explicit-worktree-cleanup.md +74 -0
- package/docs/worktree-subagents.md +21 -11
- package/package.json +2 -2
- package/pi-extension/subagents/config-path.ts +22 -0
- package/pi-extension/subagents/herdr.ts +51 -12
- package/pi-extension/subagents/index.ts +260 -26
- package/pi-extension/subagents/launch.ts +18 -2
- package/pi-extension/subagents/model-config.ts +234 -41
- package/pi-extension/subagents/pane-config.ts +6 -8
- package/pi-extension/subagents/persistent-config.ts +6 -11
- package/pi-extension/subagents/plan-skill.md +2 -2
- package/pi-extension/subagents/role-config.ts +6 -8
- package/pi-extension/subagents/runtime-routing.ts +95 -3
- package/pi-extension/subagents/status.ts +6 -8
- package/pi-extension/subagents/supervision-config.ts +6 -8
- package/pi-extension/subagents/task-model-init.ts +86 -0
- package/pi-extension/subagents/worktree-cleanup.ts +789 -0
- package/skills/orchestrate/SKILL.md +3 -1
package/AGENTS.md
CHANGED
|
@@ -48,7 +48,7 @@ Preserve these invariants when changing worktree behavior:
|
|
|
48
48
|
6. Herdr creates the workspace without stealing focus; launch targets the returned root pane explicitly.
|
|
49
49
|
7. Successful, failed, and help-requesting runs retain their worktree workspace.
|
|
50
50
|
8. Completion reports reviewable Git state; inspection failures are unknown, never guessed clean or conflict-free.
|
|
51
|
-
9. The extension does not push, create PRs, merge, cherry-pick, switch the parent checkout, or remove worktrees
|
|
51
|
+
9. The extension does not push, create PRs, merge, cherry-pick, switch the parent checkout, or remove worktrees automatically. Explicit parent-owned cleanup uses cwd containment and fail-closed eligibility; branches are never deleted.
|
|
52
52
|
10. Ordinary non-worktree subagent behavior remains unchanged.
|
|
53
53
|
|
|
54
54
|
Read [`docs/worktree-subagents.md`](docs/worktree-subagents.md) before changing any of these semantics.
|
|
@@ -105,8 +105,8 @@ Use `PI_TEST_MODEL="openai-codex/gpt-5.6-luna" PI_TEST_TIMEOUT=180000 npm run te
|
|
|
105
105
|
Before committing:
|
|
106
106
|
|
|
107
107
|
- inspect `git status` and the final diff;
|
|
108
|
-
- confirm the package preview includes `CHANGELOG.md`, `skills/orchestrate/SKILL.md`, `skills/orchestrate/adversarial-review.md`, and `skills/orchestrate/adversarial-review-example.js`, while excluding `pi-extension/subagents/workflow-worker.js`, plans, journals, sessions, prototypes, generated evidence,
|
|
109
|
-
- run `npm pack --dry-run` when package contents or documentation paths changed;
|
|
108
|
+
- confirm the package preview includes `CHANGELOG.md`, `skills/orchestrate/SKILL.md`, `skills/orchestrate/adversarial-review.md`, and `skills/orchestrate/adversarial-review-example.js`, while excluding `pi-extension/subagents/workflow-worker.js`, plans, journals, sessions, prototypes, generated evidence, local config, and `openspec/`;
|
|
109
|
+
- run `npm pack --dry-run` when package contents or documentation paths changed; durable configuration is `$PI_CODING_AGENT_DIR/herdr-agents/config.json`, never package-root `config.json` (move old files manually or re-run `/subagents-init`);
|
|
110
110
|
- confirm that no generated plans, journals, sessions, provider configuration, test scripts, or review artifacts are staged; and
|
|
111
111
|
- confirm that no accidental empty directory exists at the repository root:
|
|
112
112
|
|
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
9
9
|
|
|
10
|
-
## [
|
|
10
|
+
## [v2.0.1](https://github.com/giuseppecrj/pi-herdr-agents/compare/v2.0.0...v2.0.1)
|
|
11
|
+
|
|
12
|
+
### Merged
|
|
13
|
+
|
|
14
|
+
- fix: initialize task routing from the live model registry [`#51`](https://github.com/giuseppecrj/pi-herdr-agents/pull/51)
|
|
15
|
+
|
|
16
|
+
## [v2.0.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.7.0...v2.0.0) - 2026-09-18
|
|
17
|
+
|
|
18
|
+
### Merged
|
|
19
|
+
|
|
20
|
+
- feat(subagents): task-category model routing, durable global config, and /subagents-init [`#49`](https://github.com/giuseppecrj/pi-herdr-agents/pull/49)
|
|
21
|
+
- feat(subagents): managed worktree inventory and explicit cleanup [`#50`](https://github.com/giuseppecrj/pi-herdr-agents/pull/50)
|
|
22
|
+
|
|
23
|
+
### Commits
|
|
24
|
+
|
|
25
|
+
- feat(subagents): explicit worktree cleanup (astra build) [`76736b2`](https://github.com/giuseppecrj/pi-herdr-agents/commit/76736b23f661331d028c56f95541c5b859b0f6e2)
|
|
26
|
+
- fix(subagents): verify cleanup safety and disclose process visibility [`5ec8631`](https://github.com/giuseppecrj/pi-herdr-agents/commit/5ec86310bb4a33c737557cb1664fda669a971ff8)
|
|
27
|
+
- add openspec [`7a73e0c`](https://github.com/giuseppecrj/pi-herdr-agents/commit/7a73e0c9cd3fadfaae941fabd6c71e07226f8214)
|
|
28
|
+
- fix(subagents): harden worktree cleanup after adversarial review [`d3c7e7f`](https://github.com/giuseppecrj/pi-herdr-agents/commit/d3c7e7f732b88b895077e8a8c2a946d5f616562c)
|
|
29
|
+
- feat(subagents): add task model routing [`07fce0d`](https://github.com/giuseppecrj/pi-herdr-agents/commit/07fce0d10febce30d21db862c2ae91f435f627de)
|
|
30
|
+
- fix(subagents): repair task model routing review findings [`912ac78`](https://github.com/giuseppecrj/pi-herdr-agents/commit/912ac78fadbb5ab2011f6b9ec680ffb51ad7f9d5)
|
|
31
|
+
- docs(openspec): propose task-model-routing change [`6b97fc7`](https://github.com/giuseppecrj/pi-herdr-agents/commit/6b97fc73bd9cf68dce2275db4882622d1fcee6e2)
|
|
32
|
+
- docs(openspec): address adversarial review of task-model-routing [`7942440`](https://github.com/giuseppecrj/pi-herdr-agents/commit/7942440c0fe21b8900f56aba2563f57330037e82)
|
|
33
|
+
- chore: release v2.0.0 [`211a43b`](https://github.com/giuseppecrj/pi-herdr-agents/commit/211a43b01c304b59e867ee7003b6f68573cebeda)
|
|
34
|
+
- test(integration): harden cleanup test against wrap and holder races [`635c79b`](https://github.com/giuseppecrj/pi-herdr-agents/commit/635c79b5fe4f5cda35ad46be9575144ed7f9bf7a)
|
|
35
|
+
- docs(openspec): check off 5.2 after live /subagents-init verification [`879438d`](https://github.com/giuseppecrj/pi-herdr-agents/commit/879438d50b1944631c42cec6009bab9ba8226a6c)
|
|
36
|
+
|
|
37
|
+
## [v1.7.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.6.0...v1.7.0) - 2026-09-09
|
|
11
38
|
|
|
12
39
|
### Merged
|
|
13
40
|
|
|
@@ -23,6 +50,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
|
23
50
|
- fix(subagents): keep no-progress recovery guidance evidence-based (#40) [`9cbf155`](https://github.com/giuseppecrj/pi-herdr-agents/commit/9cbf155e291c063f70ef86800ef3bfe3c742c6c6)
|
|
24
51
|
- test(integration): hang advisory coverage (#40) [`075f4fe`](https://github.com/giuseppecrj/pi-herdr-agents/commit/075f4feb17938df28a92fa00851d606e5a83b258)
|
|
25
52
|
- fix(subagents): preserve boundary-aligned records in bounded session tails (#40) [`f096fdb`](https://github.com/giuseppecrj/pi-herdr-agents/commit/f096fdb681921e9ab460610d53e456758802725f)
|
|
53
|
+
- chore: release v1.7.0 [`371265e`](https://github.com/giuseppecrj/pi-herdr-agents/commit/371265e74fb7485afbfbc7c028d60dc4f98d2779)
|
|
26
54
|
|
|
27
55
|
## [v1.6.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.5.1...v1.6.0) - 2026-09-08
|
|
28
56
|
|
package/CONTEXT.md
CHANGED
|
@@ -99,3 +99,64 @@ _Avoid_: Completed agent process, disposable pane, automatic worktree cleanup
|
|
|
99
99
|
The lifetime-exclusive binding between a persistent specialist generation and
|
|
100
100
|
one managed worktree, when that specialist writes in a worktree.
|
|
101
101
|
_Avoid_: Rebindable checkout, shared worktree ownership
|
|
102
|
+
|
|
103
|
+
**Worktree inventory**:
|
|
104
|
+
An inspect-only view joining managed checkout discovery, Git registration/state,
|
|
105
|
+
Herdr workspace association, and reachable owned manifests, including orphans.
|
|
106
|
+
_Avoid_: Session-only resource list, cleanup action
|
|
107
|
+
|
|
108
|
+
**Cwd containment**:
|
|
109
|
+
Cleanup authorization requiring the canonical source repository root to equal or
|
|
110
|
+
be a descendant of the invoking session's canonical cwd.
|
|
111
|
+
_Avoid_: Managed-path containment, manifest ownership authorization
|
|
112
|
+
|
|
113
|
+
**Cleanup eligibility**:
|
|
114
|
+
Fresh evidence of cwd containment, registered checkout identity, a named branch,
|
|
115
|
+
no detected process holder, known live child, or persistent lease, and clean Git
|
|
116
|
+
state. Only Herdr-confirmed idle retained shells are exempt from process checks,
|
|
117
|
+
never runtimes at the same PID. Ignored files and individual process-visibility
|
|
118
|
+
gaps are disclosed, not blockers. Other unknown evidence blocks removal.
|
|
119
|
+
_Avoid_: Guessed idle, presumed clean
|
|
120
|
+
|
|
121
|
+
**Process-inspection warning**:
|
|
122
|
+
Non-blocking disclosure of incomplete same-user process visibility, separate
|
|
123
|
+
from cleanup blockers and requiring no override flag. Scanning continues after
|
|
124
|
+
unreadable details; detected holders still block. Other-user processes are not
|
|
125
|
+
inspected, and a protected process could hold the checkout undetected. Failed
|
|
126
|
+
global enumeration and unsupported platforms remain blockers.
|
|
127
|
+
_Avoid_: Proven unrelated, machine-wide inactivity, bypass permission
|
|
128
|
+
|
|
129
|
+
**Explicit worktree removal**:
|
|
130
|
+
A parent-requested removal of one named managed checkout and its open workspace,
|
|
131
|
+
with absence verification and retained branch history. Never automatic reaping.
|
|
132
|
+
_Avoid_: Branch deletion, completion cleanup
|
|
133
|
+
|
|
134
|
+
**Dirty-state preservation**:
|
|
135
|
+
Explicit opt-in staging and WIP commitment of a worktree's uncommitted and
|
|
136
|
+
untracked files on its retained branch before rechecking removal eligibility.
|
|
137
|
+
Ignored files are not captured. Commit failure restores the original index.
|
|
138
|
+
_Avoid_: Implicit commit, stash, discard
|
|
139
|
+
|
|
140
|
+
**Task-category model preference**:
|
|
141
|
+
An ordered authenticated model shortlist in `models.tasks` for `coding`,
|
|
142
|
+
`review`, `recon`, `qa`, `architecture`, or `docs`. Recon maps to scouts,
|
|
143
|
+
architecture to planning and diagnosis, coding to workers, review to reviewers,
|
|
144
|
+
QA to software and test runners, and docs to documentation workers. Categories
|
|
145
|
+
describe work, not complexity. `/subagents-init [preferences]` drafts them from
|
|
146
|
+
the active extension-loaded registry's synchronous snapshot and existing saved
|
|
147
|
+
choices, with source-based research when available. A dynamic provider awaiting
|
|
148
|
+
its initial catalog refresh might be absent. `task:<category>` is a subagent
|
|
149
|
+
model selector, not a command or parent model change. Ordered authenticated
|
|
150
|
+
candidate plans resolve before launch; ordinary nonpersistent runs can retry
|
|
151
|
+
after launch failure or a running child's provider/agent error, not a completed
|
|
152
|
+
negative task result. Persistent specialists do not advance after a running-child
|
|
153
|
+
error. Worktrees select the first authenticated candidate only, without fallback
|
|
154
|
+
retries. Reviews use exact IDs from a different author family when the author
|
|
155
|
+
is known.
|
|
156
|
+
_Avoid_: Generic tier, reviewer-family enforcement, per-step routing
|
|
157
|
+
|
|
158
|
+
**Loop template**:
|
|
159
|
+
A future reusable orchestration definition beside `models`, describing stages,
|
|
160
|
+
task categories, and a termination/report contract. Loop templates are not
|
|
161
|
+
implemented by task-model routing.
|
|
162
|
+
_Avoid_: Current executable workflow
|
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ Subagent tabs, panes, and worktree workspaces are created without stealing keybo
|
|
|
119
119
|
|
|
120
120
|
### Extensions
|
|
121
121
|
|
|
122
|
-
**Subagents** —
|
|
122
|
+
**Subagents** — 9 parent-session tools + 7 commands, plus 2 child-only tools:
|
|
123
123
|
|
|
124
124
|
| Tool | Description |
|
|
125
125
|
| -------------------- | ------------------------------------------------------------------------------------------- |
|
|
@@ -128,7 +128,10 @@ Subagent tabs, panes, and worktree workspaces are created without stealing keybo
|
|
|
128
128
|
| `subagent_send` | Deliver a follow-up task to an idle persistent specialist |
|
|
129
129
|
| `subagent_stop` | Gracefully stop a persistent specialist after its active task settles |
|
|
130
130
|
| `subagents_list` | List available agent definitions |
|
|
131
|
+
| `worktree_list` | Parent-only inspect-only inventory of managed worktrees and cleanup blockers |
|
|
132
|
+
| `worktree_remove` | Parent-only explicit removal by `target` path, branch, or workspace ID; optional `preserve: true` commits dirty state first |
|
|
131
133
|
| `subagent_resume` | Resume a previous Pi-backed sub-agent session in a new ordinary pane (async) |
|
|
134
|
+
| `subagents_write_task_models` | Parent-only internal tool that validates and atomically writes `models.tasks` preferences |
|
|
132
135
|
|
|
133
136
|
| Pi child-only tool | Description |
|
|
134
137
|
| ---------------- | ------------------------------------------------------------------------- |
|
|
@@ -143,6 +146,7 @@ Subagent tabs, panes, and worktree workspaces are created without stealing keybo
|
|
|
143
146
|
| `/btw-close` | Close the current BTW session |
|
|
144
147
|
| `/worktree <name> [task]` | Continue this session in a new managed worktree (`/worktree list` lists them) |
|
|
145
148
|
| `/subagent <agent> <task>` | Spawn a named agent directly (`/subagent list` lists available agents) |
|
|
149
|
+
| `/subagents-init [preferences]` | Draft task-category model preferences from the live authenticated registry, with optional ranking preferences |
|
|
146
150
|
|
|
147
151
|
### Taxonomy and discovery
|
|
148
152
|
|
|
@@ -271,20 +275,13 @@ A fixed internal watchdog marks a run as `stalled` when pane inspection fails or
|
|
|
271
275
|
|
|
272
276
|
#### Configuration
|
|
273
277
|
|
|
274
|
-
The
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
-
|
|
280
|
-
-
|
|
281
|
-
|
|
282
|
-
Project-local installs use the corresponding `.pi/npm/` or `.pi/git/` root.
|
|
283
|
-
From the actual package root, copy the example when you want local overrides:
|
|
284
|
-
|
|
285
|
-
```bash
|
|
286
|
-
cp config.json.example config.json
|
|
287
|
-
```
|
|
278
|
+
The durable user configuration is `$PI_CODING_AGENT_DIR/herdr-agents/config.json`,
|
|
279
|
+
defaulting to `~/.pi/agent/herdr-agents/config.json`. It is not read from the
|
|
280
|
+
installed package root, so npm and git package upgrades do not overwrite it.
|
|
281
|
+
Create it by copying the installed package's `config.json.example`, or run
|
|
282
|
+
`/subagents-init` to seed and draft model task preferences. This is a breaking
|
|
283
|
+
migration: manually move an existing package-local `config.json` to this path,
|
|
284
|
+
or re-run `/subagents-init`.
|
|
288
285
|
|
|
289
286
|
```json
|
|
290
287
|
{
|
|
@@ -327,11 +324,93 @@ exact IDs from your authenticated model catalog:
|
|
|
327
324
|
"agents": {
|
|
328
325
|
"scout": "your-provider/your-fast-model",
|
|
329
326
|
"reviewer": "your-provider/your-review-model"
|
|
327
|
+
},
|
|
328
|
+
"tasks": {
|
|
329
|
+
"coding": ["your-provider/your-coding-model"],
|
|
330
|
+
"review": ["your-provider/your-review-model"],
|
|
331
|
+
"recon": ["your-provider/your-fast-model"],
|
|
332
|
+
"qa": ["your-provider/your-qa-model"],
|
|
333
|
+
"architecture": ["your-provider/your-architecture-model"],
|
|
334
|
+
"docs": ["your-provider/your-docs-model"]
|
|
335
|
+
},
|
|
336
|
+
"tasksMeta": {
|
|
337
|
+
"generatedAt": "2026-09-17T00:00:00Z",
|
|
338
|
+
"method": "research"
|
|
330
339
|
}
|
|
331
340
|
}
|
|
332
341
|
}
|
|
333
342
|
```
|
|
334
343
|
|
|
344
|
+
`models.tasks` candidates are ordered exact authenticated IDs. Use
|
|
345
|
+
`task:<category>` only in the `subagent` tool's `model` argument; it is not
|
|
346
|
+
valid in frontmatter or model defaults. For review when the authoring family is
|
|
347
|
+
known, choose an exact shortlist ID from a different family rather than
|
|
348
|
+
`task:review`; this is guidance, not extension enforcement.
|
|
349
|
+
|
|
350
|
+
Run `/subagents-init [preferences]` to draft task-model preferences. For example:
|
|
351
|
+
|
|
352
|
+
```text
|
|
353
|
+
/subagents-init Prefer capability over price for implementation; keep recon inexpensive
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
The command supplies a sanitized snapshot of **all available models from the
|
|
357
|
+
active session registry**, including extension-registered providers, exact IDs,
|
|
358
|
+
display names, reported base token costs, context/output limits, input
|
|
359
|
+
modalities, reasoning, and supported thinking levels. Safe extension-registration and auth-source
|
|
360
|
+
metadata is included when Pi exposes it; credentials, endpoints, and raw auth
|
|
361
|
+
labels are not. Configured authentication does not prove account access or a
|
|
362
|
+
successful request. Missing costs remain unknown; reported zero does not mean
|
|
363
|
+
free, and OAuth does not establish subscription billing. The brief uses compact
|
|
364
|
+
JSON without truncating models and reports its model count and JSON character
|
|
365
|
+
count (not a token estimate); large catalogs still consume context. This is the
|
|
366
|
+
current synchronous snapshot: a dynamic provider whose initial catalog refresh
|
|
367
|
+
has not completed might be absent. Init does not refresh providers or probe the
|
|
368
|
+
network for availability.
|
|
369
|
+
|
|
370
|
+
The draft considers current saved task, default, and per-agent preferences.
|
|
371
|
+
Optional command arguments set ranking preferences. Otherwise it favors
|
|
372
|
+
capability for substantive work and efficiency for bounded reconnaissance and
|
|
373
|
+
test execution. Categories describe work, not complexity tiers:
|
|
374
|
+
|
|
375
|
+
| Category | Work |
|
|
376
|
+
| --- | --- |
|
|
377
|
+
| `coding` | Implementation workers |
|
|
378
|
+
| `review` | Code reviewers |
|
|
379
|
+
| `recon` | Reconnaissance scouts |
|
|
380
|
+
| `qa` | Software and test runners |
|
|
381
|
+
| `architecture` | Planning and diagnosis |
|
|
382
|
+
| `docs` | Documentation workers |
|
|
383
|
+
|
|
384
|
+
Init asks the agent to research major candidates across providers using primary
|
|
385
|
+
sources, disclose uncertainty and notable exclusions, and avoid duplicate
|
|
386
|
+
upstream models across routes unless deliberate redundancy is explained. Display
|
|
387
|
+
names help identify candidates but, like aliases, do not prove upstream
|
|
388
|
+
equivalence; research is still required. Price or context size alone is not
|
|
389
|
+
quality evidence. It reports `registry-only` when
|
|
390
|
+
search is unavailable or yields no usable evidence; no live model probes run.
|
|
391
|
+
|
|
392
|
+
The writer validates and atomically replaces `models.tasks` and `tasksMeta`,
|
|
393
|
+
preserving unrelated settings. Its tool schema accepts partial nonempty
|
|
394
|
+
categories (omitted categories are removed), rejects empty `tasks: {}` input,
|
|
395
|
+
and rejects exact duplicate refs within a category after trimming;
|
|
396
|
+
IDs remain case-sensitive. Its result includes normalized saved `tasks`,
|
|
397
|
+
`tasksMeta`, `configPath`, and `missingCategories`. Init requests all six categories
|
|
398
|
+
and a before/after table based on that saved result, not the unsaved draft. It
|
|
399
|
+
must explain missing categories or changed choices; with no available models,
|
|
400
|
+
it must report the limitation without writing.
|
|
401
|
+
|
|
402
|
+
`task:<category>` values select subagent models; they are not slash commands and
|
|
403
|
+
do not change the parent model. Ordered authenticated candidate plans resolve
|
|
404
|
+
before launch. Ordinary nonpersistent runs can retry later candidates after
|
|
405
|
+
launch failure or after a running child settles with a provider/agent error,
|
|
406
|
+
not after a completed negative task result. Persistent specialists do not
|
|
407
|
+
advance after a running-child error. This is not per-step routing; worktrees
|
|
408
|
+
use the first authenticated candidate only, without fallback retries.
|
|
409
|
+
Shortlists do not enforce reviewer independence: select an exact reviewer from
|
|
410
|
+
a different author family when the author is known, and a different provider
|
|
411
|
+
when project policy requires it. Another route to the same family is not
|
|
412
|
+
independent review. Run `/reload` (or start a new session) after writing preferences.
|
|
413
|
+
|
|
335
414
|
Set `persistent.maxAgents` to the maximum concurrently retained persistent specialists. It defaults to `3`; a persistent spawn at the cap is rejected before Herdr creates a pane or workspace, and no specialist is evicted.
|
|
336
415
|
|
|
337
416
|
Set `roles.bundled` to `false` to exclude this package's bundled role definitions from listing and exact-name launch. It defaults to `true`. Registered role packs remain available, and global and project definitions keep their existing precedence. A role-pack name collides with a bundled role only while that bundled layer is enabled; when it is disabled, the role pack can supply that name.
|
|
@@ -344,7 +423,7 @@ collection; it never establishes a result by itself. If the watcher or shared
|
|
|
344
423
|
pane inspection becomes unavailable, supervision quietly returns to the legacy
|
|
345
424
|
one-second polling cadence. No caller action is required.
|
|
346
425
|
|
|
347
|
-
Set `supervision.forcePolling` to `true` in the
|
|
426
|
+
Set `supervision.forcePolling` to `true` in the durable user `config.json` to
|
|
348
427
|
disable wake-ups and use that legacy cadence deliberately. The setting is read
|
|
349
428
|
when the coordinator is created, so run `/reload` after changing it.
|
|
350
429
|
`subagents_list` reports the active transport mode (`wake+batch`,
|
|
@@ -405,7 +484,9 @@ Run `/reload` after changing role, model, or pane settings.
|
|
|
405
484
|
followed by agent frontmatter, per-agent config, the global default, and finally
|
|
406
485
|
the parent model. Model values must be exact authenticated `provider/model-id`
|
|
407
486
|
references. A value can contain an ordered comma-separated fallback list, for
|
|
408
|
-
example `provider/preferred, provider/fallback`. The
|
|
487
|
+
example `provider/preferred, provider/fallback`. The tool argument also accepts
|
|
488
|
+
`task:<category>` as its complete value (not in a list), for configured
|
|
489
|
+
`coding`, `review`, `recon`, `qa`, `architecture`, or `docs` preferences. The extension validates every
|
|
409
490
|
candidate before launch, then launches later candidates only after the selected
|
|
410
491
|
child settles with a provider/agent error. Pi owns any automatic transient
|
|
411
492
|
retrying inside that child; the extension does not infer retry counts or
|
|
@@ -425,10 +506,9 @@ claim a permanent failure or a retry count that Pi has not exposed. Reliable
|
|
|
425
506
|
structured permanence and retry counts require an upstream Pi/ExtensionAPI
|
|
426
507
|
diagnostics seam for final provider errors and retry outcomes.
|
|
427
508
|
|
|
428
|
-
`config.json` is
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
status, model, role, and pane configuration are loaded when the extension starts.
|
|
509
|
+
`config.json` is durable user state under the Pi agent directory and is loaded
|
|
510
|
+
when the extension starts. Run `/reload` after changing it. Package-root
|
|
511
|
+
`config.json` files are ignored; move them manually or re-run `/subagents-init`.
|
|
432
512
|
|
|
433
513
|
---
|
|
434
514
|
|
|
@@ -468,7 +548,7 @@ subagent({
|
|
|
468
548
|
| `fork` | boolean | `false` | Force the full-context fork mode for this spawn, overriding any agent `session-mode` frontmatter |
|
|
469
549
|
| `persistent` | boolean | `false` | Keep one specialist session alive for sequential tasks; follow-ups use `subagent_send` only |
|
|
470
550
|
| `interactive` | boolean | derived | Mark this spawn as interactive (don't wake the parent on stall/recovery). Defaults to the agent's `interactive` frontmatter, otherwise the inverse of `auto-exit`. |
|
|
471
|
-
| `model` | string | configured or parent | Exact authenticated `provider/model-id`,
|
|
551
|
+
| `model` | string | configured or parent | Exact authenticated `provider/model-id`, ordered fallback list, or whole-value `task:<category>` (coding, review, recon, qa, architecture, docs). Task routing is tool-only; worktrees use its first authenticated candidate. Resolution is tool argument → agent frontmatter → per-agent config → global config → parent |
|
|
472
552
|
| `thinking` | string | parent level | Pick the model tier first, then set thinking within that model's range: minimal/low for bounded mechanical work, medium for ordinary implementation or review, high+ for architecture, security, or hard diagnosis. Omitting still inherits the parent level; this is a discouraged fallback for orchestrated children. |
|
|
473
553
|
| `systemPrompt` | string | — | Role/system-prompt text for a bare spawn; named agents keep their definition body |
|
|
474
554
|
| `skills` | string | — | Comma-separated skill names |
|
|
@@ -636,10 +716,18 @@ BTW shares the current working directory. It treats inherited work as reference
|
|
|
636
716
|
|
|
637
717
|
## The `/worktree` Workflow
|
|
638
718
|
|
|
639
|
-
`/worktree <worktree> [task]` creates a Herdr-managed worktree from the current committed branch and launches a new interactive Pi session there with the active conversation branch. The original session remains available. Use `/worktree list` to
|
|
719
|
+
`/worktree <worktree> [task]` creates a Herdr-managed worktree from the current committed branch and launches a new interactive Pi session there with the active conversation branch. The original session remains available. Use `/worktree list` or `worktree_list({})` to inspect managed worktrees, including cross-session orphans, whose canonical source repositories are inside the session's cwd subtree. This is a new-process handoff, not an in-place move of the existing shell or Pi process.
|
|
640
720
|
|
|
641
721
|
---
|
|
642
722
|
|
|
723
|
+
### Explicit worktree cleanup
|
|
724
|
+
|
|
725
|
+
Parent sessions can call `worktree_remove({ target: "<path|branch|workspace-id>", preserve: true })` or `/worktree remove <target> [--preserve]`. Preservation is optional and never implied: dirty work is blocked unless explicitly committed first or preserved as a WIP commit. The result reports its SHA even if removal later fails or is refused. A failed preservation commit restores the pre-preservation index and never proceeds to removal. Inventory and removal reports disclose exact ignored-file counts: enumeration is streamed rather than buffered as one listing. Ignored files do not block cleanup and are not captured by preservation; failed counting still blocks removal.
|
|
726
|
+
|
|
727
|
+
Eligibility is rechecked at removal time: canonical source-repository cwd containment, registered linked checkout, no detected process holder, known live child, or persistent lease, and clean Git state with no untracked files or conflicts. Unknown inspection, identity disagreements, detached HEAD, locked checkouts, and initialized submodules block removal. Out-of-scope repositories are never eligible. Open workspaces use Herdr removal; orphans use Git removal and registration pruning after checkout absence is verified. Owned reachable manifests are marked `removed`; manifests from other sessions are not required or rewritten. Stale removed manifests never govern a recreated checkout. Missing or dangling manifest paths do not affect unrelated checkouts; undecidable or conflicting manifest identity still blocks removal. A failed manifest update after removal is reported as a warning. Symlinked ancestors are supported; checkout symlinks escaping the canonical managed root are blocked. Process inspection covers observable same-user processes across sessions, regardless of runtime name, exempting only Herdr-confirmed idle retained shells, never runtimes at the same PID. Unreadable individual process details produce non-blocking warnings without an override flag; scanning continues so another observable holder still blocks removal. Human and structured inventory/removal results disclose incomplete coverage, including warnings seen before a later recheck or failure. Same-user inspection is permission-limited, and other-user processes are not inspected: a protected process could hold the checkout undetected. Warnings aggregate counts and bounded PID samples, not commands or environments. Linux uses `/proc`; macOS uses same-user `lsof` cwd records and warns for unreadable individual records. Unsupported platforms, failed global enumeration (including a failed `lsof` with partial output), and other unverifiable eligibility evidence still block removal. Cleanup Git and Herdr calls have 30-second timeouts.
|
|
728
|
+
|
|
729
|
+
Cleanup never deletes or rewrites branches, uses force flags, or runs automatically. Session start only reports retained-worktree counts. Child sessions retain `/worktree list` and `/worktree <name>`, but receive neither cleanup tools nor the remove subcommand. Their repository-local listing labels detached entries `(detached HEAD)`; a detached sibling does not prevent inspection of named branches. See [cleanup and recovery](docs/worktree-subagents.md#cleanup) for details.
|
|
730
|
+
|
|
643
731
|
## Custom Agents
|
|
644
732
|
|
|
645
733
|
Custom agent roles are the package's primary extension mechanism. Create one
|
package/RELEASING.md
CHANGED
|
@@ -41,7 +41,7 @@ The optional live-provider smoke test is not a release gate:
|
|
|
41
41
|
PI_TEST_MODEL="openai-codex/gpt-5.6-luna" PI_TEST_TIMEOUT=180000 npm run test:integration:live
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Do not release from skipped Herdr tests. Confirm the package preview includes `README.md`, `CHANGELOG.md`, `AGENTS.md`, `docs/`, `agents/`, `skills/orchestrate/SKILL.md`, `skills/orchestrate/adversarial-review.md`, `skills/orchestrate/adversarial-review-example.js`, and excludes `pi-extension/subagents/workflow-worker.js`. Confirm it excludes plans, journals, sessions, prototypes, generated evidence,
|
|
44
|
+
Do not release from skipped Herdr tests. Confirm the package preview includes `README.md`, `CHANGELOG.md`, `AGENTS.md`, `docs/`, `agents/`, `skills/orchestrate/SKILL.md`, `skills/orchestrate/adversarial-review.md`, `skills/orchestrate/adversarial-review-example.js`, and excludes `pi-extension/subagents/workflow-worker.js`. Confirm it excludes plans, journals, sessions, prototypes, generated evidence, local `config.json` and `openspec/`, and that the worktree integration tests leave no test workspace behind. Durable user configuration is `$PI_CODING_AGENT_DIR/herdr-agents/config.json`; package-root configuration is ignored, so users must move an older file manually or re-run `/subagents-init`.
|
|
45
45
|
|
|
46
46
|
## npm authentication
|
|
47
47
|
|
|
@@ -40,7 +40,9 @@ identify missing evidence.
|
|
|
40
40
|
`fork: false` does not override role `session-mode: fork`. Stop for a
|
|
41
41
|
non-standalone or unknown mode so no reviewer inherits coordinator context.
|
|
42
42
|
4. Use the model-catalog source identified by the `subagent` tool guidance or
|
|
43
|
-
another project-approved source.
|
|
43
|
+
another project-approved source. Curated task shortlists can inform selection,
|
|
44
|
+
but review must name an exact ID and exclude known author families; never use
|
|
45
|
+
`task:review` when that exclusion is required. Record that source, how authentication was
|
|
44
46
|
confirmed, eligible distinct exact IDs, and only IDs actually considered but
|
|
45
47
|
omitted, with reasons. Never guess unknown catalog entries. Identify every
|
|
46
48
|
provider/model family that authored the reviewed material, or confirm that it
|
package/docs/README.md
CHANGED
|
@@ -41,6 +41,7 @@ uses an isolated Herdr server and writes uncommitted raw samples to
|
|
|
41
41
|
| [`0008`](adr/0008-adopt-pi-only-subagent-execution.md) | Accepted; implemented | Remove the external CLI adapter and make subagent execution Pi-only. |
|
|
42
42
|
| [`0009`](adr/0009-remove-workflow-subsystem.md) | Accepted | Remove the workflow subsystem; use public subagent fan-out and parent synthesis. |
|
|
43
43
|
| [`0010`](adr/0010-persistent-specialists-as-session-generations.md) | Accepted | Define persistent specialists as logical identities with policy-bound session generations. |
|
|
44
|
+
| [`0011`](adr/0011-explicit-worktree-cleanup.md) | Accepted | Authorize explicit worktree cleanup by cwd containment; retain branches and reject automatic reaping. |
|
|
44
45
|
|
|
45
46
|
## Historical material
|
|
46
47
|
|
|
@@ -143,7 +143,7 @@ Apply these rules:
|
|
|
143
143
|
|
|
144
144
|
1. Prefer per-invocation `model` and `thinking` for a workflow's deliberate
|
|
145
145
|
diversity or cost/quality policy.
|
|
146
|
-
2. Use
|
|
146
|
+
2. Use `$PI_CODING_AGENT_DIR/herdr-agents/config.json` for a person's durable role preferences; package-root configuration is ignored, so move an older file manually or re-run `/subagents-init`.
|
|
147
147
|
3. Leave bundled role `model` unset unless a particular model is a functional
|
|
148
148
|
prerequisite.
|
|
149
149
|
4. State runtime prerequisites before launch and fail closed when a required
|
|
@@ -81,7 +81,7 @@ Within the package layer:
|
|
|
81
81
|
|
|
82
82
|
- bundled roles are protected fallbacks while enabled;
|
|
83
83
|
- a role pack colliding with an enabled bundled name is rejected;
|
|
84
|
-
- copying `config.json.example` to
|
|
84
|
+
- copying the packaged `config.json.example` to `$PI_CODING_AGENT_DIR/herdr-agents/config.json` and setting `roles.bundled` to `false` removes only the bundled layer; registered role packs remain package roles and may supply those names; package-root configuration is ignored, so manually move older files or re-run `/subagents-init`;
|
|
85
85
|
- a name contributed by multiple role packs is disabled;
|
|
86
86
|
- collisions never resolve through incidental extension load order.
|
|
87
87
|
|
|
@@ -95,7 +95,7 @@ before Herdr creates a pane or worktree.
|
|
|
95
95
|
## Reload and security
|
|
96
96
|
|
|
97
97
|
Role files are read on each list or launch, so editing Markdown does not require
|
|
98
|
-
`/reload`. Changing
|
|
98
|
+
`/reload`. Changing durable user role configuration, installing, removing,
|
|
99
99
|
updating, or changing a role-pack extension uses Pi's normal reload flow.
|
|
100
100
|
Contributor `session_shutdown` cleanup removes the
|
|
101
101
|
old event listener before replacement extensions register. Already-running
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ADR-0011: Authorize explicit worktree cleanup by cwd containment
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-17
|
|
5
|
+
- **Scope:** Managed subagent worktree cleanup (issue #45)
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
A parent may explicitly remove one managed worktree when its canonical source
|
|
10
|
+
repository is within the session's canonical cwd subtree. This supersedes the
|
|
11
|
+
session-manifest-only ownership policy discussed in issue #45, per its
|
|
12
|
+
2026-09-17 scope update: ended sessions leave orphans whose manifests are no
|
|
13
|
+
longer reachable. A reachable owned manifest enriches inventory and is marked
|
|
14
|
+
removed after successful cleanup, but is not the authorization boundary.
|
|
15
|
+
|
|
16
|
+
Discovery joins the managed filesystem root, Git registration and working-tree
|
|
17
|
+
state, live Herdr workspace state, and current-session manifests. Unknown or
|
|
18
|
+
conflicting eligibility evidence blocks removal, subject to the approved
|
|
19
|
+
individual process-visibility exception below. Detected same-user process
|
|
20
|
+
holders, known live children, and persistent leases block removal; process
|
|
21
|
+
names are not a runtime allowlist. Only Herdr-confirmed idle retained shell
|
|
22
|
+
PIDs are exempt, never active runtimes at those PIDs. Dirty work requires an
|
|
23
|
+
explicit WIP preservation commit on the retained branch; detached HEAD,
|
|
24
|
+
conflicts, locks, and initialized submodules cannot be bypassed.
|
|
25
|
+
|
|
26
|
+
The approved process-visibility policy treats unreadable individual process
|
|
27
|
+
details as non-blocking warnings, without an override flag. Inspection continues
|
|
28
|
+
so one hidden process cannot mask an observable holder. Inventory and removal
|
|
29
|
+
results report incomplete coverage separately from blockers, retaining warnings
|
|
30
|
+
through rechecks, preservation, and failures when available. Reports use counts
|
|
31
|
+
and bounded PID samples, not commands or environments. Linux uses `/proc`;
|
|
32
|
+
macOS uses same-user `lsof` cwd records and warns for individual unreadable
|
|
33
|
+
records. Unsupported platforms and failed global enumeration remain blockers;
|
|
34
|
+
partial output from a failed `lsof` is not successful enumeration.
|
|
35
|
+
|
|
36
|
+
Paths are canonicalized, so symlinked ancestors are supported while checkout
|
|
37
|
+
symlinks escaping the managed root are blocked. Ignored files are counted and
|
|
38
|
+
disclosed, but do not block removal and are not captured by preservation. A
|
|
39
|
+
failed preservation commit restores the original index. A created preservation
|
|
40
|
+
SHA remains in the report even when subsequent removal fails.
|
|
41
|
+
|
|
42
|
+
Removed manifests are no-op evidence only and never classify a recreated
|
|
43
|
+
checkout. Post-removal manifest-write failures are warnings, not removal
|
|
44
|
+
failures. Cleanup Git and Herdr calls have bounded 30-second timeouts. Child
|
|
45
|
+
sessions retain their existing `/worktree list` and `/worktree <name>` surfaces;
|
|
46
|
+
only the cleanup tools and removal subcommand are parent-only.
|
|
47
|
+
|
|
48
|
+
Open workspaces are removed through Herdr. Git-only orphans use Git removal,
|
|
49
|
+
verify checkout absence, and then prune stale registrations. Branches and their
|
|
50
|
+
commits are retained. Session start only reports inventory counts.
|
|
51
|
+
|
|
52
|
+
## Rejected alternatives
|
|
53
|
+
|
|
54
|
+
- Session-only ownership cannot recover cross-session orphans.
|
|
55
|
+
- Global authorization would allow one project to remove another's review state.
|
|
56
|
+
- Automatic reaping at completion, shutdown, or on a timer erases the parent's
|
|
57
|
+
opportunity to review retained work.
|
|
58
|
+
- Force removal and branch deletion are outside this feature's authority.
|
|
59
|
+
- Repo-global stashes make preserved work less visible than retained commits.
|
|
60
|
+
- Recursive filesystem deletion to bypass submodule protection is not supported.
|
|
61
|
+
|
|
62
|
+
## Consequences
|
|
63
|
+
|
|
64
|
+
Broad cwd values authorize broad repository subtrees, so callers must choose cwd
|
|
65
|
+
deliberately. Worktrees are not sandboxes. Fresh eligibility checks reduce but
|
|
66
|
+
cannot eliminate races with external writers; underlying refusals are surfaced,
|
|
67
|
+
not overridden. Same-user process inspection is permission-limited, and
|
|
68
|
+
other-user processes are not inspected. A protected process could hold the
|
|
69
|
+
checkout undetected; unreadability is not proof that it is unrelated. This
|
|
70
|
+
accepted blind spot replaces the earlier strict individual-unreadability
|
|
71
|
+
blocker, not the global enumeration or identity guards. Restart inventory does
|
|
72
|
+
not reattach watchers or rewrite other sessions' manifests.
|
|
73
|
+
|
|
74
|
+
See the [operating guide](../worktree-subagents.md#cleanup) for the shipped API.
|
|
@@ -50,7 +50,7 @@ For a worktree launch:
|
|
|
50
50
|
- Uncommitted and untracked files from the parent checkout are not copied. Commit anything the child must see before spawning it, or pass the needed context in the task.
|
|
51
51
|
- Worktree creation does not steal terminal focus.
|
|
52
52
|
|
|
53
|
-
For an explicit interactive handoff, use `/worktree <worktree> [task]`. It creates the worktree from the current committed branch, forks the active conversation branch into the target-cwd session, launches a normal long-lived Pi process in the returned root pane, and focuses the destination workspace only after Herdr confirms Pi is running with the expected session and worktree cwd. Use `/worktree list` to inspect worktrees
|
|
53
|
+
For an explicit interactive handoff, use `/worktree <worktree> [task]`. It creates the worktree from the current committed branch, forks the active conversation branch into the target-cwd session, launches a normal long-lived Pi process in the returned root pane, and focuses the destination workspace only after Herdr confirms Pi is running with the expected session and worktree cwd. Use `/worktree list` to inspect managed worktrees whose source repositories are inside the current cwd subtree, including cross-session orphans. The original process and session remain intact; pane movement is not used to change a running shell's cwd.
|
|
54
54
|
|
|
55
55
|
`worktree` cannot be set in agent frontmatter and is not exposed by the `/subagent <agent> <task>` shorthand. It is selected per call to the `subagent` tool. Ordered model fallback lists are not supported for worktree subagents: a failed attempt retains its worktree and branch for review, so a retry cannot safely reuse the requested branch. A persistent specialist either holds one worktree lease for its full lifetime or runs read-only in an ordinary pane; it cannot be re-bound.
|
|
56
56
|
|
|
@@ -112,6 +112,7 @@ Possible states are:
|
|
|
112
112
|
| `ready_for_review` | Child exited successfully; workspace retained |
|
|
113
113
|
| `needs_help` | Child called `caller_ping`; workspace retained |
|
|
114
114
|
| `failed` | Creation, launch, or execution failed; any created workspace is retained |
|
|
115
|
+
| `removed` | Explicit parent cleanup verified checkout absence; branch and manifest retained |
|
|
115
116
|
|
|
116
117
|
The manifest supports ownership and inspection; v1 does not provide automatic reconciliation after a full Pi/Herdr restart. Do not edit manifests by hand.
|
|
117
118
|
|
|
@@ -131,7 +132,7 @@ The parent receives the normal child summary plus:
|
|
|
131
132
|
|
|
132
133
|
`clean` means there are no staged, unstaged, or untracked files. It does **not** mean the branch has no commits or diff relative to its base.
|
|
133
134
|
|
|
134
|
-
If Git inspection fails, SHA/count/state/file fields are reported as unknown rather than guessed, and the warning is included in the handoff. Inspect the retained workspace directly before integrating or deleting it. Every retained handoff
|
|
135
|
+
If Git inspection fails, SHA/count/state/file fields are reported as unknown rather than guessed, and the warning is included in the handoff. Inspect the retained workspace directly before integrating or deleting it. Every retained handoff points to `/worktree remove` and `worktree_remove`, with the raw Herdr command as an operator override after independent safety checks.
|
|
135
136
|
|
|
136
137
|
## Parallel pull-request review without new worktrees
|
|
137
138
|
|
|
@@ -207,21 +208,30 @@ This manual continuation is not watched by the original parent lifecycle. Do not
|
|
|
207
208
|
|
|
208
209
|
Worktree and branch cleanup is always explicit. Ordinary temporary reviewer panes close after result delivery; Herdr removes their tab only if its last pane closes. The retained worktree root shell is excluded from automatic cleanup, and user-added panes are preserved. Persistent specialists retain their pane between tasks and follow the existing explicit stop semantics.
|
|
209
210
|
|
|
210
|
-
|
|
211
|
+
Parent sessions have an inspect-only inventory and an explicit removal surface:
|
|
211
212
|
|
|
212
|
-
```
|
|
213
|
-
|
|
213
|
+
```text
|
|
214
|
+
/worktree list
|
|
215
|
+
/worktree remove <path|branch|workspace-id> [--preserve]
|
|
214
216
|
```
|
|
215
217
|
|
|
216
|
-
|
|
218
|
+
The equivalent tools are `worktree_list({})` and `worktree_remove({ target, preserve?: true })`. Use the exact path when a branch name is ambiguous. Children are not offered these tools or `/worktree remove`. Their existing `/worktree list` (repository-local Herdr listing) and `/worktree <name>` handoff remain available. Detached entries are labeled `(detached HEAD)` in the child listing and do not prevent cleanup inspection of named-branch siblings; the detached checkout itself remains blocked.
|
|
217
219
|
|
|
218
|
-
|
|
220
|
+
Authorization uses **cwd containment**: the canonical source repository root must equal or descend from the canonical session cwd. It does not use the managed checkout's location or require a current-session manifest. Discovery scans `~/.herdr/worktrees/*/*/`, joins Git registration and Herdr workspace state, and includes orphans from ended sessions. Out-of-scope entries are never removable; unregistered residue and failed probes are unknown, not clean. Inventory includes path, branch, source, workspace, Git state, manifest presence, concrete blockers, and separate process-inspection warnings. The managed root, source, and cwd are canonicalized: symlinked ancestors work normally, but a checkout symlink escaping the managed root is blocked.
|
|
219
221
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
Removal rechecks containment, Git registration, no detected process holder, known live child, or persistent-specialist lease, and no uncommitted, untracked, or conflicted files. Liveness checks observable same-user processes from any session with a cwd inside the checkout, regardless of runtime name (including thread-suffixed Node names). Only Herdr-confirmed idle retained shell PIDs are exempt; an active runtime at the same PID is not. Detached HEAD, locked checkouts, initialized submodules, and conflicting identity evidence block removal. For initialized submodules, deinitialize them deliberately or use operator removal. No force option is provided.
|
|
223
|
+
|
|
224
|
+
Unreadable individual process details are **warnings, not blockers**, with no override flag. Scanning continues after each unreadable entry, so another observable holder still blocks removal. Unknown processes are not classified as unrelated. Inventory rows expose separate `blockers` and `warnings` arrays; removal results expose `warnings` and retain warnings from earlier inspections, including refusals and preservation/removal failures when available. Human-readable output includes the same warnings, with counts and at most ten sampled PIDs per inspection, not process commands or environments. Pi tool refusals and failures use thrown errors, so their warnings travel in the error message rather than structured tool details.
|
|
225
|
+
|
|
226
|
+
This is not proof of machine-wide inactivity: same-user inspection is permission-limited, other-user processes are not inspected, and a protected process could hold the checkout undetected. Linux enumerates `/proc` and checks readable cwd paths; disappeared processes and confirmed zombies do not hold a checkout. macOS uses same-user `lsof` cwd records; unreadable or missing details in individual returned records warn, but a failed global `lsof` blocks even when it returns partial output. Neither platform can establish coverage of processes hidden by the OS. Unsupported platforms, failed or empty global enumeration, and unknown Git, containment, or ownership state remain blockers.
|
|
227
|
+
|
|
228
|
+
Dirty work requires an explicit `--preserve` or `preserve: true`. This stages all uncommitted and untracked files and creates a WIP commit on the retained branch. A failed commit restores the pre-preservation index and leaves the checkout and its uncommitted files in place; removal does not proceed. Success reports the preservation SHA even if the subsequent recheck or removal fails. Conflicts, detached HEAD, and other blockers cannot be bypassed with preservation.
|
|
229
|
+
|
|
230
|
+
Ignored files do not block cleanup. Inventory shows their exact file count, and a successful removal reports how many were deleted. Counting streams the NUL-delimited Git listing rather than buffering all paths; errors and the 30-second timeout still block removal, never report a guessed zero. Preservation does not capture ignored files; attempted and successful preservation reports disclose that exclusion when ignored files are present.
|
|
231
|
+
|
|
232
|
+
Open workspaces are removed through Herdr. Git-only orphans use `git worktree remove`, verify checkout absence, and then prune stale Git registrations. Failed removal is reported, never forced. A reachable owned manifest is merge-updated to `removed` with `workspaceRemovedAt` only after success; absent cross-session manifests are reported and are not rewritten. A manifest update failure after removal is a removed-with-warning result, not a failed removal. Removed manifests provide already-removed no-op evidence only; a new checkout at the same path is classified independently. Missing and dangling manifest paths are non-matches. Permission errors, symlink loops, and other undecidable manifest identities remain blockers, as do genuine identity disagreements. Cleanup Git and Herdr calls have 30-second timeouts; a timed-out operation fails closed rather than proceeding with removal.
|
|
223
233
|
|
|
224
|
-
|
|
234
|
+
Branches and their commits are always retained: cleanup never deletes, force-updates, or rewrites a branch. It never runs on completion, shutdown, or a timer. Parent session start emits only one compact inventory count when contained worktrees remain. Unsupported platforms or failed global process enumeration block removal; individual visibility gaps are disclosed warnings, never proof that an orphan is idle.
|
|
225
235
|
|
|
226
236
|
## Current limits
|
|
227
237
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-herdr-agents",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Asynchronous Pi subagents in Herdr, with optional isolated Git worktrees",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"format": "biome format --write pi-extension test skills/orchestrate/adversarial-review-example.js",
|
|
34
34
|
"format:check": "biome format pi-extension test skills/orchestrate/adversarial-review-example.js",
|
|
35
35
|
"lint": "oxlint pi-extension test skills/orchestrate/adversarial-review-example.js",
|
|
36
|
-
"test": "node --experimental-strip-types --test test/test.ts test/launch.test.ts test/runtime-routing.test.ts test/release-workflow.test.ts test/package-skill.test.js test/evals/*.test.mjs",
|
|
36
|
+
"test": "node --experimental-strip-types --test test/test.ts test/worktree-cleanup.test.ts test/launch.test.ts test/runtime-routing.test.ts test/release-workflow.test.ts test/package-skill.test.js test/evals/*.test.mjs",
|
|
37
37
|
"test:integration": "node --experimental-strip-types --test --test-concurrency=1 test/integration/*.test.ts",
|
|
38
38
|
"test:integration:live": "PI_TEST_LIVE=1 node --experimental-strip-types --test --test-concurrency=1 test/integration/*.test.ts",
|
|
39
39
|
"version": "auto-changelog --package && git add CHANGELOG.md"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
7
|
+
|
|
8
|
+
export function getAgentConfigDir(): string {
|
|
9
|
+
return process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getSubagentsConfigPath(): string {
|
|
13
|
+
return join(getAgentConfigDir(), "herdr-agents", "config.json");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getSubagentsConfigExamplePath(): string {
|
|
17
|
+
return join(PACKAGE_ROOT, "config.json.example");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getSubagentsPackageRoot(): string {
|
|
21
|
+
return PACKAGE_ROOT;
|
|
22
|
+
}
|