diff --git a/checkpoint.json b/checkpoint.json index a50ebda53..6dd892bd2 100644 --- a/checkpoint.json +++ b/checkpoint.json @@ -1,26 +1,30 @@ { - "timestamp": "2026-03-20T21:30:57.855582Z", - "current_node": "toolchain", + "timestamp": "2026-03-20T21:32:22.973101Z", + "current_node": "preflight_compile", "completed_nodes": [ "start", - "toolchain" + "toolchain", + "preflight_compile" ], "node_retries": { + "preflight_compile": 1, "start": 1, "toolchain": 1 }, "context_values": { - "internal.thread_id": "start", - "current_node": "toolchain", + "internal.thread_id": "toolchain", + "current_node": "preflight_compile", "graph.goal": "# Plan: Support Anthropic fast mode (`speed: fast`) — Issue #120\n\n## Context\n\nAnthropic's API supports [fast mode](https://platform.claude.com/docs/en/build-with-claude/fast-mode) (beta) for Claude Opus 4.6 — up to 2.5x faster output at 6x pricing ($30/$150 per MTok vs $5/$25 standard). The API requires:\n- `\"speed\": \"fast\"` top-level field in request body\n- `anthropic-beta: fast-mode-2026-02-01` header\n- Response includes `usage.speed` confirming actual speed used\n\nThis follows the same pattern as `reasoning_effort`, which is already implemented end-to-end.\n\n## Implementation\n\n### 1. Foundation: `lib/crates/fabro-llm/src/types.rs`\n\n- Add `speed: Option` to `Request` (after `reasoning_effort`, line ~454)\n- Add `speed: Option` to `Usage` (before `raw`, line ~372)\n- In `Usage::Add` impl (line ~389): propagate `speed` from `self` (left-hand side wins, since all requests in a session use the same speed)\n\n### 2. Graph layer: `lib/crates/fabro-graphviz/src/graph/types.rs`\n\nAdd after `reasoning_effort()` (~line 211):\n```rust\n#[must_use]\npub fn speed(&self) -> Option<&str> {\n self.str_attr(\"speed\")\n}\n```\nReturns `Option` (no default) — unlike `reasoning_effort` which defaults to \"high\", speed defaults to None (standard) since fast mode is 6x cost.\n\n### 3. Stylesheet: `lib/crates/fabro-workflows/src/stylesheet.rs`\n\nAdd `\"speed\"` to `STYLESHEET_PROPERTIES` array (line 5):\n```rust\nconst STYLESHEET_PROPERTIES: &[&str] = &[\"model\", \"provider\", \"reasoning_effort\", \"speed\", \"backend\"];\n```\n\n### 4. Agent config: `lib/crates/fabro-agent/src/config.rs`\n\n- Add `pub speed: Option` field to `SessionConfig` (line ~66)\n- Add `speed: None` to `Default` impl (line ~135)\n\n### 5. Session wiring: `lib/crates/fabro-agent/src/session.rs`\n\n- Add `speed: self.config.speed.clone()` to Request construction (~line 908)\n- Add `set_speed()` method analogous to `set_reasoning_effort()` (~line 458)\n\n### 6. Anthropic provider: `lib/crates/fabro-llm/src/providers/anthropic.rs`\n\n**6a. `ApiRequest` struct** (~line 87): Add `speed: Option` field\n\n**6b. Beta header constant** (~line 510):\n```rust\nconst FAST_MODE_BETA_HEADER: &str = \"fast-mode-2026-02-01\";\n```\n\n**6c. `build_beta_header()`** (~line 582): Add `include_fast_mode_header: bool` parameter. Inject fast-mode header (same pattern as cache header).\n\n**6d. `build_api_request()`** (~line 1082):\n- Set `speed: request.speed.clone()` on `ApiRequest`\n- Compute `let is_fast = request.speed.as_deref() == Some(\"fast\")`\n- Pass `is_fast` to `build_beta_header()`\n\n**6e. `ApiUsage`** (~line 160): Add `speed: Option` field\n\n**6f. Response parsing**: Map `api_resp.usage.speed` → `Usage.speed` in both:\n- Non-streaming path (~line 1192)\n- Streaming `message_start` handler (~line 690)\n\n### 7. Usage tracking: `lib/crates/fabro-workflows/src/outcome.rs`\n\n- Add `speed: Option` to `StageUsage` (with `serde(default, skip_serializing_if)`)\n- Update `From<&StageUsage> for Usage` to include `speed`\n\n### 8. Cost multiplier: `lib/crates/fabro-workflows/src/cost.rs`\n\nApply 6x multiplier when `speed == \"fast\"`:\n```rust\nlet multiplier = if usage.speed.as_deref() == Some(\"fast\") { 6.0 } else { 1.0 };\n// Apply multiplier to final cost\n```\n\n### 9. Backend wiring: `lib/crates/fabro-workflows/src/backend/api.rs`\n\n- SessionConfig construction (~line 195): add `speed: node.speed().map(String::from)`\n- Prompt-mode Request construction (~line 285): add `speed: node.speed().map(String::from)`\n- Both StageUsage constructions (~lines 389, 589): add `speed: response.usage.speed.clone()` / `total_usage.speed.clone()`\n\n### 10. Tests\n\n- `anthropic.rs`: `build_api_request_sets_speed`, `build_api_request_injects_fast_mode_beta_header`, `beta_header_includes_both_cache_and_fast_mode`\n- `cost.rs`: `compute_stage_cost_fast_mode_6x_multiplier`\n- `stylesheet.rs`: `apply_speed_property`\n- `session.rs`: `set_speed_mid_session`\n- Update all existing struct literals (`Request`, `Usage`, `SessionConfig`, `StageUsage`, `ApiRequest`, `ApiUsage`) in tests to include `speed: None`\n\n## Implementation order\n\n1. `types.rs` (Request + Usage) — everything depends on this\n2. `graph/types.rs` + `stylesheet.rs` — graph layer\n3. `config.rs` + `session.rs` — agent layer\n4. `anthropic.rs` — provider (ApiRequest, beta header, response parsing)\n5. `outcome.rs` + `cost.rs` — usage tracking\n6. `backend/api.rs` — final wiring\n7. Tests throughout\n\n## Verification\n\n```\ncargo build --workspace\ncargo test --workspace\ncargo fmt --check --all\ncargo clippy --workspace -- -D warnings\n```\n", "internal.node_visit_count": 1, "thread.start.current_node": "toolchain", - "command.output": "cargo 1.94.0 (85eff7c80 2026-01-15)\n", + "command.output": "", "failure_signature": "", "internal.retry_count.start": 1, "command.stderr": "", + "internal.retry_count.preflight_compile": 1, "graph.model_stylesheet": "\n * { backend: api; model: claude-opus-4-6;}\n ", - "current.preamble": "Goal: # Plan: Support Anthropic fast mode (`speed: fast`) — Issue #120\n\n## Context\n\nAnthropic's API supports [fast mode](https://platform.claude.com/docs/en/build-with-claude/fast-mode) (beta) for Claude Opus 4.6 — up to 2.5x faster output at 6x pricing ($30/$150 per MTok vs $5/$25 standard). The API requires:\n- `\"speed\": \"fast\"` top-level field in request body\n- `anthropic-beta: fast-mode-2026-02-01` header\n- Response includes `usage.speed` confirming actual speed used\n\nThis follows the same pattern as `reasoning_effort`, which is already implemented end-to-end.\n\n## Implementation\n\n### 1. Foundation: `lib/crates/fabro-llm/src/types.rs`\n\n- Add `speed: Option` to `Request` (after `reasoning_effort`, line ~454)\n- Add `speed: Option` to `Usage` (before `raw`, line ~372)\n- In `Usage::Add` impl (line ~389): propagate `speed` from `self` (left-hand side wins, since all requests in a session use the same speed)\n\n### 2. Graph layer: `lib/crates/fabro-graphviz/src/graph/types.rs`\n\nAdd after `reasoning_effort()` (~line 211):\n```rust\n#[must_use]\npub fn speed(&self) -> Option<&str> {\n self.str_attr(\"speed\")\n}\n```\nReturns `Option` (no default) — unlike `reasoning_effort` which defaults to \"high\", speed defaults to None (standard) since fast mode is 6x cost.\n\n### 3. Stylesheet: `lib/crates/fabro-workflows/src/stylesheet.rs`\n\nAdd `\"speed\"` to `STYLESHEET_PROPERTIES` array (line 5):\n```rust\nconst STYLESHEET_PROPERTIES: &[&str] = &[\"model\", \"provider\", \"reasoning_effort\", \"speed\", \"backend\"];\n```\n\n### 4. Agent config: `lib/crates/fabro-agent/src/config.rs`\n\n- Add `pub speed: Option` field to `SessionConfig` (line ~66)\n- Add `speed: None` to `Default` impl (line ~135)\n\n### 5. Session wiring: `lib/crates/fabro-agent/src/session.rs`\n\n- Add `speed: self.config.speed.clone()` to Request construction (~line 908)\n- Add `set_speed()` method analogous to `set_reasoning_effort()` (~line 458)\n\n### 6. Anthropic provider: `lib/crates/fabro-llm/src/providers/anthropic.rs`\n\n**6a. `ApiRequest` struct** (~line 87): Add `speed: Option` field\n\n**6b. Beta header constant** (~line 510):\n```rust\nconst FAST_MODE_BETA_HEADER: &str = \"fast-mode-2026-02-01\";\n```\n\n**6c. `build_beta_header()`** (~line 582): Add `include_fast_mode_header: bool` parameter. Inject fast-mode header (same pattern as cache header).\n\n**6d. `build_api_request()`** (~line 1082):\n- Set `speed: request.speed.clone()` on `ApiRequest`\n- Compute `let is_fast = request.speed.as_deref() == Some(\"fast\")`\n- Pass `is_fast` to `build_beta_header()`\n\n**6e. `ApiUsage`** (~line 160): Add `speed: Option` field\n\n**6f. Response parsing**: Map `api_resp.usage.speed` → `Usage.speed` in both:\n- Non-streaming path (~line 1192)\n- Streaming `message_start` handler (~line 690)\n\n### 7. Usage tracking: `lib/crates/fabro-workflows/src/outcome.rs`\n\n- Add `speed: Option` to `StageUsage` (with `serde(default, skip_serializing_if)`)\n- Update `From<&StageUsage> for Usage` to include `speed`\n\n### 8. Cost multiplier: `lib/crates/fabro-workflows/src/cost.rs`\n\nApply 6x multiplier when `speed == \"fast\"`:\n```rust\nlet multiplier = if usage.speed.as_deref() == Some(\"fast\") { 6.0 } else { 1.0 };\n// Apply multiplier to final cost\n```\n\n### 9. Backend wiring: `lib/crates/fabro-workflows/src/backend/api.rs`\n\n- SessionConfig construction (~line 195): add `speed: node.speed().map(String::from)`\n- Prompt-mode Request construction (~line 285): add `speed: node.speed().map(String::from)`\n- Both StageUsage constructions (~lines 389, 589): add `speed: response.usage.speed.clone()` / `total_usage.speed.clone()`\n\n### 10. Tests\n\n- `anthropic.rs`: `build_api_request_sets_speed`, `build_api_request_injects_fast_mode_beta_header`, `beta_header_includes_both_cache_and_fast_mode`\n- `cost.rs`: `compute_stage_cost_fast_mode_6x_multiplier`\n- `stylesheet.rs`: `apply_speed_property`\n- `session.rs`: `set_speed_mid_session`\n- Update all existing struct literals (`Request`, `Usage`, `SessionConfig`, `StageUsage`, `ApiRequest`, `ApiUsage`) in tests to include `speed: None`\n\n## Implementation order\n\n1. `types.rs` (Request + Usage) — everything depends on this\n2. `graph/types.rs` + `stylesheet.rs` — graph layer\n3. `config.rs` + `session.rs` — agent layer\n4. `anthropic.rs` — provider (ApiRequest, beta header, response parsing)\n5. `outcome.rs` + `cost.rs` — usage tracking\n6. `backend/api.rs` — final wiring\n7. Tests throughout\n\n## Verification\n\n```\ncargo build --workspace\ncargo test --workspace\ncargo fmt --check --all\ncargo clippy --workspace -- -D warnings\n```\n\n", + "current.preamble": "Goal: # Plan: Support Anthropic fast mode (`speed: fast`) — Issue #120\n\n## Context\n\nAnthropic's API supports [fast mode](https://platform.claude.com/docs/en/build-with-claude/fast-mode) (beta) for Claude Opus 4.6 — up to 2.5x faster output at 6x pricing ($30/$150 per MTok vs $5/$25 standard). The API requires:\n- `\"speed\": \"fast\"` top-level field in request body\n- `anthropic-beta: fast-mode-2026-02-01` header\n- Response includes `usage.speed` confirming actual speed used\n\nThis follows the same pattern as `reasoning_effort`, which is already implemented end-to-end.\n\n## Implementation\n\n### 1. Foundation: `lib/crates/fabro-llm/src/types.rs`\n\n- Add `speed: Option` to `Request` (after `reasoning_effort`, line ~454)\n- Add `speed: Option` to `Usage` (before `raw`, line ~372)\n- In `Usage::Add` impl (line ~389): propagate `speed` from `self` (left-hand side wins, since all requests in a session use the same speed)\n\n### 2. Graph layer: `lib/crates/fabro-graphviz/src/graph/types.rs`\n\nAdd after `reasoning_effort()` (~line 211):\n```rust\n#[must_use]\npub fn speed(&self) -> Option<&str> {\n self.str_attr(\"speed\")\n}\n```\nReturns `Option` (no default) — unlike `reasoning_effort` which defaults to \"high\", speed defaults to None (standard) since fast mode is 6x cost.\n\n### 3. Stylesheet: `lib/crates/fabro-workflows/src/stylesheet.rs`\n\nAdd `\"speed\"` to `STYLESHEET_PROPERTIES` array (line 5):\n```rust\nconst STYLESHEET_PROPERTIES: &[&str] = &[\"model\", \"provider\", \"reasoning_effort\", \"speed\", \"backend\"];\n```\n\n### 4. Agent config: `lib/crates/fabro-agent/src/config.rs`\n\n- Add `pub speed: Option` field to `SessionConfig` (line ~66)\n- Add `speed: None` to `Default` impl (line ~135)\n\n### 5. Session wiring: `lib/crates/fabro-agent/src/session.rs`\n\n- Add `speed: self.config.speed.clone()` to Request construction (~line 908)\n- Add `set_speed()` method analogous to `set_reasoning_effort()` (~line 458)\n\n### 6. Anthropic provider: `lib/crates/fabro-llm/src/providers/anthropic.rs`\n\n**6a. `ApiRequest` struct** (~line 87): Add `speed: Option` field\n\n**6b. Beta header constant** (~line 510):\n```rust\nconst FAST_MODE_BETA_HEADER: &str = \"fast-mode-2026-02-01\";\n```\n\n**6c. `build_beta_header()`** (~line 582): Add `include_fast_mode_header: bool` parameter. Inject fast-mode header (same pattern as cache header).\n\n**6d. `build_api_request()`** (~line 1082):\n- Set `speed: request.speed.clone()` on `ApiRequest`\n- Compute `let is_fast = request.speed.as_deref() == Some(\"fast\")`\n- Pass `is_fast` to `build_beta_header()`\n\n**6e. `ApiUsage`** (~line 160): Add `speed: Option` field\n\n**6f. Response parsing**: Map `api_resp.usage.speed` → `Usage.speed` in both:\n- Non-streaming path (~line 1192)\n- Streaming `message_start` handler (~line 690)\n\n### 7. Usage tracking: `lib/crates/fabro-workflows/src/outcome.rs`\n\n- Add `speed: Option` to `StageUsage` (with `serde(default, skip_serializing_if)`)\n- Update `From<&StageUsage> for Usage` to include `speed`\n\n### 8. Cost multiplier: `lib/crates/fabro-workflows/src/cost.rs`\n\nApply 6x multiplier when `speed == \"fast\"`:\n```rust\nlet multiplier = if usage.speed.as_deref() == Some(\"fast\") { 6.0 } else { 1.0 };\n// Apply multiplier to final cost\n```\n\n### 9. Backend wiring: `lib/crates/fabro-workflows/src/backend/api.rs`\n\n- SessionConfig construction (~line 195): add `speed: node.speed().map(String::from)`\n- Prompt-mode Request construction (~line 285): add `speed: node.speed().map(String::from)`\n- Both StageUsage constructions (~lines 389, 589): add `speed: response.usage.speed.clone()` / `total_usage.speed.clone()`\n\n### 10. Tests\n\n- `anthropic.rs`: `build_api_request_sets_speed`, `build_api_request_injects_fast_mode_beta_header`, `beta_header_includes_both_cache_and_fast_mode`\n- `cost.rs`: `compute_stage_cost_fast_mode_6x_multiplier`\n- `stylesheet.rs`: `apply_speed_property`\n- `session.rs`: `set_speed_mid_session`\n- Update all existing struct literals (`Request`, `Usage`, `SessionConfig`, `StageUsage`, `ApiRequest`, `ApiUsage`) in tests to include `speed: None`\n\n## Implementation order\n\n1. `types.rs` (Request + Usage) — everything depends on this\n2. `graph/types.rs` + `stylesheet.rs` — graph layer\n3. `config.rs` + `session.rs` — agent layer\n4. `anthropic.rs` — provider (ApiRequest, beta header, response parsing)\n5. `outcome.rs` + `cost.rs` — usage tracking\n6. `backend/api.rs` — final wiring\n7. Tests throughout\n\n## Verification\n\n```\ncargo build --workspace\ncargo test --workspace\ncargo fmt --check --all\ncargo clippy --workspace -- -D warnings\n```\n\n\n## Completed stages\n- **toolchain**: success\n - 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`\n - Stdout:\n ```\n cargo 1.94.0 (85eff7c80 2026-01-15)\n ```\n - Stderr: (empty)\n", + "thread.toolchain.current_node": "preflight_compile", "failure_class": "", "outcome": "success", "internal.retry_count.toolchain": 1, @@ -34,6 +38,15 @@ "status": "success", "duration_ms": 0 }, + "preflight_compile": { + "status": "success", + "context_updates": { + "command.output": "", + "command.stderr": "" + }, + "notes": "Script completed: cargo check -q --workspace 2>&1", + "duration_ms": 80882 + }, "toolchain": { "status": "success", "context_updates": { @@ -44,9 +57,10 @@ "duration_ms": 218 } }, - "next_node_id": "preflight_compile", + "next_node_id": "preflight_lint", "node_visits": { "start": 1, + "preflight_compile": 1, "toolchain": 1 } } \ No newline at end of file diff --git a/nodes/preflight_compile/script_invocation.json b/nodes/preflight_compile/script_invocation.json new file mode 100644 index 000000000..ccbef36c7 --- /dev/null +++ b/nodes/preflight_compile/script_invocation.json @@ -0,0 +1,5 @@ +{ + "command": "cargo check -q --workspace 2>&1", + "language": "shell", + "timeout_ms": null +} \ No newline at end of file diff --git a/nodes/preflight_compile/script_timing.json b/nodes/preflight_compile/script_timing.json new file mode 100644 index 000000000..6b0e5f447 --- /dev/null +++ b/nodes/preflight_compile/script_timing.json @@ -0,0 +1,5 @@ +{ + "duration_ms": 80880, + "exit_code": 0, + "timed_out": false +} \ No newline at end of file diff --git a/nodes/preflight_compile/status.json b/nodes/preflight_compile/status.json new file mode 100644 index 000000000..a4785999a --- /dev/null +++ b/nodes/preflight_compile/status.json @@ -0,0 +1,6 @@ +{ + "status": "success", + "notes": "Script completed: cargo check -q --workspace 2>&1", + "failure_reason": null, + "timestamp": "2026-03-20T21:32:22.972444+00:00" +} \ No newline at end of file