AdPulse — Google Ads Competitor Intelligence
Pricing
$100.00 / 1,000 competitor snapshots
AdPulse — Google Ads Competitor Intelligence
Competitive intelligence for Google Ads. Monitor competitors over time, detect evidence-backed creative changes, ad activity, format shifts, and actionable signals through dashboards, alerts, and structured outputs.
Pricing
$100.00 / 1,000 competitor snapshots
Rating
0.0
(0)
Developer
Pedro Henrique
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
AdPulse
AdPulse monitors Google Ads Transparency competitors over time and turns raw ad observations into evidence-backed change intelligence. Give it exact advertiser IDs (recommended) or competitor names, choose a market, and run it manually or on a schedule. AdPulse preserves monitoring history, separates real creative changes from source noise, and produces an interactive dashboard, competitive brief, alerts, and structured data for downstream workflows.
What you get
- Competitor change monitoring — baseline, newly observed, changed, missing, confirmed missing, and reappeared creative states with explicit evidence.
- Creative intelligence — exact-content families, format mix, lifecycle evidence, lineage, strategy deltas, and evidence-weighted testing opportunities.
- Human-readable outputs — an interactive dashboard and competitive brief in addition to raw structured data.
- Automation-ready outputs — Dataset rows plus versioned summaries, alert records, and a read-model API in the default Key-Value Store.
- Fail-closed reliability — blocked, rate-limited, unavailable, ambiguous, or schema-drifted source responses are never silently converted into “no ads.”
- Optional advanced analysis — semantic enrichment, ad-to-landing intelligence, cross-platform Meta evidence, and first-party experiment validation are opt-in.
Quick start
For recurring monitoring, use exact Google Ads Transparency advertiser IDs and keep the same stateNamespace across runs:
{"advertiserIds": ["AR16735076323512287233"],"searchTerms": [],"region": "BR","maxAds": 100,"mode": "pulse","stateNamespace": "my-monitor","proxyConfiguration": {"useApifyProxy": true},"strictSchema": true}
A first run establishes a baseline. Later runs compare against persisted history. Use reconciliation when you need stronger disappearance confirmation.
Pricing model
The Store release is designed around one visible value unit: one competitor-snapshot per resolved competitor in a completed run, priced at US$0.10 per snapshot.
A run with 5 competitors therefore requests 5 snapshot units. AdPulse does not separately bill semantic provider calls, cache hits, retries, fingerprints, dashboard rendering, or internal deduplication. Partial or failed runs are not intentionally settled as successful snapshot units, and the PPE preflight refuses to start expensive work when the effective Apify event budget cannot cover the full admitted run.
AdPulse supports at most 50 competitor targets per run and 100 creatives per competitor per run. These ceilings keep the public value unit inside the operating envelope used for pricing acceptance. The public release gate remains fail-closed in code until the owner explicitly enables Store release after the Publishing checklist is complete. The intended public offer does not rely on a hidden second semantic charge.
Main outputs
Start with Dashboard for the product view, Competitive Brief for a concise evidence-backed report, and Dataset for row-level creative observations. The output schema also links directly to run, alert, quality, intelligence, strategy, opportunity, lineage, landing-page, cross-platform, and first-party validation summaries. The default Key-Value Store is organized into named collections so these product outputs are easy to filter in Apify Console, while internal billing/economics records remain audit-only.
The Dataset has two views:
- Competitor changes overview — compact fields for normal review and integrations.
- Full evidence detail — the broader audit surface, including quality, fingerprint, semantic, lineage, and landing evidence.
Optional features and secrets
Semantic enrichment is disabled by default. If you enable it, configure OPENAI_API_KEY in the Actor environment; AdPulse does not accept that secret in ordinary Actor input. Webhook delivery is also opt-in and reads the destination URL from an environment variable rather than persisting it in output records.
Landing-page intelligence only follows defensible public final URLs and never requests Google ad click trackers. Cross-platform Meta evidence requires an explicit Google advertiser ID plus a verified advertiser domain mapping so external evidence cannot silently rewrite Google monitoring identity.
Evidence boundaries
AdPulse analyzes public advertising evidence. It does not infer spend, impressions, clicks, conversions, CTR, CPA, ROAS, profitability, or “winner/loser” status from ordinary Google Ads Transparency data. Semantic labels and opportunity hypotheses remain model- or rule-derived evidence with explicit provenance and coverage limits.
Google Ads Transparency is not a supported public API contract, so source behavior can change. AdPulse treats source blocks, ambiguity, incomplete pagination, and schema drift as explicit states instead of guessing.
Store release safety
Public billing is disabled by default in the runtime. Private canary charging was accepted with a bounded two-snapshot / US$0.20 live test; production-scale Store billing requires a third explicit owner release gate, ADPULSE_PPE_PUBLIC_STORE_RELEASE_ENABLED=true, in addition to the billing and live-charge flags. The code also preserves a 50-snapshot public-run ceiling and full-run budget admission before expensive work.
See docs/STORE_RELEASE.md for the owner-only publication checklist and docs/INNOVATION_ROADMAP.md for engineering acceptance status.
Engineering status and acceptance evidence
Implemented milestones:
- Actor/GitHub foundation
- Resilient HTTP collector
- Nike + Brazil raw-collection regression
- Stable creative normalizer
- Durable State Store
- Explainable multi-channel fingerprinting
- Evidence-aware explainable Diff Engine
- Dimensioned Evidence Quality Engine
- Evidence-backed Intelligence Engine
- Evidence-Bound Semantic Enrichment Engine (accepted on real Apify)
- Evidence-Bound Opportunity Engine (accepted on real Apify)
- Evidence-Aware Monitoring & Alert Engine (accepted on real Apify) 12B. Monitoring Orchestration & Delivery (accepted on real Apify)
- Evidence-Backed Competitive Brief / Reporting (accepted on real Apify)
- Evidence Experience API & Dashboard (Stage 14.2 accepted on live Apify)
- Private PPE Economics & Monetization Readiness (accepted; completed-run p95 gate closed)
- Commercial Policy & PPE SDK Integration (bounded live-money canary accepted at 2 × US$0.10)
16.1. Post-Settlement Billing Consistency (accepted; commercial artifacts reconciled after settlement) 16.2. Store Readiness & Release Safety (code-ready; manual Apify Publishing gates remain pending)
Innovation Track (prioritized without replacing release gates):
- I1. Creative Lineage / Mutation Graph (accepted on real Apify)
- I2. Strategy Delta Engine (accepted on real Apify)
- I3. Ad ↔ Landing Page Intelligence (accepted on live Apify)
- I4. Cross-platform Content Graph (accepted live with Google + Meta)
- I5. Evidence-weighted Opportunity Engine (accepted on live Apify)
- I6. First-party Validation Loop (accepted on live Apify)
The backbone/release and innovation roadmaps are tracked together in
docs/INNOVATION_ROADMAP.md.
Stage 6 received real Apify acceptance on 2026-10-02 using two separate Nike/BR
runs against the same stage6-nike-br-acceptance namespace. The second run
advanced State Store v2 from revision 1 to 2 with all 14 creatives recognized
as existing, zero new creatives, and the same creative-set hash. All 14 content,
representation, and metadata fingerprints remained stable across runs even
though observation time changed and both JavaScript previews received different
runtime htmlParentId / responseCallback values. The compact acceptance
evidence is recorded in benchmarks/stage6_apify_acceptance_2026-10-02.json.
Stage 7 received real Apify acceptance on 2026-10-03 using four Nike/BR runs
against stage7-nike-br-acceptance. The sequence validated cold-start
BASELINE semantics, a stable complete rerun, controlled truncation with
missing inference suppressed, and complete recovery. The creative omitted by
the truncated run, CR17955367121756094465, returned as
UNCHANGED/no_change with previousObservedRevision=2 and
candidateRevision=4, proving that incomplete revision 3 was not treated as
authoritative absence evidence. The compact acceptance evidence is recorded in
benchmarks/stage7_apify_acceptance_2026-10-03.json.
Stage 8 received real Apify acceptance on 2026-10-03 using four Nike/BR runs
against stage8-nike-br-acceptance. The accepted State sequence was
0 -> 1 -> 2 -> 3 -> 4: baseline kept trend/transition closed, the stable
second run opened them, controlled truncation preserved local creative
eligibility while closing every population-level Quality gate, and complete
recovery reopened those gates. The complete creative-set hash
c5166b60557b976320c14029b5ad6ec3dcb9b4a3b78f72feff3a12eed724732c
was replaced during truncation by
f757b6e7b85f43e155ecd0de336d3e1bfd6ed3ea4ea9a84f08e5b639063e2a1b
and restored on recovery. The omitted creative
CR17955367121756094465 returned as UNCHANGED/no_change with
previousObservedRevision=2 and candidateRevision=4, proving incomplete
revision 3 was not authoritative absence evidence. The compact acceptance
evidence is recorded in
benchmarks/stage8_apify_acceptance_2026-10-03.json.
Stage 9 received real Apify acceptance on 2026-10-03. Four Nike/BR runs against
stage9-nike-br-acceptance validated the sequence 0 -> 1 -> 2 -> 3 -> 4:
baseline snapshot intelligence with trend closed, a stable complete rerun with
activity/persistence/trend enabled, controlled truncation that preserved local
family evidence while blocking all population intelligence, and complete recovery.
The recovery trendDelta correctly used revision 2 rather than truncated revision
3, and the omitted creative CR17955367121756094465 returned
UNCHANGED/no_change with three actual monitoring observations while the other
13 creatives had four. A separate Nike + ADIDAS run against
stage9-cross-advertiser-acceptance made cross-advertiser reuse eligible across
two advertisers and returned zero exact shared families, with no fuzzy similarity
inference. The compact acceptance evidence is recorded in
benchmarks/stage9_apify_acceptance_2026-10-03.json.
Stage 10 received real Apify acceptance on 2026-10-04 using the corrected provider
boundary from commit 38a63f48662d49cfe9677f709a68def0828e880a. A one-family
provider canary first proved successful structured vision enrichment. A clean Nike/BR
acceptance baseline against stage10-nike-br-acceptance-v2 then produced semantic
analysis for all 12 eligible image_snapshot families: one cache hit from the canary
plus 11 new provider analyses, with zero provider or cache-persistence failures. The
two JavaScript previews remained explicitly unsupported, so population semantic mix
correctly stayed closed rather than publishing partial percentages. An identical
second run reused all 12 exact-family cache entries with zero provider calls and zero
provider tokens, while all 14 creatives remained UNCHANGED. Cached semantic
analyses were validated equal to the prior accepted results and no performance claim
was emitted. The compact acceptance evidence is recorded in
benchmarks/stage10_apify_acceptance_2026-10-04.json.
Stage 11 received real Apify acceptance on 2026-10-04 using the cache-only
Opportunity Engine from commit 38c2974e161a48c5ca741966fa54a7496e305a64.
A Nike/BR run with Stage 10 disabled reused 12 accepted exact-family semantic cache
entries with zero provider calls and zero provider tokens while leaving two JavaScript
representations explicitly unsupported and population gap claims closed. A separate
Nike + ADIDAS baseline then proved cross-advertiser subset comparison with one explicit
ADIDAS semantic cache miss and no provider fallback. An identical rerun advanced both
targets from revision 1 to 2 with all 15 creatives UNCHANGED, opened transition/trend
Quality gates, and reproduced the Strategy output exactly apart from candidate revision,
including all five Nike hypothesis IDs. No absence or performance claim was emitted.
The compact acceptance evidence is recorded in
benchmarks/stage11_apify_acceptance_2026-10-04.json.
Innovation I1 Creative Lineage / Mutation Graph received real Apify acceptance on
2026-10-14 against the historical stage7-nike-br-acceptance namespace. Run A
advanced State 6 -> 7 and explicitly adopted Lineage with
prior_snapshot_seed, historicalBackfillComplete=false, and
lineageStartedRevision=7. Run B advanced 7 -> 8 with a synchronized sidecar while
preserving the original observation boundary. All 15 exact family -> lineage node IDs
remained stable, all creatives were UNCHANGED, three source-volatility-only
transitions created zero lineage mutations, and Lineage introduced zero provider calls
or performance/causal claims. Evidence is recorded in
benchmarks/i1_creative_lineage_apify_acceptance_2026-10-14.json.
Innovation I2 Strategy Delta Engine received real Apify acceptance on
2026-10-14 against the historical stage7-nike-br-acceptance namespace.
Run A advanced State 9 -> 10 and seeded the first synchronized Strategy Delta
baseline at revision 10 without inventing a retroactive 9 -> 10 comparison.
Run B advanced 10 -> 11 with the sidecar synchronized, preserved
strategyDeltaStartedRevision=10, compared format mix over 15/15 creatives and
all six semantic dimensions over the same 12 exact-family semantic records, and
reported zero movement. Semantic coverage remained 0.8, so subset comparisons
remained explicit and population semantic claims stayed blocked. Three
source-volatility-only Diff changes created zero Strategy Delta events. I2 added
zero provider calls and emitted no performance, spend, or causal-strategy claim.
Evidence is recorded in
benchmarks/i2_strategy_delta_apify_acceptance_2026-10-14.json.
Stage 12 received real Apify acceptance on 2026-10-04 using the Alert Engine from
commit 55ca256bdb675448b7af2f17ce10d6cb4c8af3ef. A fresh Nike/BR monitor
baseline against stage12-nike-br-alert-acceptance observed 14 creatives and
seeded five existing Stage 11 hypothesis IDs while emitting zero alert events and
zero delivery recommendations. An identical second run advanced revision 1 to 2
with all 14 creatives UNCHANGED/no_change, opened transition/trend Quality
gates, and suppressed all five hypotheses as already seen. Before the third run,
only the Stage 12 alert sidecar key was deleted. The Actor advanced revision 2 to
3 with the same creative-set hash, reported alertStateSynchronized=false,
suppressed all five existing hypotheses under fail-closed deduplication, emitted
zero false alerts, and reseeded the current hypothesis set without a persistence
failure. No provider call, provider token, delivery recommendation, or performance
claim was emitted during acceptance. The compact evidence is recorded in
benchmarks/stage12_apify_acceptance_2026-10-04.json.
The Stage 3 historical creative benchmark was captured on 2026-10-01:
- advertiser: Nike, Inc.
- advertiser ID:
AR16735076323512287233 - region:
BR - 15 unique creative IDs
The Stage 4 normalizer benchmark was captured on 2026-10-02. It records the observed source-format distribution, preview-representation distribution, and representative time-semantics assertions without treating them as a permanent truth about Nike's inventory.
Reliability rules
- A failed fetch is never represented as an empty result.
- CAPTCHA and Google
/sorry/responses becomeSOURCE_BLOCKED. - Exhausted HTTP 429 retries become
SOURCE_RATE_LIMITED. - Persistent transient failures become
SOURCE_UNAVAILABLE. - Malformed RPC exchanges become
SOURCE_PROTOCOL_ERROR. - Structural source drift becomes an explicit schema-change failure.
- Raw Google rows are preserved after normalization.
- Known source fields are type-checked before mapping.
- Unknown source fields are retained and reported instead of discarded.
- Creative format and preview representation are separate concepts.
- Source-reported days shown are never replaced by calendar-span math.
- Persistent state is versioned and never silently reset on schema mismatch.
- Truncated snapshots never replace the last untruncated snapshot.
- Known creatives are not deleted merely because a later scan does not return them.
- Overlapping state writers are detected before the metadata commit pointer moves.
- Older observations cannot rewind newer persisted state.
- Fingerprints never hash the entire normalized/raw JSON object.
- Content, source representation, advertiser metadata, lifecycle evidence, and source volatility are separate fingerprint dimensions.
- Signed preview plumbing such as
sig,htmlParentId, andresponseCallbackis excluded from content identity. - Missing content evidence produces an explicit insufficient-confidence result instead of a fabricated hash.
- Free-text advertiser resolution is conservative; ambiguous names require an explicit advertiser ID.
- Semantic labels are model inference with explicit provider/model/prompt provenance.
- Exact creative families are enriched once and cached; duplicate IDs do not multiply provider work.
- Population semantic mix requires complete inventory and complete semantic coverage.
- Semantic enrichment never turns longevity, recurrence, or visible copy into performance claims.
- Stage 11 reads semantic cache entries only; it never invokes a paid semantic provider or fallback.
- Strategy hypotheses are testing directions that require first-party validation, not performance predictions.
- Stage 12 never turns every observed difference into a notification; Diff and Quality gates remain authoritative.
- Cold-start baselines seed monitoring and strategic dedupe state without generating delivery recommendations.
- Representation-only, metadata-only, and indeterminate transitions do not become actionable alerts.
- Strategic hypotheses are deduplicated across synchronized monitor revisions.
- Stage 12B never changes Alert Engine eligibility; it consumes only accepted alert output.
- MISSING watch events can request reconciliation without becoming external notifications.
- Delivery outbox messages are deterministic and versioned; only
deliveryRecommended=truealerts enter the outbox. - Webhook delivery is optional, fail-soft, and uses deterministic idempotency keys.
- Stage 13 report claims resolve to exact creative/family/transition evidence IDs.
- 24h/7d reporting windows require persisted AdPulse observation history; source ad dates are never substituted.
- Volatile JavaScript preview plumbing is never promoted into a stable report evidence link.
- Competitive Brief generation performs zero provider calls and preserves all upstream performance/absence guardrails.
- Creative Lineage uses exact-content families as graph nodes before considering semantic similarity.
- Same-ID content mutation is an observed sequence; it is never rewritten as causal ancestry.
- Semantic variant relationships remain candidates with explicit confidence and ambiguity; tied top candidates select no parent.
- Creative Lineage performs zero paid provider calls and reuses current semantic results or validated exact-family cache only.
- Lineage continuity lives in a revision-bound sidecar and cannot rewrite State, Diff, Quality, or Alert truth.
- Public-ad persistence/reuse/lineage never becomes a winner, profitability, ROAS, CPA, or conversion claim.
- Strategy Delta compares exact revision-aligned evidence membership, not aggregate percentages alone.
- Hook/offer/CTA/message/visual/promise deltas always expose previous/current denominators and semantic coverage.
- Semantic profile changes block semantic delta comparison instead of masquerading as competitor movement.
- Partial semantic coverage remains subset evidence and cannot silently become a population-level strategy claim.
- Truncated inventory never replaces the last complete Strategy Delta baseline.
- Strategy Delta events are non-delivery evidence records and never imply spend, performance, or causal strategy intent.
- Display URLs are never silently promoted to exact landing URLs.
- I3 never requests Google ad click trackers; encoded destination evidence is decoded locally where defensible.
- Direct landing-page observation validates every URL/redirect against public DNS/IP safety rules.
- Raw landing-page HTML is never persisted; page message/action/offer/structure are separate fingerprint channels.
- Ad/page cochange is temporal evidence, never causal attribution or a performance claim.
See docs/ARCHITECTURE.md, docs/NORMALIZER.md, docs/STATE_STORE.md,
docs/FINGERPRINTING.md, docs/DIFF_ENGINE.md, docs/QUALITY_ENGINE.md,
docs/INTELLIGENCE_ENGINE.md, docs/SEMANTIC_ENRICHMENT.md,
docs/OPPORTUNITY_ENGINE.md, docs/ALERT_ENGINE.md, docs/MONITORING_DELIVERY.md,
docs/COMPETITIVE_BRIEF.md, docs/CREATIVE_LINEAGE.md,
docs/STRATEGY_DELTA.md, docs/AD_LANDING_INTELLIGENCE.md, and
docs/INNOVATION_ROADMAP.md.
Actor input
For deterministic monitoring, prefer advertiserIds. searchTerms are
supported, but ambiguous advertiser names intentionally fail instead of silently
attaching monitoring state to the wrong entity.
Example:
{"advertiserIds": ["AR16735076323512287233"],"searchTerms": [],"region": "BR","maxAds": 100,"proxyConfiguration": {"useApifyProxy": true},"mode": "pulse","stateNamespace": "default","strictSchema": true}
stateNamespace is an advanced isolation key. Runs with the same namespace,
advertiser ID, and region share persistent history. Different namespaces keep
independent histories.
Each Dataset item contains the stable normalized observation plus the original
Google source row under sourceRow. RUN_SUMMARY contains collection,
normalization, and state-commit diagnostics. Expected AdPulse failures are saved
as RUN_ERROR before the run fails.
Normalization semantics
The current inferred source enum is versioned:
1 -> TEXT2 -> IMAGE3 -> VIDEOother -> UNKNOWN
Known mappings are deliberately marked formatConfidence = medium because
Google does not publish a supported SearchCreatives schema.
Preview representation is independent:
image_snapshotjavascriptmixedunknown
For example, a TEXT creative may be archived as an image snapshot. AdPulse does
not relabel that creative as IMAGE merely because its preview is an <img>.
Time fields are also deliberately distinct:
firstShownAt source timestamplastShownAt source timestampsourceDaysShown source-reported countsourceDateSpanDays derived calendar-date spanobservedAt when AdPulse saw the row
These fields must not be substituted for one another.
Durable state
The active fingerprint-aware State Store uses the named key-value store adpulse-state-v2. Each target
keeps a current snapshot, previous snapshot, latest untruncated snapshot, and
compact per-creative memory.
Creative state is distributed across 64 deterministic shards. Updated shards are written under unique commit-specific keys and target metadata is moved last, making the metadata record the commit pointer. Superseded shards are retired after a successful commit.
State Store v2 still records observation memory only. Stage 7 reads that committed history before the next State commit and performs change classification without changing the Stage 6 state schema.
Fingerprinting
Every normalized observation now receives explicit versioned fingerprint dimensions:
contentFingerprintrepresentationFingerprintmetadataFingerprintlifecycleFingerprintsourceVolatilityFingerprint
The content channel uses only defensible content-bearing evidence. Image archive
locators are canonicalized without query/fragment noise. Google JavaScript
preview URLs retain only content-bearing parameters such as creativeId,
itemIds, and assets; signed/runtime parameters are excluded.
Fingerprinting is local and deterministic: no extra Google request, browser, image download, LLM, or external database is required.
The active profile is versioned as
adpulse-creative-fingerprint-v2. Each dimension also exposes per-component
digests so the future Diff Engine can identify which semantic field changed
without storing signed preview URLs or re-hashing raw JSON. See
docs/FINGERPRINTING.md.
Explainable Diff Engine
Stage 7 classifies creative transitions with policy
adpulse-explainable-diff-v2.
The first monitor run is BASELINE, never automatically NEW. A creative can
be called NEW only when a prior pagination-complete baseline proves it was
absent. Without that evidence the safer status is NEWLY_OBSERVED.
For existing creatives, Diff compares the fingerprint dimensions independently:
contentrepresentationmetadatalifecyclesource_volatility
CHANGED is reserved for defensible content mutation. Representation, advertiser-metadata,
lifecycle, and source-volatility movement remain independently visible while non-content drift
keeps the creative status UNCHANGED. If content evidence cannot be compared safely, the result is
INDETERMINATE rather than a fabricated binary answer.
Absence follows an evidence ladder:
truncated/incomplete -> no absence inferenceone complete absence -> MISSINGprior complete absence + complete reconciliation -> CONFIRMED_MISSINGlater return -> REAPPEARED
Every result includes deterministic reason codes, changed semantic component
names, confidence, attention level, alert eligibility, and a human-readable
explanation. Non-trivial compact transitions are also written to DIFF_EVENTS
in the default key-value store.
A small versioned Diff sidecar remembers already-confirmed missing creatives so scheduled reconciliation does not repeatedly alert the same disappearance. Stale sidecar revisions are ignored fail-safe rather than overriding State.
See docs/DIFF_ENGINE.md.
Evidence Quality Engine
Stage 8 uses policy adpulse-evidence-quality-v1 to decide which observations
and transitions are admissible to later competitive-intelligence layers.
It deliberately does not emit a weighted evidence score. Instead it reports independent coverage dimensions for identity, content, format, lifecycle, transition comparability, and creative-family readiness. A critical failure in one dimension cannot be hidden by strengths in unrelated dimensions.
Per creative, the Dataset exposes gates for snapshot, transition, novelty,
creative-family, and lifecycle intelligence. Per advertiser target,
QUALITY_SUMMARY exposes separate gates for inventory analysis, format mix,
family concentration, lifecycle aggregation, trends, and absence reasoning.
A truncated run can therefore preserve valid local creative evidence while blocking advertiser-wide population metrics. Likewise, a stale Diff sidecar blocks absence intelligence without incorrectly invalidating content or format evidence.
Stage 8 does not infer spend, impressions, clicks, conversions, ROAS, profitability, or "winner/loser" status from public Transparency data.
See docs/QUALITY_ENGINE.md. Stage 8 real Apify acceptance was completed on
2026-10-03 and is recorded under benchmarks/stage8_apify_acceptance_2026-10-03.json.
Evidence-backed Intelligence Engine
Stage 9 uses policy adpulse-evidence-backed-intelligence-v1 and consumes Stage 8
Quality gates without weakening them.
The v1 engine adds deterministic exact-content creative families, format mix, family concentration, lifecycle distributions, observed activity, monitoring persistence, complete-baseline trend deltas, and exact cross-advertiser family reuse.
A small Intelligence sidecar preserves the last complete aggregate baseline across truncated runs, so recovery compares complete inventory to complete inventory rather than treating a partial scan as the new reference.
Stage 9 intentionally does not call longevity, repeated observation, or family concentration "performance". It makes no spend, impressions, clicks, conversions, ROAS, profitability, or winner/loser inference from ordinary Google Ads Transparency evidence.
See docs/INTELLIGENCE_ENGINE.md. Stage 9 real Apify acceptance was completed on
2026-10-03 and is recorded under benchmarks/stage9_apify_acceptance_2026-10-03.json.
Evidence-Bound Semantic Enrichment Engine
Stage 10 introduces optional, cost-bounded semantic creative analysis under policy
adpulse-evidence-bound-semantic-enrichment-v1.
It is disabled by default. When enabled, Stage 10 currently analyzes only
Quality-admitted image_snapshot creatives with a vision-capable OpenAI Responses
API model. OPENAI_API_KEY is read only from the Actor environment; the secret is
never accepted in Actor input or persisted in Dataset/KVS/logs.
The semantic contract exposes visible-evidence-bound labels for hook, offer, CTA,
message angle, visual theme, and primary promise. Every signal includes confidence
and short evidence observations, and unsupported signals remain unknown / none
rather than being forced into a category.
Provider work is deduplicated by the Stage 9 exact intelligenceFamilyId. One exact
family receives at most one new provider analysis per run, and successful results are
cached by prompt version + provider + model + image detail + family ID. Cache hits do not consume
the run-level maxFamiliesPerRun budget.
Population semantic mix is stricter than local enrichment: it is emitted only when Stage 8 inventory intelligence is eligible and every observed creative has semantic coverage. Counts use exact content families so duplicate creative IDs cannot artificially overweight a hook/CTA/offer pattern.
Stage 10 does not infer spend, impressions, clicks, conversions, CTR, ROAS, profitability, audience targeting, or winner/loser status. Semantic labels are model inference with explicit provenance, not source-authenticated facts.
See docs/SEMANTIC_ENRICHMENT.md and docs/STAGE10_MARKET_RESEARCH.md. Stage 10
real Apify acceptance was completed on 2026-10-04 and is recorded under
benchmarks/stage10_apify_acceptance_2026-10-04.json.
Evidence-Bound Opportunity Engine
Stage 11 turns existing Quality, Intelligence, and cached Stage 10 semantic evidence
into deterministic creative testing hypotheses under policy
adpulse-evidence-bound-opportunity-v1.
Stage 11 is cache-only. It does not require OPENAI_API_KEY, does not invoke the
OpenAI API, and has no paid-provider fallback. Missing semantic cache entries remain
explicit cache misses. Every STRATEGY_SUMMARY hard-codes provider-call telemetry to
zero so a live acceptance run can prove the cost boundary operationally.
Strategy aggregation uses exact Stage 9 families. It reports observed semantic pattern counts and dominant patterns over the available cached subset, then emits deterministic contrast-test hypotheses that explicitly require validation with first-party campaign metrics. It never labels a pattern as a winner or infers spend, CTR, ROAS, conversions, or profitability.
Population-level gap claims are allowed only with complete inventory and complete semantic coverage. With partial semantic coverage, cross-advertiser differences are phrased only as categories observed in one available semantic subset and not the other.
See docs/OPPORTUNITY_ENGINE.md and docs/STAGE11_MARKET_RESEARCH.md. Stage 11
real Apify acceptance was completed on 2026-10-04 and is recorded under
benchmarks/stage11_apify_acceptance_2026-10-04.json.
Evidence-Aware Monitoring & Alert Engine
Stage 12 turns the typed Diff, final Quality, and deterministic Strategy outputs into a
small notification stream under policy adpulse-evidence-aware-alert-v1.
It writes ALERT_EVENTS, ALERT_SUMMARY, and RUN_SUMMARY.alerts. Cold-start
baselines create no delivery recommendations. Proven NEW creatives, admitted content
changes, and confirmed disappearances can become actionable; one-observation missing
states and unproven novelty remain watch-level. REAPPEARED creatives are recovery
events.
Stage 12 also keeps a revision-aligned alert sidecar containing previously seen strategic hypothesis IDs. Existing Stage 11 hypotheses therefore do not generate the same strategic notification every run. Missing or stale alert state fails that deduplication closed instead of calling every current hypothesis "new".
Stage 12 v1 deliberately stops at a deliveryRecommended boundary. It does not send
email, Slack, or arbitrary webhooks and does not add PPE billing calls. This keeps live
acceptance deterministic while providing a stable contract for the next product layer.
See docs/ALERT_ENGINE.md and docs/STAGE12_MARKET_RESEARCH.md. Stage 12 real Apify acceptance was completed on 2026-10-04 and is recorded under benchmarks/stage12_apify_acceptance_2026-10-04.json.
Monitoring Orchestration & Delivery
Stage 12B operationalizes the accepted Stage 12 output under two new, separate
contracts: adpulse-monitoring-orchestration-v1 and
adpulse-alert-delivery-v1.
The default cadence is configurable but starts at a 24-hour pulse, 7-day periodic
reconciliation, and a 6-hour reconciliation hint after a single complete MISSING
watch. That watch remains deliveryRecommended=false; orchestration may request a
stronger confirmation scan but cannot promote the alert itself.
Every run writes ORCHESTRATION_SUMMARY, DELIVERY_OUTBOX, and
DELIVERY_SUMMARY. The outbox contains only Stage 12 events already marked
deliveryRecommended=true, with deterministic message IDs and channel-ready
presentations for webhook/API/Slack/email.
Delivery defaults to outbox, which performs no network call. Optional webhook
mode reads the HTTPS destination from an Actor environment variable, adds
Idempotency-Key, retries only bounded transient failures, records successful
message IDs in a separate delivery sidecar, and leaves delivery failures fail-soft.
Apify schedules remain explicit platform resources rather than hidden Actor side
effects. Stage 12B publishes schedule intent; provisioning can use two exclusive
schedules with the same monitor namespace: routine pulse plus periodic
reconciliation.
See docs/MONITORING_DELIVERY.md and docs/STAGE12B_APIFY_NOTES.md.
Stage 12B received real Apify acceptance on 2026-10-04 using commit
74ee33af933a95c866239bf9a4edbf3551e2ad7d. A fresh Nike/BR baseline and
stable rerun proved the 24h pulse / 168h reconciliation / 6h MISSING-follow-up
cadence contract, zero alert noise for stable evidence, and zero delivery network
activity in default outbox mode. A separate clean semantic identity
(imageDetail=low) first proved 12 Stage 11 cache misses with no hypotheses.
The next run enabled Stage 10, analyzed all 12 eligible exact families with 12
provider calls and zero provider failures, after which Stage 11 read the newly
written cache without provider fallback and produced five evidence-bound hypotheses.
Stage 12 emitted five synchronized strategic alerts, all
deliveryRecommended=true, and Stage 12B produced exactly five versioned delivery
messages with webhook/API/Slack/email presentations while still performing zero
network calls in outbox mode. The Alert Engine policy remained
adpulse-evidence-aware-alert-v1, alertContractReinterpreted=false, and no
performance claim was emitted. Compact evidence is recorded in
benchmarks/stage12b_apify_acceptance_2026-10-04.json.
Evidence-Backed Competitive Brief / Reporting
Stage 13 introduces policy adpulse-evidence-backed-competitive-brief-v1.
It writes COMPETITIVE_BRIEF, REPORT_SUMMARY, and
RUN_SUMMARY.reporting. The brief is deterministic and cache-only: the reporting
layer never invokes a paid semantic provider.
Unlike a generic summary layer, Stage 13 builds a claim-to-evidence graph. Pattern claims and hypotheses reference exact Stage 9 family evidence; family records reference their member creative IDs; creative evidence preserves content-fingerprint provenance and source timestamps. JavaScript preview URLs with signed/runtime plumbing remain visible in the Dataset but are not promoted to stable report links.
The report distinguishes:
- previous complete observation comparisons from unavailable transition evidence;
- 24-hour and 7-day monitoring windows from source first/last-shown dates;
- observed/derived claims from test hypotheses;
- available semantic-subset patterns from exhaustive population claims.
A bounded reporting-history sidecar stores compact observation summaries for future 24h/7d comparisons. Missing or malformed reporting history fails those time windows closed without affecting State, Diff, Quality, Strategy, Alerts, or Delivery.
Every hypothesis still requires first-party validation, and every brief hard-codes
performanceClaimMade=false.
See docs/COMPETITIVE_BRIEF.md.
Stage 13 received real Apify acceptance on 2026-10-04 using source commit
5203abf9a97af3ab107c11f90408318d9fabe3e7. A fresh Nike/BR baseline
proved that the reporting layer creates a claim-to-evidence ledger without
fabricating prior-change or 24h/7d narratives, while keeping reporting provider
calls and paid-provider calls at zero. A stable rerun advanced revision 1 to 2
with all 14 creatives UNCHANGED/no_change, enabled the previous-observation
comparison, and produced the bounded conclusion that no evidence-backed creative
change was detected; 24h/7d windows correctly remained unavailable because the
persisted observation history was only minutes old. Creative/family evidence IDs
and hypothesis IDs remained stable while brief, claim, and transition identities
remained revision-scoped. A separate Nike + ADIDAS canary then proved subset-safe
cross-advertiser reporting: Nike had 12/14 semantic-family coverage while ADIDAS
had one observed family and zero cached semantic families. The comparison stayed
scoped to available_cached_semantic_exact_families, kept
gapClaimEligible=false, absenceClaimMade=false, and
performanceClaimMade=false, and did not convert ADIDAS's zero semantic
coverage into a competitor-absence claim. Compact evidence is recorded in
benchmarks/stage13_apify_acceptance_2026-10-04.json.
Evidence Experience API & Dashboard
Stage 14 introduces two product-facing policies:
adpulse-evidence-experience-v1 and adpulse-read-model-api-v1.
It writes DASHBOARD.html, DASHBOARD_DATA, API_INDEX,
EXPERIENCE_SUMMARY, and a non-billable COMMERCIAL_METERING preview.
The dashboard is a CQRS-style read model over the accepted engines. It never recomputes creative identity, Diff status, Quality eligibility, Strategy hypotheses, Alert severity, or absence claims. The user experience follows progressive disclosure:
signal-> plain-language explanation-> exact evidence-> forensic fingerprint/source detail
The HTML dashboard is self-contained: no React, CDN, external JavaScript, external CSS, browser-side AI, or browser-side evidence inference. Stable archived images can render as evidence; volatile JavaScript preview URLs are never promoted to stable dashboard links.
The read-model API exposes overview, competitors, timeline, creatives, exact families, opportunities, signals, claims, evidence, comparison, filters, and guardrails.
Stage 14 also adds a separate bounded presentation timeline sidecar. It preserves significant Diff/Alert events across runs without changing State, Diff, Quality, Strategy, Alerts, or Delivery truth.
COMMERCIAL_METERING does not charge users. It measures candidate visible-value
units for the later PPE experiment. The candidate primary unit is one
competitor-snapshot; internal fingerprints, Diff comparisons, Quality gates,
cache hits, dashboard rendering, claim resolution, retries, and alert deduplication
remain explicitly non-billable.
See docs/STAGE14_EXPERIENCE.md and docs/STAGE14_MARKET_PRICING.md.
Stage 14 / 14.2 received live Apify acceptance on 2026-10-14 using final
renderer commit b2c721bf578a6e23df2766bc882a724507301be6 and build
0Kv6WY9nw5KWjzs4D. A corrected Nike/BR stable rerun advanced revision
1 -> 2 with 15 unchanged creatives and two persisted monitoring snapshots.
A separate Nike + ADIDAS canary observed 16 creatives across two complete
inventories while preserving the semantic-subset boundary: Nike had 80%
semantic-family coverage and ADIDAS 0%, but Compare emitted no competitor-absence,
gap, or performance claim. The final UI passed the 12-locale/Arabic-RTL contract,
the corrected I5 hypothesis presentation, provider-free localization, and the
HTML/CSV/JSON/Print export contract. Commercial metering remained non-billable
and produced one candidate competitor-snapshot per visible target. Evidence is
recorded in
benchmarks/stage14_experience_apify_acceptance_2026-10-14.json.
Stage 14.1 adds provider-free dashboard localization for English, Brazilian Portuguese,
Spanish, French, German, Italian, Simplified Chinese, Japanese, Korean, Arabic, Hindi,
and Russian. Canonical evidence IDs/categories remain unchanged, Arabic uses RTL,
locale preference is saved in the browser, and #lang=<locale> can override the UI
without modifying an Apify signed request. Localization makes zero provider calls and
is explicitly non-billable internal work.
Stage 14.2 hardens that experience under
adpulse-dashboard-localization-v2: all 12 locales now carry the complete canonical
UI-string set; Arabic receives mixed-script bidi isolation; confidence, creative-format,
and event-kind labels are localized; primary Evidence cards hide raw internal scope
codes behind progressive technical disclosure; Evidence Health no longer publishes a
synthetic blended percentage; and Monitoring Pulse reconstructs the current aggregate
creative count from each competitor's latest persisted observation. These changes
remain browser-side, provider-free, non-billable, and do not change evidence identity.
Stage 14.2 also adds a low-cost adpulse-dashboard-export-v1 layer. Users can export
the self-contained dashboard as HTML, export a canonical creative inventory as CSV,
download DASHBOARD_DATA as JSON, or use the browser print pipeline to print/save a
PDF. Export actions run entirely in the browser, make zero provider/backend calls,
remain non-billable, and never expose volatile JavaScript preview URLs in CSV.
Private PPE Economics & Monetization Readiness
Stage 15 adds three private observability artifacts under
adpulse-private-ppe-economics-v1: ECONOMICS_SUMMARY, COST_LEDGER, and
PPE_BENCHMARK_PLAN.
The optional economicsBenchmark input reads a provisional safe subset of current
Apify Run usage. It does not enable billing and cannot alter evidence truth. Raw
platform usage is repriced across paid Apify cost tiers, semantic-provider token cost
stays separate, cache hits and retries remain internal work, and unknown non-zero usage
dimensions block a complete cost conclusion instead of being silently ignored.
Running-run cost telemetry is not treated as final. Private acceptance compares it with finalized completed-run usage after Apify's eventual-consistency delay. The pricing gate also refuses to calculate p95 from a single run and requires a representative multi-run sample set.
See docs/STAGE15_ECONOMICS.md and docs/STAGE15_MARKET_RESEARCH.md.
Commercial Policy & Billing Dry-Run
Stage 16 converts the accepted Stage 15 p95 evidence into a versioned beta pricing
candidate while keeping billing disabled. The candidate primary event is one visible
competitor-snapshot at USD 0.10. Semantic provider work remains included in the
snapshot candidate during beta rather than becoming a second charge. Each run emits
COMMERCIAL_POLICY with simulated customer charge, developer net, p95 cost allowance,
profit/margin, cadence forecast, and actualChargeCalls=0.
Stage 16 live dry-run acceptance is complete for visible snapshot metering and real
semantic provider work. Activation preflight is accepted, and the private Apify Console
PPE configuration now contains only competitor-snapshot at USD 0.10 with platform
usage included. A guarded SDK integration reads effective pricing/remaining budget
before expensive work and settles only after a completed run.
Runtime billing is disabled by default. ADPULSE_PPE_BILLING_ENABLED=true is required
for any charge path, and live platform charging additionally requires
ADPULSE_PPE_ALLOW_LIVE_CHARGES=true. The runtime is now locked to Apify SDK 4.1.0
and every settlement uses an idempotency key. CI/local tests prove idempotent replay
and fail-closed max-charge admission. The post-upgrade Apify platform canary is accepted: SDK 4.1.0 observed the configured
USD 0.10 event, admitted the full two-snapshot workload, and finished with zero charged
events. All non-billing technical gates are now closed. The next gate is explicit human
approval for a private, bounded two-snapshot live-money canary; public Store activation
remains blocked until that canary is accepted.
Accepted live evidence is recorded in
benchmarks/stage16_live_dry_run_acceptance_2026-10-16.json.
See docs/STAGE16_COMMERCIAL_POLICY.md.
Innovation I3 live acceptance
Ad ↔ Landing Page Intelligence received real Apify acceptance on 2026-10-14.
A live Google renderer delimiter artifact first produced a false duplicate landing
identity; destination profile v2 corrected the parser boundary and failed the old
sidecar closed. The corrected baseline advanced State 12 -> 13 with one exact landing
URL, five display-only destinations, nine unavailable destinations, zero provider calls,
zero ad-click requests, and zero false events. The synchronized warm run advanced
13 -> 14, reused all 15 stable creative-detail records with zero detail lookups,
reobserved the single exact Nike landing page successfully at HTTP 200, retained
rawHtmlPersisted=false, and emitted zero page/cochange events. The user-observed
warm runtime was 17 seconds. Evidence is recorded in
benchmarks/i3_ad_landing_apify_acceptance_2026-10-14.json.
Productization roadmap
With Stage 12B live acceptance complete, the Store/product track continues with:
- HTML dashboard / human-readable monitoring view;
- finish the friendly Store input/output presentation around the now-stable contracts;
- complete the Stage 15 private Pay-per-event cost matrix before enabling billing;
- private end-to-end tests with schedules, alert consumers, quotas, and failure cases;
- Store beta publication.
The Stage 12 branch already starts item 2 by reorganizing the existing Apify input UI into Competitors, Monitoring, Reliability, and Optional AI sections without breaking the underlying input keys.
Development
The project uses Python 3.14 and uv.
uv syncuv run ruff check .uv run ruff format --check .uv run ty check adpulse testsuv run pytest -q
Source/state canaries are manual regression tools:
uv run python -m adpulse.canaryuv run python -m adpulse.normalizer.canaryuv run python -m adpulse.state.canaryuv run python -m adpulse.fingerprint.canary
They distinguish legitimate source-data changes from transport, normalization, or state regressions.
Pipeline
Collector-> Normalizer-> Fingerprinting-> Diff Engine (reads previous committed state)-> Quality creative gates-> Intelligence evaluation-> Semantic enrichment (optional provider/cache)-> Dataset-> Semantic cache + State / Diff / Intelligence sidecars-> Quality + Intelligence + Semantic summaries-> Opportunity Engine (read-only semantic cache, zero provider calls)-> STRATEGY_SUMMARY-> Alert Engine (Diff + final Quality + Strategy policy)-> ALERT_EVENTS + ALERT_SUMMARY-> Monitoring Orchestration-> ORCHESTRATION_SUMMARY-> Delivery Outbox / optional webhook-> DELIVERY_OUTBOX + DELIVERY_SUMMARY-> Competitive Brief / Reporting-> COMPETITIVE_BRIEF + REPORT_SUMMARY-> Experience Read Model / Dashboard-> DASHBOARD_DATA + API_INDEX + DASHBOARD.html-> Private Economics Observer-> COST_LEDGER + ECONOMICS_SUMMARY + PPE_BENCHMARK_PLAN-> Guarded PPE admission (before expensive work)-> Product/state/output completion-> Guarded PPE settlement (disabled by default; visible competitor snapshots only)-> PPE_BILLING + COMMERCIAL_POLICY
Each layer must have explicit acceptance criteria and regression coverage before the next layer is treated as complete.
Cross-platform Content Graph
Innovation I4 adds a provenance-preserving graph above Creative Lineage and Ad↔Landing evidence. Platform-specific families remain separate nodes; exact asset reuse, normalized copy reuse, shared exact landing destinations, and semantic concept similarity are represented as typed edges rather than silent identity merges.
The accepted graph substrate is Google-backed. A public Meta Ad Library second-source adapter is now implemented behind explicit advertiser/domain mapping and is in live validation. Graph comparison itself remains provider-free; when the optional Meta adapter is enabled, its bounded public-source requests are metered separately and remain non-billable internal work.
See docs/CROSS_PLATFORM_CONTENT_GRAPH.md.
Innovation I4 Google substrate live acceptance
The Cross-platform Content Graph substrate plus Google Ads Transparency adapter was accepted on live Apify on 2026-10-14. Two same-namespace runs advanced State 14 -> 15 -> 16, kept all 15 graph node IDs stable, synchronized the revision-bound sidecar, created zero same-platform cross-platform edges, added zero provider/source calls, and preserved all identity/performance/causality guardrails. Warm runtimes observed by the user were 18s and 22s.
This is intentionally not full I4 acceptance. Cross-platform comparison remains ineligible until a second real platform source is integrated.
Evidence: benchmarks/i4_google_substrate_apify_acceptance_2026-10-14.json.
Meta second-source adapter
I4 now includes an optional public Meta Ad Library SSR adapter. It requires an explicit mapping from an existing Google advertiser ID to a Meta search query and verified advertiser domain. Only public Meta ads whose landing/display evidence matches that domain are admitted to the same advertiser entity.
The adapter uses anonymous public-library evidence, no login, no private account data, no provider calls, and no GraphQL pagination in v1. Meta failure is fail-soft: Google monitoring still completes, and the graph records the external-source status instead of interpreting failure as absence.
Innovation I4 full live acceptance
Cross-platform Content Graph received full live acceptance on 2026-10-14 using two real public sources: Google Ads Transparency and Meta Ad Library.
Two same-namespace runs advanced State 16 -> 17 -> 18, kept the 15 accepted Google nodes stable, retained one verified Meta Nike node with stable source identity/copy/ landing evidence, and preserved cross-platform eligibility. The Meta adapter examined 30 public SSR rows per run and admitted exactly one after nike.com ownership verification; 29 non-matching rows were rejected rather than merged.
No cross-platform edge was emitted because the current Google and Meta evidence did not share exact copy or exact landing identity. This zero-edge result is accepted evidence of noise suppression, not missing functionality.
All identity/performance/causality guardrails remained false, provider calls remained zero, and observed runtimes were 20 seconds on both runs.
Evidence:
benchmarks/i4_cross_platform_apify_acceptance_2026-10-14.json.
Evidence-weighted Opportunity Engine
Innovation I5 upgrades Stage 11 strategy hypotheses with deterministic evidence weighting. Each test candidate exposes observed-family coverage, semantic coverage, per-dimension usable coverage, source freshness, Strategy Delta comparability, ambiguity, limitations, and a stable opportunity ID.
The 0–100 evidenceStrengthScore measures evidence sufficiency only. It is not a winner, conversion, ROAS, spend, or business-value score. Coverage/freshness gates can cap confidence, and strong observed-population gap claims are refused unless inventory, family, semantic, dimension, and freshness gates are complete.
I5 makes zero provider calls and zero additional ad-source requests. All action-oriented hypotheses require first-party validation.
See docs/EVIDENCE_WEIGHTED_OPPORTUNITY.md.
Innovation I5 received live Apify acceptance on 2026-10-14. Two consecutive same-namespace Nike/BR runs advanced State 18 -> 19 -> 20, kept all 15 creatives unchanged, reproduced the same five durable opportunity IDs with identical source-hypothesis bindings, scores and confidence classes, refused all six strong-gap dimensions under 80% semantic-family coverage, and introduced zero provider/performance/causal/market-absence claims. Evidence is recorded in benchmarks/i5_opportunity_apify_acceptance_2026-10-14.json.
First-party Validation Loop
Innovation I6 links customer-owned experiment results to the durable opp_*
identities accepted in I5. The input is optional and disabled by default.
Each result is an immutable aggregate record containing a stable validationId,
advertiser/opportunity identity, a customer/system-declared outcome
(validated, rejected, or inconclusive), one primary metric, control/variant
values, optional aggregate sample sizes, timestamp, and source-system provenance.
AdPulse calculates descriptive absolute/relative deltas only. It does not infer
statistical significance, causal effect, or winner status from aggregate values.
A first-party result also never changes the public-competitor
evidenceStrengthScore, confidence caps, gap eligibility, or performance truth.
Replaying an identical validation record is idempotent. Reusing the same
validationId with different payload content fails closed and preserves the
original record. The persistent ledger is exposed through
FIRST_PARTY_VALIDATION_SUMMARY, FIRST_PARTY_VALIDATION_LEDGER, the Stage 14
read model, and API index.
See docs/FIRST_PARTY_VALIDATION.md.
Innovation I6 received live Apify acceptance on 2026-10-14 using a dedicated
i6-nike-br-acceptance namespace. Four successful runs advanced State
0 -> 1 -> 2 -> 3 -> 4 and proved new-record ingestion, exact-payload
idempotency, fail-closed immutable-ID conflict handling, and ledger continuity while
ingestion was disabled. The accepted synthetic fixture remained customer-declared
inconclusive; AdPulse recorded descriptive deltas only and never inferred
significance, causality, winner status, or competitor performance. A modified
validationId payload was rejected while the original record, payload hash,
ingestion revision, and firstPartyEvidenceId remained intact. A separate Google
source timeout between successful runs did not advance State or mutate the I6 ledger.
Evidence is recorded in
benchmarks/i6_first_party_validation_apify_acceptance_2026-10-14.json.