dev-loops 0.2.3 → 0.2.5

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-loops",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Agent-harness-agnostic dev-loop: agents, skills, and hooks for GitHub/Copilot-driven development loops.",
5
5
  "author": {
6
6
  "name": "Manuel Fittko",
@@ -53,7 +53,11 @@ Use this helper output as source of truth for the normal routing seam. Interpret
53
53
  ```sh
54
54
  node <resolved-skill-scripts>/loop/run-watch-cycle.mjs --repo <owner/name> --pr <number>
55
55
  ```
56
- Persistent async watch/fix loop, not handoff-only behavior: `watch → detect → if threads found, fix + reply + resolve → re-request → watch again → … → pre_approval_gate → merge`. **PERSISTENCE MODEL: Subagents do bounded implementation tasks and exit on external wait. The main session drives the loop and re-dispatches when continuation is feasible.** A single returned watch cycle is never completion by itself. If `cycleDisposition` is `pending` and `terminal` is `false`, the subagent exits on the wait boundary; the main session re-dispatches another watch boundary. Max watch timeout: **30 minutes** (from `policy-constants.mjs` COPILOT_REVIEW_WAIT_TIMEOUT_MS); expired budget + still `waiting_for_copilot_review` = hard stop. If the user explicitly asks for async handoff-only behavior, say that out loud and stop after the handoff boundary.
56
+ Persistent async watch/fix loop, not handoff-only behavior: `watch → detect → if threads found, fix + reply + resolve → re-request → watch again → … → pre_approval_gate → merge`. A single returned watch cycle is never completion by itself.
57
+
58
+ > Under the Claude Code harness, run this loop **inline in a single agent**: the helper-owned wait tools (`dev-loops loop watch-cycle`, `gh run watch`, `dev-loops gate probe-copilot`) block inline and return — when `cycleDisposition` is `pending` and `terminal` is `false`, run the next watch cycle yourself. Do not exit on the wait boundary to have a parent re-dispatch the loop; keep driving it in this agent until a terminal state or the watch budget expires. (Delegating a bounded fix to the `fixer` agent, per Step 6, is still fine — that is task delegation, not re-dispatching the watch loop.)
59
+
60
+ Max watch timeout: **30 minutes** (from `policy-constants.mjs` COPILOT_REVIEW_WAIT_TIMEOUT_MS); expired budget + still `waiting_for_copilot_review` = hard stop. If the user explicitly asks for async handoff-only behavior, say that out loud and stop after the handoff boundary.
57
61
 
58
62
  **4. Low-level helpers**
59
63
  ```sh
@@ -133,8 +137,8 @@ already has an outer-loop checkpoint, check whether the checkpoint implies an au
133
137
  (including `timestamp` and potentially incrementing `waitCycles`).
134
138
  Read the on-disk artifact without mutating it.
135
139
  2. If `outerAction` is `continue_wait`:
136
- - The loop was waiting. The subagent exits; the main session re-dispatches a fresh
137
- `dev-loop` async subagent that resumes from the checkpoint.
140
+ - The loop was waiting; resume it from the checkpoint.
141
+ Under the Claude Code harness, continue the wait inline (run the next watch cycle yourself).
138
142
  3. If `outerAction` is `reenter_copilot_loop`:
139
143
  - The copilot inner loop needs action. Run `copilot-pr-handoff.mjs` to determine the
140
144
  exact next step and proceed.
@@ -145,8 +149,8 @@ already has an outer-loop checkpoint, check whether the checkpoint implies an au
145
149
  6. If no checkpoint exists or `outerAction` is `done`:
146
150
  - Continue with normal step sequencing.
147
151
 
148
- Do not skip this guard when transitioning between async subagent runs on the same PR.
149
- The outer-loop checkpoint is the canonical re-attachment artifact for the subagent.
152
+ Do not skip this guard when resuming work on the same PR.
153
+ The outer-loop checkpoint is the canonical re-attachment artifact.
150
154
 
151
155
  ## Step 6: Async watch behavior
152
156
 
@@ -162,8 +166,7 @@ Preferred approach:
162
166
  - `timeout`/`idle` → re-run `copilot-pr-handoff.mjs --watch-status <status>` once to refresh state; if still `waiting_for_copilot_review` after 30-minute watch budget exhausted, hard stop with `watch timeout — PR #<number> needs manual attention`
163
167
  - zero-timeout `idle` probes are for explicit one-shot status/reattach checks only; they are not the normal async wait mechanism
164
168
  - after a successful fix / reply-resolve / re-request cycle, returning to `waiting_for_copilot_review` is a persistence boundary: resume the watcher instead of reporting completion
165
- - if a child async run exits and the refreshed state remains non-terminal (for example `waiting_for_copilot_review`) before merge and without a hard stop, treat that as early exit and the main session re-dispatches the same-PR follow-up path when feasible (the subagent exits on external wait)
166
- - dispatch fix findings to `fixer` subagent; do not run inline fix passes in-watcher
169
+ - dispatch fix findings to the `fixer` agent; do not run inline fix passes in-watcher
167
170
  - do not report completion while unresolved Copilot feedback remains
168
171
 
169
172
  ### Canonical async dispatch wording
@@ -177,8 +180,8 @@ Key rules:
177
180
  - agent-authored shell polling is forbidden: do not use `nohup`, detached shell jobs, `tmux`, `screen`, or ad hoc `for i in $(seq ...)`, `while true`, `until ...; do sleep ...; done`, or `sleep`-retry bash loops
178
181
  - do not wrap repeated `gh pr view`, `gh pr checks`, `gh api`, or `detect-copilot-loop-state.mjs` calls inside shell polling loops
179
182
  - do not bypass session-based async notifications with detached shell automation
180
- - if Pi async subagents or the designated async follow-up skill are not appropriate or available, stop and report rather than improvising a shell watcher
181
- - the async-start contract is enforced in code: `outer-loop.mjs` fails closed without a visible Pi-managed async run id when `workflow.asyncStartMode: required`
183
+ - if the designated async follow-up skill is not appropriate or available, stop and report rather than improvising a shell watcher
184
+ - the async-start contract is enforced in code: `outer-loop.mjs` fails closed without a visible async run id when `workflow.asyncStartMode: required` (relaxed automatically under the Claude Code harness — see #830)
182
185
 
183
186
  ### Async delegation guard rules (#524)
184
187
 
@@ -21,29 +21,25 @@ Required installed runtime contract docs are shared bundled copies under `../doc
21
21
 
22
22
  ## Startup procedure
23
23
 
24
- ### Main agent (read-only)
24
+ ### Resolve authoritative state
25
25
 
26
- The main agent must **always** dispatch the `dev-loop` async subagent for any dev-loop work.
27
- Do not run `dev-loops loop startup` or any startup resolver in the main agent.
28
- For async-required routes (config `workflow.asyncStartMode`, default `required`) the resolver needs an async run-id marker (`DEVLOOPS_RUN_ID`, or the `PI_SUBAGENT_RUN_ID` alias) that the Pi harness injects when it dispatches the async subagent; under the Claude Code harness the requirement is relaxed automatically (no marker needed). The startup resolver also runs without a marker for non-async routes. Regardless, only the `dev-loop` subagent runs it — never the main agent.
26
+ > Under the Claude Code harness the dev-loop runs as a single agent: run these steps directly — no read-only boundary and no separate async-subagent dispatch. See [Main Agent Contract](../docs/main-agent-contract.md).
29
27
 
30
- ### Dev-loop subagent (post-dispatch)
31
-
32
- The subagent resolves authoritative state via the startup resolver (`npx dev-loops loop startup --issue <n>` for issues, `npx dev-loops loop startup --pr <n>` for PRs), then immediately builds the handoff envelope via `npx dev-loops loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
28
+ Resolve authoritative state via the startup resolver (`npx dev-loops loop startup --issue <n>` for issues, `npx dev-loops loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
33
29
 
34
30
  **Retrospective checkpoint gate:** the resolver reads `.pi/dev-loop-retrospective-checkpoint.json` and injects the state. When the checkpoint is `missing` and the repo config `workflow.requireRetrospective` (set via `.devloops` at repo root) is `true`, the resolver returns `needs_reconcile`. Complete or explicitly skip the retrospective before starting.
35
31
 
36
- **Pre-delegation gate (mandatory — subagent only):** Before delegating async work targeting an existing PR, the dev-loop subagent must run `node scripts/loop/copilot-pr-handoff.mjs --repo <owner/name> --pr <number>` and abort if `action: "stop"`. When `terminal: true`, proceed inline. When `terminal: false`, resolve the blocking condition first.
32
+ **Pre-flight PR gate (mandatory):** Before working an existing PR, the dev-loop must run `node scripts/loop/copilot-pr-handoff.mjs --repo <owner/name> --pr <number>` and abort if `action: "stop"`. When `terminal: true`, proceed inline. When `terminal: false`, resolve the blocking condition first.
37
33
 
38
- **Worktree cwd (mandatory — subagent only):** Always use a worktree checkout for git operations, file reads/writes, and validation commands — never use the `main` checkout.
34
+ **Worktree cwd (mandatory):** Always use a worktree checkout for git operations, file reads/writes, and validation commands — never use the `main` checkout.
39
35
 
40
- **Worktree fetch (mandatory — subagent only):** Always run `git fetch origin` before creating or reusing any worktree.
36
+ **Worktree fetch (mandatory):** Always run `git fetch origin` before creating or reusing any worktree.
41
37
 
42
38
  ### Resume from existing loop state
43
39
 
44
40
  When the startup resolver returns a fresh-start routing but an existing outer-loop checkpoint
45
41
  (`tmp/copilot-loop/<owner>/<repo>/pr-<n>/outer-loop-state.json`) is present on disk, the
46
- subagent must check the checkpoint before treating the start as a fresh intake or follow-up:
42
+ dev-loop must check the checkpoint before treating the start as a fresh intake or follow-up:
47
43
 
48
44
  1. Read the outer-loop checkpoint (authored by `outer-loop.mjs`).
49
45
  2. If `outerAction` is `continue_wait`, `reenter_copilot_loop`, or `reenter_reviewer_loop`:
@@ -87,7 +83,6 @@ Do not preload route packs before the resolver selects the strategy.
87
83
 
88
84
  **Async dispatch rule (enforced):** the resolver fails closed for GitHub-first strategies when `canonicalStateSummary.requiresAsyncDispatch` is `true` (default `required` mode) — inline invocation without an async run-id marker (`DEVLOOPS_RUN_ID`, or the `PI_SUBAGENT_RUN_ID` alias) is rejected for those routes. Under the Claude Code harness this requirement is relaxed automatically. See [Startup procedure](#startup-procedure).
89
85
 
90
-
91
86
  ## Fallback gate-comment poster
92
87
 
93
88
  When the `@dev-loops/core` package is not installed in the consumer repo, the full `scripts/github/upsert-checkpoint-verdict.mjs` helper (referenced from the copilot-pr-followup skill procedure) is unavailable. To keep the PR audit trail intact in that mode, the dev-loop skill ships a small gh-only fallback poster at `scripts/post-gate-verdict-fallback.mjs` (relative to the dev-loop skill root) that renders the same visible comment format and fails closed if posting cannot succeed.
@@ -104,14 +99,14 @@ When `@dev-loops/core` is available again, switch back to the full helper. The f
104
99
 
105
100
  ## Read-only info shortcut
106
101
 
107
- The main agent may handle info/handoff requests directly via `npx dev-loops loop info` without dispatching the async `dev-loop` subagent:
102
+ Info/handoff requests can be served directly via `npx dev-loops loop info` (read-only; no full dev-loop run required):
108
103
  - `npx dev-loops loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
109
104
  - `npx dev-loops loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
110
105
  - `npx dev-loops loop info --issue <n> --json` — machine-readable JSON output
111
106
 
112
- ## Guard rules (subagent reference)
107
+ ## Guard rules
113
108
 
114
- **Handoff envelope precedence:** The subagent builds the envelope immediately after authoritative-state resolution and treats it as the first handoff artifact. Read it first, load only `requiredReads`, execute `nextAction`. See [Dev-loop subagent](#dev-loop-subagent-post-dispatch). Derivation contract: [Workflow Handoff Contract](../docs/workflow-handoff-contract.md).
109
+ **Handoff envelope precedence:** The dev-loop builds the envelope immediately after authoritative-state resolution and treats it as the first handoff artifact. Read it first, load only `requiredReads`, execute `nextAction`. See [Resolve authoritative state](#resolve-authoritative-state). Derivation contract: [Workflow Handoff Contract](../docs/workflow-handoff-contract.md).
115
110
 
116
111
  **Handoff contract rule:** When no envelope is present, use the `workflow-handoff-contract.md` contract. Never delegate with abbreviated task summaries. Include deterministic routing inputs, explicit `cwd`, bounded task scope, exit conditions.
117
112
 
@@ -134,7 +129,7 @@ All PRs must pass the full gate pipeline before merge. No scope is exempt: docs-
134
129
  ## Authority boundary
135
130
 
136
131
  - Source code, tests, config, CI, and shared contract docs are authoritative.
137
- - Main-agent delegation contract: [Main Agent Contract](../docs/main-agent-contract.md) — absolute read-only boundary; all mutations flow through `dev-loop` async subagent.
132
+ - Main-agent delegation contract: [Main Agent Contract](../docs/main-agent-contract.md) — how dev-loop work is structured per harness (Pi: read-only main agent + async-subagent dispatch; Claude: a single agent runs the steps directly).
138
133
  - Before any state-changing action, get explicit confirmation unless already authorized.
139
134
  - A question requires an answer, not an action.
140
135
  - Stop and ask rather than guessing when facts don't agree.
@@ -1,85 +1,13 @@
1
1
  # Main-agent delegation contract
2
2
 
3
- > **Absolute read-only boundary.** The main agent must never mutate files tracked by the repository.
4
- > All mutations flow through the `dev-loop` async subagent.
3
+ How dev-loop work is structured depends on the harness.
5
4
 
6
- ## Contract
5
+ **Under the Claude Code harness, the dev-loop runs as a single agent.** The agent invoked for
6
+ dev-loop work performs the steps directly — it reads and writes repository files, runs git and PR
7
+ lifecycle operations, runs the `dev-loops` CLI (including state-changing `gate` / `pr` / `loop`
8
+ subcommands), and posts gate verdicts under the operating session's identity. There is no separate
9
+ read-only "main agent" and no mandatory async-subagent dispatch: the dev-loop agent owns the work
10
+ end to end. The draft-gate `gh pr ready` guard still applies (harness-agnostic). A read-only
11
+ boundary can be re-imposed optionally via the Write/Edit guard hook — opt-in with
12
+ `DEVLOOPS_MAIN_AGENT_READONLY=1` (default fail-open) — for repos that want it.
7
13
 
8
- The main agent is **read-only** for every file tracked by the repository. Every
9
- write, edit, delete, commit, branch, push, and PR lifecycle operation must flow
10
- through the `dev-loop` async subagent.
11
-
12
- This contract is a hard rule, not a default or guideline. The main agent must
13
- never rationalize a direct mutation — not because the work is small, not
14
- because "the user said yes," not because it is running from a worktree.
15
-
16
- ## Main agent owns (allowed)
17
-
18
- - Read, inspect, search any repo file
19
- - `git worktree list`, `git status`, `git log` (read-only git). `git fetch` is also allowed (updates local refs but does not touch tracked working-tree files).
20
- - `gh issue view / create / edit / comment / close` (GitHub API, not file mutations)
21
- - `gh pr view / list` (read-only GitHub API)
22
- - Write to `/tmp` or other non-repo paths (e.g., issue body drafts)
23
- - Delegate to the `dev-loop` agent (async, with worktree cwd)
24
- - Report findings, ask questions, get confirmation
25
- - `npm test`, `npm run verify` (read-only validation)
26
-
27
- ## Main agent must NEVER
28
-
29
- - `write`, `edit`, or delete any file tracked by the repo
30
- - `git commit`, `git push`, create branches, create worktrees
31
- - Run state-changing dev-loops CLI subcommands (`gate`, any state-changing `loop` subcommand, `pr` commands — those belong inside `dev-loop`).
32
- - Delegate implementation to any agent other than `dev-loop`
33
-
34
- ## Dev-loop agent (async) owns
35
-
36
- - ALL file mutations in the repo (write, edit, delete)
37
- - ALL git operations (branch, commit, push)
38
- - ALL PR lifecycle (create, draft, review, merge)
39
- - Sub-delegation to developer, fixer, review, quality agents
40
-
41
- ## Boundary examples
42
-
43
- | Operation | Verdict |
44
- |---|---|
45
- | `gh issue create --title "..." --body "..."` | Allowed — mutates GitHub, not files tracked by the repository |
46
- | Write to `/tmp/issue-body.md` | Allowed — outside the repo |
47
- | Write to `packages/core/src/foo.mjs` | **BREACH** — must delegate to `dev-loop` |
48
- | `git status` | Allowed — read-only |
49
- | `git commit -m "..."` | **BREACH** — must delegate to `dev-loop` |
50
- | `subagent dev-loop` | Allowed — correct delegation |
51
- | `subagent fixer` | Allowed only when called from within `dev-loop`; describe the task as part of the message |
52
-
53
- ## Dev-loop startup
54
-
55
- When a user triggers the dev loop, the main agent must immediately dispatch the
56
- `dev-loop` async subagent. The subagent owns the startup resolver, route selection,
57
- and all subsequent implementation steps. The main agent never runs `dev-loops loop startup`
58
- directly.
59
-
60
- ## Enforcement posture
61
-
62
- - Under **Pi**, this contract is enforced by convention and review.
63
- - Under **Claude Code**, the **Edit/Write tool** path is enforced **mechanically** by a
64
- `PreToolUse` Write/Edit hook (`.claude/hooks/pre-tool-use-write-guard.mjs`, wired via
65
- `.claude/settings.json` for this repo's own sessions and via `.claude/hooks/hooks.json` for the
66
- Claude plugin): a Write/Edit whose target is inside the repo working tree and not
67
- gitignored is **denied** when it originates from the main agent, and allowed only inside the
68
- `dev-loop` subagent context (detected via the neutral `DEVLOOPS_RUN_ID` run-id contract, or
69
- the dev-loop `agent_type` — a generic subagent is not authorized).
70
- Strict enforcement is opt-in via `DEVLOOPS_MAIN_AGENT_READONLY=1` (default fail-open) so
71
- adopting the harness does not retroactively break a repo's own interactive dev; full run-id
72
- propagation into the Claude subagent context completes with the headless/agent wiring.
73
- - **Scope of mechanical enforcement:** the hook covers the Edit and Write tools. Bash-driven
74
- repo mutations the contract also forbids (`git commit`/`git push`/branch creation, in-place
75
- edits like `sed -i`, shell redirection `> file` / `tee`) run through the Bash tool and remain
76
- **convention-enforced** for now; the only Bash command the gate hook blocks is the ungated
77
- `gh pr ready`. Tightening Bash-mutation coverage is possible follow-up.
78
- - A companion `PreToolUse` Bash hook reproduces the `gh pr ready` draft-gate guard.
79
- - A `dev-loop` async subagent should still reject delegation attempts that bypass the contract.
80
-
81
- ## Non-goals
82
-
83
- - Pre-commit hooks (out of scope; the boundary is enforced at the Claude tool layer).
84
- - Changing dev-loop resolver behavior
85
- - Modifying the subagent API itself
package/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.2.5
6
+
7
+ ### Changed
8
+
9
+ - **Claude Code: the Copilot PR follow-up loop runs inline** (#838, completing the umbrella
10
+ collapse from #837). The copilot-pr-followup skill's Pi "persistence model" — *subagents do
11
+ bounded work and exit on the wait boundary; the main session re-dispatches* — is now scoped to
12
+ Pi via `<!-- pi-only -->`. Under the Claude harness the single dev-loop agent runs the
13
+ `watch → fix/reply/resolve → re-request → watch` loop **inline**: the helper-owned wait tools
14
+ (`dev-loops loop watch-cycle`, `gh run watch`, `dev-loops gate probe-copilot`) block inline and return, so
15
+ the agent keeps looping until terminal or the watch budget expires — no exit-and-redispatch. The
16
+ outer-loop checkpoint, watch budget, the forbidden-shell-watcher rules, and the gate requirements
17
+ are unchanged and harness-agnostic. Pi behavior is unchanged.
18
+
19
+ ## 0.2.4
20
+
21
+ ### Changed
22
+
23
+ - **Claude Code: the dev-loop runs as a single agent** (#837). The Pi "umbrella" execution model —
24
+ a strictly read-only main agent that must dispatch an async `dev-loop` subagent, with all
25
+ mutations and state-changing CLI (`gate`/`pr`/`loop`) confined to that subagent — is now scoped
26
+ to Pi only. Under the Claude harness the dev-loop agent performs the steps directly: it reads and
27
+ writes repo files, runs git/PR operations, runs the `dev-loops` CLI, and **posts gate verdicts
28
+ under the operating session's identity** (fixing clean gates that previously stalled, unable to
29
+ record their verdict without separate "coordinator authority"). The `gh pr ready` draft-gate
30
+ guard still applies, and the read-only boundary remains available opt-in via
31
+ `DEVLOOPS_MAIN_AGENT_READONLY=1`. Implemented by scoping the Pi read-only/dispatch contract in
32
+ `main-agent-contract.md` and the dev-loop skill's startup procedure behind `<!-- pi-only -->`
33
+ markers; the asset generator now applies that stripping to bundled contract docs too, so the
34
+ Claude plugin ships the single-agent model while Pi keeps the full contract. Pi behavior is
35
+ unchanged. (Follow-up #838 tracks the copilot-pr-followup/conductor async-execution model.)
36
+
5
37
  ## 0.2.3
6
38
 
7
39
  ### Added
package/package.json CHANGED
@@ -68,7 +68,7 @@
68
68
  },
69
69
  "dependencies": {
70
70
  "mermaid": "11.15.0",
71
- "@dev-loops/core": "^0.2.3"
71
+ "@dev-loops/core": "^0.2.5"
72
72
  },
73
73
  "repository": {
74
74
  "type": "git",
@@ -78,7 +78,7 @@
78
78
  "url": "https://github.com/mfittko/dev-loops/issues"
79
79
  },
80
80
  "homepage": "https://github.com/mfittko/dev-loops#readme",
81
- "version": "0.2.3",
81
+ "version": "0.2.5",
82
82
  "files": [
83
83
  "cli/",
84
84
  "lib/",
@@ -14,7 +14,7 @@ import fs from "node:fs";
14
14
  import path from "node:path";
15
15
  import { fileURLToPath } from "node:url";
16
16
 
17
- import { transformAgent, transformSkill } from "@dev-loops/core/claude/asset-generation";
17
+ import { transformAgent, transformSkill, stripPiOnlyBlocks } from "@dev-loops/core/claude/asset-generation";
18
18
 
19
19
  /**
20
20
  * Collect the generated assets as { target, content } pairs (target is repo-relative).
@@ -63,7 +63,12 @@ export function collectGeneratedAssets({ repoRoot = process.cwd() } = {}) {
63
63
  return assets;
64
64
  }
65
65
 
66
- /** Recursively collect `*.md` files under a source dir as verbatim {target, content} bundle assets. */
66
+ /**
67
+ * Recursively collect `*.md` files under a source dir as {target, content} bundle assets.
68
+ * Bodies are passed through `stripPiOnlyBlocks` so bundled contract docs can scope Pi-runtime
69
+ * prose out of the Claude copies via `<!-- pi-only -->` markers (a no-op for marker-free docs,
70
+ * so the existing verbatim bundle is unchanged).
71
+ */
67
72
  function collectBundle(repoRoot, srcRel, targetRel) {
68
73
  const out = [];
69
74
  const absDir = path.join(repoRoot, srcRel);
@@ -74,7 +79,7 @@ function collectBundle(repoRoot, srcRel, targetRel) {
74
79
  } else if (entry.name.endsWith(".md")) {
75
80
  out.push({
76
81
  target: `${targetRel}/${entry.name}`,
77
- content: fs.readFileSync(path.join(absDir, entry.name), "utf8"),
82
+ content: stripPiOnlyBlocks(fs.readFileSync(path.join(absDir, entry.name), "utf8")),
78
83
  });
79
84
  }
80
85
  }
@@ -57,7 +57,15 @@ Use this helper output as source of truth for the normal routing seam. Interpret
57
57
  ```sh
58
58
  node <resolved-skill-scripts>/loop/run-watch-cycle.mjs --repo <owner/name> --pr <number>
59
59
  ```
60
- Persistent async watch/fix loop, not handoff-only behavior: `watch → detect → if threads found, fix + reply + resolve → re-request → watch again → … → pre_approval_gate → merge`. **PERSISTENCE MODEL: Subagents do bounded implementation tasks and exit on external wait. The main session drives the loop and re-dispatches when continuation is feasible.** A single returned watch cycle is never completion by itself. If `cycleDisposition` is `pending` and `terminal` is `false`, the subagent exits on the wait boundary; the main session re-dispatches another watch boundary. Max watch timeout: **30 minutes** (from `policy-constants.mjs` COPILOT_REVIEW_WAIT_TIMEOUT_MS); expired budget + still `waiting_for_copilot_review` = hard stop. If the user explicitly asks for async handoff-only behavior, say that out loud and stop after the handoff boundary.
60
+ Persistent async watch/fix loop, not handoff-only behavior: `watch → detect → if threads found, fix + reply + resolve → re-request → watch again → … → pre_approval_gate → merge`. A single returned watch cycle is never completion by itself.
61
+
62
+ <!-- pi-only -->
63
+ **PERSISTENCE MODEL: Subagents do bounded implementation tasks and exit on external wait. The main session drives the loop and re-dispatches when continuation is feasible.** If `cycleDisposition` is `pending` and `terminal` is `false`, the subagent exits on the wait boundary; the main session re-dispatches another watch boundary.
64
+ <!-- /pi-only -->
65
+
66
+ > Under the Claude Code harness, run this loop **inline in a single agent**: the helper-owned wait tools (`dev-loops loop watch-cycle`, `gh run watch`, `dev-loops gate probe-copilot`) block inline and return — when `cycleDisposition` is `pending` and `terminal` is `false`, run the next watch cycle yourself. Do not exit on the wait boundary to have a parent re-dispatch the loop; keep driving it in this agent until a terminal state or the watch budget expires. (Delegating a bounded fix to the `fixer` agent, per Step 6, is still fine — that is task delegation, not re-dispatching the watch loop.)
67
+
68
+ Max watch timeout: **30 minutes** (from `policy-constants.mjs` COPILOT_REVIEW_WAIT_TIMEOUT_MS); expired budget + still `waiting_for_copilot_review` = hard stop. If the user explicitly asks for async handoff-only behavior, say that out loud and stop after the handoff boundary.
61
69
 
62
70
  **4. Low-level helpers**
63
71
  ```sh
@@ -137,8 +145,10 @@ already has an outer-loop checkpoint, check whether the checkpoint implies an au
137
145
  (including `timestamp` and potentially incrementing `waitCycles`).
138
146
  Read the on-disk artifact without mutating it.
139
147
  2. If `outerAction` is `continue_wait`:
140
- - The loop was waiting. The subagent exits; the main session re-dispatches a fresh
141
- `dev-loop` async subagent that resumes from the checkpoint.
148
+ - The loop was waiting; resume it from the checkpoint.
149
+ <!-- pi-only -->Under Pi the subagent exits and the main session re-dispatches a fresh
150
+ `dev-loop` async subagent that resumes from the checkpoint.<!-- /pi-only -->
151
+ Under the Claude Code harness, continue the wait inline (run the next watch cycle yourself).
142
152
  3. If `outerAction` is `reenter_copilot_loop`:
143
153
  - The copilot inner loop needs action. Run `copilot-pr-handoff.mjs` to determine the
144
154
  exact next step and proceed.
@@ -149,8 +159,8 @@ already has an outer-loop checkpoint, check whether the checkpoint implies an au
149
159
  6. If no checkpoint exists or `outerAction` is `done`:
150
160
  - Continue with normal step sequencing.
151
161
 
152
- Do not skip this guard when transitioning between async subagent runs on the same PR.
153
- The outer-loop checkpoint is the canonical re-attachment artifact for the subagent.
162
+ Do not skip this guard when resuming work on the same PR<!-- pi-only --> (under Pi, between async subagent runs)<!-- /pi-only -->.
163
+ The outer-loop checkpoint is the canonical re-attachment artifact.
154
164
 
155
165
  ## Step 6: Async watch behavior
156
166
 
@@ -166,8 +176,10 @@ Preferred approach:
166
176
  - `timeout`/`idle` → re-run `copilot-pr-handoff.mjs --watch-status <status>` once to refresh state; if still `waiting_for_copilot_review` after 30-minute watch budget exhausted, hard stop with `watch timeout — PR #<number> needs manual attention`
167
177
  - zero-timeout `idle` probes are for explicit one-shot status/reattach checks only; they are not the normal async wait mechanism
168
178
  - after a successful fix / reply-resolve / re-request cycle, returning to `waiting_for_copilot_review` is a persistence boundary: resume the watcher instead of reporting completion
179
+ <!-- pi-only -->
169
180
  - if a child async run exits and the refreshed state remains non-terminal (for example `waiting_for_copilot_review`) before merge and without a hard stop, treat that as early exit and the main session re-dispatches the same-PR follow-up path when feasible (the subagent exits on external wait)
170
- - dispatch fix findings to `fixer` subagent; do not run inline fix passes in-watcher
181
+ <!-- /pi-only -->
182
+ - dispatch fix findings to the `fixer` agent; do not run inline fix passes in-watcher
171
183
  - do not report completion while unresolved Copilot feedback remains
172
184
 
173
185
  ### Canonical async dispatch wording
@@ -181,8 +193,8 @@ Key rules:
181
193
  - agent-authored shell polling is forbidden: do not use `nohup`, detached shell jobs, `tmux`, `screen`, or ad hoc `for i in $(seq ...)`, `while true`, `until ...; do sleep ...; done`, or `sleep`-retry bash loops
182
194
  - do not wrap repeated `gh pr view`, `gh pr checks`, `gh api`, or `detect-copilot-loop-state.mjs` calls inside shell polling loops
183
195
  - do not bypass session-based async notifications with detached shell automation
184
- - if Pi async subagents or the designated async follow-up skill are not appropriate or available, stop and report rather than improvising a shell watcher
185
- - the async-start contract is enforced in code: `outer-loop.mjs` fails closed without a visible Pi-managed async run id when `workflow.asyncStartMode: required`
196
+ - if the designated async follow-up skill is not appropriate or available, stop and report rather than improvising a shell watcher
197
+ - the async-start contract is enforced in code: `outer-loop.mjs` fails closed without a visible async run id when `workflow.asyncStartMode: required` (relaxed automatically under the Claude Code harness — see #830)
186
198
 
187
199
  ### Async delegation guard rules (#524)
188
200
 
@@ -22,29 +22,33 @@ Required installed runtime contract docs are shared bundled copies under `../doc
22
22
 
23
23
  ## Startup procedure
24
24
 
25
+ <!-- pi-only -->
25
26
  ### Main agent (read-only)
26
27
 
27
28
  The main agent must **always** dispatch the `dev-loop` async subagent for any dev-loop work.
28
29
  Do not run `dev-loops loop startup` or any startup resolver in the main agent.
29
30
  For async-required routes (config `workflow.asyncStartMode`, default `required`) the resolver needs an async run-id marker (`DEVLOOPS_RUN_ID`, or the `PI_SUBAGENT_RUN_ID` alias) that the Pi harness injects when it dispatches the async subagent; under the Claude Code harness the requirement is relaxed automatically (no marker needed). The startup resolver also runs without a marker for non-async routes. Regardless, only the `dev-loop` subagent runs it — never the main agent.
31
+ <!-- /pi-only -->
30
32
 
31
- ### Dev-loop subagent (post-dispatch)
33
+ ### Resolve authoritative state
32
34
 
33
- The subagent resolves authoritative state via the startup resolver (`npx dev-loops loop startup --issue <n>` for issues, `npx dev-loops loop startup --pr <n>` for PRs), then immediately builds the handoff envelope via `npx dev-loops loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
35
+ > Under the Claude Code harness the dev-loop runs as a single agent: run these steps directlyno read-only boundary and no separate async-subagent dispatch. See [Main Agent Contract](../docs/main-agent-contract.md).
36
+
37
+ Resolve authoritative state via the startup resolver (`npx dev-loops loop startup --issue <n>` for issues, `npx dev-loops loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
34
38
 
35
39
  **Retrospective checkpoint gate:** the resolver reads `.pi/dev-loop-retrospective-checkpoint.json` and injects the state. When the checkpoint is `missing` and the repo config `workflow.requireRetrospective` (set via `.devloops` at repo root) is `true`, the resolver returns `needs_reconcile`. Complete or explicitly skip the retrospective before starting.
36
40
 
37
- **Pre-delegation gate (mandatory — subagent only):** Before delegating async work targeting an existing PR, the dev-loop subagent must run `node scripts/loop/copilot-pr-handoff.mjs --repo <owner/name> --pr <number>` and abort if `action: "stop"`. When `terminal: true`, proceed inline. When `terminal: false`, resolve the blocking condition first.
41
+ **Pre-flight PR gate (mandatory):** Before working an existing PR, the dev-loop must run `node scripts/loop/copilot-pr-handoff.mjs --repo <owner/name> --pr <number>` and abort if `action: "stop"`. When `terminal: true`, proceed inline. When `terminal: false`, resolve the blocking condition first.
38
42
 
39
- **Worktree cwd (mandatory — subagent only):** Always use a worktree checkout for git operations, file reads/writes, and validation commands — never use the `main` checkout.
43
+ **Worktree cwd (mandatory):** Always use a worktree checkout for git operations, file reads/writes, and validation commands — never use the `main` checkout.
40
44
 
41
- **Worktree fetch (mandatory — subagent only):** Always run `git fetch origin` before creating or reusing any worktree.
45
+ **Worktree fetch (mandatory):** Always run `git fetch origin` before creating or reusing any worktree.
42
46
 
43
47
  ### Resume from existing loop state
44
48
 
45
49
  When the startup resolver returns a fresh-start routing but an existing outer-loop checkpoint
46
50
  (`tmp/copilot-loop/<owner>/<repo>/pr-<n>/outer-loop-state.json`) is present on disk, the
47
- subagent must check the checkpoint before treating the start as a fresh intake or follow-up:
51
+ dev-loop must check the checkpoint before treating the start as a fresh intake or follow-up:
48
52
 
49
53
  1. Read the outer-loop checkpoint (authored by `outer-loop.mjs`).
50
54
  2. If `outerAction` is `continue_wait`, `reenter_copilot_loop`, or `reenter_reviewer_loop`:
@@ -105,14 +109,14 @@ When `@dev-loops/core` is available again, switch back to the full helper. The f
105
109
 
106
110
  ## Read-only info shortcut
107
111
 
108
- The main agent may handle info/handoff requests directly via `npx dev-loops loop info` without dispatching the async `dev-loop` subagent:
112
+ Info/handoff requests can be served directly via `npx dev-loops loop info` (read-only; no full dev-loop run required):
109
113
  - `npx dev-loops loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
110
114
  - `npx dev-loops loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
111
115
  - `npx dev-loops loop info --issue <n> --json` — machine-readable JSON output
112
116
 
113
- ## Guard rules (subagent reference)
117
+ ## Guard rules
114
118
 
115
- **Handoff envelope precedence:** The subagent builds the envelope immediately after authoritative-state resolution and treats it as the first handoff artifact. Read it first, load only `requiredReads`, execute `nextAction`. See [Dev-loop subagent](#dev-loop-subagent-post-dispatch). Derivation contract: [Workflow Handoff Contract](../docs/workflow-handoff-contract.md).
119
+ **Handoff envelope precedence:** The dev-loop builds the envelope immediately after authoritative-state resolution and treats it as the first handoff artifact. Read it first, load only `requiredReads`, execute `nextAction`. See [Resolve authoritative state](#resolve-authoritative-state). Derivation contract: [Workflow Handoff Contract](../docs/workflow-handoff-contract.md).
116
120
 
117
121
  **Handoff contract rule:** When no envelope is present, use the `workflow-handoff-contract.md` contract. Never delegate with abbreviated task summaries. Include deterministic routing inputs, explicit `cwd`, bounded task scope, exit conditions.
118
122
 
@@ -135,7 +139,7 @@ All PRs must pass the full gate pipeline before merge. No scope is exempt: docs-
135
139
  ## Authority boundary
136
140
 
137
141
  - Source code, tests, config, CI, and shared contract docs are authoritative.
138
- - Main-agent delegation contract: [Main Agent Contract](../docs/main-agent-contract.md) — absolute read-only boundary; all mutations flow through `dev-loop` async subagent.
142
+ - Main-agent delegation contract: [Main Agent Contract](../docs/main-agent-contract.md) — how dev-loop work is structured per harness (Pi: read-only main agent + async-subagent dispatch; Claude: a single agent runs the steps directly).
139
143
  - Before any state-changing action, get explicit confirmation unless already authorized.
140
144
  - A question requires an answer, not an action.
141
145
  - Stop and ask rather than guessing when facts don't agree.
@@ -1,6 +1,18 @@
1
1
  # Main-agent delegation contract
2
2
 
3
- > **Absolute read-only boundary.** The main agent must never mutate files tracked by the repository.
3
+ How dev-loop work is structured depends on the harness.
4
+
5
+ **Under the Claude Code harness, the dev-loop runs as a single agent.** The agent invoked for
6
+ dev-loop work performs the steps directly — it reads and writes repository files, runs git and PR
7
+ lifecycle operations, runs the `dev-loops` CLI (including state-changing `gate` / `pr` / `loop`
8
+ subcommands), and posts gate verdicts under the operating session's identity. There is no separate
9
+ read-only "main agent" and no mandatory async-subagent dispatch: the dev-loop agent owns the work
10
+ end to end. The draft-gate `gh pr ready` guard still applies (harness-agnostic). A read-only
11
+ boundary can be re-imposed optionally via the Write/Edit guard hook — opt-in with
12
+ `DEVLOOPS_MAIN_AGENT_READONLY=1` (default fail-open) — for repos that want it.
13
+
14
+ <!-- pi-only -->
15
+ > **Absolute read-only boundary (Pi).** The main agent must never mutate files tracked by the repository.
4
16
  > All mutations flow through the `dev-loop` async subagent.
5
17
 
6
18
  ## Contract
@@ -83,3 +95,4 @@ directly.
83
95
  - Pre-commit hooks (out of scope; the boundary is enforced at the Claude tool layer).
84
96
  - Changing dev-loop resolver behavior
85
97
  - Modifying the subagent API itself
98
+ <!-- /pi-only -->