Skip to main content
Wanderer’s Guide releases reviewed feature branches to main. GitHub Actions verifies the code and archives a release manifest. The production frontend is served through Render and the API through Supabase project fdrjqcyjklatdrmjdnys. The Docker main function is a self-host router and must never be included in the cloud function inventory.

Release checks

Install frontend dependencies first; the release CLI reuses the repository’s TypeScript and Zod dependencies. It performs read-only checks and does not deploy, apply migrations, repair migration history, or delete remote functions.
Remote verification requires Supabase CLI and a management access token in SUPABASE_ACCESS_TOKEN. On macOS it can use the existing Supabase CLI native credential. Never paste the token into a command argument or report. The check downloads each cloud function into its own temporary directory, compares all runtime source files, reads schema metadata through Supabase’s read-only SQL API, and removes the temporary downloads. It checks function inventory again afterward to reject a snapshot collected during another deployment. The function-only check reports deployed source and gateway drift without querying the database, so it can run before a pending schema migration. The full check evaluates each distinct migration requirement query once and identifies failed predicates by check name and predicate ID. For checks larger than the management API request limit, use the same full gate with a direct database transport:
Configure that environment variable locally with the project’s direct or Supabase pooler URL for the postgres database. Do not place the URL or password in command arguments. This option requires psql with libpq 17 or newer and system CA roots. For a project CA, set PGSSLROOTCERT to the absolute path of the certificate downloaded from Supabase Database settings. The default is system. It verifies the TLS hostname and certificate, disables inherited libpq connection overrides, runs each original requirement in a read-only transaction with bounded statement/lock timeouts, and verifies the server’s read-only witness before accepting rows. It does not execute migrations. Function downloads still use the management credential. Transport failures and malformed results stop verification; checks are never shortened or skipped to fit the API. The manifest records the commit, dirty-worktree state, every cloud entry point, explicit gateway policy, shared runtime dependencies, declarations, import map, Deno lockfile, migration hashes and schema requirements. A shared helper change therefore invalidates every function that imports it. Remote verification fails on missing or extra functions, unexpected modules, old source bytes, changed gateway settings, changed schema effects or an uncommitted checkout. It verifies source/configuration compatibility, not provider secrets or remote package/CDN integrity. Preserve the manifest and verification report with each release. CI runs the local checks without production credentials. The owner confirms that Render automatically deploys main. Run the remote gate against the reviewed commit before merging, then verify that the live frontend publishes the new artifact and passes its smoke checks. A Render dashboard login is not needed to trigger that deployment. Render’s exact build command, cache behavior, rollback configuration and automatic enforcement of this gate remain infrastructure audit follow-ups. Until automatic enforcement is verified, the release operator must require a passing remote report before merging to main. Any provider release step should use the same command above, rather than maintaining a second inventory or schema checker.

Reconciled database baseline

Production had no supabase_migrations.schema_migrations ledger when inspected on September 5, 2026. supabase/release/baseline.json records the hashes of the seven historical migration files and 134 observed schema objects/grants. This confirms their resulting effects, not the order or mechanism by which they ran:
  • Character updated_at is non-null with its update trigger and function.
  • Anonymous and authenticated roles can read public profile/campaign columns, and cannot read public_user.api, public_user.patreon or campaign.join_key.
  • The campaign join-grant table has the expected keys, RLS with no public policies, and membership trigger/function.
  • All 13 content tables have timestamp columns/triggers, 12 child tables bump source timestamps, and both functions exclude generated search_tsv columns when checking whether content changed.
  • All 12 content metadata columns are JSONB with GIN indexes.
The read-only query is supabase/release/schema-state.sql. Baseline format 2 names each fingerprint with a sha256 field, making its algorithm explicit even when the schema identifier names a protected column. These fingerprints describe schema definitions and migration files; they contain no account credentials. Definition hashes pin the inspected database functions, enabled triggers, columns, indexes, constraints and grants. A difference requires investigation and a reviewed compatibility update. Do not overwrite the baseline from an unreviewed production snapshot just to make a failing gate green. Do not run supabase db push against this project to replay historical files. Do not run migration repair merely to create a ledger. The baseline historical migration bytes remain immutable; use new migrations for new effects. The reviewed Treasure Vault compatibility wrappers preserve their original migration bodies and release queries byte-for-byte and depend on the versioned terminal helper. They do not authorize rewriting historical effects. The terminal helper also recognizes one exact source-correction successor: both Dragon Damage Types tables use Bludgeoning for conspirator or horned dragons, and Major Resonating Fork uses +19 in its description and casting metadata, with DC 29 unchanged. All three complete corrected records must match together. Mixed states and unrelated field changes still fail. Historical comparisons retain the original ledger; this allowance does not modify saved items or permit later arbitrary edits. The helper permits direct execution by postgres, service_role and the provider’s existing supabase_read_only_user. Its exact ACL excludes PUBLIC, anon and authenticated, has no grant options, and does not grant role membership. Every new migration needs an entry and read-only effect check in supabase/release/requirements.json. If a ledger is introduced in a separately reviewed reconciliation, the gate also checks recorded post-baseline versions and rejects unknown versions. Absence of a ledger never substitutes for effect checks. 20261015000000_rust_cloud_release_compatibility.sql recognizes only the exact cleaned Rust Cloud description already present in the catalog. The complete row must match, including all rules, identity and source fields. The shared comparison then projects it to the historical representation without writing spell data. The upgrade requires the exact predecessor body, metadata and permissions, and verifies the complete successor. Historical repair bodies and ledgers remain unchanged. Native checks cover both descriptions, rejected drift, pending edits, upgrade replay, full historical replays and complete rollback preservation.

Legacy Heal Companion and Incarnate recovery

