Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .loopdeck/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,8 @@ _Older decisions archived to [decisions-archive.md](./decisions-archive.md)._
- **Status**: accepted
- **Context**: Unattended Codex turns used read-only sandboxing with on-request approval, so Cargo could wait for a host popup that the worker UI could not show.
- **Consequences**: Autonomous turns use `workspaceWrite` with `approvalPolicy: on-request`; interactive turns retain read-only approval cards, while all requests remain subject to LoopDeck's destructive-command floor.

## 2026-10-30 — Execute run-plan phases one turn each with a ready-set scheduler
- **Status**: accepted
- **Context**: The run queue merged every queued phase into one LLM turn, so roles could not be attributed, budgeted per phase, or run concurrently. `prd-parallel-scheduling` makes the phase the unit of execution.
- **Consequences**: `scheduler.rs` validates the dependency graph (cycles rejected at plan creation and run start) and selects a ready set. Each phase runs in its own worktree and branch cut from the default branch, with its own provider session, token budget, and watchdog. `RunPlan.max_concurrency` defaults to four and is configurable in the plan-tonight picker. Dependents enter `Waiting` and are released only when every upstream is Completed or Delivered; a non-green upstream fails its dependents while independent in-flight phases continue. Restart reconciliation auto-resumes a phase only when its worktree and saved session both survive, and parks the rest with a reason. Dependents receive upstream output only as seeded handoff artifacts, never as unmerged branch code.
32 changes: 32 additions & 0 deletions .loopdeck/handoff-artifacts/parallel-scheduling-budget-split.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
artifact: parallel-scheduling-budget-split
author_role: planner
phase: parallel-scheduling/budget-split
type: report
created: 2026-10-30
summary: Token and wall-clock budgets apply per in-flight phase, so a budget kill ends only that phase and independent phases keep running.
cites:
- parallel-scheduling-one-turn-per-phase
---

## Summary
Each `PhaseTask` gets its own `TokenBudget` sized from `RunBudgets.per_phase_token_cap`, with no multiplication by batch size. Its watchdog is `min(phase wall-clock cap, remaining run wall-clock)`. `resolve_phase_token_cap` and `resolve_phase_timeout` keep their per-phase meaning. Token or wall-clock exhaustion marks only that phase Killed, fails its dependents, and lets siblings continue.

## Requirements
- R1: Every phase turn owns a separate `TokenBudget` built from the per-phase cap.
- R2: The per-phase wall-clock watchdog is the smaller of the phase cap and the run's remaining cap.
- R3: Exceeding either cap marks only that phase Killed with the budget message.
- R4: A budget kill fails the phase's dependents and does not stop independent in-flight phases.
- R5: The run-level wall-clock cap still stops every unfinished phase when it expires.
- R6: Per-phase token usage and wall-clock seconds are recorded on the phase for the morning report.

## Constraints
- C1: Budget enforcement reuses the existing `TokenBudget` and watchdog kill mechanics; no new timeout machinery.
- C2: The run-level cap remains a hard ceiling across all concurrent phases.

## Non-Goals
- Per-phase cap overrides distinct from the plan-level value.
- Global or cross-project token budgets.

## Open Questions
- Whether a phase should receive a partial refill when a sibling frees capacity is deferred.
32 changes: 32 additions & 0 deletions .loopdeck/handoff-artifacts/parallel-scheduling-gated-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
artifact: parallel-scheduling-gated-start
author_role: planner
phase: parallel-scheduling/gated-start
type: report
created: 2026-10-30
summary: Dependent phases hold in Waiting until every upstream phase lands green; a non-green upstream fails its Waiting dependents while independent in-flight phases continue.
cites:
- parallel-scheduling-parallel-batches
---

## Summary
`RunPhaseStatus::Waiting` is a real gated state. At run start `scheduler::mark_waiting` holds every queued phase with an unsatisfied dependency. `reconcile_gates` releases Waiting phases to Queued when all dependencies are green, and fails Waiting or Queued phases whose upstream ended Parked, Failed, Interrupted, or Killed. `fail_dependents` then cascades that failure to the rest of the chain. Running siblings are never touched.

