Both production-handoff gates are closed pending stakeholder ratification. The mockup passed Cycle 20's audit (build · lint · typecheck all clean). Next step is Kamran sending two emails so the ~7-day default-ship windows can start.
v2.1 Phase D2 days ago
21-cycle loopjust shipped
Gate 1 · Schema handoverdrafted
Gate 2 · Billing passresolved + drafted
Send emailsKamran
Defaults ship~2026-05-25
Production handoffWeek 0
Closed — landed in repo, drafted in docs, or ready for productionNow — needs Kamran's actionNext — automatic once defaults ship
2What landed in 21 cycles
Each row = one cycle, one theme, one shipped outcome.
The plan estimated Bucket A (Claude resolves, no stakeholder gate) at ~85% of total work. After a 3-sweep corrective investigation, actual share lands at ~85-90% — within the plan's range. Details below the bar.
Work item distribution · 33 items totalBucket A actual: ~85-90% · plan estimate: 85%
15 · A direct
5 · B→A
4 · D
3 · Lejla
3 · Usama
3 · settled
Bucket A — Claude resolved, planned (15/15 = 100% of plan)Bucket B → A — internally resolved (5 items: B1, B2, B3 via Cycle 2; B5, B8 via Cycle 4)Bucket D — Kamran orchestration confirmed (D1, D2, D3, D6)Lejla — 3 product asks (drafted)Usama — 3 engineering asks (drafted)Already settled (3) — informationals, not real asks
One item is BLOCKING: Usama's SF9 legacy coupon shape pick gates the production migration — no default ships. Every other ask carries a recommended default and ships on its window.
Honest accounting: A 3-sweep corrective investigation found that 3 items in the original "9 asks" count were already-settled informationals (OQ-SF14-1, Zoom regex, R4-Q1) — they appear in source artifacts but don't need stakeholder input. The real surface is 6 asks (1 BLOCKING + 5 ratifications-with-defaults) + 3 informationals, and Bucket A actually landed at ~85-90% of work — within the plan's headline range.
5Open questions — to discuss in the meeting
5 standalone questions for the meeting — Lejla (2) + Usama (3, one BLOCKING). Each has a recommended default; the plan is to walk through them together rather than route as email. The full ask cards live on their own page so you can share them without the 21-cycle retrospective context:
Send the billing-ratification email. Audience split inside the doc — Lejla gets B1 + SF5, Usama gets B2a + SF9 + B5. Send date sets the ~7-day default-ship window for 5 items.
→ 2026-05-17-billing-stakeholder-ratification.md
Send the ADR-007 sign-off email to Lejla + Usama. The 2026-05-25 date in the body is firm — send before EOD today to preserve the 7-day window.
→ 2026-05-18-ADR-007-signoff-email.md
Deliver the 3 Granjur handover packets to Usama / Granjur team. Recommended bundling: one email linking all 3 packets + ADR-018 ratification + the 6 sign-off asks.
→ Packets at docs/admin-spec-v2-1/2026-05-18-phase-d-handover-{teacher-comm, reporting-admin, cross-cutting}.md
After defaults ship (~2026-05-25) — apply confirmed answers to the billing resolutions doc + v2 spec, and land the gated follow-up mockup PR (B1, B8, SF1, SF2, SF4).
AAppendix — what this work actually looks like on disk
Up to here, this page has used a lot of names — "ADR-014", "Phase D handover Packet 2", "pre-flight checklist". The appendix grounds those names in concrete files and shows one decision moving end-to-end across every layer it touches. If you've ever wondered what is the actual unified view, where do the files live, what do they contain — this section answers that.
A.1Where everything lives
A tour of the directories that hold the loop's output. Each line shows status by color:
StatusGreen = created in loopYellow = edited in loopGrey = pre-existing, untouched
The work organizes into 5 layers. Raw stakeholder input flows into analysis docs, which feed decision records, which produce two parallel outputs (schema + mockup), all wrapped in coordination artifacts that move things to production. Each layer has a different shelf life and a different audience.
Layer 1Raw input — what stakeholders said
Transcripts, mockup-run reports, walkthrough notes. Long-form prose. Owned by whoever was in the room.
One markdown file per decision. Format: Context / Decision / Schema implications / Rationale / Implementation notes / Open questions / Fallback / Cross-references. Outlives the loop. Production engineers read these before writing code.
decisions/ADR-007-tag-as-saved-filter.md through ADR-018-family-plans-coupon-strategy.md — 11 ADRs landed in the loop. 8-25KB each.
↓ shapes both ↓
Layer 4aSchema — what production builds
Canonical DDL spec for the production Postgres database. Per-table blocks + Migration table + Indexes Summary + Source line. Granjur reads this to write Drizzle migrations.
docs/production-architecture-research/30-design/01-schema.md — ~2900 lines after loop edits. Edited by every TYPE-1 ADR cycle.
Layer 4bMockup — what to see
React + TypeScript + shadcn/ui front-end mockup. Demonstrates that the decisions render coherently. Live at quranflow-admin.pages.dev/v2.
Layer 5Coordination — moving decisions to production
Delivery wrappers. Handover packets bundle ADRs + schema deltas for Granjur. Ratification docs ask stakeholders to confirm. Pre-flight checklist sequences the Day-1 production work. Audit report verifies the mockup. Cycle log + plan + this summary are loop-process artifacts.
3 handover packets · billing ratification + ADR-007 sign-off email · pre-flight checklist · final audit report · cycle log · plan docket · this summary
A.3End-to-end trace — one ADR from idea to mockup
When we say "ADR-014 landed," what actually happened? Following one decision — dual-cadence semesters (Recitation + Mastery running in parallel) — through every file it touches. Each step below is a real file path with a real excerpt. Click any path to open the file on GitHub.
What happened: Lejla raised this at the May 5 walkthrough. Two cadences run in parallel — each with its own enrollments, content schedule, TAs, and close cycle. The v2 schema had a globally-unique is_current index that breaks the moment two cadences are simultaneously active.
Three options weighed:
Option A — drop is_current, derive from start_date / end_date ranges
Option B — keep is_current, scope partial uniqueness per-cadence
Option C — streams as first-class entity (own table + CRUD)
First-principles call: Option B
Matrix score 7-1-2; cites three documented `is_current` consumers
that break under Option A; Option C is entity-weight without need.
What happened: The Architecture Evaluation doc weighed three options against each other, citing the three documented consumers of is_current (Activate Semester action, v2-CRON-F end-of-semester, Auto-Transition toggle) — all of which break if you drop the column. Option B (keep is_current, scope per-cadence) emerged from re-evaluation. Recommendation only — not yet a decision.
# ADR-014: Stream cadence_track — per-cadence is_current uniqueness
Status: Accepted · Date: 2026-05-18
Source: ARCHITECTURE-EVALUATION-2026-05-06.md Q3 (lines 284-512)
## Decision
Option B from the Q3 trade-off matrix: keep `is_current`,
scope its partial uniqueness to per-cadence, and add
`cadence_track` as an enum column on `semesters`.
What happened: Cycle 7 of the loop. A writer-agent drafted the ADR; a review-agent cross-checked it; the reviewer caught a stale index entry as a BLOCKER, which the lead fixed before commit. The ADR file (~13KB) is the production-team reference — it has the Decision, the SQL, the rationale, and three explicit per-consumer impact rows so the production engineers don't have to reverse-engineer the intent.
- `is_current` boolean NOT NULL DEFAULT false — at most one row TRUE
**per cadence_track** at a time (enforced by partial unique index).
Read by 3 consumers: v2-CRON-F end-of-semester, Activate Semester
action, Auto-Transition toggle.
- `cadence_track` `cadence_track_enum` NOT NULL DEFAULT `'recitation'` —
NEW v2.1 (ADR-014); cadence label set at semester creation. Two values
today (`'recitation'`, `'mastery'`); add via `ALTER TYPE … ADD VALUE`.
Enables parallel cadences running simultaneously.
Enums: `cadence_track_enum` (`'recitation' | 'mastery'`) — ADR-014.
Indexes:
- `semesters_is_current_per_cadence_unique` UNIQUE on `(cadence_track)
WHERE is_current = true` (ADR-014). Replaces `semesters_is_current_unique`.
What happened: The same cycle edited the production schema doc — added the new column on the semesters block, added the new enum to the §Enums sub-section, swapped the unique index entry in §5 Indexes Summary, appended ADR-014 to the §Source line. The reviewer's "standing cross-table sweep" exists precisely to catch parity drift across these four places.
// src/data/types.ts:213-220
/** v2.1 ADR-014: cadence track distinguishes the per-week pacing model
* for the semester. "recitation" is the default for Year 1 (L1-L4);
* "mastery" applies to Year 2. Production lands as NOT NULL DEFAULT
* 'recitation'. Retention denominator and Returning detection scope to
* the prior _same-track_ semester. */
cadenceTrack: "recitation" | "mastery";
}
// src/data/semesters.ts
{ ..., cadenceTrack: "recitation" } // 4 mock rows all on 'recitation'
What happened: The mockup data layer was updated in Cycle 20's final audit (a SHOULD-FIX in Slice 4): the Semester type was renamed from streamId?: "A"|"B" to the real cadenceTrack: "recitation"|"mastery", and all 4 mock semester rows updated. This is what makes the mockup honest — the prop name matches what Granjur will see in the production schema.
From Packet 2 (OQ-SF14-1 reconciliation):
ADR-014 reconciliation flagged — OQ-SF14-1 (semesters.stream_id) and
ADR-014 (cadence_track_enum) are the same concept under different names.
Recommended canonical naming: use ADR-014's column (`cadence_track_enum`)
and treat "stream" as legacy binding-doc terminology.
From Pre-flight Phase 1 (Schema deploy DDL order):
Phase 1.1 — Create `cadence_track_enum` (Recitation/Mastery)
Phase 1.2 — Add `semesters.cadence_track` NOT NULL DEFAULT 'recitation'
Phase 1.3 — Drop `semesters_is_current_unique`
Phase 1.4 — Create `semesters_is_current_per_cadence_unique`
What happened: The Phase D handover Packet 2 noted that one of Granjur's existing Phase D open-questions (OQ-SF14-1 wanting a semester_stream_enum) appeared to collide with ADR-014. The pre-flight checklist Phase 1 sequenced this as a specific ordered DDL deploy. No outstanding stakeholder ask: the reconciliation is already settled across the schema doc (01-schema.md:556), the mockup (src/data/types.ts:219), and pre-flight checklist line 115 (1.1.12). Granjur sees cadence_track_enum the moment they open the schema doc; the legacy binding-doc "stream" terminology won't appear in production schema. Settled informational, not a ratification.
That's what one ADR looks like end-to-end — 5 layers, 6 distinct files touched, one open question for Granjur. The other 10 ADRs follow the same shape. ADRs that affect UI (e.g. ADR-013 Reset Progress, ADR-007 Tag-as-saved-filter) also have a Layer 4b mockup component change; ADRs that are doc-only (ADR-008 privacy, ADR-009 RBAC) skip Layer 4 entirely.
A.4Glossary — what the terms actually mean
ADR Architecture Decision Record
One markdown file per decision. Lives in docs/admin-spec-v2-1/decisions/. Format: Context / Decision / Schema implications / Rationale / Fallback / Open questions. 8-25KB. Production engineers read these before writing code.
OQ-ID Open Question
Identifier for a single schema question raised by the mockup against the production schema doc — e.g. OQ-T6-1 = Teacher Mgmt question #1. The 45 OQs across the 3 handover packets are the "schema decisions that need ratification by Granjur."
Handover Packet
Date-prefixed markdown file at the root of docs/admin-spec-v2-1/. Bundles already-shipped decisions for delivery to Granjur. Format: item-by-item table with OQ-ID, citation, recommended action.
Pre-Flight Checklist
One 1186-line file (2026-05-18-production-week-0-preflight-checklist.md) sequencing every Day-1 action for the production team. 12 phases × 344 line items + 3 appendices (landmines / collisions / gaps).
Audit Report
One file (2026-05-18-final-audit-report.md) capturing the 4-slice integration audit from Cycle 20: 104 findings, 10 BLOCKERs (all fixed), 30 SHOULD-FIXes (9 fixed, 21 deferred).
Bucket A / B / D
The plan's 3-bucket framing for resolution work. A = Claude resolves no stakeholder gate. B = needs Lejla. D = needs Kamran's strategic orchestration. (There's no Bucket C in the plan.)
Schema doc
One file at docs/production-architecture-research/30-design/01-schema.md. The canonical DDL specification — what Granjur turns into Drizzle migrations. ~2900 lines after loop edits; edited by every TYPE-1 ADR cycle.
Mockup
The React+TypeScript app at src/. Live at quranflow-admin.pages.dev/v2. v1 at /* is frozen; v2 + v2.1 at /v2/* is active.
Spec
Two layers. docs/admin-spec-v2/ is the BEFORE-state baseline (frozen reference). docs/admin-spec-v2-1/ is the AFTER-state diff + new canonical layer for v2.1 work.
Cycle log
2026-05-17-cycle-log.md — append-only journal, one entry per cycle. Captures status (in_progress / completed), agents spawned, files touched, summary, next cycle.
Plan docket
2026-05-17-next-phase-plan.md §5 — the 21-cycle list with dependencies and types. Edited in-place to strike completed cycles.
TYPE-1 / TYPE-2 / TYPE-3 / TYPE-4 / TYPE-5
Plan §5 cycle-shape taxonomy. TYPE-1 = ADR write-up. TYPE-2 = Phase D handover. TYPE-3 = pre-review pipeline (4-lens → depth → packages). TYPE-4 = doc cycle. TYPE-5 = final audit. Defines how many agents and what teamwork pattern.
A.5So where is the "unified final view"?
There isn't one single file. The system is described by three parallel tracks that all align. Granjur reads all three before they start writing code; Lejla reviews the live mockup; you (Kamran) coordinate across all three.
Track 1 · What to see
The live mockup
Deployed React app. Demonstrates that every decision renders coherently in the admin UI. Stakeholders click around to verify the design works in practice. This is the most concrete artifact — you can run it and use it.
Canonical DDL specification for the production Postgres database. Per-table column lists + enums + indexes + source citations. Granjur turns this into Drizzle migrations in production Week 1.
The 11 ADRs (decisions of record) + the v2.1 revision summary + the 3 Phase D handover packets + the screen-data bindings. Tells Granjur why the schema is what it is and how screens use it.
The three tracks align by design. The mockup demonstrates what the schema enables. The schema doc cites the ADRs that drove each column. The ADRs cite the meeting transcripts that drove each decision. You can read in either direction — start from a screen and trace back to the transcript, or start from a transcript and forward to the live render. The loop's job was to make sure all three tracks tell the same story.
Where things get added vs overwritten:
Created new (never overwrites)
ADRs — one file per decision in decisions/
Handover packets — one file per delivery
Ratification + sign-off emails
This summary + companion HTML
Cycle log entries (append-only)
Execution-status rows (append-only)
Edited in place
Schema doc — every ADR modifies the relevant table block