Skip to content

fix(db,migrate): repair invalid workspace slugs (047), log why provision refuses its own answer - #414

Merged
ABB65 merged 3 commits into
mainfrom
fix/migrate-provision-log-validation
Oct 4, 2026
Merged

ABB65 merged 3 commits into
mainfrom
fix/migrate-provision-log-validation

Conversation

@ABB65

@ABB65 ABB65 commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

What

Staging E2E: POST /api/migrate/provision answered 502 for an account whose workspace slug is not one Migrate accepts, and logged nothing.

  1. Root cause. handle_new_user() replaced [^a-z0-9-] with - and only then lowercased, so a GitHub name with capitals (ABB65) became ---65-1a2b3c4d. @contentrain/types validates the answer's slug against ^[a-z0-9][a-z0-9-]{0,62}$, so Studio refused its own answer.
  2. Migration 047_workspace_slug_repair.sql.
    • The trigger now lowercases first, collapses runs of other characters to one hyphen, trims hyphens, and falls back to user.
    • Repair rewrites only slugs that fail the pattern (lowercase, collapse and trim hyphens, at most 54 chars, workspace if empty). A collision gets the workspace id's first 8 characters, then a counter. A valid slug is never touched; a second run changes nothing.
  3. Defence in depth. Provision logs the order id, state, slug and the validator's errors before any 502 on its own answer (covered and bound paths). It does not rewrite a slug on the fly: after 047 every stored slug is valid, and the one creation path that did not check length or emptiness already goes through slugify.

What a slug change can break

  • Workspace address /w/<slug> and any email link already sent to it: changes for the repaired workspaces only. Accepted; they were unusable for Migrate anyway.
  • Migrate grants: store workspace_id; the slug is only returned in the answer. Not affected.
  • Billing, CDN, MCP, CLI: use workspace and project ids; no slug stored. Not affected.
  • Uniqueness: workspaces_slug_key is kept by the collision handling.

Deploy order

Managed pair: automatic, the Railway pre-deploy node scripts/migrate-postgres.mjs applies 047 before the image serves. Supabase pair: pnpm db:migrate first. The old image works against 047. Numbering follows #413 (046); 047 applies fine with or without it.

Evidence (local, throwaway Postgres 16)

  • Migrations applied through scripts/migrate-postgres.mjs: 48 applied.
  • Trigger: users ABB65, Jane.Doe_X, __ get abb65-…, jane-doe-x-…, user-….
  • Repair: ---65-1a2b3c4d, ABC_def, Taken (collides with taken), !!!, a 70-char slug and -taken- all become valid and unique; the valid taken stays; second run identical.
  • New contract test workspace-slug.contract.test.ts; full contract suite 163 passed, 1 skipped.
  • pnpm test:ci: unit 2039, integration 487, nuxt 274 green (one earlier unit run showed 4 failures in conversation-engine tests that did not repeat; unit re-run was clean). pnpm typecheck clean, eslint 0 errors.

@ABB65 ABB65 changed the title fix(migrate): log why Studio's own provision answer failed validation fix(db,migrate): repair invalid workspace slugs (047), log why provision refuses its own answer Oct 4, 2026
@ABB65

ABB65 commented Oct 4, 2026

Copy link
Copy Markdown
Member Author

ONAY @ e4aa7f9 — ORK review: 047 repairs only slugs failing ^[a-z0-9][a-z0-9-]{0,62}$ (idempotent, collision suffix), handle_new_user lowercases before replace (managed pair fires the same trigger: managed-auth.ts:18/120); provision logs own-answer validation errors (no checkout URL logged). Merge of main resolved CHANGELOG only. CI ci+postgres-lineage green; local: 49 migrations on temp PG, contract 166, unit 2053. Fixes E2E B15 (covered provision 502).

@ABB65
ABB65 merged commit 1b39285 into main Oct 4, 2026
2 checks passed
@ABB65
ABB65 deleted the fix/migrate-provision-log-validation branch October 4, 2026 21:16
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