20261011000000_restore_heal_companion.sql restores only the missing deprecated Heal Companion feat, ID 22108, UUID 7476666523704164. It retains the original source, citation, description and repaired Ranger operations. An exact existing row is a no-op; conflicting IDs, UUIDs, names or citation aliases, changed source dependencies and pending curator edits abort. It does not edit saved characters, moderation state or sequences. The insert must refresh source 14’s cache token; a missing or disabled cache trigger, different readback or sequence change aborts the transaction. The original grant requirement remains, with an additional whole-row recovery requirement. Before production application, independently verify that the actual ability-block identity sequence safely allocates beyond the restored historical ID. This is an apply precondition, not established by the migration’s unchanged-sequence postcondition or an isolated fixture’s synthetic allocations. Run this bounded read-only witness using the approved connection and require passed=true:
Missing access, a different sequence configuration or a failed witness blocks application. Investigate separately; never advance, reset or replace a sequence to make this recovery pass. 20261011000100_incarnate_release_compatibility.sql validates one exact Incarnate trait successor: ID 2584 retains every field and UUID while its source changes from Rage of Elements (15) to Common Core (3). It does not move or edit content. The read-only compatibility wrapper preserves the complete original Tech Core predicate and its complete source-only successor: all 82 spells, 92 dependencies, seven source projections and eight binding literals remain required. Normalized identity-alias and pending-edit guards apply to both states, including when the complete original predicate passes without the successor guard. Other source moves, partial states, duplicate identities and unrelated edits fail. The historical migrations and original release queries remain unchanged. The Tech Core citation checks additionally accept one complete, paired successor: Junk (5225) cites its Tech Core trait page and Station (5236) cites its own trait page. Every other field in both records remains exact. The original introductory state still permits Station to be absent; the cited state requires both complete records. Mixed old and cited records, unrelated metadata changes, identity aliases and pending curator edits fail. Both original Incarnate source choices retain the complete spell, dependency, source and binding predicates. The historical query files remain explicit native-fixture inputs; the three requirement registrations use the additive citation checks without changing the registered migration inventory. For Tech Core’s source header, NULL and an empty required-source array both mean no dependencies. The additive checks accept only these two representations; nonempty arrays, including arrays containing NULL, remain invalid. All other source fields and content guards remain exact. Docker bootstrap and the two CI replay passes use this complete successor check instead of the obsolete guard. The focused construction and native-plan contracts run with:
Construction checks do not execute SQL. The hosted-only native entry requires an owner-verified, isolated fixture and executes the focused 225-case matrix. Its receipt names the exact executed controls; the larger prepared matrix is not implicitly covered. Neither result is a whole-book audit or a production check. Before applying recovery, require isolated native PostgreSQL apply, idempotence, rollback, sequence, cache and negative-case tests against the reviewed SQL hashes. npm run test:release includes the inert native-fixture preparation contracts. The reviewed 360-case plan can also be inspected without Docker, SQL or network:
Require a native receipt bound to the current reviewed SQL hashes. A predecessor 358-case receipt does not validate the shared outer alias guard or the two new original-state duplicate-name cases. Explicit --execute-owned-native --output /private/tmp/<new-attempt> runs one bounded, network-disabled fixture and removes only its verified owned container. The runner requires the reviewed cached PostgreSQL 17 linux/arm64 image by full image ID, never pulls an image and never controls existing containers. That execution route is local, not a GitHub amd64 CI job. Independently pin and review the matching fixture image before extending native execution to another platform; do not replace the image identity with a mutable tag. The fixture uses actual content-table constraints and cache migrations, synthetic moderation data and explicit synthetic spell IDs resolved by real UUIDs. It is not a production, Supabase Auth or RLS proof. Preserve the SQL pins and native receipt for review. Expected SQL rejections require psql’s completed ON_ERROR_STOP script-error exit code 3 and the expected diagnostic. Timeouts, signals, buffer failures and other exit codes fail the attempt even if partial stderr contains that diagnostic. Each SQL receipt retains the process termination fields before validation. Use ON_ERROR_STOP and one transaction for each reviewed migration. Recheck the fresh mutation guards at execution, independently read back both effects, and rerun the full read-only release gate against the frozen candidate commit. A private proposal or partial gate is not release approval. Do not merge until all required checks pass; do not replay historical migrations to restore this row.

