-
Recovers tasks abandoned mid-flight (status in queued/planning_brd_prd_sdd/ coding/validating with no recent execution_event) by re-driving them through task_execution.run_or_resume_task_lifecycle on a background poll, guarded by an in-process in-flight lock to avoid double dispatch. Responds to a real 2026-08-06 incident where a task sat stuck in 'validating' for 15+ hours with nothing polling it. Scoped to FR-2 only from docs/todo/scheduler-production-polling-loop- requirements.md; FR-1 (new-goal auto-dispatch) stays out per that doc's own non-goals -- it's a separate, larger autonomy-boundary decision. Design recorded in docs/todo/scheduler-production-polling-loop-design.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
Root cause traced (docs/implementation-gap-analysis.md): the Benchmark document Validator relies on for its verdict was frequently a narrative about the work session ("The planner workflow completed and produced a Benchmark defining 18 scenarios...") instead of the actual scenarios -- found via a real skill-detail-page task's benchmark.md, then confirmed present since the earliest real self-hosting runs. Validator's own judgment was, in practice, resting on thin, sometimes-vacuous prose. Planner's work graph (brd->prd->sdd->benchmark) extends to benchmark->test_dataset->test_script: test_dataset/test_script are always present as nodes, but the generating subagent judges per-goal whether a real dataset/script applies (many goals, e.g. UI wiring, legitimately don't have one -- replying 'N/A: <reason>' is a real, expected answer, not a failure). test_script gets a genuinely mechanical verify_fn (real py_compile/node --check, not LLM judgment) -- the one node type where an objective check is actually possible. All four document prompts also gained an explicit anti-narration instruction (only 'brd' had a weaker version of this before). Coder's implementation prompt now receives the real benchmark/dataset/ script and must make the Planner's real script pass for real, not just whatever tests it invents on its own. Validator gets its own 3-node Work Graph (validator_benchmark -> validator_test_dataset -> validator_test_script), generated independently from SDD/PRD -- deliberately NOT reusing Coder's or Planner's own copies, same reasoning as a held-out eval set vs. the dev set a model was tuned against. The resulting script, if real, is actually executed against the current repository state; a non-zero real exit code force-overrides the verdict to FAIL in code (user-confirmed hard gate: the script is not up for LLM debate on a failure, only on a pass can subjective judgment add INCONCLUSIVE/risk notes). schema.py: artifact.artifact_type's CHECK constraint gains the 5 new types, plus migrate_expand_artifact_types() -- SQLite has no ALTER-based constraint migration, so this is the standard rebuild-copy-swap dance, applied for real to the live ~/.graphlemon/graphlemon.db (42 rows, verified byte-identical count before/after, integrity_check clean). skills/qa-task-validation.md and skills/project-sdd-planning.md already gained a standing principle from this same incident: verify cross-task data dependencies against real system state, not seeded fixtures alone (see prior commit d7035fc) -- this commit is the mechanism-level half of the same fix, not just the prose-guidance half. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
New target projects never got .claude/agents/*.md deployed, so their first Planner call always failed immediately (claude --agent planner finds nothing in cwd) -- self-target usage never noticed since those files are already committed as part of this repo. create_project() now deploys and commits them alongside the existing .graphlemon/ init commit. Also: unroutable execution failures were recorded as a bare blocked_reason='execution_error' with no way to see what actually broke. scheduler._block_task()/task_execution._mark_blocked() now persist the real exception message into execution_event.payload (blocked_reason itself is unchanged -- budget_guardrail matches its exact value elsewhere). Found via a real console Start-button failure on a freshly created project; see docs/implementation-gap-analysis.md for the full writeup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
Users had no way to actually start a queued goal from the console -- task.status just sits at 'queued' forever. Traced the root cause through 03-C: c_task_create's own contract ("若 max_concurrent_goals 已达上限, 任务落 queued 但不触发调度") implies task creation auto-triggers scheduling otherwise, and 03-C/04-P repeatedly assume an always-on Org Graph scheduler that watches the queue -- src/scheduler.py builds the mechanism but nothing ever spins it up as a running process, so no "start" button was ever designed because none was supposed to be needed. Design (confirmed with user, both recommended options chosen): - Pause/Stop are a soft stop, not a real subprocess kill -- the current in-flight `claude` call finishes naturally, only the next phase is withheld. Hard-killing would need Popen + PID tracking, out of scope. - No new task.status enum value -- reuses blocked_pending_human with blocked_reason='user_paused'/'user_stopped', riding the existing manual_resume mechanism instead of a schema change. New src/task_execution.py: run_or_resume_task_lifecycle() resumes from whichever phase actually hasn't run yet (queued/planning_brd_prd_sdd/ coding), start_task_execution_in_background() runs it to completion on its own daemon thread + sqlite connection, request_pause() blocks an in-flight task. Extended /route's manual_resume handling so resuming from a user-initiated pause/stop actually re-continues execution in the background, not just flips the status field back. Real gap found while building this: backend_server.create_server() never stored db_path on the server object -- needed for the new background thread to open its own connection to the same db. 22 new tests (14 src/test_task_execution.py + 8 TaskExecutionRouteTest in test_backend_server.py, HTTP wiring mocked around the already-unit-tested background thread mechanics). Verified against a real running server via curl: /execute 409s a non-queued task, /pause 409s a queued task and a double-pause, succeeds on a coding task. src/ suite: 496 tests, all green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>