dev-loops 0.2.2 → 0.2.4
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/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/skills/copilot-pr-followup/SKILL.md +1 -0
- package/.claude/skills/dev-loop/SKILL.md +11 -16
- package/.claude/skills/docs/main-agent-contract.md +9 -81
- package/CHANGELOG.md +31 -0
- package/README.md +1 -1
- package/extension/README.md +3 -1
- package/package.json +2 -2
- package/scripts/claude/generate-claude-assets.mjs +8 -3
- package/scripts/loop/copilot-pr-handoff.mjs +23 -5
- package/skills/copilot-pr-followup/SKILL.md +1 -0
- package/skills/dev-loop/SKILL.md +14 -10
- package/skills/docs/main-agent-contract.md +14 -1
|
@@ -225,6 +225,7 @@ When unresolved feedback exists, use a narrow follow-up loop:
|
|
|
225
225
|
- if the refreshed snapshot reports unresolved threads, re-enter the reply/resolve loop for the missed threads
|
|
226
226
|
12. only after GitHub-side reply/resolve work is done for the addressed threads and the refreshed thread snapshot proves zero unresolved threads remain, decide whether another Copilot pass is desired
|
|
227
227
|
- resolve the review-round cap from config via `resolveRefinementConfig(config, "maxCopilotRounds")` from `@dev-loops/core/config`; default config ships `maxCopilotRounds: 5`
|
|
228
|
+
- **Opt out entirely:** `maxCopilotRounds: 0` disables the external Copilot review gate for the repo — the loop runs `draft_gate → pre_approval_gate` with the local harness only, never requesting or waiting on Copilot. Use this when the repo has no Copilot reviewer configured or prefers local-harness-only review.
|
|
228
229
|
- use the completed Copilot review-round count from `detect-copilot-loop-state.mjs` / `copilot-pr-handoff.mjs` as the current PR's review-round count
|
|
229
230
|
- if completed review rounds have reached the maximum (default: 5), do **not** re-request Copilot review
|
|
230
231
|
- when the round limit is reached **and** the refreshed thread snapshot proves zero unresolved threads **and** current-head CI is green or credibly green, treat that clean state as eligible for `pre_approval_gate` fallback instead of deadlocking on another Copilot rerequest
|
|
@@ -21,29 +21,25 @@ Required installed runtime contract docs are shared bundled copies under `../doc
|
|
|
21
21
|
|
|
22
22
|
## Startup procedure
|
|
23
23
|
|
|
24
|
-
###
|
|
24
|
+
### Resolve authoritative state
|
|
25
25
|
|
|
26
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
107
|
+
## Guard rules
|
|
113
108
|
|
|
114
|
-
**Handoff envelope precedence:** The
|
|
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) —
|
|
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
|
-
|
|
4
|
-
> All mutations flow through the `dev-loop` async subagent.
|
|
3
|
+
How dev-loop work is structured depends on the harness.
|
|
5
4
|
|
|
6
|
-
|
|
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,37 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.2.4
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Claude Code: the dev-loop runs as a single agent** (#837). The Pi "umbrella" execution model —
|
|
10
|
+
a strictly read-only main agent that must dispatch an async `dev-loop` subagent, with all
|
|
11
|
+
mutations and state-changing CLI (`gate`/`pr`/`loop`) confined to that subagent — is now scoped
|
|
12
|
+
to Pi only. Under the Claude harness the dev-loop agent performs the steps directly: it reads and
|
|
13
|
+
writes repo files, runs git/PR operations, runs the `dev-loops` CLI, and **posts gate verdicts
|
|
14
|
+
under the operating session's identity** (fixing clean gates that previously stalled, unable to
|
|
15
|
+
record their verdict without separate "coordinator authority"). The `gh pr ready` draft-gate
|
|
16
|
+
guard still applies, and the read-only boundary remains available opt-in via
|
|
17
|
+
`DEVLOOPS_MAIN_AGENT_READONLY=1`. Implemented by scoping the Pi read-only/dispatch contract in
|
|
18
|
+
`main-agent-contract.md` and the dev-loop skill's startup procedure behind `<!-- pi-only -->`
|
|
19
|
+
markers; the asset generator now applies that stripping to bundled contract docs too, so the
|
|
20
|
+
Claude plugin ships the single-agent model while Pi keeps the full contract. Pi behavior is
|
|
21
|
+
unchanged. (Follow-up #838 tracks the copilot-pr-followup/conductor async-execution model.)
|
|
22
|
+
|
|
23
|
+
## 0.2.3
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **Opt out of the Copilot review gate via `refinement.maxCopilotRounds: 0`** (#832). For repos
|
|
28
|
+
without a Copilot reviewer configured (or that prefer local-harness-only review), setting
|
|
29
|
+
`maxCopilotRounds: 0` disables the external Copilot review cycle entirely — the loop runs
|
|
30
|
+
`draft_gate → pre_approval_gate` with no Copilot request or wait. The config schema now accepts
|
|
31
|
+
`0` (`nonnegative`; negative still rejected); `evaluatePrGateCoordination` routes `0` through the
|
|
32
|
+
existing `internal_only` path, `shouldGuardCopilotReviewRequest` never forces a request at `0`,
|
|
33
|
+
and the watch-cycle handoff (`copilot-pr-handoff`) skips the request too. Default (`5`) unchanged.
|
|
34
|
+
Documented in the README, extension config docs, and the `copilot-pr-followup` skill.
|
|
35
|
+
|
|
5
36
|
## 0.2.2
|
|
6
37
|
|
|
7
38
|
### Fixed
|
package/README.md
CHANGED
|
@@ -193,7 +193,7 @@ npx dev-loops gates # see what reviewers will check
|
|
|
193
193
|
Key surfaces:
|
|
194
194
|
- **Gate angles** — which review lenses run at draft and pre-approval gates
|
|
195
195
|
- **Persona prompts** — focused instructions per angle (DRY, KISS, YAGNI, SRP, SoC, and more)
|
|
196
|
-
- **Refinement** — fan-out count and mode for parallel review variants
|
|
196
|
+
- **Refinement** — fan-out count and mode for parallel review variants; `refinement.maxCopilotRounds` caps Copilot re-review rounds (default `5`), and **`maxCopilotRounds: 0` disables the Copilot review gate entirely** for local-harness-only review (`draft_gate → pre_approval_gate`, no Copilot) — useful when the repo has no Copilot reviewer configured
|
|
197
197
|
- **Autonomy** — which gates require operator confirmation
|
|
198
198
|
- **Workflow defaults** — retrospective enforcement, draft-first posture, dev-mode policy
|
|
199
199
|
|
package/extension/README.md
CHANGED
|
@@ -133,7 +133,9 @@ personas:
|
|
|
133
133
|
|
|
134
134
|
# Override gate requirements
|
|
135
135
|
refinement:
|
|
136
|
-
fanOut: 5
|
|
136
|
+
fanOut: 5 # run 5 parallel review variants instead of 3
|
|
137
|
+
maxCopilotRounds: 0 # 0 disables the Copilot review gate entirely (local-harness-only
|
|
138
|
+
# review: draft_gate → pre_approval_gate, no Copilot). Default: 5.
|
|
137
139
|
|
|
138
140
|
autonomy:
|
|
139
141
|
stopAt:
|
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.
|
|
71
|
+
"@dev-loops/core": "^0.2.4"
|
|
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.
|
|
81
|
+
"version": "0.2.4",
|
|
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
|
-
/**
|
|
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
|
}
|
|
@@ -366,12 +366,31 @@ export async function runHandoff(options, { env = process.env, ghCommand = "gh"
|
|
|
366
366
|
// Detect internal tooling PRs — suppress Copilot review request step entirely.
|
|
367
367
|
// Internal-only PRs (scripts/docs/tests/config) skip the request, not just the wait.
|
|
368
368
|
let internalOnlySkipCopilot = false;
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
369
|
+
// Config opt-out (#832): maxCopilotRounds: 0 disables the external Copilot review
|
|
370
|
+
// gate for the repo (local-harness-only review). Treat it like an internal-only PR
|
|
371
|
+
// — skip the request, not just the wait — regardless of stub/sequence mode. This
|
|
372
|
+
// mirrors the gate-coordination side, which routes maxCopilotRounds: 0 to internal_only.
|
|
373
|
+
if (refinementConfig?.maxCopilotRounds === 0 &&
|
|
374
|
+
options.watchStatus === undefined &&
|
|
373
375
|
(interpretation.state === STATE.PR_READY_NO_FEEDBACK ||
|
|
374
376
|
interpretation.state === STATE.READY_TO_REREQUEST_REVIEW)) {
|
|
377
|
+
internalOnlySkipCopilot = true;
|
|
378
|
+
interpretation = {
|
|
379
|
+
...interpretation,
|
|
380
|
+
state: STATE.INTERNAL_TOOLING_DIRECT_GATE,
|
|
381
|
+
nextAction: NEXT_ACTIONS[STATE.INTERNAL_TOOLING_DIRECT_GATE],
|
|
382
|
+
allowedTransitions: TRANSITIONS[STATE.INTERNAL_TOOLING_DIRECT_GATE] || [STATE.DONE],
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
// Skip internal detection in sequential stub/test mode to avoid consuming stub entries.
|
|
386
|
+
// Claims-mode stubs handle interleaved calls; detection runs normally.
|
|
387
|
+
if (
|
|
388
|
+
!internalOnlySkipCopilot &&
|
|
389
|
+
(!env.GH_SEQUENCE_PATH || env.GH_STUB_MODE === "claims") &&
|
|
390
|
+
options.watchStatus === undefined &&
|
|
391
|
+
(interpretation.state === STATE.PR_READY_NO_FEEDBACK ||
|
|
392
|
+
interpretation.state === STATE.READY_TO_REREQUEST_REVIEW)
|
|
393
|
+
) {
|
|
375
394
|
try {
|
|
376
395
|
const internalCheck = await detectPrInternalOnly(options, { env, ghCommand });
|
|
377
396
|
if (internalCheck.ok && internalCheck.internalOnly) {
|
|
@@ -387,7 +406,6 @@ export async function runHandoff(options, { env = process.env, ghCommand = "gh"
|
|
|
387
406
|
// Best-effort: if detection fails, fall through to normal request behavior
|
|
388
407
|
}
|
|
389
408
|
}
|
|
390
|
-
}
|
|
391
409
|
|
|
392
410
|
let reviewRequestStatus;
|
|
393
411
|
const shouldRequestReview = !internalOnlySkipCopilot && options.watchStatus === undefined
|
|
@@ -229,6 +229,7 @@ When unresolved feedback exists, use a narrow follow-up loop:
|
|
|
229
229
|
- if the refreshed snapshot reports unresolved threads, re-enter the reply/resolve loop for the missed threads
|
|
230
230
|
12. only after GitHub-side reply/resolve work is done for the addressed threads and the refreshed thread snapshot proves zero unresolved threads remain, decide whether another Copilot pass is desired
|
|
231
231
|
- resolve the review-round cap from config via `resolveRefinementConfig(config, "maxCopilotRounds")` from `@dev-loops/core/config`; default config ships `maxCopilotRounds: 5`
|
|
232
|
+
- **Opt out entirely:** `maxCopilotRounds: 0` disables the external Copilot review gate for the repo — the loop runs `draft_gate → pre_approval_gate` with the local harness only, never requesting or waiting on Copilot. Use this when the repo has no Copilot reviewer configured or prefers local-harness-only review.
|
|
232
233
|
- use the completed Copilot review-round count from `detect-copilot-loop-state.mjs` / `copilot-pr-handoff.mjs` as the current PR's review-round count
|
|
233
234
|
- if completed review rounds have reached the maximum (default: 5), do **not** re-request Copilot review
|
|
234
235
|
- when the round limit is reached **and** the refreshed thread snapshot proves zero unresolved threads **and** current-head CI is green or credibly green, treat that clean state as eligible for `pre_approval_gate` fallback instead of deadlocking on another Copilot rerequest
|
package/skills/dev-loop/SKILL.md
CHANGED
|
@@ -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
|
-
###
|
|
33
|
+
### Resolve authoritative state
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
> 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).
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
117
|
+
## Guard rules
|
|
114
118
|
|
|
115
|
-
**Handoff envelope precedence:** The
|
|
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) —
|
|
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
|
-
|
|
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 -->
|