-
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>
-
Real incident (2026-08-13, task 71868db6): ConcurrencyLimitReachedError isn't an error -- it just means this dispatch attempt found no free slot, which is normal, expected backpressure. Routing it through the same blocked_pending_human/ execution_error bucket as a genuine failure was itself a bug: it forced a task that should just wait its turn into looking like it needs human judgment. _check_concurrency() raises before any task.status transition happens for this attempt, so task_execution.py::_mark_blocked() now leaves status untouched (still 'queued' for a fresh dispatch) instead of blocking -- the next new_goal_dispatch tick naturally retries it, no human/Boss Agent involvement needed. Still logged as a new 'concurrency_wait' execution_event (new migrate_add_concurrency_wait_event_type, same accumulated-CHECK-enum pattern as the existing event-type migrations) so the wait stays auditable, not silently invisible. This is the first concrete instance of FR-7 in docs/todo/boss-agent-autonomous-blocked-task-arbiter-requirements.md (system should emit distinguishable blocked_reasons/non-error states at the point of failure, not dump everything into one generic bucket). 803/803 src tests, 134/134 supervisor tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
Real self-hosting DB (2026-08-13) has 10 task_ids with multiple non-terminal work_graph_run rows each (legacy per-org_node-row leftovers from before the merged-graph architecture) -- the migration's final CREATE UNIQUE INDEX ux_work_graph_run_active ON work_graph_run(task_id) crashed against this real data (IntegrityError), and because executescript() commits each DDL statement individually, the crash left the live DB in a partially-migrated state: org_node column dropped, but the index never created, with the old idempotency check (`if "org_node" not in cols: return`) then permanently skipping any further attempt to create it. Adds _reconcile_duplicate_active_work_graph_runs(), which marks all but the most-recently-created non-terminal run per task_id as 'failed' (not 'blocked' -- the index's WHERE clause treats 'blocked' as still-active, which was the first fix attempt and still failed) before the index is created. Recency is ranked by rowid, not run_id: real data has started_at=NULL on every legacy row, so a run_id tiebreak would pick among duplicates by UUID string value, unrelated to actual insertion order. Also splits the idempotency check into column-drop and index-creation halves so a database already in the partially-migrated state (like the live one) gets the missing index created on the next run instead of being skipped. Dry-run verified against a copy of the live 36-row production DB before applying to the real ~/.graphlemon/graphlemon.db (backend_server was down for this, no concurrent writers). 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>
-
migrate_add_reviewing_planner_output_status() rebuilds task (create-copy- drop-rename, SQLite's only way to change a CHECK constraint), but task is a parent table referenced by work_graph_run/artifact/execution_event/ bug_record. DROP TABLE task under PRAGMA foreign_keys=ON (schema.connect()'s default) trips FK enforcement the instant any child row exists -- unit tests never seeded a referencing child row, so this never surfaced until a real restart against the live production database (months of real history). Found and fixed live: no data loss (INSERT INTO task_new already succeeded before the DROP failed; verified the two tables held byte-identical rows, cleaned up the stray task_new, backed up the db, re-applied the fixed migration, verified via PRAGMA foreign_key_check, then restarted the real process against production and confirmed a real end-to-end task run through the new Planner -> planner_reviewer -> Coder -> Validator flow). Fix follows the migrate_relax_org_node_constraints() precedent already in this file: PRAGMA foreign_keys=OFF around the rebuild, PRAGMA foreign_key_check after, ON in a finally block. New regression test seeds one referencing row per child table so this class of bug can't pass silently again. Also adds real (non-mocked) coverage for the planner_reviewer role in test_e2e_pipeline_e2e.py: one real call confirming it flags real narrative- text repro fixtures, one confirming it passes real structured content. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
Phase 1 (planner_reviewer review gate + P0 resume-bug fix) shipped as ea19c7d, matching org-graph-unification-phase1-review-gate-design.md exactly (new task.status value, org role, _REQUIRED_ARTIFACTS narrowing, both bugs fixed). Moving that design doc and the P0 bug doc it subsumed to docs/done/, updating their status headers, and fixing the doc-path references left pointing at their old docs/todo/ location across agents/planner_reviewer/{SOUL,AGENTS}.md, src/schema.py migration docstrings, and the other Phase 2/deblackbox docs. Full suite green (731 tests) both before and after. Also ran Phase 2 SDD's §9 interface-reconciliation checklist against the real Phase 1 implementation now that it exists for real: all three items check out (run_id is included in plan_and_review_documents()'s return shape, the new task.status value is exactly reviewing_planner_output as assumed, and planner_reviewer's role.json doesn't pre-reserve a skill slot for Phase 2's new skill, which is fine -- a one-line addition when Phase 2c lands, not an incompatibility). Updated §9 to reflect this. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
Inserts Planner -> planner_reviewer -> Coder -> Validator into the fixed Org Graph flow (was Planner -> Coder -> Validator), per docs/todo/org-graph-unification-phase1-review-gate-design.md. A new planner_reviewer role independently judges whether Planner's BRD/PRD/SDD/ Benchmark output is real structured content (not narrative prose) before Coder is allowed to start, with a 3-attempt retry loop and a narration heuristic informed by the real narrative-output incident (docs/todo/validator-test-script-narrative-output-issue.md). Also fixes, in the same code path: - The P0 bug in task-execution-resume-assumes-planner-artifacts-exist.md: resuming a task no longer assumes sdd/prd/benchmark artifacts exist -- falls through to real replanning when they don't. - A fresh-run KeyError (not resume-specific) when a self-planned graph omits the prd/benchmark node types. - agent_profile.migrated_from_profile was NOT NULL, which broke as soon as a role with no legacy Hermes profile (planner_reviewer) was recorded. org_node_lifecycle._REQUIRED_ARTIFACTS["coder"] is narrowed from (brd,prd,sdd,benchmark) to (sdd,) -- the old 4-artifact gate never actually blocked anything since artifact_write.py sets validation_status='passed' unconditionally; planner_reviewer is the first real semantic gate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
work-graph-partial-artifact-persistence and work-graph-self-planning- and-loop-back (requirements + design) are all shipped and tested; move them out of docs/todo/ and fix up the docs/todo/*.md and src/*.py path references that pointed at their old location. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
…4843-main-checkout-drift
-
Per CLAUDE.md's docs/ role split (todo = pending, done = shipped into the SDD/code): all seven Graph Engineering series items with a real implementation (B, D, E, F, G, H -- A had no separate doc, resolved directly in graphlemon_architecture.md; C is explicitly won't-do) now have their requirements+design docs moved from docs/todo/ to docs/done/, git mv'd to preserve history. Every cross-reference to the 12 moved files -- across the other moved docs themselves, graph-engineering-open-items.md, 2 Skill files, and 9 src/*.py docstrings/comments -- updated from docs/todo/<file> to docs/done/<file>. Verified no stale docs/todo/ references to any of the 12 filenames remain anywhere in the repo. graph-engineering-open-items.md itself stays in docs/todo/ (not moved) -- it's a living index that keeps gaining new entries from later comparison-note/gap-analysis passes (H itself was added this way, and a new item already appeared while this cleanup was in progress), not a single finished conclusion. Its own header now reflects that all of A/B/D/E/F/G/H are done and explains why it wasn't moved. Also fixed two pre-existing stray path references (missing todo/done/ prefix entirely, predating this cleanup) in its G section. Full src suite (625 tests) passes -- this is a pure documentation/comment change, no behavior touched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
Implements docs/todo/role-package-abstraction-design.md. Requirements review (docs/todo/graph-engineering-open-items.md's H section) had already corrected the requirements doc's own dependency analysis before this; scope confirmed with user 2026-08-06: relax all 4 org_node CHECK constraints in one pass, not staged. Format is JSON, not YAML: 05 §5.7's pure-stdlib constraint rules out PyYAML, and unlike Skill files (whose YAML frontmatter only Claude Code itself ever parses, never this codebase's own Python), role.json genuinely needs to be read by agent_profile.py -- json.load is the zero-risk choice over a hand-rolled parser. role.json is a REFERENCE manifest, not a content copy: identity.soul_md_path/ agents_md_path still point at the untouched agents/<role>/SOUL.md/AGENTS.md files. This is what makes "deployed output must stay identical" close to automatic -- render_agent_definition()'s body-assembly logic doesn't change at all, only the frontmatter's data source does (hardcoded FRONTMATTER dict -> parsed role.json). Verified byte-for-byte: diffed the old (git HEAD) render_agent_definition() output against the new one for all three roles, zero differences. agent_profile.py: ORG_NODES is now discovered by scanning roles/ for role.json files (_discover_role_ids()) instead of a hardcoded 3-tuple; FRONTMATTER/_DESCRIPTIONS/_MIGRATED_FROM_PROFILE dicts are gone. Every public function takes an optional roles_dir override specifically so a test can prove "a new role needs zero code changes" against a throwaway fixture directory (NewRoleWithoutCodeChangeTest) rather than asserting it by inspection. schema.py: skill.owner_org_node/task.assigned_org_node/work_graph_run.org_node/ agent_profile.org_node relaxed from CHECK(x IN ('planner','coder','validator')) to CHECK(x != ''). Real blast radius found during design (bigger than the requirements doc's own risk note): task/work_graph_run/skill are referenced by several other tables' foreign keys, but since only the parent tables' own CHECK text changes (child FK definitions are untouched), only these 4 parent tables need rebuilding -- followed SQLite's documented procedure for schema changes under active foreign keys (foreign_keys=OFF for the duration, rebuild, foreign_key_check to confirm nothing went dangling, foreign_keys=ON). migrate_relax_org_node_constraints() depends on D's migrate_add_work_graph_fork_columns() having run (called unconditionally, idempotent) so work_graph_run's rebuild has the right column set either way. ux_work_graph_run_active and task's 3 indexes verified to survive the rebuild. work_graph_run_query.py's own separately-hardcoded _VALID_ORG_NODES (a real dependency the requirements doc's risk section missed) now reuses agent_profile.ORG_NODES instead of maintaining a second copy. Explicitly NOT done (scoped out during requirements review, not an oversight): frontend/app.js's 4 hardcoded 3-role spots (route regex, sort order, dropdown, lookup object) -- a new role still won't be recognized by the PH3 console. A `role` table with real FK referential integrity was also not built; this uses the simpler "just relax the enum" approach. Full src suite (623 tests) + supervisor suite (125 tests) pass with zero regressions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -
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> -
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> -
…c231-main-checkout-drift
-
Adds supervisor/deploy_stage.py: stage_deployment() creates a deployment row, git-worktree-checks-out the target commit into a fresh directory (--detach, never in-place git pull), copies the production DB to a one-time candidate copy, and Popens a real candidate backend process on a free port. Return value follows 04-P §4.6's state-machine table (status after the call is 'candidate_starting') rather than 03-C's one-line 'staged' summary -- 04-P's own trigger text ("c_deploy_supervisor_stage 完成" moves the row to candidate_starting) is the more detailed/authoritative source when the two disagree, same precedent used throughout this project. Found and closed two real gaps along the way: - deployment's own field list never included where a candidate's checkout directory lives -- added candidate_instance_dir to schema.py (also what the NEXT staging call's previous_instance_dir bootstraps from once a deployment reaches 'finalized'). - backend_server.py had no CLI entry point at all since U-007 -- create_server() was only ever instantiated in-process by tests. Added main() (argparse + serve_forever + SIGTERM->shutdown() from a separate thread, per http.server's own same-thread-deadlock warning). RealCandidateLaunchTest spawns and HTTP-probes a genuine `python3 -m src.backend_server` subprocess against a real git-worktree checkout of this repo (not mocked) -- caught a real ordering issue doing so: git worktree only sees committed content, so testing before this Unit's own commit landed would check out backend_server.py without main() at all. Fixed by using `git stash create` (builds a commit object from the current working tree without touching HEAD/index) as the commit-to-test-against, falling back to plain HEAD once the tree is clean post-commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -
Adds the two Deploy Supervisor entities from 01-M §1.15/§1.16 (17 tables total): deployment (full 15-value blue-green state machine, per 01-M's own text rather than supervisor-auto-rollback-design.md §3's abbreviated summary table) and deploy_health_signal (append-only health/monitoring samples). ux_deployment_active enforces 01-M's "only one non-terminal deployment at a time" constraint via an expression-based unique index (CREATE UNIQUE INDEX ... ON deployment((1)) WHERE status NOT IN (...)) -- the same partial-unique-index idiom work_graph_run already uses, just with a constant instead of a real grouping column since there's exactly one graphlemon self-target. bug_record.root_cause_category's schema-level CHECK now accepts core_engine, but bug_record.py's own application-layer whitelist deliberately does not follow yet -- classifying a bug as core_engine is a file-path-based rule (05 §5.8), not something the Validator's existing 3-perspective judge_root_cause can produce, and wiring that classification + c_org_loop_route's routing is U-035's job. 392 tests green. 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>
-
Implements docs/sdd/01-M-模型设计.md §1.0-1.14 as stdlib sqlite3 DDL in src/ (deployment/deploy_health_signal §1.15-1.16 are PH5/U-034, deliberately excluded). Covers all entities' fields, enum CHECK constraints, FKs, and the two non-trivial unique indexes (work_graph_run's per-(task_id,org_node) active-run partial index, artifact's (task_id,artifact_type,git_commit_sha) composite key). Per user decision (2026-07-30): bug_record.root_cause_category excludes 'core_engine' for now -- that value is PH5-scoped and development-plan.md assigns adding it to U-034, not U-001; a test locks this in. 14 stdlib unittest cases in src/test_schema.py cover table creation and the Done criterion's named constraint classes (unique/FK/enum), all passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>