## Requirements
- R1: A phase with an unsatisfied `depends_on` set is `Waiting`, not `Queued`, and is never selected for a turn.
- R2: When every dependency is Completed or Delivered, a Waiting phase becomes Queued and eligible immediately.
- R3: A non-green upstream fails its transitively dependent Waiting and Queued phases, and the payload names the upstream and its reason.
- R4: Independent in-flight phases continue after another phase parks, fails, or hits its budget.
- R5: `StallPolicy::Halt` additionally parks every remaining not-yet-started phase.
- R6: `transition_phase` accepts Queued→Waiting, Waiting→Queued, and Waiting→Running.

## Constraints
- C1: Gate reconciliation is pure and runs to a fixed point, so a failure can cascade within one pass.
- C2: A scheduler stall with no runnable phase fails the remaining phases instead of spinning.

## Non-Goals
- Releasing a dependent on a WARN or BLOCK verdict; only Completed or Delivered count as green.
- Interactive approval of a gated start.

## Open Questions
- Whether a UI control should let a user force-release a Waiting phase is deferred.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
artifact: parallel-scheduling-one-turn-per-phase
author_role: planner
phase: parallel-scheduling/one-turn-per-phase
type: report
created: 2026-10-30
summary: The run executor replaces the combined queued-batch turn with one independent turn per run-plan phase, each in its own worktree and branch.
cites: []
---

## Summary
`execute_run` no longer calls `next_queued_batch` or `build_combined_phase_prompt` with many loops. It prepares each ready phase as its own `PhaseTask`, builds a single-loop prompt for it, spawns one provider turn, and records that phase's own verdict, tokens, and wall clock. The plan is mutated only by the serialized scheduler loop.

## Requirements
- R1: Each run-plan phase executes in exactly one executor turn; no turn covers two phases.
- R2: Each phase gets its own prompt built from one loop location, its pinned interview answers, and its handoff topic.
- R3: Each phase records its own token usage and wall-clock seconds; nothing is divided across a batch.
- R4: The phase prompt names its own branch and forbids merging another phase's branch.
- R5: Only the scheduler loop writes `run-plan.yaml`; phase turns run in Tokio tasks and touch no plan state.
- R6: A missing `execution.yaml` focus slot is skipped, not treated as an error; `run-plan.yaml` stays authoritative per phase.

## Constraints
- C1: `AppState.claude_sessions` is keyed by worktree path, so one worktree per phase keeps concurrent turns isolated.
- C2: Pending question, permission, plan, and interrupt slots use the phase worktree as the control key.

## Non-Goals
- Concurrent fan-out and the Waiting gate, which are `parallel-scheduling/parallel-batches` and `gated-start`.
- Changes to the one-run-per-project admission lock.

## Open Questions
- Whether a phase should inherit an interactive `execution.yaml` current pointer when it is not the first started is left to the UI-focus design.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
artifact: parallel-scheduling-parallel-batches
author_role: planner
phase: parallel-scheduling/parallel-batches
type: report
created: 2026-10-30
summary: Phases with satisfied, disjoint depends_on run concurrently in separate worktrees under RunPlan.max_concurrency, default four and configurable in the picker.
cites:
- parallel-scheduling-one-turn-per-phase
---

## Summary
A ready-set scheduler replaces topological batches. `scheduler::select_ready` returns queued phases whose dependencies are all green, capped by spare concurrency. `execute_run` prepares each one in its own worktree cut from the default branch, spawns a Tokio task, and joins completions with `JoinSet`. `RunPlan.max_concurrency` defaults to four and the plan-tonight wizard exposes it.

## Requirements
- R1: `RunPlan.max_concurrency` is persisted and defaults to four; values outside 1..=16 are rejected at plan creation.
- R2: `scheduler::select_ready` returns only queued phases whose every `depends_on` phase is Completed or Delivered.
- R3: The executor starts ready phases until `max_concurrency` turns are in flight; a completion immediately frees a slot.
- R4: Each phase worktree is cut from the repository default branch; no phase stacks on a sibling's branch.
- R5: Each phase opens its own draft PR from its own branch.
- R6: The wizard passes the concurrency value through `createRunPlan` and shows it in the review summary.

## Constraints
- C1: Worktree creation stays in the serialized scheduler loop, so concurrent `git worktree add` calls cannot race.
- C2: One worktree per phase keeps provider sessions keyed by path and prevents cross-phase session sharing.

## Non-Goals
- Auto-merging concurrent branches; arbitration remains `prd-arbitration`.
- Cross-project scheduling and global budgets.

