Add deterministic command steps and loop gates to workflow engine - #34
Merged
Conversation
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
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
6 tasks
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
commandfield on WfStep — runs shell commands directly viash -c, captures output with exit code, costs $0 (no LLM tokens). Mutually exclusive withprompt,parallel, andloop.gatefield 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.self-lint.ymlas 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
src/engine/workflow.goCommandfield onWfStep,Gatefield onLoop, validation for mutual exclusionsrc/engine/engine.gorunCommand()method, command step handling inrunStep(), gate/revert logic inrunLoop()src/engine/engine_test.goworkflows/self-lint.ymlskills/creating-workflows/SKILL.mdTest plan
self-lint.ymlparses correctly viaTestParseRealWorkflowsgo test ./... -count=1— all green