Skip to content

feat(workflows): add --dry-run flag to preview spec/plan output without AI invocation#2704

Open
fuleinist wants to merge 2 commits into
github:mainfrom
fuleinist:feat/2661-dry-run
Open

feat(workflows): add --dry-run flag to preview spec/plan output without AI invocation#2704
fuleinist wants to merge 2 commits into
github:mainfrom
fuleinist:feat/2661-dry-run

Conversation

@fuleinist
Copy link
Copy Markdown

Summary

Implements GitHub issue #2661 — add a --dry-run flag to specify specify and specify plan commands to preview rendered prompts without invoking the AI.

Changes

Core engine

  • src/specify_cli/workflows/base.py: Add dry_run: bool = False field to StepContext dataclass
  • src/specify_cli/workflows/engine.py: Add dry_run: bool = False parameter to WorkflowEngine.execute(); passes flag to StepContext

CLI commands

  • src/specify_cli/__init__.py:
    • Add --dry-run option to specify workflow run command
    • Add new specify specify command: specify specify --spec "..." [--dry-run]
    • Add new specify plan command: specify plan --spec "..." [--dry-run]

Step behavior

  • CommandStep (workflows/steps/command/__init__.py): When context.dry_run=True, renders the resolved command/integration/model and returns COMPLETED without spawning the integration CLI subprocess
  • GateStep (workflows/steps/gate/__init__.py): When context.dry_run=True, skips interactive prompt and returns COMPLETED immediately

Tests

  • tests/test_workflows.py: Add dry-run tests for TestCommandStep, TestGateStep, and TestWorkflowEngine

Usage examples

# Preview the specify step prompt
specify specify --spec 'Build a kanban board with drag-and-drop' --dry-run

# Preview the plan step prompt
specify plan --spec 'Build a kanban board with drag-and-drop' --dry-run

# Preview any workflow
specify workflow run speckit --input spec='...' --dry-run

Dry-run output shows the resolved command, integration, model, and arguments without making any AI API calls — suitable for CI/template iteration.

Acceptance criteria

  • --dry-run flag prints rendered prompt/inputs without AI API calls
  • Clear dry-run message displayed in output
  • specify specify --dry-run --spec ... works
  • specify plan --dry-run --spec ... works
  • specify workflow run ... --dry-run works
  • All 2971 existing tests pass
  • 3 new dry-run tests added and passing

Closes #2661

…ut AI invocation

Implements GitHub issue github#2661.

- Add dry_run field to StepContext (workflows/base.py)
- Add dry_run parameter to WorkflowEngine.execute() (workflows/engine.py)
- Add --dry-run to 'specify workflow run' CLI command
- Add 'specify specify' and 'specify plan' CLI commands with --dry-run support
- CommandStep: in dry-run mode, renders the command/integration/model and
  returns COMPLETED without spawning the integration CLI subprocess
- GateStep: in dry-run mode, skips interactive prompt and returns COMPLETED
- Add tests for dry-run in TestCommandStep, TestGateStep, and
  TestWorkflowEngine

Usage:
  specify specify --spec 'Build a kanban board' --dry-run
  specify plan --spec 'Build a kanban board' --dry-run
  specify workflow run speckit --input spec='Build kanban' --dry-run
@fuleinist fuleinist requested a review from mnriem as a code owner May 26, 2026 12:50
Copilot AI review requested due to automatic review settings May 26, 2026 12:50
Copy link
Copy Markdown
Contributor

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

Note

Copilot was unable to run its full agentic suite in this review.

Adds a workflow “dry-run” mode to preview rendered inputs and skip AI/interactive execution, and exposes it via CLI entrypoints.

Changes:

  • Introduces dry_run on WorkflowEngine.execute() and propagates it through StepContext.
  • Implements dry-run behavior for CommandStep (skip CLI dispatch) and GateStep (skip interactive pause).
  • Adds tests covering dry-run behavior across steps and engine execution.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_workflows.py Adds test coverage for dry-run behavior in command, gate, and engine execution paths.
src/specify_cli/workflows/steps/gate/init.py Skips interactive gating and returns COMPLETED during dry-run.
src/specify_cli/workflows/steps/command/init.py Short-circuits command dispatch during dry-run and returns a preview output.
src/specify_cli/workflows/engine.py Adds dry_run parameter to execute() and passes it to StepContext.
src/specify_cli/workflows/base.py Extends StepContext with a dry_run flag.
src/specify_cli/init.py Adds dry-run CLI options and new direct “specify/plan” CLI commands.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

invoke_str = f"{command} {args_str}".strip()
output["dispatched"] = False
output["dry_run"] = True
output["exit_code"] = None
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed: changed to in dry-run mode. Since the step returns , exit_code=0 accurately reflects that the step succeeded — and it avoids breaking expression evaluation in downstream steps that expect an integer.

Comment on lines 414 to 420
self,
definition: WorkflowDefinition,
inputs: dict[str, Any] | None = None,
run_id: str | None = None,
dry_run: bool = False,
) -> RunState:
"""Execute a workflow definition.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed: added comprehensive docstring documentation for the dry_run parameter in WorkflowEngine.execute(), covering what is skipped, what side-effects remain (run persistence), and expected status behavior.

Comment thread src/specify_cli/__init__.py Outdated
)
console.print(f"\n[{status_color}]Status: {state.status.value}[/{status_color}]")
if dry_run:
console.print("[dim]Run with --dry-run to see step details. Run without --dry-run to execute.[/dim]")
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed: changed message from 'Run with --dry-run to see step details. Run without --dry-run to execute.' to simply 'Run without --dry-run to execute.' — the confusing 'Run with --dry-run' part was contradictory since the message only appears inside the dry-run block.

Comment thread src/specify_cli/__init__.py Outdated
)
console.print(f"\n[{status_color}]Status: {state.status.value}[/{status_color}]")
if dry_run:
console.print("[dim]Run with --dry-run to see step details. Run without --dry-run to execute.[/dim]")
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Same fix as above for the plan command: changed message from 'Run with --dry-run to see step details. Run without --dry-run to execute.' to simply 'Run without --dry-run to execute.'.

- Set exit_code=0 in dry-run mode (CommandStep) instead of None, matching
  the COMPLETED status and not breaking expression evaluation
- Add dry_run parameter documentation to WorkflowEngine.execute() docstring
- Fix contradictory 'Run with --dry-run' hint messages in specify specify/plan
  commands (the message appeared inside the dry-run block itself)
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.

[Feature]: Add dry-run flag to preview spec output without AI invocation

2 participants