## Open Questions
- Whether a provider-level rate limit should further cap effective concurrency is deferred.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
artifact: parallel-scheduling-restart-reconcile
author_role: planner
phase: parallel-scheduling/restart-reconcile
type: report
created: 2026-10-30
summary: Restart reconciliation handles multiple in-flight phases, auto-resuming each phase whose worktree and saved provider session survive and parking the rest with a reason.
cites:
- parallel-scheduling-one-turn-per-phase
---

## Summary
`reconcile_running_phases` now classifies every `Running` phase instead of downgrading all of them to Interrupted. `scheduler::classify_interrupted_phase` requires both the phase worktree and a saved session id. A survivor returns to `Queued` with `resume_session_id` set; the executor spawns with `--resume` in the same worktree. Anything less parks with a reason that names what was lost.

## Requirements
- R1: Every `Running` phase on disk is reconciled on the next queue attempt or status read.
- R2: A phase whose worktree exists and whose session id is saved returns to Queued with that session pinned for resume.
- R3: The resumed turn spawns in the recorded worktree with the saved provider session.
- R4: A phase missing either survivor becomes Parked, and the payload names the missing piece.
- R5: Legacy plans fall back to the plan-level worktree and the active transcript's last session id.
- R6: `transition_phase` accepts Running→Queued for this auto-resume edge.

## Constraints
- C1: The decision is pure (`classify_interrupted_phase`) and unit-tested independently of the filesystem.
- C2: A stale `execution.yaml` current pointer left on an auto-resumed phase is abandoned so queueing can proceed.

## Non-Goals
- Resuming a provider process across an app restart; a new process resumes the saved session id.
- Automatic retry of a parked restarted phase.

## Open Questions
- Whether a resumed phase should replay its last partial turn or start a fresh instruction is still provider-defined.
34 changes: 34 additions & 0 deletions .loopdeck/handoff-artifacts/parallel-scheduling-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
artifact: parallel-scheduling-tests
author_role: planner
phase: parallel-scheduling/tests
type: report
created: 2026-10-30
summary: Scheduler property tests cover diamond graphs, cycle rejection, restart recovery, gates, and worktree cleanup isolation; the full suite passes.
cites:
- parallel-scheduling-parallel-batches
- parallel-scheduling-gated-start
- parallel-scheduling-restart-reconcile
---

## Summary
`src-tauri/src/scheduler.rs` ships fourteen unit tests: diamond validation and ordering, gate release only after both parents, transitive dependent failure, independent in-flight survival, Halt parking, cycle and dangling-edge rejection, concurrency caps, watchdog selection, restart decisions, branch uniqueness, and stall detection. `commands/run_queue.rs` adds worktree tests: disjoint phase paths, isolated cleanup, resume, recreation from a surviving branch, and default-branch basing.

## Requirements
- R1: A diamond graph validates, orders roots first, and releases its leaf only when both parents are green.
- R2: A cycle, a self-dependency, and a dangling edge are rejected before any worktree exists.
- R3: A non-green upstream fails transitively dependent phases while an independent in-flight phase keeps Running.
- R4: Restart reconciliation auto-resumes only when worktree and session both survive; otherwise it parks with a reason.
- R5: Two phase worktrees are disjoint, and removing one never touches its sibling.
- R6: The full Rust library suite and the frontend test suite pass.

## Constraints
- C1: Scheduler tests are pure and need no provider process or Tauri state.
- C2: Worktree tests use scratch git repositories under the system temp directory.

## Non-Goals
- Live provider turns in the automated suite; the role demo remains an explicitly ignored test.
- Load or soak testing above four concurrent phases.

## Open Questions
- Whether the ignored role demo should become a scheduled integration test is deferred.
18 changes: 16 additions & 2 deletions .loopdeck/loops.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ _Older loops archived to [loops-archive.md](./loops-archive.md)._

## Current

