Skip to content

feat: add per-workflow model configuration#5

Open
BryanR57 wants to merge 2 commits into
robertguss:mainfrom
BryanR57:main
Open

feat: add per-workflow model configuration#5
BryanR57 wants to merge 2 commits into
robertguss:mainfrom
BryanR57:main

Conversation

@BryanR57
Copy link
Copy Markdown

  • Add optional model field to WorkflowConfig for specifying Claude model per workflow
  • Update Executor interface to accept model parameter
  • Add --verbose flag to fix Claude CLI stream-json requirement
  • Configure code-review workflow to use claude-sonnet-4-20250514

Bryan and others added 2 commits January 12, 2026 18:17
- Add optional `model` field to WorkflowConfig for specifying Claude model per workflow
- Update Executor interface to accept model parameter
- Add --verbose flag to fix Claude CLI stream-json requirement
- Configure code-review workflow to use claude-sonnet-4-20250514

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jan 12, 2026

Greptile Overview

Greptile Summary

This PR introduces per-workflow model configuration, allowing different workflows to use specific Claude models. The implementation is well-structured and follows existing patterns in the codebase.

Key Changes

Configuration Layer

  • Added optional Model field to WorkflowConfig for specifying Claude models per workflow
  • Implemented Config.GetModel() method to retrieve workflow-specific model settings
  • Updated code-review workflow in config to use claude-sonnet-4-20250514

Executor Layer

  • Updated Executor.ExecuteWithResult() interface to accept an optional model parameter
  • Added --verbose flag to both Execute and ExecuteWithResult methods (required for proper stream-json output)
  • Model parameter is conditionally appended to CLI arguments only when non-empty

Workflow Layer

  • Modified workflow.Runner methods (RunSingle, RunRaw, RunFullCycle) to retrieve and pass model configuration
  • All test mocks updated to match new interface signature

Strengths

  • Backwards compatible: empty model string uses Claude CLI defaults
  • Consistent implementation across all workflow execution paths
  • Proper parameter validation (only appends --model flag when non-empty)
  • Test coverage updated for interface changes

Areas for Improvement

  • Missing test coverage for the new Config.GetModel() method
  • Documentation in docs/PACKAGES.md and docs/ARCHITECTURE.md still shows old ExecuteWithResult signature without model parameter

Confidence Score: 4/5

  • This PR is safe to merge with minor follow-up improvements recommended
  • The implementation is solid and follows existing patterns, with all breaking interface changes properly propagated through tests. The changes are backwards compatible (empty model uses defaults), and the core logic is straightforward. Score reduced from 5 to 4 due to: (1) missing test coverage for Config.GetModel(), which could mask edge case bugs, and (2) outdated documentation that could confuse developers. These are non-critical issues that don't affect functionality but should be addressed for maintainability.
  • Documentation files (docs/PACKAGES.md, docs/ARCHITECTURE.md) need updates to reflect the new ExecuteWithResult signature. Consider adding test coverage for internal/config/config.go GetModel() method.

Important Files Changed

File Analysis

Filename Score Overview
config/workflows.yaml 5/5 Added model field to code-review workflow specifying claude-sonnet-4-20250514
internal/config/config.go 4/5 Added GetModel method to retrieve workflow-specific model configuration; missing test coverage
internal/claude/client.go 4/5 Updated Executor interface with model parameter and added --verbose flag to both Execute methods
internal/workflow/workflow.go 5/5 Updated RunSingle, RunRaw, and RunFullCycle to retrieve and pass model parameter to executor

Sequence Diagram

sequenceDiagram
    participant User
    participant Runner as workflow.Runner
    participant Config as config.Config
    participant Executor as claude.Executor
    participant Claude as Claude CLI

    User->>Runner: RunSingle(ctx, "code-review", "PROJ-123")
    Runner->>Config: GetPrompt("code-review", "PROJ-123")
    Config-->>Runner: "/bmad:...:code-review - Review story: PROJ-123..."
    Runner->>Config: GetModel("code-review")
    Config-->>Runner: "claude-sonnet-4-20250514"
    Runner->>Executor: ExecuteWithResult(ctx, prompt, handler, "claude-sonnet-4-20250514")
    Executor->>Claude: exec.Command("claude", "--dangerously-skip-permissions", "-p", prompt, "--output-format", "stream-json", "--verbose", "--model", "claude-sonnet-4-20250514")
    Claude-->>Executor: Stream JSON events
    Executor->>Runner: handler(event) for each event
    Claude-->>Executor: Exit code
    Executor-->>Runner: exitCode, err
    Runner-->>User: exitCode
