Skip to main content

CDP 26Q3 Engineering Initiatives

Bucket initiative for small, unrelated engineering-owned work handled by the CDP team in 26Q3 — server/infra migration, EOL / dependency upgrades, unit-test-coverage pushes, latency / performance improvements, and similar non-product-feature work. Each request is a self-contained item with its own RFC under rfcs/ — engineering work is documented as an RFC, not a PRD (PRD = product); they do not share a single theme beyond being engineering work that rolls up to Jira epic TF-3500.

This is why the work lives here as a bucket rather than one standard initiative per item: the stream is heterogeneous and grows mid-quarter, and each item is priced, classed, pulled and completed independently. A bucket is one container (one Portfolio row, one Epic) but nothing inside shares a fate — so a new EOL bump or latency fix slots in as an appended row, never a roadmap ## Priority renumber. Full doctrine + real-world grounding: .claude/reference/adhoc-interrupt-doctrine.md.

Per the repo's Jira alignment convention, jira_epic lives here on the initiative README (not on the individual RFCs) so the manual per-AC sync is never accidentally triggered. Each RFC references its own Jira Story key in its body.

Scope Changes

  • Backend — service/dependency upgrades, EOL runtime migration, test-coverage, query/latency work; CRM/chat → CDP back-sync of crm_data_id/chat_data for CDP-originated contacts (A6/A7); contact source ID/label split (A5); possible qontak_customer_id lookup reuse for the legacy contact redirect guard (A8, pending A6); Team Owner backfill once CRM's Team data migrates into USMAN (A9, blocked, no ETA).
  • Infra — server / cluster / platform migration and hardening.
  • Frontend — qontak-customer-fe display/filter changes for the contact source split (A5); legacy CRM contact page restrict/redirect guard for Qontak One tenants (A8).
  • Data — field-property reconciliation + contact backfill for the source migration (A5); team-owner backfill once CRM's Team → USMAN migration lands (A9).