- **Status**: in_progress — `provider-contract` Phases 2–3 are implemented in worktree `run/provider-contract-normalize-claude-plus3-1aa529a93261`: both adapters dual-emit normalized `RunEvent`s, ts-rs bindings are CI-drift-checked, the run-queue consumer migrated, fixture/frontend contract tests pass, and the verify gate is PASS. Draft PR #110 is open.
- **Next**: review and merge draft PR #110; then start the next queued `execution-foundations` PRD.
- **Status**: in_progress — all six `prd-parallel-scheduling` loops are implemented in worktree `run/parallel-scheduling-one-turn-per-phase-plus5-d13a619fe69b`: per-phase turns, dependency-aware fan-out with per-phase worktrees, the `Waiting` gate, multi-phase restart reconciliation, per-phase budgets, and scheduler property/worktree tests. Verify gate is PASS; draft PR #113 is open.
- **Next**: review and merge the parallel-scheduling draft PR; then run a real overnight plan with two disjoint phases to confirm concurrent draft PRs on the user's machine.

## Next Steps
- [x] Resolve PR #113 against agent-handoff support on main; preserve bounded injection, citation attribution, and waiting-phase restart recovery.
- [ ] Split `commands/run_queue.rs` and `run_executor.rs` (both exceed 1,500 lines).
- [ ] Review & merge: https://github.com/suprie/loopdeck/pull/113
- [x] `role-based-orchestration` / `prd-parallel-scheduling` — all six loops (one-turn-per-phase, parallel-batches, gated-start, restart-reconcile, budget-split, tests).
- [x] Fix the Claude control_response schema and commit it.
- [x] Create a fresh run worktree and plan from `feat/provider-event-fixtures`.
- [x] `execution-foundations` / `prd-provider-contract` Phase 2 — normalize Claude and Codex provider output into the versioned `RunEvent` envelope.
Expand All @@ -28,6 +32,11 @@ _Older loops archived to [loops-archive.md](./loops-archive.md)._

## History

### 2026-10-30 — role-based-orchestration / prd-parallel-scheduling (all six loops)
- **Status**: completed
- **Completed**: 2026-10-30
- **Summary**: The run executor now fires one turn per run-plan phase instead of one combined queued-batch turn. A new `scheduler.rs` validates the dependency graph (cycles, self-edges, and dangling edges are rejected at plan creation and again at run start), holds dependents in a persisted `Waiting` state, and releases them only when every upstream is Completed or Delivered. Ready phases fan out concurrently under `RunPlan.max_concurrency` (default 4, configurable in the plan-tonight picker), each in its own worktree and branch cut from the default branch, each with its own provider session, token budget, and wall-clock watchdog. A non-green upstream fails its Waiting dependents while independent in-flight phases continue. Restart reconciliation now classifies every `Running` phase, auto-resuming only when worktree and saved session both survive and parking the rest with a reason. Scheduler property tests cover generated DAGs, cycle rejection, and restart decisions; worktree tests prove disjoint paths and isolated cleanup. `cargo test` (747 passed), `cargo clippy --all-targets` (0 errors), `npx tsc --noEmit`, `npm run build`, and `npm run test:frontend` (34 passed) are green.

### 2026-10-28 — execution-foundations / prd-provider-contract Phases 2–3
- **Status**: completed
- **Completed**: 2026-10-28
Expand Down Expand Up @@ -99,6 +108,11 @@ _Older loops archived to [loops-archive.md](./loops-archive.md)._

## History

### 2026-10-30 — role-based-orchestration / prd-parallel-scheduling (all six loops)
- **Status**: completed
- **Completed**: 2026-10-30
- **Summary**: The run executor now fires one turn per run-plan phase instead of one combined queued-batch turn. A new `scheduler.rs` validates the dependency graph (cycles, self-edges, and dangling edges are rejected at plan creation and again at run start), holds dependents in a persisted `Waiting` state, and releases them only when every upstream is Completed or Delivered. Ready phases fan out concurrently under `RunPlan.max_concurrency` (default 4, configurable in the plan-tonight picker), each in its own worktree and branch cut from the default branch, each with its own provider session, token budget, and wall-clock watchdog. A non-green upstream fails its Waiting dependents while independent in-flight phases continue. Restart reconciliation now classifies every `Running` phase, auto-resuming only when worktree and saved session both survive and parking the rest with a reason. Scheduler property tests cover generated DAGs, cycle rejection, and restart decisions; worktree tests prove disjoint paths and isolated cleanup. `cargo test` (747 passed), `cargo clippy --all-targets` (0 errors), `npx tsc --noEmit`, `npm run build`, and `npm run test:frontend` (34 passed) are green.

### 2026-08-30 — Memory hygiene: 3,000-token budget + compaction
- **Status**: completed
- **Completed**: 2026-08-30
Expand Down
Loading
Loading