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)