Implements the §P2 acceptance gate from plan-migration.md: a single
POST /api/devloop/projects/:pid/run?only=todo-be drives the L1 inner
loop (Coder → Tester → fail → Coder → Tester → pass), with the second
Coder pass demonstrably reflecting on the first failure.
Mock protocol — stateful sequences:
- packages/acp-core/protocol/ClaudeCliProtocol.js
* Mock fixtures now support `sequence: [{chunks,result,files}, ...]`.
Each protocol.connect() + sendPrompt() consumes the next variant;
sticks on the last entry after exhausted.
* Static `ClaudeCliProtocol.resetMockState()` clears counters between
test runs.
Fixtures:
- fixtures/devloop-mock/devloop-architect.json — units.json now has
status:pending + retries:0 + retry_budget per unit, plus
units/{todo-be,todo-db,todo-fe}/spec.json so Tester has acceptance
criteria to read.
- fixtures/devloop-mock/devloop-coder.json — sequence: implement (with
intentional missing 400 branch) → retry (reflective fix, coder.log
attempt:2 has last_failure/last_cause/this_fix/rationale).
- fixtures/devloop-mock/devloop-tester.json — sequence: fail (1/3
failure with hint pointing at missing body validation) → pass (3/3).
LoopController:
- server/src/core/devloop/loop-controller.js — adds:
* runUnit(pid, unitId, opts) — drives the L1 loop via unit-registry
FSM (pending → coding → testing → failed → coding → testing →
passed/blocked). Calls Coder then Tester per attempt; on failure,
builds a retry context summarizing prior coder.log + tester
failures and feeds it into the next Coder prompt.
* _buildRetryContext + _findLatestTesterResult helpers.
* callAgent now rewrites units.json.project_id to the real pid after
mock writes (so unit-registry validation passes).
Route:
- server/routes/devloop.js — adds POST /projects/:pid/run?only=<unitId>.
Without ?only, returns 501 (full project run lands in P3).
Acceptance:
- scripts/phase2-verify.js — 6 steps:
1. POST /projects (PM + Architect)
2. POST /run?only=todo-be returns ok=true, attempts=2, final=passed
3. FSM trail matches expected 6-step transition sequence
4. coder.log second entry has all 4 reflection fields
5. runs/ has ≥2 result.json with first.ok=false, last.ok=true
6. sandbox cross-unit + prd writes blocked by ToolSandboxError
Tests (5 new cases):
- server/tests/devloop/run-unit.test.js (4) — FSM trail, coder.log
reflection, runs/ result chain, sandbox enforcement.
- server/tests/devloop/routes.test.js (+3) — POST /run validation,
full P2 loop, 404 on unknown unit.
Results:
- phase0-verify: 8/8
- phase0-test-call: 7/7
- phase1-verify: 7/7
- phase2-verify: 6/6
- vitest devloop+acp-core: 181/181 (was 174 → +7)
Specs:
- docs/prd/newfunction/_spec/plan-migration.md · P2 验收
- docs/prd/newfunction/_spec/loops-l1.md (retryWithReflection)
- docs/prd/newfunction/_spec/agents-coder.md
- docs/prd/newfunction/_spec/agents-tester.md
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>