Files
rfc-app/backend/app/projects.py
T
Ben Stull 6f901e3e2c feat(projects): M1 — the multi-project schema spine (§22)
Lays the additive foundation for hosting N projects per deployment, with
today's single corpus as the N=1 case. No behavior change: the app runs
exactly as before, single project, with the spine underneath.

- migration 025: `projects` + `project_members` tables; seed the `default`
  project (visibility=public, preserving open-by-default); thread
  `project_id NOT NULL DEFAULT 'default'` onto all 19 slug-bearing tables,
  backfilling existing rows. Additive — no table rebuilds; the slug-keyed
  uniqueness/PK rework is enumerated in the migration header and deferred to
  the slice that activates project #2.
- app/projects.py: §22.13 startup backfill of the default project's
  content_repo from META_REPO (idempotent — never clobbers a value the
  future registry mirror sets).
- config: REGISTRY_REPO wired (optional through M1; consumed by the M3
  mirror), documented in .env.example as META_REPO's successor.
- tests: 6 vertical assertions on the spine (seed, backfill, column shape,
  role CHECK, idempotency, config). Full suite 381 passed.
- docs: align Part C M1/M3 boundaries with the landed code (registry mirror
  + redirect move to M3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 18:14:53 -07:00

41 lines
1.6 KiB
Python

"""Project registry — the §22 multi-project layer.
A deployment hosts one or more projects (§22.1). Through Slice M1 the only
project is the migration-seeded `default` one (the N=1 case, §22.13); the git
registry mirror that lets a deployment declare more projects lands in M3 and
will grow this module. For now this holds just the §22.13 step-1 backfill:
completing the default project's row from deployment config, which pure-SQL
migration 025 could not read.
"""
from __future__ import annotations
from . import db
from .config import Config
DEFAULT_PROJECT_ID = "default"
def seed_default_project(config: Config) -> None:
"""Fill the default project's content_repo from config.
Migration 025 seeds `default` with content_repo NULL because SQL cannot
read the environment. This sets it from META_REPO (the pre-multi-project
single-corpus repo) the first time the configured app boots, so the row
accurately names the corpus the existing rows belong to. Idempotent: only
touches the row while content_repo is still unset, so a later registry
mirror (M3) that has populated it is never clobbered.
The display `name` is deliberately left as-is: the deployment's
user-visible name lives in the frontend build (VITE_APP_NAME) today and
moves to the registry + GET /api/deployment in M3 (§22.9); the backend has
no authoritative name to backfill from yet.
"""
db.conn().execute(
"""
UPDATE projects
SET content_repo = ?, updated_at = datetime('now')
WHERE id = ? AND content_repo IS NULL
""",
(config.meta_repo, DEFAULT_PROJECT_ID),
)