feat(registry): hard-cut META_REPO -> REGISTRY_REPO; wire mirror into startup/webhook/sweep

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-03 23:16:51 -07:00
parent f7bd466f31
commit cecc6c0b41
18 changed files with 231 additions and 115 deletions
+29 -27
View File
@@ -1,11 +1,9 @@
"""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 026 could not read.
A deployment hosts one or more projects (§22.1). The git registry mirror
that lets a deployment declare projects lands in M3 and drives this module.
`seed_default_project` (the §22.13 META_REPO backfill) is retired in M3;
the registry mirror (`registry.refresh_registry`) is authoritative.
"""
from __future__ import annotations
@@ -15,26 +13,30 @@ from .config import Config
DEFAULT_PROJECT_ID = "default"
def seed_default_project(config: Config) -> None:
"""Fill the default project's content_repo from config.
def resolved_default_id(config: Config) -> str:
"""The id of the deployment's bootstrap/default project. Plan A: always
'default' (the re-stamp to a config slug rides Plan B). The config knob is
read here so Plan B can flip the resolution without touching call sites."""
return config.default_project_id.strip() or DEFAULT_PROJECT_ID
Migration 026 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),
)
def default_content_repo(config: Config) -> str | None:
"""The content repo the single-corpus mirror reads, from the default
project's row (filled by the registry mirror). Replaces the retired
META_REPO. None until the registry mirror has run."""
row = db.conn().execute(
"SELECT content_repo FROM projects WHERE id = ?",
(resolved_default_id(config),),
).fetchone()
return row["content_repo"] if row and row["content_repo"] else None
def project_initial_state(project_id: str) -> str:
"""§22.4b landing state for new entries in a project. Defaults to
'super-draft' for an unknown/unset row (the safe, today's-flow default)."""
row = db.conn().execute(
"SELECT initial_state FROM projects WHERE id = ?", (project_id,)
).fetchone()
if row is None or not row["initial_state"]:
return "super-draft"
return row["initial_state"]