-
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>
-
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>
-
v_task_detail's org-strip only ever covered planner/coder/validator -- no way to see planner_reviewer's verdict trail from the task detail page at all, only from v_graph_console. Rather than maintain two parallel, drifting implementations of "org node status for this task", v_task_detail now reuses the same 4-stage Org meta-graph component (renderOrgMetaGraph/mountOrgMetaGraph) v_graph_console already has. mountOrgMetaGraph/renderOrgMetaStageDetailHtml gain an optional enableNodeClick option so v_task_detail's usage gets real per-node live log-tail expansion (wired through the existing activeLogViewers/closeAllLogViewers/mountLogViewer machinery) while v_graph_console keeps its existing read-only snapshot behavior unchanged. ORG_NODE_DETAIL_RE now also accepts planner_reviewer so deep links into the 4th stage are reachable; landing on one auto-expands and scrolls to it (focusOrgMetaStage), matching the old focusOrgNodePanel's behavior. Dashboard's own 3-node org-strip is untouched. Deleted the now-dead renderTaskDetailOrgStripHtml/renderOrgComboPanelContent/ wireOrgComboArtifactClicks/focusOrgNodePanel. node frontend/test_graph_console_smoke.js passes (extended with coverage for the regex widening and the enableNodeClick threading), independently re-verified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
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>
-
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>
-
v_task_board's task_id cell now links to #/graph?task_id=<id> for quick navigation to a task's execution graph. v_graph_console reads that param to preselect the task, and syncs the URL (via history.replaceState) on every task load -- including the initial auto-preselect -- so the address bar always reflects the task actually showing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
…76ae-main-checkout-drift
-
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>
-
…10c5-main-checkout-drift
-
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> -
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>
-
Adds src/deployment_query.py (list_deployments/get_current_deployment/ get_deployment -- no project_id filter needed, deployment has no project_id column since there's exactly one graphlemon self-target) + three new backend_server.py routes (GET /api/deployments, /current, /:id, the last with recent deploy_health_signal history) + a new v_deployment_status page in frontend/app.js polling every 5s (deployment.status changes don't go through c_ws_subscribe -- Deploy Supervisor writes deployment/ deploy_health_signal directly, no execution_event of its own). No 02-V spec exists for this view (PH5 was added after 02-V was written) -- DOM/class conventions follow the existing pages' own patterns. Verified against a real running server via urllib (browser extension unavailable this session), plus 9 deployment_query unit tests and 5 real HTTP route tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
U-031: closes a structural integration gap found during the REQ-01~14 regression audit -- plan_work_graph/execute_work_graph/run_work_node were each individually tested but never actually called together in production code (e2e_pipeline.py's only validated closed loop bypassed all three via flat org_node_trigger calls). Adds work_graph_orchestrate.py (topological sort over graph_manifest + one one-node dynamic-workflow agent() call per Work Node, needed for REQ-06's per-node breakpoint recovery), rewires e2e_pipeline.py's plan_documents/ implement_code onto the real chain, and fixes two bugs a real API run surfaced: graph_manifest's id/node_id/depends_on normalization (work_graph_plan._normalize_graph_manifest) and a missing repo_path in run_work_node's cwd resolution. work_node_run.py also now marks a node 'blocked' instead of leaving it stuck 'running' on any unretryable run_fn exception. U-024.5: the regression audit also found REQ-12's reconnect boundary case (refetch a snapshot after a dropped WS reconnects) was never implemented -- only the dashboard reacted to onStatusChange, and even it never refetched. Fixes mountDashboard/mountTaskBoard/mountTaskDetail (which embeds v_graph_visualizer) to refetch their current snapshot on reconnect. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
三个都是 config_id='default' 单例配置,首次使用即创建、之后原地更新 (upsert),不需要单独的初始化流程: - c_concurrency_config_update:新增 src/settings_config.py( org_concurrency_config 之前完全没人碰过),校验 max_concurrent_goals 落在 1-50。 - c_budget_config_update:budget_guardrail.py 新增 upsert_budget_config() ——跟 U-019 已有的 update_budget_limit_and_resume()(只更新已存在的 行)不是一回事,这个是真正完整的设置页控制器,支持首次创建,沿用 同一套"额度调高后自动恢复 budget_exceeded 阻塞任务"副作用(提取成 _resume_budget_blocked_tasks() 共享)。 - c_log_retention_config_update:同样在 settings_config.py 里,校验 retention_days > 0。 三处校验都在业务层做完整检查,HTTP 层额外兜了 except (InvalidParamError, TypeError)——payload 数值字段缺失时 None<=0 这类比较会抛 TypeError 而 不是业务异常,两个都要接住才能保证返回干净的 400 而不是裸 500。 backend_server.py 第一次加了 do_PUT。 前端三个页面共享一个 renderSettingsSubnav() 小组件模拟 02-V 原型里的 设置下拉菜单。真实服务器端到端验证过三个 PUT/GET 的完整读写循环。 PH3(人机交互控制台)至此全部完成:U-022~U-030。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
c_skill_diff 和 U-028 的 c_artifact_get 一样,只存在于机器可读的 sdd-mvcp-contract.json,03-C 正文从未写出来,已订正补全。 c_skill_list/c_skill_get 两份文档都没定义,是 v_skill_library/ v_skill_audit_log 自己的展示需求倒逼出来的缺口。 新增 src/skill_query.py:list_skills()、get_skill_audit_log()、 get_diff()(git diff <before> <after>——skill_update.py 的 _write_and_commit 把每次迭代做成原子单文件 commit,整仓库范围的 diff 天然只涉及那一个 Skill 文件)。 关键安全考量:Skill 库活在 graphlemon 自己的仓库里,c_skill_rollback 会真的在这个仓库里跑 git commit——如果 HTTP 层写死指向真实仓库根目 录,任何针对这条路由的测试都会在正在跑的这个 graphlemon 仓库里留下 真实提交。参照 db_path 已有的做法,给 create_server() 加了 skill_repo_path 参数(默认 graphlemon 真实仓库根,测试传临时仓库), 不通过 HTTP 暴露这个参数,避免变成任意写入的口子。记录在 05 §5.4.1.4。 真实服务器 + 临时仓库端到端验证过完整的 update→diff→rollback 闭环, 确认过跑完之后 graphlemon 自己仓库的 git log 完全没变化。 v_skill_library/v_skill_audit_log 不检查"当前项目"——Skill 是平台级 资产,跟项目无关。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
c_artifact_get 在机器可读的 sdd-mvcp-contract.json 里其实定义过 (GET /api/artifacts/:artifact_id),只是 03-C 正文一直没写出来,已订正 补全。c_artifact_list 连 MVCP JSON 里都没有 operation 定义,纯粹是 v_artifact_browser 自己的过滤字段倒逼出来的缺口,跟 U-025 补 c_task_list 是同一类问题。 新增 src/artifact_query.py:list_artifacts() 返回裸数组(跟已有的 list_tasks() 保持同款风格);get_artifact() 把血缘链(按 depends_on_artifact_ids 往回走,只走第一个依赖——这条流水线的实际产出 只有线性 BRD→PRD→SDD 链)和文件内容(artifact 表本身没有 content 列, 走 project.target_folder_path + artifact.file_path 现读磁盘)一起返回, 预览面板一次请求就够。 前端预览统一用 <pre> 纯文本展示,不做 Markdown 渲染/代码高亮——02-V 原文想要那个效果,但需要引入第一个前端第三方库,跟这个控制台一贯的纯 vanilla JS、无构建步骤做法冲突,是有意简化。顺手给 index.html 常驻导航 栏加上了"产物"链接。 真实服务器验证过三级血缘链(BRD→PRD→SDD)返回顺序正确。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
execution_log_file 的行生命周期 U-013 早就管好了,但从来没有代码真的往 日志文件里写过内容——src/execution_log.py 新增 append_log() 补上这条 缺失的写路径,设计成 run_fn 执行过程中随时可调用,不关心节点在做什么。 get_log() 按 offset/limit 分页读,download=true 时走原始文件流。 c_ws_log_tail(WS /api/ws/work-nodes/:node_exec_id/log)复用 U-024 手写 的 RFC 6455 机制,轮询 execution_event(log_appended);节点一旦转终态 (done/blocked/failed)服务端主动发关闭帧断开,不让客户端一直轮询。 p_log_retention_cleanup 实现成纯函数 run_log_retention_cleanup():过期 扫描 + max_total_size_mb 超限按最旧优先追加候选 + 单个文件删除失败只 记录不阻塞其余项,没有配调度器(不在本 Unit 覆盖范围),留给未来的 调度进程周期调用。 过程中意外抓到一个真的并发 bug:_Server.server_close() 关闭共享 sqlite 连接时原来没拿 conn_lock,如果这时候正好有个 WS 轮询线程在 另一边持锁执行 SQL,两个线程无同步地并发操作同一个 C 级 sqlite3 连接 对象,实测直接把解释器崩了(SIGSEGV)——U-024 就已经有这个隐患,这次 高频 WS 测试把时序窗口撞开了。修好后连续跑几十次全部稳定通过,记录 在 05 §5.4.1.3。 frontend/app.js 的 v_log_viewer 挂在 v_task_detail 每个 to-do 节点的 展开详情里,首次展开才建连接(懒加载),运行中态走 c_ws_log_tail 实时 滚动、终态走 c_execution_log_get 静态拉取,活跃连接在任务级刷新/离开 页面时统一关闭。真实服务器 + Node 原生 WebSocket/fetch 端到端验证过 实时追加与终态自动断开的完整链路。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
纯前端 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>
-
三个页面需要的读接口此前都不存在,本 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>
-
c_task_create(U-009 已实现的 task_create.create_task())第一次接上 POST /api/tasks。c_bug_report_create 此前完全没有实现,新增 bug_record.create_bug_report() + POST /api/bug-reports。 发现并订正 03-C 一处输入契约遗漏:c_bug_report_create 的 input_contract 没写 project_id,但 execution_event.project_id 是 NOT NULL、bug_record 表自己没有 project_id 列可以在 related_task_id 留空时兜底推导,字面上无法满足约束——按 c_task_create 同款处理方式把 project_id 变成必填,03-C 已订正。 frontend/app.js 补上 #/goals/new、#/bugs/new 真实交互;控制台还没有 "当前项目"状态的正式载体(v_dashboard 是 U-025 才做),project_id 暂存 localStorage,v_project_setup 创建成功时写入。related_task_id 因为 c_task_list 未被任何 Unit 认领,简化成文本输入而非下拉,等 U-025 补上 任务列表后再换。提交成功后原型写的是跳转 v_task_detail,该页面也是 U-025 才建,暂时改成原页面内展示确认信息。 端到端冒烟时额外发现一个真 bug:create_bug_report() 原本没校验 project_id/related_task_id 是否存在,坏 project_id 会在 execution_event 插入时触发未捕获的 IntegrityError,请求直接 500——补了存在性校验, 改为干净的 INVALID_PARAM/400。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
c_pick_folder 补上 Windows(PowerShell FolderBrowserDialog)和 Linux (zenity 优先、回退 kdialog)实现,三平台都覆盖到。c_project_create (U-002 已实现的 project_scaffold.create_project())第一次接上 HTTP 路由 POST /api/projects——backend_server.py 因此第一次需要持有 sqlite 连接,schema.connect() 加 check_same_thread=False 参数,配合 server.conn_lock 序列化 ThreadingHTTPServer 各请求线程的访问。 frontend/app.js 补上 #/setup 真实交互(选文件夹→校验 Git 仓库→非 Git 仓库二次确认→创建→跳转仪表盘),DOM class 命名对齐 docs/wireframes/v_project_setup.html。 REQ-11 完整 happy 用例(含创建 task)还需要 U-023 的 v_goal_composer/c_task_create,本 Unit 只完成 v_project_setup/ c_pick_folder/c_project_create 部分,development-plan.md 已记录。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
frontend/{index.html,app.js,style.css}: no build step, no npm, vanilla JS hash router (05 §5.7). Real page content is PH3 (U-022+, starting from docs/wireframes/); this is only the empty shell + working navigation the Done criterion asks for. backend_server.py serves the 3 files via an exact-path whitelist (not a generic static-file/directory server) -- no path-traversal surface to reason about for 3 fixed files. Verified in an actual browser this session (navigated to localhost, clicked all 3 nav links, screenshotted each) rather than only trusting stdlib tests -- a Python-only test can't exercise real hashchange/DOM behavior without a JS engine. Nav highlighting and content swap both confirmed working, no page reload, no build step. 7 new stdlib unittest cases covering what the server is actually responsible for (static files served with correct content-type, 404 on unknown GET path). 66 total across U-001-008, all passing. This closes out PH0 (U-001~U-008) -- all three Phase-acceptance criteria now hold for real: target-project directory scaffold, 15-table schema with constraint tests, and a real claude --agent round-trip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>