Rollout order

  1. Freeze the reviewed commit and preserve the previous compatible release manifest, function source, frontend artifact and provider configuration. Check CI/E2E and the current remote report. Investigate differences instead of treating them as permission to overwrite production.
  2. Apply additive prerequisites before dependent functions. Apply 20260905000000_edge_work_budget.sql using the approved database connection, ON_ERROR_STOP and a transaction. Its quota table/RPC is compatible with old handlers; new paid-work handlers return 503 if it is missing. The read-only requirement verifies the reviewed RPC body and service-role-only access. Apply 20260921000000_fix_dominion_epithet.sql in the same phase; it replaces only the duplicated nested identifiers for the Trespasser option. Its read-only requirement verifies both Dominion options and all four nested operation identifiers remain distinct. No historical migration replay is needed. Apply 20260924000000_fix_reclaimant_plea.sql for the three Reclaimant Plea records. It repairs their boolean heightening switch and third innate spell, preserves saved selection identifiers, and aborts on unexpected content changes or pending curator submissions. Its read-only requirement checks all three records; it does not modify characters or homebrew. Apply 20260925000000_fix_rootball_intelligence.sql for Rootball Chair’s Intelligence modifier. It changes only the published template’s +4 to -4, preserves operation identifiers and other attributes, and aborts on an unexpected value or pending curator submission. Its read-only requirement verifies the corrected modifier. Existing saved companion copies are untouched.
  3. Deploy the complete cloud inventory from the reviewed checkout. Use node scripts/release.mjs function-names as the name source. Deploy those names explicitly with Supabase CLI and the repository supabase/config.toml; do not use an unscoped deploy-all command or a global --no-verify-jwt override. A shared-module change must deploy all its dependents, and deploying the full 55-function inventory is the simple safe default.
  4. For a future restrictive grant/schema change, first deploy handlers compatible with both schemas, smoke-test them, then apply the restriction. July 18’s public_user permission failure is the reason this order matters.
  5. Run semantic smoke checks: public content and a public profile; authenticated own-profile and character reads; an authorized test-character save followed by a read; conflict handling; admin authorization and rejected anonymous paid-work requests. Use dedicated test identities/data. HTTP 200 alone is not success: inspect the JSend status and saved value. Expensive service success tests should use a controlled quota and approved test input.
  6. Retire obsolete functions after consumer review, then run release:verify again and require a passing report before publishing the frontend. On September 5 the unexpected live routes were find-campaigns, find-characters, find-encounters, get-sheet-content and self-host main. Seven complete UTC days (August 29–September 4) contained 1,764,530 function-edge events and zero events for all five routes, including OPTIONS. Their source bundles and gateway inventory are backed up under .agents/legacy/infra-release-2026-09-05/. This supports retirement but does not establish that dormant clients do not exist. A retained compatibility route must be brought back into reviewed source with current authorization and tests; do not simply allowlist an opaque old bundle.
  7. Merge the verified commit to main to trigger Render’s auto-deploy. Confirm the live entry point references the new frontend artifact, inspect the rendered app, repeat public/authenticated/save smoke checks, and inspect available logs for permission, auth, worker and paid-work failures. Archive the final manifest and successful report. Provider telemetry and semantic checks remain necessary even when hashes agree.
For War of Immortals hazards, first apply 20260928010000_hazard_type.sql. Then deploy create-creature, find-creature, and search-data with the current shared helper; deploy the full cloud inventory to satisfy the release gate. Smoke-test ordinary creature reads, search, and an authorized test create before applying 20260928020000_war_of_immortals_hazards.sql. Afterward, confirm the five hazards appear only in hazard results and that hazard edits are rejected. Old find and search handlers do not distinguish hazards from creatures.

Rollback

Stop promotion on any failed gate or smoke check. Restore the previous verified frontend artifact and compatible function set using their saved manifest. Restore all affected shared-module dependents together; restoring one entry point does not restore the helper bundled into its neighbors. Keep additive quota schema in place when rolling back callers. Never roll back to a handler that reopens anonymous paid work; disable the affected costly route or retain its authorization patch while resolving an upstream problem. Never restore public SELECT access to protected account columns or campaign join keys to make an old handler work. Prefer a forward handler fix when a schema restriction makes the old release incompatible. After rollback, run the remote gate against the rollback checkout and repeat semantic profile/save checks. If no compatible rollback artifact has been tested, record that limitation and agree on the recovery plan before promotion; do not claim rollback readiness. Dashboard configuration review and a restore drill remain necessary to establish that capability.

Sanitized content snapshots

The GitHub content-snapshot workflow runs weekly on Tuesday at 09:00 UTC or by manual dispatch. Code pushes no longer trigger production exports. Exports are serialized, have a 15-minute job deadline, a 60-second statement deadline and a 5-second lock deadline. An export failure does not deploy or change production. Account, character, campaign, encounter, moderation, join-grant and quota state are excluded from the production data export. Sanitized content supports local development; it is not a backup of user data. Production backups, restore drills and provider resource monitoring are separate operational responsibilities.

Authorization rollout for the next release

Deploy update-user, gm-add-to-group, gm-regenerate-code, and create-content-update from the reviewed commit before applying 20260905010000_account_moderation_writes.sql. These handlers support both permission sets. Run the account-setting and moderation smoke checks, then apply this new restriction transactionally. Never restore broad profile or moderation writes to make an old handler work. The added release requirement checks INSERT/UPDATE column grants as well as table privileges and policies; the old baseline remains immutable. No existing profiles are deleted or demoted by this migration. The production preflight found no duplicate user IDs and confirmed the server-owned signup trigger. Existing elevated accounts require a separate identity review before cleanup.

