Skip to content

v0.13.2

Latest

Choose a tag to compare

@thatskiff33 thatskiff33 released this 23 Jul 08:13
a41d913

AlpineClubBookingsNZ v0.13.2

Release target: v0.13.2 on 2026-07-23. This patch public reference release
brings all changes landed after v0.13.1 into one supported tag. It carries
five operator-relevant workstreams, one of which needs deliberate handling at
deploy time:

  1. The club-theme orphan-column expand→contract pair (#2187). The theme
    rebuild dropped from seven hand-picked brand colours to three seeds, leaving
    four former ClubTheme columns dead to code. This release ships both the
    additive expand migration that lets the new runtime stop naming them and
    the destructive contract migration that drops them — so the contract half
    needs the blue/green handling described below and is legal only once the new
    runtime has drained the previous colour
    .
  2. Config-bundle format version 2 (#2187). Configuration bundles now export
    at format version 2; a bundle exported by an older app (version 1) is refused
    on import with a clear message rather than importing stale colour columns.
  3. Guided provider setup wizards for Xero, Stripe, and Google sign-in
    (#2080/#2081/#2082/#2087).
    Provider credentials, already DB-only since
    v0.13.1 (#2079), are now captured through step-by-step in-app wizards.
  4. The opt-in AI help assistant (#2094). A new, default-off, spend-metered
    module that answers members' free-text questions grounded strictly in each
    page's curated help.
  5. Backup configuration re-homed to the encrypted store (#2095). The runtime
    now reads backup settings only from the in-app credential store; the
    legacy BACKUP_* environment variables are no longer honoured, and an
    unmigrated legacy config makes the nightly backup report FAILURE until the
    operator re-enters credentials in the new backup settings UI.

The canonical detailed entries remain in CHANGELOG.md; this page is the
release/operator overview.

⚠️ The ClubTheme orphan-column drop is destructive and must not run while a
pre-#2187 colour is still draining.
The four dropped columns
(brandCharcoal / brandRidge / brandMist / brandSnow) were named in the
SQL of every colour up to and including v0.13.1 — Prisma projects every
scalar of a model in an unnarrowed SELECT and in a mutation's implicit
RETURNING. Dropped while v0.13.1 is still the draining colour, the contract
migration breaks the old colour's theme reads and writes. The v0.13.2 runtime
names none of them, which is what makes the drop legal — but only after
v0.13.2 has replaced and drained v0.13.1.
Either defer the contract drop
until then (see below) or run it in a later quiet window with the breaking-
migration override. If the previous colour is still live, do not run the
drop.

Highlights

Club theming: three-seed substrate and the orphan-column drain (#2187)

  • Site Style now derives the whole theme from three seed colours instead of
    seven hand-picked ones.
    A vendored Radix colour generator turns one required
    accent plus two optional seeds into the full light/dark 12-step palette, with
    cross-colour text contrast guaranteed by construction, and the whole member and
    admin app — plus the public website — now paints from that generated palette.
  • 20260722140000_expand_club_theme_orphan_column_defaults — EXPAND
    (additive, blue/green safe).
    Adds a DB DEFAULT to each of the four former
    columns so the new runtime can INSERT a ClubTheme row without naming them,
    while the draining previous colour (which still writes explicit values) is
    unaffected. No data is rewritten and no column is dropped.
  • 20260722160000_contract_drop_club_theme_orphan_columns — CONTRACT
    (destructive).
    Drops brandCharcoal / brandRidge / brandMist /
    brandSnow. The four surfaces are derived from the generated substrate at
    render time (deriveBrandShims), so there is zero resolvable data loss, but the
    drop is irreversible without a restore. It carries a full rationale row in
    docs/BLUE_GREEN_MIGRATION_SAFETY.tsv
    (previous_expand_release = 20260722140000_expand_club_theme_orphan_column_defaults,
    old_code_compatible = yes) and refuses to run without the breaking-migration
    acknowledgement.
  • Configuration bundles move to format version 2. A bundle exported by an
    older app (version 1) is refused on import with a clear message rather than
    importing stale colour columns.

Guided provider setup wizards (#2080, #2081, #2082, #2087)

  • Xero (#2080/#2081). A reusable wizard shell walks a Full Admin through app
    creation, credential capture, and the OAuth connect, then completes setup with
    verified webhooks, item-code mappings, and member import, showing an amber
    badge on the setup page while webhook verification still needs attention.
  • Stripe (#2082). Captures its keys into the encrypted store and reads the
    publishable key at runtime, so changing keys takes effect immediately rather
    than needing a fresh build.
  • Google sign-in (#2087). Captures its OAuth credentials in-app, runs on a
    request-scoped NextAuth config, and keeps the module locked until a real Google
    round-trip verifies, so an unconfigured provider can never render a visibly
    broken sign-in path.
  • In all three, legacy provider env vars are detected, warned about, and ignored
    — re-enter the values in the wizard, then remove the env vars.

AI help assistant and the chat-style help widget (#2094)

  • The in-app help is now a chat-style widget on every surface (#2209, #2210).
    A single curated help corpus feeds a widget that answers page-specific
    questions and shows a full page guide, replacing the older scattered help UIs.
  • The AI help assistant module is opt-in and off by default (#2211, #2212).
    With it on, authenticated members can ask free-text questions that a paid AI
    model (Anthropic Claude Haiku 4.5) answers grounded strictly in the curated
    corpus
    . The club supplies its own Anthropic API key, held only in the
    encrypted vault (never an env var). A hard monthly spend cap (default
    NZ$10, integer cents) stops AI answers for the rest of the month once
    reached; the budget gate fails closed, reserving a conservative worst-case
    cost before each call and refusing to spend if it can no longer record usage.
    Enabling the module without a key is harmless — the ask box degrades to a
    structured fallback and curated page help still works. Members are told the
    answer may be wrong and that their question is sent to Anthropic (United
    States); the question text itself is never stored.

Backup configuration re-homed to the encrypted store (#2095)

  • Backup settings move into the encrypted credential store and out of the
    environment.
    The S3 access key and secret, destination bucket and region,
    retention window, restore-validation shadow database, and the on/off switch are
    managed from a new Admin → Integrations → Database Backups page
    (/admin/backups), with a Run backup now action that runs as a background
    job behind a database-level cross-process lock. The S3 secret and the
    restore-validation connection string are write-only and never echoed back.
  • The legacy BACKUP_* environment variables are no longer read. An install
    that configured backups through the old env vars upgrades to an empty store, so
    until the operator re-enters the backup settings the nightly backup reports a
    loud FAILURE rather than silently skipping
    — a stopped disaster-recovery path
    must alert, not disappear. Only BACKUP_CRON_SCHEDULE (cron-leader timing —
    when the nightly job fires) stays in the environment.

Hardening and fixes

  • Config-transfer column audit (#2178). A reverse-drift guard fails the build
    if a club-settings column is neither exported nor explicitly excluded,
    complementing the singleton-table audit (#2200).
  • Season-subscription charge-coverage reconcile (#2179). The guard that flips
    a stale NOT_REQUIRED seed subscription row now checks for an active
    charge-coverage claim (both at read and, relationally, at write) instead of a
    null-check on a to-many relation that was always true, so the reconcile stands
    aside when live coverage exists and proceeds when it does not.
  • Dependency advisories (#2196, #2224). Cleared newly published advisories in
    hono, fast-uri, dompurify, and sharp, and moved Next.js to 16.2.11 to
    clear a middleware-bypass and a Server-Actions denial-of-service advisory.

Compatibility and operator impact

  • Runtime requirements remain Node.js 24 LTS, npm 11+, and PostgreSQL 16.
  • The release contains six migrations: five additive/expand migrations plus one
    destructive contract migration.
    • Expand (no operator action):
      20260722120000_add_integration_wizard_progress (the wizard cursor for the
      guided setup shell, #2080), 20260722130000_add_xero_webhook_validation_receipt
      (the Xero webhook validation receipt sink, #2081),
      20260722140000_expand_club_theme_orphan_column_defaults (the theme
      orphan-column defaults, #2187), 20260722150000_add_backup_run (the backup-run
      ledger, #2095), and 20260723120000_add_ai_assistant (the AI assistant models,
      #2211). Each is additive and blue/green safe.
    • Contract (needs handling):
      20260722160000_contract_drop_club_theme_orphan_columns drops the four dead
      ClubTheme columns. The blue/green validator refuses it without
      ALLOW_BREAKING_BLUE_GREEN_MIGRATIONS=1 plus a non-empty
      BLUE_GREEN_MIGRATION_OVERRIDE_REASON. Because the paired expand migration
      ships in this same tag, the contract drop is legal only after the
      v0.13.2 runtime has replaced and drained v0.13.1
      . Either defer it
      mark it applied without running it (prisma migrate resolve --applied 20260722160000_contract_drop_club_theme_orphan_columns) until the expand-
      carrying release has soaked, then run it in a later deploy — or run it in
      a quiet window with the override once the previous colour is fully drained.
      Set the override for that deploy only, then unset it so the next deploy does
      not inherit it.
  • Backups need re-entry immediately after upgrade (#2095). With BACKUP_*
    env vars no longer read, an install that configured backups through them
    upgrades to an empty store. Re-enter the backup settings at Admin →
    Integrations → Database Backups
    — until you do, the nightly backup reports a
    loud FAILURE (never a silent skip). Only BACKUP_CRON_SCHEDULE remains an
    environment variable.
  • Older configuration bundles no longer import (#2187). A bundle exported by
    a pre-v0.13.2 app (format version 1) is refused on import; re-export from the
    upgraded source install to move configuration between installs.
  • The provider wizards and the AI assistant add no forced action. The
    wizards are the new, guided path to the same DB-only credential store already
    introduced in v0.13.1; the AI assistant module is off by default and does
    nothing until a Full Admin enables it and enters an Anthropic key.
  • A fresh, restore-tested backup is mandatory before the contract drop. There
    is no down-migration: a DROP COLUMN cannot be undone by rolling the app
    back, so restore from backup is the only recovery for the dropped columns
    (their values are derived at render time, so no theme surface is lost, but the
    raw columns are gone).

Upgrade and verification

Follow docs/UPGRADING.md from v0.13.1 to v0.13.2 ("v0.13.1 → v0.13.2"). In
summary: deploy v0.13.2, letting the five expand migrations run; defer the
ClubTheme orphan-column contract drop
(mark it applied without running it)
until the new runtime has drained v0.13.1, or run it later in a quiet window
with ALLOW_BREAKING_BLUE_GREEN_MIGRATIONS=1 and a
BLUE_GREEN_MIGRATION_OVERRIDE_REASON recording the soak and a restore-tested
backup id; then unset the override. After the app is healthy, re-enter the
backup settings
at Admin → Integrations → Database Backups so nightly backups
resume.

After cutover, verify at minimum:

  1. Expand migrations applied cleanly and the app is healthy on the new
    colour; the contract drop is either deferred (marked applied) or run in its
    own quiet window once v0.13.1 is fully drained.
  2. Backups re-entered (#2095). The Backups page shows the durable
    destination configured, a manual Run backup now succeeds, and the nightly
    run reports SUCCESS rather than the legacy-env FAILURE.
  3. Theme unchanged (#2187). A club with a non-default palette still renders
    its colours in light and dark after the orphan-column drop — the four dropped
    surfaces are derived from the generated substrate.
  4. Config bundle round-trips. Export a bundle from the upgraded install and
    confirm it imports; a pre-upgrade (version 1) bundle is refused with a clear
    message.
  5. Provider wizards reachable (#2080/#2082/#2087). Admin → Integrations shows
    the Xero, Stripe, Google sign-in, and Backups cards, each opening its wizard.
  6. AI assistant opt-in (#2094). With the module off, the chat-style help
    widget answers curated page questions on every surface; enabling the module
    and entering an Anthropic key adds the free-text ask box, and the monthly spend
    cap hard-stops answers once reached.

This release does not publish an npm package (package.json remains private).
The supported artefacts are the Git tag/GitHub release and the repository's
container-image release workflow.