(Anticipated surfaces — refined as real stories land; each story's PRD carries the subset it changes.)

Stories — per-story tracker (canonical)

This table is the canonical per-story home for the bucket. Each row is one self-contained story = one Jira Story under Epic TF-3500, priced, classed, pulled and completed independently. The bucket is one container initiative (one Portfolio row, one Epic), but nothing here shares a fate. It is classed, not ranked: it takes no ## Priority rank on the roadmap (a heterogeneous bucket can't be ordered by one integer, and an urgent engineering fix can outrank a ranked initiative) — instead each story carries a Class of Service and the roadmap renders the bucket in its parallel ## Adhoc / Interrupt lane. The tracker is append-only — the bucket grows by adding rows, never by restructuring the roadmap (see How the bucket grows).

Column contract:

  • Story — stable identity A<n>, assigned in creation order and never reused/renumbered (keys the sprint queues; new items get the next free A<n>).
  • Class — per-story Class of Service (Kanban): 🔴 Expedite (may preempt ranked work — WIP-limited to expedite_wip_limit: 2 in flight + logged in ## Expedite log) · 📅 Fixed-Date (hard external date — reuses commitment_date) · 🟡 Standard (normal pull-in fill, never ahead of ranked work) · ⚪ Intangible (cleanup / slack only). A class, not a rank, so a new story slots in without renumbering.
  • DRIper-item accountable lead (repo-local column — CDP's engineering items vary in ownership, so the DRI is captured per story, not just the container-level dri frontmatter above).
  • Status — live completion, the canonical per-story state: backlogin-progressin-reviewdone. The roadmap forecast row's % done is the rollup done ÷ total of this column.
  • BE · FE · QA · Mobile — canonical per-story staffing, one column per discipline; the roadmap Implementors, capacity.md Allocation and the sprint queues mirror these. = that discipline isn't in the story's Scope. Every row needs a QA owner (Lane B default).
  • Effort is deliberately unsized (captures-never-estimates) — these enter the sprint as goal-led-Kanban pull-in fill, not a capacity-boxed load.
StoryClassDRIStatusAddedScopeBEFEQAMobileJira StorySummary (→ RFC)
A1🟡 StandardGhozi, Julio Jeffer, Berliantobacklog2026-07-10Backend, InfraGhozi, Julio Jeffer, BerliantoHendryTF-3501Bump Go 1.25 → 1.26 (approaching EOL ~2026-08) → rfc
A2🔴 ExpediteGhozi, Julio Jeffer, Berliantobacklog2026-07-10Backend, InfraGhozi, Julio Jeffer, BerliantoHendryTF-3502Upgrade Alpine 3.18 (EOL) → 3.21/3.22 runtime base → rfc
A3⚪ IntangibleGhozi, Julio Jeffer, Berliantobacklog2026-07-10BackendGhozi, Julio Jeffer, BerliantoHendryTF-3503Migrate off gocraft/work (EOL) — evaluate asynq/river → rfc
A4🟡 StandardGhozi, Julio Jeffer, Berliantoin-review2026-07-10BackendGhozi, Julio Jeffer, BerliantoHendryTF-3504Remove satori/go.uuid (EOL) → standardize on google/uuid → rfc — code done via Task TF-3499 (Julio Jeffer, Done 2026-07; incl. bonus pgx/v4→v5); Story open pending QA gate + closure
A5🔴 Expedite (reclassified 2026-07-21, was 🟡 Standard)Julio Jefferbacklog2026-07-21Backend, Frontend, DataJulio Jeffer, Ghozi, BerliantoTBDHendryTF-3560 (Tasks: TF-3523, TF-3549)Contact source field: make Contact.Source a stable ID, Contact.SourceName the resolved label (cross-repo: contact-service, qontak.com, chat producer, qontak-customer-fe). BE split 2026-07-21: Jeffer owns contact-service (BE CDP), Ghozi owns chat-producer coordination (BE Chat), Berlianto co-owns; FE + Mobile owners still need confirming → rfc
A6🔴 ExpediteJulio Jefferbacklog2026-07-21BackendJulio JefferHendryTF-3551 (Task: TF-3517)CRM publishes crm_data_id back to CDP on CDP-originated contact create (Qontak One only) — BI reports null/empty crm_data_id; sibling of A7, same root cause → prd
A7🔴 ExpediteGhoziin-progress2026-07-21BackendGhoziHendryTF-3552 (Task: TF-3526)Chat domain acks chat_data back to CDP on CDP-originated contact create (Qontak One only) — client can't select CDP-created customers as campaign recipients; sibling of A6 → prd
A8🔴 ExpediteJovibacklog2026-07-21Frontend, BackendTBDJoviHendryTF-3561 (Tasks: TF-3540, TF-3541)Guard the legacy CRM contact page for Qontak One (unified_app = TRUE) users who force-navigate its URL — restrict or redirect to CDP /customer/{id} instead of silently rendering the legacy page; likely depends on A6's qontak_customer_id back-sync → rfc
A9🟡 Standard (not startable — see RFC Open Questions)Ghozibacklog2026-07-21Backend, DataGhoziHendrypending mintTeam Owner (team_owner_ids) backfill for existing/migrated contacts — blocked until CRM migrates its Team data into USMAN (no committed date); tracked separately from Team Owner Field & Team Permission (P4) so this open-ended external gate doesn't sit inside that initiative's 08-31 commitment → rfc
A11🟡 StandardAzanidone2026-07-22FrontendAzaniHendrypending mint (Task: TF-3555)Confirm stable @mekari/pixel3 version with the Pixel3 team (qontak-customer-fe — exact pin on a prerelease, no confirmed stable line) → rfc
A12🟡 StandardAzaniin-progress2026-07-22FrontendAzaniHendrypending mint (Task: TF-3556)Upgrade pnpm 9.15.3 → 10/11 (qontak-customer-fe — pnpm 9 approaching end of support) → rfc
A13🟡 StandardAzaniin-progress2026-07-22FrontendAzaniHendrypending mint (Task: TF-3557)Add explicit TypeScript devDependency pin (qontak-customer-fe — currently only a transitive dependency) → rfc

Code-evidence disposition (2026-07-26 pass-03):

  • TF-3555 is board-Done but has no merged-code trace in the registered repositories. A11 keeps its board-derived done tracker state, but the issue is 0-credited for code-corroborated delivery until a merge is cited or Jira is corrected.
  • TF-3572 and TF-3584 are board-Done engineering-bucket issues with no merged-code trace and no canonical A<n> tracker rows. They are 0-credited and excluded from the done / total rollup. The DRI must classify each item, append tracker rows through the bucket-growth checklist, and cite merged evidence or correct Jira before either can affect delivery progress.

Progress rollup: 1 of 12 done (8%) as of 2026-07-27 (A4 in-review — its execution Task TF-3499 is Done, Story not yet closed; A5 backlog, prioritized across team this sprint; A6 backlog; A7 in-progress, Ghozi; A8 backlog, newly added, unowned; A9 backlog, newly added, blocked pending CRM's Team → USMAN migration, no ETA; A10 re-homed 2026-07-27 to the adhoc bucket, no longer counted; A11 done, A12/A13 in-progress, newly added, FE tooling-debt batch, Jira Story pending mint) — the count of Status: done rows over total rows. This is the single number mirrored to the roadmap ## Portfolio progress & forecast row; it grows as Statuses move, with no roadmap edit required beyond refreshing that one derived cell.

Owner rollup (for the mirrors): BE — Ghozi, Julio Jeffer, Berlianto co-own A1–A4; A5 is now split (Julio Jeffer: contact-service/BE-CDP; Ghozi: chat-producer coordination/BE-Chat; Berlianto: squad co-owner backup) per 2026-07-21 sprint planning; Julio Jeffer solo-owns A6, Ghozi solo-owns A7 (CDP-side owners — the CRM/chat squads own the publish/ack halves of those two), and Ghozi solo-owns A9 (blocked, no active work yet). Azani Ramadhan solo-owns A11/A12/A13 (FE tooling debt — pixel3 version, pnpm upgrade, TypeScript pin; matches the Jira Tasks' current assignee). QA — Hendry (all, Lane B); FE — not yet assigned for A5 (the only pre-existing item with real Frontend scope, in qontak-customer-fe — needs an FE owner before it can be pulled) and Mobile scope for A5 also needs confirming. Jovi solo-owns A8's FE/DRI (🔴 Expedite, queued in his Sprint 2 plan); BE owner for A8 remains TBD.

A5 is not the same problem as A6/A7, despite sharing Ghozi/Jeffer, the CDP↔Chat/CRM boundary, and (as of 2026-07-21) the same 🔴 Expedite class: A6/A7 build a missing back-sync link so CRM/Chat recognize a narrow subset of contacts (CDP-originated, Qontak One only) exist at all — the failure mode is absence. A5 fixes the Contact.Source field's own ID-vs-label representation for every contact regardless of origin or tenant — the failure mode is inconsistency (wrong/mismatched values), not a missing link. A5's fix is almost entirely internal to contact-service; A6/A7 are each a full cross-system sync feature. Do not conflate them when reading the tracker or the sprint queues.

The whole BE squad shares the EOL items A1–A4, so those rows' BE and DRI cells carry all three (comma-separated); the linter reconciles each name individually against the README implementors union — no drift.

Expedite log

Append-only record of the bucket's 🔴 Expedite classifications. Classifying a story Expedite is free; the act of pulling one in flight (in-progress/in-review) — where preemption actually happens — must have a row here (linter BUCKET_EXPEDITE_UNJUSTIFIED). Preempting a ranked initiative is also persisted into the displaced initiative's dated delivery/status/. WIP limit: expedite_wip_limit: 2 in flight at once (CDP's chosen cap; linter BUCKET_EXPEDITE_WIP).

StoryDateAuthorized byPreemptsReason
A22026-07-10Ghozi Humama— (backlog)Security-facing: Alpine 3.18 is EOL (2025-05-01) and receives no security fixes; classed 🔴 Expedite so it may preempt ranked work when pulled. Currently backlog — no preemption yet, so no ranked initiative is displaced. Add the Preempts target + a note in that initiative's dated status/ if/when it is pulled in flight (WIP ≤ 2).
A72026-07-21Zhelia Alifa (PM — set both TF-3517/TF-3526 High-P1 and requested Sprint 1–2 scheduling in the 2026-07-17 team thread; engineering acked)Ghozi's ranked Sprint-2 queue (Export BE, P3 — TF-3526 went In Progress 2026-07-21 while that queue is open; TPM: record a dated status/ note in the displaced initiative if this proves a true preemption rather than gap-fill)Customer-facing tech debt: CDP-created contacts (manual/bulk add) get no chat_data back-sync, so a real client cannot select them as campaign recipients. Sibling A6 (TF-3517, crm_data_id) is classed 🔴 with it — same root cause, BI-reported data-integrity impact — but A6 is still backlog (Julio Jeffer, queued under his Segmentation close-out in Sprint 2), so no pull row for it yet. In-flight 🔴 count: 1 of the WIP ≤ 2 cap (A2 is backlog).
A52026-07-21Sprint 2 planning (reclassified 🟡 Standard → 🔴 Expedite; blocker, not just prioritized; also prioritized ahead of sibling Expedite item A6 the same day)— (backlog)Blocking, not just prioritized: per the RFC's "Cost of doing nothing," source sits on a deletable non-default field for the still-unreconciled company population (irreversible per-company data-loss risk), search-by-source is already silently incomplete for most contacts, segments/broadcasts can silently misfire on the wrong source value, and CRM's Crm::Source registry keeps fragmenting — all live today, not hypothetical. TF-2991 (Phase 0) is a zero-code fix whose free window shrinks daily it isn't run. Outranks A6 in Jeffer's pull order per the same 2026-07-21 call (both Expedite; A5 goes first) — see the Sprint 2 plan. Currently backlog — no preemption yet. In-flight 🔴 count: 1 of the WIP ≤ 2 cap (A7 in-progress; A2/A6 backlog) — pulling A5 in flight alongside A7 hits the cap; do not also pull A2/A6 without closing one out first.
A10 (re-homed)2026-07-21Sprint 2 planning direction (classified 🔴 Expedite and placed in Ghozi's Sprint 2 plan on explicit instruction)— (backlog)Re-homed 2026-07-27 to the adhoc bucket as its A6 (tracker, Story TF-3609 under Epic TF-3477) — row retained because this log is append-only; it no longer counts against this bucket's WIP. The unresolved classification travels with it. No documented risk/urgency analysis yet — unlike every other Expedite item in this bucket, this classification was made directly rather than derived from a scoped problem. The ask itself (Chat's Sticky Agents needs CDP support for set/remove assignment actions) is an intake placeholder with no confirmed contract, entity, or repo surface. Currently backlog — no preemption yet. In-flight 🔴 count unaffected (still 1 of the WIP ≤ 2 cap, A7 in-progress). Revisit this classification once the Chat squad's actual requirement is scoped — confirm it stays Expedite or reclassify with a real justification.

Pull rule (deterministic — how an item gets scheduled)

A bucket takes no ## Priority rank — it holds stories of incompatible urgency (a 🔴 Expedite production-latency fix beside a ⚪ Intangible test-coverage chore), so no single integer can order them. Handling is a per-story pull rule, run off data already captured above (Class / Scope / owner) × capacity.md load × the sprint queue's blocked flags: an item is pulled when the owner's ranked (P1…Pn) queue is blocked or between items — unless it is 🔴 Expedite, which may interrupt ranked work (bounded by expedite_wip_limit + the Expedite log). 🟡 Standard / ⚪ Intangible never run ahead of ranked work; 📅 Fixed-Date interrupts only as its date nears. Among eligible stories the pull order is Class → closeability (can it reach done this window with current staffing) → domain affinity. Generic contract: .claude/reference/adhoc-interrupt-doctrine.md.

How the bucket grows (adding a new item)

The bucket absorbs new engineering requests mid-quarter without touching the roadmap ## Priority ranks. To add one:

  1. Author the RFC under rfcs/ (type: RFC — engineering work is an RFC, not a PRD; lightweight is fine — context, proposed change, execution plan, risks).
  2. Append a tracker row above with the next free Story id (A5, A6, …), its Class, per-item DRI, Status: backlog, today's Added date, Scope, discipline owners, Jira Story key, and summary linking the RFC.
  3. Mint the Jira Story under Epic TF-3500 (one Story per item, never per-AC — use jira-alignment-check), and mirror its key into the row.
  4. Mirror staffing into the roadmap Implementors/QA (union), capacity.md Allocation (engineering-q3-2026 (DISC) TBD — no man-days), and, if worked this sprint, the sprint queue. If it's 🔴 Expedite and pulled in flight, add an ## Expedite log row (WIP ≤2) and, if it displaces ranked work, a note in that initiative's dated delivery/status/.
  5. Refresh the rollups — the tracker's Progress line (done ÷ total), the roadmap forecast % done cell, and the roadmap ## Adhoc / Interrupt lane class buckets. The bucket's lane row, Portfolio row and Epic do not change — that stability under growth is the point.

What you never do: give a new item its own roadmap ## Priority rank (a bucket is classed, not ranked — that would renumber P1…Pn on every request), or split the bucket into multiple Epics.

QA Lane

Lane B (keeps a human QA gate) — advisory, as of 2026-07-10.

  • Basis: team-policy safe default. Every CDP initiative is Lane B, and engineering items here (server/infra migration, EOL upgrades, performance) can carry data-integrity or availability risk that reaches paying customers.
  • Owner: Ghozi Humama (BE coordinator). Scope: each item's RFC execution/verification steps; QA runs per-item as each is pulled.
  • Set as the conservative default without a full qa-lane-advisor pass. A genuinely low-risk item (e.g. a pure unit-test-coverage push) may warrant a lighter per-story gate — re-confirm with qa-lane-advisor when the item is added. Advisory-only; no CI gate.