Self Top-up — Task Breakdown (as-built reconciliation)
Mode change vs prior version. Previous breakdown was a forward plan against mocked BE (
hub_core not checked out). This rewrite is a reconciliation against the as-built code in five repos (hub-chat-v2,hub-core,hub-service,qontak-billing,modpanel/moderator-be) and a direct read of every PRD-cited Jira ticket. The build is materially complete; the leftover is entirely in the QA / PO-sign-off lane.
Sources of truth, grounded:
- PRD:
prds/self-topup.md(Confluence50190516285).- RFC (now linked): "Self Top-up Technical Documentation" — Confluence
51046842375. Describes the Ruby/Rails hub-core flow + Go qontak-billing config source as actually shipped.- Epic: BIF-7797 (not BIF-5840 — see Drift §0).
- All ticket statuses verified via Jira on 2026-06-30.
0. Drift correction before reading any task
Three drift items invalidate the prior breakdown's framing:
- PRD says Epic = BIF-5840. That Epic is Done (it's the 25Q4 BE-only forerunner). The actual
delivery Epic for everything in this PRD is BIF-7797 "26Q2 Self Topup (Call, AI, User, Email,
Recording)" — status To Do, target 26Q2, deprio'd 2026-04-21 per Addo's comment. Every
PRD-cited Story (BIF-7869…BIF-7879) is a child of BIF-7797.
README.mdjira_epicand the PRD header must be updated to BIF-7797. - PRD says "RFC Link = TBD". The RFC exists:
51046842375"Self Top-up Technical Documentation" (Hafriz, Feb 2026, v3). It describes the as-built system. It is not linked from the Epic or the PRD — pure linkage gap, not a missing artefact. - The build is not blocked on
hub_core. The Go serviceqontak-billingalready ownstopup_configurationsend-to-end and already calls Mekari Billing's/api/v4/packages/{id}frominternal/app/api/mekari_billing/get_detail_package.go:20. The prior breakdown's "STP-S09 fully blocked" framing was wrong.
1. Build status — as-built evidence per story
Legend: ✅ Implemented (file + line) · ⚠️ Implemented with caveat · ❌ Not built · 🟡 Code done, awaiting PO confirm / QA sign-off.
| Story | Jira (status, 2026-06-30) | Code evidence | Verdict |
|---|---|---|---|
| STP-S01 Feature flag + V3 gate | BIF-7879 — In Progress | Two-layer flag: hub-core/spec/dummy/db/data/20200219071220_default_data_flag.rb:1648,1666 (billing_new_topup_page, billing_new_topup); per-CID organization.settings['bifrost_new_topup'] evaluated in hub-core/app/apps/mekari_pay/interactors/user_pay_invoiced.rb:35. FE consumes via hub-chat-v2/common/store/BillingStore.ts:17 (is_new_topup_page?: boolean) and hub-chat-v2/features/finances/topup/FinancesTopupPage.vue:314. Sub-tasks BIF-8248 (BE) Done, BIF-8249 (FE) Done, BIF-8250 (QA) Done, BIF-8457 (Nuxt4 toggle) Done. | 🟡 Code complete; Story still In Progress because it doubles as the rollout-by-CID coordination ticket. |
| STP-S02 WA min 1,000,000 + excess (UPDATE) | BIF-7871 — Waiting Confirmation | hub-chat-v2/features/finances/topup/FinancesTopupPage.vue:373 (minimumTopup.value.wa_balance = mcc?.is_enabled ? mcc.minimum_value : 1000000); spec components/__tests__/FinancesTopupPage.spec.ts:19; BE row task BIF-7900 (Done) added wa_balance to topup_configurations. | 🟡 |
| STP-S03 MUV predefined options + hide on Unlimited (UPDATE) | BIF-7872 — Waiting Confirmation | Options stored in qontak-billing/db/migrations/20251009090000_create_topup_configurations.up.sql:12 (option_value JSONB). Hide-by-component in qontak-billing/internal/app/usecase/topup_configuration/fetch_configuration.go:60-66 (isHideComponent/isShowComponent against hide_by_component/show_by_component JSONB). FE: hub-chat-v2/features/finances/topup/store/useTopupStore.ts:118 (isToggleForHideMUV); components/TopupStepTwo.vue:77 MUV row. BE row task BIF-7899 (Done). | 🟡 |
| STP-S04 User Quota (NEW) | BIF-7873 — Waiting Confirmation | BE row task BIF-7898 (Done). Package code field present in qontak-billing/db/migrations/20251015093000_add_fields_to_topup_configurations.up.sql (package_code, quota_type, billing_version_for_pi, excess_*); period semantics carried via quota_type column. PRD's "period = contract end_date" rule is not visible in the moderator-be paid_pi payload at modpanel/moderator-be/app/controllers/api/v1/billing/subscriptions_controller.rb:41 (action takes active_from + paid_date; no period_end/contract_end_date parameter mapped). | ⚠️ Code in qontak-billing complete; verification gap: confirm contract_end_date actually reaches Mekari Billing via paid_pi. |
| STP-S05 Voice Balance (NEW) | BIF-7874 — Waiting Confirmation | BE row task BIF-7897 (Done). Component-code gate via show_by_component JSONB (migration 20251015093000). hub-core add-on mapping: app/core/domains/services/billing/bulk_add_on.rb:42 (voice_call_extra mapped behind is_voice_call_enabled flag billing_voice_call_topup). | 🟡 |
| STP-S06 AI Dialog (NEW) | BIF-7875 — Waiting Confirmation | BE row task BIF-7896 (Done). hub-core add-on mapping: app/core/domains/services/billing/bulk_add_on.rb:30 (chatbot_ai_extra behind billing_chatbot_ai_topup). ×1000 multiplier is encoded in seed data via option_value JSONB — not in app code; cross-check seed when CID rollout begins. | ⚠️ Code complete; verification gap: assert seeded option_value for the AI row produces N → N×1,000 dialogs end-to-end. |
| STP-S07 Review Order + confirmation popup (NEW) | BIF-7876 — Waiting Confirmation | FE: hub-chat-v2/features/finances/topup/components/TopupDynamicStep2.vue, TopupStepTwo.vue. Confirmation modal evidenced by BIF-8558 (Done) ("Missing close button in confirmation self top up modal"). UI revision BIF-8324 (Done) "Adjust UI Self top up based on new UI Step two". | 🟡 |
| STP-S08 Excess usage on Topup page (NEW) | BIF-8175 — In Progress (separate Story; PRD row 12 has empty Jira cell — PRD↔Jira link gap) | FE composable hub-chat-v2/features/finances/topup/composables/useExcessBalance.ts; rendered at FinancesTopupPage.vue:196 (:excess-balances="excessBalances"). BE: dedicated endpoint GET /iag/v1/topup-configurations/excess-quota at qontak-billing/internal/app/server/rest_router.go:65; usecase internal/app/usecase/topup_configuration/excess_quota_topup.go. Migration 20251015093000 added excess_key, excess_package_id, excess_package_code. Executor sub-tasks BIF-8023 (FE) Done and BIF-8024 (BE) Done — they are split-from BIF-8175, not from a PRD-cited Story. | 🟡 |
| STP-S09 Mekari Billing pricing API + Redis cache (BE) | BIF-7869 — Waiting Confirmation | Already implemented in qontak-billing. Client: internal/app/api/mekari_billing/get_detail_package.go:19-20 (GET /api/v4/packages/{id}). Caller: internal/app/usecase/topup_configuration/fetch_configuration.go:144 (s.mekariBillingClient.GetDetailPackage(ctx, packageID)). Cache: same file, lines 21-22 (cacheTopupConfigurationDuration = 1 * time.Hour, key topup_configuration:). Fallback on cache miss → DB → API → re-cache; on API failure, the row is skipped with a logged error. Dynamic placeholders in label/description replaced via {{mekari_billing}} token (fetch_configuration.go:replacePlaceholder). | ✅ |
Non-PRD scope additions on Epic BIF-7797 (also worth tracking)
| Ticket | Status | Why it matters |
|---|---|---|
| BIF-7889 (BE) / BIF-7891 (FE) Placeholder: partial toggle to several CIDs | Done / Won't Fix | Rollout-list plumbing. |
BIF-7892 (BE) add label and description to topup_configurations | Done | Migration 20260331120000_add_label_and_description_to_topup_configurations.up.sql (qontak-billing). |
| BIF-7893 (FE) Create a new topup page | Done | Net-new hub-chat-v2/features/finances/topup/FinancesTopupPage.vue shell. |
BIF-7894 (FE) Hit and map topup_configurations from BE | Done | BillingStore.ts:228 (getTopupConfigurations). |
| BIF-7895 (FE) Create/adjust review top-up page for POST orders | Done | Review step. |
| BIF-7901 (BE) Add label and description to fetch endpoint | Done | Response field label, description (response/topup_configuration.go:24-25). |
| BIF-7960 (BE) Adjust wording all email notification | Done | Topup notification copy. |
| BIF-8198 (FE) Recheck API integration | Done | Post-rewire smoke. |
| BIF-8324 (FE) Adjust UI Self top up based on new UI Step two | Done | Step-2 redesign — not in PRD AC table. |
BIF-8342 (BE) add component_name_excess to topup_configurations | Won't Fix | Deferred refinement. |
| BIF-8456 (QA) Testing new Nuxt4 Self Top Up page + automation | Done | Nuxt4 migration QA. |
| BIF-8457 (FE) Toggle new Self Top Up nuxt4 page | Done | Nuxt4 migration toggle. |
| BIF-8382 (QA) E2E Testing Self Top Up | In Testing | This is the actual blocker. |
| BIF-8557 (Bug) WA top-up button shown without WABA | Done | Component-code gating regression fixed. |
| BIF-8558 (Bug) Missing close button in confirmation popup | Done | Review-popup polish. |
2. Effort Summary (rewritten on actuals)
The prior breakdown estimated 22 person-days remaining (FE 12.5 + BE 5 + QA 4.5). Reality:
| Bucket | Forward estimate (prior breakdown) | Actual outcome |
|---|---|---|
| Phase 1 — UI mocked | FE 10.5 + QA 2.5 = 13 d | Shipped under BIF-7893/7894/7895/8023/8324/8457. Closed. |
| Phase 2 — API integration | FE 2 + BE 5 + QA 2 = 9 d | Shipped under BIF-7896/7897/7898/7899/7900/7901/7892/8024/8248/8249. Closed. |
| Remaining work | — | ~3–5 person-days of verification + PO sign-off + rollout coordination. See §3. |
Effort confidence on the remaining work: high. All five repos inspected on disk; every PRD-cited Story has either Done sub-tasks shipping the change, or is itself paused only on PO confirmation. The two outstanding code questions (STP-S04 contract-end date in
paid_pi, STP-S06 ×1000 multiplier seed) are localized investigations, not features-to-write.
3. Leftover work (the actual backlog)
Everything below is what stands between code-as-built and BIF-7797 closing. No new features.
Task L1: [PO/PM] Review and confirm the 10 Stories awaiting PO sign-off
Status: ✅ Done — 10 of 10. Verified against Jira 2026-07-01, in two passes the same day: BIF-7871–7878 resolved first (PO walkthrough session), then BIF-7869 and BIF-7870 also resolved
Doneon a follow-up review. All ten PRD-cited Stories are now closed.
What to do — complete.
Effort: 0.5 d (PM) + 1 d (PO) — spent.
Depends on: nothing.
Task L2: [QA] Finish BIF-8382 E2E Testing Self Top Up
Status: 🟡 In Testing on the board, actively in motion. This is the only QA ticket not yet Done. The other two QA tickets in scope (BIF-8250 testcase, BIF-8456 Nuxt4 testing) are Done. Target: closed on/before the 2026-08-31 hard deadline — ~2 md against a ~2-month runway, no capacity concern (Yoddi/Izzul have headroom).
What to verify in this E2E pass — these are the gaps surfaced by the code reconciliation:
- STP-S04 contract-end propagation. For a User Quota (
agent_extra) top-up on a V3 CID, assert that the resulting Paid PI in Mekari Billing carriesperiod_end = contract.end_date. Inspect thepaid_pipayload sent from hub-core tomodpanel/moderator-be/app/controllers/api/v1/billing/subscriptions_controller.rb:41. If the payload lacks anend_datefield, file a follow-up to hub-core'sCreatePaidPi. - STP-S06 AI ×1000 multiplier. Submit
qty = 5for the AI Dialog row and assertchatbot_ai_extraincreases by 5,000 onOrganizationPackage. The multiplier is data-only (topup_configurations.option_value), so a regression here is a seed/data issue, not code. - STP-S05 + STP-S06 component-code gating. For three CIDs (with Voice component, with AI
component, with neither), assert that the qontak-billing
GET /iag/v1/topup-configurationsresponse filters rows pershow_by_componentevaluated inqontak-billing/internal/app/usecase/topup_configuration/fetch_configuration.go:66-71. The BIF-8557 bug (WA shown without WABA) means this gating path has had at least one regression. - STP-S09 Mekari Billing pricing. Force an
is_fixed_cost = falserow and assertbase_pricein the response reflects the current Mekari Billing/api/v4/packages/{id}price, not the stale DB column. Validate fallback when the upstream returns an error (fetch_configuration.go:84-87— the row is dropped, not stubbed). - STP-S01 + S07 happy-path E2E. V3 CID with flag ON → top-up → Review Order → confirm popup → Mekari Pay redirect → webhook → balance increment → Paid PI created in Jurnal. End-to-end.
- Idempotency. Replay a paid webhook and assert no double-credit (Redis lock at
hub-core/app/apps/mekari_pay/services/redis/set_invoice_webhook.rb-style flow per RFC §2.3).
Effort: 2 d (QA — Yoddi/Izzul; this is the existing BIF-8382 ticket, just needs to finish).
Depends on: nothing — environment already exists.
Task L3: [PM/TPM] Fix PRD ↔ Jira ↔ RFC linkage drift
Status: 🟡 3 of 4 done (2026-07-01). First three fixed directly in the PRD; the fourth (scope-addition note) is optional and still open.
| Fix | Where | Action | Status |
|---|---|---|---|
| PRD Epic field is wrong | prds/self-topup.md HEADER BLOCK + README.md jira_epic | Change BIF-5840 → BIF-7797. | ✅ Done |
| PRD row for "Show excess Usage on Topup Page" has empty Jira cell | prds/self-topup.md §8.2 row 12 | Add BIF-8175 link; note its split-from chain to BIF-8023 / BIF-8024. | ✅ Done |
| RFC unlinked from Epic + PRD | BIF-7797 description; prds/self-topup.md HEADER RFC Link | Link Confluence 51046842375 from both. | ✅ Done in the PRD. Still open on the BIF-7797 Jira description — Jira-side edit not yet made. |
| PRD missing scope additions | prds/self-topup.md §8.2 or a new "Post-PRD additions" note | Either fold BIF-7960 (email), BIF-8324 (Step-2 UI), BIF-8456/8457 (Nuxt4) into ACs, or list them explicitly as accepted scope drift. | ❌ Open (optional) |
Effort: 0.5 d (PM) — ~0.4 d spent, ~0.1 d remaining (Jira description link + optional scope note).
Depends on: nothing.
Task L4: [BE] Resolve STP-S04 paid_pi end-date question (only if L2 verification fails)
Status: ⚠️ Conditional. Run only if L2 item 1 fails.
If paid_pi does not carry the contract end_date for User Quota top-ups, add the field in:
hub-core(path TBD on inspection; theCreatePaidPiservice identified in RFC §1.4 Step B);- and accept it in
modpanel/moderator-be/app/controllers/api/v1/billing/subscriptions_controller.rb(thepaid_piaction signature).
Effort: 1 d BE + 0.5 d QA. Triggered only by L2 outcome.
Depends on: L2 result.
Task L5: [TPM] Reconcile Epic priority + close
Status: ✅ Resolved 2026-07-01, and mirrored into Jira the same day. TPM confirmed Self Top-up stays in 26Q3 — deprio lifted,
target_quarter: 2026-Q3/commitment_date: 2026-08-31stand. Seedelivery/decisions/0001-lift-bif-7797-deprio-confirm-26q3.md. BIF-7797 itself movedTo Do→In Progressin Jira (verified 10:49 same day) — the deprio is lifted on the board, not just documented here.
BIF-7797 was To Do + P2, deprio'd on 2026-04-21. With the code done and L1 now fully
resolved, the TPM lifted the deprio and closed on 26Q3 rather than slipping target_quarter —
only L2 (BIF-8382) remains, well inside the quarter.
Effort: 0.5 d (TPM).
Depends on: L1 outcome.
4. Updated Effort Summary (leftover only, as of 2026-07-01)
| Task | Discipline | Days | Status |
|---|---|---|---|
| L1 PO sign-off coordination | PM | 0.5 | ✅ Done |
| L1 PO sign-off itself | PO | 1 | ✅ Done (10/10 Stories) |
| L2 E2E QA (BIF-8382) | QA | 2 | 🟡 Open — In Testing |
| L3 doc/link fixes | PM | 0.5 | 🟡 3 of 4 done (~0.1 d left, optional) |
| L4 paid_pi end-date fix | BE+QA | 1.5 | Conditional — only if L2 #1 fails |
| L5 Epic priority decision | TPM | 0.5 | ✅ Done, mirrored into Jira |
| Total remaining — unconditional | — | ~2.1 d | — |
| Total remaining — if L4 triggers | — | ~3.6 d | — |
progress_done_days should be updated in README.md from 0 to a value close to `effort_total_days
- 4.5 ≈ 17.5
(out of22),progress_pct ≈ 80,progress_as_of: 2026-06-30. The earlierprogress_done_days: 0` reflects the prior breakdown's "nothing built yet" framing, which the code disagrees with.
5. Ordering rationale
The bottleneck is organisational, not technical:
- L3 first (0.5 d, no deps). Fix the linkage drift so reviewers walking the docs land on
BIF-7797 and
51046842375, not BIF-5840 and "RFC TBD". Cheapest, highest-leverage. - L1 + L2 in parallel. L1 clears the 10 Stories' review checkpoint with one PO session; L2 finishes the only open QA ticket. Both can start the same day.
- L4 only if L2 surfaces the end-date gap. Don't pre-emptively touch hub-core / moderator-be.
- L5 last. Once L1+L2 land, BIF-7797 is closable; TPM owns the priority cleanup.
6. Skipped — none
The prior breakdown skipped STP-S09 and the BE half of STP-S04 as "blocked on external
contracts". Both are shipped in qontak-billing (fetch_configuration.go + the
add_fields_to_topup_configurations migration). No items remain skipped.
7. Changelog
| Version | Date | Author | Summary |
|---|---|---|---|
| 2.3 | 2026-07-01 | L1 fully closed | Second Jira pass same day: BIF-7869 and BIF-7870 also resolved Done — all 10 L1 Stories now closed. BIF-7797 Epic moved To Do → In Progress in Jira (deprio lifted on the board, not just in this repo). L5 fully resolved. Only L2 (BIF-8382 QA E2E, In Testing) remains open. Initiative moved Tentative → Commit on the team readiness dashboard. |
| 2.2 | 2026-07-01 | Jira verification + L3 fix | Verified live against Jira: 8 of 10 L1 Stories (BIF-7871–7878) resolved Done today. BIF-7869 and BIF-7870 remain To Do, untouched — not part of the completed walkthrough. BIF-8382 QA E2E still In Testing. BIF-7797 Epic still To Do/P2 with no comment reflecting the 26Q3 decision — the ADR (0001) is recorded in this repo but not yet mirrored into Jira. Also closed 3 of 4 L3 items: prds/self-topup.md HEADER Epic corrected to BIF-7797, RFC Link added, and the excess-usage row's empty Jira cell filled with BIF-8175. |
| 2.1 | 2026-07-01 | TPM decision | Task L5 resolved: TPM confirmed BIF-7797 stays in 26Q3 (deprio lifted). See delivery/decisions/0001-lift-bif-7797-deprio-confirm-26q3.md. L1 (PO sign-off) + L2 (BIF-8382 E2E) remain the only open blockers. |
| 2.0 | 2026-06-30 | Reconciliation | Rewrote against as-built code across all five repos and verified Jira ticket statuses. Found build materially complete; leftover is PO sign-off (10 Stories in Waiting Confirmation) and one QA ticket (BIF-8382 In Testing). Corrected three drift items (wrong Epic, missing RFC link, PRD↔Jira gap on excess row). Effort drops from 22 d forward-estimate to 4.5 d remaining (6 d if a conditional BE fix triggers). |
| 1.0 | 2026-06-29 | Forward plan | Horizontal slice plan against mocked BE; hub_core not checked out. |