From ee0cfa611d2c8586ae08b6ad01e56241f95da337 Mon Sep 17 00:00:00 2001 From: Brandon Krigbaum Date: Thu, 3 Sep 2026 08:55:26 -0500 Subject: [PATCH] Tag transactions with the Omeda brand The first week of `matchedBy` data could not answer its own question. 30.7% of identifications matched by email with `none-supplied`, and there was no way to tell whether that is the genuine new-member rate or one publisher that never deployed the client change -- two situations needing completely different responses, indistinguishable in the data. Adoption sat flat at 65-70% across four days, which ruled out rollout lag in aggregate but said nothing about any single tenant. The brand was the missing dimension. It already reaches the `apiRequest` mongo log, but that cannot be faceted alongside transaction attributes, which is the whole point. Recorded in the plugin rather than in a resolver, for two reasons. The brand is a property of the request, so this covers every operation instead of one mutation. More importantly it is present on transactions that **throw** -- a resolver-level call never runs when the operation fails, and an errored request with no brand attached is unattributable. That matters here specifically: a client sending a field an older service does not accept fails at validation, before any resolver. SELECT count(*) FROM Transaction WHERE omedaMatchedBy IS NOT NULL FACET omedaBrand, omedaIdResolution SINCE 1 week ago No schema change, no client change, no coordinated deploy. The agent no-ops when disabled or outside a transaction. Co-Authored-By: Claude Opus 5 --- services/graphql/src/plugins/omeda.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/services/graphql/src/plugins/omeda.js b/services/graphql/src/plugins/omeda.js index 11fa1c0..47a10e0 100644 --- a/services/graphql/src/plugins/omeda.js +++ b/services/graphql/src/plugins/omeda.js @@ -52,6 +52,22 @@ class OmedaGraphQLPlugin { context.brand = brand; + /** + * Tag the transaction with the brand. Deliberately here rather than in a resolver: the brand is + * a property of the *request*, so recording it at this point means it is present on every + * operation and -- more importantly -- on transactions that **throw**, where a resolver-level + * call would never run. An errored request with no brand attached is unattributable. + * + * This is what makes the `omedaMatchedBy` / `omedaIdResolution` attributes decomposable by + * tenant. Without it, a rapid-identification that matched by email cannot be told apart from + * one publisher not having deployed the client change -- the two need completely different + * responses and looked identical in the first week of data. + * + * The brand already reaches the `apiRequest` mongo log below, but that cannot be faceted + * alongside transaction attributes, which is the whole point. + */ + newrelic.addCustomAttributes({ omedaBrand: brand }); + const apiClient = new OmedaApiClient({ appId, brand,