diff --git a/docs/design/multi-project-spec.md b/docs/design/multi-project-spec.md index d6faa46..f1c1191 100644 --- a/docs/design/multi-project-spec.md +++ b/docs/design/multi-project-spec.md @@ -115,6 +115,13 @@ removed. The displayed *noun* around a slug ("RFC", "Spec", "Feature") is a presentation concern driven by the project's type (§22.4a), not part of the identity. +**Legacy numbers.** Entries graduated *before* this change keep their existing +`id` (`RFC-NNNN`) in frontmatter as a **frozen, non-identity legacy label** — +preserved and shown in the UI so external "RFC-0001"-style citations still +resolve, but never used for routing or lookup (the slug is). New entries are +never assigned one, and graduation does not write `id`. The field is read-only +provenance from here on. + ### 22.4a Project type Every project declares a `type` in the registry (§22.2), chosen at creation @@ -312,12 +319,16 @@ the §8 RFC view, the §14 philosophy — all per project). ### 22.10 Routing and the deployment landing Every corpus-scoped route gains a project segment: `/p//…` carries -the §7 catalog, the §8 `/p//rfc/`, the §9/§10 -`/p//proposals/`, and the §14 `/p//philosophy`. The -root `/` is the **deployment landing**: a directory of the projects the -visitor can see (per §22.5), plus sign-in. An anonymous or non-member visitor -sees only `public` projects there. The §8.1 breadcrumb gains a leading -project segment: `OHM / RFC-0042 · Human › main`. +the §7 catalog, the §8 entry view at `/p//e/`, the §9/§10 +`/p//proposals/`, and the §14 `/p//philosophy`. The entry +segment is the **generic `/e/`** for every type — the displayed noun ("RFC", +"Spec", "Feature") is a type-driven label (§22.4a), not part of the path, so +routing stays a single type-agnostic path and avoids colliding with the +reserved sibling segments (`proposals`, `philosophy`, …). The root `/` is the +**deployment landing**: a directory of the projects the visitor can see (per +§22.5), plus sign-in. An anonymous or non-member visitor sees only `public` +projects there. The §8.1 breadcrumb gains a leading project segment: +`OHM / Human › main` (slug, with the type-driven noun as its label). ### 22.11 Notifications span projects, one inbox @@ -345,11 +356,17 @@ single **default project** so it keeps running unchanged: `META_REPO → content_repo`, `VITE_APP_NAME → name`, `visibility = public` (preserving the deployment's current open-by-default posture), `type = document` (every pre-multi-project corpus is a document corpus), - and `id` a slug derived from the deployment. + and the `id` a **config-derived slug**: `DEFAULT_PROJECT_ID` if set, else a + slug of the deployment name, falling back to the literal `default`. (M1's + migration 025 seeds the bootstrap id `default`; the §C-M3 step re-stamps it + to the config-derived slug before any `/p//` route is public, so the id + is meaningful — e.g. `/p/ohm/…` — and never renamed after URLs go live. The + id stays framework-generic: the framework supplies no deployment name.) 2. Every existing RFC-scoped row (§5 amendment list) is stamped with that `project_id`. 3. Old corpus-root URLs (`/rfc/`, `/proposals/`) 308-redirect to - their `/p//…` equivalents, so existing links survive. + their `/p//…` equivalents — the entry view to + `/p//e/` (§22.10) — so existing links survive. 4. The operator creates the registry repo (§22.2) declaring the default project; until they add a second project, the deployment is functionally identical to before, with one extra path segment. @@ -371,7 +388,9 @@ Applied in place, in the established amendment-note style (cf. §1's - **§2 Meta schema / §2.3 IDs.** Slugs are unique within a project; entry filenames are unchanged (per content repo). §2.3's `RFC-NNNN` `max+1` allocation is **removed** — the slug is the identity (§22.4); there is no - per-project number. The entry frontmatter schema becomes type-dependent + per-project number. Entries graduated before this change keep their `id` + as a frozen, read-only legacy display label (§22.4), never used for lookup. + The entry frontmatter schema becomes type-dependent (§22.4a): `document` keeps today's fields, `specification` and `bdd` add their type metadata. New `active`-entry fields: `unreviewed` (bool) and the `reviewed_at`/`reviewed_by` provenance pair (§22.4c), paralleling @@ -475,9 +494,12 @@ the gates. **M3 — Registry mirror + routing + runtime branding.** The §4 registry mirror (webhook + reconciler over the `REGISTRY_REPO`, populating `projects` -rows beyond `default`); the `/p//` route prefix and the 308 -redirects off the old corpus-root URLs; `GET /api/deployment` and `GET -/api/projects/:id`; the frontend cut from `VITE_APP_NAME` to runtime config, +rows beyond the default); the **re-stamp of the default project's bootstrap +`id`** (`default` → the config-derived slug, §22.13 step 1) which must land +here, before any `/p//` URL is public; the `/p//` route prefix +with the generic `/e/` entry segment (§22.10) and the 308 redirects off +the old corpus-root URLs (`/rfc/` → `/p//e/`); `GET +/api/deployment` and `GET /api/projects/:id`; the frontend cut from `VITE_APP_NAME` to runtime config, per-project `theme` token overlay. The deployment directory at `/` and the project switcher in deployment chrome. This slice also adds the additive `type` and `initial_state` columns to `projects` (a small migration — M1 diff --git a/docs/design/multi-project.md b/docs/design/multi-project.md index 74cafab..7fb917f 100644 --- a/docs/design/multi-project.md +++ b/docs/design/multi-project.md @@ -269,8 +269,10 @@ Two chrome layers result: ## 5. Routing & UX -- RFC routes gain a project prefix: `/p//rfc/`, - `/p//proposals/`, `/p//philosophy`, etc. +- Entry routes gain a project prefix with a **generic `/e/` segment**: + `/p//e/`, `/p//proposals/`, + `/p//philosophy`, etc. The segment is the same for every type; the + noun shown around the slug is a type-driven label, not part of the path. - Root `/` becomes the **deployment landing = project directory** (the Wiggleverse home). For an anonymous or non-member visitor under gated default, that's only public/unlisted-by-link projects. @@ -293,10 +295,13 @@ The N=1 path keeps existing single-project deployments working: 1. Migration creates one **default project** from current config (`META_REPO` → `content_repo`, `VITE_APP_NAME` → `name`, visibility seeded to match the - deployment's current open posture, likely `public`). + deployment's current open posture, likely `public`). Its `id` is a + config-derived slug (`DEFAULT_PROJECT_ID`, else slug of the deployment + name, else `default`); M1's `default` bootstrap id is re-stamped to it in + M3 before any `/p/` URL is public. 2. Every existing row's `project_id` is stamped to that default project. 3. An optional default-project redirect keeps old `/rfc/` URLs alive - (308 → `/p//rfc/`). + (308 → `/p//e/`). This is the SPEC §20 upgrade-steps block. @@ -346,10 +351,15 @@ This confirms the registry decision and fixes how it integrates: `bdd` scenario model stay free-form markdown or get a structured Given/When/Then schema the app parses? Drafted shallow; pin before the type-surface slice. -- **Entry-noun in URLs/labels.** Slug-only identity means no `RFC-NNNN`. Is - the route segment generic (`/p//e/`) with the displayed noun - type-driven, or does the segment itself vary by type? (Leaning: generic - path segment, type-driven label.) -- **Existing graduated numbers.** OHM already has `RFC-0001…` in filenames. - On dropping numeric identity, do those numbers survive as a display/legacy - artifact, or get normalized to slugs? (Migration/back-compat detail.) +- **Entry-noun in URLs/labels** — *resolved 2026-06-02:* generic route + segment `/p//e/` for every type; the displayed noun + ("RFC"/"Spec"/"Feature") is a type-driven label, not part of the path + (§22.10, §22.4a). +- **Existing graduated numbers** — *resolved 2026-06-02:* pre-change graduated + entries keep their `RFC-NNNN` `id` in frontmatter as a frozen, read-only + legacy display label (preserves citations); never used for lookup, never + assigned to new entries (§22.4). +- **Default project `id`** — *resolved 2026-06-02:* a config-derived slug + (`DEFAULT_PROJECT_ID`, else slug of the deployment name, else `default`); + M1's `default` bootstrap id is re-stamped in M3 before any `/p/` URL is + public, so it's meaningful (e.g. `/p/ohm/`) and never renamed live (§22.13).