• Reviewed docs/todo/role-package-abstraction-requirements.md against the
    actual codebase before confirming direction. Two corrections to the
    document's own risk analysis:
    
    1. Its dependency list for agent_profile.ORG_NODES/FRONTMATTER was wrong
       in one place and missing the largest one: work_graph_orchestrate.py
       doesn't import agent_profile at all (only org_node_trigger.py and
       project_scaffold.py::deploy_agent_definitions() do -- smaller real
       footprint than claimed). But the document missed two real hardcoded
       3-role dependencies entirely: src/work_graph_run_query.py's
       _VALID_ORG_NODES, and -- the actually large one -- four separate
       hardcoded spots in frontend/app.js (a route regex, a sort-order array,
       a task-creation <option> dropdown, and a lookup object literal).
    2. Acceptance criterion 1 ("a new role needs no code/schema changes")
       only covers schema.py + agent_profile.py per its own non-goal #1 (a new
       role doesn't need real Org Graph wiring) -- the console genuinely won't
       recognize it either way. Flagged so the design doc states this
       explicitly instead of letting "no code changes" read as end-to-end.
    
    Confirmed for design phase with these two additions folded in; the
    document's own open questions (§8) are left for the design pass, not
    pre-decided here.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Code »
  • Implements docs/todo/verifier-fan-out-jury-design.md. root_cause_trace.py:
    _tally_perspectives() extracts the existing >=2/3 rule out of
    judge_root_cause so the closure-based version and the new
    judge_root_cause_via_work_graph() share one implementation, not two that
    could drift apart. The fan-in vote stays plain Python (zero_token
    aggregation per the design-dynamic-work-graph Skill's own edge_type
    distinction) rather than becoming a fourth Work Node -- no reason to pay
    for an Agent call just to count votes.
    
    judge_root_cause_via_work_graph(): each perspective becomes an
    independent Work Node with real schema-constrained structured output
    ({"category", "detail"} directly), not real_validator_perspective's
    manual "CATEGORY:"/"DETAIL:" text-line parsing. judge_root_cause /
    real_validator_perspective are kept, not removed -- documented as the
    lightweight fallback for scenarios that don't need worktree/Work Graph
    overhead.
    
    org_level_loop.run_fix_loop (the one real production caller) now calls
    the Work Graph version; the fixer/verify calls are unchanged flat
    trigger_org_node calls.
    
    Real bug found and fixed along the way: this is the first production code
    path that actually triggers a run_graph() multi-node CONCURRENT layer
    (every graph_manifest before F was single-node or a strict serial chain,
    so G's ThreadPoolExecutor path had never really run in production).
    scheduler.py::run_goal_to_completion and
    task_execution.py::start_task_execution_in_background both created their
    conn without check_same_thread=False -- fixed both, since their conn can
    now reach run_graph()'s worker threads via run_fix_loop.
    
    Test boundary migrated in test_org_level_loop.py/test_scheduler.py: the
    3 perspective calls are now mocked at work_graph_execute.write_workflow_script/
    execute_work_graph (matching test_work_graph_orchestrate.py's pattern)
    instead of org_node_trigger.trigger_org_node. New tests cover: three real
    independent Work Node executions, judgment-rule equivalence with the
    closure version, clean per-node input_payload, and concurrent wall-clock
    overlap across the three perspectives.
    
    Full src suite (586 tests) + supervisor suite (125 tests) pass with zero
    regressions.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Code »
  • Implements docs/todo/dynamic-work-graph-design-skill-design.md. G already
    answered FR-2 (how to mark a parallel-eligible node group): no new field
    needed -- topological_layers() infers independence purely from
    depends_on/edges, so the new Skill's job is just "express real
    dependencies honestly, don't add fake ones."
    
    New Skill skills/design-dynamic-work-graph/SKILL.md: the node-boundary /
    zero_token-vs-needs_judgment / per-node self-contained task_prompt
    methodology, wired into agent_profile.py's FRONTMATTER for coder and
    validator (Planner's own doc-chain path is untouched -- FR-4 only names
    Coder/Validator).
    
    work_graph_plan.py: new _GENERIC_PROMPT_TEMPLATE + generic_node_prompt_builder(),
    used whenever org_node != 'planner'. Planner keeps its existing fixed
    brd/prd/sdd/benchmark/... chain (_PROMPT_TEMPLATE) completely unchanged --
    zero regression risk to the one real production planning path.
    
    Scoped per the requirements doc's non-goal #1 ("prove the mechanism works,
    don't cover every real scenario yet"): does NOT wire into
    e2e_pipeline.py::implement_code()'s existing single-node production path.
    New tests prove the mechanism itself: a Coder scenario that naturally
    splits into 3 independent modules produces a correctly-shaped
    graph_manifest, topological_layers() puts all three in one concurrent
    layer (ties E directly to G), and generic_node_prompt_builder drives it
    through run_graph() to completion.
    
    Full src suite (580 tests) passes with zero regressions.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Code »
  • Implements docs/todo/work-graph-parallel-execution-design.md: run_graph()
    now schedules graph_manifest nodes layer-by-layer (topological_layers(),
    Kahn's algorithm grouped by round instead of flattened), running a
    multi-node layer's nodes concurrently via ThreadPoolExecutor (default 4
    workers) with a barrier between layers; a single-node layer stays
    synchronous, matching every real graph_manifest observed so far.
    topological_order() is now a thin flattening wrapper, unchanged behavior
    (regression-tested).
    
    _LockedConn/_FetchedRows makes the existing scattered conn.execute() call
    sites in work_node_run.py/work_graph_orchestrate.py/execution_log.py safe
    to call from multiple threads without touching those call sites -- note
    the lock must cover the full execute()-through-fetchone()/fetchall()
    cycle, not just execute() itself, or two threads can still collide on the
    shared sqlite3 connection handle. That gap was caught for real (not
    theoretically) by RunGraphRealSqliteConcurrencyTest during development --
    committing the fixed version, not the first draft.
    
    create_worktree() (work_node_run.py) gets a module-level lock around the
    `git worktree add` call for concurrent layers touching the same repo.
    
    New tests: layer grouping, same-layer concurrency + inter-layer barrier,
    one node's failure not cancelling a concurrent sibling (WorkGraphLayerError
    aggregates), breakpoint-recovery resume of a concurrent layer, a quota-wait
    timeout coexisting with a sibling's real persisted output, real (non-mocked)
    sqlite under concurrent writes across several rounds, and real concurrent
    `git worktree add` calls. Full src (575 tests) + supervisor (125 tests)
    suites pass with zero regressions.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Code »
  • …de13-main-checkout-drift
    graphlemon authored
     
    Browse Code »
  • The literal token value was committed in plaintext across 4 files
    (agent-memory notes, a stored run artifact, and a test assertion)
    while documenting that it had been redacted from migrated skill
    files -- the documentation itself failed to redact it. Replacing
    with placeholders/omission ahead of pushing this repo to a remote
    for the first time; the pre-existing commits (2a71bd5, 0cbf4b1,
    14dee05) still contain the plaintext value in history.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Code »
  • …85fa-main-checkout-drift
    graphlemon authored
     
    Browse Code »
  • 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 Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • Real incident (2026-08-06): the skill-content migration task and the skill
    detail page bug fix were two separate tasks. Neither was asked to verify
    that migrated content is actually reachable through the DB-backed lookup
    the detail page uses. Both passed their own tests cleanly -- because both
    only ever exercised the mechanism against seeded/mocked fixture data, never
    checked whether the real producer's real output actually exists in the
    real system. The result: the skill detail page returned "not found" for
    all 12 real migrated skills, and nobody caught it until a human clicked
    through the real console.
    
    qa-task-validation (§8): passing unit tests against seeded fixtures proves
    the mechanism is correct given valid input -- it does not prove that input
    exists for real. When a feature reads data another task/process was
    supposed to have already written, Validator must independently query the
    real running system against a real (not newly-created) object.
    
    project-sdd-planning (§10): when a task's acceptance depends on another
    task's real output, the Benchmark must name a real, specific object as the
    acceptance anchor, not just describe the mechanism.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Code »
  • …11fd-main-checkout-drift
    graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • graphlemon authored
     
    Browse Code »
  • …c231-main-checkout-drift
    graphlemon authored
     
    Browse Code »
  • _launch_candidate() spawns a real `python -m src.backend_server` process
    (`revived`) to re-verify the previous version during a post-cutover
    rollback. On the success path it's correctly left running -- it IS the new
    Live. But on every failure path (never bound live_port, failed
    re-verification, git revert conflict) nothing ever terminated it -- found
    via 31 accumulated orphaned processes on this machine, one per failed
    rollback_path_b call across many real test runs this session.
    
    This is also the likely cause of RollbackPathBFailureTest's own reported
    non-determinism (three independent real Validator sessions saw it fail,
    never reproduced manually): a leaked process from an earlier failed run
    can still be squatting on the port a later run's _find_free_port() picks,
    producing a false "alive" health-check reading.
    
    Fix: call deploy_cutover._stop_process(revived.pid) in both failure
    branches before raising, mirroring the pattern cutover() already uses for
    its own stop_timeout-bounded process teardown. Also fixes
    RollbackPathBHappyPathTest's own matching leak (it re-appended bad_live.pid
    a second time instead of tracking the actual revived process, a
    copy-paste bug, not real cleanup) by capturing the real pid via a thin
    _launch_candidate wrapper.
    
    31 pre-existing orphaned processes killed by hand; verified 5 repeated
    test runs after the fix leak zero new ones.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Code »