Skip to content

feat: configurable task header patterns#309

Draft
bronislav wants to merge 2 commits intoumputun:masterfrom
bronislav:task-header-patterns-config
Draft

feat: configurable task header patterns#309
bronislav wants to merge 2 commits intoumputun:masterfrom
bronislav:task-header-patterns-config

Conversation

@bronislav
Copy link
Copy Markdown
Contributor

@bronislav bronislav commented Apr 28, 2026

Summary

Adds task_header_patterns config 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}.

  • template-to-regex compiler in pkg/plan/patterns.go with {N} (required) and {title} (optional) placeholders
  • config field + loader with embedded default, per-field fallback, CLI passthrough
  • ParsePlan/ParsePlanFile accept variadic patterns ...string
  • task.txt prompt rewritten to reference {{TASK_HEADER_PATTERNS}}
  • completed plan archived under docs/plans/completed/

Test plan

  • make test
  • make lint
  • e2e run with toy project using default patterns
  • e2e run with a custom pattern (e.g. ## {N}. {title})

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.
@bronislav
Copy link
Copy Markdown
Contributor Author

@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.

@bronislav bronislav marked this pull request as ready for review April 29, 2026 03:17
@bronislav bronislav requested a review from umputun as a code owner April 29, 2026 03:18
@bronislav
Copy link
Copy Markdown
Contributor Author

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.

@bronislav bronislav force-pushed the task-header-patterns-config branch from c551897 to 6290a7e Compare April 29, 2026 14:32
Copy link
Copy Markdown
Owner

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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:

  1. 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.

  1. Progress-log persistence + dashboard recovery. TaskHeaderPatterns: line in progress files, re-emission at restart markers, ParseProgressHeader full-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 / headingLevel refactor in parse.go, replaces the hand-rolled isH2 / isH1AfterTitle tangle, real readability win
  • task_header_patterns config field with comma-separated list (regex instead of templates)
  • prompt rewrite in task.txt to 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.TaskHeaderPatternsSet and CompileTaskHeaderPattern(s) are exported with no external callers, lowercase them
  • ParsePlan(content, patterns ...string) variadic doesn't pay rent, every production caller has []string and spreads it. Just take []string
  • if any progress-log machinery survives, ParseProgressHeader reads the entire progress file on every fsnotify event, needs to be tail-bounded like isProgressCompleted

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_patterns through 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.

Comment thread pkg/web/session_progress.go Outdated
Comment on lines +74 to +89
// 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
}
Comment thread pkg/config/values.go Outdated
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)
Comment thread pkg/web/session_progress_test.go Outdated
// 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.
@bronislav
Copy link
Copy Markdown
Contributor Author

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
@bronislav bronislav force-pushed the task-header-patterns-config branch from 6290a7e to 3e29f56 Compare April 30, 2026 03:26
@bronislav bronislav marked this pull request as draft April 30, 2026 03:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants