A run whose baseline store cannot be opened no longer dies. It used to call Actor.fail(),
so a customer — or Apify's automated quality test, which runs this Actor on their account —
saw a failed run with no output at all. That is what flagged the Website Change Monitor under
maintenance on 2026-08-22 and the App Store Review Monitor on 2026-08-28, and this Actor's public
runs show the same signature. It now falls back to the run's own key-value store, publishes every
location as a first sighting, puts the new STATE_STORE_UNAVAILABLE warning on every row, and says
so in the run's status message. It never claims a change. STATE_UNAVAILABLE is still raised
when the named store and the run's own store both refuse.
A first run now produces rows. With onlyReportChanges on — the default — a first sighting
was treated as "nothing changed" and nothing was pushed, so a customer's first run ended with an
empty dataset. That contradicted this Actor's README ("every location is a baseline row") and its
charge tiering, which prices a baseline as a check because "the customer got a starting point out
of it". The Store's daily auto-test is the sharper consequence: it flags an Actor whose default
input yields an empty dataset three days running as under maintenance. A first sighting is now
published and charged as a check; a genuinely quiet check still publishes nothing and still
advances the baseline.
The alert charge tier could never fire. event_for_record() compared record.event to the
enum member with is. PublicModel sets use_enum_values=True, so an enum field passed
explicitly to the constructor holds the plain string — and the runner always passes event
explicitly — making the comparison false for every row this Actor can produce. A real reputation move was
therefore charged at the cheap check tier. No customer was affected, because this Actor is free
and no prices are set; pricing it without this fix would have set a tier that silently earned
nothing, which is exactly the failure that importing the event names into
scripts/set_pricing.py is meant to prevent and cannot see.
Hardened the same comparison in runner.py, which tested published.status against
ManifestStatus.FAILED with is not. That one was not live: the failure manifests are built
with model_copy, which skips validation and so keeps the enum member, and the check behaved
correctly. It was one refactor away from not doing — any path that re-validates the record yields
the string instead — and a baseline that advances after a failed publish loses the change
permanently.
Added tests/test_storage.py, which drives event_for_record() with real rows rather than
asserting the CHARGED_EVENTS tuple, and tests/test_runner_ordering.py, which pins the
publish-then-advance ordering against both representations. Asserting the tuple is what let this
survive: it proves the two names exist, not that either is reachable.