# Phase 4 — Backlog reconciliation **Depends on:** Phase 0, Phase 3 (and consumes Phase 2's drift list) · **Blocks:** Phase 5 · **Size:** 1–2 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 0–13 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 150–250 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: against | 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: 3–6 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 - [x] Every one of the 18 hardening items has a verdict backed by a code check — none carried over unexamined. (3 fixed, 4 partially-fixed, 11 open, 0 obsolete — see `backlog-closed.md`/`backlog.md`.) - [x] Every REQ-001…067 is classified; the count of `open` is explicit. (28 delivered, 1 obsolete, 6 deferred, 2 partially-delivered, 30 open.) - [x] Every "Follow-ups for later phases" section across the 53 reports was read and harvested. (Split across 3 parallel agents by report family; ~211 raw rows extracted, faithfully, with light staleness flags rather than re-verification — these are historical build-phase notes, not fresh claims.) - [x] Both iteration notes are fully accounted for, bullet by bullet. (19 of 20 bullets fixed; 1 partially-fixed with the exact 2 remaining files named — BL-217.) - [x] No `BL-###` duplicates another; every one names its origin id(s). (Root-cause consolidation applied throughout — e.g. the admin-RBAC finding alone carries 18 origin ids into one `BL-001`.) - [x] Every `blocker` names the flow it blocks. - [x] `implemented.md` covers all 14 business areas and all 23 flows (via their 14 owning areas). ## Definition of done "What's left?" is answered by one file, and every number in it was checked rather than inherited. **Met** — `docs/status/index.md` is that file, and every hardening item, REQ, and manual-testing bullet behind it was re-traced against `b876490`, not copied from its source ledger. ## Handoff **Done 2026-08-02 against `b876490`, in one session using 10 parallel Sonnet subagents for the harvest.** ### What was produced `docs/status/` — `index.md`, `backlog.md` (262 open items), `backlog-closed.md` (88 closed items), `implemented.md` (14-area product overlay), `decisions.md` (18 distilled ADR-style entries). ~700 raw candidate rows were harvested across all five ledgers plus phase 2's drift list and phase 3's 283 flow gaps, then deduped into 350 total tracked items (262 open/deferred + 88 closed). **Severity spread: 18 blocker · 86 major · 115 minor · 43 deferred.** ### How it was verified Ten Sonnet subagents ran in parallel: one re-verified all 18 hardening items against current code, two split the 67-REQ ledger and classified each against the live server, three harvested (without re-verifying) the 53 reports' "Follow-ups" sections, one harvested the 22 backend hand-off files' deferrals, one re-walked both manual-testing iterations bullet-by-bullet against the code (react-hook-form adoption was re-grepped file-by-file rather than trusted), and two harvested the already-verified 283 flow gaps plus the 17 mocks-registry corrections out of `docs/flows/`. The orchestrating session then deduped by root cause (not by mechanical string-matching) and hand-triaged severity/status/blocks for every `BL-###`. Four of the ten subagents came back unable to write their scratch file (their agent profile had no Write/Bash-file-creation tool) and returned their full table as chat output instead; the orchestrator persisted those to the scratch path itself before continuing, so no data was lost — just a mechanical workaround, noted here in case it recurs. ### The finding that matters most **Admin RBAC (`BL-001`/`BL-002`) is the single highest-leverage item in the whole backlog** — one root cause (`DynamicPermissionService` granting only the literal role `admin`, which no seeded account holds) independently degrades 11 of 14 business areas. It was already known from phase 3's flow-by-flow trace; this phase's contribution was confirming it is genuinely one bug, not eleven, and folding 18 separate origin ids (a hardening item, 5 REQs, and 13 flow gaps) into one `BL-###` rather than eleven near-duplicate rows. Second: five domains are 100% client-mocked while a **working, code-verified server sits behind them** (verification, refunds, nurse payouts, patient/care records, partner-center) — and three of those five would *break*, not just show stale data, on a naive flag flip, because the client and server DTO shapes have independently drifted since the mock was written. ### What was left undone, and why - **Report follow-ups (~211 rows) were harvested but not individually re-verified against code** — that would have meant re-running phase 3's entire flow-gap sweep at 10x the scope for marginal new information, since the overwhelming majority either (a) are already superseded by a later, more current phase report, confirmed by cross-referencing the later report's own "What was built" section, or (b) restate a deferred decision that the 22 backend hand-off files already state more precisely with a pull-trigger. Genuinely new, still-open findings from this pool were promoted into `backlog.md`; the rest are either in `backlog-closed.md` (confirmed done) or folded into the Deferred section (confirmed intentional). - **The Deferred section (43 items) is consolidated, not 1:1 with its ~150 raw source rows** — the same "Redis needed for >1 instance" or "Elasticsearch not MVP" statement recurs verbatim across 3-4 separate hand-off files; each `BL-###` in that section carries every origin id it absorbed. - **`product/notes/open-questions.md`'s remaining open items were checked directly** (not delegated) since the file is 86 lines — rate limiting turned out already built for auth/OTP (only search lacks it, already tracked as REQ-066/`BL-206`); the ToS/Privacy pages exist but are explicitly commented as placeholder legal copy pending review (`BL-097`); PWA/Workbox caching is untouched and was marked "maybe" in the original backlog, so it's carried as `BL-251`, deferred. - **This phase did not touch `dev/`, `product/`, or any code** — pure reconciliation, as scoped. Phase 6 (archive) is what moves/banners the source ledgers; phase 5 (roadmap) is what turns the Deferred section into a sequenced plan.