-
Implements the §P3 acceptance gate from plan-migration.md: a single POST /api/devloop/projects/:pid/run drives the full project from L1 through L2 with 4 distinct BugTriager branches, observability via SSE stream + bugs.ndjson + changelog.ndjson, and a tiny static status page. Mock protocol — fallback dir: - packages/acp-core/protocol/ClaudeCliProtocol.js * New options.mockFallbackDir: when scenario dir lacks a per-agent fixture, fall back to the baseline fixtures/devloop-mock/. Enables scenario dirs to override only the agents that differ (Integrator + Triager + sometimes Architect). Scenario fixtures — 4 L2 branches: - fixtures/devloop-mock-scenarios/contract_mismatch/ * Architect: sequence[init, patch] — patch bumps sdd_rev → 2, units.json l2_rounds → 1, marks todo-be back to pending. * Integrator: sequence[fail (500 vs 201), pass]. * Triager: contract_mismatch confidence 0.85 with proposed_sdd_patch. - fixtures/devloop-mock-scenarios/impl_bug/ * Integrator: sequence[fail (ReferenceError), pass]. * Triager: impl_bug confidence 0.78 affected=[todo-be], proposed=null. - fixtures/devloop-mock-scenarios/spec_ambiguous/ * Triager: confidence 0.4 → LoopController emits human_intervention. - fixtures/devloop-mock-scenarios/infra_flake/ * Integrator: sequence[crashed (EADDRINUSE), pass]. * Triager: infra_flake confidence 0.95 affected=[]. - Each scenario also includes a 1-unit (todo-be) Architect.init fixture so baseline Coder/Tester (todo-be) can drive L1. LoopController: - server/src/core/devloop/loop-controller.js — adds: * runProject(pid, opts) — wave-scheduled L1 via _groupUnitsByWave, runIntegrator (devloop-integrator), runTriager (devloop-triager), then branch dispatch: - contract_mismatch+conf≥0.5 → Architect(patch) writes new units.json (already pending, l2_rounds++); emits sdd.updated + changelog sdd_rev entry; loop back to L1. - impl_bug+conf≥0.5 → unitRegistry.reset(affected); loop, no budget burn. - spec_ambiguous OR conf<0.5 → emit human_intervention_needed SSE, exit l2_blocked. - infra_flake → loop directly (no reset, no budget burn). * runDocsPhase + runUnit + callAgent all thread opts.mockFixtureDir through to the protocol. * Auto-attaches sseBroker to unit-registry and bug-tracker on module load — every FSM transition + bug op fires an SSE event. * bug_open at integration failure, bug_transition → fixed when subsequent L2 round passes. Routes: - server/routes/devloop.js: * POST /projects/:pid/run now defaults to full runProject; ?only=<id> still drills into single-unit runUnit (P2 compatibility). * GET /projects/:pid/stream — SSE with Last-Event-ID replay. * GET /projects/:pid/bugs?status=&unit_id=&severity= — bug list. - server/index.js + agent-server.js mount /public for static assets. Static status page: - server/public/devloop-status.html (~100 lines) — pure HTML, polls GET /state every 2s, renders unit DAG with status pills + timeline tail. URL ?pid= or input box. Acceptance: - scripts/phase3-verify.js — 9 steps across 4 scenarios + observability: ① contract_mismatch ships at sdd_rev=2 l2_rounds=1, sdd.updated SSE, changelog sdd_rev entry, bug open → fixed. ② impl_bug ships, sdd_rev=1, l2_rounds=0 (no budget burn). ③ spec_ambiguous → l2_blocked + human_intervention_needed SSE. ④ infra_flake ships, rerun_integrator action, l2_rounds=0. ⑤ Observability: timeline/bugs/changelog/stream.log present. ⑥ SSE replay from Last-Event-ID returns ≥2 historical events. Tests (+13 cases): - server/tests/devloop/run-project.test.js (11) — 4 scenarios end to end + SSE event capture. - server/tests/devloop/routes.test.js (+2) — full-project mode + bugs 404. Updated P2 ?only stub test → full-project 404 on unknown pid. Results: - phase0-verify: 8/8 - phase0-test-call: 7/7 - phase1-verify: 7/7 - phase2-verify: 6/6 - phase3-verify: 9/9 - vitest devloop+acp-core: 194/194 (was 181 → +13) Specs: - docs/prd/newfunction/_spec/plan-migration.md · P3 验收 - docs/prd/newfunction/_spec/loops-l2.md (failure attribution + budget) - docs/prd/newfunction/_spec/agents-integrator.md - docs/prd/newfunction/_spec/agents-triager.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> -
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> -
Implements the §P1 acceptance gate from plan-migration.md: a single POST /api/devloop/projects produces a runnable todo-demo project with multi-page PRD/SDD sites, units.json, integration spec, agent-state, and history snapshots — all via mock agents (no real Claude CLI cost). Code additions: - packages/acp-core/protocol/ClaudeCliProtocol.js * mock mode now emits an extra `mock/files` acp_event when the fixture contains a files[] array. LoopController applies it to the project root, letting mock agents produce real outputs. - server/src/core/devloop/loop-controller.js (now ~280 lines) * callAgent(pid, agentName, prompt, opts) — single-agent invocation: reads system/templates/{agent}/prompt.md, runs mock protocol, applies fixture files[], timeline-logs, auto-commits. * runDocsPhase(pid, requirement) — orchestrates PM → Architect with `docs.complete` timeline marker. * getProjectState(pid) — returns project.json + units + prd_rev/sdd_rev + timeline tail for the GET /state endpoint. - server/routes/devloop.js (new) * POST /api/devloop/projects — createProject + runDocsPhase * GET /api/devloop/projects * GET /api/devloop/projects/:pid/state - server/index.js / server/agent-server.js — mount /api/devloop. Mock fixtures (drive the full acceptance file tree): - fixtures/devloop-mock/devloop-pm.json * 14 files: prd/{index,reading-guide,product/*,feature/*,flow/*,prototype/*}.html, prd/assets/{shell.css,shell.js,search-index.json}, agent-state/{pm.json,pm-todo.json}, history/prd-rev-001.html. - fixtures/devloop-mock/devloop-architect.json * 13 files: sdd/{index,mvcp/*}.html, sdd/assets/*, units.json (3 units, DAG), integration/spec.json, agent-state/{architect.json,architect-todo.json}, history/sdd-rev-001.html. Acceptance: - scripts/phase1-verify.js — spins ephemeral express app, POSTs the todo-demo project, asserts 23 expected paths exist + 3 units with l2_rounds/l2_budget + git ≥4 commits + timeline events. Tests (12 new cases): - server/tests/devloop/loop-controller.test.js (7) — callAgent + runDocsPhase + state. - server/tests/devloop/routes.test.js (5) — POST validation, full POST, GET state, 404 on unknown pid. Results: - phase0-verify: 8/8 - phase0-test-call: 7/7 - phase1-verify: 7/7 - vitest devloop+acp-core: 174/174 (was 162 → +12) Specs: docs/prd/newfunction/_spec/plan-migration.md · P1 Docs MVP Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>