Composable Sandbox QA
PR #241 — ci(sandbox): composable QA actions — review / plan / test / fix Branch ci/qa-composable → main. One file changed: .github/workflows/sandbox-qa.yml (+70 / −46).
Read this first: what kind of change this is
This is internal engineering tooling, not a Dentolize product feature. Nothing here appears in the clinic web app, the patient portal, or the mobile app. No dentist, receptionist, or patient will ever see it.
What changed is the GitHub Actions workflow that runs Dentolize's AI QA agent against a pull request's sandbox environment. It turns one monolithic "review everything and maybe fix it" button into four separate buttons a human can run in any order, pausing in between to correct the AI's work.
If you are on Marketing or Sales, that framing matters — see For Marketing and For Sales for what you can and cannot say about it. The short version: this is a quality-practice proof-point, not something to announce.
Business view
The problem it solves
Before this change, Dentolize's AI QA agent ran as a single long pipeline. You picked a "mode" from a dropdown and it went off for up to five hours: read the code, invent test scenarios, drive a browser through them, file issues, and in some modes push fix commits to your branch.
Two things went wrong with that shape.
You could not correct it mid-flight. If the AI misunderstood what a change was supposed to do, everything downstream inherited the misunderstanding. The test plan tested the wrong thing, the issues described the wrong failures, and the fixes — if you had let it fix — patched code against a wrong premise. Your only lever was to run the whole thing again and hope.
The stages were not separable. There was a plan-only mode and an execute mode, so you could inspect the test plan before it ran. But the AI's understanding of the code — the review that the plan was built on — was never surfaced. The first artifact a human could correct was already two reasoning steps downstream of the source.
What it does now
Four actions, each a separate dispatch, each leaving an editable artifact on the pull request:
| Action | Produces | Commits code? |
|---|---|---|
| review | A technical + business code review, posted as a PR comment | No |
| plan | User stories to test, built on the review, posted as a PR comment | No |
| test | Runs the plan in a browser → one GitHub issue per story + a summary | No |
| fix | Repairs the previous test's failures, re-tests, updates the issues | Yes |
| full-auto | All four in one run | Yes |
The important word is editable. When review posts its comment, you can edit that comment in GitHub like any other. When you then dispatch plan, the workflow reads your edited text back out of the PR and hands it to the agent. Same for plan → test: whatever the plan comment says at dispatch time is what gets tested.
So a realistic session looks like:
- Dispatch review. Read the AI's understanding of the change.
- It got the insurance-claim edge case backwards. Edit the comment; fix that
paragraph. Delete the two findings that are noise.
- Dispatch plan. It builds stories on your corrected review.
- Add the two scenarios it missed. Delete the flaky one.
- Dispatch test. It executes exactly your plan.
- Three stories fail. Dispatch fix. It repairs, re-tests, and closes the
issues it actually fixed.
Each step is a decision point with a human in it. That is the whole idea.
Two smaller things in this PR
testcan re-run only what failed. Ascopedropdown offersallor
failed-only, so after a fix you don't pay for a full re-test.
- Artifact uploads no longer break on branch names containing
/. Branch
names like feat/insurance-v2 produced illegal GitHub artifact names, so the run's logs and evidence silently failed to upload. The workflow now uses a sanitized slug.
Technical view
The one file
Everything is in .github/workflows/sandbox-qa.yml (250 lines at head). The workflow is a thin GitHub-side wrapper. It has exactly three jobs of its own:
- Fetch the human-edited review/plan comments off the PR into files
(.github/workflows/sandbox-qa.yml:60-82).
- Invoke the agent — one shell line
(.github/workflows/sandbox-qa.yml:93).
- Publish whatever the agent produced back to GitHub as comments and
issues (.github/workflows/sandbox-qa.yml:95-239), then upload the state directory as an artifact (.github/workflows/sandbox-qa.yml:241-250).
Where the intelligence actually lives
run: |
set -e
/opt/homelab/sandbox/bin/sandbox-qa dentolize "$GITHUB_REF_NAME" "${{ inputs.action }}" >> "$GITHUB_OUTPUT"
— .github/workflows/sandbox-qa.yml:91-93
sandbox-qa is a script on the homelab runner host. It is not in this repository. Every behavior the workflow header advertises but that you cannot find in the YAML — auto-planning when no plan exists, honouring scope=failed-only, bounding the fix loop, adding [skip ci] to AI commits, self-redeploying the sandbox, chaining the four stages for full-auto, and the "live agent activity streaming" the PR description mentions — is implemented there.
This is worth stating plainly because it shapes what a reviewer of this PR can verify. The diff proves the GitHub-side contract changed correctly. It cannot prove the agent honours it. See Operating notes & limits.
The contract between the two halves
The script communicates with the workflow through GITHUB_OUTPUT lines (KEY=VALUE on stdout, appended at line 93). The workflow reads back:
| Output | Read at | Used for |
|---|---|---|
QA_REVIEW | :126 | Path to the review markdown |
QA_PLAN | :126 | Path to the plan markdown |
QA_STORIES | :153 | Path to stories.json |
QA_ISSUES | :210 | Path to extra findings JSON |
QA_SUMMARY | :226 | Path to the short summary |
QA_STATE_DIR | :242, :248 | Directory to upload as the artifact |
QA_SLUG | :247 | Filesystem-safe branch slug for the artifact name |
QA_STATE_DIR and QA_SLUG are new in this PR; they replace QA_RUN_DIR. The rename from "run dir" to "state dir" is the naming half of the headline feature — state now persists per branch across dispatches, which is what makes plan able to build on a review that ran an hour ago in a different job.
Read next
- The five actions — what each dispatch does, line by line
- Editable handoffs — the comment-marker protocol
- Issues & reporting — story issues, labels, closing rules
- Artifacts & outputs — the slug fix and evidence trail
- Operating notes & limits — permissions, concurrency, sharp edges
- Migration: modes → actions — old dropdown → new dropdown
Status
Unreleased. This PR is open against main. The PR description states the host-side script is already deployed and was smoke-tested with a real review run on another branch; that deployment is outside this repository and is not evidenced by the diff.