feat: configurable task header patterns#309
feat: configurable task header patterns#309bronislav wants to merge 2 commits intoumputun:masterfrom
Conversation
Plan to replace hardcoded ~/.gitconfig mount with dynamic discovery using `git config --list --show-origin --global`. Captures XDG config, include files, and adds fallback for standard paths.
|
@umputun this is quite a big pull request and i am worried that I might miss some important things. I am going to mark it as ready for review even though I did not test it manually myself yet. I am going to do it over next several days. I open to changes in the design or approach if you think they needed. I never used web dashboard myself to monitor several sessions, so my assumptions around that might be different from what you think is appropriate. |
|
Already found a "bug": ralphex creates a branch and worktree based on the plan file name and ignoring directory it resides in. I probably need to create a separate pull request to make branch configurable or dependent on the subdirectory as well. |
c551897 to
6290a7e
Compare
umputun
left a comment
There was a problem hiding this comment.
@bronislav I asked for configurable patterns in #306 but didn't expect a +2400 PR. The bulk isn't justified for the stated need. Here's what I think we should ship instead.
Two big offenders:
pkg/plan/patterns.go(208 LOC) +patterns_test.go(394 LOC). The{N}/{title}DSL with its own compiler, brace validator, whitespace normalizer. Our users are devs running ralphex on their own plans, they can write regex. I'd rather have:
task_header_patterns = ^### (?:Task|Iteration) ([^:]+?):\s*(.*)$, ^## (\d+(?:\.\d+)?)\.\s*(.*)$Capture group 1 = task id (required), capture group 2 = title (optional). Compile via regexp.Compile, surface compile errors as-is. About 50 prod LOC instead of 208, far fewer tests.
Default stays exactly what we have today (Task/Iteration only, no behavior change for existing users). For common shapes we add a small named-preset registry in pkg/plan as a map[string]string. Seed it with two entries: default (current Task/Iteration regex, what task_header_patterns resolves to when unset) and openspec (OpenSpec header regex). The value of task_header_patterns is either a known preset name, looked up in the map, or a raw regex passed to regexp.Compile:
# opt into openspec parsing
task_header_patterns = openspec
# or write a regex for some other shape
task_header_patterns = ^# Phase (\d+):\s*(.*)$OpenSpec users get a one-liner. Power users can write regex. Adding the next spec flavor later is a one-entry map change, not a config schema change.
- Progress-log persistence + dashboard recovery.
TaskHeaderPatterns:line in progress files, re-emission at restart markers,ParseProgressHeaderfull-file scan,loadSessionPlanWithFallback. This is a configuration transport bolted onto an execution log to handle "single dashboard process watches multiple repos with different non-default header configs." I don't think anyone is doing that. The dashboard reading from its own config plus parser defaults is fine. Drop the whole layer (around 250-400 LOC across progress/session/web plus tests).
What I'd keep:
closesTask/headingLevelrefactor inparse.go, replaces the hand-rolledisH2/isH1AfterTitletangle, real readability wintask_header_patternsconfig field with comma-separated list (regex instead of templates)- prompt rewrite in
task.txtto expose configured shapes via{{TASK_HEADER_PATTERNS}} - README/CLAUDE.md doc updates
Net shape: roughly +200/-50 across 8 or so files, down from +2403/-177 across 30.
Smaller things, whatever shape we ship:
Config.TaskHeaderPatternsSetandCompileTaskHeaderPattern(s)are exported with no external callers, lowercase themParsePlan(content, patterns ...string)variadic doesn't pay rent, every production caller has[]stringand spreads it. Just take[]string- if any progress-log machinery survives,
ParseProgressHeaderreads the entire progress file on every fsnotify event, needs to be tail-bounded likeisProgressCompleted
There was a problem hiding this comment.
Pull request overview
Adds a configurable task_header_patterns mechanism so the plan parser (and the web dashboard/progress logs/prompts) can recognize task sections beyond the hard-coded ### Task / ### Iteration headers, enabling spec-driven plan formats like ## {N}. {title} while preserving default behavior.
Changes:
- Introduces a template-to-regex compiler (
{N}required,{title}optional) and updates plan parsing to accept variadic pattern templates. - Plumbs
task_header_patternsthrough config loading, runner validation, web dashboard plan parsing, and progress log metadata (including restart re-emission). - Updates prompts and documentation to reference
{{TASK_HEADER_PATTERNS}}, plus adds tests across affected packages.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/web/session_progress_test.go | Adds coverage for parsing TaskHeaderPatterns: from headers and restart markers. |
| pkg/web/session_progress.go | Extends progress header parsing to capture task header patterns and restart overrides. |
| pkg/web/session.go | Adds TaskHeaderPatterns to per-session metadata. |
| pkg/web/server_test.go | Tests that server plan endpoint uses configured/per-session patterns and default fallbacks. |
| pkg/web/server.go | Threads per-session/dashboard patterns into session plan parsing. |
| pkg/web/plan.go | Passes patterns into plan parsing and merges defaults for watched sessions. |
| pkg/web/parse_test.go | Ensures TaskHeaderPatterns: lines near restarts are suppressed in SSE stream parsing. |
| pkg/web/parse.go | Skips restart-emitted TaskHeaderPatterns: lines in event stream parsing. |
| pkg/web/dashboard.go | Plumbs TaskHeaderPatterns from dashboard config into server config. |
| pkg/progress/progress_test.go | Tests writing/re-emitting sanitized TaskHeaderPatterns: in progress logs. |
| pkg/progress/progress.go | Writes sanitized TaskHeaderPatterns: in header and on restart markers. |
| pkg/processor/runner.go | Uses configured patterns for plan validation and completion checks; improves “no tasks” error text. |
| pkg/processor/prompts_test.go | Adds tests for {{TASK_HEADER_PATTERNS}} prompt variable expansion. |
| pkg/processor/prompts.go | Implements {{TASK_HEADER_PATTERNS}} expansion and hint formatting. |
| pkg/plan/patterns_test.go | Adds extensive tests for template compiler and default-compatibility. |
| pkg/plan/patterns.go | Implements template compiler and DefaultTaskHeaderPatterns. |
| pkg/plan/parse_test.go | Adds tests for custom patterns, mixed patterns, and heading/task-closing behavior. |
| pkg/plan/parse.go | Updates ParsePlan/ParsePlanFile signatures and integrates compiled pattern matching. |
| pkg/config/values_test.go | Tests INI parsing/merge semantics for task_header_patterns. |
| pkg/config/values.go | Adds Values fields for TaskHeaderPatterns and explicit-set tracking. |
| pkg/config/defaults/prompts/task.txt | Rewrites prompt language to reference {{TASK_HEADER_PATTERNS}} instead of fixed headers. |
| pkg/config/defaults/config | Documents new task_header_patterns option in embedded config template. |
| pkg/config/config_test.go | Tests config-level defaulting/override behavior for task_header_patterns. |
| pkg/config/config.go | Applies runtime defaults for TaskHeaderPatterns without importing domain packages. |
| llms.txt | Documents {{TASK_HEADER_PATTERNS}} and task_header_patterns config option. |
| docs/plans/completed/20260427-task-header-patterns-config.md | Archives the completed implementation plan. |
| cmd/ralphex/main.go | Passes configured patterns into progress logger and dashboard config. |
| README.md | Documents task_header_patterns and adds template-variable reference. |
| CLAUDE.md | Updates plan-format guidance and variable documentation to include configurable patterns. |
| .golangci.yml | Adds test-only exclusion for gosec G704 SSRF taint warning. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // scan the remainder for restart-marker TaskHeaderPatterns overrides. | ||
| // last occurrence wins so the freshest run's patterns are used. | ||
| for { | ||
| line, readErr := reader.ReadString('\n') | ||
| line = trimLineEnding(line) | ||
| if val, found := strings.CutPrefix(line, "TaskHeaderPatterns: "); found { | ||
| if patterns := parsePatternList(val); len(patterns) > 0 { | ||
| meta.TaskHeaderPatterns = patterns | ||
| } | ||
| } | ||
| if readErr != nil { | ||
| if !errors.Is(readErr, io.EOF) { | ||
| return SessionMetadata{}, false, fmt.Errorf("read file: %w", readErr) | ||
| return meta, complete, fmt.Errorf("read file: %w", readErr) | ||
| } | ||
| break // EOF after processing final line | ||
| return meta, complete, nil | ||
| } |
| ReviewModel string // model for review phases (falls back to TaskModel if empty) | ||
| ClaudeErrorPatterns []string // patterns to detect in claude output (e.g., rate limit messages) | ||
| TaskHeaderPatterns []string // templates used to recognize task section headers (e.g., "### Task {N}: {title}") | ||
| TaskHeaderPatternsSet bool // tracks if task_header_patterns was explicitly set (allows empty to disable) |
| // the progress writer re-emits TaskHeaderPatterns next to a restart | ||
| // marker so retried runs advertise the current patterns even if the | ||
| // original header has stale or absent values. ParseProgressHeader | ||
| // scans the file tail for that override; the last occurrence wins. |
|
I completely agree with your feedback. Going to address it. |
adds task_header_patterns config option (comma-separated list of preset
names or raw Go regexes) that controls which headers the plan parser
recognizes as task sections. supports built-in presets (default,
openspec) and arbitrary raw regexes for custom plan formats.
- preset registry in pkg/plan/presets.go with DefaultHeaderPatterns(),
ResolveHeaderPattern(), ResolveHeaderPatterns(), PresetDescription()
- ParsePlan/ParsePlanFile accept []*regexp.Regexp; nil/empty falls back
to DefaultHeaderPatterns()
- {{TASK_HEADER_PATTERNS}} template variable expands to human-readable
descriptions of configured patterns in task.txt
- raw regexes validated at config load time via regexp.Compile so users
get a clear error instead of a silent fallback to defaults
- web dashboard plan panel updated to pass patterns from config
- docs and embedded config template updated
6290a7e to
3e29f56
Compare
Summary
Adds
task_header_patternsconfig option (and{{TASK_HEADER_PATTERNS}}template variable) so the plan parser can recognize task sections beyond the default### Task {N}: {title}/### Iteration {N}: {title}shapes. Useful for spec-driven workflows (e.g. OpenSpec) that use headers like## {N}. {title}.pkg/plan/patterns.gowith{N}(required) and{title}(optional) placeholdersParsePlan/ParsePlanFileaccept variadicpatterns ...stringtask.txtprompt rewritten to reference{{TASK_HEADER_PATTERNS}}docs/plans/completed/Test plan
make testmake lint## {N}. {title})