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_datafor CDP-originated contacts (A6/A7); contactsourceID/label split (A5); possibleqontak_customer_idlookup 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
sourcesplit (A5); legacy CRM contact page restrict/redirect guard for Qontak One tenants (A8). - Data — field-property reconciliation + contact backfill for the
sourcemigration (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 identityA<n>, assigned in creation order and never reused/renumbered (keys the sprint queues; new items get the next freeA<n>).Class— per-story Class of Service (Kanban): 🔴 Expedite (may preempt ranked work — WIP-limited toexpedite_wip_limit: 2in flight + logged in## Expedite log) · 📅 Fixed-Date (hard external date — reusescommitment_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.DRI— per-item accountable lead (repo-local column — CDP's engineering items vary in ownership, so the DRI is captured per story, not just the container-leveldrifrontmatter above).Status— live completion, the canonical per-story state:backlog→in-progress→in-review→done. The roadmap forecast row's% doneis the rollupdone ÷ totalof this column.BE·FE·QA·Mobile— canonical per-story staffing, one column per discipline; the roadmapImplementors,capacity.mdAllocationand the sprint queues mirror these.—= that discipline isn't in the story'sScope. 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.
| Story | Class | DRI | Status | Added | Scope | BE | FE | QA | Mobile | Jira Story | Summary (→ RFC) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| A1 | 🟡 Standard | Ghozi, Julio Jeffer, Berlianto | backlog | 2026-07-10 | Backend, Infra | Ghozi, Julio Jeffer, Berlianto | — | Hendry | — | TF-3501 | Bump Go 1.25 → 1.26 (approaching EOL ~2026-08) → rfc |
| A2 | 🔴 Expedite | Ghozi, Julio Jeffer, Berlianto | backlog | 2026-07-10 | Backend, Infra | Ghozi, Julio Jeffer, Berlianto | — | Hendry | — | TF-3502 | Upgrade Alpine 3.18 (EOL) → 3.21/3.22 runtime base → rfc |
| A3 | ⚪ Intangible | Ghozi, Julio Jeffer, Berlianto | backlog | 2026-07-10 | Backend | Ghozi, Julio Jeffer, Berlianto | — | Hendry | — | TF-3503 | Migrate off gocraft/work (EOL) — evaluate asynq/river → rfc |
| A4 | 🟡 Standard | Ghozi, Julio Jeffer, Berlianto | in-review | 2026-07-10 | Backend | Ghozi, Julio Jeffer, Berlianto | — | Hendry | — | TF-3504 | Remove 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 Jeffer | backlog | 2026-07-21 | Backend, Frontend, Data | Julio Jeffer, Ghozi, Berlianto | TBD | Hendry | — | TF-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 | 🔴 Expedite | Julio Jeffer | backlog | 2026-07-21 | Backend | Julio Jeffer | — | Hendry | — | TF-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 | 🔴 Expedite | Ghozi | in-progress | 2026-07-21 | Backend | Ghozi | — | Hendry | — | TF-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 | 🔴 Expedite | Jovi | backlog | 2026-07-21 | Frontend, Backend | TBD | Jovi | Hendry | — | TF-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) | Ghozi | backlog | 2026-07-21 | Backend, Data | Ghozi | — | Hendry | — | pending mint | Team 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 | 🟡 Standard | Azani | done | 2026-07-22 | Frontend | — | Azani | Hendry | — | pending 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 | 🟡 Standard | Azani | in-progress | 2026-07-22 | Frontend | — | Azani | Hendry | — | pending mint (Task: TF-3556) | Upgrade pnpm 9.15.3 → 10/11 (qontak-customer-fe — pnpm 9 approaching end of support) → rfc |
| A13 | 🟡 Standard | Azani | in-progress | 2026-07-22 | Frontend | — | Azani | Hendry | — | pending 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
donetracker 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 thedone / totalrollup. 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).
| Story | Date | Authorized by | Preempts | Reason |
|---|---|---|---|---|
| A2 | 2026-07-10 | Ghozi 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). |
| A7 | 2026-07-21 | Zhelia 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). |
| A5 | 2026-07-21 | Sprint 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-21 | Sprint 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:
- Author the RFC under
rfcs/(type: RFC— engineering work is an RFC, not a PRD; lightweight is fine — context, proposed change, execution plan, risks). - Append a tracker row above with the next free
Storyid (A5,A6, …), itsClass, per-itemDRI,Status: backlog, today'sAddeddate,Scope, discipline owners, Jira Story key, and summary linking the RFC. - 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. - Mirror staffing into the roadmap
Implementors/QA(union),capacity.mdAllocation(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 logrow (WIP ≤2) and, if it displaces ranked work, a note in that initiative's dateddelivery/status/. - Refresh the rollups — the tracker's Progress line (
done ÷ total), the roadmap forecast% donecell, and the roadmap## Adhoc / Interrupt laneclass 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-advisorpass. A genuinely low-risk item (e.g. a pure unit-test-coverage push) may warrant a lighter per-story gate — re-confirm withqa-lane-advisorwhen the item is added. Advisory-only; no CI gate.