Skip to content

Release 0.15.0 - #91

Merged
LucaGerlich merged 11 commits into
masterfrom
development
Oct 12, 2026
Merged

LucaGerlich merged 11 commits into
masterfrom
development

Conversation

@LucaGerlich

Copy link
Copy Markdown
Owner

Summary

Release 0.15.0: phase 1a of the SaaS boundary
(docs/superpowers/specs/2026-10-05-saas-boundary-design.md, plan
docs/superpowers/plans/2026-10-12-saas-boundary-1a.md). The database gains the columns the
control plane will drive, and the legacy billing code starts recording entitlements. Nothing
reads the new columns yet, so enforcement is unchanged.

Merging deploys production, and the production build runs prisma migrate deploy: one
additive migration (20261012_org_entitlements_locked_at, two nullable columns, no default,
no backfill) on the shared database.

Changes

Full detail and upgrade notes: CHANGELOG.md, section 0.15.0.

  • organizations.entitlements (JSONB) and organizations.lockedAt (timestamp), both nullable.
  • GET/POST /api/organizations and GET/PUT /api/organizations/[id] return an allow-list
    of fields: no more plan, Stripe ids, trialEndsAt, maxAssets, maxUsers or
    suspendedAt for org admins, and never the new columns.
  • The Stripe webhook, the trial cron and the register trial record entitlements alongside
    the legacy plan columns while Stripe is configured.
  • scripts/backfill-entitlements.ts: maps each org's legacy plan to entitlements. Dry run by
    default, explicit --schema, printed target, --apply --confirm "<target>", never touches
    rows that already hold a value, skips a row whose plan changed since planning, reports
    invalid stored values.
  • Plans for phase 1a and 1b, spec revision 3, debt register and developer guide updates.

Before merging

  • Preview deployments of development fail on organization reads until this release has
    migrated the shared database (they run 0.15.0 code against the old schema).

After deploying (production database only)

  • Dry run, with the production deployment's real SELF_HOSTED value:
    bun scripts/backfill-entitlements.ts --schema public --deployment-self-hosted <true|false>
  • Review the per-org output (currentAccess, proposed, losesFeatures, usage,
    overLimit), then apply with the printed target:
    ... --apply --confirm "46.62.169.116:5432/postgres/public"
  • Gate to phase 1b: a fresh dry run shows "unset":0 and "invalid":0.

Verification

  • Unit tests: 1571 passed locally; CI on development (cf140e6) green, including the
    DB-backed backfill tests against a throwaway Postgres (5/5) and the Bun bundle check.
  • Type check, lint and build clean.
  • Per-task spec and quality reviews, a whole-branch review and one fix wave.

🤖 Generated with Claude Code

Revision 3 of the boundary spec records the corrections from mapping it against
the 0.14.0 code and the decisions taken while planning.
The Stripe webhook, the trial cron and the register trial now also write the
entitlements column, derived from the legacy plan row. Nothing reads it yet. The
cron and the register trial write it only while legacy billing is active.
describeTarget now throws unless the URL names host, port and database explicitly and has
no host, hostaddr, port, dbname or service query parameter, so the printed and confirmed
target is where pg really connects. The database name and host are printed decoded.

The CLI argument checks move into parseBackfillArgs so they can be tested; it also accepts
--dry-run and rejects --dry-run with --apply and --confirm without --apply. The CLI checks
every column planBackfill reads, including asset and user. Tests cover assertColumns,
qualified and parseBackfillArgs; the DB test measures limits against active users.
Phase 1a of the SaaS boundary: changelog entry with upgrade notes for the
entitlements backfill, debt register cross-reference in item 8a, and the
version bump to 0.15.0.
- describeTarget requires the postgres: or postgresql: scheme (pg reads
  socket: URLs differently) and rejects a percent escape in the host or
  database name when the string holds a space or a malformed escape, the
  case where pg-connection-string re-encodes the whole string.
- applyBackfill only updates a row while plan, maxAssets and maxUsers
  still equal what planning read, so a row changed in between is skipped.
- planBackfill classifies stored entitlements against the strict schema;
  the summary line gains an invalid count, and invalid values are never
  overwritten. The summary is now a tested pure function.
- The credential leak assertions pair .not.toThrow with a positive
  toThrow on the same call.
bun build compiles scripts/backfill-entitlements.ts without running it,
so a broken @/ alias in the script fails the type check job instead of
the production run.
…guide

- CHANGELOG: enforcement is unchanged apart from the API change, one fenced
  command per block with the flag rules after them, all rejected DATABASE_URL
  parameters, the SQL route for per-organization entitlements, the invalid
  count, the full gate 1a to 1b list and the migration order for Preview.
- TECHNICAL_DEBT: item 81 records the response-level test gap on the
  organization routes and that the backfill has never run under Bun.
- DEVELOPER_GUIDE: entitlements and lockedAt on the Organization model.
- 1a plan: describeTarget's interface line and the quoted db-target code and
  test now match the committed files (scheme, explicit port, rejected query
  parameters, percent rule; no default port).
- 1b task-02 and task-13: the quoted before-blocks of the register route and
  the organizations route match the committed files, indentation included
  (marked prettier-ignore so the formatter keeps it), and the nested register
  test replacement is described as nested.
- 1b task-05 and task-18: explicit-port DATABASE_URL for the cut-over script;
  the gate also needs "invalid":0; the new debt items start at 82 because
  1a took item 81.
@vercel

vercel Bot commented Oct 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
assett-tracker Ready Ready Preview Oct 12, 2026 1:17pm UTC

@LucaGerlich
LucaGerlich merged commit 917c487 into master Oct 12, 2026
12 checks passed

This branch was successfully deployed

1 active deployment
Preview — cf140e6e Deployed Oct 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant