Skip to content

Commit e3a28a7

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@cfc7760af31f29f1acc7153a7e9fcb48d35f730b
1 parent 1fc4fda commit e3a28a7

7 files changed

Lines changed: 129 additions & 9 deletions

File tree

bun.lock

Lines changed: 2 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/src/__tests__/freebuff-placements.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
PLACEMENT_METRIC_LABELS,
1212
PLACEMENT_PREVIEW_WIDTHS,
1313
PLACEMENT_SLOTS,
14+
TRACKED_LINK_PLACEMENT_ID,
15+
placementSlotLabel,
1416
PLACEMENT_STATUS_LABELS,
1517
PRIMARY_METRICS,
1618
UNDERSPEND_COPY,
@@ -246,3 +248,48 @@ describe('copy and configuration', () => {
246248
expect(PLACEMENT_METRIC_LABELS.ecpm).toBe('Effective CPM')
247249
})
248250
})
251+
252+
/**
253+
* The reporting grain is wider than the slot catalog, and the labeller has to
254+
* know it.
255+
*
256+
* `PLACEMENT_SLOTS` lists what an advertiser can buy a position in. A tracked
257+
* link is not one of those — nothing auctions it and nothing serves an
258+
* impression into it — but the delivery rollup groups by `placement_id`, so
259+
* every surface that labels a placement will meet it.
260+
*/
261+
describe('placementSlotLabel', () => {
262+
it('renders every real slot exactly as the breakdown table already did', () => {
263+
// This is a behaviour-preservation assertion, not a new format: these
264+
// strings are what the table showed before the labeller moved here.
265+
expect(placementSlotLabel('waiting-room-1')).toBe('Waiting room 1')
266+
expect(placementSlotLabel('CLI-Chat-Inline')).toBe('CLI Chat Inline')
267+
expect(placementSlotLabel('Web-Chat-After-User-Message')).toBe(
268+
'Web Chat After User Message',
269+
)
270+
for (const slot of PLACEMENT_SLOTS) {
271+
expect(placementSlotLabel(slot.id).length).toBeGreaterThan(0)
272+
}
273+
})
274+
275+
it('names the tracked-link grain, which no slot describes', () => {
276+
expect(placementSlotLabel(TRACKED_LINK_PLACEMENT_ID)).toBe('Tracked links')
277+
// That it is not a slot is proved by the compiler rather than asserted
278+
// here: `PLACEMENT_SLOTS` is `as const`, so comparing a slot id against
279+
// this constant is a type error ("no overlap"). Adding it to the catalog
280+
// would put a tracked link in front of an advertiser choosing where their
281+
// ad appears, which is not what it is.
282+
expect(
283+
(PLACEMENT_SLOTS as readonly { id: string }[]).some(
284+
(slot) => slot.id === TRACKED_LINK_PLACEMENT_ID,
285+
),
286+
).toBe(false)
287+
})
288+
289+
it('degrades an unknown grain to something readable, never undefined', () => {
290+
// The next grain after tracked links must render as a string a human can
291+
// read, not as a gap that looks like a bug in the numbers beside it.
292+
expect(placementSlotLabel('some-future-grain')).toBe('Some future grain')
293+
expect(placementSlotLabel('')).toBe('')
294+
})
295+
})

common/src/constants/freebuff-placements.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,39 @@ export const PLACEMENT_SLOTS = [
121121
},
122122
] as const
123123

124+
/**
125+
* The reporting grain a TRACKED LINK click lands on.
126+
*
127+
* Deliberately NOT a `PLACEMENT_SLOTS` entry: a tracked link is not a slot,
128+
* nothing auctions it, nothing serves an impression into it, and adding it to
129+
* that list would put it in front of an advertiser choosing where their ad
130+
* appears. But the delivery rollup groups by `placement_id` and `surface`, so
131+
* an external click has to carry SOME value for both, and every surface that
132+
* labels a placement will meet these two.
133+
*
134+
* `placementSlotLabel` below is what stops that meeting rendering `undefined`.
135+
*/
136+
export const TRACKED_LINK_PLACEMENT_ID = 'tracked-link'
137+
export const TRACKED_LINK_SURFACE = 'tracked_link'
138+
139+
/**
140+
* A human label for any `placement_id`, including grains no slot describes.
141+
*
142+
* `PLACEMENT_SLOTS` is a catalog of things an advertiser can BUY, and the
143+
* reporting grain is strictly wider than it -- tracked links today, and
144+
* whatever the next one is. So this is a formatter with a special case, not a
145+
* dictionary lookup with a hole: `waiting-room-1` still becomes
146+
* `Waiting room 1` and `CLI-Chat-Inline` still becomes `CLI Chat Inline`,
147+
* exactly as the breakdown table already rendered them, and an unknown id
148+
* degrades to a readable string rather than to `undefined`.
149+
*/
150+
export function placementSlotLabel(placementId: string): string {
151+
if (placementId === TRACKED_LINK_PLACEMENT_ID) return 'Tracked links'
152+
const [head, ...rest] = placementId.split('-')
153+
if (!head) return placementId
154+
return [head[0]!.toUpperCase() + head.slice(1), ...rest].join(' ')
155+
}
156+
124157
/**
125158
* The metrics an advertiser sees, split by role.
126159
*

common/src/util/__tests__/ad-experiment.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
IMPREZIA_EXPERIMENT_PERCENT,
77
adExperimentArmForUser,
88
firstPartyAdRouteForUser,
9+
firstPartyPrimaryBasisPoints,
910
isImpreziaAudienceEmail,
1011
} from '../ad-experiment'
1112

@@ -53,6 +54,13 @@ describe('imprezia experiment arm', () => {
5354
})
5455

5556
describe('first-party request routing', () => {
57+
test('normalizes decimal percentages to the same integer basis points used by campaign allocation', () => {
58+
expect(firstPartyPrimaryBasisPoints(1.234)).toBe(123)
59+
expect(firstPartyPrimaryBasisPoints(-1)).toBe(0)
60+
expect(firstPartyPrimaryBasisPoints(101)).toBe(10_000)
61+
expect(firstPartyPrimaryBasisPoints(Number.NaN)).toBe(0)
62+
})
63+
5664
test('keeps an absent runtime configuration on the paid-network-only path', () => {
5765
expect(DEFAULT_FIRST_PARTY_PRIMARY_PERCENT).toBe(0)
5866
expect(DEFAULT_FIRST_PARTY_BACKFILL).toBe(false)

common/src/util/__tests__/axiom-only-log.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ describe('getAxiomOnlyLogEvent', () => {
156156
first_party_route: 'gravity_then_first_party',
157157
first_party_primary_percent: 10,
158158
first_party_backfill_enabled: true,
159+
first_party_primary_cohort: 'pilot-a',
160+
first_party_primary_cohort_percent: 1,
161+
first_party_served_cohort: 'pilot-a',
162+
first_party_entrypoint: 'primary',
159163
gravity_outcome: 'no_fill',
160164
selection_reason: 'gravity_no_fill_backfill',
161165
ad_count: 1,
@@ -186,6 +190,10 @@ describe('getAxiomOnlyLogEvent', () => {
186190
first_party_route: 'gravity_then_first_party',
187191
first_party_primary_percent: 10,
188192
first_party_backfill_enabled: true,
193+
first_party_primary_cohort: 'pilot-a',
194+
first_party_primary_cohort_percent: 1,
195+
first_party_served_cohort: 'pilot-a',
196+
first_party_entrypoint: 'primary',
189197
gravity_outcome: 'no_fill',
190198
selection_reason: 'gravity_no_fill_backfill',
191199
ad_count: 1,
@@ -204,6 +212,7 @@ describe('getAxiomOnlyLogEvent', () => {
204212
axiomEvent: ADS_FIRST_PARTY_DECISION_EVENT,
205213
outcome: 'no_fill',
206214
no_fill_reason: 'no_eligible_campaign',
215+
primary_allocation_invalid: true,
207216
placement_count: 2,
208217
candidate_count: 4,
209218
candidate_load_ms: 8,
@@ -223,6 +232,7 @@ describe('getAxiomOnlyLogEvent', () => {
223232
data: {
224233
outcome: 'no_fill',
225234
no_fill_reason: 'no_eligible_campaign',
235+
primary_allocation_invalid: true,
226236
placement_count: 2,
227237
candidate_count: 4,
228238
candidate_load_ms: 8,

common/src/util/ad-experiment.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ export interface FirstPartyRoutingConfig {
5252
backfill: boolean
5353
}
5454

55+
/**
56+
* Normalize the percentage knob to the 10,000-bucket precision used by both
57+
* request routing and campaign allocation. Keeping this conversion shared
58+
* prevents decimal environment values from opening a route that the campaign
59+
* selector later rejects (or vice versa).
60+
*/
61+
export function firstPartyPrimaryBasisPoints(primaryPercent: number): number {
62+
const configuredPercent = Number.isFinite(primaryPercent)
63+
? primaryPercent
64+
: DEFAULT_FIRST_PARTY_PRIMARY_PERCENT
65+
return Math.round(Math.min(100, Math.max(0, configuredPercent)) * 100)
66+
}
67+
5568
export type AdExperimentArm = 'imprezia_forced' | 'imprezia_first' | 'control'
5669

