1dab24eef0
Integrates the multi-project work so far — the §22 design drafts, M1 (the schema spine), and M2 (project-scoped authorization + the §22.7 resolver) — on top of the v0.32.0 retire/§13 changes that landed on main meanwhile. Integration decisions: - Renumbered the M1 projects migration 025_projects.sql -> 026_projects.sql. v0.32.0 shipped 025_retired_state.sql, which rebuilds cached_rfcs and predates the project_id column; running projects *after* the retire rebuild is required so project_id survives on a fresh database (the runner applies *.sql in filename order). Comment/doc references bumped to match. - Resolved the get_rfc conflict in api.py by composing both gates: compute the viewer once, apply the §22.5 visibility gate, then v0.32.0's §13.7 retired-entry owner-only check. - api_graduation.py / api_discussion.py auto-merged cleanly (M2's threaded viewer/visibility gate coexists with the new retire endpoints + retired state). Full suite 401 passed on a fresh DB. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
106 lines
6.7 KiB
SQL
106 lines
6.7 KiB
SQL
-- §22 (multi-project deployments) — Slice M1: the project spine.
|
|
--
|
|
-- A deployment now hosts one or more *projects*, each a corpus with its own
|
|
-- content repo, slug/RFC-NNNN namespace, catalog, roster, and branding. The
|
|
-- pre-multi-project single-corpus deployment is the N=1 case: this migration
|
|
-- generates one 'default' project and stamps every existing RFC-scoped row
|
|
-- to it, so the app keeps running exactly as before with the spine
|
|
-- underneath (see docs/design/multi-project-spec.md §22.13).
|
|
--
|
|
-- STRATEGY — additive, no table rebuilds. Every slug-bearing table gets a
|
|
-- `project_id TEXT NOT NULL DEFAULT 'default'` column. The constant default
|
|
-- means every existing INSERT in the codebase that does not yet mention
|
|
-- project_id keeps working and lands rows in the default project; no query
|
|
-- breaks because, with a single project, slugs remain globally unique. The
|
|
-- column carries no inline REFERENCES clause: SQLite's ALTER TABLE ADD
|
|
-- COLUMN forbids a FK column with a non-NULL default. project_id referential
|
|
-- integrity is therefore enforced at the app layer for now; the FK lands
|
|
-- with the table rebuilds below.
|
|
--
|
|
-- ============================================================================
|
|
-- DEFERRED to the slice that activates project #2 (M3/M4). Until a second
|
|
-- project exists these are correct as-is; the moment two projects can share a
|
|
-- slug or a Gitea PR number they become cross-project collision bugs and MUST
|
|
-- be rebuilt (SQLite needs a create-copy-drop-rename per table) to fold
|
|
-- project_id into the key, and to add the project_id FK:
|
|
-- * cached_rfcs PRIMARY KEY (slug) -> (project_id, slug)
|
|
-- * cached_branches UNIQUE (rfc_slug, branch_name) -> +project_id
|
|
-- * branch_visibility UNIQUE (rfc_slug, branch_name) -> +project_id
|
|
-- * branch_contribute_grants UNIQUE (rfc_slug, branch_name, grantee_user_id) -> +project_id
|
|
-- * stars UNIQUE (user_id, rfc_slug) -> +project_id
|
|
-- * watches UNIQUE (user_id, rfc_slug) -> +project_id
|
|
-- * pr_seen UNIQUE (user_id, rfc_slug, pr_number) -> +project_id
|
|
-- * branch_chat_seen UNIQUE (user_id, rfc_slug, branch_name) -> +project_id
|
|
-- * funder_consents PRIMARY KEY (user_id, rfc_slug) -> +project_id
|
|
-- * rfc_collaborators UNIQUE INDEX (rfc_slug, user_id) -> +project_id
|
|
-- * contribution_requests UNIQUE INDEX (rfc_slug, requester_user_id) WHERE pending -> +project_id
|
|
-- * proposed_use_cases UNIQUE (scope, pr_number) -> +project_id
|
|
-- (PR numbers are per-content-repo = per-project)
|
|
-- cached_prs UNIQUE (repo, pr_number) is already globally unique (repo is the
|
|
-- full 'org/repo' string, distinct per project) and needs no rebuild.
|
|
-- ============================================================================
|
|
|
|
-- The project registry cache (mirrored from the git registry by the M3
|
|
-- reconciler; rows are never written from user actions). content_repo is
|
|
-- NULL until the mirror — or the M1 startup backfill (§22.13 step 1) — sets
|
|
-- it from the deployment's configured repo.
|
|
CREATE TABLE IF NOT EXISTS projects (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
content_repo TEXT,
|
|
visibility TEXT NOT NULL DEFAULT 'gated'
|
|
CHECK (visibility IN ('gated', 'public', 'unlisted')),
|
|
config_json TEXT,
|
|
registry_sha TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- The default project. visibility='public' preserves the pre-multi-project
|
|
-- open-by-default posture (§22.5, §22.13). name is a placeholder the startup
|
|
-- backfill / registry overwrites with the deployment's display name.
|
|
INSERT OR IGNORE INTO projects (id, name, visibility)
|
|
VALUES ('default', 'default', 'public');
|
|
|
|
-- Per-(user, project) membership and the §22.6 middle-tier role. This is the
|
|
-- new tier between the §6.1 deployment role (users.role, now deployment-scope
|
|
-- only) and the §6.3 per-RFC authority.
|
|
CREATE TABLE IF NOT EXISTS project_members (
|
|
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL DEFAULT 'project_viewer'
|
|
CHECK (role IN ('project_admin', 'project_contributor', 'project_viewer')),
|
|
granted_by INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
granted_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
PRIMARY KEY (project_id, user_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_project_members_user ON project_members(user_id);
|
|
|
|
-- The project_id spine across every slug-bearing table. Backfills existing
|
|
-- rows to 'default' via the constant column default.
|
|
ALTER TABLE cached_rfcs ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE cached_branches ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE cached_prs ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE branch_visibility ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE branch_contribute_grants ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE stars ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE threads ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE changes ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE pr_seen ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE branch_chat_seen ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE watches ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE notifications ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE actions ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE pr_resolution_branches ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE funder_consents ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE rfc_invitations ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE rfc_collaborators ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE proposed_use_cases ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
ALTER TABLE contribution_requests ADD COLUMN project_id TEXT NOT NULL DEFAULT 'default';
|
|
|
|
-- The catalog/directory lookup the M3 surfaces will make (RFCs in a project).
|
|
-- The per-table composite-key rebuilds in the DEFERRED block above will add
|
|
-- their own (project_id, …) indexes when they land.
|
|
CREATE INDEX IF NOT EXISTS idx_cached_rfcs_project ON cached_rfcs(project_id);
|