Dentolize · Composable Sandbox QA Walkthrough
On this pageBusiness viewTechnical view

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:

ModeDid
test-onlyReview → stories → browser execution → summary + issues. Never committed.
fix-issuesTook the previous run's failures, fixed them on the branch, re-tested, updated the issues.
plan-onlyStopped after review + stories; posted the plan for humans to edit.
executeRan the previously posted (possibly edited) plan. No fixes.
full-autoTest and fix in one run.

The new dropdown

QA action to run — five options, but a different decomposition:

ActionDoes
reviewCode review only → editable comment. New.
planUser stories, built on the review → editable comment.
testExecutes the plan → issues + summary. Never commits. Auto-plans if no plan exists.
fixFixes the previous test's failures, re-tests, updates issues.
full-autoreview → plan → test → fix.

What each old mode becomes

OldNewNotes
test-onlytestDirect equivalent. test will auto-plan if no plan comment exists, matching test-only's self-contained behavior.
executetestMerged. test fetches the edited plan comment when one exists (:61), which is exactly what execute did.
plan-onlyplanDirect equivalent, but plan now also consumes an edited review comment if one is present.
fix-issuesfixDirect equivalent.
full-autofull-autoSame name, one behavior change — it now also runs a review stage.
reviewNo predecessor. This is the new capability.

The genuine consolidation is test-only + executetest. 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-onlyexecute two-step is now plantest.
  • Consider adding a review dispatch in front of it. It is the cheapest

action and it catches the most expensive class of error.

  • If you used test-only as your default, keep using test — 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-159 still refers to fix-issues and "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.