On this page
What this PR touches (and doesn't)What to test, and howWhere to actually run thisThings this PR explicitly does not cover — don't file these as bugsFor Quality
What this PR touches (and doesn't)
This is a single-file change to .github/workflows/sandbox-ops.yml (+61/-0). There is no application code, no database migration, and no Dentolize product surface in the diff. QA here means validating a GitHub Actions workflow's logic, not exercising the clinic app — though the outcome of a correct redeploy is observable in the app (e.g. the WhatsApp Bot settings tab, see the Walkthrough).
What to test, and how
Since the compose step is a small, self-contained Python block reading two JSON env vars, it's realistic to test in isolation (outside GitHub Actions) by simulating SANDBOX_VARS / SANDBOX_SECRETS / REF_NAME and checking the printed JSON. The PR description claims this is already unit-tested (in the sandbox CLI's own test suite, outside /work/repo); the same cases are worth re-confirming end-to-end against a real workflow run:
| Case | Expected outcome | Why it matters |
|---|---|---|
SANDBOX_MAIN_WHATSAPP=true (variable), branch = main | {"whatsapp": true} | Core scoping — branch-specific keys apply |
SANDBOX_OTHER_WHATSAPP=true, branch = main | whatsapp absent/default | Cross-branch isolation — no leak into an unrelated branch |
SANDBOX_ALL_CRON=false, branch = main (no SANDBOX_MAIN_CRON) | {"cron": false} | The "every branch" prefix works standalone |
SANDBOX_ALL_CRON=false and SANDBOX_MAIN_CRON=true | {"cron": true} | Branch-specific overrides ALL on the same key |
SANDBOX_MAIN_FOO=bar (variable) and SANDBOX_MAIN_FOO=baz (secret) | {"env": {"FOO": "baz"}} | Secrets win over variables on a name clash |
A variable/secret named github_token, or SANDBOX_MAIN_GITHUB_TOKEN | Never appears in output | No path exists for github.token to be selected — worth confirming this stays true after any refactor of the prefix-matching loop |
An unprefixed variable, e.g. plain SOME_REPO_VAR | Excluded | Only SANDBOX_ALL_* / SANDBOX_<REF>_* keys are read at all |
No SANDBOX_* variables/secrets set at all | {} | Falls through to the CLI's own defaults — equivalent to today's main behavior |
A branch name with special characters, e.g. feature/foo-bar | Maps to prefix SANDBOX_FEATURE_FOO_BAR_ | ref_key derivation via re.sub(r"[^A-Z0-9]+", "_", ...) — worth a case with consecutive separators (feature//foo) to confirm no double-underscore edge case breaks matching |
WHATSAPP=false, WHATSAPP=0, WHATSAPP=no, WHATSAPP="", WHATSAPP=maybe | Only true/1/yes/on (case-insensitive) read as true; everything else, including nonsense strings, reads as false | truthy() has no "invalid value" error path — a typo like WHATSAPP=treu silently becomes false rather than failing loudly |
Where to actually run this
Because the compose step only executes if: inputs.action == 'redeploy', exercising it requires Actions → Sandbox Ops → redeploy against a real branch (a disposable non-main branch is safer for first-pass QA — the prefix mechanism works identically for any branch name once you know its SANDBOX_<REF>_ prefix). Confirm via the run log's 🧪 sandbox settings (from environment) group, then confirm the deployed sandbox's actual behavior matches (e.g. WhatsApp Bot settings tab no longer shows "Feature Disabled").
Things this PR explicitly does not cover — don't file these as bugs
reseed,reset-data,destroy, andupdate-docsactions do not readsandbox-Environment config — onlyredeploydoes (sandbox-ops.yml:65). This is intentional, not a gap.- The automatic PR-open/PR-sync
sandbox.ymlflow is entirely untouched — this mechanism only fires through the manually-dispatchedsandbox-ops.yml. keep_on_mergehas no environment-variable equivalent — expected, since it's a merge-time concept andmainnever merges.