fix(llmo-customer-analysis): route v2 brandalf dispatches by changeKind (LLMO-4744)#2480
Open
rainer-friederich wants to merge 3 commits into
Open
fix(llmo-customer-analysis): route v2 brandalf dispatches by changeKind (LLMO-4744)#2480rainer-friederich wants to merge 3 commits into
rainer-friederich wants to merge 3 commits into
Conversation
…nd (LLMO-4744) Under brandalf, the v1 LLMO config S3 mirror is frozen, so the existing snapshot diff in `runLlmoCustomerAnalysis` always returns no-change and the three downstream triggers (cdn-logs-report, drs-brand-detection, geo-brand-presence-trigger-refresh) silently never fire on real user edits made via brandalf-UI / elmo-UI. This change adds a v2 dispatch path that short-circuits the snapshot diff when an audit message carries `auditContext.changeKind`. spacecat-api-service BrandsController will dispatch one such message per site after every customer-config mutation, with `changeKind` identifying what changed (brands, competitors, categories, topics, entities, prompts). Routing: - brands, competitors -> triggerGeoBrandPresenceRefresh - categories, topics, entities -> drsClient.triggerBrandDetection - prompts -> both triggers - cdn-logs-report is intentionally not fired for v2 (out of scope under brandalf — owned by a separate path) Defensive checks: unknown changeKind, missing organizationId, brandalf flag disabled, and DRS not configured all return a no-op completed result with the appropriate warn log. The branch sits before the first-onboarding logic so subsequent v2 edits do not re-create BP schedules (createAndTriggerBrandPresenceSchedule is not idempotent). The v1 path is untouched: messages without changeKind fall through to the existing snapshot-diff flow unchanged. 13 new test cases cover all changeKind values, defensive paths, and v1 backward compatibility.
Contributor
Author
|
Self-review pass via local code-review agent. No actionable findings — reviewer reported two lower-confidence (~50%) design notes:
Reviewer summary: "The PR is in good shape. The fix is correct, well-tested, and does not regress the v1 path." |
…ts kind)
Branch coverage on handler.js fell from 100% to 99.18% on the
'triggeredSteps.join(', ') || 'none'' fallback at line 392 — the '\u2018none\u2019'
side never fired in tests because every existing changeKind that
reaches the trigger fan-out either fires brand-detection (when DRS is
configured) or BP refresh (for brands/competitors/prompts).
Add one case where DRS is unconfigured AND changeKind is 'categories'
(BP refresh not in scope), so triggeredSteps stays empty and the
'triggered: none' log path executes. Restores 100% branch coverage.
Contributor
|
This PR will trigger a patch release when merged. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pre-cutover blocker for the Adobe brandalf_migration. Sibling of LLMO-4743 (Reader 4).
Problem
Under
brandalf, the v1 LLMO config S3 mirror is frozen. The existing snapshot diff inrunLlmoCustomerAnalysis(handler.js:439, 448) reads two versioned S3 snapshots and compares them — both return identical data, so the diff is always empty and the three downstream triggers silently never fire on real user edits made via brandalf-UI / elmo-UI:triggerCdnLogsReportdrsClient.triggerBrandDetectiontriggerGeoBrandPresenceRefreshAudit reports show
status: completed, configChangesDetected: falseregardless of what the user actually changed.There is also a second, deeper root cause confirmed during investigation: the audit is not even being dispatched on subsequent v2 edits.
BrandsControllerv2 endpoints (the ones elmo-UI / brandalf-UI hits) write to normalized Postgres tables but never enqueue anllmo-customer-analysisaudit. The legacy v1 PUT path was the only dispatcher and is bypassed under brandalf. The companion api-service PR adobe/spacecat-api-service#2337fixes the dispatch side.Investigation confirmed:
triggerBrandDetectionandtriggerGeoBrandPresenceRefreshhave NO scheduled equivalent —llmo-customer-analysischange-detection is the sole firing path.triggerCdnLogsReporthas scheduled equivalents but only this code path setscategoriesUpdated: true(forces URL→category pattern rebuild).Architecture choice
Three options were considered (full notes in chat thread):
llmo_customer_configJSONB is seeded once at onboarding and never refreshed by BrandsController, so it has no live data to diff against. A snapshot mechanism (new column, new table, S3, DDB) is its own design problem and races on concurrent edits.spacecat-shared-utilsbug fix; full JSONB rebuild on every write; fragile schema-shape adapter.changeKindfrom BrandsController and route triggers from it. No diff, no snapshot, no migration. Smallest blast radius.Fix
Add a v2 dispatch path that short-circuits the snapshot diff when an audit message carries
auditContext.changeKind. The companionspacecat-api-servicePR (adobe/spacecat-api-service#2337) dispatches one such message per site after every customer-config mutation.Routing:
brands,competitors→triggerGeoBrandPresenceRefreshcategories,topics,entities→drsClient.triggerBrandDetectionprompts→ both triggers (prompts feed BP queries directly and affect topic/category attribution)cdn-logs-reportis intentionally not fired for v2 (out of scope under brandalf — owned by a separate path)Why
triggerGeoBrandPresenceRefreshis correct under brandalftriggerGeoBrandPresenceRefreshultimately callsdrsClient.publishBrandPresenceAnalyzefor each existing BP collection sheet. That method publishes an SNSJOB_COMPLETEDevent withreanalysis: trueandresult_locationpointing at the uploaded sheet — same shape DRS sees when an external provider job (BrightData / Google AI / OpenAI) completes. DRS picks up the event, runs BP analysis against the current Postgres v2 customer config, and thebrand_presence_executionsDB sync runs as the natural tail of that pipeline. The DB ends up eventually-consistent with the user's edit. We don't run the analysis ourselves — we just signal DRS to re-run it with the new config.The case this does not cover is adding a brand entity whose name was never collected from providers; for that you'd need a new DRS schedule run, which is an additive follow-up, not a regression fix.
Defensive checks (each warns + returns no-op completed result):
changeKindorganizationIdin both site and auditContextprompts)The branch sits before the first-onboarding logic so subsequent v2 edits do not re-create BP schedules (
createAndTriggerBrandPresenceScheduleis not idempotent).The v1 path is untouched: messages without
changeKindfall through to the existing snapshot-diff flow unchanged.Files
src/llmo-customer-analysis/handler.js— newhandleV2ChangeKindDispatchfunction + branch inrunLlmoCustomerAnalysistest/audits/llmo-customer-analysis.test.js— 14 new test cases underdescribe('v2 changeKind dispatch')Test plan
changeKindstill diff S3 snapshots and trigger as beforenpm test(affected file) — 65 tests pass (51 existing + 14 new)handler.jsDeferred follow-ups (not in this PR)
warn→errorso silent-no-op cases page on alerts. The api-service dispatcher and the audit-worker brandalf-disabled path both currently log atwarn.isBrandalfEnabled(true / false / unknown) so transient feature-flag-fetch failures don't masquerade as "brandalf off" and silently drop triggers. Bigger refactor — own ticket.Related
Deployment ordering
This PR is safe to deploy first. The new branch is dead until a v2 message arrives (i.e., until the api-service PR ships). No existing behavior changes.
Related Issues