Repository navigation
feat(staging): sync staging from main automatically, seed it, one way in - #105
Conversation
…ne way in - scripts/staging-sync.mjs applies migrations and deploys changed Edge Functions to brifeyypjauyusmkptkx only, then sets a staging-sync commit status and opens or closes a drift issue. - ops/staging-sync/ holds the user systemd timer that runs it every 5 minutes from a dedicated clone of main, without npm install. - scripts/staging/seed.mjs seeds invented debates, sources and saved results for Pro, trial and expired accounts plus Adam's. It is idempotent. - Protected staging E2E waits for staging-sync on its commit. - PRs that change supabase/ get a note that staging runs main's backend. - docs/staging.md is the one way in; the manual sync procedure is gone. Plan and audit: plans/2026-10-03-staging-loop.md Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…its body The search index lagged during a drill and a retry opened a second issue. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Warning Review limit reachedEnable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Next included review available in 24 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (17)
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing. Comment |
- Pass --agent no to every Supabase CLI call and ask for JSON from the migration dry run. Outside an agent the CLI printed text, so the timer would have refused every migration. - Fail the sync while a function is deployed on staging but gone from the commit, instead of reporting success. - Split staging E2E into a gate job that waits for staging-sync on its commit and skips commits main has moved past, so it never tests another commit's backend. - seed.mjs --session <account> signs agents in as trial or expired accounts, which demo codes would turn into Pro; --owner-tier switches Adam's plan. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Seeded staging, driven through a local build pinned to staging ( Trial and expired accounts, signed in with seeded-desktop.webmseeded-mobile.webm |
…er run - Give each seeded chat message its own created_at. Chats load ordered by that column alone, and a bulk insert gave every message the same time. - run.sh holds a new commit's sync while Protected staging E2E is still testing an older commit, so no run passes against a backend that changed under it. The browser job now times out after 30 minutes, which bounds the hold. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…; report refusals A failed run-list query no longer counts as no active run. With --report, a refusal before syncing (expired login, wrong target) now sets a failed staging-sync status and opens the drift issue instead of failing silently. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
run.sh queried the last 20 runs, so a busy PR stream could hide an older run that was still testing. It now asks per active status. The browser job checks that main and staging still run its commit on every attempt, because re-running failed jobs skips the gate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… runs Runs for the synced commit were exempt, so an hourly drift repair could redeploy under them. Only runs still waiting in the gate are exempt now. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The real PR preview works on a phone viewport (Pixel 7). This is Also on this head, a real phone-preview.webm |
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nning browser jobs - Deploy dates only say when a function was deployed. The sync now downloads what staging runs and compares it byte for byte with the commit, so an older checkout deployed after a merge no longer passes as current. Proven against a checkout from before #95: dates said nothing was pending, and the content check flagged the 13 functions that run the newer cors.ts. - run.sh holds a sync only while a browser job is running. A queued or gated run isn't using staging, and the job timeout doesn't cover queue time. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A browser job could start between run.sh's check and a drift repair while the old success status still stood. The sync now sets staging-sync to pending before any change, so new browser jobs refuse to test. It then checks once more for a running job, and if one started it restores the status, changes nothing and exits 3, which run.sh retries on the next tick. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
grep -q exiting early sent gh SIGPIPE, and under pipefail the step read that as no marker and posted a duplicate note. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # docs/operations.md








Staging now follows main by itself. Within about 5 minutes of each commit on main, a timer on the VPS applies migrations and deploys changed Edge Functions to staging, reseeds invented but realistic data, and reports the result on the commit. docs/staging.md is the one place that says how to get in.
Plan and audit: plans/2026-10-03-staging-loop.md
Split with other PRs
VITE_SUPABASE_ENVpin, thedebatelab-stagingVercel project, preview CORS and the Auth allowlists. I had my own version of this and dropped it.What changes
scripts/staging-sync.mjsrefuses to run unless the Supabase CLI seesbrifeyypjauyusmkptkxnameddebate-trainer-staging, and it refuses whenSUPABASE_PROJECT_IDpoints anywhere else. It passes--project-refto every CLI call. It applies pending migrations only afterdb push --dry-runlists exactly those files, then deploys missing, stale or wrong-JWT functions (neverworkspace-invites) and rechecks. It stops if staging's migration history diverges from the commit. With--reportit sets astaging-synccommit status and opens, comments on or closes the issue "Staging is out of sync with main".ops/staging-sync/holds a user systemd timer that fires every 5 minutes,run.sh, which keeps a dedicated clone of main and never runsnpm install, andinstall.sh. A synced commit is rechecked hourly, so hand deploys show up as drift.scripts/staging/seed.mjsandfixtures.mjscreateseed-admin,seed-pro,seed-trialandseed-expired, and make Adam's Google address a Pro admin. Theseed-prodemo code is kept in~/.config/debatelab/staging-access.envon the VPS. Each account gets copies of three invented debates with sources and saved results. Rows have fixed ids and are upserted, so a rerun is safe. The seed refuses to write unless the fetched keys carry staging's JWT ref.--session <account>signs agents in as any seeded account (a demo code would make it Pro), and--owner-tiermoves Adam's account to trial or expired until the next seed.staging-syncsuccess on its commit and skips commits main has moved past. The browser job rechecks this on every attempt and times out after 30 minutes.run.shholds a sync while a browser run is testing. The workflow gainsstatuses: read.staging-preview-note.ymlposts one comment on any PR that touchessupabase/migrationsorsupabase/functions. The comment says the PR's staging preview runs main's backend until merge.docs/staging.mdis new.operations.mddrops the manual sync procedure, because needing approval for every run is why staging drifted.AGENTS.md,testing.md, the docs index and the plans index now point todocs/staging.md.Credential and blast radius
Adam chose to have the timer use the Supabase CLI token already on the VPS. That is a classic token, and it can reach production. The ref checks keep the sync on staging, but they are not a security boundary. Whoever merges to main controls the script the timer runs, and it runs within 5 minutes. The follow-up is a Supabase token scoped to staging only.
Outside the repo (done)
debatelab-staging-sync.timeron the VPS. Linger is on, andnode,supabaseandghall resolve under the unit'sPATH. The first run on main exited cleanly, because main doesn't have the script yet.debatelab-staging. It is stored asVERCEL_STAGING_BYPASSin the GitHubstagingenvironment and in the VPS access file. Without it a preview returns 302; with it the preview returns 200.Verification
All checks ran locally. GitHub Actions jobs fail in 2 to 3 seconds with zero steps because the account is out of minutes, so there is no CI pass. Copilot is out of quota, so Codex (
gpt-6.1-sol, high) did the reviews: five rounds, each finding checked against the code and fixed (see the commits).On the branch, after merging main (#95, #100, #101):
docs:check,lint,typecheck, unit tests (278),test:edge(344) andbuildpass atc6a90c5. Atde705f8, before ci: three-minute PR check and two previews per PR #95 cut the unit suite, the full unit suite with coverage (1232), collaboration (30), edge, build andtest:swalso passed. Desktop and mobile browser E2E passed at34d2ec0(250 passed, 6 skipped).097aeb3against synced staging (2 passed, 28.4 s, including one real AI request). It ran on port 18080 with the seeded Pro code, because the local copy of the CI code has expired.staging-sync.mjs --reportrun outside an agent environment deployed 13 functions to staging, rechecked, and setstaging-syncto success. The code was byte-identical to main, which the ci: three-minute PR check and two previews per PR #95 thread had already deployed. This branch's merge commit dated the functions as changed.debatelab-stagingVercel preview works on a phone viewport. Every request went to staging, the seeded Pro sign-in worked, and the seeded debate showed (screenshots and video in the comments).SUPABASE_PROJECT_IDset to production is refused with exit code 2, and with--reportthe refusal sets a failed status and opens the drift issue.Not yet proven:
🤖 Generated with Claude Code