Skip to content

2026-07-17 — XP-139 pricing read-path wave hoist

  • packages/service/src/pricing/list-price-change-batches.ts — the 5 human-label lookups (academicYears / segmentasi / tingkat / subjects / golongan) hoisted out of the old trailing wave into wave 1 as a nested Promise.all beside rowsByTable; the id→label Map build moved up with them. Independent-blocking waves 3 → 2. auditByTable (needs batched row ids) stays wave 2.
  • packages/service/src/pricing/freeze-sesi-pricing.ts — the conditional tutor-golongan read now rides wave 1 (session.golongan_id ? Promise.resolve(null) : read) alongside resolveStudentPrice + the 4 label reads; only resolveTutorPrice (the sole golongan-dependent read) stays gated as a micro wave 2.
  • Behavior-preserving reorder only, zero logic/formula/column-set change — freeze/list output byte-identical, guarded by the existing money + pricing integration suites (583 integration tests green). No new tests per YAGNI.
  • Common-path trade-off: when session.golongan_id IS set, resolveTutorPrice now defers to micro-wave 2 rather than running concurrently — implemented exactly as the perf-sweep ticket prescribed; the real win is the null-session-golongan branch.
  • freeze-sesi-pricing.ts sits on the money-freeze/snapshot path (domain-pricing-snapshot.md). Confirmed scheduling-only: frozenPriceColumns, buildUnpricedCell, resolve call args, and archived_axes derivation all untouched; unpriced-tutor (null golongan → no throw) semantics preserved. TS narrows golonganId to string inside the truthy ternary, so the old as string cast on resolveTutorPrice dropped — net readability gain.

None. No canonical doc (plans/ARCHITECTURE.md, plans/scope/*.md, .claude/rules/*.md) changed.