All notable changes to this Actor are recorded here. Versions follow the ACTOR_VERSION constant
in src/change_monitor/constants.py.
First working version. Free; charge events are declared but no prices are set.
- A run whose saved baseline store cannot be opened now continues instead of failing. It records
each page fresh in the run's own storage and publishes every row with a
STATE_STORE_UNAVAILABLE warning, and the terminal status message leads with the fact that
nothing was compared. No page is ever reported as unchanged on such a run, because nothing was
there to compare it against — the rows are all first sightings, which is what they say.
Previously the whole run failed, which protected no customer data and cost the listing three
days of "under maintenance" when Apify's automated quality tests hit it on their own account.
If the run's own storage is unavailable too, the run still fails: there is no honest degraded
mode left.
- Checks a list of public URLs and reports
baseline, page-checked, or change-detected per
page per run.
- A noise filter that removes cookie and consent banners, carousels and sliders, ad slots, live
regions, and rendered timestamps before anything is compared, so a page whose only difference is
a rotating promo comes back 100% similar.
- Price, percentage, and stock-phrase detection. A change to any of them is reported at every
sensitivity, however small, and named in
changedSignals.
sensitivity, ignoreSelectors, and watchSelectors for narrowing what counts. Changing either
selector list starts a fresh baseline, because the old capture is no longer comparable.
- Baselines persist between runs in a named key-value store on the customer's own account,
configurable through
stateStoreName.
- Guards that report doubt rather than a change: a non-2xx response, a page that collapsed to a
fraction of its previous size, a watch selector that stopped matching, and a truncated read.
- Three charge events (
page-checked, change-detected, ai-summary-generated) wired through
the shared commit protocol, so monetising later is a price change rather than a redesign.
useBrowser renders pages in Chromium for sites that build their content in JavaScript. Off by
default, because it costs several times more per page. The destination policy is applied to
every request the page makes, not just the one asked for, and service workers are blocked so
they cannot route around it. A failed render falls back to the plain HTML and marks the row
warning rather than failing the check.
notifyWebhookUrl POSTs each change record to an address the customer supplies. Validated
through the same destination policy as a watched page, on every send rather than once, and
redirects are refused rather than followed — a redirect is a second destination that nothing
validated. Only changes are delivered, never the quiet checks.
aiApiKey, aiModel, and aiSummaryLanguage add a one-sentence summary to detected changes,
on the customer's own Anthropic or OpenAI key and their own bill. The provider is read from the
key prefix. Only the diff is sent, never the page, and only on a real change.
- The baseline is advanced only after the row has been published. If that write then fails the next
run may report the same change again; the run's status message says so. The alternative order
would lose an alert silently, which is worse.
- A summary that fails is a
AI_SUMMARY_FAILED warning on a row that still publishes, still
carries its diff, and is still charged at the change tier rather than the summary tier — billed
for what it actually carries.
- A webhook that fails is deliberately not a warning on the row. The check was fine; only the
delivery was not, and degrading
status for a customer's endpoint being down would drain that
field of the meaning it exists to carry. It is counted in the run's status message instead,
which is the one failure a webhook cannot report through itself.