Browser diagnostics and semantic monitoring

The app reports a maximum of one event per failure category per page to report-client-error. Supabase function logs receive a fixed category, page category, build revision and random trace ID. The endpoint rejects extra fields, accepts at most 512 bytes and caps logging at 60 reports per minute per isolate. These are sampled operational signals, not an exhaustive error census. Provider log retention and alert delivery still need to be configured for the desired response window. The Production health workflow runs four read-only probes every 15 minutes: frontend HTML, Auth health, a known content source and source timestamps. It rejects HTTP-200 failure envelopes and malformed data. It does not submit content, write characters or invoke paid services. Configure the public anonymous key in repository variable WG_MONITOR_ANON_KEY. Repository owners receive failures through their GitHub Actions notification settings; verify that the responsible operator has workflow failure notifications enabled. GitHub schedules can be delayed and are not an availability guarantee. Controlled authenticated save checks remain a release step, not a recurring mutation of a real user’s character. App updates are checked quietly and activate after existing tabs close. They never show a reload toast or interrupt an open editor. Fatal page errors keep technical details in logs and offer a simple link home.

Quiet character saving

Normal saving, successful retries and compatible remote updates run without a status row, toast or recovery controls on the character sheet and builder. Changes to that layout require an intentional UI scope decision. Temporary network failures retry quietly while input remains in the editor. Session expiry, lost editing permission, rejected saves, and unavailable draft storage also stay quiet. An explicitly rejected payload stays buffered and is not repeatedly submitted; a corrected edit can save again. Conflicting edits pause saving while retaining the local input and its original base. Background reads and ordinary edits recheck the competing version, and saving resumes when merging is safe. Calculation failures retry quietly without publishing partial math. There is no download or discard workflow for save history. Calculations also reject a content package that cannot resolve a saved primary or secondary class, ancestry, or background. Unselected Builder choices remain valid; missing selected content must recover before a new result can be published. Retained drafts stay on the device under the same character ID. They do not create additional server characters. Versioned drafts with a known common ancestor can be reconciled automatically. Older drafts without enough information for a safe merge remain untouched; their presence alone does not interrupt the user or replace a newer server save. Recovery verification covers reload, account changes and builder/sheet navigation, with server reads proving saved values and local checks proving unreplayable drafts are preserved. The Cypress saveRecovery scenario also checks that the ordinary sheet has no added saving controls or notifications at desktop and phone widths. Connection, encounter and remote-update scenarios verify real acknowledgements, reopening, and quiet recovery rather than relying on a displayed Saved label.

Recovery evidence and next drill

On September 5, 2026, production had seven completed daily physical backups (August 30 through September 5), with PITR disabled. The newest backup was at 04:44:18 UTC. A daily backup implies up to roughly one day of potential database changes to recover separately; it does not prove a restore time. Database backups cover Storage metadata, not uploaded object bytes. Back up both portraits and backgrounds to an independently recoverable, private destination. See Supabase backups. Before declaring recovery ready:
  1. Select an isolated restore destination, encrypted object-backup destination, recovery-point target and recovery-time target. Keep encryption-key recovery separate from the backup; never put plaintext user exports into Git or CI artifacts.
  2. Restore a database backup into that isolated destination, with outbound Discord, email, Patreon and paid integrations disabled. Never restore over production as a test. Record backup age, start/finish times and any missing schema/functions.
  3. Verify profiles, Auth, RLS denial tests, controlled character reads/saves, campaign joins and moderation state. Use dedicated test identities rather than messaging real users. Preserve an evidence report without account data or tokens.
  4. Restore object bytes with their matching metadata and compare counts, sizes and checksums. Load sample restored images through the isolated app.
  5. Record measured recovery time, cleanup and gaps. A sanitized development dump or successful backup listing is not evidence that production can be restored.
