Problem Statement
The workflow shell step hardcodes a 300-second execution timeout with no way to override it. In init.py, ShellStep.execute runs:
proc = subprocess.run(
run_cmd, shell=True, capture_output=True, text=True, cwd=cwd, timeout=300,
)
and the step config is only ever read for run and output_format (in both execute and validate). Any shell command that legitimately takes longer than 5 minutes — a full build, a linter aggregator, an integration-test target — is killed with TimeoutExpired, which the step maps to StepResult(status=FAILED, output={"exit_code": -1, "stderr": "timeout"}), failing the entire run.
This makes shell steps unusable as a probe/gate over real project QA commands. Concretely: a step running a repo's make code-qa (a MegaLinter/golangci-lint aggregator + build + tests, ~10+ min) fails at exactly 300s regardless of whether the command itself would pass, and there is no YAML knob to raise the limit.
Proposed Solution
Add an optional timeout field (seconds) to the shell step, defaulting to the current 300 for backward compatibility:
- id: qa
type: shell
timeout: 1800 # seconds; optional, default 300
run: make code-qa
The change is minimal and additive:
timeout = config.get("timeout", 300)
proc = subprocess.run(
run_cmd, shell=True, capture_output=True, text=True, cwd=cwd, timeout=timeout,
)
plus a validate check that timeout, when present, is a positive number. (Optionally timeout: 0/null could mean "no timeout" for intentionally long-running steps, though requiring an explicit positive value may be preferable to avoid runaway steps.)
Alternatives Considered
Global env var (e.g. SPECKIT_SHELL_TIMEOUT): coarser than per-step and easy to forget; a per-step field is more precise. Could be layered in as a lower-precedence default.
Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
None
Use Cases
No response
Acceptance Criteria
- A
timeout: value on a shell step overrides the 300s default and is honored by subprocess.run.
- Omitting
timeout: preserves the current behavior (300s).
validate rejects a non-positive or non-numeric timeout.
- A test asserts the configured value is threaded through (and/or that a step which would exceed the default succeeds when the timeout is raised).
Additional Context
- Per-step timeouts are standard in comparable systems (e.g. GitHub Actions
timeout-minutes), so this is an idiomatic, non-breaking addition.
shell is the only step type with a hardcoded wall-clock limit; command steps (agent subprocess) have no equivalent cap — so long-running work is blocked only when it's a plain shell command.
- Source: init.py (as of
0.12.5.dev0).
Problem Statement
The workflow
shellstep hardcodes a 300-second execution timeout with no way to override it. In init.py,ShellStep.executeruns:and the step config is only ever read for
runandoutput_format(in bothexecuteandvalidate). Any shell command that legitimately takes longer than 5 minutes — a full build, a linter aggregator, an integration-test target — is killed withTimeoutExpired, which the step maps toStepResult(status=FAILED, output={"exit_code": -1, "stderr": "timeout"}), failing the entire run.This makes
shellsteps unusable as a probe/gate over real project QA commands. Concretely: a step running a repo'smake code-qa(a MegaLinter/golangci-lint aggregator + build + tests, ~10+ min) fails at exactly 300s regardless of whether the command itself would pass, and there is no YAML knob to raise the limit.Proposed Solution
Add an optional
timeoutfield (seconds) to the shell step, defaulting to the current 300 for backward compatibility:The change is minimal and additive:
plus a
validatecheck thattimeout, when present, is a positive number. (Optionallytimeout: 0/nullcould mean "no timeout" for intentionally long-running steps, though requiring an explicit positive value may be preferable to avoid runaway steps.)Alternatives Considered
Global env var (e.g.
SPECKIT_SHELL_TIMEOUT): coarser than per-step and easy to forget; a per-step field is more precise. Could be layered in as a lower-precedence default.Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
None
Use Cases
No response
Acceptance Criteria
timeout:value on a shell step overrides the 300s default and is honored bysubprocess.run.timeout:preserves the current behavior (300s).validaterejects a non-positive or non-numerictimeout.Additional Context
timeout-minutes), so this is an idiomatic, non-breaking addition.shellis the only step type with a hardcoded wall-clock limit;commandsteps (agent subprocess) have no equivalent cap — so long-running work is blocked only when it's a plain shell command.0.12.5.dev0).