making the mess clean plans added

This commit is contained in:
hamid
2026-07-29 22:46:38 +03:30
parent 96b57eb1b8
commit c99e3f4a6e
9 changed files with 1365 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
# The Clarify Chain — a plan to clean up Balinyaar's documentation
**Status:** planned, not started · **Written:** 2026-07-29 · **Baseline commit:** `96b57eb`
This folder is a **plan**, not documentation. It describes eight phases that take the repo from its
current state — 260 markdown files, ~10 competing rule sources, five unreconciled backlogs, and a
contract layer that stopped tracking the code on 2026-07-13 — to a single navigable `docs/` tree that
tells the truth.
When the chain finishes, **this folder moves to `archive/`**. It is temporary by design.
---
## Why this exists (the diagnosis)
Measured against the working tree at `96b57eb`:
| Finding | Evidence |
| --- | --- |
| **3.0 MB / 199 files of build history in [`dev/`](../../dev/)** reads as instruction, not record | [`dev/phases/`](../../dev/phases/) last touched 2026-06-28 (already-executed prompts) sits beside [`dev/post-phase/`](../../dev/post-phase/) touched 2026-07-28 (live plan). Nothing marks the difference. |
| **Rules live in ~10 places** | root [CLAUDE.md](../../CLAUDE.md) · [client/CLAUDE.md](../../client/CLAUDE.md) **161 KB** · [server/CLAUDE.md](../../server/CLAUDE.md) 75 KB · [server/CONVENTIONS.md](../../server/CONVENTIONS.md) · [client/messages/STYLE.md](../../client/messages/STYLE.md) · [.claude/skills/frontend-designer/SKILL.md](../../.claude/skills/frontend-designer/SKILL.md) · [dev/phases/_shared/](../../dev/phases/_shared/) ×4 · [dev/contracts/conventions/](../../dev/contracts/conventions/) ×2 · 3× `AGENTS.md` · [.githooks/README.md](../../.githooks/README.md) |
| **Loading `client/CLAUDE.md` costs ~40k tokens** on any client edit | 161 KB, of which lines 103421 are a project-structure listing and lines 4211100 are explanatory reference |
| **Remaining work is split across 5 ledgers** | 67 REQs in a 1,115-line append-only file · **18 hardening items, none ticked** · per-phase "Follow-ups" buried in 50+ reports · backend deferrals-with-pull-triggers · [product/notes/open-questions.md](../../product/notes/open-questions.md) · two raw feedback notes in [dev/manual-testing/](../../dev/manual-testing/) |
| **Contracts drifted from code** | [`dev/contracts/`](../../dev/contracts/) frozen 2026-07-13; `server/src` moved through 2026-07-28. OpenAPI snapshot is from 07-13; a second stale copy sits at [temp/swagger.json](../../temp/swagger.json) (07-06). |
| **18 files still instruct you to use `dotnet user-secrets`** — removed in `5885280` | includes [manual-testing-plan.md](../../dev/post-phase/manual-testing-plan.md), the closest thing to a test guide |
| **No single "what's implemented + how to test it"** | knowledge split across manual-testing-plan, [RUNBOOK.md](../../dev/post-phase/refinement/RUNBOOK.md), `product/business/`, and 18 contract files |
| **The client↔server dependency is implicit** | envelope shape, casing, idempotency, cookies/refresh, CORS, `NEXT_PUBLIC_API_URL`, 3 containers + Caddy + remote SQL + the OTP bot — described nowhere as one thing |
The code itself is **not** the mess. 83 client routes, 22 service domains, 125 client tests; 56
controllers, 199 handlers, 24 migrations. The problem is that nothing tells you which parts of that
are real, which are mocked, and which are described by a document written three weeks ago.
---
## Decisions this plan implements
Agreed 2026-07-29, before any file was written:
| Decision | Choice |
| --- | --- |
| Doc home | **New top-level `docs/` tree.** `product/` stays as-is; `dev/` becomes `archive/`; CLAUDE.md files shrink to hard rules + pointers |
| `dev/` history | **Distill, then archive in-repo.** Every live fact extracted first; raw files moved under `archive/` with a do-not-obey banner |
| Rules | **Tiered.** CLAUDE.md keeps only non-negotiables (~200 lines); everything explanatory becomes reference read on demand |
| Verification | **Verify the load-bearing claims.** Flow status, the 18 hardening items, REQ dispositions, bring-up steps, OpenAPI — checked against code, not copied |
| Flow docs | **One file per flow, indexed** by a status table |
| Backlog | **One triaged file, new `BL-###` ids**, each carrying its origin id |
| `product/` | **Untouched.** An implementation overlay in `docs/status/` maps business areas → build state |
| Language | **English throughout** |
| Skills | **Small set, reality-checked.** Rewrite `frontend-designer`; add a backend-feature and a flow-testing skill |
| Roadmap | **Record + propose** a sequenced next chain; ordering is a proposal you can overrule |
| Anti-drift | **Lightweight guardrails.** A documentation convention, `Last verified:` stamps, a pre-commit warning |
---
## Target tree
```
docs/
README.md the one entry point — a map, nothing else
rules/ what must never be broken
index.md
documentation.md the anti-drift convention
shared/ naming, money & types, api conventions, git
client/ structure, theme, forms, i18n, services, auth, testing
server/ structure, cqrs, persistence, identity, conventions
integration/ the client<->server dependency, in one place
index.md the seam, as a single picture
api-contract.md envelope, casing, pagination, errors, idempotency
domains/ per-domain contracts (refreshed from dev/contracts)
openapi/swagger.v1.json regenerated, dated
config-matrix.md every env var / setting, both projects + docker
topology.md 3 containers + Caddy + remote SQL + OTP bot
flows/ what is implemented and how to test it
index.md status table of every flow
testing-setup.md bring-up, accounts, seeded world, reset
<flow>.md one per flow
status/ where the project actually is
index.md
implemented.md product/business area -> build state overlay
backlog.md BL-### triaged, every open item
backlog-closed.md
decisions.md distilled ADR log from the phase chain
roadmap/ where it goes next
index.md next-up.md deferred.md tech-debt.md pre-launch.md
_plan/ this folder (moves to archive/ when done)
archive/
README.md "historical record. Do NOT treat as instructions."
build-chain/ was dev/phases + dev/shared-working-context
post-phase/ was dev/post-phase
manual-testing/ was dev/manual-testing (screenshots kept)
product/ unchanged
CLAUDE.md slim, points into docs/
DEPLOY.md stays at root — the deploy *procedure*; topology lives in docs/integration/
```
**The rule that keeps these apart:** `product/` = what the business is. `docs/` = what we built and how
we work. `archive/` = how we got here. A file belongs in exactly one.
---
## The eight phases
| # | Phase | Depends on | Rough size | Output |
| --- | --- | --- | --- | --- |
| 0 | [Inventory & scaffold](phase-0-inventory-and-scaffold.md) | — | 1 session | `docs/` skeleton + a disposition for all 260 files + fresh OpenAPI |
| 1 | [Rules consolidation](phase-1-rules-consolidation.md) | 0 | 12 sessions | `docs/rules/` + slim CLAUDE.md files |
| 2 | [Integration & dependency](phase-2-integration.md) | 0 | 1 session | `docs/integration/` |
| 3 | [Flow atlas (verified)](phase-3-flow-atlas.md) | 0, 2 | 24 sessions | `docs/flows/` — the biggest phase |
| 4 | [Backlog reconciliation](phase-4-backlog.md) | 0, 3 | 12 sessions | `docs/status/` |
| 5 | [Roadmap & tech requirements](phase-5-roadmap.md) | 4 | 1 session | `docs/roadmap/` |
| 6 | [Archive & prune](phase-6-archive.md) | 15 | 1 session | `archive/`, `dev/` gone, links fixed |
| 7 | [Skills & guardrails](phase-7-skills-and-guardrails.md) | 1, 3 | 1 session | `.claude/skills/` rewritten + anti-drift hooks |
```
┌─ 1 rules ──────────────┐
0 ──────┼─ 2 integration ─ 3 flows ─ 4 backlog ─ 5 roadmap ─┼─ 6 archive
└────────────────────────┴──── 7 skills ────────────┘
```
Phases 1 and 2 can run in parallel with each other after 0. Everything else is a chain.
---
## How to run a phase
Point a fresh agent session at one phase file:
> Execute `docs/_plan/phase-2-integration.md`.
Each phase file states its own inputs, outputs, steps, verification, and definition of done. It ends
by ticking its own row in the progress table below and writing a short handoff note at the bottom of
its own file. **Do not run two phases in one session** — the point of this exercise is that context
stays small.
## Non-negotiables for every phase
1. **Nothing is deleted before it is distilled.** Phase 6 is the only phase that removes files, and it
may only remove files that Phase 0's inventory marked as *archived* or *extracted*.
2. **Verify, don't copy.** If a claim is load-bearing (a flow works, an item is closed, a command
boots the app), check it against the code or run it. If you cannot verify it, write it with an
explicit `UNVERIFIED:` prefix rather than asserting it.
3. **Every status/flow doc carries a `> Last verified: <date> against <commit>` header line.**
4. **Write short.** The failure mode this chain fixes is length. A reference doc over ~400 lines
should be split. A phase that produces a 30 KB file has misunderstood the assignment.
5. **Record contradictions, don't silently pick.** When two docs disagree, check the code, write the
truth, and log the contradiction in `docs/status/decisions.md`.
6. **Update this README's progress table** in the same change that finishes a phase.
## Progress
| Phase | Status | Finished | Notes |
| --- | --- | --- | --- |
| 0 Inventory & scaffold | not started | | |
| 1 Rules consolidation | not started | | |
| 2 Integration & dependency | not started | | |
| 3 Flow atlas | not started | | |
| 4 Backlog reconciliation | not started | | |
| 5 Roadmap | not started | | |
| 6 Archive & prune | not started | | |
| 7 Skills & guardrails | not started | | |
@@ -0,0 +1,148 @@
# Phase 0 — Inventory & scaffold
**Depends on:** nothing · **Blocks:** every other phase · **Size:** one session
## Goal
Before anything is moved, merged, or deleted, produce two things:
1. **A disposition for every one of the ~260 markdown files in the repo** — so that "summarize
without losing data" is a checkable claim rather than a hope.
2. **The empty `docs/` skeleton** with a real entry point, so later phases have somewhere to write.
Plus one piece of housekeeping every later phase needs: a **fresh OpenAPI snapshot**, because the
committed one is 16 days behind the server code.
This phase writes almost no prose. It is a survey.
---
## Inputs
- The whole repo. Specifically: `dev/` (199 files), `product/` (48), root (4), `client/` (5),
`server/` (6), `telegram-otp-bot/` (2), `.claude/skills/` (1), `.githooks/` (1).
- [`../README.md`](README.md) — the target tree and the decisions.
## Outputs
| File | What it is |
| --- | --- |
| `docs/_plan/inventory.md` | The disposition table — every markdown file, one row |
| `docs/README.md` | The single entry point: a map of `docs/`, `product/`, `archive/` and what belongs where |
| `docs/rules/index.md` `docs/integration/index.md` `docs/flows/index.md` `docs/status/index.md` `docs/roadmap/index.md` | Stub indexes, each with a one-line purpose and a "populated by phase N" note |
| `docs/integration/openapi/swagger.v1.json` | Regenerated from the running server |
| `docs/_plan/open-contradictions.md` | Running list of doc-vs-doc and doc-vs-code conflicts spotted during the survey — later phases resolve these |
---
## Steps
### 1. Build the inventory
Walk every `.md` file (exclude `node_modules`, `bin`, `obj`, `.git`). For each, record:
| Column | Meaning |
| --- | --- |
| `path` | repo-relative |
| `bytes` | size |
| `last commit` | `git log -1 --format=%ad --date=short -- <path>` |
| `kind` | `rule` · `reference` · `contract` · `plan` · `report` · `history` · `business` · `ops` · `feedback` |
| `disposition` | `keep-as-is` · `move` · `distill` · `merge-into` · `archive` · `delete` |
| `target` | where its content ends up (a `docs/…` path, or `archive/…`) |
| `owner phase` | which phase (17) handles it |
| `live facts?` | `yes` / `no` — does it contain anything not yet captured elsewhere? |
Rules for assigning disposition:
- **`dev/phases/backend/*` and `dev/phases/frontend/*`** (32 files, ~1 MB): these are *prompts that
were already executed*. Disposition `archive`. But scan each for **decisions and constraints stated
in the prompt that are not visible in the code** — those are `live facts: yes` and go to Phase 1
(if a rule) or Phase 4 (`decisions.md`).
- **`dev/shared-working-context/reports/*` (50 files)**: `archive`, but every "Follow-ups for later
phases" / "Deferred" / "Known gap" section is a live fact for **Phase 4**. Extract the section
headings now; Phase 4 reads them in detail.
- **`dev/shared-working-context/*/STATUS.md`** (86 KB combined): `distill` → Phase 4 (`decisions.md`
+ `implemented.md`), then `archive`.
- **`dev/shared-working-context/frontend/requests/for-backend.md`** (67 REQs): `distill` → Phase 4.
- **`dev/post-phase/hardening/issues.md`** (18 open items): `distill` → Phase 4, **with
re-verification** — the UI and refinement chains ran after this file was written and some items are
probably already fixed.
- **`dev/contracts/**`**: `move``docs/integration/` — Phase 2 owns it, and refreshes it against code.
- **`dev/post-phase/manual-testing-plan.md`** + **`refinement/RUNBOOK.md`**: `distill` → Phase 3
(`docs/flows/testing-setup.md` + the per-flow files). Both are stale on secrets handling.
- **`dev/manual-testing/iteration-*/`**: `distill` → Phase 4 (unfinished items) then `archive`
(**keep the PNG screenshots** — they are the only visual record of the pre-overhaul UI).
- **`product/**`**: `keep-as-is`, every one. Not this chain's business.
- **`client/CLAUDE.md`, `server/CLAUDE.md`, `server/CONVENTIONS.md`, `client/messages/STYLE.md`,
`dev/phases/_shared/*`, `dev/contracts/conventions/*`, `.claude/skills/*/SKILL.md`**: `distill`
Phase 1.
- **`DEPLOY.md`**: `keep-as-is` at root (it is the deploy *procedure* and it is current) — but Phase 2
fixes its stale `user-secrets` reference and Phase 2's `topology.md` links to it.
- **`Prompt.md`** (0 bytes): `delete`.
- **`temp/swagger.json`** (stale duplicate): `delete` — Phase 2 replaces it.
- **`client/graphify-out/GRAPH_REPORT.md`, `server/graphify-out/GRAPH_REPORT.md`**: generated tool
output. Decide `keep-as-is` if the tool is still used, else `delete`; check `.gitignore` first.
### 2. Log contradictions as you go
Write `docs/_plan/open-contradictions.md` with one row per conflict: *claim A (file:line) vs claim B
(file:line or code:line) — unresolved*. Seed it with the ones already known:
- **`user-secrets` is required** (18 files) vs **`user-secrets` was removed and the store isn't read**
(root [CLAUDE.md](../../CLAUDE.md) §6, commit `5885280`).
- **`dev/contracts/` describes the API** (frozen 2026-07-13) vs **`server/src`** (through 2026-07-28).
- **Design language in [.claude/skills/frontend-designer/SKILL.md](../../.claude/skills/frontend-designer/SKILL.md) §§17**
vs **[client/CLAUDE.md](../../client/CLAUDE.md) "Theme System" / "Anti-patterns"** — overlapping, possibly divergent.
- **Both** vs **the reality after iterations 12** (mobile-scoped shell, bottom nav, react-hook-form,
new icon set) — the skill predates that overhaul.
- **18 hardening items open** (2026-07-16) vs **UI phases 013 and the deploy commits that ran after**.
Do **not** resolve these here. Phase 0 only finds them.
### 3. Regenerate the OpenAPI snapshot
Boot the server and capture the live swagger document:
```bash
cd server && dotnet run --project src/API/Baya.Web.Api/Baya.Web.Api.csproj
# then, from another shell:
curl -sk https://localhost:5002/swagger/v1/swagger.json -o docs/integration/openapi/swagger.v1.json
```
Record in the file's sibling `README.md`: the date, the commit, and the endpoint count. Diff the
endpoint *paths* against the old `dev/contracts/openapi/swagger.v1.json` and write the added/removed
list into `docs/_plan/open-contradictions.md` — Phase 2 uses it as its worklist.
If the server will not boot (config, DB reachability), **stop and report** rather than guessing. A
stale snapshot is what caused half of this mess.
### 4. Scaffold `docs/`
Create the directories and stub index files from the target tree in [`README.md`](README.md). Each
stub is ~10 lines: what lives here, which phase fills it, and a `> Populated by phase N — not yet
written` banner. `docs/README.md` is the only one with real content: the map, and the
`product/` vs `docs/` vs `archive/` rule.
Do **not** create `archive/` yet — Phase 6 does that, once there is something safe to put in it.
---
## Verification
- [ ] `docs/_plan/inventory.md` has a row for every `.md` file the survey walked; the count matches
`find . -name "*.md" -not -path "./node_modules/*" … | wc -l` (~260 at baseline).
- [ ] No row has an empty `disposition` or `owner phase`.
- [ ] Every row with `live facts: yes` names the phase that will extract them.
- [ ] `docs/integration/openapi/swagger.v1.json` exists and its endpoint count is recorded.
- [ ] Every stub index exists and says which phase fills it.
- [ ] Nothing outside `docs/` was modified, except deleting `Prompt.md` and `temp/swagger.json`.
## Definition of done
You can answer, for any markdown file in the repo, "where does this end up and who handles it?"
without re-reading the file.
## Handoff
_(filled in by the agent that runs this phase — file counts, surprises, anything that changes a later
phase's scope)_
+175
View File
@@ -0,0 +1,175 @@
# Phase 1 — Rules consolidation
**Depends on:** Phase 0 · **Can run in parallel with:** Phase 2 · **Size:** 12 sessions
## Goal
Collapse ~10 rule sources into one tiered system, and cut the cost of editing client code from
~40k tokens to ~5k.
**The tiering rule:**
| Tier | Where | What goes in it | Budget |
| --- | --- | --- | --- |
| **Hard rules** | `client/CLAUDE.md`, `server/CLAUDE.md`, root `CLAUDE.md` | Constraints whose violation breaks the build, the gate, or a business invariant. Stated imperatively, no explanation. | ~200 lines each |
| **Reference** | `docs/rules/{shared,client,server}/*.md` | The *how* and the *why*. Read on demand when working on that area. | ~200400 lines per file |
| **Procedure** | `.claude/skills/` | Step-by-step playbooks for recurring tasks. **Phase 7** owns these. | — |
A rule that only matters when you are already editing theme code is **reference**, not a hard rule.
A rule like "never change `Seams:FieldEncryption:Key`" is a hard rule — it belongs inline.
---
## Inputs
Read these in full; they are the raw material:
| Source | Size | Contains |
| --- | --- | --- |
| [client/CLAUDE.md](../../client/CLAUDE.md) | 161 KB | 22 sections; lines 103421 are a structure listing, 421+ is reference |
| [server/CLAUDE.md](../../server/CLAUDE.md) | 75 KB | 11 sections; lines 89603 are the project map |
| [server/CONVENTIONS.md](../../server/CONVENTIONS.md) | 27 KB | naming, layering, CQRS shape |
| [client/messages/STYLE.md](../../client/messages/STYLE.md) | 8 KB | Persian copy rules (enforced by `lint:copy`) |
| [.claude/skills/frontend-designer/SKILL.md](../../.claude/skills/frontend-designer/SKILL.md) | 21 KB | brand, tokens, typography, component library, layout, icons |
| [dev/phases/_shared/agent-operating-rules.md](../../dev/phases/_shared/agent-operating-rules.md) | 13 KB | how agents were told to work |
| [dev/phases/_shared/definition-of-done.md](../../dev/phases/_shared/definition-of-done.md) | 3 KB | the gate |
| [dev/phases/_shared/backend-conventions-checklist.md](../../dev/phases/_shared/backend-conventions-checklist.md) | 4 KB | |
| [dev/phases/_shared/frontend-conventions-checklist.md](../../dev/phases/_shared/frontend-conventions-checklist.md) | 3 KB | |
| [dev/contracts/conventions/api-conventions.md](../../dev/contracts/conventions/api-conventions.md) | 3 KB | → **Phase 2 owns this**; read for cross-check only |
| [dev/contracts/conventions/money-and-types.md](../../dev/contracts/conventions/money-and-types.md) | 3 KB | → **Phase 2 owns this**; read for cross-check only |
| root [CLAUDE.md](../../CLAUDE.md) · 3× `AGENTS.md` · [.githooks/README.md](../../.githooks/README.md) | small | working agreements, pointers, the pre-commit hook |
Plus: `docs/_plan/inventory.md` and `docs/_plan/open-contradictions.md` from Phase 0.
## Outputs
```
docs/rules/
index.md what's here, and the tiering rule restated
documentation.md the anti-drift convention (Phase 7 adds the hook that enforces it)
shared/
naming.md Baya* vs balinyaar-client, @/* alias, file/dir conventions
git-and-gates.md branch, commit, pre-commit hook, what "done" means per project
code-quality.md no dead code, comment the why, no starter scaffolding
client/
structure.md the route/folder map (regenerated from reality, not copied)
theme.md tokens, palette, dark mode, RTL, fonts
components.md the App* library, when to reach for it vs raw MUI
forms.md react-hook-form (post-iteration-2), validation, field components
i18n.md next-intl v4, en/fa parity, STYLE.md copy rules folded in
services.md services/{domain} pattern, fetch layer, envelope, query keys
auth.md cookies, session state, refresh, RoleGuard, middleware/PUBLIC_PATHS
testing.md what is tested, how, the 125 existing tests
server/
structure.md the project map (condensed from server/CLAUDE.md lines 89-603)
cqrs.md how a feature is shaped: command/query/handler/validator
persistence.md EF Core, migrations, interceptors, the audit interceptor
identity.md JWE, sessions, rotation, field encryption, PhoneHash
conventions.md successor to CONVENTIONS.md
```
Rewritten in place: `CLAUDE.md` (root), `client/CLAUDE.md`, `server/CLAUDE.md`, 3× `AGENTS.md`.
Deleted after distillation: `server/CONVENTIONS.md`, `client/messages/STYLE.md` (content moves to
`docs/rules/client/i18n.md`; **check `lint:copy` doesn't read STYLE.md by path before deleting**).
---
## Steps
### 1. Extract every distinct rule into a ledger first
Before writing any output file, build a working list (scratch, not committed) of every *rule* found
across all inputs, tagged with: source file:line, scope (client/server/shared), tier
(hard/reference), and whether it is **contradicted or obsoleted** by another source or by the code.
This is the step that prevents loss. Merging by reading two documents side by side and writing a
third loses whatever was in neither's first half.
### 2. Reality-check the client rules against the post-overhaul code
`client/CLAUDE.md` and the frontend-designer skill both predate iterations 12 (commits `baa3cc6`,
`e6a8f93`). Those commits changed load-bearing things:
- **Mobile-scoped shell** — max-width container, no desktop sidebar for the nurse side
- **Bottom navigation** replacing the drawer, grouped root pages with domain summaries
- **Icon set replaced** wholesale
- **react-hook-form** for every form with >1 field (28 files currently import it — confirm coverage
and note any state-based forms left behind; that gap belongs in Phase 4's backlog)
- **Theme/language switches** moved into settings only, out of every top bar
- **Paper border-radius** reduced globally
Any rule in either document that describes the *old* behaviour is obsolete. Rewrite it against the
code, and log the correction in `docs/_plan/open-contradictions.md` as resolved.
Do the same, more briefly, for the server: `server/CLAUDE.md`'s "Project map" (514 lines) must match
the 14 `.csproj` projects and 56 controllers actually present.
### 3. Write the reference layer
One file per row of the Outputs tree. Each opens with a two-line purpose and a
`> Last verified: <date> against <commit>` line. Keep them under ~400 lines; if `client/structure.md`
wants to be longer, it is listing files it should be describing patterns for.
### 4. Rewrite the three CLAUDE.md files
Each becomes, in order:
1. One paragraph: what this project is.
2. **Stack** and **Commands** (keep — they are consulted constantly).
3. **Quality gates** — the exact commands that must pass.
4. **Hard rules** — a numbered list, imperative, no prose. Target 1525 items.
5. **Where to read more** — a table mapping "working on X" → `docs/rules/…/X.md`.
Root `CLAUDE.md` keeps its "What Balinyaar is", "Repository layout" (updated for `docs/` and
`archive/`), and the working agreements — but agreement 7 ("keep the architecture map current") now
points at `docs/rules/` as well.
Rules that must survive into the hard-rule lists verbatim (do not soften):
- `Seams:FieldEncryption:Key` / `:HashKey` are load-bearing — changing them makes every PII read throw
and every phone lookup miss.
- Config lives in files, not a secret store — **`dotnet user-secrets` is not used and is not read**.
(This is the single most-repeated stale instruction in the repo; state it loudly.)
- Stay within one project per change.
- No dead code; the client fails the build on unused vars.
- Don't reintroduce starter scaffolding or `_TITLE_`/`_DESCRIPTION_` placeholders.
- Read `product/` before changing behaviour.
### 5. Rewrite the three `AGENTS.md`
They stay thin pointers. Update the paths they point at.
### 6. Write `docs/rules/documentation.md`
The anti-drift convention, as agreed:
- What to update when X changes (endpoint → `docs/integration/`; flow ships → `docs/flows/<flow>.md`;
backlog item closed → tick it in `docs/status/backlog.md`, never delete it; structure changes →
the matching architecture section).
- The `> Last verified: <date> against <commit>` header convention, and which docs must carry it.
- The one-home rule: `product/` = business, `docs/` = engineering + status, `archive/` = history.
- Length budgets, so this doesn't regrow.
Phase 7 adds the pre-commit warning that enforces the first bullet.
---
## Verification
- [ ] `client/CLAUDE.md` under 250 lines; `server/CLAUDE.md` under 250 lines.
- [ ] Every rule in the Step-1 ledger appears in exactly one output file — spot-check 20 at random.
- [ ] No rule describes pre-iteration-1/2 client behaviour.
- [ ] `grep -rn "user-secrets" client/ server/ CLAUDE.md docs/` returns only statements that it is
**not** used.
- [ ] `cd client && npm run check` still passes (in case `lint:copy` referenced `STYLE.md` by path).
- [ ] Both `AGENTS.md` pointer files resolve.
- [ ] Every contradiction Phase 0 logged in the rules domain is marked resolved with its resolution.
## Definition of done
An agent opening `client/CLAUDE.md` learns what it may not do in under 250 lines, and knows exactly
which one file to open next for the area it is touching.
## Handoff
_(filled in by the agent that runs this phase)_
+149
View File
@@ -0,0 +1,149 @@
# Phase 2 — Integration & dependency
**Depends on:** Phase 0 · **Can run in parallel with:** Phase 1 · **Blocks:** Phase 3 · **Size:** one session
## Goal
Make the client↔server dependency a **thing you can read in one place**, and re-sync the contract
layer with the code it stopped tracking on 2026-07-13.
Right now the seam is real but scattered: envelope shape and casing in one contract doc, cookies and
refresh in `client/CLAUDE.md`, CORS in a refinement report, `NEXT_PUBLIC_API_URL` in `.env` files,
the container topology in `DEPLOY.md` and `docker-compose.yml`, and the OTP relay in its own README.
Nobody can answer "what does the client actually need from the server?" without reading six files.
---
## Inputs
- `docs/integration/openapi/swagger.v1.json` — the **fresh** snapshot from Phase 0, plus the
added/removed endpoint diff Phase 0 wrote into `docs/_plan/open-contradictions.md`.
- [dev/contracts/](../../dev/contracts/) — 18 domain files + 2 convention files + the stale snapshot.
- [dev/shared-working-context/frontend/requests/for-backend.md](../../dev/shared-working-context/frontend/requests/for-backend.md) —
67 REQs; many *are* contract amendments that were delivered but never folded back into the domain docs.
- [dev/shared-working-context/reports/mocks-registry.md](../../dev/shared-working-context/reports/mocks-registry.md) — which seams are mocked.
- [DEPLOY.md](../../DEPLOY.md) · [docker-compose.yml](../../docker-compose.yml) · [deploy/Caddyfile](../../deploy/Caddyfile)
- [telegram-otp-bot/README.md](../../telegram-otp-bot/README.md) + [INTEGRATION-PROMPT.md](../../telegram-otp-bot/INTEGRATION-PROMPT.md)
- `client/.env.development`, `client/.env.production`, `server/src/API/Baya.Web.Api/appsettings*.json`
- Code, for the seam itself: `client/src/lib/api/`, `client/src/services/*/`, the server's
`ApiResult` envelope, CORS setup, and auth middleware.
## Outputs
```
docs/integration/
index.md THE seam, one page: what crosses the wire and what each side owes the other
api-contract.md envelope, casing, pagination, errors, idempotency, auth headers/cookies
domains/ 22 files, one per service domain — refreshed against the live swagger
openapi/
swagger.v1.json (from Phase 0)
README.md how to regenerate, when it was last taken, endpoint count
config-matrix.md every env var / appsettings key, both projects + docker + the bot
topology.md the runtime dependency graph
```
Also updated: [DEPLOY.md](../../DEPLOY.md) — fix the stale `user-secrets` reference and link to
`docs/integration/topology.md`. It stays at root and stays the deploy *procedure*.
---
## Steps
### 1. Write `index.md` first — the seam on one page
Before touching the per-domain detail, answer these in one page:
- **Transport**: HTTP/JSON over `NEXT_PUBLIC_API_URL`; where gRPC exists and whether it is used.
- **The envelope**: `ApiResult``{ isSuccess, statusCode, message, requestId, data }`, payload
always under `data`, `requestId` is a W3C trace id (refinement-phase-9). Client unwraps in
`clientFetch`/`unwrap()`.
- **Casing**: JSON bodies camelCase; URL segments snake_case. (REQ-001 settled this — confirm against
the live swagger, don't trust the doc.)
- **Pagination**: `{ items, total, page, pageSize }`; the server binds `pageSize` case-insensitively
(REQ-010).
- **Errors**: status codes, machine-readable error codes (REQ-003), what the client does with 401 /
403 / 5xx / network.
- **Auth**: the cookie set, JWE opacity to the client, silent refresh, session rotation and
reuse-detection, `/me` and `me/select_role`, role hydration.
- **Idempotency**: which endpoints require `Idempotency-Key` and what the client generates.
- **Money**: IRR on the wire, Toman at the UI boundary — link to `docs/rules/shared/`.
- **What the server owes the client** and **what the client owes the server**, as two short lists.
### 2. Refresh the 22 domain contracts against the live swagger
For each domain, compare the contract doc against `swagger.v1.json` and the handler code. Mark each
endpoint: `matches` · `drifted (describe)` · `undocumented (in code, not in contract)` ·
`phantom (in contract, not in code)`.
Fold in the delivered REQs — 17+ were delivered in refinement-phase-3 and amended shapes that the
domain docs still describe the old way. **The domain doc is the thing that should be true; the REQ
ledger is a change log.** After this phase, a reader should never need to read the REQ file to know
the current shape.
Note the domain-file cleanup: there is both a `messaging.md` (851 B stub) and a
`messaging-notifications-admin.md` (10.6 KB) — merge. Align the file set with the client's 22
`services/` domains so the mapping is one-to-one where it can be.
Anything you cannot verify from swagger or code: mark `UNVERIFIED:` and add a row to Phase 4's input
list. Do not guess a shape.
### 3. Write `config-matrix.md`
One table: **key · where it's set (appsettings / .env / docker-compose / Caddyfile) · consumed by ·
required? · default · notes**. Cover at minimum:
- `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_NESHAN_KEY`, and the rest of the client's `NEXT_PUBLIC_*`
- `IdentitySettings:SecretKey` / `:Encryptkey`
- `Seams:FieldEncryption:Key` / `:HashKey`**flag as load-bearing and immutable**
- the connection strings (app DB + log DB), and that the DB is remote and **not** containerised
- `OpenTelemetry:Otlp:Endpoint`, the health endpoints (`/healthz/live`, `/healthz/ready`)
- the seam selectors that choose real vendor adapters vs mocks (refinement-phase-8)
- the Telegram OTP bot's token/chat config
- CORS origins, and the Caddy hostnames `balinyaar.ir` / `api.balinyaar.ir`
State plainly, once, that config lives in files and `dotnet user-secrets` is not used — and that this
is a deliberate pre-launch trade with live credentials in git (link to `DEPLOY.md` "Going to
Production" and to Phase 5's `pre-launch.md`).
### 4. Write `topology.md`
The runtime dependency graph — a mermaid diagram plus a short table:
```
browser -> Caddy (caddy_net) -> client container (Next.js) -> server container (ASP.NET)
-> server container -> remote SQL Server (not containerised)
-> telegram-otp-bot (OTP relay)
-> object storage / external rails (per seam config)
```
For each edge: what flows over it, what breaks if it is down, and where it is configured. This is the
"dependency between projects, documented in a proper place" deliverable.
### 5. Point the old locations at the new one
`dev/contracts/README.md` gets a one-line "moved to `docs/integration/`" banner (Phase 6 archives the
folder; until then, don't leave two live copies competing).
---
## Verification
- [ ] Every endpoint in `swagger.v1.json` appears in exactly one `docs/integration/domains/*.md`, or
is listed in that file's "undocumented, intentionally" section with a reason.
- [ ] No `phantom` endpoints remain undeclared — each is either removed from the doc or listed as
"planned, not built" with a backlog reference for Phase 4.
- [ ] `config-matrix.md` accounts for every key in `appsettings.Production.json`,
`docker-compose.yml`, and both `.env` files — diff mechanically, don't eyeball.
- [ ] `DEPLOY.md` no longer instructs anyone to use `user-secrets`.
- [ ] `index.md` fits on one screen-and-a-half and answers the seam question without a single click.
## Definition of done
A frontend agent can build against the API by reading `docs/integration/index.md` plus one domain
file, and a deploy question is answered by `topology.md` + `config-matrix.md` without opening
`docker-compose.yml`.
## Handoff
_(filled in by the agent that runs this phase — especially: the drift list, since Phase 4 turns the
`phantom` and `drifted` rows into backlog items)_
+203
View File
@@ -0,0 +1,203 @@
# Phase 3 — The flow atlas (verified)
**Depends on:** Phase 0, Phase 2 · **Blocks:** Phase 4 · **Size:** 24 sessions — the biggest phase
## Goal
Build **the one go-to place** that answers, for every flow in the product:
1. What is it, and who does it?
2. **Is it actually implemented — really, or only mocked?**
3. What screens and endpoints does it use?
4. **How do I test it, step by step, with which account?**
5. What's known to be broken or missing?
This is the phase where "verify the load-bearing claims" earns its keep. The existing
[manual-testing-plan.md](../../dev/post-phase/manual-testing-plan.md) is the closest predecessor and
it is already wrong about bring-up (it tells you to set `user-secrets`, which the code no longer
reads). Copying it forward would reproduce the problem this chain exists to fix.
**Run this phase in slices.** One session does `testing-setup.md` + 46 flows. Later sessions pick up
the next slice. The index table tracks which flows are done.
---
## Inputs
- [dev/post-phase/manual-testing-plan.md](../../dev/post-phase/manual-testing-plan.md) — flow
walkthroughs, test accounts, the seeded world, the mock-vs-real map, the numbers the UI must respect
- [dev/post-phase/refinement/RUNBOOK.md](../../dev/post-phase/refinement/RUNBOOK.md) — bring-up, demo
accounts, login round-trip, reset, troubleshooting
- [dev/shared-working-context/reports/mocks-registry.md](../../dev/shared-working-context/reports/mocks-registry.md)
- `docs/integration/` — from Phase 2, the endpoint truth
- `product/business/*` — the 14 requirement areas; what "correct" means
- The seeder code (`DemoWorldSeeder`, `DemoLifecycleSeeder`) — **the authority on test accounts and
seeded state**, over any doc
- `client/src/app/[locale]/**` — 83 routes across `(customer)`, `(customer-focused)`, `nurse`,
`admin`, `partner`, `(public-routes)`
- `client/src/services/*/` — 22 domains, each with a mock/real flag
## Outputs
```
docs/flows/
index.md the status table — every flow, one row
testing-setup.md bring-up, accounts, seeded world, OTP, reset, troubleshooting
<flow>.md one per flow (see the candidate list)
```
---
## Candidate flow list
Derived from the route groups, the 22 service domains, and the 14 business areas. Confirm and adjust
in the first session; the point is one file per *user-meaningful journey*, not per screen.
| # | Flow | Actor | Primary routes |
| --- | --- | --- | --- |
| 1 | `auth-login-otp` | all | `(public-routes)/login`, `select-role` |
| 2 | `public-front-door` | guest | `/`, `welcome`, `terms`, `privacy` |
| 3 | `onboarding-customer` | customer | onboarding, account |
| 4 | `care-circle-patients` | customer | patients / care-circle |
| 5 | `addresses-and-map` | customer | addresses, Neshan pin |
| 6 | `onboarding-nurse` | nurse | profile, bank account |
| 7 | `nurse-service-areas` | nurse | coverage, whole-city (`districtId = null`) |
| 8 | `nurse-verification` | nurse + admin | verification journey, document upload, review queue |
| 9 | `nurse-catalog-and-pricing` | nurse | services, variant builder |
| 10 | `search-and-discovery` | customer | C1C3, filters, nurse profile |
| 11 | `booking-request` | customer + nurse | C4/C5, nurse inbox, countdown, accept/reject |
| 12 | `checkout-and-payment` | customer | C6, gateway return, confirmation, escrow |
| 13 | `bnpl-installments` | customer | D1D5 |
| 14 | `booking-lifecycle-evv` | nurse + customer | check-in/out, two-stage clinical gate |
| 15 | `cancellation-and-refunds` | customer + admin | cancel, policy, refund settlement |
| 16 | `reviews` | customer | post-visit review, moderation |
| 17 | `patient-care-records` | nurse + customer | append-only records |
| 18 | `nurse-earnings-and-payouts` | nurse | earnings, weekly payout run |
| 19 | `messaging-tickets` | all | threads, `is_internal` boundary |
| 20 | `notifications` | all | bell, day-grouped list |
| 21 | `admin-backoffice` | admin | config, holidays, audit, RBAC, queues |
| 22 | `partner-center` | partner | the separately-scoped portal |
| 23 | `account-and-settings` | all | profile, theme/language (settings-only since iteration 1) |
---
## Steps
### 1. Write `testing-setup.md` — and **actually boot it**
Do not transcribe RUNBOOK.md. Follow it, note where it is wrong, and write what really happens.
Must cover:
- Prerequisites and the two-terminal run (`dotnet run` + `npm run dev`).
- **Configuration** — the current, correct story: config in `appsettings.*.json` and `.env.*`;
`user-secrets` **is not used and is not read** (`<UserSecretsId>` was removed). The four crypto
values still matter and must match whatever the target DB was encrypted under — booting with
different `Seams:FieldEncryption` keys makes every phone lookup miss and every PII read throw
`Padding is invalid`. Say where they live now.
- **Which database.** The dev config has pointed at a *remote* SQL Server. Confirm the current
target, and give the local-DB alternative.
- **Test accounts** — read them out of the seeder, not the old doc. Note the phone-OTP admins
(`09120000020` super_admin, `09120000021` finance) and what each demo account is set up to
demonstrate.
- **Getting the OTP** — server console (`MOCK SMS — OTP code …`), `GET /api/v1/dev/last_otp/{phone}`,
or the Telegram relay. Note the limits: 120 s resend window, 5 wrong attempts, 60 s validity, and
that the OTP endpoints are IP rate-limited (scripted logins will 429).
- **The seeded world** — what `DemoLifecycleSeeder` builds (the 8-booking world), and the fact that
**time-relative scenarios age out** and need a reseed.
- **Reset** — the drop-and-reseed procedure, verified.
- **Troubleshooting** — the failures you actually hit while doing the above.
### 2. Build the mock-vs-real map — from code
The single most important input to every flow file, and the thing most likely to be stale in the
docs. For each of the 22 client `services/` domains, read the code:
- Is the mock flag on or off?
- If real, does the whole domain hit the API, or only part of it?
- Server-side: which seams are mocked (SMS, payment gateway, BNPL provider, object storage,
geocoder, search) and which have a real adapter available behind config (refinement-phase-8)?
Cross-check `mocks-registry.md` and **correct it** — Phase 0 already flagged that the registry has
had stale rows before. Put the result in `docs/flows/index.md` as a column, and in each flow file as
a header block.
Known suspects from the hardening audit (2026-07-16, never ticked off — verify each against current
code, do not assume): verification fully mocked while catalog/search are real; the refunds mock
reading a retired store; the BNPL wizard on a disconnected store; nurse earnings fabricated despite
live endpoints; `patientRecords` id-type mismatch.
### 3. Write one file per flow
Template — keep each under ~200 lines:
```markdown
# Flow — <name>
> Last verified: <date> against <commit>
**Actor(s):** · **Status:** built | partial | mocked | not started
**Business source:** product/business/NN-….md
## What it does
Two or three sentences. The user's intent, not the implementation.
## Screens
| Step | Route | Component/notes |
## API
| Call | Endpoint | Notes |
Link to docs/integration/domains/<domain>.md — don't restate shapes here.
## Rules that must hold
The load-bearing numbers and invariants, with their product/ source.
(e.g. commission 0.15, VAT 0.10 on commission only; forward-only status;
escrow released after confirmed check-out; whole-city = districtId NULL)
## How to test
1. Log in as <account> (see testing-setup.md)
2.
**Expect:** …
## Known gaps
- [BL-xxx] … (filled by Phase 4; leave a plain list here for now)
```
**Verification standard per flow.** For each, do at least the cheap check — trace the route → service
→ endpoint → handler and confirm the chain is real. For the money flows (1115, 18) and auth (1), do
the expensive check too: run the walkthrough against a booted app. If you cannot run it, mark the
flow's status `UNVERIFIED` in the index and say why — an honest gap beats a confident guess.
### 4. Write `index.md`
The status table, and nothing else of substance:
| Flow | Actor | Status | Client | Server | Verified | File |
| --- | --- | --- | --- | --- | --- | --- |
| booking-request | customer + nurse | built | real | real | 2026-…-… | [link] |
Statuses: `built` (end-to-end real) · `partial` (real but with gaps) · `mocked` (UI real, data fake) ·
`not started`. `Client`/`Server` columns say `real`/`mock` independently — a flow can have a real UI
on a mocked service, which is exactly the trap the hardening audit found.
---
## Verification
- [ ] `testing-setup.md` was executed, not transcribed — the agent booted the API and the client.
- [ ] Every flow file has a status backed by a code trace, or an explicit `UNVERIFIED` mark.
- [ ] The mock-vs-real map was derived from code and disagreements with `mocks-registry.md` are logged.
- [ ] No flow file restates an API shape that `docs/integration/` already owns.
- [ ] The index accounts for all 22 client service domains and all 14 business areas — anything with
no flow is either intentional or a Phase 4 backlog item.
- [ ] Every flow file carries a `Last verified:` line.
## Definition of done
You can hand someone `docs/flows/testing-setup.md` and `docs/flows/index.md` and they can test the
product without asking you a single question — and without hitting an instruction that no longer works.
## Handoff
_(filled in per slice — which flows are done, which are `UNVERIFIED` and why, and the running list of
gaps found, which is Phase 4's primary input)_
+158
View File
@@ -0,0 +1,158 @@
# Phase 4 — Backlog reconciliation
**Depends on:** Phase 0, Phase 3 (and consumes Phase 2's drift list) · **Blocks:** Phase 5 · **Size:** 12 sessions
## Goal
Turn **five unreconciled ledgers plus 50+ scattered "follow-ups" sections** into one triaged backlog
where every open item is real, deduplicated, and carries its origin.
This is the "remaining works from plans, dev, refine and ui phases should be well documented"
deliverable. Today the honest answer to "what's left?" is *nobody knows* — the hardening ledger has
18 items with **zero ticked**, written before three more chains ran on top of it.
---
## Inputs
| Source | Volume | Note |
| --- | --- | --- |
| [dev/post-phase/hardening/issues.md](../../dev/post-phase/hardening/issues.md) | 18 items, **all unticked** | Written 2026-07-16. UI phases 013 and the deploy commits ran after. **Every item must be re-verified against current code.** |
| [dev/shared-working-context/frontend/requests/for-backend.md](../../dev/shared-working-context/frontend/requests/for-backend.md) | 67 REQs, 1,115 lines | Most delivered in refinement-phase-3; some `partially delivered`; REQ-066/067 blocked on a privacy sign-off |
| `dev/shared-working-context/reports/*.md` | 50 files | Each has a "Follow-ups for later phases" / "Deferred" section |
| `dev/shared-working-context/backend/STATUS.md` + `frontend/STATUS.md` | 86 KB | Deferrals recorded with explicit **pull-triggers** (e.g. Elasticsearch `INurseSearch`, SMS/push dispatcher, analytics, holiday feed, 8 unbuilt product tables) |
| [dev/manual-testing/iteration-1/](../../dev/manual-testing/iteration-1/) + [iteration-2/](../../dev/manual-testing/iteration-2/) | 2 raw notes + screenshots | Your own most recent feedback. Some applied in `baa3cc6` / `e6a8f93`; **which parts landed is unknown** |
| [product/notes/open-questions.md](../../product/notes/open-questions.md) | build backlog + research questions | |
| Phase 2's output | drift list | `phantom` and `drifted` endpoints |
| Phase 3's output | gap list | everything found while verifying flows |
| Root [CLAUDE.md](../../CLAUDE.md) §6 | | credential rotation before real users — a real pre-launch item |
## Outputs
```
docs/status/
index.md where the project is, in one page
implemented.md the product/business overlay: 14 areas -> build state
backlog.md BL-### — every open item, triaged
backlog-closed.md items confirmed done, with what closed them
decisions.md the distilled decision log
```
---
## Steps
### 1. Harvest everything into one raw list
One row per candidate item, with its **origin id** (`H-01`, `REQ-042`, `ui-phase-9 follow-up`,
`iteration-2`, `deferral: elasticsearch`, `phase-3 finding`, `phase-2 drift`). Expect 150250 raw
rows before dedup. Do not filter while harvesting.
### 2. Verify each item's current state
This is the phase's real work, and the reason it comes after Phase 3.
- **The 18 hardening items**: re-locate each in the code. The file itself warns that line numbers
drifted. Several are likely fixed by the UI chain — e.g. **H-01** (the auth gate never running,
traced to a stray `pnpm-lock.yaml` and the `middleware.ts``proxy.ts` question) overlaps directly
with what ui-phase-13 did to the root route and matcher. Verify; don't assume either way.
- **The 67 REQs**: classify `delivered` / `partially delivered — what's left` / `open` / `obsolete`.
The ones marked `partially delivered in refinement-phase-3` need the residue spelled out. Once
Phase 2 folded delivered REQs into the domain docs, a delivered REQ is *closed*, not an item.
- **Iterations 1 & 2**: walk each bullet against the code. The clearest testable one:
*"replace all forms with more than 1 field with react-hook-form"* — 28 files import it today;
find the forms that still use raw state and file the residue.
- **Deferrals**: these are not bugs; they are *decisions with pull-triggers*. Keep them as
`deferred`, carry the trigger, and hand them to Phase 5's `roadmap/deferred.md`.
### 3. Dedup and assign `BL-###`
Many items appear in three ledgers. Merge them into one `BL-###` carrying **all** origin ids.
Old ids stay greppable — never rewrite the archived sources.
### 4. Write `backlog.md`
```markdown
> Last verified: <date> against <commit>
| ID | Area | Sev | Item | Origin | Status | Blocks |
|----|------|-----|------|--------|--------|--------|
| BL-001 | client | blocker | … | H-01 | open | flow: auth-login-otp |
```
- **Area**: `client` · `server` · `contract` · `ops` · `product` · `docs`
- **Sev**: `blocker` (product is wrong or unusable) · `major` · `minor` · `deferred`
- **Status**: `open` · `in-progress` · `blocked (on what)` · `deferred (trigger)`
- **Blocks**: the flow(s) from `docs/flows/` it degrades — this is what connects the backlog to
something a user can feel
Sort by severity, then area. Anything closed goes to `backlog-closed.md` with *what* closed it
(commit or phase), so the count of open items is honest at a glance.
### 5. Write `implemented.md` — the product overlay
The agreed alternative to touching `product/`. One row per business area:
| Business area | Doc | State | Flows | Gaps |
| --- | --- | --- | --- | --- |
| 08 Payments & escrow | [product/business/08-…](../../product/business/08-payments-and-escrow.md) | built | checkout-and-payment, cancellation-and-refunds | BL-023 |
States: `built` · `partial` · `mocked` · `not started` · `deferred`. Add the reverse link from each
`docs/flows/*.md` back to its business area. This gives the two-way mapping without editing a single
`product/` file.
Do the same, briefly, for the data model: the STATUS logs mention **8 product tables that were never
built** — name them here rather than leaving them in a status log.
### 6. Write `decisions.md`
The distilled decision log — the highest-value thing buried in 3 MB of reports. Extract every
**non-obvious decision with a reason** and write it as a short ADR-style entry: *what was decided,
when, why, and where it binds*. Candidates already visible in the STATUS logs and memory:
- Commission 0.15 / VAT 0.10 **on commission only** (the canonical fee model, refinement-phase-3)
- `district_id = NULL` means whole-city — in both directions
- Verification `status` is the source of truth; `is_verified` is a guarded flip
- Booking status is forward-only; the three-amount split has a CHECK constraint
- The two-stage clinical-disclosure gate; EVV is **advisory**, never a block
- Webhook idempotency is upsert-first; ledger postings must balance
- Reviews recompute from source; nurse care records are append-only
- `is_internal` ticket messages never appear in user-facing types
- One payout per booking (UNIQUE); whole-clawback greedy netting
- Escrow released after a confirmed check-out; weekly payout generation automatic, processing manual
- Config in files, not a secret store — deliberate pre-launch trade
- Error state is never an empty state (the client convention)
- Root `/` forks by auth via middleware **rewrite**, never redirect
Each entry: 36 lines. Where a decision is already stated in `product/`, link rather than restate —
`product/` wins for business rules; `decisions.md` is for *engineering* decisions and for business
decisions that were made **during** the build and never made it back into `product/`. Anything in the
second category should also get a note in `product/notes/` so the business truth stays complete.
### 7. Write `index.md`
One page: how many flows are built/partial/mocked, how many backlog items are open by severity, the
top five things standing between here and a usable product, and links to everything. This is the page
you open when you come back after two weeks away.
---
## Verification
- [ ] Every one of the 18 hardening items has a verdict backed by a code check — none carried over
unexamined.
- [ ] Every REQ-001…067 is classified; the count of `open` is explicit.
- [ ] Every "Follow-ups for later phases" section across the 50 reports was read and harvested.
- [ ] Both iteration notes are fully accounted for, bullet by bullet.
- [ ] No `BL-###` duplicates another; every one names its origin id(s).
- [ ] Every `blocker` names the flow it blocks.
- [ ] `implemented.md` covers all 14 business areas and all 23 flows.
## Definition of done
"What's left?" is answered by one file, and every number in it was checked rather than inherited.
## Handoff
_(filled in by the agent that runs this phase — item counts by severity, and anything that turned out
to be worse than the old ledgers claimed)_
+121
View File
@@ -0,0 +1,121 @@
# Phase 5 — Roadmap & technical requirements
**Depends on:** Phase 4 · **Size:** one session
## Goal
Answer "what next?" — recording every already-decided future item **and** proposing a sequenced
order, with the technical prerequisites each step needs. The ordering is a proposal you can overrule;
the recording is not negotiable.
---
## Inputs
- `docs/status/backlog.md` and `implemented.md` — Phase 4's output, the ground truth
- `docs/flows/index.md` — what's mocked vs built
- The **deferrals with pull-triggers** from the STATUS logs: Elasticsearch `INurseSearch`, real
SMS/push `INotificationDispatcher`, the analytics pipeline, the holiday feed, **8 unbuilt product
tables**, and the refinement-phase-9 items 9.79.11
- [product/notes/future-ideas.md](../../product/notes/future-ideas.md) and
[open-questions.md](../../product/notes/open-questions.md) — including PWA/Workbox caching
- ui-phase-13's follow-ups: **tier (c)** — guest search + public nurse profiles (REQ-066/067), blocked
on a backend phase **and an explicit privacy sign-off on the nurse-profile field list**; the OG
image Persian variant
- Root [CLAUDE.md](../../CLAUDE.md) §6 + [DEPLOY.md](../../DEPLOY.md) "Going to Production" — the
credential-rotation obligation
- `product/business/*` — anything specified but never built
- `product/research/*` — go-to-market, legal landscape (informs sequencing, not scope)
## Outputs
```
docs/roadmap/
index.md the proposed sequence + the reasoning, in one page
next-up.md the next 3-5 units of work, each spec'd enough to start
deferred.md recorded, with the trigger that pulls each one forward
tech-debt.md what must be paid before scale, and what it costs to defer
pre-launch.md the hard gate before real users touch this
```
---
## Steps
### 1. `pre-launch.md` first — it's the one with a deadline
Everything that must be true before a real user with real money uses the platform. Known already:
- **Rotate the committed credentials** and move the secret half out of git. The repo currently
contains live credentials by deliberate pre-launch decision. Note the exception: `Seams:FieldEncryption:Key`
/ `:HashKey` decrypt existing PII and derive the phone-lookup hash — rotating those requires a data
migration, not a config edit. Spell out that migration as its own item.
- **Real external rails** — which seams are still mocked in production config (payment gateway, SMS,
BNPL provider, object storage). Phase 3's mock-vs-real map is the input; this is the list of seams
that must flip.
- **Running as Development in production** — `DEPLOY.md` documents this as a deliberate trade.
State what it implies (gRPC reflection, dev endpoints such as `/dev/last_otp`, seeding behaviour,
log verbosity) and what must change.
- Anything Phase 4 marked `blocker`.
- Legal/tax items from `product/business/13-tax-invoicing-and-legal.md` that are code-side.
Each item: what, why it blocks launch, roughly what it takes.
### 2. `deferred.md` — record faithfully, don't re-decide
One row per deferral: **item · why deferred · the pull-trigger (the condition that makes it
necessary) · rough size · where it was decided**. The pull-triggers already exist in the STATUS logs
— preserve them verbatim in substance. A deferral with a trigger is a decision; a deferral without
one is just a forgotten task, so any you find without a trigger, give one.
Include the 8 unbuilt product tables, named individually.
### 3. `tech-debt.md`
Debt is different from backlog: it doesn't block a flow, it raises the cost of everything after it.
Candidates to assess: the single-instance in-process scheduler; search without Elasticsearch; the
absence of E2E tests over the money paths; the remaining raw-state forms; the Windows-generated
client lockfile wrinkle in `DEPLOY.md`; test coverage asymmetry between the two projects.
For each: what it costs now, what it costs at 10× usage, and the trigger to pay it.
### 4. `next-up.md` — the opinionated part
Three to five units of work, each with: goal, why now, what it unblocks, technical prerequisites,
affected flows, rough size, and the backlog ids it closes. Enough that a fresh agent session could
start one without re-deriving the context.
**Sequencing principle to apply, and state explicitly in `index.md`:** the product's own promise is
trust-first and money-holding. So the order is (1) anything that makes a *money or trust* flow lie to
a user, (2) anything that makes a built flow unusable, (3) anything that makes a mocked flow real,
(4) new surface area. Tier (c) guest search is new surface — it ranks below making the authenticated
flows honest, regardless of how visible it is.
Where a proposed item conflicts with something in `product/`, say so and defer to `product/`.
### 5. `index.md`
The one-page sequence: a table of the proposed order with rationale per step, plus the standing
answer to "what are we not doing, and why" (a pointer to `deferred.md`). Mark clearly which parts are
**recorded decisions** and which are **this document's proposal** — the reader must be able to tell
your judgement from the project's.
---
## Verification
- [ ] Every `deferred` item in `docs/status/backlog.md` appears in `deferred.md` with a trigger.
- [ ] Every recorded deferral from the STATUS logs is present, including the 8 unbuilt tables.
- [ ] `pre-launch.md` covers credential rotation, every still-mocked production seam, and the
Development-in-production trade.
- [ ] Every `next-up.md` item names the backlog ids it closes and the flows it affects.
- [ ] Proposal is visibly distinguished from record.
## Definition of done
You can pick the next piece of work in five minutes, and you can explain to someone else why it's
that piece and not another.
## Handoff
_(filled in by the agent that runs this phase)_
+124
View File
@@ -0,0 +1,124 @@
# Phase 6 — Archive & prune
**Depends on:** Phases 15 all complete · **Size:** one session
## Goal
Move the 3 MB build-chain history out of the way — **after**, and only after, every live fact in it
has been extracted. Then fix every link that pointed into it.
This phase is mechanical. Its only real risk is running it early, so it has a hard gate.
---
## Gate — do not start until all of these are true
- [ ] Phase 0's `docs/_plan/inventory.md` has a disposition for every file, with no blanks.
- [ ] Phases 1, 2, 3, 4, 5 are marked complete in [`README.md`](README.md)'s progress table.
- [ ] `docs/_plan/open-contradictions.md` has no unresolved rows (or each remaining one is explicitly
accepted with a reason).
- [ ] Every inventory row marked `live facts: yes` has been extracted by its owner phase — check them
off one by one, not in bulk.
If any box is unticked, stop and report which phase is incomplete.
---
## Inputs
- `docs/_plan/inventory.md` — the authority on what moves where
- The whole `dev/` tree
## Outputs
```
archive/
README.md "Historical record. Do NOT treat as instructions."
build-chain/
phases/ was dev/phases (32 executed prompt files + _shared)
working-context/ was dev/shared-working-context (STATUS, handoffs, 50 reports)
contracts/ was dev/contracts (superseded by docs/integration/)
post-phase/ was dev/post-phase (hardening, refinement, server, ui + audits)
manual-testing/ was dev/manual-testing (notes + screenshots)
docs/_plan/ moved to archive/clarify-chain/ at the very end
```
---
## Steps
### 1. Write `archive/README.md` before moving anything
It must say, unambiguously and at the top:
> **This folder is a historical record of how Balinyaar was built. It is not instruction.**
> Nothing here describes current behaviour, current rules, or current plans. Do not follow a
> procedure from this folder. Current truth lives in [`docs/`](../docs/README.md) and
> [`product/`](../product/README.md).
Then: what each subfolder was, the date range it covers, and a table mapping each archived area to
the `docs/` file that superseded it. Someone finding `archive/post-phase/hardening/issues.md` must be
able to see in one step that it became `docs/status/backlog.md`.
### 2. Move, with `git mv`
Preserve history. Move whole directories rather than file-by-file. Keep the screenshots in
`manual-testing/` — they are the only visual record of the pre-overhaul UI.
Delete outright (per Phase 0's inventory): `Prompt.md` (0 bytes), `temp/swagger.json` (stale
duplicate), and the `graphify-out/GRAPH_REPORT.md` files if Phase 0 marked them generated-and-unused.
### 3. Fix inbound links
Every doc outside `archive/` that links into `dev/` must be repointed at its `docs/` successor — not
at the archived copy. Sweep at minimum: root `CLAUDE.md`, `client/CLAUDE.md`, `server/CLAUDE.md`,
`DEPLOY.md`, all three `AGENTS.md`, `product/README.md`, `product/index.md`,
`telegram-otp-bot/README.md`, `.githooks/README.md`, and everything under `docs/`.
Mechanical check:
```bash
grep -rn "](\.\./dev/\|](dev/\|(dev/" --include="*.md" . | grep -v "^./archive/"
```
should return nothing. Do the same sweep for `product/*.html` — the generated view has its own
cross-links, and if any point at `dev/`, fix the **`.md` source** and regenerate
(`cd product && node build-docs.mjs`), never the HTML.
### 4. Update the architecture map
Root `CLAUDE.md`'s "Repository layout" table is canonical for repo structure (its own working
agreement 7). Update it: `dev/` is gone, `docs/` and `archive/` are in, and the sentence describing
`dev/` as the build plan is replaced.
### 5. Update the memory index
`MEMORY.md` has ~50 entries, most of them phase memories whose file paths now point into `archive/`.
Add a single note at its top recording that the build-chain docs moved and where current truth lives,
so a future session doesn't chase dead paths. Don't rewrite 50 memory files.
### 6. Retire this plan
Move `docs/_plan/``archive/clarify-chain/`, and leave one line in `docs/README.md` noting when the
cleanup ran and where its plan lives.
---
## Verification
- [ ] `dev/` no longer exists.
- [ ] The link grep above returns nothing outside `archive/`.
- [ ] `cd product && node build-docs.mjs` runs clean and the regenerated HTML has no `dev/` links.
- [ ] `cd client && npm run check` passes; `cd server && dotnet build Baya.sln` passes — confirming
nothing moved was referenced by tooling.
- [ ] `git status` shows moves as renames, not delete+add.
- [ ] `archive/README.md` maps every archived area to its successor.
## Definition of done
Nothing in the repo tells you to do something that was true three weeks ago, and nothing that was
learned in those three weeks is gone.
## Handoff
_(filled in by the agent that runs this phase)_
+125
View File
@@ -0,0 +1,125 @@
# Phase 7 — Skills & anti-drift guardrails
**Depends on:** Phase 1 (rules) and Phase 3 (reality) · **Can run before or after Phase 6** · **Size:** one session
## Goal
Two things:
1. **Skills that match reality.** The one existing skill was written before the UI overhaul; parts of
it describe an app that no longer exists.
2. **Guardrails so this doesn't happen again** — the reason the cleanup was needed at all.
**The division of labour, restated:** a *rule* is a constraint that must never break → `docs/rules/`.
A *skill* is a procedure for a recurring task → `.claude/skills/`. If you're writing "always do X",
it's a rule. If you're writing "to do X: first…, then…", it's a skill.
---
## Inputs
- [.claude/skills/frontend-designer/SKILL.md](../../.claude/skills/frontend-designer/SKILL.md) — 21 KB,
10 sections (brand, tokens, typography, component library, layout, icons, hard rules, workflow,
anti-patterns, Figma)
- `docs/rules/` — Phase 1's output, so the skill can stop restating rules and link instead
- `docs/flows/` — Phase 3's output, especially `testing-setup.md` and the mock-vs-real map
- The client code as it is **after** iterations 12
- [.githooks/pre-commit](../../.githooks/pre-commit) (3.4 KB) + [.githooks/README.md](../../.githooks/README.md)
- `client/package.json` scripts (`check`, `lint:copy`, `test:ci`) and the server's build/test commands
## Outputs
```
.claude/skills/
frontend-designer/SKILL.md rewritten against current reality
backend-feature/SKILL.md NEW — adding a feature to the .NET server
flow-testing/SKILL.md NEW — boot, seed, and walk a flow end-to-end
```
Updated: `.githooks/pre-commit`, `docs/rules/documentation.md` (Phase 1 wrote the convention; this
phase adds the enforcement note), and status-doc headers.
---
## Steps
### 1. Rewrite `frontend-designer`
Reality-check every section against the code, then rewrite. Specifically at risk (confirm each):
- **§5 Layout & page shells** — the app is now **mobile-scoped**: a max-width container so the shell
never stretches on desktop, and the nurse side moved from a drawer to a **bottom navigation** with
grouped root pages that summarize their domain. Any desktop-sidebar guidance is obsolete except
where admin still uses one.
- **§6 Icons** — the icon set was replaced wholesale in iteration 1. The old mapping guidance is dead.
- **§2 Design tokens / §3 Typography** — check against the current theme, including the reduced
Paper border-radius.
- **§7 Non-negotiable rules** — most of these are now **rules**, not skill content. Cut them down to
a link into `docs/rules/client/`, keeping only what's design-specific.
- **§1 Brand / §4 Component library** — verify the `App*` inventory against `client/src/components/`
(which currently holds ~40 feature components in addition to the shared kit).
- Add what's missing: **forms are react-hook-form** now, so the "build a form" procedure changes; and
theme/language switches live **only in settings**, not in top bars.
Target: shorter than the current 21 KB, because the rules half moves out.
### 2. Write `backend-feature`
The procedure for adding a feature to the server, derived from how the 199 handlers are actually
shaped: where the command/query goes, the handler and validator, the DTO, the controller action, the
EF configuration and migration, the tests, and the doc updates it must trigger
(`docs/integration/domains/<domain>.md` + the OpenAPI snapshot). Link to `docs/rules/server/` for
constraints; keep the skill to the sequence.
### 3. Write `flow-testing`
The procedure a session follows to actually exercise a flow: boot both sides per
`docs/flows/testing-setup.md`, get an OTP, log in as the right seeded account, walk the flow, and —
importantly — **check the mock-vs-real map first** so a "working" flow isn't just a mock answering.
Include the reseed step for when the time-relative scenarios age out.
### 4. Extend the pre-commit hook
Read the existing hook first and match its style. Add **warnings, not blocks** — a hook that blocks
commits gets bypassed, and then it protects nothing.
Warn when:
- files under `server/src/**/Controllers/**` or `**/Handlers/**` changed but nothing under
`docs/integration/` did → *"API surface changed — update `docs/integration/domains/…` and refresh
the OpenAPI snapshot."*
- `client/src/services/**` changed but no `docs/integration/` or `docs/flows/` change →
*"a service domain changed — does a flow doc need updating?"*
- a file under `docs/status/` or `docs/flows/` is committed with a `Last verified:` date more than
~30 days old → *"this doc claims to be verified as of <date>."*
- root `CLAUDE.md`, `client/CLAUDE.md`, or `server/CLAUDE.md` exceeds its line budget →
*"the rulebook is regrowing; move reference material to `docs/rules/`."*
Document each warning in `.githooks/README.md` and in `docs/rules/documentation.md`.
### 5. Stamp the freshness convention
Ensure every file under `docs/status/` and `docs/flows/` carries
`> Last verified: <date> against <commit>` as its second line, and that
`docs/rules/documentation.md` names exactly which files must carry it and who updates it.
---
## Verification
- [ ] No sentence in `frontend-designer` describes pre-iteration-1/2 behaviour — spot-check the
layout, icon, and form sections against real components.
- [ ] No skill restates a rule that `docs/rules/` owns; each links instead.
- [ ] The three skills' trigger descriptions don't overlap (a task should match exactly one).
- [ ] The pre-commit hook runs and its new warnings fire on a deliberate test commit — and **never**
block one.
- [ ] Every `docs/status/` and `docs/flows/` file has a `Last verified:` line.
## Definition of done
The skills describe the app that exists, and the next time someone changes an endpoint without
touching a doc, something says so.
## Handoff
_(filled in by the agent that runs this phase)_