Skip to content

[codex] Add contextual signal coverage forecasts#5088

Merged
bokelley merged 1 commit into
mainfrom
adcp-5086
May 27, 2026
Merged

[codex] Add contextual signal coverage forecasts#5088
bokelley merged 1 commit into
mainfrom
adcp-5086

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Adds contextual signal coverage forecasts for get_signals and product-relative availability planning.

  • adds SignalCoverageForecast with explicit denominator scope, bucket overlap/completeness semantics, and availability-only forecast points
  • adds kind: "signal" forecast dimensions with canonical signal_ref, presence semantics, and signal-value buckets
  • adds coverage_rate as a forecastable availability metric
  • carries coverage_forecast through both get_signals.signals[] and wholesale signal feed event payloads
  • documents when to use signal-level coverage vs product/proposal forecast rows
  • adds a protocol changeset

Closes #5086.

Validation

  • npm run test:schemas
  • npm run test:composed
  • npm run test:json-schema
  • npm run test:examples
  • npm run test:build-schemas-hoist-enums
  • npm run test:oneof-discriminators
  • npm run test:schema-utf8
  • npm run build:schemas
  • git diff --check
  • precommit hook: npm run test:unit, npm run test:test-dynamic-imports, npm run test:callapi-state-change, npm run typecheck
  • pre-push hook: version sync and Mintlify broken-link check

Expert Review

Ran protocol, product/workflow, and schema/versioning expert reviews. Addressed blockers around wholesale feed parity, canonical signal identity, bucket semantics, stricter schema invariants, changeset coverage, and conformance hints.

@aao-release-bot aao-release-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Additive wire change with the right shape — new signal-coverage-forecast.json composes on top of existing ForecastPoint/ForecastRange primitives with a proper kind: "signal" discriminator, and coverage_rate joins the closed enum the same way measured_impressions and plays did. Follow-ups noted below.

Things I checked

  • static/schemas/source/core/forecast-point.json:48-58coverage_rate overlay uses allOf: [forecast-range.json, {properties: {low/mid/high: maximum:1}}]. allOf is conjunctive, so forecast-range.json's anyOf (mid OR low+high) is preserved. Cap is per-point, not per-sum — correctly orthogonal to bucket_semantics: "overlapping".
  • static/schemas/source/core/forecast-point-dimensions.json:181-263 — new signal arm carries kind: const "signal". Audit walker at scripts/audit-oneof.mjs stays satisfied; no baseline ratchet needed.
  • static/schemas/source/core/forecast-point-dimensions.json:221-262if/then conditionals on presence are wired both directions: absent forces signal_value: null AND required, present constrains type to [string,number,boolean]. Asymmetry on signal_value requiredness in the present branch is intentional per the description at L204.
  • static/schemas/source/enums/forecastable-metric.json:24,43coverage_rate appended to enum + enumDescription. Closed-enum additive precedent holds.
  • static/schemas/source/core/wholesale-feed-event.json:583-593 — wholesale parity uses $ref to the same signal-coverage-forecast.json. Identity stays structurally aligned with get_signals.signals[].
  • .changeset/contextual-signal-coverage-forecasts.mdminor correctly matches an additive wire change. Description names the coverage_percentage compatibility story.
  • Docs vs schema: docs/signals/tasks/get_signals.mdx:93-142,684-686 and docs/media-buy/product-discovery/media-products.mdx:984,1180 — field names, optionality, and the bucket_semantics/bucket_completeness orthogonality match the schema. No drift.

Follow-ups (non-blocking — file as issues)

  • coverage_percentage is required and deprecated: true at the same time. static/schemas/source/signals/get-signals-response.json:114-121 keeps it in required; L87-93 of the same file marks it deprecated. Same pattern at static/schemas/source/core/wholesale-feed-event.json:583-589. Generated client types will surface a required-and-deprecated field, which is a confusing surface. Either drop from required now (the planned exit ramp) or downgrade the field to a description-only "legacy" note until a future major. Both subagents flagged this independently.
  • enclosing_signal_identity_match is prose-only. static/schemas/source/core/signal-coverage-forecast.json:8-10 defines the resolution rule but JSON Schema cannot enforce it. A buyer materializing a wholesale signal.* event out of context — webhook fan-out, event-stream replay — has no schema guarantee that a bare signal_id shorthand under coverage_forecast.points[].dimensions belongs to the enclosing signal. Either require signal_ref (drop shorthand) when the enclosing scope is wholesale-feed-event, or pin the gap with a conformance test.
  • Test helpers in tests/schema-validation.test.cjs:622-682 prove themselves, not the wire. signalDimensionMatchesEnclosingSignal and completeExclusiveCoverageRatesPartition run against hand-crafted JS objects, never against a payload that has just passed validateGetSignalsResponse. Feeding a schema-valid mismatched-identity payload through and asserting the helper fails would document the gap so the next person who tries to enforce it on the wire sees a red test.
  • coverage_rate cap tested only on mid. tests/schema-validation.test.cjs:509-513 exercises { mid: 1.2 }. The schema caps low/mid/high; add two assertInvalid cases for { low: 1.5, high: 2.0 } and { low: 0.2, high: 1.5 } so per-bound mechanics are pinned.

Minor nits (non-blocking)

  1. additionalProperties: true at the top of signal-coverage-forecast.json:152. A typo like bucket_completness would silently ride the wire. Open is intentional inside scope (L100) for seller qualifiers; the top of the object doesn't need it.
  2. presence: "present" with omitted signal_value has no assertValid test. static/schemas/source/core/forecast-point-dimensions.json:244-260 deliberately allows this per the description at L204 — worth one positive test to pin the intent.

Approving on the strength of clean ad-tech-protocol-expert and code-reviewer verdicts plus the schema/docs coherence audit.

@bokelley bokelley merged commit f3705ae into main May 27, 2026
31 checks passed
@bokelley bokelley deleted the adcp-5086 branch May 27, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add contextual signal coverage forecasts

1 participant