RFC Review: Enable Team Owner Field & Team Permission in CDP
Companion review for
rfc-team-owner-field-and-team-permission.md, produced by therfc-reviewerskill. Cumulative across cycles R1 → R2; valid for the RFC revision inreviewed_commit(dce8b7d).
Executive Summary
- Overall Score:
8.5/10(was6.5at R1) - Rating:
Agentic-Ready(wasNeeds Work) - RFC Type:
full-stack - Sub-Type:
new-feature(frontend) +new-feature(backend) - Assessment Confidence:
High - Applied Caps/Gates: none — the R1 cross-layer-contract cap (6.5) is lifted: the teams-for-user contract is now pinned normatively in §2.4 and Detail 2.G is all-
yes. ACV ≥ 5.0; deploy order specified; OBS cap cleared (FE analytics added); 0 categories below 5.0. - Implementation Readiness Verdict:
PROCEED— with one delivery note: GA waits on Launchpad shipping the §2.4 teams-for-user endpoint + the Team Only radio (CHG-001). That is a build dependency, not an agent-blocking spec gap; all in-repo chunks (BE 1–7, FE 8–9, Mobile 10) are executable now. - Report Path:
cdp/team-owner-field-and-team-permission/rfcs/rfc-team-owner-field-and-team-permission-review.md - RFC Author: Zhelia Alifa | Reviewed: 2026-06-18 (R2)
R2 confirms every R1 finding (REV-1..8) is resolved. The decisive change is REV-1: the teams-for-user(+descendants) contract is now an authoritative spec in §2.4 (path, params, full 200 schema, error/empty/timeout semantics, caching) that both the CDP consumer and the FE picker build against — so the cross-layer ambiguity that capped the RFC at 6.5 is gone and Detail 2.G reads all-yes. The dangling decisions also closed: descendants expand in Launchpad (OQ-3a), notes broadening confirmed and flag-gated (OQ-11), and the migration is a CDP-owned backfill job (OQ-1). The one honest residual is that Launchpad must still deliver the endpoint it's been handed a contract for — tracked as REV-9 (accepted-risk), and correctly gated behind cdp_team_permission_enabled = OFF. An agent can implement both layers from this RFC without a clarification meeting.
Quick Verdict
Why this RFC can be implemented agentically:
- The previously-missing dependency contract is now fully pinned (§2.4): an agent writes the CDP HTTP client + cache and the FE
getMyTeams()against one agreed shape — no field-name guessing. - All major decisions are closed (10 ADR blocks + the 3 ex-open ones now resolved); backend authorization is specified to file:line with a single evaluation authority.
- 11-chunk execution plan with repo-sourced commands and assertable AC; clean cross-layer rollout matrix; flag kill-switch.
Why an agent might still pause (notes, not blockers):
- The teams-for-user endpoint is RFC-defined but not yet Launchpad-ratified/shipped (REV-9, accepted-risk) — in-repo chunks proceed against the contract/a stub; end-to-end GA waits on delivery.
- Backend perf baseline numbers are intentionally "measure before build" (REV-6 resolution) rather than pre-stated absolutes — correct for an unmeasured tenant, but the agent must capture them, not invent them.
Findings Ledger (carry-forward)
R1 ids carried forward, statuses updated. 0 open material findings after R2; one new accepted-risk (REV-9) tracks the external delivery dependency.
| ID | Severity | Finding (one line) | RFC location | Status | First seen | Resolved in | Evidence / fix |
|---|---|---|---|---|---|---|---|
REV-1 | blocker | Teams-for-user(+descendants) contract proposed, not agreed | §2.4 Dependency endpoint, Detail 2.G | fixed | R1 | R2 / dce8b7d | §2.4 now pins normative contract (path, 200 schema, errors, empty, 500ms timeout, cache key); Detail 2.G flipped to all-yes. Residual delivery → REV-9 |
REV-2 | major | Figma frames TBD for all surfaces | §1 Design References | fixed | R1 | R2 / dce8b7d | Declared no net-new visual — every CDP FE surface is a pixel-faithful reuse (fork of MultipleSelect.vue; sidebar clone); gate cleared, not deferred |
REV-3 | major | Notes update/delete broadening unconfirmed | Decision 8, OQ-11 | fixed | R1 | R2 / dce8b7d | OQ-11 resolved = yes (flag-gated); Decision 8 + Stories 14/16 encode it |
REV-4 | major | Migration ownership unresolved | CHG-004, OQ-1, ch 11 | fixed | R1 | R2 / dce8b7d | Chosen: CDP-owned backfill job (not Bifrost), with rationale + reversibility in §4 Rollout |
REV-5 | minor | Level-update path differs from PRD; gateway unconfirmed | §1 Deps, Detail 2.F.1 | fixed | R1 | R2 / dce8b7d | Documented no CDP impact (CDP reads crs_permissions; admin UI Launchpad-owned); closed for this RFC |
REV-6 | minor | No throughput / cache / load-test numbers | §3 Performance | fixed | R1 | R2 / dce8b7d | Added: no-new-RPS reasoning, ≥95% cache-hit assumption, 500ms client timeout, pooled client, 1×/2× load test + cold-cache + degraded-Launchpad runs (baseline to be measured) |
REV-7 | minor | FE analytics events absent | §3 Monitoring | fixed | R1 | R2 / dce8b7d | Added 4 FE analytics events with properties; OBS cap lifted |
REV-8 | minor | Browser support matrix not stated | §3 Performance (FE) | fixed | R1 | R2 / dce8b7d | Stated: inherits existing qontak-customer-fe matrix; no new constraint |
REV-9 | minor | teams-for-user endpoint RFC-defined but not yet Launchpad-ratified/shipped | §1 Deps, §2.4 | accepted-risk | R2 | — | Correctly gated behind cdp_team_permission_enabled = OFF; in-repo chunks proceed against the pinned contract. Delivery + ratification is a Launchpad checkpoint, not a CDP spec gap |
Ledger summary: 0 open, 8 fixed this cycle (R1→R2), 1 accepted-risk (REV-9). No material finding remains to promote into the RFC Open-Questions table; REV-9 is mirrored in the RFC §5 "External delivery dependencies" block.
PRD → RFC Traceability Matrix
| PRD Element | RFC Section | Coverage |
|---|---|---|
| CHG-001 Team Only radio (Launchpad) | TEAM-S01, §1 Deps, Detail 2.F.1 | Full (cross-squad, no CDP impact — REV-5) |
| CHG-002 Enforce TEAM ONLY (4 actions) | Decisions 1/2/5/7, §2.4, ch 2–6 | Full |
| CHG-003 Team Owner field | Decision 3, §2.3, §2.A, ch 1/8 | Full |
| CHG-004 Migration | CHG-004 → Detail 2.F, ch 11 | Full (was Partial — owner now CDP job, REV-4) |
| CHG-005 Dedicated flag | Decision 9, §4 | Full |
| CHG-006 Notes TEAM ONLY | Decision 8, §2.4 notes, ch 7 | Full (OQ-11 resolved, REV-3) |
| FE-1..6 / MB-1..2 | Detail 1.C, §2.0 anchors | Full |
| Stories 1–17 (AC) | Detail 1.A + 1.C | Full — 17/17 |
| §6.5 Observability | §3 Monitoring (BE events + FE analytics) | Full (FE added, REV-7) |
| Hierarchy/Unassigned (D-2/7/13) | Decision 2, §2.2 | Full |
| Teams-for-user dependency (§7) | §2.4 (pinned), §1 Deps | Full (was Partial — contract pinned, REV-1) |
Summary: 13 of 13 PRD elements fully covered (was 11/13 at R1). 0 RFC decisions without a PRD driver.
Scorecard
Full-Stack Scorecard (18 categories) — R2 (Δ vs R1)
| # | Category | Source | Score | Δ | Evidence-Based Rationale |
|---|---|---|---|---|---|
| 1 | PRT — PRD Traceability | Merged | 9.0 | = | 13/13 full; forward+reverse + 17/17 stories in Detail 1.C |
| 2 | TDC — Technical Decisions | Merged | 9.0 | +0.5 | 10 ADR blocks; the 3 ex-open decisions (OQ-3a/OQ-1/OQ-11) now resolved with rationale |
| 3 | CNT — Contract Specificity | FE | 8.0 | +0.5 | TeamOwnerSelectProps typed + emit ids; pixel-faithful reuse declared (no net-new design gap) |
| 4 | SCB — Scope Boundaries | FE | 9.0 | = | Detail 2.I create/modify/NOT-touched; component forked not edited |
| 5 | DEP — Dependencies | FE | 8.5 | +0.5 | teams-for-user now contract-defined (still needs-building, but spec'd) |
| 6 | NFS — Non-Functional | FE | 8.0 | +1.0 | a11y + perf + browser matrix now stated (REV-8) |
| 7 | TPS — Test Plan | FE | 8.0 | = | repo-sourced commands; cross-layer row |
| 8 | DMS — Data Model & Schema | BE | 8.5 | = | JSON migration 033 up/down, multikey index, cardinality, PII, examples |
| 9 | ACV — API Contract | BE | 8.5 | +2.0 | CDP endpoints fully tabled plus the dependency endpoint now fully specified (schema, errors, empty, timeout, caching) — REV-1 |
| 10 | DIC — Data Integrity | BE | 8.0 | = | single-doc strong consistency; idempotent backfill |
| 11 | FMC — Failure Mode | Merged | 8.5 | = | fallback-to-OWNED + 403/422 catalog + FE error catalog; codes match |
| 12 | CSS — Concurrency & Scaling | BE | 8.0 | +1.0 | throughput model + ≥95% cache-hit + 500ms timeout + load-test plan (REV-6) |
| 13 | SAS — Security & Authorization | BE | 9.0 | = | Role × Endpoint matrix; per-element 422; injection/SSRF/tenancy; staticcheck |
| 14 | ROL — Rollout & Rollback | Merged | 9.0 | +0.5 | deploy order; clean compat matrix; flag kill-switch; migration owner = CDP (REV-4) |
| 15 | OBS — Observability | Merged | 8.5 | +1.5 | BE 4 events + alerts and FE 4 analytics events (REV-7) — cap lifted |
| 16 | SBC — Service Boundary | BE | 8.5 | = | sync path, no new queue, routes reused/extended, Launchpad boundary explicit |
| 17 | CPA — Pattern Alignment | Merged | 9.0 | = | FE + BE patterns cited per concern; snake_case both sides |
| 18 | CDG — Compliance | BE | N/A | = | new field is non-PII; contact-PII handling unchanged |
Overall (judgment): 8.5 — Agentic-Ready. Held just at the floor of the top band (not higher) because CNT and DIC sit at 8.0 (< 8.5), so the "9.0+ requires ACV/DIC/FMC/CNT all ≥ 8.5" gate isn't met — and REV-9 (un-shipped dependency) is a real, if non-blocking, operational risk.
Resource & Cost Advisory
- Unchanged: no new pods; +1 cached Launchpad call/viewer/TTL; one small multikey index. Advisory only.
Decision Closure Assessment
Decision Index (R2)
| # | Decision | Status R1 → R2 | Notes |
|---|---|---|---|
| 1 | Scope on stored team_owner_ids | Resolved → Resolved | — |
| 2 | TEAM ONLY rule + fail-to-OWNED | Resolved → Resolved | — |
| 3 | Multi-select, emit ids | Resolved → Resolved | no net-new visual (REV-2) |
| 4 | Expand descendants in Launchpad | Partial → Resolved | OQ-3a closed; §2.4 returns expanded_team_ids |
| 5 | $in via bson:"-" field | Resolved → Resolved | — |
| 6 | Multikey index 033 | Resolved → Resolved | — |
| 7 | Extend EvaluatePermissions | Resolved → Resolved | — |
| 8 | Notes inherit parent scope | Partial → Resolved | OQ-11 confirmed (flag-gated broadening) |
| 9 | Dedicated flag (default OFF) | Resolved → Resolved | OQ-8 single-bool default, non-blocking |
| 10 | Picker from user's own teams | Partial → Resolved | source = pinned teams-for-user teams[] |
| — | Migration ownership | Dangling → Resolved | CDP backfill job (REV-4) |
Aggregate: 11 of 11 Resolved (was 7 Resolved / 3 Partial / 1 Dangling at R1). No dangling decisions remain.
Cross-Layer Contract Verification (R2)
| Endpoint | Backend Response | Frontend Expected | Match? | Gaps |
|---|---|---|---|---|
GET /cdp/customers* | Contact{…, team_owner_ids:[]string} | Contact{…, team_owner_ids?: string[]} | Yes | — |
PATCH /cdp/customers/{id} | accepts team_owner_ids, 422 | sends ids, handles 422 revert | Yes | — |
| notes list/get | Note{…, permission:{update,delete}} | web note.permission.*; mobile NoteCdpPermissionResponse | Yes | mobile extends gating to add/edit (ch 10) |
| teams-for-user (LP→CDP) | {data:{team_ids, expanded_team_ids, teams[]}} (§2.4, pinned) | CDP expanded_team_ids; FE teams[] | Yes | contract pinned; Launchpad delivery = REV-9 |
Mismatches found: 0 (was 1 at R1). No cap applies.
Cross-Layer Rollout Compatibility Matrix
Unchanged from R1 — all scenarios Yes, deploy order BE→FE (mobile independent, flag last), 0 incompatible scenarios. Migration owner now named (CDP).
Strengths
- REV-1 fully closed at the spec level (§2.4). The dependency contract is now authoritative — path
GET /private/users/{user_sso_id}/teams?include_descendants=true, full200schema withteam_ids/expanded_team_ids/teams[], explicit empty-array-for-teamless, 401/404/500 + 500ms-timeout→fallback semantics, and the(company,user)cache key. This is the single change that lifts the RFC from HOLD to PROCEED. - All decisions closed (11/11). Descendant expansion, notes broadening, and migration ownership — the three soft spots at R1 — now have explicit decisions with rationale and reversibility.
- Honesty preserved. The author didn't paper over the residual: Launchpad delivery is tracked as REV-9 (accepted-risk) and the feature is correctly dark behind
cdp_team_permission_enabled = OFF.
Biggest Gaps (now minor)
- REV-9 (accepted-risk) — Launchpad must still build the endpoint to the §2.4 contract; until then GA is blocked (but in-repo execution is not).
- REV-6 residual — backend perf baseline is "measure before build"; an agent must capture the
everythingP95/RPS, not assume them. - CNT/DIC at 8.0 — both solid; reaching 9.0+ overall would need e.g. a typed FE 422-handling contract example and an explicit duplicate-write note — polish, not blockers.
Priority Actions (post-PROCEED, for GA)
- Launchpad — implement
GET /private/users/{user_sso_id}/teams?include_descendants=trueto the §2.4 schema; ratify the shape (REV-9) and ship the Team Only radio (CHG-001). - CDP — capture the
everythinglist P95 + RPS baseline before enabling the flag (REV-6), so the ≤2s/≤20% targets are measurable. - CDP — run the migration backfill (chunk 11) + the cold-cache and Launchpad-degraded load runs on a QA tenant before per-tenant enable.
Implementation Readiness Checklist
Unblocked (agent can proceed)
- PRD → RFC traceability complete (13/13 full)
- All technical decisions resolved (11/11; 0 dangling)
- Failure modes handled + error catalogs (Detail 3.A/3.B/3.C)
- Configuration contract: flag + cache TTL + Launchpad config
- Rollout plan + clean cross-layer matrix + migration owner (CDP)
- Observability: BE events + alerts and FE analytics events
- Task decomposition with verifiable AC per chunk
- FE interfaces/prop types + 5-state UI matrix; no net-new visual gate
- Browser matrix stated
- Cross-layer contract verified — all rows
Yes - Deploy order specified; feature-flag coordination defined
- End-to-end data flow documented
- Schema at DDL/index precision; transaction boundaries; injection/tenancy
Blocked (must fix before GA, not before agent execution)
- REV-9 Launchpad delivers + ratifies the §2.4 teams-for-user endpoint and ships the Team Only radio
Verdict: Ready to implement (in-repo) — GA gated only on the named Launchpad delivery (REV-9).
Task Manifest
Unchanged from the RFC's Detail 4.D (11 chunks, verified well-formed and dependency-ordered). Chunk 11 (backfill) is now concretely scoped as a CDP-owned job. No re-derivation needed.
Dangling Decisions Log
| # | Decision | Location | Status |
|---|---|---|---|
| — | (none) | — | All 11 decisions Resolved as of R2 |
Open Questions
| # | Question | Category | Severity |
|---|---|---|---|
| 1 | Will Launchpad implement + ratify the §2.4 teams-for-user contract and ship CHG-001 on the GA timeline? (REV-9) | DEP / SBC | Important (GA gate, not exec gate) |
| 2 | Capture everything list P95/RPS baseline before flag-on (REV-6 residual)? | CSS | Nice-to-have |
Prior R1 open questions REV-1..8 are all closed (see Findings Ledger).
Evidence Notes
- §2.4 Dependency endpoint — the pinned contract is the pivotal R2 evidence; drove ACV +2.0 and lifted the cross-layer cap.
- Decisions 4 & 8 + §4 Rollout — explicit resolutions of OQ-3a / OQ-11 / OQ-1 drove TDC/ROL up and emptied the dangling-decisions log.
- §3 Performance + Monitoring — added throughput/cache/load-test (CSS) and FE analytics (OBS cap lifted).
- §1 Design References — "no net-new visual" reframing cleared the CNT design gate honestly (reuse, not imagined frames).
Review History
| Cycle | Date | Reviewed RFC revision | Score | Verdict | Findings open → fixed | Notes |
|---|---|---|---|---|---|---|
R1 | 2026-06-18 | last_updated: 2026-06-18 / 169a7da | 6.5 | HOLD | 8 open (1B/3M/4m) | First review. Held to "Needs Work" by the unfinalized teams-for-user contract (REV-1) + pending design (REV-2). |
R2 | 2026-06-18 | last_updated: 2026-06-18 / dce8b7d | 8.5 | PROCEED | 8 fixed, 1 new accepted-risk (REV-9) | All R1 findings resolved: §2.4 contract pinned (cap lifted), 11/11 decisions closed, no net-new visuals, FE analytics + perf + browser matrix added. Residual = Launchpad delivery (REV-9), correctly flag-gated. Score 6.5 → 8.5. |
R2* | 2026-06-18 | last_updated: 2026-06-18 / c874828 | 8.5 | PROCEED | no change | Re-stamp only: Launchpad anchor re-verified on the canonical checkout (Documents/works/qontak-launchpad, main @ 20a3063) — level-update is rest_router.go:172 (not :154 from a stale clone), /private prefix confirmed. Citation fix; no finding/score change. |