From 4a5f9f698fd7585eef2596c6599e57480c7adbd0 Mon Sep 17 00:00:00 2001 From: "brynary-fabro[bot]" <265161896+brynary-fabro[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:27:03 -0400 Subject: [PATCH] Strip markdown headings and "Plan:" prefix from GOAL column in `fabro ps` (#111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change cleans up the `GOAL` column in `fabro ps` output by stripping common markdown noise before displaying goal text. Specifically, `truncate_goal` now removes leading `#` heading markers and an optional `Plan:` prefix (applied in order, so `## Plan: migrate DB` correctly becomes `migrate DB`). Whitespace is trimmed after each strip step to avoid leading spaces in the result. As a minor refactor, the truncation logic was extracted into a standalone `truncate_str` helper. This allows `truncate_goal` to delegate cleanly after preprocessing, and also lets the workflow name column (which doesn't need heading/prefix stripping) call `truncate_str` directly. Five new unit tests cover the stripping behavior, combined stripping, passthrough of plain text, and that truncation still applies correctly after stripping. ### Fabro Details
Ran 9 stages in 9m 0s for $1.17 | Stage | Duration | Cost | Retries | |---|---|---|---| | start | 0s | – | 0 | | toolchain | 0s | – | 0 | | preflight_compile | 1m 10s | – | 0 | | preflight_lint | 12s | – | 0 | | implement | 2m 28s | $0.42 | 0 | | simplify_opus | 3m 20s | $0.75 | 0 | | simplify_gpt | 0s | – | 0 | | verify | 1m 20s | – | 0 | | fmt | 1s | – | 0 | | **Total** | **9m 0s** | **$1.17** | **0** |
Ran ImplementAndSimplify.fabro (12 nodes and 15 edges) ```dot digraph ImplementAndSimplify { graph [ goal="Implement and simplify", model_stylesheet=" * { backend: api; model: claude-opus-4-6;} " ] rankdir=LR start [shape=Mdiamond, label="Start"] exit [shape=Msquare, label="Exit"] toolchain [label="Toolchain", shape=parallelogram, 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", max_retries=0] preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0] preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1", max_retries=0] fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3] implement [label="Implement", prompt="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."] simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"] simplify_gpt [label="Simplify (GPT-54)", prompt="@prompts/simplify.md", model="gpt-54"] verify [label="Verify", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1", goal_gate=true, retry_target="fixup"] fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings and test failures.", max_visits=3] fmt [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", max_retries=0] start -> toolchain toolchain -> preflight_compile [condition="outcome=success"] toolchain -> exit preflight_compile -> preflight_lint [condition="outcome=success"] preflight_compile -> exit preflight_lint -> implement [condition="outcome=success"] preflight_lint -> fix_lints fix_lints -> preflight_lint implement -> simplify_opus -> simplify_gpt -> verify verify -> fmt [condition="outcome=success"] verify -> fixup fixup -> verify fmt -> exit } ```
⚒️ Generated with [Fabro](https://fabro.sh) --------- Co-authored-by: Fabro --- lib/crates/fabro-cli/src/commands/runs.rs | 44 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/lib/crates/fabro-cli/src/commands/runs.rs b/lib/crates/fabro-cli/src/commands/runs.rs index 129b0c5c5..22632964e 100644 --- a/lib/crates/fabro-cli/src/commands/runs.rs +++ b/lib/crates/fabro-cli/src/commands/runs.rs @@ -228,11 +228,17 @@ fn short_run_id(id: &str) -> &str { fn truncate_goal(goal: &str, max_len: usize) -> String { let line = goal.lines().next().unwrap_or(""); - let char_count = line.chars().count(); + let line = line.trim_start_matches('#').trim(); + let line = line.strip_prefix("Plan:").map(|s| s.trim()).unwrap_or(line); + truncate_str(line, max_len) +} + +fn truncate_str(s: &str, max_len: usize) -> String { + let char_count = s.chars().count(); if char_count <= max_len { - return line.to_string(); + return s.to_string(); } - let truncated: String = line.chars().take(max_len - 3).collect(); + let truncated: String = s.chars().take(max_len - 3).collect(); format!("{truncated}...") } @@ -404,7 +410,7 @@ fn df_from(args: &DfArgs, data_dir: &Path, runs_base: &Path, logs_base: &Path) - }; vec![ short_run_id(&detail.run_id).cell(), - truncate_goal(&detail.workflow_name, 16).cell(), + truncate_str(&detail.workflow_name, 16).cell(), detail.status.to_string().cell(), age.cell().justify(Justify::Right), size_display.cell().justify(Justify::Right), @@ -588,4 +594,34 @@ mod tests { assert_eq!(format_size(1024), "1.0 KB"); assert_eq!(format_size(1024 * 1024), "1.0 MB"); } + + #[test] + fn truncate_goal_strips_markdown_headings() { + assert_eq!(truncate_goal("## Fix bug", 50), "Fix bug"); + assert_eq!(truncate_goal("# Title", 50), "Title"); + assert_eq!(truncate_goal("### Deep heading", 50), "Deep heading"); + } + + #[test] + fn truncate_goal_strips_plan_prefix() { + assert_eq!(truncate_goal("Plan: do stuff", 50), "do stuff"); + } + + #[test] + fn truncate_goal_strips_heading_and_plan_prefix() { + assert_eq!(truncate_goal("## Plan: migrate DB", 50), "migrate DB"); + } + + #[test] + fn truncate_goal_plain_text_unchanged() { + assert_eq!(truncate_goal("Fix the login bug", 50), "Fix the login bug"); + } + + #[test] + fn truncate_goal_still_truncates_after_stripping() { + assert_eq!( + truncate_goal("## A long goal description", 10), + "A long ..." + ); + } }