Package & Menu Cover Image Self-Serve — QA Point-of-View PRD
Companion documents (branch
feat/cover-image-edit, PR #374 — not yet merged tomain):
- Product PRD:
Partner_Cover_Image_SelfServe_20260909000000.md- Frontend spec:
FE_Partner_Cover_Image_SelfServe_20260909000000.md- Backend spec:
BE_Partner_Cover_Image_SelfServe_20260909000000.md- Existing QA test-readiness draft:
QA_Partner_Cover_Image_SelfServe_20260909000000.md— this document supersedes it in structure and scope; see §7 for what carries forward and what’s corrected.- Design: Figma — Partner-Portal-Desktop, node 18795-357
- Prior structure of this document (flat step-by-step test-case tables, filed as
QA_TestCases_Partner_Cover_Image_SelfServe_20260914000000.mdbefore this rename) — superseded in structure, not in substance; see §7.
| Owner | QA (qa-team@hungryhub.com) |
| Status | Rev. 2 — cross-referenced against the full source-document set (product PRD, FE spec, BE spec, and the existing QA draft) and the Figma design. Structured as a QA POV-PRD, consistent with the Birthday Add-On precedent in this knowledge base (QA_POV_PRD_Birthday_AddOn_20260731000000.md). See §7. |
| Real-code status | 0% implemented. Every item in the product PRD’s “What has to be done before launch” checklist, and every item in the FE and BE specs’ own deliverables checklists, is marked “Not started.” Every case below is pre-implementation, not regression coverage. |
1. QA Objective & Scope
Objective: validate that partners can instantly self-serve a package’s cover image and (once designed) a single menu item’s photo — on any package, regardless of who created it or whether it’s toggled on — without those edits ever entering the existing Admin review queue, while the pre-existing whole-package edit flow and its known risks stay untouched, and while the two independent permission gates (restaurant ownership vs. creator ownership) are never conflated with each other or with the new content-availability and status checks this feature introduces.
In scope for v1 QA:
| Surface | What’s being validated |
|---|---|
hh-server | cover_image field across all HhPackage::Package::* subtypes; new single-item PackageMenu#image endpoint (currently routed but unimplemented); audit record for both image edits; change_active ownership check + :partner_full_package_control Flipper override; Api::Partner::RestaurantPackageSerializer’s created_by_admin/can_toggle/can_edit_other_fields; rate limiting; TouchWorker/search-sync propagation parity with Admin-side edits |
Partner Portal UI — most likely book-bite (see §4 Conflict #1) | Package-list row actions, Add/Edit Cover Image panel, Remove-image confirm modal, “Created by Admin” badge + tooltip, toggle/“Edit Package” disabled states |
Explicitly out of scope for v1 — do not write E2E cases against these, do not treat them as CUJs:
- Flow 2’s UI. The product PRD writes Flow 2 (menu-item image edit) up in full with copy and edge cases, and the FE spec’s i18n table even has placeholder keys ready (
partnerMenuImage.edit,.success, error copy) — but no Figma design exists for it, confirmed independently by the product PRD’s own “Still Open” table, the FE spec’s explicit “don’t build UI until design lands” instruction, and the absence of any menu-item-level screen among the reviewed Figma screens. QA scope is backend-only until that changes (CUJ-07). - There is no diner-facing half to this feature at all — every flow is Partner Portal-only, so there’s no separate “out of reach” surface to carve out; the only scope reduction is Flow 2’s UI.
2. Critical User Journeys (CUJs) & Isolated Risk Vectors
Ordered by priority. Every image-edit CUJ is P0 because the product PRD’s own framing treats instant, creator-agnostic image editing as the entire point of the feature (the North Star metric is a 50% drop in Admin-side manual image-update requests) — a failure in any of CUJ-01 through CUJ-04 defeats that metric directly, not just a UI polish item.
CUJ-01 — Partner sets a package’s cover image, instantly, from the list-row action — P0
From the package list, a partner clicks the row’s “Add Cover Image” (no image set) or “Edit Cover Image” (image already set) action, which opens a slide-over panel — not a detail-screen navigation, correcting the product PRD’s stated trigger (§4 Conflict #2). Partner selects a valid file (jpeg/jpg/png/heic/tif/tiff/webp, ≤10MB); Save enables once a real change is staged (§3.2 covers the exact dirty-state rule, which is itself unconfirmed). On Save: cover_image updates directly on the relevant HhPackage::Package::* subtype, no PackageSubmission/PackageDraftVersion is created or touched, a lightweight audit record is written, TouchWorker/search-sync fires the same as an Admin-side edit, the row’s action label flips from “Add” to “Edit,” a toast (“Great success! / Your cover image is updated.”) confirms, and the partner_cover_image_updated analytics event fires. Assert this succeeds identically across all 9 package types and regardless of creator (Admin-created vs. partner-created) — gate #2 (package_created_by_partner?) must never apply to this endpoint.
CUJ-02 — Partner removes an existing cover image via a two-step confirm — P0
Not documented anywhere in the product PRD’s Flow 1 requirements, but fully designed and explicitly listed as in-scope in the BE spec’s deliverables (“supports set + remove”): from the Edit-mode panel, “Remove Image” opens a separate centered modal (“Are you sure you want to remove this cover image?”, Back/Remove). “Back” returns to the panel with the image untouched; “Remove” clears cover_image server-side (exact request shape unconfirmed — §3.7), reverts the row to “Add Cover Image,” falls back to the placeholder, and writes its own audit entry (action_type = remove, distinct from set). Assert removal on a package with active/upcoming bookings doesn’t break the booking-confirmation display.
CUJ-03 — Row action visibility is gated by package status, never by creator — P0
Two independent facts, one CUJ: (a) the Add/Edit Cover Image action renders for Active and Inactive rows and is absent entirely (omitted from the Action column, not disabled) for Pending Review and Rejected rows; (b) that gate is completely orthogonal to who created the package — an Admin-created, Active package shows the action exactly like a partner-created one, even though the same row’s toggle and “Edit Package” button are independently muted for the creator gate (CUJ-05). Don’t let a test suite conflate these two gates; they use different UI patterns (hidden vs. disabled-with-tooltip, §3.1) precisely because they’re different rules.
CUJ-04 — Status gate has no confirmed server-side enforcement (backend-only integration test, not a UI journey) — P0
The product PRD’s own “Still Open” table lists this as an unresolved question (“confirm whether the backend endpoint should also enforce a status gate or whether it’s UI-only”); the BE spec states a working assumption (“UI-only for v1 … flag to confirm with PM/Eng”) without treating it as decided. Only the existing QA draft accompanying the same PR records it as a settled decision — this document does not inherit that framing, since neither of the two documents actually authoritative over the requirement (the product PRD) or the implementation (the BE spec) treat it as closed. Assert directly against the endpoint (bypassing the UI): PATCH a cover-image change for a package in Pending Review or Rejected status. Until PM/Eng explicitly sign off on relying on client-side hiding alone for a mutation endpoint, a 200 response here should be treated as an open P0 gap, not accepted behavior. This is the full extent of server-side status-gate testing possible until that sign-off happens — there’s no UI path to reach this state, since the action is never rendered for these rows in the first place.
CUJ-05 — change_active tightens to creator-only, with a per-restaurant Flipper escape hatch — P0
POST /api/partner/v1/restaurant_packages/change_active has no ownership check today, so this is an intentional permission tightening, not a bug fix — any partner currently toggling an Admin-created package loses that ability by default and this must be called out in release notes, not silently absorbed as a regression. Add the same package_created_by_partner? guard UpdatePackageService already uses; gate it behind Flipper.enabled?(:partner_full_package_control, restaurant) so a restaurant with the flag on regains full toggle + non-image-field-edit control over every package under it, Admin-created or not, reversible without an app release. In the UI, this reads as the toggle and “Edit Package” button rendering in a muted state with an explanatory tooltip for gated packages — not fully hidden, which is what the product PRD’s Flow 5 edge-case text claims should happen (§4 Conflict #3). Assert can_toggle/can_edit_other_fields on the serializer are computed as package_created_by_partner?(package) || Flipper.enabled?(...) in both flag states, and that flipping the flag re-renders both controls as interactive with no redeploy.
CUJ-06 — “Created by Admin” badge never drifts from the real permission state — P0
The badge is purely informational (created_by_admin = !package_created_by_partner?(package)) and must keep rendering even once the Flipper override makes a package’s toggle/edit controls interactive — the product PRD is explicit that the badge and the actual controls must never visually contradict each other. Assert the badge is driven solely by created_by_admin, the controls solely by can_toggle/can_edit_other_fields, that no code path lets them diverge across every creator × flag-state combination, and that the partner_created_by_admin_badge_viewed analytics event fires on badge impression (not merely on hover or tap). Also assert the badge never appears at the menu-item level, and — pending the FE spec’s own open question on this — confirm whether a package detail screen (if one exists at all, see §4 Conflict #1) needs to show the badge too, or only the list row.
CUJ-07 — Flow 2 menu-item image edit (backend-only integration test, no UI consumer yet) — P0
Mirrors CUJ-04’s framing: there is no design to walk this through end-to-end, so this is tested as an isolated API contract. Implement the currently-missing PATCH/PUT /api/partner/v1/package_menus/:id action (the route exists; the controller action doesn’t) scoped to image, using HhPackage::PackageMenuUploader’s actual allowed set — {jpg, jpeg, png, pdf, heic}, which differs from the cover-image set (no tiff/webp, but PDF is allowed) — don’t reuse the cover-image error copy verbatim (the FE spec’s own i18n table already keeps them as distinct keys, partnerMenuImage.error.* vs. partnerCoverImage.error.*). Assert the restaurant-ownership check this same controller’s existing create action is missing today gets fixed here too; assert no creator check and no menu_type check apply (an Admin-created package’s item, or an hh_menu-nested item under a section with no direct package_id, must resolve ownership correctly); assert an imenupro-only package with zero package_menus records returns an empty, non-403 result — a content-availability state, not a permission block. This is the full extent of Flow 2 testing possible in v1 — re-derive UI-level cases from this contract once FE has a design to build against.
CUJ-08 — The pre-existing whole-package edit flow (Flow 3) keeps working, unaffected — P1
Not new functionality — included so it isn’t mistaken for replaced functionality. PATCH restaurant_packages/:id with package_menus_attributes, for partner-created packages only, must still queue into the normal PackageSubmission/PackageDraftVersion review flow exactly as today, including its known, not-fixed-by-this-feature destroy-by-omission risk (mark_missing_nested_records_for_destroy! deletes any existing menu item omitted from a resubmitted array — the product PRD’s own Risks table logs this as pre-existing, flagged for a future defensive fix, not blocking this feature). Assert the new Flow 1/Flow 2 endpoints don’t interfere with an in-flight Flow 3 submission on the same package, beyond the specific clobber risk tracked separately in §3.4.
3. Architectural & Technical Risks
3.1 Two permission-UI patterns exist and must not share test assertions
The status gate (CUJ-03) omits the cover-image action from the Action column entirely for Pending Review/Rejected rows. The creator gate (CUJ-05) instead renders the toggle and “Edit Package” button in a muted, disabled state with a hover tooltip for Admin-created rows. These look superficially similar (“something’s locked”) but are different mechanisms gating different things for different reasons — a shared “action is unavailable” assertion across both will mask a regression that flips one pattern into the other.
3.2 Save-button dirty-state machine is explicitly unresolved, not just under-specified
This isn’t an inference from a screenshot — the FE spec’s own open-questions list names this exact gap: “design draws Save gray in 4 of 7 modal copies. Is Save disabled until a new image is selected, or only while uploading? … in Edit mode with an image present, is Save re-enabled on any change?” Don’t treat any single design frame’s Save state as the confirmed rule; get the state machine confirmed with FE before automating it.
3.3 The “420×240px recommended” hint has no backing validation rule
The slide-over shows “Recommended dimensions: 420x240px (Max size: 10 MB),” but nothing in the product PRD’s validation rules mentions pixel dimensions — only format and the 10MB size cap are real requirements. Treat the dimension text as cosmetic. If the backend actually rejects an off-dimension image, that’s a new finding to escalate, not confirmation of an existing spec.
3.4 Pending-draft clobber — unresolved, and independently flagged by both the product PRD and the BE spec
If a partner has an in-flight Flow 3 submission (e.g., a pricing change) that happens to carry a stale cover_image snapshot, and then makes a newer instant cover-image edit via Flow 1, it’s unconfirmed whether approving that older submission later overwrites the newer image with the stale one. The product PRD’s Risks table and the BE spec’s edge-case list both flag this independently, with the BE spec suggesting the likely fix is “a conditional skip on the image field during draft application” — but neither confirms it’s actually implemented that way. Record actual behavior (CUJ-01’s instant-apply assertion plus a follow-up approval step) and escalate as a defect only if the newer image is actually lost.
3.5 Rate limit: the numeric value is decided, the implementation mechanism isn’t
The product PRD’s Design Decisions table resolves the limit itself — 10 image changes per package/menu item per rolling 24h — as a settled decision, not a worked example. What’s still open, per the BE spec’s own edge-case list, is the mechanism: “per-package vs. per-menu-item? Rolling 24h window via Redis?” Confirm the storage/counting mechanism and the rejection response shape (429 vs. an in-band soft error) before writing a strict boundary assertion. Separately, concurrent edits are decided as last-write-wins server-side, logged on both sides in the audit trail — but the FE spec’s own open questions flag that the client-side UX during a race (optimistic update vs. waiting for server confirmation) is a distinct, still-undecided question from the backend guarantee.
3.6 The audit trail’s storage mechanism, not just its field sufficiency, is unresolved
Because image edits never enter the review queue, this lightweight record is the only place Admin/Support can see who changed an image and when — and the BE spec’s own edge-case list asks the literal open question: “should the audit record reuse PackageSubmission with approval_status: approved, or a new lightweight table?” Until that’s answered, CUJ-01/02’s “audit record created” assertion can’t be automated against a concrete schema. Once it is, separately confirm the field set (restaurant_id, package_id, actor, timestamp, action_type) is actually sufficient for a real support lookup.
3.7 Cover-image removal request shape is unresolved
The BE spec’s edge-case list asks directly: cover_image: null in the same endpoint, or a dedicated DELETE sub-action? And after clearing, does image_cover_url fall back the same way an always-empty cover image would, or does the fallback need TouchWorker/search-sync to re-fire explicitly? CUJ-02 needs this resolved before its request/response contract can be automated.
3.8 Two low-severity design wobbles worth a lint-style note, not a blocking defect
Per the FE spec’s own review of the Figma file: the “Change Image” overlay text is authored as camelCase (“change Image”) in the mock — cosmetic, fix before build, not worth a functional test case. The Remove-confirm modal’s underlying component is a copy of an unrelated “Reject this booking” modal — also cosmetic/implementation-detail only, per the FE spec, and shouldn’t surface as a user-visible difference once built fresh. Worth a one-line check in a design-QA pass, not a CUJ.
4. Spec Conflicts & Assumptions
Every item below is sourced from an actual disagreement between the real documents/design, not an invented ambiguity.
Conflict #1 — Platform: native Flutter app (product PRD) vs. desktop web Partner Portal (Figma, and the FE spec itself) — three signals point one way, the PRD text hasn’t caught up.
The product PRD’s “Platform” field reads “App (Partner Portal / hungryhubpartner, backed by api/partner/v1),” and its Risks table separately lists “App release cadence for Partner Portal UI … UI needs an app release to reach users” as a real delivery risk. But the Figma file is titled Partner-Portal-Desktop and shows a desktop web UI, and — the most concrete signal — the FE spec itself, written by the frontend team against that same Figma, scopes the entire build to book-bite, proposing Vue single-file components (MainPackageList.vue, CoverImageModal.vue, AdminBadge.vue, etc.) under book-bite’s existing packages module. Three independent sources (design, and the FE team’s own spec) point to a web book-bite build; only the PRD’s Platform field and Risks row still describe a native Flutter app. The most likely explanation is that the Platform field is stale/templated rather than a deliberate scoping decision — but it’s still on record and unconfirmed, so §5/§6 treat FE automation as highly likely to be Playwright against book-bite, pending a one-line PM confirmation that the Platform field is stale, rather than as a fully open architectural question.
Conflict #2 — Entry point and Remove flow: PRD Flow 1 text vs. the actual design. The PRD’s Flow 1 trigger reads “Partner taps ‘Edit Cover Image’ on a package’s detail screen,” and the PRD’s Flow 1 requirements never mention a remove/clear capability at all — both gaps are independently confirmed as open by the PRD’s own “Still Open” table and the FE spec’s own open-questions list (“design only shows list row action; PRD mentions detail screen — confirm whether both”). The actual design shows a list-row action opening a slide-over, plus a fully designed Remove Image flow with its own confirmation modal (CUJ-01, CUJ-02). Test cases in this document follow the design as the more concretely verified source; the PRD text should be corrected by PM.
Conflict #3 — Toggle visibility: “never shown, not even disabled” (PRD) vs. muted-but-visible-with-tooltip (design). The PRD’s Flow 5 edge case states packages with no self-serve path “should never show a toggle… not even disabled.” The only Admin-created row captured in the design (a Party Pack, which is a self-serve-creatable type) instead shows the toggle rendered in a muted color with an explanatory tooltip, and shows “Edit Package” as a grayed-out (not hidden) button. It’s unconfirmed whether genuinely non-self-serve-creatable types (Xperience, DIY Set, etc.) get the fully-hidden treatment the PRD describes, or the same muted-but-visible pattern this creator-gated-but-creatable example shows. Confirm with design before finalizing CUJ-05’s assertions for those package types specifically.
Conflict #4 — Status-gate enforcement layer: the product PRD and BE spec both call it open; only the existing QA draft calls it settled. See CUJ-04 for the full framing — restated here because it’s a genuine three-document disagreement, not a two-way one: the product PRD’s “Still Open” table lists it as unresolved, the BE spec states a working assumption pending confirmation, and only the QA draft accompanying the same PR treats “UI-only, endpoint stays permissive” as a decision already made. This document sides with the two documents that are actually authoritative over the requirement and the implementation, not the QA draft that inherited an unconfirmed assumption as settled.
Conflict #5 — Tooltip copy: “photo” (design and FE spec’s i18n table) vs. “photos” (PRD’s UX Writing table) — two of three sources agree.
The design’s captured tooltip and the FE spec’s own partnerAdminBadge.tooltip key both read “…you can still update its photo — turning it on/off…” (singular, em dash). Only the PRD’s UX Writing table uses “…update its photos, turning it on/off…” (plural, comma). With design and the FE i18n table in agreement, “photo” (singular) is the more likely correct final copy — but get it explicitly signed off before CUJ-06’s tooltip assertion is automated against a literal string, rather than assuming the majority source wins by default.
Conflict #6 — Locale scope: PRD’s launch checklist names TH/SG/MY; the existing QA draft’s i18n test assumed EN/TH/CN.
The product PRD’s “What has to be done before launch” table has an explicit line item: “Copy/UX writing — all toast messages, error messages, badge text for TH/SG/MY.” But the FE spec’s i18n table lists only flat English key→copy pairs with no locale breakdown yet, and the QA draft accompanying the same PR wrote its i18n completeness case as “all partnerCoverImage.* keys render in en/th/cn” — a different three-market set entirely (Chinese instead of Singapore/Malaysia coverage), with no cited source for that specific set. This looks like it was carried over from an unrelated feature’s locale convention rather than sourced from this PRD. Reconcile the real target market list with PM/Localization before writing any i18n completeness test — don’t inherit the QA draft’s en/th/cn assumption without checking it against the PRD’s own TH/SG/MY line first.
Resolved, not actually open:
- Flow 2 UI existence: previously a question of whether to write test cases against the PRD’s described (but undesigned) UI. Confirmed absent by the product PRD’s own “Still Open” table, the FE spec’s explicit “don’t build UI until design lands” instruction, and the absence of any menu-item-level screen in the reviewed design. Scope is backend-only for v1 (CUJ-07), not a gap in this document’s coverage.
5. Test Execution Strategy
Automate (CI-gated)
Backend (hh-server, RSpec) — CUJ-01 through CUJ-05, CUJ-07, and CUJ-08’s regression check, plus §3.4’s clobber scenario, §3.6’s audit-record contract (once its storage mechanism is confirmed), and §3.7’s removal request shape (once confirmed). Coverage gate: 80% project-wide, 100% on critical paths (cover-image set/remove, ownership rejection, the CUJ-04 status-gate check once it has a confirmed expected behavior, change_active permission tightening, audit trail).
Frontend — most likely Playwright against book-bite, per the FE spec’s own Vue/book-bite component scoping (§4 Conflict #1); component tests for the slide-over’s validation/dirty-state/error states (§3.2, §3.3) and the Remove-confirm flow (CUJ-02), E2E for the row-action visibility matrix (CUJ-03) and the badge/permission-drift matrix (CUJ-06). Hold on writing any of this until Conflict #1’s one-line PM confirmation lands — if the Platform field turns out to be correct rather than stale, this entire section needs to be rewritten against Flutter widget/integration-test conventions instead, and none of the book-bite-specific work would carry over.
Manual (not CI-gated, but required before flag flips)
- Internal QA pass on staging across every package type and every
menu_typecombination (img,hh_menu,imenupro), per the product PRD’s own rollout strategy. - Visual regression on the package-list Action column across all 4 statuses (Active/Inactive/Pending Review/Rejected) and both slide-over modes (Add/Edit), if the relevant surface’s visual-regression tooling is set up for it.
- Copy sign-off on the Admin-created tooltip (§4 Conflict #5) before CUJ-06 is automated against a fixed string, and on the real target locale set (§4 Conflict #6) before any i18n completeness pass is scoped.
change_activepermission-tightening behavior explicitly documented in release notes, per CUJ-05.
Explicitly not automated in v1
Flow 2’s UI (CUJ-07 is backend-only until FE has a design to build against). Any FE automation at all until Conflict #1 is resolved — writing Playwright specs against a surface that turns out to be a Flutter screen (or vice versa) would need to be thrown away, not adapted.
6. Definition of Done & Pre-Rollout Checklist
Per-ticket gate:
- Tests pass in CI (hh-server RSpec; FE suite per whichever stack Conflict #1 resolves to).
- Coverage not decreased (80% project-wide / 100% on the touched critical-path module).
- No new
rubocop/lint offenses. - API contract tests pass for any ticket touching a serialized endpoint (
RestaurantPackageSerializer, the menu-item serializer). - Manual smoke documented in the PR description for any UI-facing change.
- Analytics events (
partner_cover_image_updated,partner_created_by_admin_badge_viewed, and the Flow 2 equivalent once it exists) verified end-to-end where applicable. - PR linked to this document for any ticket touching a §2 CUJ.
Pre-rollout (before flipping ff_partner_cover_image_selfserve_v1 for any cohort):
| Check | Owner |
|---|---|
| All §2 P0 CUJs pass in CI | Backend + FE eng, QA |
| Conflict #1 (platform) resolved and FE built/automated against the confirmed surface | PM + FE eng |
| CUJ-04’s status-gate endpoint behavior explicitly confirmed with PM/Eng (server-side reject vs. accepted permissive) | PM + Backend eng |
| Conflict #3 (toggle visibility for genuinely non-creatable types) confirmed with design | Design + QA |
| Conflict #5 (tooltip copy) and Conflict #6 (TH/SG/MY vs. EN/TH/CN locale scope) both signed off | PM/Localization + QA |
| §3.7 removal request shape confirmed and CUJ-02 automated against the real contract | Backend eng |
| §3.6 audit-record storage mechanism confirmed | Backend eng |
change_active permission tightening documented in release notes | PM + QA |
Internal staging pass recorded across all package types × menu_type combinations | QA |
| §3.4 pending-draft clobber scenario result recorded (pass or filed as a defect) | Backend eng + QA |
7. Revision Notes
This document adopts the QA Point-of-View PRD structure established elsewhere in this knowledge base (QA_POV_PRD_Birthday_AddOn_20260731000000.md), replacing an earlier flat test-case-table draft (QA_TestCases_Partner_Cover_Image_SelfServe_20260914000000.md) with this document, renamed to match the QA_POV_PRD_* naming convention. All P0/P1 substance from that earlier draft is preserved across the CUJs (§2) and Architectural Risks (§3); granular per-field boundary/error tables are compressed into inline assertions, consistent with the Birthday Add-On precedent’s own convention of embedding “Assert: X, Y, Z” directly in each CUJ.
Rev. 2 cross-references the full PR #374 document set — product PRD, FE spec, BE spec, and the existing QA draft — rather than the product PRD and QA draft alone. This surfaced several corrections and additions: the FE spec’s own Vue/book-bite component scoping substantially strengthens Conflict #1 (platform) beyond what the product PRD and Figma alone suggested; the FE and BE specs’ own open-questions lists independently corroborate several risks already tracked here with direct sourcing rather than inference (Save-button dirty state, §3.2; the cover-image removal request shape, §3.7; audit-record storage mechanism, §3.6); a new locale-scope conflict (§4 Conflict #6) was found between the product PRD’s stated TH/SG/MY launch markets and the existing QA draft’s assumed EN/TH/CN key set; and the partner_created_by_admin_badge_viewed analytics event, present in the product PRD’s tracked-events table but previously missing from this document, is now asserted in CUJ-06.