This release does not enable PITR, purchase a second project or claim an unperformed production restore drill. Those steps require the chosen destination and recovery policy.

Shared base ancestries

If production still has the original terminal helper, first apply 20261008105800_treasure_vault_terminal_catalog_compatibility.sql. Its wrapper upgrades the exact original helper through the reviewed intermediate definition to the current successor without writing content. Then apply 20261010000000_shared_ancestry_release_compatibility.sql before 20261010000100_shared_ancestries.sql. The first upgrades only the exact reviewed terminal helper and retains its complete historical ledger, metadata and grants. The second moves Elf, Dwarf, Gnome, Goblin, Halfling and Orc and their canonical traits from Player Core to Common Core. Existing IDs, descriptions, operations, languages, senses and saved characters remain unchanged. Source-derived UUIDs are recomputed using the existing upload rule. Human already belongs to Common Core. The compatibility proof accepts only all twelve original rows or all twelve complete successors. Partial moves, unrelated edits, duplicate identities and pending submissions under either source or UUID remain failures. Historical migration bodies and release predicates remain intact inside their wrappers. Existing Bagpipes classifications and the three source corrections retain their separate exact allowances. Rival Academies’ reviewed feat-count successor is limited to 78 becoming 79. Every other source field must match, and the live feat count must independently be 79. This accounts for the published Lesson of Memory addition without rewriting book metadata or accepting arbitrary later changes. Preserve full preflight, backup, independent readback and the remote release report for the rollout.

Incarnate release compatibility

The Incarnate trait was moved from Rage of Elements to Common Core after the Tech Core general spell import. 20261011000100_incarnate_release_compatibility.sql is a guard-only transaction. It accepts the exact original trait or the exact source-only successor; it does not move content or write spell or trait rows. Both registered requirements use tech-core-general-spells-incarnate-compatibility.sql. Its two branches retain the complete original spell, reference, source and binding checks. All other row fields must match, Common Core must be published, and duplicate identities or pending submissions for either source remain failures. The original migration and release file remain unchanged and captured in the native input manifest. Native verification exercises both source states, alias and pending-submission rejections, complete no-op replay preservation and unrelated dependency failures in addition to the original general-spell control matrix. Historical migration replays and the full release gate must pass before this compatibility change is merged.

October curator compatibility

20261026010000_curator_release_compatibility.sql recognizes the exact reviewed Scent link correction and Enthrall rules correction while retaining the complete original Treasure Vault ledgers. It writes no content. Every other row field, reference, source, helper setting and grant remains pinned. Enthrall’s successor UUID also receives duplicate-identity and pending-submission checks. The early helper installer includes this same successor for complete historical replays. Existing upgrade installers retain their exact previous SQL inside an outer compatibility wrapper. Native rollback controls cover both original and successor combinations, upgrade/replay, unrelated edits, queue aliases and helper drift. Apply only the new dated compatibility migration to production. 20261027000000_curator_book_count_compatibility.sql additionally recognizes the published Litany Against Wrath and Litany Against Sloth feats in Core Rulebook. The book counter may change from 282 to 284 only when the real feat count is 284, both complete published rows match, no duplicate identity or unresolved matching submission exists, and every other book field is unchanged. A counter edit alone fails. The original content, template, source and curator checks remain intact; this migration changes only the helper definition. Native rollback controls cover the original counter, the complete successor, missing or altered feats, unrelated metadata and content drift, rejected helper predecessors, and historical replay. 20261026000000_mine_mod_rules.sql repairs eight existing Tech Core records: Maneuvering Mines, Healing Mines, Aerial Hologram, Aerobatics Mastery, Expert Mechanic, Programmer, Auto Spellshape and Magical Shorthand. It compares complete original rows and refuses pending curator work before adding operations. Verify its registered release predicate and source cache timestamp after application.