5770
export function isImpreziaAudienceEmail(
@@ -104,11 +117,9 @@ export function firstPartyAdRouteForUser(
104117
config: FirstPartyRoutingConfig,
105118
): FirstPartyAdRoute {
106119
if (!userId) return 'paid_network_only'
107-
const configuredPercent = Number.isFinite(config.primaryPercent)
108-
? config.primaryPercent
109-
: DEFAULT_FIRST_PARTY_PRIMARY_PERCENT
110-
const primaryPercent = Math.min(100, Math.max(0, configuredPercent))
111120
const bucket = fnv1a(`${FIRST_PARTY_ROUTING_EXPERIMENT}:${userId}`) % 10_000
112-
if (bucket < primaryPercent * 100) return 'first_party_primary'
121+
if (bucket < firstPartyPrimaryBasisPoints(config.primaryPercent)) {
122+
return 'first_party_primary'
123+
}
113124
return config.backfill ? 'gravity_then_first_party' : 'paid_network_only'
114125
}

common/src/util/axiom-only-log.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ const ADS_FETCH_COMPLETED_FIELDS = {
7575
first_party_route: 'string',
7676
first_party_primary_percent: 'number',
7777
first_party_backfill_enabled: 'boolean',
78+
/**
79+
* Exact primary allocation is intentionally represented by an opaque,
80+
* operator-chosen cohort label rather than a campaign or advertiser id.
81+
* Producers emit `none` / 0 when the request is not assigned to a primary
82+
* cohort, so an absent field is distinguishable from a deliberate control.
83+
*/
84+
first_party_primary_cohort: 'string',
85+
first_party_primary_cohort_percent: 'number',
86+
/** The opaque cohort that actually produced a first-party fill, or `none`. */
87+
first_party_served_cohort: 'string',
88+
/** `primary`, `gravity_no_fill_backfill`, or `none`. */
89+
first_party_entrypoint: 'string',
7890
/** Whether the immediately preceding Gravity attempt filled, no-filled, or
7991
* failed. This makes recovered no-fill inventory observable without logging
8092
* any campaign or creative identity. */
@@ -96,6 +108,7 @@ const ADS_FETCH_COMPLETED_FIELDS = {
96108
*/
97109
const ADS_FIRST_PARTY_DECISION_FIELDS = {
98110
outcome: 'string',
111+
primary_allocation_invalid: 'boolean',
99112
no_fill_reason: 'string',
100113
selection_reason: 'string',
101114
ad_count: 'number',

0 commit comments

Comments
 (0)