Loading

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment thread internal/config/config.go
Comment on lines +140 to +145
func (c *Config) GetModel(workflowName string) string {
workflow, ok := c.Workflows[workflowName]
if !ok {
return ""
}
return workflow.Model
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new GetModel method lacks test coverage. Consider adding tests to verify:

  1. Returns correct model when workflow exists and has model configured
  2. Returns empty string when workflow exists but model is not set
  3. Returns empty string when workflow doesn't exist

Example test structure:

func TestConfig_GetModel(t *testing.T) {
    cfg := &Config{
        Workflows: map[string]WorkflowConfig{
            "with-model": {Model: "claude-sonnet-4"},
            "no-model":   {PromptTemplate: "test"},
        },
    }
    
    assert.Equal(t, "claude-sonnet-4", cfg.GetModel("with-model"))
    assert.Equal(t, "", cfg.GetModel("no-model"))
    assert.Equal(t, "", cfg.GetModel("unknown"))
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/config/config.go
Line: 140:145

Comment:
The new `GetModel` method lacks test coverage. Consider adding tests to verify:
1. Returns correct model when workflow exists and has model configured
2. Returns empty string when workflow exists but model is not set
3. Returns empty string when workflow doesn't exist

Example test structure:
```go
func TestConfig_GetModel(t *testing.T) {
    cfg := &Config{
        Workflows: map[string]WorkflowConfig{
            "with-model": {Model: "claude-sonnet-4"},
            "no-model":   {PromptTemplate: "test"},
        },
    }
    
    assert.Equal(t, "claude-sonnet-4", cfg.GetModel("with-model"))
    assert.Equal(t, "", cfg.GetModel("no-model"))
    assert.Equal(t, "", cfg.GetModel("unknown"))
}
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jan 12, 2026

Additional Comments (2)

docs/PACKAGES.md
The ExecuteWithResult signature shown in documentation is outdated. It's missing the new model parameter that was added in this PR.

Current documented signature:

ExecuteWithResult(ctx context.Context, prompt string, handler EventHandler) (int, error)

Actual signature after this PR:

ExecuteWithResult(ctx context.Context, prompt string, handler EventHandler, model string) (int, error)
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/PACKAGES.md
Line: 306:306

Comment:
The `ExecuteWithResult` signature shown in documentation is outdated. It's missing the new `model` parameter that was added in this PR.

Current documented signature:
```go
ExecuteWithResult(ctx context.Context, prompt string, handler EventHandler) (int, error)
```

Actual signature after this PR:
```go
ExecuteWithResult(ctx context.Context, prompt string, handler EventHandler, model string) (int, error)
```

How can I resolve this? If you propose a fix, please make it concise.

docs/ARCHITECTURE.md
The documentation shows the old ExecuteWithResult call signature without the new model parameter. This should be updated to:

executor.ExecuteWithResult(ctx, prompt, handler, model)

Additionally, the command shown on line 197 should now include --verbose and potentially --model flags when a model is configured.

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/ARCHITECTURE.md
Line: 196:196

Comment:
The documentation shows the old `ExecuteWithResult` call signature without the new `model` parameter. This should be updated to:

```
executor.ExecuteWithResult(ctx, prompt, handler, model)
```

Additionally, the command shown on line 197 should now include `--verbose` and potentially `--model` flags when a model is configured.

How can I resolve this? If you propose a fix, please make it concise.

ibro45 referenced this pull request in ibro45/bmaduum Feb 2, 2026
This commit combines and implements all open issues and PRs into a cohesive,
production-ready solution:

Fixes:
- Fix #8, #11: Add missing cmd/bmad-automate/main.go entry point
- Fix #8, #11: Fix .gitignore to not exclude cmd/bmad-automate directory
- Fix #9: Reorder CLI arguments (--flags before -p prompt) and add --verbose
- Fix #9: Add sync.WaitGroup for stderr handling in ExecuteWithResult
- Fix #9: Use errors.As instead of type assertion for Go 1.13+ compatibility
- Fix #4: Add Windows support to justfile with PowerShell and platform-specific recipes

Features:
- Feat #5, #6: Add per-workflow model configuration (opus/sonnet/haiku)
- Feat #6: Add --auto-retry flag for rate limit handling (run/queue/epic/all-epics)
- Feat #6: Add all-epics command to process all active epics at once
- Feat #6: Add rate limit detection package with intelligent wait time parsing
- Feat: Add version command displaying current version and release date
- Feat: Enhanced dry-run mode showing which model will be used per workflow

New Files:
- cmd/bmad-automate/main.go - Entry point for the CLI
- internal/cli/all_epics.go - New command to process all epics
- internal/cli/retry.go - Auto-retry logic with rate limit handling
- internal/cli/version.go - Version command
- internal/cli/version_test.go - Tests for version command
- internal/ratelimit/detector.go - Rate limit error detection
- internal/ratelimit/detector_state.go - Thread-safe rate limit state
- internal/ratelimit/detector_test.go - Tests for detector
- internal/ratelimit/detector_state_test.go - Tests for state

Modified:
- internal/claude/client.go - Updated Executor interface with model parameter
- internal/cli/root.go - Added new commands and interfaces
- internal/cli/run.go - Added --auto-retry flag and model display in dry-run
- internal/cli/epic.go - Added --auto-retry flag and model display
- internal/cli/queue.go - Added --auto-retry flag and model display
- internal/config/types.go - Added Model field to WorkflowConfig
- internal/config/config.go - Added GetModel() method
- internal/workflow/workflow.go - Updated to pass model to executor
- internal/workflow/steps.go - Added Model field to Step
- internal/router/lifecycle.go - Added Model field to LifecycleStep
- internal/status/reader.go - Added GetAllEpics() method
- justfile - Added Windows support
- .gitignore - Fixed to not exclude cmd directory
- All test files updated for new interfaces

Co-Authored-By: Claude <noreply@anthropic.com>
ibro45 referenced this pull request in ibro45/bmaduum Feb 2, 2026
This commit combines and implements all open issues and PRs into a cohesive,
production-ready solution:

Fixes:
- Fix #8, #11: Add missing cmd/bmad-automate/main.go entry point
- Fix #8, #11: Fix .gitignore to not exclude cmd/bmad-automate directory
- Fix #9: Reorder CLI arguments (--flags before -p prompt) and add --verbose
- Fix #9: Add sync.WaitGroup for stderr handling in ExecuteWithResult
- Fix #9: Use errors.As instead of type assertion for Go 1.13+ compatibility
- Fix #4: Add Windows support to justfile with PowerShell and platform-specific recipes

Features:
- Feat #5, #6: Add per-workflow model configuration (opus/sonnet/haiku)
- Feat #6: Add --auto-retry flag for rate limit handling (run/queue/epic/all-epics)
- Feat #6: Add all-epics command to process all active epics at once
- Feat #6: Add rate limit detection package with intelligent wait time parsing
- Feat: Add version command displaying current version and release date
- Feat: Enhanced dry-run mode showing which model will be used per workflow

New Files:
- cmd/bmad-automate/main.go - Entry point for the CLI
- internal/cli/all_epics.go - New command to process all epics
- internal/cli/retry.go - Auto-retry logic with rate limit handling
- internal/cli/version.go - Version command
- internal/cli/version_test.go - Tests for version command
- internal/ratelimit/detector.go - Rate limit error detection
- internal/ratelimit/detector_state.go - Thread-safe rate limit state
- internal/ratelimit/detector_test.go - Tests for detector
- internal/ratelimit/detector_state_test.go - Tests for state

Modified:
- internal/claude/client.go - Updated Executor interface with model parameter
- internal/cli/root.go - Added new commands and interfaces
- internal/cli/run.go - Added --auto-retry flag and model display in dry-run
- internal/cli/epic.go - Added --auto-retry flag and model display
- internal/cli/queue.go - Added --auto-retry flag and model display
- internal/config/types.go - Added Model field to WorkflowConfig
- internal/config/config.go - Added GetModel() method
- internal/workflow/workflow.go - Updated to pass model to executor
- internal/workflow/steps.go - Added Model field to Step
- internal/router/lifecycle.go - Added Model field to LifecycleStep
- internal/status/reader.go - Added GetAllEpics() method
- justfile - Added Windows support
- .gitignore - Fixed to not exclude cmd directory
- All test files updated for new interfaces

Co-authored-by: Claude <noreply@anthropic.com>
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.

1 participant