How to read, write, and update the files in this folder.
Per-view file template#
Every route gets a file. Every file follows this template exactly. Sections may
be empty (write _None._) but must be present, so a reader knows what they're
looking at.
---
path: /exact/route/pattern
surface: customer | studio | operator | workspace
domain: <the controlled domain slug used by the matching routes.csv row>
auth: anon | signed-in | signed-in + role:<role> | signed-in + tenant:<tenant>
source: apps/oshun/web/src/app/<path>/page.tsx
status: stub | drafted | walked | stale
last_walked: 'YYYY-MM-DD by <walker/method>, against commit <sha>'
---
# <Human-readable view name>
## Purpose
One paragraph (≤ 3 sentences). What is this view for, and who uses it. Avoid
restating what's already in `V1/features.md` — link to the relevant anchor
instead.
## Entry points
How a user arrives at this view.
- Linked from `<route>` via `<link element / button label>`
- Deep link from `<email / push / SMS / share target>`
- Search result from `/search` for query `<example>`
- Direct URL (bookmarkable / shareable / role-gated / hidden)
- Shell nav (home / explore / activity / library / profile / domain switcher)
## Layout regions
The named visual regions a user can identify, top to bottom (or left-to-right on
wide breakpoints). Use the actual region names from the page component.
- **Header**: <variant — shell header / domain header / minimal>
- **Hero / above-the-fold**: <what's here>
- **Main**: <primary content panel>
- **Aside / secondary panel**: <if any>
- **Footer / sticky bottom**: <if any>
## States
Every observable visual state of the view. Produce it in local development when
the required system is available; otherwise mark it N/A with a reason or leave
it unchecked with the exact external prerequisite. Never invent reachability.
- [ ] **Loading** — skeleton or spinner matches final layout dimensions (no
CLS); skeleton respects theme tokens; loading attribute announced to
screen readers
- [ ] **Empty** — no data; on-brand copy; CTA points to a productive next
action; not just a "nothing here" placeholder
- [ ] **Populated (short)** — 1–5 items; expected layout
- [ ] **Populated (long)** — 50+ items; pagination/virtualization/infinite
scroll works; scroll position restored on back-navigation
- [ ] **Error (recoverable)** — failed query; retry button works; original query
parameters preserved
- [ ] **Error (unrecoverable)** — 500/403; support contact link; navigation away
still works
- [ ] **Offline** — network unreachable; SW serves cached read where sensible;
writes are queued or disabled with explanation; offline banner visible
- [ ] **Gated** — auth/role/feature-flag denies; user sees why and how to
unlock; no information leak
- [ ] **Standalone PWA** — view renders cleanly when launched from home-screen
icon; URL bar absent; safe-area insets respected
If a state cannot occur for this view, write
`- [x] **<state>** — N/A because <reason>` so the omission is auditable.
## Interactions
One bullet per discrete interactive element. Drill into nested elements (e.g., a
menu's items, a modal's buttons).
Each bullet uses this micro-template:
```
- [ ] **<Element label>** (<element type: button / input / link / tab / etc.>)
- Function: <what it does>
- Keyboard: <key activates; tab order position>
- Screen reader: announces "<text>"
- Touch target: ≥ 44×44 CSS px on coarse pointers
- Mobile (≤ 640 px): <any layout/behavior delta>
- Disabled when: <condition> → tooltip/aria-disabled text "<text>"
- Offline behavior: <queue / disable / no-op>
- Telemetry: <event name fired>
```
If a sub-element exists (e.g., menu items, dialog content), nest its bullets one
level deeper.
## Data & contracts
What this view reads, writes, and subscribes to.
- **Reads**: `<BFF route or lib call>` — returns `<contract type>`
- **Writes**: `<BFF route>` — accepts `<contract type>`
- **Realtime**: `<channel/topic if any>` — `<events handled>`
- **Caching**: SSR / SWR / ISR / static / SW-cached (which cache key)
- **Auth/role check**: `<middleware.ts rule or BFF policy>`
Link to the contract in `libs/contracts/` or `libs/openapi/`.
## Cross-references
- Feature spec: [`V1/features.md#<anchor>`](../V1/features.md#anchor)
- Architecture: [`V1/ARCHITECTURE.md#<anchor>`](../V1/ARCHITECTURE.md#anchor)
- Journeys that pass through this view: `journeys/<file>.md`
- Sibling routes: `<list>`
- Component sources: `apps/oshun/web/src/<dir>/<file>.tsx`
## Open questions / known gaps
- [ ] <items the walker noted but couldn't resolve in one pass>
Status legend#
| Status | Meaning |
|---|---|
| stub | File exists with header; content not written. |
| drafted | Content written from code reading; not yet verified against a running app. |
| walked | Direct runtime evidence (manual or automated) verifies every reachable check; the method and date are recorded. |
| stale | The file is walked but the code under source has changed since last_walked. |
Status is the truth in matrix/routes.csv. The status header inside each file
mirrors the CSV. When you walk a view, update both.
Cross-reference rules#
- A bare
§Nor§N.Mresolves toV1/TODOS.md(the execution backlog). features§"<anchor>"resolves toV1/features.mdby heading anchor.arch§"<anchor>"resolves toV1/ARCHITECTURE.mdby heading anchor.- Within the walkthrough, link with relative Markdown paths (for example,
[PWA behavior](./shell/03-pwa-behavior.md)).
E2E test coverage (specs ↔ journeys)#
Journeys are executable targets. Automation-backed journeys link one or more
Playwright specs under apps/oshun/web/e2e/; journeys without meaningful
automation are labelled as manual in the Docs Center registry. Where a spec
exists, the link is bidirectional and mechanically checkable so a reviewer
can jump from a flow to the test that guards it and back, and drift between the
two is visible.
1. Spec → journey (header comment). Every spec that covers a journey opens with a fenced header block naming the journey doc and the steps it exercises:
/**
* E2E · Journey: WALKTHROUGH/journeys/<journey-name>.md
* Covers: steps 1–6 (proxy boundary 307/404 → /aaa-upgrade page → AaaGate embed leak).
* Coverage matrix: WALKTHROUGH/journeys/coverage.md
*
* A spec may cover one journey, part of a journey, or steps shared by several
* journeys — list each journey it backs on its own `Journey:` line.
*/
The Journey: line uses the repo-relative path (greppable, unambiguous). A spec
that is purely per-view (not tied to a journey) omits the block — only
journey-backing specs carry it.
2. Journey → spec (## E2E coverage section). Every journey doc carries an
## E2E coverage section, placed immediately after Failure modes and before
Per-view files touched:
## E2E coverage
- [`apps/oshun/web/e2e/<file>.spec.ts`](../../apps/oshun/web/e2e/<file>.spec.ts)
— steps 1–6; drives the proxy boundary, the disclosure page, and the embed
gate.
- **Coverage depth**: deep | partial | shallow | none — see
[`coverage.md`](./coverage.md).
- **Uncovered**: list any step/failure-mode no spec exercises yet, with the
reason (blocked on a missing route, requires a testnet, etc.). `_None._` when
whole.
3. Master matrix. journeys/coverage.md is the
single index: every journey → its primary spec(s) → coverage depth → status. It
is the source of truth for "is this flow tested, and how well". Update it in the
same change that adds or deepens a spec.
Coverage-depth rubric (used in the matrix and the per-journey section):
| Depth | Meaning |
|---|---|
| deep | Drives the journey's interactions (click/type/nav), asserts state transitions + post-conditions, and exercises ≥1 failure mode. |
| partial | Exercises some steps but skips key interactions, failure modes, or post-conditions. |
| shallow | Smoke only — asserts a heading/URL renders; does not drive the journey's interactions. |
| none | No spec meaningfully covers the journey. |
A shallow spec is the test-equivalent of a stub: it would pass against an almost
empty page. Treat shallow like stub in the route matrix — it is not done.
Check authoring rules#
- One element, one bullet. A "save and continue" button is one bullet, not two — but if the bullet's behavior depends on which mode the form is in, the bullet says so.
- Verbs, not descriptions.
- [ ] **Save** opens confirmation— not- [ ] There is a confirmation. - Per-state, not per-pixel. Don't itemize visual styling unless the spec calls out a specific token or animation.
- No hypotheticals. Only list elements that exist in code today. If the spec says something must exist and the code doesn't have it, log it under "Open questions / known gaps".
- Use the actual label / placeholder / aria-label text so a reader can use Ctrl+F in the running app to find the element.
Walking discipline#
Walking a file means:
- Open the per-view file. Read
source:. - Open
apps/oshun/web/src/app/<route>/page.tsx. Make sure the file exists and the imports are recognizable. - Run the app locally. Navigate to the route as the auth role specified in the header.
- For each state in States: produce that state in the running app (use dev tools / mock data / network throttling / role switcher) and tick the box.
- For each bullet in Interactions: trigger it; verify function, keyboard, screen reader, touch target, mobile, disabled, offline, and telemetry. Tick when all sub-bullets pass.
- Verify Data & contracts by tracing a network request in DevTools.
- Update
status: walkedandlast_walked: <date> by <name>, against commit <sha>in the file header. Update the matching CSV row.
If you can't reach a state or trigger an interaction, leave it unchecked and write what blocked you in Open questions / known gaps. Never tick a box you didn't verify.
File naming#
Files within a folder use kebab-case derived from the route, with the leading slash stripped:
| Route | File |
|---|---|
/ |
customer/02-home-discovery/home.md |
/tara |
customer/03-tara/tara.md |
/arete/coaching |
customer/04-arete/arete-coaching.md |
/domains/arete/journal |
customer/04-arete/domains-arete-journal.md |
/studio/design-language |
studio/design/studio-design-language.md |
/operator/review-queues |
operator/operator-review-queues.md |
A file named README.md inside a domain folder is the domain index — it lists
every route in that folder with one-line status and a link.