diff --git a/checkpoint.json b/checkpoint.json new file mode 100644 index 000000000..050160a3f --- /dev/null +++ b/checkpoint.json @@ -0,0 +1,52 @@ +{ + "timestamp": "2026-03-21T15:48:10.888360Z", + "current_node": "toolchain", + "completed_nodes": [ + "start", + "toolchain" + ], + "node_retries": { + "toolchain": 1, + "start": 1 + }, + "context_values": { + "internal.run_id": "01KM8HA16H7HVBT2P63W2FSWT5", + "current.preamble": "Goal: # Fix: Workflow TOML config lost in detach mode\n\n## Context\n\nWhen running `fabro run -d implement-plan`, the `[pull_request]` config from `fabro.toml` / `cli.toml` / `workflow.toml` is silently dropped, so no PR is created despite `enabled = true` at all config levels.\n\n**Root cause chain**:\n1. `create.rs` tries to copy the workflow TOML as `run.toml`, but checks the **raw CLI arg** (`\"implement-plan\"` — no extension) instead of the resolved path. So `run.toml` is never saved.\n2. `RunEngine` always uses cached `graph.fabro` (a DOT file), so `prepare_workflow` returns `run_cfg = None`, losing all TOML-level config.\n3. `pull_request` and `asset_globs` in `RunConfig` only check `run_cfg` without falling back to `run_defaults`.\n\n## Fix 1: Serialize merged `run_cfg` to `run.toml` in create.rs\n\n**File**: `lib/crates/fabro-cli/src/commands/create.rs`\n\nReplace the raw-file-copy block (lines 53-58) with serialization of the already-merged `WorkflowRunConfig`:\n\n- Change `let prep = prepare_workflow(...)` to `let mut prep = ...`\n- Replace the extension check with:\n ```rust\n if let Some(mut cfg) = prep.run_cfg.take() {\n cfg.graph = \"graph.fabro\".to_string();\n let toml_str = toml::to_string_pretty(&cfg)\n .context(\"Failed to serialize run config\")?;\n tokio::fs::write(run_dir.join(\"run.toml\"), toml_str).await?;\n }\n ```\n- Add `use anyhow::Context;` if needed\n\n**Why serialize instead of copy**: The raw TOML's `graph` field (e.g. `\"workflow.fabro\"`) would point to a nonexistent file in the run dir. Serializing lets us rewrite `graph` to `\"graph.fabro\"` (the cached name). The serialized config also has all defaults merged, env vars resolved, and dockerfiles inlined — making the run dir self-contained.\n\n**Why `take()` not `clone()`**: `WorkflowRunConfig` doesn't derive `Clone`, and `prep.run_cfg` is unused after this point in `create.rs`.\n\n## Fix 2: Use `run.toml` in RunEngine path\n\n**File**: `lib/crates/fabro-cli/src/main.rs` (lines 727-733)\n\nReplace the workflow path resolution to use `run.toml`:\n\n```rust\nlet cached_toml = run_dir.join(\"run.toml\");\nlet workflow_path = if cached_toml.exists() {\n cached_toml\n} else {\n run_dir.join(\"graph.fabro\")\n};\n```\n\nWhen `run.toml` exists, `prepare_workflow` → `resolve_workflow` sees `.toml`, calls `load_run_config`, and `resolve_graph_path` resolves `\"graph.fabro\"` relative to the run dir — pointing to the cached graph that already exists there.\n\n## Fix 3: Add `run_defaults` fallbacks (defense-in-depth)\n\n**File**: `lib/crates/fabro-cli/src/commands/run.rs`\n\nEven with Fixes 1+2, bare `.fabro` files passed directly would still hit `run_cfg = None`. Add fallbacks matching the pattern already used elsewhere in the file:\n\n**3a. `pull_request`** (line 1424-1428):\n```rust\npull_request: run_cfg\n .as_ref()\n .and_then(|c| c.pull_request.as_ref())\n .or(run_defaults.pull_request.as_ref())\n .filter(|p| p.enabled)\n .cloned(),\n```\n\n**3b. `asset_globs`** (line 1429-1433):\n```rust\nasset_globs: run_cfg\n .as_ref()\n .and_then(|c| c.assets.as_ref())\n .or(run_defaults.assets.as_ref())\n .map(|a| a.include.clone())\n .unwrap_or_default(),\n```\n\n**3c. `devcontainer`** (line 969-973):\n```rust\nlet devcontainer_config = if run_cfg\n .as_ref()\n .and_then(|c| c.sandbox.as_ref())\n .or(run_defaults.sandbox.as_ref())\n .and_then(|s| s.devcontainer)\n .unwrap_or(false)\n```\n\n**3d. `sandbox.env`** (line 1300-1306):\n```rust\nif let Some(toml_env) = run_cfg\n .as_ref()\n .and_then(|c| c.sandbox.as_ref())\n .or(run_defaults.sandbox.as_ref())\n .and_then(|s| s.env.clone())\n```\n\n## Verification\n\n1. `cargo build --workspace`\n2. `cargo test --workspace`\n3. `cargo clippy --workspace -- -D warnings`\n4. Manual: `fabro run -d implement-plan` with `[pull_request] enabled = true` in `fabro.toml` → verify `run.toml` in run dir has `graph = \"graph.fabro\"` and `[pull_request]` → verify PR created\n\n", + "graph.goal": "# Fix: Workflow TOML config lost in detach mode\n\n## Context\n\nWhen running `fabro run -d implement-plan`, the `[pull_request]` config from `fabro.toml` / `cli.toml` / `workflow.toml` is silently dropped, so no PR is created despite `enabled = true` at all config levels.\n\n**Root cause chain**:\n1. `create.rs` tries to copy the workflow TOML as `run.toml`, but checks the **raw CLI arg** (`\"implement-plan\"` — no extension) instead of the resolved path. So `run.toml` is never saved.\n2. `RunEngine` always uses cached `graph.fabro` (a DOT file), so `prepare_workflow` returns `run_cfg = None`, losing all TOML-level config.\n3. `pull_request` and `asset_globs` in `RunConfig` only check `run_cfg` without falling back to `run_defaults`.\n\n## Fix 1: Serialize merged `run_cfg` to `run.toml` in create.rs\n\n**File**: `lib/crates/fabro-cli/src/commands/create.rs`\n\nReplace the raw-file-copy block (lines 53-58) with serialization of the already-merged `WorkflowRunConfig`:\n\n- Change `let prep = prepare_workflow(...)` to `let mut prep = ...`\n- Replace the extension check with:\n ```rust\n if let Some(mut cfg) = prep.run_cfg.take() {\n cfg.graph = \"graph.fabro\".to_string();\n let toml_str = toml::to_string_pretty(&cfg)\n .context(\"Failed to serialize run config\")?;\n tokio::fs::write(run_dir.join(\"run.toml\"), toml_str).await?;\n }\n ```\n- Add `use anyhow::Context;` if needed\n\n**Why serialize instead of copy**: The raw TOML's `graph` field (e.g. `\"workflow.fabro\"`) would point to a nonexistent file in the run dir. Serializing lets us rewrite `graph` to `\"graph.fabro\"` (the cached name). The serialized config also has all defaults merged, env vars resolved, and dockerfiles inlined — making the run dir self-contained.\n\n**Why `take()` not `clone()`**: `WorkflowRunConfig` doesn't derive `Clone`, and `prep.run_cfg` is unused after this point in `create.rs`.\n\n## Fix 2: Use `run.toml` in RunEngine path\n\n**File**: `lib/crates/fabro-cli/src/main.rs` (lines 727-733)\n\nReplace the workflow path resolution to use `run.toml`:\n\n```rust\nlet cached_toml = run_dir.join(\"run.toml\");\nlet workflow_path = if cached_toml.exists() {\n cached_toml\n} else {\n run_dir.join(\"graph.fabro\")\n};\n```\n\nWhen `run.toml` exists, `prepare_workflow` → `resolve_workflow` sees `.toml`, calls `load_run_config`, and `resolve_graph_path` resolves `\"graph.fabro\"` relative to the run dir — pointing to the cached graph that already exists there.\n\n## Fix 3: Add `run_defaults` fallbacks (defense-in-depth)\n\n**File**: `lib/crates/fabro-cli/src/commands/run.rs`\n\nEven with Fixes 1+2, bare `.fabro` files passed directly would still hit `run_cfg = None`. Add fallbacks matching the pattern already used elsewhere in the file:\n\n**3a. `pull_request`** (line 1424-1428):\n```rust\npull_request: run_cfg\n .as_ref()\n .and_then(|c| c.pull_request.as_ref())\n .or(run_defaults.pull_request.as_ref())\n .filter(|p| p.enabled)\n .cloned(),\n```\n\n**3b. `asset_globs`** (line 1429-1433):\n```rust\nasset_globs: run_cfg\n .as_ref()\n .and_then(|c| c.assets.as_ref())\n .or(run_defaults.assets.as_ref())\n .map(|a| a.include.clone())\n .unwrap_or_default(),\n```\n\n**3c. `devcontainer`** (line 969-973):\n```rust\nlet devcontainer_config = if run_cfg\n .as_ref()\n .and_then(|c| c.sandbox.as_ref())\n .or(run_defaults.sandbox.as_ref())\n .and_then(|s| s.devcontainer)\n .unwrap_or(false)\n```\n\n**3d. `sandbox.env`** (line 1300-1306):\n```rust\nif let Some(toml_env) = run_cfg\n .as_ref()\n .and_then(|c| c.sandbox.as_ref())\n .or(run_defaults.sandbox.as_ref())\n .and_then(|s| s.env.clone())\n```\n\n## Verification\n\n1. `cargo build --workspace`\n2. `cargo test --workspace`\n3. `cargo clippy --workspace -- -D warnings`\n4. Manual: `fabro run -d implement-plan` with `[pull_request] enabled = true` in `fabro.toml` → verify `run.toml` in run dir has `graph = \"graph.fabro\"` and `[pull_request]` → verify PR created\n", + "graph.rankdir": "LR", + "internal.fidelity": "compact", + "internal.retry_count.toolchain": 1, + "current_node": "toolchain", + "thread.start.current_node": "toolchain", + "failure_signature": "", + "outcome": "success", + "internal.node_visit_count": 1, + "failure_class": "", + "internal.thread_id": "start", + "internal.retry_count.start": 1, + "command.stderr": "", + "command.output": "cargo 1.94.0 (85eff7c80 2026-01-15)\n", + "graph.model_stylesheet": "\n * { model: claude-opus-4-6; }\n " + }, + "logs": [], + "node_outcomes": { + "start": { + "status": "success", + "duration_ms": 0 + }, + "toolchain": { + "status": "success", + "context_updates": { + "command.stderr": "", + "command.output": "cargo 1.94.0 (85eff7c80 2026-01-15)\n" + }, + "notes": "Script completed: 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", + "duration_ms": 97 + } + }, + "next_node_id": "preflight_compile", + "node_visits": { + "toolchain": 1, + "start": 1 + } +} \ No newline at end of file diff --git a/nodes/start/status.json b/nodes/start/status.json new file mode 100644 index 000000000..c6204037c --- /dev/null +++ b/nodes/start/status.json @@ -0,0 +1,6 @@ +{ + "status": "success", + "notes": null, + "failure_reason": null, + "timestamp": "2026-03-21T15:48:10.789738+00:00" +} \ No newline at end of file diff --git a/nodes/toolchain/script_invocation.json b/nodes/toolchain/script_invocation.json new file mode 100644 index 000000000..d68c414c4 --- /dev/null +++ b/nodes/toolchain/script_invocation.json @@ -0,0 +1,5 @@ +{ + "command": "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", + "language": "shell", + "timeout_ms": null +} \ No newline at end of file diff --git a/nodes/toolchain/script_timing.json b/nodes/toolchain/script_timing.json new file mode 100644 index 000000000..855db1f65 --- /dev/null +++ b/nodes/toolchain/script_timing.json @@ -0,0 +1,5 @@ +{ + "duration_ms": 87, + "exit_code": 0, + "timed_out": false +} \ No newline at end of file diff --git a/nodes/toolchain/status.json b/nodes/toolchain/status.json new file mode 100644 index 000000000..cd2b71a3b --- /dev/null +++ b/nodes/toolchain/status.json @@ -0,0 +1,6 @@ +{ + "status": "success", + "notes": "Script completed: 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", + "failure_reason": null, + "timestamp": "2026-03-21T15:48:10.888134+00:00" +} \ No newline at end of file