• 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>
    bboonnee authored
     
    Browse File »
  • 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>
    bboonnee authored
     
    Browse File »

  • 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>
    bboonnee authored
     
    Browse File »
  • 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>
    bboonnee authored
     
    Browse File »