Files
rfc-app/docs/superpowers/specs/2026-06-06-corpus-tree-design.md
T
Ben Stull dc5345cef4 docs(spec): Corpus Tree — universal directory-tree left pane (Solution Design)
Solution Design for making the left pane a universal git-directory tree:
host existing documentation repos as path-addressed corpora, full
governance lifecycle per file, dual-mode (structure/flat) pane replacing
the §7 Catalog, zero-migration onboarding ("no record = active").

Discovery output of ohm session 0081.0. Conforms to handbook §3.3
Solution Design standard. Sliced SLICE-1..4 in §7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 17:47:24 -07:00

628 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Solution Design: Corpus Tree — universal directory-tree left pane
| | |
| --- | --- |
| **Author(s)** | Ben Stull |
| **Reviewers / approvers** | Ben Stull |
| **Status** | `draft` |
| **Version** | v0.2.0 |
| **Source artifacts** | BDD corpus: §§1.9/4 below (this doc) · Prototype: brainstorm mockups, session 0081.0 (`.superpowers/brainstorm/`, not committed) · Reference: current `rfc-app` §7 Catalog + §22 three-tier + `DocsLayout` flyout nav · Supersedes: — |
**Change log**
| Date | Version | Change | By |
| --- | --- | --- | --- |
| 2026-06-06 | v0.1.0 | Initial draft (discovery session ohm 0081.0) | Ben Stull |
| 2026-06-06 | v0.2.0 | Reworked to the restructured Solution Design standard (two-part front; Pain Points; Business Actors vs Product Personas; renumbered) | Ben Stull |
---
## 1. Business Context
*The business lens — solution-agnostic throughout. No mechanism is proposed until §2.*
### 1.1 Executive Summary
Organizations keep large, living bodies of documentation, and the value in them
depends on people being able to find what they need and trust that it is current
and collectively maintained. This design targets two outcomes: readers can locate
any document by where it sits in a documentation body's own organization, and an
organization can bring an existing body of documentation under collaborative,
reviewed governance without disrupting how that documentation is already
arranged. The benefit accrues to readers (faster, more confident access),
contributors (a reviewed way to improve the docs), and the organization (a
governed, trustworthy knowledge base).
### 1.2 Background
The platform already governs collaboratively-edited documents organized as a
single shallow list within a body. Real organizational documentation — handbooks,
runbooks, design libraries — is instead deeply structured, and most of it already
exists as finished, authoritative content rather than passing through a
proposal-to-acceptance flow. There is currently no way to bring such a body onto
the platform's governance without flattening its structure, which is why
structured documentation bodies stay off-platform today.
### 1.3 Business Actors / Roles
Real-world roles, independent of any product.
| Role | Responsible for (in the business) |
| --- | --- |
| Reader | Finds and reads documents to do their work; needs current, authoritative content |
| Contributor | Proposes new documents and changes to existing ones |
| Maintainer | Reviews proposed changes and decides what becomes authoritative |
| Documentation steward | Owns an existing body of documentation and decides to bring it under collaborative governance |
### 1.4 Problem Statement
A reader cannot navigate a documentation body by its own structure, and an
organization cannot place an existing structured body under collaborative
governance without reorganizing it. The governance model assumes every document
is one entry in a single flat list and passes through a proposal flow — neither
of which holds for an established, deeply-organized documentation body whose
documents already exist as authoritative content.
### 1.5 Pain Points
| # | Pain | Who feels it | Cost / frequency today |
| --- | --- | --- | --- |
| PP-1 | In a large body presented as one flat list, a reader can't tell where a document sits or browse by area | Reader | Every lookup in a sizeable body; slow, error-prone, gives up |
| PP-2 | An existing structured body can't be brought under governance without rearranging its documents into a flat scheme | Documentation steward | Blocks adoption entirely for any living body — a rearrange is a non-starter |
| PP-3 | Contributors to an existing body have no governed, reviewed way to propose changes tied to where each document lives | Contributor, Maintainer | Changes happen outside review, or not at all; no shared record of why |
| PP-4 | When the documentation is briefly unreachable, the reader is left with nothing to orient by | Reader | Intermittent; erodes trust in the body as a dependable source |
### 1.6 Targeted Business Outcomes
| Outcome | Success metric | Baseline → Target | Guardrail (must not regress) | How / when measured |
| --- | --- | --- | --- | --- |
| Structured documentation bodies adopt the platform's governance | bodies of documentation hosted | 0 → ≥1 | existing bodies' usability unaffected | inspection at first onboarding |
| Bringing a body under governance is a decision, not a project | preparatory rearrangement / setup required | a flatten/rework → none | — | inspection at onboarding (PP-2) |
| Readers reliably reach documents in a large body | reader reaches the intended document | not feasible for structured bodies → routine | flat-body access unchanged | manual walkthrough (PP-1) |
> These are threshold/qualitative targets, not funnel metrics: this enables a new
> class of hosted documentation rather than tuning a conversion surface.
### 1.7 Scope (business)
- **In scope:** readers navigating a body by its own organization; contributors
proposing additions and changes anywhere in a body under review; stewards
bringing an existing body under governance without rearranging it.
- **Out of scope:** authoring tools beyond proposing/reviewing text documents;
governance policy changes (who may review/accept) — unchanged.
- **Non-goals:** becoming a general file store for non-document assets — the value
is governed *documents*, not arbitrary binaries.
### 1.8 Assumptions · Constraints · Dependencies
- **Assumptions:** bodies brought under governance are predominantly prose
documents (risk: a body that is mostly non-document assets gains little).
- **Constraints:** the existing collaborative-governance model (proposal →
review → acceptance) is reused, not redefined.
- **Dependencies:** the steward can grant the platform access to the existing
documentation body.
### 1.9 Business Use Cases
Solution-agnostic; no product, no technology.
**BUC-1 — As a reader, I can find and read a document by where it sits in the body, so that I get authoritative content without knowing an internal name.**
```gherkin
Scenario: BUC-1 — A reader finds and reads a document in a structured body
Given a body of documentation organized into sections
When a reader looks for a particular document
Then they can locate it by where it sits in that organization
And read its current content
```
- **BUC-1 acceptance criteria:** the reader reaches the intended document and
reads its current authoritative content.
**BUC-1a — As a reader, when the body is briefly unavailable, I am still oriented and never hit a dead end.**
```gherkin
Scenario: BUC-1a — The documentation is temporarily unavailable
Given the documentation cannot be reached for a moment
When a reader tries to access it
Then they are still shown what was last known to exist, or told clearly how to try again
And are never left at an empty, unexplained dead end
```
**BUC-2 — As a contributor, I can propose a change to a document, so that improvements are reviewed before they become authoritative.**
```gherkin
Scenario: BUC-2 — A contributor proposes a change to a document
Given a contributor authorized to change the documentation
When they propose a change to an existing document
Then the change enters review before it can become authoritative
And the document is shown as having a change under review
```
- **BUC-2 acceptance criteria:** a reviewable proposal exists against that
document; until accepted, the authoritative content is unchanged.
**BUC-2a — As a contributor, when my proposal can't be accepted as placed, I'm told why and nothing is half-done.**
```gherkin
Scenario: BUC-2a — A proposed change cannot be accepted as placed
Given a contributor proposing a document
When the chosen placement conflicts with an existing document, or is not allowed
Then the proposal is refused with a clear, specific reason
And nothing is partially recorded
```
**BUC-3 — As a contributor, I can introduce a new document anywhere in the body, so that the body grows where the content belongs.**
```gherkin
Scenario: BUC-3 — A contributor introduces a new document anywhere in the body
Given a contributor authorized to add documentation
When they propose a new document at a place within the body
Then it enters review as a draft in that place
And on acceptance it becomes the authoritative document there
```
- **BUC-3 acceptance criteria:** a draft appears at the chosen place and is
reviewable; on acceptance it is the authoritative document there.
**BUC-4 — As a documentation steward, I can bring an existing body under collaborative governance, so that it gains review and shared maintenance without disruption.**
```gherkin
Scenario: BUC-4 — A steward brings an existing doc body under governance
Given an organization with an existing body of documentation
When that documentation is brought under collaborative governance
Then all of its existing documents are immediately readable as authoritative
And none of them had to be rearranged to make that possible
```
- **BUC-4 acceptance criteria:** every existing document is readable and treated
as authoritative, with no rearrangement and no preparatory data entry.
---
## 2. Solution Proposal
Build software: make the platform's left-pane navigation a **universal
directory-tree** that mirrors a content repository's own git directory structure,
and address every document by its **path** rather than a flat slug — so a
documentation body is navigated by its real structure while keeping the existing
propose → review → accept governance on every file. The flat list becomes the
degenerate "one folder" case of the tree; a deep body is the general case. A new
`CorpusTree` left-pane component replaces the flat §7 Catalog as the *universal*
idiom (one navigation model for every project type), with **two display modes**
*structure* (the directory tree) and *flat* (today's ranked list, engaged on
search or a non-path sort) — so the four affordances the flat list provides
(search, lifecycle state, sort, the contribution surface) all survive.
An existing body is onboarded as an ordinary registry project pointed at its
repository, with **no content migration and no state backfill**: a document
present with no lifecycle record is treated as authoritative ("no record =
active").
**Why this approach, over the alternatives:**
- *Do nothing / keep flat + rearrange repos* — rejected: PP-2 makes a rearrange a
non-starter for a living body.
- *A read-only documentation viewer* — rejected: delivers PP-1 but not PP-3
(no governed contribution).
- *A second, separate tree view beside the flat catalog* — rejected: forks the
navigation idiom and doubles the surface; the tree generalizes the flat list
rather than sitting beside it.
**Solution-specific scope / non-goals:**
- *In:* path-addressed documents; a directory-tree read surface; the dual-mode
pane; lifecycle by path; propose/edit/review at any path; zero-migration
onboarding; legacy slug→path URL compatibility.
- *Out:* lazy per-folder tree loading (a scale follow-on; v1 serves the full
document tree from cache); a rich renderer for non-document files (listed but
inert); changes to graduation / integer-ID assignment.
- *Non-goals:* merging folders with collections (kept distinct — INV-3); a new
project *type* (existing types render via the tree — D5).
---
## 3. Product Personas
| Product persona | In rfc-app | Maps to business role(s) |
| --- | --- | --- |
| Reader | Browses a project's corpus and opens documents | Reader |
| Contributor | Holds collection `can_contribute`; proposes/edits docs at any path | Contributor, Maintainer |
| Deployment operator | Registers a content repo as a project and flips the cutover flag | Documentation steward |
## 4. Product Use Cases
UX-level; steps are about the Product Personas (§3). Each links to the Business UC
it realizes.
```gherkin
Scenario: PUC-1 — Browse the directory tree in structure mode (realizes BUC-1)
Given the left pane is in structure mode
When the reader opens the project
Then folders and files are shown in path order
And each markdown file shows its lifecycle-state badge
And folders expand and collapse, with expansion persisted across visits
```
```gherkin
Scenario: PUC-2 — Search or non-path sort flips to flat mode (realizes BUC-1)
Given the pane is in structure mode
When the reader types a search query or picks a non-path sort
Then folders are hidden and a ranked flat list of matches is shown
And clearing the search with a path sort returns the tree with folders restored
```
```gherkin
Scenario: PUC-3 — Open a document by its path (realizes BUC-1)
When the reader selects a file in the tree
Then the main column renders that document
And the tree highlights the corresponding node
And the document's lifecycle state is visible
```
```gherkin
Scenario: PUC-4 — Propose a new doc at a path (realizes BUC-3)
Given a contributor with contribute capability
When they choose "Propose new doc" and give a path like deploy/runbooks/rollback.md
Then intermediate folders are created in the proposal branch
And a PR is opened via the bot
And a super-draft entry appears at that path
```
```gherkin
Scenario: PUC-5 — Propose an edit to an existing doc (realizes BUC-2)
Given a contributor viewing an existing document
When they propose an edit
Then an edit PR is opened against that document's path
And the file's node shows the in-review badge with the open PR reference
```
```gherkin
Scenario: PUC-6 — Filter and see lifecycle state (realizes BUC-1, BUC-2)
Given files exist in active, in-review, and super-draft states
When the reader enables the "in review" state filter
Then only files with an open edit PR are listed
```
```gherkin
Scenario: PUC-7 — Star a doc; starred pins in flat mode (product-only)
Given the reader has starred a file
When the pane is in flat mode
Then the starred file is pinned above the ranked results
```
```gherkin
Scenario: PUC-8 — Legacy slug URL redirects to its path (product-only, compat)
When the reader navigates to a legacy /p/:pid/c/:cid/e/:slug URL
Then they are redirected to the path-addressed URL for that entry
And the document renders
```
```gherkin
Scenario: PUC-9 — Operator onboards an existing repo (realizes BUC-4)
Given an existing doc repo and bot read+write access
When the operator registers it as a project's content repo and flips the cutover flag
Then every pre-existing file renders as active in the tree
And no content was moved and no state was backfilled
```
```gherkin
Scenario: PUC-10 — Tree fetch fails (realizes BUC-1a)
Given a previously cached tree exists
When the tree-listing fetch to gitea fails
Then the last good tree is served from cache
And when no cache exists, a graceful empty state with a retry action is shown
```
```gherkin
Scenario: PUC-11 — Propose at an invalid path (realizes BUC-2a)
When a contributor proposes a doc at an existing or out-of-repo path
Then the proposal is rejected with a clear validation error
And no branch or PR is left behind
```
```gherkin
Scenario: PUC-12 — Non-markdown files are listed but inert (product detail)
Given the repo contains schemas/app.json
When the tree is listed
Then schemas/app.json appears in the tree
But it has no lifecycle badge and opens no entry view
```
## 5. UX Layout
### 5.1 Left pane — `CorpusTree` (serves PUC-1..PUC-7, PUC-12)
- **Purpose:** navigate a project's corpus and reach any document; surface
lifecycle state and the contribution affordances.
- **Layout (top → bottom):**
- **Toolbar:** search input; lifecycle state filter-chips; sort selector
(path · recent · title · id · state); `+ Propose new doc` action.
- **Body — structure mode (default):** indented directory tree; folders with
expand/collapse in path order; file rows show a state dot
(active / in-review / super-draft) and a star marker; the active file is
highlighted. Non-markdown files render dimmed and inert.
- **Body — flat mode (search / non-path sort):** folders hidden; a ranked flat
list of file rows (path as secondary text); starred pinned to top; pending
super-drafts grouped.
- **States:** happy: tree/list rendered · empty: "No documents yet" + propose CTA
if permitted · loading: skeleton rows · error: stale tree if cached, else an
empty state with Retry · permission: propose control hidden without
`can_contribute`.
- **Notifications:** inline validation error on invalid propose (PUC-11); existing
PR/discussion notifications unchanged.
### 5.2 Main column (serves PUC-3, PUC-5)
Unchanged — rendered markdown + discussion + PR/contribution affordances —
addressed by path instead of slug.
## 6. Technical Design
### 6.1 Invariants
- **INV-1:** An entry is addressed by its repo `path` within a
`(project, collection)`. Slug addressing exists only as a legacy redirect.
- **INV-2:** A markdown file present on `main` with no explicit lifecycle record
is `active`. Onboarding requires no state backfill.
- **INV-3:** Folders are organizational only. Access control is the collection's;
a folder never carries permissions.
- **INV-4:** Lifecycle records are keyed by `(collection, path)` and follow file
renames on merge — no orphaned records survive a `git mv`.
- **INV-5:** The framework names no deployment. The backing repo is supplied via
the registry (`CLAUDE.md` separation-of-concerns).
- **INV-6:** Non-markdown files are listed but carry no lifecycle and open no
entry view.
- **INV-7:** The tree pane never renders blank — stale cache or an explicit
empty/retry state on fetch failure.
### 6.2 High-level architecture
```mermaid
flowchart LR
CT[CorpusTree pane] -->|GET tree / entry by path| API[API service]
CT -->|propose / edit at path| API
API --> CACHE[(TTL cache)]
CACHE --> GT[Gitea git-trees / raw]
API -->|branch / PR writes| BOT[Bot account]
BOT --> GT
API --> DB[(App DB: lifecycle, stars)]
REG[rfc-registry] -->|project to content-repo| API
WH[Gitea webhooks] -->|invalidate / reconcile| CACHE
```
- **CorpusTree** — owns left-pane rendering and mode state; owns no source of
truth; must never assume a flat namespace.
- **API service** — owns the tree-listing and path-addressed entry/contribution
endpoints; derives lifecycle state; must never write content except via the bot.
- **App DB** — owns lifecycle records, stars, reviewed-marks keyed by
`(collection, path)`; system of record for collaboration state.
- **Gitea** — system of record for document content and PRs.
- **Cache** — holds the materialized tree + per-path metadata; invalidated by the
webhook/PR-merge signals used today (§4.1 reconciler pattern).
### 6.3 Data model & ownership
| Entity | Owned by | Key fields | System of record |
| --- | --- | --- | --- |
| Document (entry) | Gitea | `(project, collection, path)`, body | Gitea repo `main` |
| Lifecycle record | App DB | `(collection, path)`, state, reviewed-by | App DB |
| Star | App DB | `(user, collection, path)` | App DB |
| Open-PR mapping | Gitea | `path` → PR number | Gitea |
| Tree node (materialized) | Cache | `path`, type, `last_commit_at`, state, `open_pr`, `starred_by_me` | derived (cache) |
### 6.4 Interfaces & contracts
- **`GET /api/projects/{pid}/collections/{cid}/tree`** — in: pid, cid, viewer ·
out: nodes `{path, type, last_commit_at, lifecycle_state, open_pr,
starred_by_me}` (full markdown tree) · errors: `404` unknown project/collection;
gitea failure → stale cache or `503` with a retriable marker (INV-7).
- **Path-addressed entry read** — generalize `…/rfcs/{slug}` to a path
(`…/entries/{path}`); out: existing entry payload; errors: `404`.
- **Propose-at-path / edit / discussion** — generalize existing contribution/PR/
discussion endpoints from `slug` to `path`; propose-new in: target path, body;
out: PR ref; errors: `409` path exists, `422` invalid/out-of-repo path
(PUC-11), `403` no `can_contribute`.
### 6.5 PerProduct-Use-Case design
#### PUC-1 — Browse the tree (realizes BUC-1; honors INV-2, INV-7)
```mermaid
sequenceDiagram
actor R as Reader
participant CT as CorpusTree
participant A as API
participant C as Cache
participant G as Gitea
R->>CT: open project
CT->>A: GET …/tree
A->>C: get materialized tree
alt cache hit
C-->>A: tree
else miss
A->>G: git trees + last-commit
G-->>A: entries
A->>A: derive state (no record = active, INV-2)
A->>C: store
end
A-->>CT: nodes
CT-->>R: structure-mode tree (or stale/empty per INV-7)
```
- **Implementation:** materialize the markdown tree once per cache cycle; join
per-path lifecycle/star records; "no record = active." Folder expansion is
client state (localStorage keyed by project/collection).
#### PUC-4 — Propose a new doc at a path (realizes BUC-3; honors INV-1, INV-3)
```mermaid
sequenceDiagram
actor Cn as Contributor
participant CT as CorpusTree
participant A as API
participant B as Bot
participant G as Gitea
Cn->>CT: + Propose new doc (path)
CT->>A: propose-at-path(path, body)
A->>A: validate path (unique, in-repo) else 409/422
A->>B: create branch + file (mkdir -p path)
B->>G: commit + open PR
G-->>A: PR ref
A-->>CT: super-draft at path
```
- **Implementation:** reuse the existing bot/branch/PR flow; only the target path
and intermediate-folder creation change. PUC-5 (edit), PUC-6 (filter), PUC-8
(redirect) reuse existing flows with `slug``path` substitution and need no new
sequence.
### 6.6 Non-functional requirements & cross-cutting concerns
- **Security & privacy:** read follows existing project/collection visibility;
propose gated by `can_contribute` (INV-3); all writes via the bot; secrets stay
references, never bytes (§6.3-handbook). No new PII.
- **Performance & scale:** v1 returns the full markdown tree from the TTL cache;
cold build is one git-trees call + a last-commit join. Lazy per-folder loading
is the scale follow-on if a body's tree is large enough to hurt cold-build
latency.
- **Availability & resilience:** stale-cache fallback + empty/retry state
(INV-7); no new hard dependency beyond what the corpus already needs.
- **Observability:** log tree cold-builds, cache hit/miss, gitea-failure
fallbacks; reuse existing health signals.
- **Accessibility:** the tree uses ARIA `tree`/`treeitem` roles with keyboard
expand/collapse and roving focus; state conveyed by text/label, not color alone.
### 6.7 Key decisions & alternatives considered
| Decision | Chosen | Alternatives considered | Why chosen |
| --- | --- | --- | --- |
| D1 Interaction model | Full governance lifecycle on any file | read-only viewer; read + light comments | operator intent; reuses existing machinery |
| D2 Topology | Tree becomes universal | tree as a second view; tree only for a new type | one navigation model; no idiom fork |
| D3 Entry identity | Path-addressed | keep slug + side table of paths | path is the natural key for a real body |
| D4 Folders vs collections | Distinct | merge folders into collections | collections are access-control, folders are structure |
| D5 Project type | No new type | a `tree`/`docs` type | "universal" means all types render via tree |
| D6 Build approach | New `CorpusTree`, port flat mode, retire Catalog | evolve Catalog in place; promote DocsLayout flyout | clean dual-mode boundary; safe staged cutover |
| D7 Existing files | "No record = active" | backfill a record per file at onboard | zero-migration onboarding |
| D8 Affordances | Keep all four via dual mode | drop sort/pending on a tree | operator marked all non-negotiable |
| D9 Routing | `e/*` splat + `?pr=` + legacy shim | encode path in slug; new `/f/` segment | least collision; reuses redirect pattern |
### 6.8 Testing strategy
Tests are part of each slice (not a follow-up).
- **Unit (vitest):** `CorpusTree` mode-switching (search→flat, sort→flat,
clear→structure), badge rendering, expand/collapse persistence, starred-pin,
flat-mode parity with the retired Catalog.
- **Backend (DI at boundaries):** tree endpoint state derivation (INV-2), open-PR
detection, path-addressed read/contribution, slug→path migration, rename
reconciliation (INV-4).
- **Two-tier:** Tier-1 local-Docker gitea for branch/PR flows; Tier-2 PPE for the
onboard-a-real-repo path (SLICE-4). e2e: browse → open; propose-at-path → PR →
merge → state transitions; search flatten; legacy redirect.
### 6.9 Failure modes, rollback & flags
- **Failure:** gitea unavailable → **behavior:** stale cache or empty/retry
(INV-7) → **rollback:** none needed.
- **Failure:** a project regresses on the new pane → **behavior/rollback:** flip
the **per-project-type cutover flag** off; the Catalog renders again (not
deleted until parity is proven).
- **Feature flag:** per-project-type cutover flag, default **off**; deployments
enable per project after validating parity.
## 7. Delivery Plan
### 7.1 Approach / strategy
Riskiest-foundation-first: land the path/state model and tree read behind the
existing flat UI (no user-visible change), then the dual-mode UI behind a flag,
then contribution-at-path, then onboard a real body. Per the handbook execution
convention, **each slice is its own coding session** — plan just-in-time →
execute → verify → ship → merge + version bump — in dependency order; this design
pass happens once and is amended only if a slice proves it wrong. The flag keeps
every intermediate state shippable.
### 7.2 Slicing plan
#### SLICE-1 — Backend foundation → completes (no user-visible PUC)
- **Depends on:** —
- **DoD:** path-keyed lifecycle + slug→path migration + "no record = active"
(INV-2) + tree endpoint + path-addressed reads; Catalog still renders; backend
tests green.
#### SLICE-2 — `CorpusTree` frontend → completes PUC-1, PUC-2, PUC-3, PUC-6, PUC-7, PUC-8, PUC-10, PUC-12
- **Depends on:** SLICE-1
- **DoD:** dual-mode pane behind the cutover flag; path routing + legacy shim;
flat-mode parity; Catalog retired once a project is flipped; unit + e2e green.
#### SLICE-3 — Contribution-at-path → completes PUC-4, PUC-5, PUC-11
- **Depends on:** SLICE-1
- **DoD:** propose/edit/discussion at arbitrary paths; rename reconciliation
(INV-4); validation (BUC-2a); tests green.
#### SLICE-4 — Onboard an existing doc body → completes PUC-9 (BUC-4)
- **Depends on:** SLICE-2, SLICE-3
- **DoD:** a real external repo registered + bot access + flag flipped; all
pre-existing files render active with zero migration; Tier-2 PPE validation.
### 7.3 Rollout / launch plan
Pre-v1, single-prod: each slice ships to prod on merge with its CHANGELOG upgrade
steps; the per-project-type flag defaults off, so deployments opt in per project.
Rollback trigger: flag off → Catalog returns (§6.9).
### 7.4 Risks & mitigations
| Risk | Likelihood / impact | Mitigation |
| --- | --- | --- |
| Full-tree fetch slow on a large body | M / M | TTL cache; lazy per-folder loading as a defined follow-on |
| Catalog flat-mode parity gaps after cutover | M / H | port logic verbatim; flag-gated per project; keep Catalog until parity proven |
| Rename leaves orphan state records | M / M | webhook reconciliation (INV-4) + test |
| slug→path migration mis-maps non-`document` entry folders | L / M | resolve entry folder per project type, not hardcoded; migration test |
## 8. Traceability matrix
| Pain | Business UC | Product UC | Slice | Tests |
| --- | --- | --- | --- | --- |
| PP-1 | BUC-1 | PUC-1, PUC-2, PUC-3, PUC-6, PUC-7 | SLICE-2 | `test_tree_*`, `corpustree.*.test` |
| PP-4 | BUC-1a | PUC-10 | SLICE-2 | `test_tree_fallback_*` |
| PP-3 | BUC-2 | PUC-5, PUC-6 | SLICE-3 | `test_edit_at_path_*` |
| PP-3 | BUC-2a | PUC-11 | SLICE-3 | `test_propose_validation_*` |
| PP-3 | BUC-3 | PUC-4 | SLICE-3 | `test_propose_at_path_*` |
| PP-2 | BUC-4 | PUC-9 | SLICE-4 | e2e `onboard_repo_*` |
| PP-1 | (compat) | PUC-8 | SLICE-2 | `test_legacy_redirect_*` |
| PP-1 | (detail) | PUC-12 | SLICE-2 | `test_tree_non_markdown_*` |
## 9. Open Questions & Decisions log
**Open**
| # | Question | Owner | Blocks |
| --- | --- | --- | --- |
| — | none outstanding | — | — |
**Resolved**
| # | Decision | Resolution | Date |
| --- | --- | --- | --- |
| D1 | Interaction model | Full governance lifecycle on any file | 2026-06-06 |
| D2 | Topology | Tree becomes the universal left pane | 2026-06-06 |
| D3 | Entry identity | Path-addressed; slug is legacy-redirect-only | 2026-06-06 |
| D4 | Folders vs collections | Kept distinct | 2026-06-06 |
| D5 | Project type | No new type; existing types render via tree | 2026-06-06 |
| D6 | Build approach | New `CorpusTree`; port flat mode; retire Catalog | 2026-06-06 |
| D7 | Pre-existing files | "No record = active"; zero-migration onboarding | 2026-06-06 |
| D8 | Affordances | Keep all four via dual display mode | 2026-06-06 |
| D9 | Routing | `e/*` splat + `?pr=` + legacy shim | 2026-06-06 |
Deferred (not open): lazy per-folder tree loading; raw/preview view for
non-markdown files.
## 10. Glossary & References
- **Structure mode** — left pane showing the directory tree in path order.
- **Flat mode** — left pane showing a ranked flat list (search/non-path sort);
the retired Catalog's behavior.
- **Entry** — a markdown document, addressed by its repo path within a
`(project, collection)`.
- **Lifecycle record** — app-DB row keyed by `(collection, path)` carrying
non-default state; absence means `active`.
- **References:** `SPEC.md` §7 (catalog), §22 (three-tier), §8 (revision), §13
(graduation), §20 (versioning); `CLAUDE.md` (separation-of-concerns);
handbook `solution-design/GUIDE.md` (this doc's standard).