Skip to content

Add deterministic command steps and loop gates to workflow engine - #34

Merged
5uck1ess merged 2 commits into
mainfrom
feat/deterministic-command-steps
Apr 9, 2026
Merged

Add deterministic command steps and loop gates to workflow engine#34
5uck1ess merged 2 commits into
mainfrom
feat/deterministic-command-steps

Conversation

@5uck1ess

@5uck1ess 5uck1ess commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • command field on WfStep — runs shell commands directly via sh -c, captures output with exit code, costs $0 (no LLM tokens). Mutually exclusive with prompt, parallel, and loop.
  • gate field on Loop — shell command run after each loop iteration. Exit 0 = keep + commit. Non-zero = git revert. Gate startup errors are fatal (not retried). 3 consecutive gate failures trigger stuck detection.
  • Rewrites self-lint.yml as proof of concept: linter runs deterministically via command steps, Claude only fixes issues, gate enforces no regressions.

Why

Skills like self-lint, self-test, and pr-ready have strict sequential procedures where skipping or reordering steps breaks the outcome. The workflow engine already supports loops, branches, and parallel dispatch — but all execution went through the LLM runner, even for deterministic operations like running a linter. This adds the missing primitive: direct shell execution with quality enforcement.

Changes

File What
src/engine/workflow.go Command field on WfStep, Gate field on Loop, validation for mutual exclusion
src/engine/engine.go runCommand() method, command step handling in runStep(), gate/revert logic in runLoop()
src/engine/engine_test.go 12 new tests: command steps, gate pass/fail/recovery, validation, parsing
workflows/self-lint.yml Rewritten to use command steps for linter + gate for enforcement
skills/creating-workflows/SKILL.md Schema docs and example for command + gate

Test plan

  • All existing engine tests pass (no regressions)
  • New command step tests: execution, interpolation, chaining with prompts, non-zero exit
  • New gate tests: pass, fail with revert, recovery after failure, stuck detection
  • New validation tests: command+prompt, command+loop, parallel+command mutual exclusion
  • self-lint.yml parses correctly via TestParseRealWorkflows
  • Full test suite: go test ./... -count=1 — all green

5uck1ess added 2 commits April 9, 2026 16:24
The workflow engine now supports two new features that let the Go binary
drive deterministic execution while Claude handles the thinking:

- `command` field on WfStep: runs shell commands directly via sh -c,
  captures output with exit code, costs $0 (no LLM). Mutually exclusive
  with prompt, parallel, and loop.

- `gate` field on Loop: shell command run after each iteration. Exit 0
  keeps changes and commits. Non-zero reverts via git checkout. Gate
  startup errors (command not found, context cancelled) are fatal. Three
  consecutive gate failures trigger stuck detection and stop the loop.

Rewrites self-lint.yml as proof of concept: linter runs deterministically
via command steps, Claude only fixes issues, gate enforces no regressions.
@5uck1ess
5uck1ess merged commit 3d27bb8 into main Apr 9, 2026
3 checks passed
@5uck1ess
5uck1ess deleted the feat/deterministic-command-steps branch April 9, 2026 20:28
5uck1ess added a commit that referenced this pull request Apr 9, 2026
PR 2 of deterministic workflow conversion. Updates self-test, self-perf,
and self-improve workflows to use `command` steps for baseline/verify
and `gate` on improvement loops (matching self-lint proof of concept).
Creates new self-migrate.yml workflow. Converts all 5 commands to thin
wrappers.

Workflows: baseline prompt → command step, loop gains gate check
- self-test.yml: prompt baseline → command, add gate (max 8)
- self-perf.yml: prompt baseline → command, add gate (max 5)
- self-improve.yml: prompt baseline → command, add gate (max 10)
- self-migrate.yml: new file with command+gate (max 20)
- self-lint.yml: unchanged (already done in PR #34)

Commands: 5 files, ~643 lines removed
5uck1ess added a commit that referenced this pull request Apr 9, 2026
* Convert self-improvement loops to deterministic command+gate pattern

PR 2 of deterministic workflow conversion. Updates self-test, self-perf,
and self-improve workflows to use `command` steps for baseline/verify
and `gate` on improvement loops (matching self-lint proof of concept).
Creates new self-migrate.yml workflow. Converts all 5 commands to thin
wrappers.

Workflows: baseline prompt → command step, loop gains gate check
- self-test.yml: prompt baseline → command, add gate (max 8)
- self-perf.yml: prompt baseline → command, add gate (max 5)
- self-improve.yml: prompt baseline → command, add gate (max 10)
- self-migrate.yml: new file with command+gate (max 20)
- self-lint.yml: unchanged (already done in PR #34)

Commands: 5 files, ~643 lines removed

* Fix CI count validation and address review findings

- Update README.md and ROADMAP.md: 15 → 16 workflows (new self-migrate.yml)
- Document exit-code semantics for self-perf: benchmark must exit non-zero
  when target not met
- Document gate semantics for self-migrate: use composite gate command
  for migration completeness detection
- Clarify self-improve: metric command must exit non-zero when needed

* Fix loop termination: until must match LLM output, not gate exit code

The engine checks `until` against the LLM's text output
(engine.go:460), not the gate command's exit code. Since all
prompts instruct the LLM to "say DONE", the until condition
must be "DONE" — not "exit code: 0" which never appears in
LLM output.

Without this fix, all self-improvement loops run to max
iterations even after the gate passes, wasting tokens.

Affects: self-lint, self-test, self-perf, self-improve, self-migrate
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