A competitor snapshot is not a competitor diff.
Most competitor reports describe the present in change-flavoured language, and nothing inside them tells you which one you are reading. competitor-watch makes changed a fact the storage layer proves rather than a claim a writer makes: snapshots land in a content-addressed store, an identical week creates no revision at all, and every change ships with the revision pair and the add and delete counts behind it.
For founders, operators and agencies who get a weekly competitor update and cannot tell which lines in it are news — and who would rather be told “we could not look this week” than be quietly handed a re-description.

opvs-skills install @di-atomic/competitor-watchThen say: “run the weekly competitor scan” or “did anyone change pricing?”.
Why a weekly competitor report drifts
re-read their pricing page describe what it says now write it up as this week's update → did any of this actually move? → the crawl timed out; is that "no change"? → their LinkedIn dates are blank — stopped? → four ads vanished off page one — pulled? → what is their impressions index? (-1) (send it Monday, sound certain)
Every one of those is a place a report prints a confident number that is wrong. None of them are visible to the reader, or to the person who wrote it.
set declared per call, never inferred
store content-addressed; same bytes,
no revision, so unchanged is free
changed from_rev → to_rev, +4 / -3
timeout queued, a first-class verdict
blank not_disclosed (EU-only field)
sample labelled 30 of 377, appearances only
spend never emitted, on any platform
demand who ranks for "them + alternative"
A claim you can check without trusting the writer, because the evidence is what produced the claim in the first place.
If a claim can only be checked by the person making it, it will drift.
Five verdicts, and the last three never collapse into the second
changedCarries a revision pair and its additions and deletions counts. It cannot be written without them.unchangedThe store created no new revision from an identical write. A server-side fact, and free.queuedThe crawl is async and had not returned. Not the same as nothing having happened.not_disclosedThe platform publishes this to nobody. A US advertiser’s blank LinkedIn dates are lawful, not broken.unavailableI tried and failed. Recorded as a failure to look, never as a finding.What you actually get
A diff, not a re-snapshot
Snapshots go to a content-addressed store. Writing the same bytes back creates no revision, so “nothing moved” costs nothing and is decided by the substrate. A real change reports the revision pair and the counts between them.
It refuses to invent a number
No ad library discloses commercial spend, so none is ever emitted. A field can also be filled and still empty: impressions_with_index came back non-null on 30 of 30 ads, every one holding minus one. Sentinels are read at the leaf, never at the wrapper.
It sees who buys no ads at all
Searching a competitor plus “alternative” costs one credit and returned five rivals in the top eight, each publishing content to intercept that search. An ad library shows what someone pays for. This shows who is hunting their unhappy customers.
One competitor, one week, start to finish
> "run the weekly competitor scan"
set ............ 4 competitors, declared, resolved
credit cost printed FIRST: 63/competitor
for a full paginated ad sweep
pricing ........ changed from_rev fec68254 → 6fab9dc9
+4 / -3 (Starter 19 → 24)
changelog ...... unchanged identical write, no revision
website ........ queued async crawl, worker_id null
NOT reported as "no change"
meta ads ....... 30 of 377 sampled (8%)
appearances claimed, absences NOT
linkedin ads ... 19 of 24 disclosed (EU-targeted)
5 not_disclosed, not "stopped"
google ads ..... volume + timing only; copy costs
25 credits/creative, so not sourced
spend .......... never emitted
demand ......... 5 of top 8 "alternative" results
are rivals; 2 absent from every
ad library we just read
verify-competitor-set-declared ....... PASS
lint-non-attributable-claims ......... PASS 0 uncited
verify-diff-vs-prior-week ............ PASS real parent
verify-parse-tolerance ............... PASS
The credit cost is printed before the scan starts, and a scan that cannot be finished is refused rather than half-run — a partially paginated sweep silently corrupts the following week’s diff, which is worse than not scanning at all.
Two things it refuses to get wrong


What it will tell you it cannot do
- Ad spend: unavailable everywhere. Meta publishes it for political ads only. It is never estimated, and never prefixed with a tilde.
- LinkedIn dates: EU-targeted ads only. 19 of 24 disclosed for one advertiser, 0 of 24 for two others. Per-ad targeting decides it, not the company’s address.
- Ad lists are samples. Meta returned 30 of 377, Google 40 of about 2,000. Set subtraction on page one manufactures churn that never happened.
- Google ad copy is not sourced. It is absent from the list response and costs 25 credits per creative. Google is a volume and timing signal only.
- LinkedIn post engagement does not exist. The response carries four keys and none of them is a like. Cadence only.
- Website reads are asynchronous. A crawl can still be queued when the report is due, and that is reported as queued.
I got tired of competitor reports I could not check.
competitor-watch is one skill in the system behind Di-Atomic — the marketing and compliance agency that runs cognitoAI, SpiderIQ and OPVS. If you want a weekly read on your market that tells you what moved, in any of our seven languages and including regulated categories, that is the day job.
Book a 30-min call with Di-AtomicJust want the skill? Get @di-atomic/competitor-watch free.