Migration: modes → actions
The dispatch input was renamed and every option value changed. This is a breaking interface change with no compatibility alias.
Business view
The old dropdown
Before this PR, the input was called Review depth and offered five modes:
| Mode | Did |
|---|---|
test-only | Review → stories → browser execution → summary + issues. Never committed. |
fix-issues | Took the previous run's failures, fixed them on the branch, re-tested, updated the issues. |
plan-only | Stopped after review + stories; posted the plan for humans to edit. |
execute | Ran the previously posted (possibly edited) plan. No fixes. |
full-auto | Test and fix in one run. |
The new dropdown
QA action to run — five options, but a different decomposition:
| Action | Does |
|---|---|
review | Code review only → editable comment. New. |
plan | User stories, built on the review → editable comment. |
test | Executes the plan → issues + summary. Never commits. Auto-plans if no plan exists. |
fix | Fixes the previous test's failures, re-tests, updates issues. |
full-auto | review → plan → test → fix. |
What each old mode becomes
| Old | New | Notes |
|---|---|---|
test-only | test | Direct equivalent. test will auto-plan if no plan comment exists, matching test-only's self-contained behavior. |
execute | test | Merged. test fetches the edited plan comment when one exists (:61), which is exactly what execute did. |
plan-only | plan | Direct equivalent, but plan now also consumes an edited review comment if one is present. |
fix-issues | fix | Direct equivalent. |
full-auto | full-auto | Same name, one behavior change — it now also runs a review stage. |
| — | review | No predecessor. This is the new capability. |
The genuine consolidation is test-only + execute → test. Those two modes differed only in whether a plan already existed; the new test decides that for itself.
What to change in your habits
- The old
plan-only→executetwo-step is nowplan→test. - Consider adding a
reviewdispatch in front of it. It is the cheapest
action and it catches the most expensive class of error.
- If you used
test-onlyas your default, keep usingtest— it is now the
dropdown default (:30), so it is one fewer click.
Technical view
The input definition changed
Before:
mode:
description: Review depth
type: choice
required: true
default: test-only
options: [test-only, fix-issues, plan-only, execute, full-auto]
After:
action:
description: QA action to run
type: choice
required: true
default: test
options: [review, plan, test, fix, full-auto]
scope:
description: Test scope (test action only)
type: choice
required: true
default: all
options: [all, failed-only]
— .github/workflows/sandbox-qa.yml:23-44
Programmatic callers break
workflow_dispatch inputs are a public interface. Anything invoking this workflow outside the Actions UI must be updated:
# Before
gh workflow run sandbox-qa.yml --ref my-branch -f mode=test-only
# After
gh workflow run sandbox-qa.yml --ref my-branch -f action=test -f scope=all
There is no mode alias, no deprecation window, and no fallback. A call with -f mode=… fails with an unknown-input error. Both action and scope are required: true; scope has a default, so it may be omitted in the UI, but be explicit in scripts.
Before merging, confirm nothing else dispatches this workflow. The repository's other two sandbox workflows (sandbox.yml, sandbox-ops.yml) do not reference it. External callers — bots, dashboards, personal aliases, documentation — are outside what can be checked from this repository.
Behavior differences that are not just renames
Three places where a like-for-like swap does not give like-for-like behavior.
1. Missing plan is no longer an error.
The old execute mode hard-failed if you dispatched it without a posted plan:
const plan = comments.find(c => c.body && c.body.includes(marker));
if (!plan) { core.setFailed("no posted QA plan found — run plan-only first"); return; }
The new code writes the file only when the comment exists and otherwise says nothing:
const c = comments.find(x => x.body && x.body.includes(marker));
if (c) fs.writeFileSync(process.env.RUNNER_TEMP + file, c.body.replace(marker, ""));
— .github/workflows/sandbox-qa.yml:80-81
That is required for test's auto-plan behavior, but it converts a loud failure into a silent substitution. If you delete the plan comment by mistake, test generates a fresh plan and tests that — and the run looks identical to a successful one.
2. Missing PR is no longer an error.
// Before
if (!prs.length) { core.setFailed("no open PR for this branch"); return; }
// After
if (!prs.length) { core.info("no open PR — using stored state only"); return; }
— .github/workflows/sandbox-qa.yml:72
Correct for a ref-dispatched workflow backed by per-branch host state, but it means a branch you forgot to open a PR for produces a run with no visible output at all.
3. full-auto gained a review stage but not the fetch step.
The fetch step's condition is inputs.action == 'plan' || inputs.action == 'test' (:61), so full-auto does not read edited comments. Under the old design that was moot — full-auto had no upstream artifacts to read. Under the new one, a PR may well have a curated review and plan sitting on it that full-auto will regenerate and ignore. Use test when you have edited a plan.
Cosmetic leftovers from the rename
Two, both harmless, both worth a follow-up:
const mode = action;(:101) — a compatibility alias whose only surviving
consumer is the mode **${mode}** string in the summary comment (:234). It is assigned from action, so it prints the new vocabulary regardless.
- The comment at
:158-159still refers tofix-issuesand "every other mode",
while the code below it correctly tests action === "fix" (:161, :177).
Header documentation was updated
The file's top-of-file comment block was rewritten in this PR (.github/workflows/sandbox-qa.yml:1-19) to describe the new actions. It is the authoritative in-repo description of intent — and it documents several behaviors implemented host-side that the YAML cannot demonstrate. See Operating notes.