mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
parent
6f85a400af
commit
9856629f93
5 changed files with 67 additions and 8 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"timestamp": "2026-03-16T02:20:47.588217Z",
|
||||
"current_node": "simplify_gpt",
|
||||
"timestamp": "2026-03-16T02:22:16.076142Z",
|
||||
"current_node": "verify",
|
||||
"completed_nodes": [
|
||||
"start",
|
||||
"toolchain",
|
||||
|
|
@ -9,7 +9,8 @@
|
|||
"implement",
|
||||
"simplify_opus",
|
||||
"simplify_gemini",
|
||||
"simplify_gpt"
|
||||
"simplify_gpt",
|
||||
"verify"
|
||||
],
|
||||
"node_retries": {
|
||||
"implement": 1,
|
||||
|
|
@ -19,20 +20,23 @@
|
|||
"preflight_lint": 1,
|
||||
"preflight_compile": 1,
|
||||
"start": 1,
|
||||
"verify": 1,
|
||||
"toolchain": 1
|
||||
},
|
||||
"context_values": {
|
||||
"response.simplify_gemini": "Everything passes. Here's the summary:\n\n## Summary\n\n**Fixed: Duplicate subprocess orchestration** (the main issue found across all three reviews)\n\n`render_dot_svg` in `server.rs` was a near-copy of `render_dot` in `graph.rs` — both spawned a `dot` subprocess, piped DOT source to stdin, injected style defaults, and post-processed SVG output. The only difference was sync vs async and error types.\n\n**Changes made:**\n\n1. **`graph.rs`**: Made `render_dot` public (the single authoritative implementation). Made `inject_dot_style_defaults` and `postprocess_svg` private — they're implementation details of `render_dot`, not a two-phase protocol callers need to know about.\n\n2. **`server.rs`**: Replaced 35 lines of duplicated subprocess code in `render_dot_svg` with a 10-line function that calls `render_dot` via `tokio::task::spawn_blocking` and converts the result to a `Response`. The demo handler (`demo/mod.rs`) already delegates to `render_dot_svg`, so no changes needed there.\n\n**Skipped (not worth addressing):**\n- Efficiency findings in `postprocess_svg` (line-by-line filtering, `insert_str` shift) — SVGs are ~KB and subprocess launch dominates; not worth the complexity.\n- Extracting color constants — the coupling between DOT attrs and CSS selectors is inherent and co-locating the values wouldn't improve readability.",
|
||||
"internal.thread_id": "simplify_gemini",
|
||||
"internal.retry_count.verify": 1,
|
||||
"internal.thread_id": "simplify_gpt",
|
||||
"internal.retry_count.start": 1,
|
||||
"thread.implement.current_node": "simplify_opus",
|
||||
"current.preamble": "Goal: # Style SVG output from `fabro graph` and API\n\n## Context\n\nSVGs from `fabro graph` use raw Graphviz defaults (black strokes, white bg, Times-serif). The docs SVGs look much better: teal nodes, gray edges, Helvetica font, transparent bg, dark mode CSS. The goal is to make `fabro graph` and the API `get_graph` endpoint produce the same styled output without building a layout engine.\n\n## Approach: DOT defaults + SVG post-processing\n\n### Step 1: `inject_dot_style_defaults(source: &str) -> String` in `graph.rs`\n\nFind first `{`, insert after it:\n```dot\n bgcolor=\"transparent\"\n node [color=\"#357f9e\", fontname=\"Helvetica\", fontsize=12, fontcolor=\"#1a1a1a\"]\n edge [color=\"#666666\", fontname=\"Helvetica\", fontsize=10, fontcolor=\"#666666\"]\n```\nThese are DOT defaults — per-node/edge attrs override them. Applies to both SVG and PNG.\n\n### Step 2: `postprocess_svg(raw: Vec<u8>) -> Vec<u8>` in `graph.rs`\n\nSVG-only. Two string operations:\n1. Remove line containing `<polygon fill=\"white\" stroke=\"none\"` (the white background)\n2. Insert dark mode `<style>` block after the `<svg ...>` closing `>`\n\n### Step 3: Wire into `render_dot()` in `graph.rs`\n\n- Call `inject_dot_style_defaults` on source before piping to `dot`\n- Call `postprocess_svg` on output when format is SVG\n\n### Step 4: Wire into `get_graph()` in `server.rs` (line ~1440, ~1467, ~1472)\n\n- Wrap `dot_source` with `inject_dot_style_defaults`\n- Wrap `output.stdout` with `postprocess_svg`\n\n### Step 5: Wire into `get_run_graph()` in `demo/mod.rs` (line ~227, ~248, ~252)\n\nSame two wraps.\n\n### Step 6: Tests in `graph.rs`\n\n- `inject_dot_style_defaults` inserts expected attrs\n- `inject_dot_style_defaults` returns unchanged if no `{`\n- `postprocess_svg` removes white background\n- `postprocess_svg` injects dark mode style block\n- Update `graph_valid_workflow_svg` to assert styled output\n\n## Files to modify\n\n1. `lib/crates/fabro-workflows/src/cli/graph.rs` — add 2 pub fns, modify `render_dot`, add tests\n2. `lib/crates/fabro-api/src/server.rs` — 2-line change in `get_graph`\n3. `lib/crates/fabro-api/src/demo/mod.rs` — 2-line change in `get_run_graph`\n\n## Verification\n\n1. `cargo test -p fabro-workflows -- graph` — unit tests pass\n2. `cargo test -p fabro-api` — API tests pass\n3. `cargo clippy --workspace -- -D warnings` — no warnings\n4. `fabro graph fabro/workflows/implement/workflow.fabro -o /tmp/test.svg` — visually inspect:\n - Teal node strokes, gray edges, Helvetica font\n - No white background\n - Dark mode `<style>` block present\n - Compare with `docs/images/tutorial-plan-implement.svg`\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- **preflight_compile**: success\n - Script: `cargo check -q --workspace 2>&1`\n - Stdout: (empty)\n - Stderr: (empty)\n- **preflight_lint**: success\n - Script: `cargo clippy -q --workspace -- -D warnings 2>&1`\n - Stdout: (empty)\n - Stderr: (empty)\n- **implement**: success\n - Model: claude-opus-4-6, 37.6k tokens in / 7.8k out\n - Files: /home/daytona/workspace/lib/crates/fabro-api/src/demo/mod.rs, /home/daytona/workspace/lib/crates/fabro-api/src/server.rs, /home/daytona/workspace/lib/crates/fabro-workflows/src/cli/graph.rs\n- **simplify_opus**: success\n - Model: claude-opus-4-6, 36.2k tokens in / 13.5k out\n - Files: /home/daytona/workspace/lib/crates/fabro-api/src/demo/mod.rs, /home/daytona/workspace/lib/crates/fabro-api/src/server.rs, /home/daytona/workspace/lib/crates/fabro-workflows/src/cli/graph.rs\n- **simplify_gemini**: success\n - Model: claude-opus-4-6, 36.3k tokens in / 14.7k out\n - Files: /home/daytona/workspace/lib/crates/fabro-api/src/server.rs, /home/daytona/workspace/lib/crates/fabro-workflows/src/cli/graph.rs\n",
|
||||
"current.preamble": "Goal: # Style SVG output from `fabro graph` and API\n\n## Context\n\nSVGs from `fabro graph` use raw Graphviz defaults (black strokes, white bg, Times-serif). The docs SVGs look much better: teal nodes, gray edges, Helvetica font, transparent bg, dark mode CSS. The goal is to make `fabro graph` and the API `get_graph` endpoint produce the same styled output without building a layout engine.\n\n## Approach: DOT defaults + SVG post-processing\n\n### Step 1: `inject_dot_style_defaults(source: &str) -> String` in `graph.rs`\n\nFind first `{`, insert after it:\n```dot\n bgcolor=\"transparent\"\n node [color=\"#357f9e\", fontname=\"Helvetica\", fontsize=12, fontcolor=\"#1a1a1a\"]\n edge [color=\"#666666\", fontname=\"Helvetica\", fontsize=10, fontcolor=\"#666666\"]\n```\nThese are DOT defaults — per-node/edge attrs override them. Applies to both SVG and PNG.\n\n### Step 2: `postprocess_svg(raw: Vec<u8>) -> Vec<u8>` in `graph.rs`\n\nSVG-only. Two string operations:\n1. Remove line containing `<polygon fill=\"white\" stroke=\"none\"` (the white background)\n2. Insert dark mode `<style>` block after the `<svg ...>` closing `>`\n\n### Step 3: Wire into `render_dot()` in `graph.rs`\n\n- Call `inject_dot_style_defaults` on source before piping to `dot`\n- Call `postprocess_svg` on output when format is SVG\n\n### Step 4: Wire into `get_graph()` in `server.rs` (line ~1440, ~1467, ~1472)\n\n- Wrap `dot_source` with `inject_dot_style_defaults`\n- Wrap `output.stdout` with `postprocess_svg`\n\n### Step 5: Wire into `get_run_graph()` in `demo/mod.rs` (line ~227, ~248, ~252)\n\nSame two wraps.\n\n### Step 6: Tests in `graph.rs`\n\n- `inject_dot_style_defaults` inserts expected attrs\n- `inject_dot_style_defaults` returns unchanged if no `{`\n- `postprocess_svg` removes white background\n- `postprocess_svg` injects dark mode style block\n- Update `graph_valid_workflow_svg` to assert styled output\n\n## Files to modify\n\n1. `lib/crates/fabro-workflows/src/cli/graph.rs` — add 2 pub fns, modify `render_dot`, add tests\n2. `lib/crates/fabro-api/src/server.rs` — 2-line change in `get_graph`\n3. `lib/crates/fabro-api/src/demo/mod.rs` — 2-line change in `get_run_graph`\n\n## Verification\n\n1. `cargo test -p fabro-workflows -- graph` — unit tests pass\n2. `cargo test -p fabro-api` — API tests pass\n3. `cargo clippy --workspace -- -D warnings` — no warnings\n4. `fabro graph fabro/workflows/implement/workflow.fabro -o /tmp/test.svg` — visually inspect:\n - Teal node strokes, gray edges, Helvetica font\n - No white background\n - Dark mode `<style>` block present\n - Compare with `docs/images/tutorial-plan-implement.svg`\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- **preflight_compile**: success\n - Script: `cargo check -q --workspace 2>&1`\n - Stdout: (empty)\n - Stderr: (empty)\n- **preflight_lint**: success\n - Script: `cargo clippy -q --workspace -- -D warnings 2>&1`\n - Stdout: (empty)\n - Stderr: (empty)\n- **implement**: success\n - Model: claude-opus-4-6, 37.6k tokens in / 7.8k out\n - Files: /home/daytona/workspace/lib/crates/fabro-api/src/demo/mod.rs, /home/daytona/workspace/lib/crates/fabro-api/src/server.rs, /home/daytona/workspace/lib/crates/fabro-workflows/src/cli/graph.rs\n- **simplify_opus**: success\n - Model: claude-opus-4-6, 36.2k tokens in / 13.5k out\n - Files: /home/daytona/workspace/lib/crates/fabro-api/src/demo/mod.rs, /home/daytona/workspace/lib/crates/fabro-api/src/server.rs, /home/daytona/workspace/lib/crates/fabro-workflows/src/cli/graph.rs\n- **simplify_gemini**: success\n - Model: claude-opus-4-6, 36.3k tokens in / 14.7k out\n - Files: /home/daytona/workspace/lib/crates/fabro-api/src/server.rs, /home/daytona/workspace/lib/crates/fabro-workflows/src/cli/graph.rs\n- **simplify_gpt**: success\n - Model: claude-opus-4-6, 31.5k tokens in / 8.8k out\n - Files: /home/daytona/workspace/lib/crates/fabro-workflows/src/cli/graph.rs\n",
|
||||
"internal.run_id": "01KKT5VWYY8VQFETNRC7RTYKDW",
|
||||
"failure_class": "",
|
||||
"thread.simplify_gpt.current_node": "verify",
|
||||
"response.simplify_gpt": "The code is clean. Here's a summary of the review:\n\n**Fixed:**\n- **Long format string in `inject_dot_style_defaults`** (graph.rs line 100): Extracted the inline DOT defaults into a `DOT_STYLE_DEFAULTS` const using a `r##\"...\"##` raw string literal, matching the style of the existing `DARK_MODE_STYLE` const. The `format!` call is now a clean `format!(\"{before}{DOT_STYLE_DEFAULTS}{after}\")` instead of a 200+ character single line with embedded `\\n` escapes.\n\n**Already clean (no action needed):**\n- **Code reuse**: The `render_dot_svg` helper in `server.rs` properly deduplicates the inline dot rendering that was previously copy-pasted in both `server.rs` and `demo/mod.rs`. No other SVG/DOT utilities exist in the codebase.\n- **Color coupling**: `#357f9e` and `#666666` appear in both DOT defaults and dark mode CSS selectors — this is by design (CSS targets the DOT-generated attributes).\n- **Efficiency**: The `postprocess_svg` string allocations are negligible for SVG sizes (a few KB). The `spawn_blocking` approach is appropriate since `render_dot` does synchronous I/O + string processing.\n- **No copy-paste, no parameter sprawl, no leaky abstractions** — the change is well-structured.",
|
||||
"internal.fidelity": "compact",
|
||||
"graph.rankdir": "LR",
|
||||
"command.output": "",
|
||||
"command.output": "────────────\n Nextest run ID 00650f53-a777-49fa-9ce6-5ed9806a0a51 with nextest profile: default\n Starting 3399 tests across 38 binaries (183 tests skipped)\n────────────\n Summary [ 18.548s] 3399 tests run: 3399 passed, 183 skipped\n",
|
||||
"graph.model_stylesheet": "\n * { backend: api; model: claude-opus-4-6;}\n ",
|
||||
"failure_signature": "",
|
||||
"thread.start.current_node": "toolchain",
|
||||
|
|
@ -46,7 +50,7 @@
|
|||
"outcome": "success",
|
||||
"thread.simplify_opus.current_node": "simplify_gemini",
|
||||
"internal.node_visit_count": 1,
|
||||
"current_node": "simplify_gpt",
|
||||
"current_node": "verify",
|
||||
"internal.retry_count.simplify_opus": 1,
|
||||
"internal.retry_count.toolchain": 1,
|
||||
"command.stderr": "",
|
||||
|
|
@ -156,6 +160,15 @@
|
|||
"notes": "Script completed: cargo check -q --workspace 2>&1",
|
||||
"duration_ms": 72647
|
||||
},
|
||||
"verify": {
|
||||
"status": "success",
|
||||
"context_updates": {
|
||||
"command.stderr": "",
|
||||
"command.output": "────────────\n Nextest run ID 00650f53-a777-49fa-9ce6-5ed9806a0a51 with nextest profile: default\n Starting 3399 tests across 38 binaries (183 tests skipped)\n────────────\n Summary [ 18.548s] 3399 tests run: 3399 passed, 183 skipped\n"
|
||||
},
|
||||
"notes": "Script completed: cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1",
|
||||
"duration_ms": 86142
|
||||
},
|
||||
"simplify_opus": {
|
||||
"status": "success",
|
||||
"context_updates": {
|
||||
|
|
@ -185,9 +198,10 @@
|
|||
"duration_ms": 0
|
||||
}
|
||||
},
|
||||
"next_node_id": "verify",
|
||||
"next_node_id": "fmt",
|
||||
"node_visits": {
|
||||
"implement": 1,
|
||||
"verify": 1,
|
||||
"preflight_compile": 1,
|
||||
"simplify_gemini": 1,
|
||||
"simplify_gpt": 1,
|
||||
|
|
|
|||
29
nodes/simplify_gpt/diff.patch
Normal file
29
nodes/simplify_gpt/diff.patch
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
diff --git a/lib/crates/fabro-workflows/src/cli/graph.rs b/lib/crates/fabro-workflows/src/cli/graph.rs
|
||||
index 7f07a1c..ea73cd4 100644
|
||||
--- a/lib/crates/fabro-workflows/src/cli/graph.rs
|
||||
+++ b/lib/crates/fabro-workflows/src/cli/graph.rs
|
||||
@@ -88,6 +88,13 @@ const DARK_MODE_STYLE: &str = r##"
|
||||
}
|
||||
</style>"##;
|
||||
|
||||
+/// DOT graph-level defaults injected after the first `{`.
|
||||
+const DOT_STYLE_DEFAULTS: &str = r##"
|
||||
+ bgcolor="transparent"
|
||||
+ node [color="#357f9e", fontname="Helvetica", fontsize=12, fontcolor="#1a1a1a"]
|
||||
+ edge [color="#666666", fontname="Helvetica", fontsize=10, fontcolor="#666666"]
|
||||
+"##;
|
||||
+
|
||||
/// Inject DOT graph-level style defaults (transparent background, teal nodes,
|
||||
/// gray edges, Helvetica font) right after the first `{` in the DOT source.
|
||||
/// Per-node/edge attributes override these defaults.
|
||||
@@ -96,9 +103,7 @@ fn inject_dot_style_defaults(source: &str) -> String {
|
||||
return source.to_string();
|
||||
};
|
||||
let (before, after) = source.split_at(pos + 1);
|
||||
- format!(
|
||||
- "{before}\n bgcolor=\"transparent\"\n node [color=\"#357f9e\", fontname=\"Helvetica\", fontsize=12, fontcolor=\"#1a1a1a\"]\n edge [color=\"#666666\", fontname=\"Helvetica\", fontsize=10, fontcolor=\"#666666\"]\n{after}"
|
||||
- )
|
||||
+ format!("{before}{DOT_STYLE_DEFAULTS}{after}")
|
||||
}
|
||||
|
||||
/// Post-process raw SVG output from Graphviz:
|
||||
5
nodes/verify/script_invocation.json
Normal file
5
nodes/verify/script_invocation.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"command": "cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1",
|
||||
"language": "shell",
|
||||
"timeout_ms": null
|
||||
}
|
||||
5
nodes/verify/script_timing.json
Normal file
5
nodes/verify/script_timing.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"duration_ms": 86139,
|
||||
"exit_code": 0,
|
||||
"timed_out": false
|
||||
}
|
||||
6
nodes/verify/status.json
Normal file
6
nodes/verify/status.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"status": "success",
|
||||
"notes": "Script completed: cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1",
|
||||
"failure_reason": null,
|
||||
"timestamp": "2026-03-16T02:22:16.074523+00:00"
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue