mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-06 08:18:58 +00:00
parent
902e303c40
commit
805bf4c261
6 changed files with 244 additions and 7 deletions
File diff suppressed because one or more lines are too long
91
nodes/implement/prompt.md
Normal file
91
nodes/implement/prompt.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
Goal: # Canonical `Blocked` Run Status Plan
|
||||
|
||||
## Summary
|
||||
|
||||
- Make `Blocked` a first-class shared run status across the durable projection, server, OpenAPI, generated TypeScript client, web UI, and CLI.
|
||||
- Keep `Paused` separate. `Paused` remains operator intent; `Blocked` means the run cannot proceed until an external condition is resolved.
|
||||
- This is a full status-unification pass: align the shared contract on `submitted`, `queued`, `starting`, `running`, `blocked`, `paused`, `removing`, `completed`, `failed`, and `cancelled`; remove `dead` from the canonical serialized lifecycle.
|
||||
- No alerting/email in this pass. `BlockedReason` is introduced now so notification work can key off a stable domain contract later.
|
||||
|
||||
## Key Changes
|
||||
|
||||
- Canonical status contract: update [docs/api-reference/fabro-api.yaml](/Users/bhelmkamp/p/fabro-sh/fabro/docs/api-reference/fabro-api.yaml), [lib/crates/fabro-types/src/status.rs](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/status.rs), and the generated models under `lib/packages/fabro-api-client/src/models/`.
|
||||
- Public/internal type changes:
|
||||
- Add `Queued`, `Blocked`, `Completed`, and `Cancelled` to the shared Rust `RunStatus`.
|
||||
- Rename shared/internal `Succeeded` usages to `Completed`.
|
||||
- Add nullable `blocked_reason` with a new `BlockedReason` enum; initial value set is `human_input_required`.
|
||||
- Remove `Dead` from OpenAPI and generated API/client status enums. Callers that currently fall back to `Dead` must instead treat status as missing/unknown locally.
|
||||
- Add `blocked` to the `RunStatus` and `InternalRunStatus` enums in `fabro-api.yaml`.
|
||||
- Projection and summary behavior: update [lib/crates/fabro-store/src/run_state.rs](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-store/src/run_state.rs), `lib/crates/fabro-store/src/types.rs`, and `lib/crates/fabro-store/src/slate/mod.rs`.
|
||||
- Persist `Queued` as a real durable state by appending/projecting a `run.queued` transition when a run is start-requested and enqueued.
|
||||
- Project `run.failed` with `reason=cancelled` to canonical `Cancelled`.
|
||||
- Set canonical `Blocked` on `interview.started` with `blocked_reason=human_input_required`.
|
||||
- Clear `blocked_reason` and return to `Running` on `interview.completed`, `interview.timeout`, or `interview.interrupted` when no pending interviews remain.
|
||||
- Keep `Paused` driven only by pause/unpause control events; interview events must never produce `Paused`.
|
||||
- Update transition helpers so `Blocked` is non-terminal and `Completed`/`Failed`/`Cancelled` are terminal.
|
||||
- Server/live read model: update [lib/crates/fabro-server/src/server.rs](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-server/src/server.rs) and `lib/crates/fabro-server/src/demo/mod.rs`.
|
||||
- Remove the ad-hoc API remap layer; server responses should expose the canonical shared status directly.
|
||||
- Extend run status payloads and durable summaries to include `blocked_reason` alongside `status_reason` and `pending_control`.
|
||||
- Extend `update_live_run_from_event()` so `InterviewStarted` drives `Blocked`, and interview resolution (`InterviewCompleted`/`InterviewTimeout`/`InterviewInterrupted`) returns live runs to `Running` when no pending interviews remain.
|
||||
- Keep `/runs/{id}/questions` and answer submission unchanged; those endpoints remain the detailed question surface behind a blocked run.
|
||||
- Board/UI model:
|
||||
- Change board columns to `working`, `blocked`, `review`, `merge`.
|
||||
- Map `Running` and `Paused` to `working`; map `Blocked` to `blocked`; map `Completed` to `merge`; keep `Submitted`, `Queued`, `Starting`, `Failed`, and `Cancelled` off-board.
|
||||
- Keep paused runs in the working lane with no extra indicator in this pass.
|
||||
- Update web mappings in `apps/fabro-web/app/{data/runs.ts,routes/run-detail.tsx,routes/runs.tsx}` so `blocked` is a real lifecycle/board value and `waiting` is removed.
|
||||
- Because this pass does not add a new `run.blocked` event family, update `STATUS_EVENTS` in `apps/fabro-web/app/routes/runs.tsx` to include `interview.started`, `interview.completed`, `interview.timeout`, and `interview.interrupted` as status-affecting events.
|
||||
- CLI consumers: update `lib/crates/fabro-cli/src/{commands/run/wait.rs,commands/runs/list.rs,server_runs.rs}`.
|
||||
- Replace `Succeeded`/`Dead` handling with `Completed` plus explicit missing-status handling.
|
||||
- Add display/color handling for `Blocked`, `Queued`, and `Cancelled`.
|
||||
|
||||
## Test Plan
|
||||
|
||||
- `lib/crates/fabro-store/src/run_state.rs`:
|
||||
- `interview.started` sets `status=Blocked` and `blocked_reason=HumanInputRequired`.
|
||||
- interview completion/timeout/interruption returns the run to `Running` when no pending interviews remain.
|
||||
- pause/unpause still yields `Paused`/`Running` and never routes through `Blocked`.
|
||||
- cancelled failures project to `Cancelled`.
|
||||
- queued state round-trips through projection serialization.
|
||||
- `lib/crates/fabro-store/src/slate/mod.rs` and `lib/crates/fabro-server/src/server.rs`:
|
||||
- durable summaries and `/runs/{id}` responses expose unified statuses plus `blocked_reason`.
|
||||
- no serialized API/store status is `dead`.
|
||||
- live managed runs enter `Blocked` while a pending interview exists.
|
||||
- board response emits a `blocked` column, places blocked runs there with question text, and keeps paused runs in `working`.
|
||||
- `apps/fabro-web/app/data/runs.test.ts` and a new `apps/fabro-web/app/routes/runs.test.tsx`:
|
||||
- summary mapping accepts `blocked`, `paused`, `completed`, and `cancelled`.
|
||||
- blocked runs render in the blocked lane with the existing answer-question affordance.
|
||||
- paused runs stay in the working lane.
|
||||
- no UI code depends on `waiting`.
|
||||
- CLI tests in `lib/crates/fabro-cli/src/commands/run/wait.rs` and `lib/crates/fabro-cli/src/commands/runs/list.rs`:
|
||||
- `Completed` is the success exit state.
|
||||
- `Blocked`, `Queued`, and `Cancelled` render correctly.
|
||||
- missing status no longer masquerades as `Dead`.
|
||||
- `Succeeded` is no longer accepted or displayed; all success paths use `Completed`.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- `BlockedReason` starts with one value only: `human_input_required`.
|
||||
- Notification behavior is intentionally deferred; this plan only makes blocked state canonical and queryable.
|
||||
- `RunListItem.question` stays optional and unchanged in shape; `Blocked` plus `question` is sufficient for current UI behavior.
|
||||
- `Paused` remains visible in the working board column for now; the paused-specific visual indicator is a separate follow-up.
|
||||
|
||||
|
||||
## Completed stages
|
||||
- **toolchain**: success
|
||||
- Script: `command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1`
|
||||
- Stdout:
|
||||
```
|
||||
cargo 1.94.0 (85eff7c80 2026-01-15)
|
||||
```
|
||||
- Stderr: (empty)
|
||||
- **preflight_compile**: success
|
||||
- Script: `cargo check -q --workspace 2>&1`
|
||||
- Stdout: (empty)
|
||||
- Stderr: (empty)
|
||||
- **preflight_lint**: success
|
||||
- Script: `cargo clippy -q --workspace -- -D warnings 2>&1`
|
||||
- Stdout: (empty)
|
||||
- Stderr: (empty)
|
||||
|
||||
|
||||
Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.
|
||||
5
nodes/implement/provider_used.json
Normal file
5
nodes/implement/provider_used.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"mode": "agent",
|
||||
"provider": "anthropic",
|
||||
"model": "claude-opus-4-6"
|
||||
}
|
||||
58
nodes/implement/response.md
Normal file
58
nodes/implement/response.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
All tests pass. Let me now create a summary of all the changes made:
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
### 1. Core Types (`lib/crates/fabro-types/src/status.rs`)
|
||||
- **`RunStatus` enum**: Replaced `Succeeded`/`Dead` with `Queued`, `Blocked`, `Completed`, `Cancelled`
|
||||
- **`BlockedReason` enum**: Added with initial value `HumanInputRequired`
|
||||
- **`RunStatusRecord`**: Added `blocked_reason: Option<BlockedReason>` field; added `blocked()` constructor
|
||||
- **`is_terminal()`**: Now returns true for `Completed | Failed | Cancelled`
|
||||
- **`is_active()`**: Now includes `Queued`, `Blocked`
|
||||
- **`can_transition_to()`**: Updated to support new states; any non-terminal → `Cancelled`
|
||||
- **`Display`/`FromStr`**: Updated for all new variants
|
||||
- Export `BlockedReason` from `lib.rs`
|
||||
|
||||
### 2. OpenAPI Spec (`docs/api-reference/fabro-api.yaml`)
|
||||
- **`RunStatus`**: Added `blocked`, `removing`; removed `dead`/`succeeded` (now `completed`/`cancelled`)
|
||||
- **`InternalRunStatus`**: Added `queued`, `blocked`, `completed`, `cancelled`; removed `succeeded`/`dead`
|
||||
- **`BoardColumn`**: Changed from `working/initializing/review/merge` to `working/blocked/review/merge`
|
||||
- **`BlockedReason`**: New enum with `human_input_required`
|
||||
- Added `blocked_reason` field to `RunStatusResponse`, `RunStatusRecord`, and `StoreRunSummary`
|
||||
|
||||
### 3. Store Layer (`lib/crates/fabro-store/`)
|
||||
- **`RunSummary`**: Added `blocked_reason` field
|
||||
- **`RunProjection`**:
|
||||
- `InterviewStarted` → sets `Blocked` with `blocked_reason: HumanInputRequired`
|
||||
- `InterviewCompleted/Timeout/Interrupted` → returns to `Running` when no pending interviews remain
|
||||
- `RunFailed(reason=Cancelled)` → projects to `Cancelled` status
|
||||
- `RunCompleted` → projects to `Completed` (was `Succeeded`)
|
||||
- `build_summary()` includes `blocked_reason`
|
||||
- **Tests**: 12 new tests covering blocked/unblocked transitions, cancelled projection, queued serialization, blocked_reason round-trips
|
||||
|
||||
### 4. Server (`lib/crates/fabro-server/`)
|
||||
- **`board_column()`**: `Running/Paused` → `working`; `Blocked` → `blocked`; `Completed` → `merge`; others off-board
|
||||
- **`board_columns()`**: Returns `working/blocked/review/merge`
|
||||
- **`api_status_from_workflow()`**: Maps all new statuses including `Queued`, `Blocked`, `Cancelled`
|
||||
- **`update_live_run_from_event()`**: `InterviewStarted` → `Blocked`; interview resolution → `Running`
|
||||
- **`should_reconcile_run_on_startup()`**: Includes `Queued` and `Blocked`
|
||||
- All `RunStatusResponse` constructors include `blocked_reason: None`
|
||||
- Demo data updated (`Initializing` → `Blocked` for runs with questions)
|
||||
- Test assertions updated for new status names
|
||||
|
||||
### 5. CLI (`lib/crates/fabro-cli/`)
|
||||
- **`wait.rs`**: `Completed` is success exit; `Cancelled` displayed; missing status → `Failed`
|
||||
- **`list.rs`**: Added colors for `Queued`, `Blocked`, `Cancelled`; removed `Dead`
|
||||
- **`server_runs.rs`**: Missing status defaults to `Failed` instead of `Dead`
|
||||
- **`attach.rs`**: Success check uses `Completed`
|
||||
- All snapshot tests updated: `Succeeded` → `Completed`, `Dead` → removed
|
||||
|
||||
### 6. Workflow Engine (`lib/crates/fabro-workflow/`)
|
||||
- `finalize.rs`: Success maps to `Completed` (was `Succeeded`)
|
||||
- `resume.rs`: Completion check uses `Completed`
|
||||
- `run_lookup.rs`: Missing status defaults to `Failed`
|
||||
|
||||
### 7. TypeScript/Web (`apps/fabro-web/`, `lib/packages/fabro-api-client/`)
|
||||
- **Generated client**: Updated `BoardColumn`, `InternalRunStatus`, `RunStatus`, `RunStatusRecord`, `RunStatusResponse`; added `BlockedReason`
|
||||
- **`data/runs.ts`**: `ColumnStatus` simplified to `working/blocked/review/merge`; `RunStatus` includes all canonical values; removed `dead`/`succeeded`/`waiting`
|
||||
- **`routes/runs.tsx`**: Column styles updated; `STATUS_EVENTS` includes interview events
|
||||
- **Tests**: New `isRunStatus` tests verify `blocked/paused/completed/cancelled` accepted, `dead/succeeded/waiting` rejected
|
||||
7
nodes/preflight_lint/script_timing.json
Normal file
7
nodes/preflight_lint/script_timing.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"exit_code": 0,
|
||||
"duration_ms": 24136,
|
||||
"timed_out": false
|
||||
}
|
||||
6
nodes/preflight_lint/status.json
Normal file
6
nodes/preflight_lint/status.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"status": "success",
|
||||
"notes": "Script completed: cargo clippy -q --workspace -- -D warnings 2>&1",
|
||||
"failure_reason": null,
|
||||
"timestamp": "2026-04-15T15:54:35.496046Z"
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue