Dentolize · Composable Sandbox QA Walkthrough
On this pageRead this first: what kind of change this isBusiness viewTechnical viewStatus

Composable Sandbox QA

PR #241 — ci(sandbox): composable QA actions — review / plan / test / fix Branch ci/qa-composablemain. 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:

ActionProducesCommits code?
reviewA technical + business code review, posted as a PR commentNo
planUser stories to test, built on the review, posted as a PR commentNo
testRuns the plan in a browser → one GitHub issue per story + a summaryNo
fixRepairs the previous test's failures, re-tests, updates the issuesYes
full-autoAll four in one runYes

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 plantest: whatever the plan comment says at dispatch time is what gets tested.

So a realistic session looks like:

  1. Dispatch review. Read the AI's understanding of the change.
  2. It got the insurance-claim edge case backwards. Edit the comment; fix that

paragraph. Delete the two findings that are noise.

  1. Dispatch plan. It builds stories on your corrected review.
  2. Add the two scenarios it missed. Delete the flaky one.
  3. Dispatch test. It executes exactly your plan.
  4. 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

  • test can re-run only what failed. A scope dropdown offers all or

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:

  1. Fetch the human-edited review/plan comments off the PR into files

(.github/workflows/sandbox-qa.yml:60-82).

  1. Invoke the agent — one shell line

(.github/workflows/sandbox-qa.yml:93).

  1. 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:

OutputRead atUsed for
QA_REVIEW:126Path to the review markdown
QA_PLAN:126Path to the plan markdown
QA_STORIES:153Path to stories.json
QA_ISSUES:210Path to extra findings JSON
QA_SUMMARY:226Path to the short summary
QA_STATE_DIR:242, :248Directory to upload as the artifact
QA_SLUG:247Filesystem-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.


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.