The π§ͺ Sandbox settings block
Business view
Every pull request in this repo gets its own disposable, fully working copy of Dentolize β a real web app, API, and database, seeded with demo data β so anyone can click through a change before it merges. That preview environment has a handful of knobs (do we need the extra Saudi-region stack? the WhatsApp integration? a full demo dataset or a fast minimal one? scheduled jobs on or off?). Those knobs already existed; this PR just puts the control panel where every PR author will actually see it, pre-set to sensible defaults, with each option explained in a one-line comment.
Nobody has to do anything differently. If you never touch the block, your sandbox behaves exactly as it did the day before this PR β same regions, same seed, same everything β with one small addition: your sandbox's app containers now always get an IS_SANDBOX=true variable, because that's baked into the template's default env entry.
Technical view
The block itself
Added at .github/PULL_REQUEST_TEMPLATE.md:9-27 (the diff is fully reproduced below β this is the entire change in this PR):
two_regions: false # regional branches only β also bring up the ME-1 region + its own S3 mock
whatsapp: false # bring up the whatsapp-official service
cron: true # run scheduled jobs (set false for a frozen dataset)
seed: rich # rich = full demo dataset Β· basic = minimal seed
env: # extra environment variables for the app containers
IS_SANDBOX: "true"
It sits under a ## π§ͺ Sandbox heading with an HTML comment above the fenced block explaining the semantics (visible in the template source, not rendered in the PR body).
Where each key is consumed
The block is plain YAML in the PR description β GitHub doesn't do anything special with it. It only becomes meaningful because .github/workflows/sandbox.yml looks for it. That workflow (unchanged by this PR β it already handled this block before the template existed) does the following, in order:
- Extract β an
awkprogram (sandbox.yml:97-103) pulls out the first
fenced code block that follows a heading containing "sandbox" (case-insensitive), from the live PR body.
- Convert β if a block was found and
yqis on the runner, it's piped
through yq -p yaml -o json (sandbox.yml:104-105) into a JSON file. If no block exists, or yq isn't available, the settings file is just {} β i.e., every key falls back to whatever the sandbox CLI's own defaults are.
- Consume β the resulting file is exported as
SANDBOX_SETTINGS_FILE
and read by /opt/homelab/sandbox/bin/sandbox β¦ deploy (sandbox.yml:109, 128). That CLI binary lives on the self-hosted runner, outside this repository, so its exact handling of each key (two_regions, whatsapp, cron, seed, env) is not something this repo's source can verify β the workflow only guarantees the JSON gets passed through.
env.IS_SANDBOX specifically
This is the one key whose default value is new behavior, not just newly documented behavior:
- Before this PR: a PR opened with no
## π§ͺ Sandboxblock in its
description β SANDBOX_SETTINGS_FILE is {} β no env overrides are passed to the deploy β app containers get no IS_SANDBOX variable.
- After this PR: a PR opened without editing the template β the template's
own text (which now includes the block) becomes the PR body β the awk extractor finds it β env.IS_SANDBOX parses to the string "true" β the deploy passes IS_SANDBOX=true into the server/queue/cron containers.
The PR description calls out that this was verified end-to-end against the workflow's exact parser: two_regions=false, cron=true, seed=rich, env.IS_SANDBOX="true", with IS_SANDBOX staying a YAML string (quoted) so it doesn't get coerced to a boolean during the YAMLβJSON conversion β Python / JS truthiness would otherwise turn an unquoted true into True or a native boolean depending on the consumer, which could break a shell export IS_SANDBOX=$value expecting the literal text true.
Gap between code and docs: as of this diff, grep -rn IS_SANDBOX across packages/ in this repo returns no matches β nothing in the Dentolize application code (server, clinic-web, clinic-mobile, etc.) reads this variable yet. It's plumbed through the sandbox deploy path but not consumed by the app. Treat it as a hook for future sandbox-aware behavior, not something currently observable from inside the running app.
two_regions and the auth-server precondition
The inline comment states two_regions only applies to "regional branches (those with packages/auth-server)". This repo's current worktree has no packages/auth-server directory, so on this branch the flag is inert regardless of its value β consistent with the sandbox.yml:290-292 logic that only renders regional-stack messaging (AUTH_URL, API_ME_URL, ME-1 S3 console) when the deploy actually produced a regional stack.