Sandbox & CI wiring
Business view
The seeder is only half the story; the other half is how the CI/sandbox system runs it and surfaces the results. This PR:
- Teaches the PR preview comment to advertise a rich demo dataset (when the
dynamic seeder ran) vs a basic auto-seed, and to list the whole staff roster plus a docs site link.
- Adds a Sandbox Ops workflow to reseed / reset / redeploy / destroy / refresh
a preview on demand.
- Adds a Sandbox QA workflow that runs an AI agent to plan and (optionally)
execute test "stories" against the sandbox.
Technical view
How the seeder talks to the sandbox CLI · report.js:32-51
The summary printer emits two machine-readable blocks the homelab sandbox CLI parses out of the captured seed log:
=== SANDBOX_SEED_INFO === # report.js:32-39
SEED_MODE=rich
SEED_COMPANY=<companyLoginName>
SEED_COMPANY_ID=<uuid>
SEED_EMAIL=<ownerEmail>
SEED_USERNAME=owner
SEED_PASSWORD=<password>
=== END_SANDBOX_SEED_INFO ===
=== SANDBOX_SEED_USERS === # report.js:49-51, owner first
owner Owner · All Permissions
doctor1 Dr 1 · 45% Commission · Salary 4000
…
=== END_SANDBOX_SEED_USERS ===
These marker lines and the KEY=VALUE format are a contract — report.js:4-7 warns to keep them stable. SEED_MODE is always rich from the dynamic seeder; the "basic" path comes from a different (overlay) seeder.
The PR comment · .github/workflows/sandbox.yml
The Sandbox workflow runs on the self-hosted runner and (deliberately) does not use actions/checkout — the sandbox CLI manages its own checkout tree; a checkout step would grow the runner's persistent volume every PR (new header comment, sandbox.yml). Changes in this PR:
- New "Generate branch docs site" step, PR-open only, detached, best-effort
(sandbox-docs … generate --detach || true) — the site this walkthrough is part of, appearing at https://<slug>.docs.anastawfik.com after ~15–40 min.
- The comment now reads
SEED_MODE,SEED_USERNAME, andSEED_USERSoutputs and: - labels the clinic user block "rich demo dataset — dynamic seed" vs
"basic auto-seed, idempotent" based on seedMode === "rich";
- shows the username ("works instead of email") when present;
- appends an "All clinic logins" block from the roster (same password for
everyone), when SEED_USERS is non-empty;
- adds a Docs link line.
Sandbox Ops · .github/workflows/sandbox-ops.yml (new)
A workflow_dispatch with an action choice: reseed, reset-data, redeploy, destroy, update-docs. It invokes the sandbox CLI for the chosen branch and then refreshes the same <!-- sandbox-preview-comment --> on the PR. This is the "refresh via Sandbox Ops → update-docs" the PR comment points to.
Sandbox QA · .github/workflows/sandbox-qa.yml (new)
A workflow_dispatch with a mode choice: full-auto, plan-only, execute. It runs a QA agent against the live sandbox and then, depending on mode:
- plan-only → posts the full plan for human review as a
<!-- sandbox-qa-plan --> comment.
- full-auto / execute → creates labeled GitHub "story" issues
(labels ai-automated and a per-PR label) and posts a short <!-- sandbox-qa-report --> summary comment, uploading run artifacts.
These workflows are the automation layer that a rich, deterministic dataset makes possible: predictable data is what lets an agent write and check test stories reliably.
The one-line fix that unblocks it · seedFeatureFlags.js
seedFeatureFlags.js changed its import from ../prisma to ./prisma (seedFeatureFlags.js:1) — a corrected relative path to the Prisma client so the feature-flag seeder resolves correctly in this layout. Small, but it's part of the same "make the sandbox seed cleanly" effort.