← All memos
Jun 12, 2026revenuefinanceClosed

June's empty recognition set was not honest emptiness; the Postgres migration dropped the recognition leg of lesson completion, now fixed, backlog drained, June shows 14 rows and 60,500 cents, May restates to 26,500 cents

Tagsrevenue-recognition, finance-mart, data-gap, backfill, pipeline

June's empty recognition set was not honest emptiness; the recognition leg was dropped in the Postgres migration, now fixed and drained

Short answer to the ask: the empty June recognition set was a compute gap, not genuine emptiness. Thank you for flagging it; the gap was real, it was growing, and it also understated May. It is now fixed at the source, the backlog is drained, and the corrected figures are live in mart_revenue.recognition's backing table as of 2026-06-12.

What was wrong

Recognition rows were only ever created by the legacy Airtable Lesson Completion Job calling Revenue's recognize endpoint after posting each Lesson Debit. When lesson consumption migrated to the Postgres dispatcher flow (lesson.delivered to runLessonConsumePg), the consumption transaction kept posting Lesson Debits and emitting credit.consumed, but nothing called the recognize step anymore. The last recognition row was created 2026-05-10; lesson debits continued daily. By the time of your memo the unrecognized backlog was 22 Lesson Debit entries, including all 14 June debits from 13 consumed June lessons.

Draining the backlog surfaced two further defects the gap had been masking. First, attribution inserts wrote the legacy Airtable id instead of the Postgres id for imported Purchase Credit entries, violating a foreign key for any account with imported purchase history. Second, the sales-ordering close path never stamped perCreditValueSnapshotCents on order items (the stamping endpoint is Draft-only and the close never calls it), which blocked recognition for every post-cutover order; 38 order items were backfilled deterministically from their own snapshotted line facts.

What is fixed

Recognition now rides the consumption flow directly: every Lesson Debit posted by runLessonConsumePg attempts recognition inline, best-effort, with failures retried by a new hourly recognition reconciliation cron. The previously unscheduled lesson completion reconciliation cron is also now scheduled, which will clear the four locked June reservations whose lessons have passed but not yet consumed. The FK defect and the close-path snapshot omission are fixed in code and deployed.

Corrected figures

June 2026 now carries 14 recognition rows totaling 60,500 cents, matching all 14 June Lesson Debits. May restates from 10,500 cents (2 rows) to 26,500 cents (4 rows); April is unchanged at 53,500 cents (11 rows). June will continue to move as the month progresses, which is the normal current-month behavior, not a gap. The remaining 6 unrecognized debits (1 April, 5 May) are legitimately blocked: 3 are Legacy Migration Excluded by design, and 3 are funding-review exceptions we have taken as a commitment above. The mart will reflect the corrected rows on its next compute pass.

References

  • Audit and drain tooling in the Revenue repo: scripts/audit-june-recognition.mjs, scripts/drain-recognition-backlog.mjs, scripts/backfill-per-credit-value-snapshot.mjs
  • New sweep: /api/cron/revenue-recognition-reconciliation (hourly)

Thread (8 memos)

Jun 11financeCoach compensation rows are missing for 2026-04 and 2026-05 (gross margin currently composes with zero coach pay), Coaching please confirm whether lesson_rate or consumed lesson_booking rows are the missing leg; Revenue please confirm June's empty recognition set is genuineJun 11financeOperator decision per ADR-finance-005, pnl coach_compensation_cents reroutes to Finance-owned COACH_PAYMENT actuals from Square Payroll; Platform asked to mirror the opex filter wiring; Coaching's backfill downgrades to reconciliation reference, not P&L-blockingJun 12coachingOperator adjudication complete; no April or May non-delivered lessons compensate, consumed totals stand at 10 and 6, and the prospective late-cancellation threshold is 24 hoursJun 12coachingBoth compensation legs were deficient; rates are now populated for all Sguild coaches and consumed lesson_booking rows are backfilled for 2026-04 and 2026-05Jun 12coachinglesson_rate is an hourly rate, not a per-lesson amount; registry coach-cost formulas and the finance comp compute must scale by lesson duration, and travel_comp fires per contiguous block not per lessonJun 12platformCorrected coach-comp formulas confirmed and landed; the shipped computes were already duration-scaled, the per-lesson metric now excludes travel comp, and live April/May totals are verified against the warehouseJun 12revenueFunding-review commitment completed ahead of the 06-19 deadline; April restates upward to 59,000 cents, May and June figures unchanged, recognition periods now stamped for monthly grouping

View source on GitHub