Use ASP.NET Core 10 + EF Core 10 + Npgsql with PostgreSQL hosted on Supabase. EF Core owns application schema migrations. Drizzle is not installed or used.
From the repository root:
dotnet restore app/api --locked-mode
dotnet tool restore
dotnet build app/api --configuration ReleaseSpecThreadDbContext maps six Better Auth tables and nineteen product tables in
public, with the migrations listed under "Combined migration order" below, ending
with RequirementDocuments, RequirementUpdatedBy, RequirementCriteriaVersions,
RequirementMilestone, RequirementFollowUps, RequirementCriteriaVersionTexts,
RequirementEvidenceEvents, and RequirementEvidenceSuggestions. Startup never calls EnsureCreated, Migrate, or a database
query. /health checks process liveness only. Resolving the context without
ConnectionStrings:Database fails explicitly. Playwright verifies this failure
and provider initialization with dummy credentials, without a database connection.
Get the connection details from the Supabase project's Connect dialog.
Use a direct connection for a persistent API with IPv6 connectivity, or the
session pooler for an IPv4-only host. Copy the actual host and username from
the dashboard. Use TLS certificate verification (SSL Mode=VerifyFull).
On Windows the Supabase root CA is not trusted by default, so the API fails with
"Exception while performing SSL handshake". Add
Root Certificate=<absolute path to app/api/certs/prod-ca-2021.crt> to the local
connection string. Render does not need it (docs/DEPLOYMENT.md).
The session pooler (port 5432) accepts 15 connections in total for the project, shared
by everything that uses it. The deployed web app therefore connects through the
transaction pooler (same host, port 6543), which is meant for serverless apps and is
not limited to those 15: every Vercel instance and preview opens its own small pool, and
together they used to exhaust the session pooler (ADR-046). Better Auth's pg driver sends
plain parameterized queries, which transaction mode supports. Each web pool keeps at most
4 connections and closes idle ones after 10 s; a connection the pooler drops is logged and
replaced. The API, one long-running server, keeps the session pooler with a pool of 5 (set
Maximum Pool Size in the connection string to choose another), and migrations need the
session pooler or a direct connection. "max clients reached in session mode" in a log means
the session pooler's limit was hit: check that Vercel's DATABASE_URL uses port 6543 and
stop spare local dev servers.
Store the Npgsql connection string in .NET user-secrets under
ConnectionStrings:Database for development, or in the API host's environment
as ConnectionStrings__Database. The API .env.example documents the format;
ASP.NET does not automatically load .env files. Better Auth has separate,
server-only web database configuration for authentication tables (ADR-008).
Never put database credentials in a NEXT_PUBLIC_ variable. MCP OAuth does not supply an API
database password.
With configuration set, dotnet ef dbcontext info --project app/api verifies
context construction, not live credentials or connectivity. No live Supabase
database check is included in this foundation.
Agree on the first feature's model, authorization, and API contract first.
Generate migrations using dotnet ef migrations add <Name> --project app/api.
Review the generated migration and SQL before applying it. Document the target,
backup needs, and rollback. Prefer a direct connection for migration execution;
consult Supabase's connection guidance for IPv4-only environments.
After a merge to main, the CI job deploy-api applies pending migrations to Supabase
and then deploys the API (ADR-044). It connects with the DATABASE_MIGRATION_CONNECTION_STRING
secret in the GitHub environment api-deploy: the session pooler string (port 5432,
SSL Mode=VerifyFull), because GitHub's runners have no IPv6 for the direct connection.
A migration must work with the API version already running, because that version serves
requests until the new one starts. Rollback stays manual. dotnet ef migrations list --project app/api with the Supabase connection string shows which migrations are
applied, without changing anything.
Do not run database update, ad-hoc MCP SQL, or schema edits before EF Core is
initialized and the intended schema change is reviewed. Do not introduce a
second Supabase CLI or Drizzle migration history. Do not map or alter Supabase's
managed auth or storage schemas. Tables exposed through the Supabase Data API
require RLS and intentional grants/policies as part of their migrations. Direct
EF connections do not automatically carry a Supabase user's JWT; the API's
authorization model must be designed explicitly.
The previous handoff records InitialSchema as applied to Supabase. Its nine
tables have RLS enabled and browser-role privileges revoked, with no allow
policies. Privileged server connections need C# authorization, which is now
implemented for project reads/writes and team creation/member-only reads.
Forward SQL is in schema/initial.sql; rollback.sql drops all nine tables and
their data. These scripts are not idempotent. Do not rerun the one-time
npm run schema:generate against the existing migration. Future changes need
new EF migrations. See DEPLOYMENT.md for web TLS/CA configuration.
AuthRateLimits adds public."rateLimit" with Better Auth 1.7.5's text id/key, integer count, and bigint lastRequest (milliseconds). A unique key index supports concurrent bucket creation; a lastRequest index supports stale-row cleanup. RLS is enabled with no allow policies; PUBLIC, anon, and authenticated grants are revoked. The server database role must retain access.
Generation commands (offline connection settings suffice):
dotnet ef migrations add AuthRateLimits --project app/api
node scripts/generate-auth-rate-limits.mjsThe second command appends the explicit RLS statements and generates docs/schema/auth-rate-limits.sql and auth-rate-limits-rollback.sql. It can be rerun to regenerate these artifacts; do not rerun the initial-schema generator. Existing initial/rollback scripts remain the historical InitialSchema pair.
Before live execution, initialize EF and inspect migration history. Apply
AuthRateLimits before deploying database-backed rate limiting. Rollback requires
reverting the web app to memory storage first, then running
dotnet ef database update InitialSchema --project app/api. This drops only rate
limit counters, resetting throttles; it preserves users, sessions, and product
data. Never apply the full initial rollback to undo this change.
ProjectRepositories adds public.project_repositories: one row per project with the GitHub installation and repository IDs, the owner and name at connection time, and who connected it and when (ADR-032). RLS is enabled with no allow policies; PUBLIC, anon, and authenticated grants are revoked. It does not change any existing table.
Generation commands (offline connection settings suffice):
dotnet ef migrations add ProjectRepositories --project app/api --configuration Release
node scripts/generate-project-repositories.mjsThe second command appends the RLS statements and generates docs/schema/project-repositories.sql and project-repositories-rollback.sql. It can be rerun.
Apply it before deploying an API that has the repository endpoints, and deploy that
API before the web app that calls them. With the API stopped and
ConnectionStrings:Database set, run
dotnet ef database update ProjectRepositories --project app/api, or run
docs/schema/project-repositories.sql once in the Supabase SQL editor. Rollback:
dotnet ef database update AuthRateLimits --project app/api, or the rollback
script. It drops only the repository connections, which owners can recreate.
RequirementEvidence adds public.requirement_evidence: one row per issue or pull request linked to a requirement, with what GitHub reported when it was last read (ADR-033). A unique index allows each item once per requirement. RLS is enabled with no allow policies; PUBLIC, anon, and authenticated grants are revoked. It changes no existing table and depends on ProjectRepositories only for ordering.
dotnet ef migrations add RequirementEvidence --project app/api --configuration Release
node scripts/generate-requirement-evidence.mjsApply it after ProjectRepositories and before deploying an API with the evidence
endpoints: with the API stopped, dotnet ef database update RequirementEvidence --project app/api, or run docs/schema/requirement-evidence.sql once in the Supabase
SQL editor. Rollback: dotnet ef database update ProjectRepositories --project app/api, or requirement-evidence-rollback.sql. It deletes every evidence link;
members can link the items again, but who linked them and when is lost.
EvidenceCommits changes public.requirement_evidence so a row can be a commit
(ADR-034): number and state become optional; sha, additions, deletions,
changed_files, commit_count, and commits (jsonb) are added; check constraints
require a commit to have a 40-digit SHA and no number or state, and an issue or pull
request to have a number and a state; a unique index allows each commit once per
requirement. Existing rows stay valid. RLS and grants on the table are unchanged.
dotnet ef migrations add EvidenceCommits --project app/api --configuration Release
node scripts/generate-evidence-commits.mjsThe second command adds one statement to the migration's rollback, which deletes commit links before the old constraints return, and generates docs/schema/evidence-commits.sql and evidence-commits-rollback.sql.
Apply it after RequirementEvidence: with the API stopped,
dotnet ef database update --project app/api applies every pending migration in
order; or run the SQL scripts in order in the Supabase SQL editor. Rollback:
dotnet ef database update RequirementEvidence --project app/api, or the rollback
script. It deletes every commit link and the stored changes and commits of pull
requests, and leaves number and state with empty defaults that the original
migration did not have.
EvidenceChecks adds four columns to public.requirement_evidence: checks (jsonb),
check_count, and checks_read_at for check results (ADR-035), and source
(manual or suggested, default manual) for how the link came to be (ADR-036).
Existing rows become manual. No table is added, so RLS and grants are unchanged.
dotnet ef migrations add EvidenceChecks --project app/api --configuration Release
node scripts/generate-evidence-checks.mjsApply it after EvidenceCommits: with the API stopped,
dotnet ef database update --project app/api, or run
docs/schema/evidence-checks.sql in the Supabase SQL editor. Rollback:
dotnet ef database update EvidenceCommits --project app/api, or
evidence-checks-rollback.sql. It drops the stored check results and the source
marker; the links themselves stay.
EvidenceReleases changes public.requirement_evidence so a row can be a release
(ADR-037): tag, prerelease, merge_sha (a merged pull request's merge commit),
and contains (jsonb, which linked changes a release includes) are added; kind
allows release; the identity constraint requires a release to have a tag and no
number or state, and forbids a tag on other kinds; a unique index allows each tag
once per requirement. Existing rows stay valid. RLS and grants are unchanged.
dotnet ef migrations add EvidenceReleases --project app/api --configuration Release
node scripts/generate-evidence-releases.mjsThe second command adds one statement to the migration's rollback, which deletes release links before the earlier constraints return, and generates docs/schema/evidence-releases.sql and evidence-releases-rollback.sql.
Apply it after EvidenceChecks: with the API stopped,
dotnet ef database update --project app/api, or run
docs/schema/evidence-releases.sql in the Supabase SQL editor. Rollback:
dotnet ef database update EvidenceChecks --project app/api, or the rollback script.
It deletes every release link and the stored merge commits of pull requests.
RequirementReviews creates public.requirement_reviews (ADR-038): one row per decision
on a requirement, with decision (accepted, rejected, or more_evidence),
note (required unless accepted), requirement_version, evidence (a jsonb array,
the evidence links as they were), decided_by, and decided_at. Foreign keys to the
requirement and the user restrict deletion. RLS is enabled with no policies, and
grants to PUBLIC, anon, and authenticated are revoked.
dotnet ef migrations add RequirementReviews --project app/api --configuration Release
node scripts/generate-requirement-reviews.mjsThe second command generates docs/schema/requirement-reviews.sql and requirement-reviews-rollback.sql and adds the RLS and grant statements.
Apply it after EvidenceReleases: with the API stopped,
dotnet ef database update --project app/api, or run
docs/schema/requirement-reviews.sql in the Supabase SQL editor. Until it is applied,
requirement lists and requirement pages fail. Rollback:
dotnet ef database update EvidenceReleases --project app/api, or the rollback
script. It drops the table, and with it every recorded decision.
Teams adds public.teams and public.team_members. A team has one owner user ID;
the API derives the Owner role from that field, and other membership roles are
Admin or Member. Foreign keys preserve existing accounts, and the composite
membership key prevents duplicates. Both tables enable RLS with no allow policies;
grants to PUBLIC, anon, and authenticated are revoked. C# checks membership for
every product read rather than relying on a Supabase user JWT in EF connections.
Generate and reproduce the artifacts without connecting to a database:
dotnet ef migrations add Teams --project app/api
node scripts/generate-teams.mjsThe generator appends explicit RLS/grant SQL to the generated migration and writes
schema/teams.sql and schema/teams-rollback.sql. Rerun only the generator after
the migration exists; do not scaffold the same migration twice. Tests apply the
forward SQL in disposable PostgreSQL and exercise rollback/reapply.
Before deploying this slice, initialize EF, review the migration/SQL, inspect the
target history, and apply dotnet ef database update Teams --project app/api to
the intended database. This task does not apply it to shared Supabase. Deploy the
API before the web and configure SPECTHREAD_API_URL and matching issuer origins.
Back up team data before rollback. First revert the web/API team functionality,
then dotnet ef database update AuthRateLimits --project app/api; rollback drops
only teams and team memberships, preserving existing auth and project data.
TeamNavigationOnboarding adds is_favorite and is_expanded to memberships,
and a private user_onboarding completion record keyed by account. Existing team
members are backfilled as completed, using their earliest membership timestamp.
New first-team creation saves completion with the team/member transaction.
Completion does not depend on having a membership forever. No Better Auth column
is added or modified.
dotnet ef migrations add TeamNavigationOnboarding --project app/api
node scripts/generate-team-navigation.mjsThe generator adds the backfill/RLS/revoked-grant SQL and writes
schema/team-navigation.sql and schema/team-navigation-rollback.sql offline.
Review history and SQL before applying dotnet ef database update TeamNavigationOnboarding --project app/api. This work applies migrations only to
disposable tests, not shared Supabase.
Rollback to Teams preserves teams, members, auth, and projects, but removes completion and navigation preferences. Revert the web/API onboarding gate before rollback. Reapplying marks existing members completed; accounts that completed onboarding then left every team need their backed-up completion state restored. Back up these preferences and completion records before rollback.
The teams migrations and the evidence and review migrations were written on separate
branches, so their timestamps interleave. EF applies them in timestamp order:
Teams, TeamNavigationOnboarding, ProjectRepositories, TeamProjects,
RequirementEvidence, TeamInvitations, EvidenceCommits, EvidenceChecks,
EvidenceReleases, RequirementReviews. A database that already has one branch's
migrations gets the other's with dotnet ef database update --project app/api
(API stopped); EF runs only the missing ones, and each applies cleanly because the
evidence and review tables refer only to projects, requirements, and users.
dotnet ef migrations has-pending-model-changes reports none for the merged model.
Roll back by script (docs/schema/*-rollback.sql) for one migration at a time:
dotnet ef database update <Name> would also undo the other branch's migrations
that come later in time. scripts/test-database.mjs applies the teams scripts first
and then the evidence and review scripts. ProjectDocuments comes after all of them,
then RequirementDocuments, RequirementUpdatedBy, RequirementCriteriaVersions,
RequirementMilestone, RequirementFollowUps, RequirementCriteriaVersionTexts,
RequirementEvidenceEvents, and RequirementEvidenceSuggestions.
ProjectDocuments (ADR-047) adds documents (metadata, with check constraints on title,
file name, content type, and a 1 to 4,194,304 byte size) and document_contents (the
bytea file, keyed by document ID). Both reference their parents with restrict deletes,
have RLS enabled with no allow policies, and revoke PUBLIC, anon, and authenticated
grants.
dotnet ef migrations add ProjectDocuments --project app/api --configuration Release
node scripts/generate-project-documents.mjsThe second command adds the RLS and grant statements and generates docs/schema/project-documents.sql and project-documents-rollback.sql.
CI applies it after the merge, before the API that needs it is deployed (ADR-044); it
only adds tables, so the running API is unaffected. Rollback:
dotnet ef database update RequirementReviews --project app/api, or the rollback
script. It drops both tables and permanently deletes every uploaded file, so export
them first.
RequirementDocuments (ADR-049) adds requirement_documents: one row per document a
requirement links, keyed by (requirement_id, document_id), with who linked it
(linked_by) and when (linked_at, default now()). It references requirements,
documents, and users with restrict deletes, is indexed by document and by linker, has
RLS enabled with no allow policies, and revokes PUBLIC, anon, and authenticated grants.
That a linked document is in the requirement's project, and that a newly linked one is
active, are checked by the API under the team lock, not by the database.
dotnet ef migrations add RequirementDocuments --project app/api --configuration Release
node scripts/generate-requirement-documents.mjsThe second command adds the RLS and grant statements and generates docs/schema/requirement-documents.sql and requirement-documents-rollback.sql.
CI applies it after the merge, before the API that needs it is deployed (ADR-044); it
only adds a table, so the running API is unaffected. Do not apply it to Supabase by hand
before the merge: a database that has the table without this migration's history row
fails the CI run with "relation already exists", as ProjectDocuments did when an earlier
draft of it was applied first. Rollback: dotnet ef database update ProjectDocuments --project app/api, or the rollback script. It drops only the links; requirements and
documents stay, and the links would have to be made again.
RequirementUpdatedBy (ADR-050) adds requirements.updated_by (text, nullable, indexed,
foreign key to user with restrict delete) and fills it with created_by for every
existing requirement. It stays nullable so the API that is still running while CI
deploys can keep creating requirements; the new API sets it on every create and save
and reports the creator when it is empty.
dotnet ef migrations add RequirementUpdatedBy --project app/api --configuration Release
node scripts/generate-requirement-updated-by.mjsThe second command adds the backfill and generates docs/schema/requirement-updated-by.sql
and requirement-updated-by-rollback.sql. CI applies it after the merge (ADR-044); don't
apply it by hand first. Rollback: dotnet ef database update RequirementDocuments --project app/api, or the rollback script; it drops only the column, losing who made
each requirement's latest edit.
RequirementCriteriaVersions (ADR-051) adds requirement_criteria_versions: one row per
saved version of a requirement's acceptance criteria, with number (from 1, unique per
requirement), criteria (a JSON array of texts in order), created_by, and
created_at. It references requirements and users with restrict deletes, has RLS
enabled with no allow policies, and revokes PUBLIC, anon, and authenticated grants. It
also adds requirement_reviews.criteria_version (integer, nullable, positive), so the
API still running during the deploy keeps recording decisions. The migration writes
version 1 for every existing requirement from its current criteria, credited to
coalesce(updated_by, created_by) at updated_at, and sets criteria_version = 1 on
decisions whose requirement_version is the requirement's current version.
dotnet ef migrations add RequirementCriteriaVersions --project app/api --configuration Release
node scripts/generate-requirement-criteria-versions.mjsThe second command adds the RLS, grant, and backfill statements and generates
docs/schema/requirement-criteria-versions.sql and -rollback.sql. CI applies it after the
merge (ADR-044); don't apply it by hand first. Rollback: dotnet ef database update RequirementUpdatedBy --project app/api, or the rollback script (after rolling back the
two migrations below, or one script at a time, newest first). It drops the table and the
column, losing every earlier criteria version and which version each decision was made
against; requirements, current criteria, and decisions stay.
RequirementMilestone (ADR-053) adds requirements.milestone (varchar(100), nullable)
with a check that a value is trimmed and 1 to 100 characters. Nullable, so the running
API is unaffected.
dotnet ef migrations add RequirementMilestone --project app/api --configuration Release
node scripts/generate-requirement-milestone.mjsThe second command generates docs/schema/requirement-milestone.sql and -rollback.sql
from EF's output unchanged. CI applies it after the merge. Rollback: dotnet ef database update RequirementCriteriaVersions --project app/api, or the rollback script; it drops
only the column, losing every label.
RequirementFollowUps (ADR-052) adds requirement_follow_ups (keyed by
requirement_id, the follow-up, and original_requirement_id, with linked_by and
linked_at, and a check that the two differ) and requirement_follow_up_events (id,
both requirement IDs, action of added or removed, actor, occurred_at). Both
reference requirements and users with restrict deletes, are indexed by each requirement,
have RLS enabled with no allow policies, and revoke PUBLIC, anon, and authenticated
grants. That both requirements are in one project, active, and not linked the other way
is checked by the API under the team lock.
dotnet ef migrations add RequirementFollowUps --project app/api --configuration Release
node scripts/generate-requirement-follow-ups.mjsThe second command adds the RLS and grant statements and generates
docs/schema/requirement-follow-ups.sql and -rollback.sql. CI applies it after the merge;
it only adds tables. Rollback: dotnet ef database update RequirementMilestone --project app/api, or the rollback script; it drops both tables, losing every link and its history.
RequirementCriteriaVersionTexts (ADR-051) replaces the check on
requirement_criteria_versions.criteria so it accepts only a JSON array whose elements are
all strings (an empty array included), using
NOT jsonb_path_exists(criteria, 'strict $[*] ? (@.type() != "string")') (strict, so a nested array is not unwrapped and passed). The API reads each
version as a list of texts, so any other element would fail on read. Existing rows hold
only texts: the API writes them and the backfill copies them from acceptance_criteria.
dotnet ef migrations add RequirementCriteriaVersionTexts --project app/api --configuration Release
node scripts/generate-requirement-criteria-version-texts.mjsThe second command generates docs/schema/requirement-criteria-version-texts.sql and
-rollback.sql from EF's output unchanged. CI applies it after the merge. Rollback: dotnet ef database update RequirementFollowUps --project app/api, or the rollback script; it
restores the array-only check and loses no data.
RequirementEvidenceEvents (ADR-054) adds requirement_evidence_events: one row per
evidence link added or removed, with requirement_id, evidence_id (the link's ID,
without a foreign key, because a removal deletes the link), action (added or
removed), the link's kind, label, and title as they were, actor, and
occurred_at. It references requirements and users with restrict deletes, is indexed by
(requirement_id, occurred_at), has RLS enabled with no allow policies, and revokes
PUBLIC, anon, and authenticated grants. It starts the history with an added row for
every existing link, from its linked_by and linked_at, labelled as the API labels
links (# and the number, the tag, or the first 7 characters of the SHA).
dotnet ef migrations add RequirementEvidenceEvents --project app/api --configuration Release
node scripts/generate-requirement-evidence-events.mjsThe second command adds the RLS, grant, and backfill statements and generates
docs/schema/requirement-evidence-events.sql and -rollback.sql. CI applies it after the
merge; it only adds a table, so the running API is unaffected, though links it makes or
removes until the new API is deployed are not recorded. Rollback: dotnet ef database update RequirementCriteriaVersionTexts --project app/api, or the rollback script; it
drops only the history, losing every recorded addition and removal; links stay.
RequirementEvidenceSuggestions (ADR-056) adds requirement_evidence_suggestions: one row
per requirement and pull request (unique on requirement_id, repository_id, number) with
the item's title, state, author, and url, the reference found, status
(pending, confirmed, or rejected), found_at, and decided_by and decided_at,
which are set exactly when it is decided. It references requirements and users with
restrict deletes, has RLS enabled with no allow policies, and revokes PUBLIC, anon, and
authenticated grants. It also widens requirement_evidence_events.action to confirmed,
rejected, moved_in, and moved_out, and adds other_requirement_id (nullable, foreign
key to requirements, set exactly on moves).
dotnet ef migrations add RequirementEvidenceSuggestions --project app/api --configuration Release
node scripts/generate-requirement-evidence-suggestions.mjsThe second command adds the RLS, grant, and rollback statements and generates
docs/schema/requirement-evidence-suggestions.sql and -rollback.sql. CI applies it after
the merge; the running API only writes added and removed, which stay valid. Rollback:
dotnet ef database update RequirementEvidenceEvents --project app/api, or the rollback
script. It drops the table, losing every suggestion and which were rejected. Events stay
in the older form: moved_out becomes removed, moved_in and confirmed become
added, and rejected events are deleted. Links stay.
codex mcp add supabase --url "https://mcp.supabase.com/mcp?project_ref=hgcjtecsglksdbsmtcxt&features=docs%2Caccount%2Cdatabase%2Cdebugging%2Cdevelopment%2Cfunctions%2Cbranching"
codex mcp login supabase
codex mcp listThis machine's global configuration is authenticated with OAuth. Automatic scope discovery failed during setup; this explicit supported-scope login succeeded:
codex mcp login supabase --scopes "organizations:read,projects:read,projects:write,database:read,database:write,analytics:read,edge_functions:read,edge_functions:write,environment:read,environment:write"Each teammate authenticates independently. Reload Codex if new tools do not
appear in an existing session. /mcp is an interactive Codex command; CLI
verification uses codex mcp list. Never commit OAuth tokens. The Supabase skill
was already installed, so the optional duplicate skill installation was skipped.
References: Supabase connections, Npgsql EF provider, and Codex MCP.
TeamProjects adds the required projects.team_id FK to teams, with restricted
team deletion. Before the FK is created, each existing project gets a separate
team bearing its name; its owner and existing project members become that team's
Owner and Members. Owner membership is supplied if an old project lacks it.
Membership join timestamps, projects, requirements, criteria, and legacy membership
rows are preserved. Legacy collaborators are backfilled as onboarded. No unrelated
projects are combined. New authorization reads only team membership; legacy
project_members remains historical data for rollback and grants no access.
dotnet ef migrations add TeamProjects --project app/api
node scripts/generate-team-projects.mjsThe generator inserts the agreed backfill before the generated FK/index, removes
the temporary column default, and emits schema/team-projects.sql and
schema/team-projects-rollback.sql offline. Rerun the generator only once the
migration exists. Tests use isolated Docker databases; no shared migration is run.
Before deployment, back up the target, inspect history, initialize EF, and apply
dotnet ef database update TeamProjects --project app/api. Deploy the matching API
before the web. Rollback to TeamNavigationOnboarding drops only the project/team
association and preserves generated teams and all project/auth/requirement data.
It does not recreate individual memberships for projects created after migration,
or reproduce team role changes in the old model. Reverting authorization requires
an explicit access reconciliation from a backup. Reapplication creates new teams
per project and does not reuse the preserved teams; prefer rolling forward once
new team-owned data exists. Do not apply the full initial rollback.
TeamInvitations adds public.team_invitations: team/email/role/issuer, hashed
secret, creation/issue/expiry timestamps, and acceptance/revocation state. Team and
auth-user foreign keys restrict deletion. Check constraints limit role and expiry
and prevent simultaneous acceptance/revocation. A unique digest index supports
token lookup; a partial unique team/email index permits only one unresolved invite
per recipient. Expired unresolved invitations must be resent or revoked. RLS and
revoked PUBLIC/anon/authenticated grants keep all access on the authorized API.
dotnet ef migrations add TeamInvitations --project app/api
node scripts/generate-team-invitations.mjsThe generator adds the RLS/grant guard and emits schema/team-invitations.sql and
schema/team-invitations-rollback.sql offline. After initialization, backup, and
history review, the rollout target is dotnet ef database update TeamInvitations --project app/api. Only disposable databases are migrated during development.
Rollback to TeamProjects drops invitation records, invalidating pending links;
it preserves teams, accepted memberships, onboarding completion, projects, and auth.
Reapplication creates empty invitation storage. Back up invitation history before
rollback; hashes cannot recover plaintext links. Prefer rolling forward.