• User-reported gap: the alert banner only ever showed task.blocked_reason (a
    coarse category like 'execution_error') with zero indication of the actual
    cause -- the real message only existed in the matching task_status_changed
    execution_event's own detail text, with no path to the UI at all. Diagnosing
    today's several distinct real blockers required querying the DB directly each
    time, which the user correctly flagged as something the console itself should
    surface.
    
    task_query.get_task() now returns blocked_detail (the most recent blocking
    event's detail text) when status is blocked_pending_human. v_task_detail shows
    it as an expandable <details>/<summary> inside the alert banner (no JS wiring
    needed, matches the plain-vanilla-no-framework convention) so a long or
    multi-line real error message doesn't force itself into the banner's layout by
    default but is one click away in full, not truncated or hidden.
    
    Also investigated (not a bug, documented here for the record): two node logs
    the user found empty really are empty on disk -- execution_log_file rows exist
    but their target files were never created, for two nodes dating back to the
    task's original 2026-08-04 run, long before today's fixes. The log viewer's
    "暂无日志内容" is accurately reporting that state, not a rendering bug.
    
    820/820 tests, frontend smoke passes.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »
  • Requested by user after today's incident retrospective: a manually-assignable
    execution-order hint across a project's tasks, shown as a new "执行顺序" column
    on v_task_board. Serial steps are whole numbers (1, 2, 3); parallel siblings of
    one step share the same integer part as decimals (2.1, 2.2). Purely advisory --
    nothing in the scheduler/dispatch loop reads it, it's a human/Boss-Agent-facing
    planning aid, not an enforcement mechanism.
    
    - schema.py: task.execution_priority REAL (nullable), migrate_add_task_execution_priority
      (plain ADD COLUMN, no CHECK constraint). Two earlier CHECK-rebuild migrations that
      also touch task (migrate_relax_org_node_constraints, migrate_add_reviewing_planner_
      output_status) now call it as a prerequisite and include the column in their own
      rebuilt task_new, same "depends on a later additive column" pattern
      migrate_relax_org_node_constraints already used for migrate_add_work_graph_fork_columns.
    - task_query.py: list_tasks/get_task expose it; new update_task_execution_priority
      (unlike update_task_goal_text, NOT gated to status='queued' -- it's a display hint,
      not a planning input, so it stays settable on in-flight/blocked tasks too).
    - backend_server.py: PUT /api/tasks/:id now accepts execution_priority independently
      of goal_text on the same endpoint.
    - frontend/app.js: new editable column on v_task_board, PUTs on change.
    
    Also adds docs/todo/append-run-node-id-collision-across-org-nodes-requirements.md,
    a third real finding from today's retrospective (task 911861fe: Coder and Validator's
    suggested node catalogs both converge on node_type="test_script", so their
    self-planned node_ids collide inside a shared merged work_graph_run -- PlanningFailedError,
    uncaught by _ROUTABLE_EXCEPTIONS, falls into the same generic execution_error bucket
    FR-7 already flagged).
    
    813/813 src tests, 134/134 supervisor tests, frontend smoke passes.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »
  • FR-5a: a new, standalone four-stage Org meta-graph (renderOrgMetaGraph and
    friends) rather than extending the existing 3-node renderOrgGraphTopology
    (left untouched, it's pinned by test_graph_console_smoke.js). Each stage
    expands in place: planner/coder/validator reuse renderGraphVisualizerPanel
    unmodified against that stage's latest run (identified via org_nodes_involved,
    Phase 2c's replacement for the removed single-value org_node column);
    planner_reviewer has no self-planned work_graph_run, so it shows its
    planner_review_verdict execution_event history instead (new
    task_query.py::_planner_review_events_for_task(), mirroring the existing
    _loop_back_events_for_run() pattern).
    
    FR-5b: an attempt_number-ordered, click-to-reveal feedback timeline built
    from loop_back_events, which was already being fetched but never rendered.
    
    New frontend/test_org_meta_graph_smoke.js (35 assertions, same vm.Context
    sandbox convention as the existing 4 smoke tests).
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »
  • A merged graph's one run genuinely spans several org_nodes now (Planner/
    Coder/Validator all append their own nodes into the SAME run) -- a single
    run-level org_node column was never accurate once Phase 2b/2c landed, and
    real per-node ownership already lives in work_node_execution.subagent_type.
    
    Schema: work_graph_run.org_node column removed; ux_work_graph_run_active
    narrowed from UNIQUE(task_id, org_node) to UNIQUE(task_id) WHERE status
    NOT IN ('done', 'failed') -- 'blocked' now counts as active on purpose,
    since resuming a blocked run always appends into the same row rather than
    inserting a fresh one. New migrate_drop_work_graph_run_org_node() handles
    already-populated databases, wired into backend_server.py's startup path.
    
    Call sites updated: work_graph_plan.py/work_graph_fork.py/
    root_cause_trace.py's INSERTs; work_graph_run_query.py/task_query.py now
    return "org_nodes_involved" (derived from work_node_execution.subagent_type)
    instead of the removed single-value "org_node"; frontend/app.js's org-strip
    and run-list rendering updated to the list-based shape.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »


  • Implements docs/todo/work-graph-time-travel-debugging-design.md, backend
    and console together (user confirmed 2026-08-06).
    
    Key finding that shrank the v1 scope: ux_work_graph_run_active is a
    PARTIAL unique index (only covers non-terminal statuses), not a blanket
    one -- forking from a parent run that's already reached a terminal status
    never collides with it at all. v1 restricts forking to exactly that case,
    so no index/constraint change is needed, only two new nullable columns
    (parent_run_id, fork_of_node_exec_id).
    
    Resolved the requirements doc's FR-1 ambiguity ("override input_payload,
    re-trigger execution") as OUTPUT SUBSTITUTION instead: the fork point's
    own output_payload is replaced with the human-supplied content directly,
    it is never re-executed via a new claude -p call -- only the strict
    downstream subgraph actually re-runs. This sidesteps reconciling the
    different build_prompt/input_payload conventions each caller uses
    (Planner's artifact_type lookup, E's generic task_prompt field, F's jury)
    -- work_graph_fork.py only needs the persisted graph_manifest and each
    node's output_payload.
    
    Implementation needs ZERO changes to the execution engine: pre-seeding
    every non-downstream node as a 'done' work_node_execution row before
    calling the unmodified run_graph() on the unmodified graph_manifest is
    enough -- run_work_node()'s existing REQ-06 breakpoint-recovery
    short-circuit (already-'done' nodes return their persisted output without
    calling run_fn) does all the "reuse without re-executing" work for free.
    _reconstruct_build_prompt supports two graph shapes generically from
    persisted state alone (Planner's canonical doc chain via
    e2e_pipeline._planner_prompt_builder, and E's generic task_prompt field);
    anything else raises ForkGraphShapeUnsupportedError rather than producing
    broken prompts.
    
    New: src/work_graph_fork.py (create_fork/execute_fork/fork_work_graph_run/
    start_fork_in_background, the last splitting fast DB-only setup from slow
    real execution so an HTTP caller gets run_id back immediately, same
    pattern task_execution.start_task_execution_in_background already uses).
    work_graph_run gets parent_run_id/fork_of_node_exec_id (SCHEMA_SQL +
    migrate_add_work_graph_fork_columns for existing databases).
    work_graph_run_query.py/task_query.py surface both fields. New endpoint
    POST /api/work-nodes/:node_exec_id/fork. Console: a "从这里分叉" form on
    each 'done' node in v_run_detail, a fork badge linking back to the parent
    run, and a 🔀 marker on v_graph_console's run list -- no diff/merge UI
    (non-goal #3), read-only branch visibility only.
    
    Verified end-to-end for real: started the actual backend, sent a real
    HTTP fork request, confirmed the new run persisted with correct
    parent_run_id/fork_of_node_exec_id lineage.
    
    Tests cover requirements doc acceptance standards 1-3 (PRD swapped ->
    SDD/Benchmark genuinely re-execute, BRD untouched; original run completely
    unaffected; the same node forked twice produces independent branches) plus
    schema migration idempotency, query-layer field surfacing, the HTTP route,
    and 3 frontend smoke-test assertions. Full src suite (608 tests) + all 3
    frontend smoke tests pass.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »

  • Two new console pages requested directly by the user, beyond 02-V's
    original declared non-goal ("不做完整多项目管理 UI(项目列表/切换)"):
    
    - v_project_list: full project list + rename + archive/reactivate.
      "Import an existing directory with real code" needs no new code --
      project_scaffold.create_project() already only ever adds .graphlemon/
      inside the target folder. New: src/project_query.py (list/get/update),
      GET/PUT/DELETE /api/projects[/:id]. DELETE archives (status='archived'),
      never a physical row delete -- project_id is FK-referenced from task/
      artifact/bug_record/etc., and 01-M's own active/archived enum already
      models this as soft-delete.
    
    - v_goal_list: extends the existing v_task_board/v_task_detail rather than
      duplicating them. Added sort/order to task_query.list_tasks() (column
      whitelist, no injection surface) and edit/delete on v_task_detail, both
      restricted to status='queued' (task_query.TaskNotEditableError, HTTP 409)
      -- editing or deleting a goal after real work has referenced it would
      leave that history pointing at something that no longer matches reality.
    
    Implemented directly rather than through graphlemon's own self-hosting
    loop -- confirmed with the user first that implement_code() always uses a
    hardcoded single-node manifest (no plan_work_graph() call for Coder), so a
    multi-page/multi-endpoint feature like this would've been one unreviewable
    Coder session with no way to decompose it.
    
    Real bug found on first verification pass (not caught by the existing
    _insert_task test helper, which never writes execution_event): a real
    task_create.create_task() always writes an execution_event row in the same
    transaction (01-M's FK, no ON DELETE CASCADE) -- even a never-touched
    queued task already has one, so DELETE FROM task raised
    sqlite3.IntegrityError. Fixed by clearing the task's own execution_event
    rows first; new regression test goes through the real create_task() path.
    
    474 tests green. Verified end-to-end against a real running server (every
    new endpoint exercised via curl) since the Chrome extension is still
    unavailable this session.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »

  • 纯前端 Unit,数据已经在 U-025 的 c_task_get 里,只是 task_query.py
    的 _work_nodes_for_run 补上了 hover tooltip 需要但当时没取的字段
    (input_schema_ref/output_schema_ref/quota_limit_type/
    quota_wait_started_at)。
    
    frontend/app.js 新增 computeGraphLayers():按 graph_manifest 节点的
    depends_on 边做拓扑分层,渲染成按执行层级排布的行,fan-out/fan-in
    都能正确分层——用一个 n1→[n2,n3]→n4 的真实 graph_manifest 验证过,
    直接从 app.js 里取出 computeGraphLayers 本体(不是重新实现一份逻辑)
    跑真实服务器返回的 JSON,分层结果完全正确。节点按 03-C 3.6 节六态
    配色,waiting_for_quota 态 hover 展示 quota_limit_type + 已等待时长。
    
    02-V §2.7 原描述还包含一份 Org Graph 层,但 v_task_detail 页面顶部
    已有等价的 org-strip 面板,这里判断重复渲染没有意义,只做了 Work
    Graph DAG 层,Org Graph 复用页面已有的那个。
    
    嵌入位置:v_task_detail 里原来"随 U-026 补上"的占位提示换成了真实
    的 DAG 面板。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »
  • 三个页面需要的读接口此前都不存在,本 Unit 一并建了:
    
    - c_task_list/c_task_get(03-C §3.2,此前无 Unit 认领):新增
      src/task_query.py + GET /api/tasks、GET /api/tasks/:task_id。
      c_task_get 的返回比 03-C 字面契约更完整——额外带上
      work_graph_run(含解析后的 graph_manifest)+ 每个 run 的
      work_node_execution 列表,因为 v_task_detail 的 to-do 树需要这份数据,
      03-C 已订正补全。
    - c_org_loop_route(U-016 已实现 route_org_loop(),从未接 HTTP)第一次
      接上 POST /api/tasks/:task_id/route,v_task_detail 的人工干预
      (manual_resume)按钮需要它。
    - 预算护栏此前没有任何读接口(03-C 只写了 PUT),新增
      budget_guardrail.list_budget_configs() + GET /api/settings/budget
      给仪表盘预算条用。
    
    frontend/app.js 补上真实的 #/(org graph 三节点卡片、当前任务概要、
    预算条、事件时间线,全部通过 c_ws_subscribe 实时刷新)、#/tasks
    (关键字/状态/org_node 过滤)、#/tasks/:task_id(task 字段、to-do 树、
    关联 artifact/bug_record、blocked_pending_human 告警条 + manual_resume
    按钮)。路由改成对象形式支持挂载态清理,切页面时关掉上一个页面的
    WebSocket 连接。v_graph_visualizer/v_log_viewer 子模块明确标成占位
    提示(随 U-026/U-027 补上),不是假造内容。
    
    顺带把 U-023 里因 c_task_list 当时不存在而简化的 related_task_id
    换回真下拉,goal_composer/bug_feedback 提交成功后也改回跳转真实的
    v_task_detail。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »