13 KiB
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 | 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 | 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/ + 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 | 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 §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.yamland themiddleware.ts→proxy.tsquestion) 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 markedpartially delivered in refinement-phase-3need 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'sroadmap/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
> 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-… | 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 = NULLmeans whole-city — in both directions- Verification
statusis the source of truth;is_verifiedis 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_internalticket 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
- 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.) - Every REQ-001…067 is classified; the count of
openis explicit. (28 delivered, 1 obsolete, 6 deferred, 2 partially-delivered, 30 open.) - 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.)
- 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.)
- 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 oneBL-001.) - Every
blockernames the flow it blocks. implemented.mdcovers 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 inbacklog-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 asBL-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.