# Changelog of Romanian Public Contracts Scraper (e-licitatie.ro / SEAP) (`darknezz/ro-public-contracts-scraper`) Actor

- **URL**: https://apify.com/darknezz/ro-public-contracts-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/darknezz/ro-public-contracts-scraper.md

## Changelog

### 0.3.2 — input typos stop failing the run, scans fit the budget (6 Nov 2026)

No input or output change. A run that hits either of these behaves better; nothing else moves.

- **An invalid `supplierCui` no longer fails the run.** Pasting a company name or a
  registration number ("Dedeman", "J40/1234/2020") used to end the run with `Actor.fail`,
  which paints a red X on the Store page and drags the actor's public success rate down for
  what is the caller's typo. The error record is still pushed to the dataset (never charged)
  and the status message names the problem; the run exits successfully.
- **The default `supplierCui` scan fits inside the time budget.** `maxScanNotices` default
  drops 1.000 → 250. At ~0.4 s per notice with 3 in flight, 1.000 notices outran the 240 s
  budget, so a scan that found nothing looked hung and was aborted by the caller. 250
  finishes well inside the budget; raise it deliberately for a deeper search.
- **One slow notice can no longer hold the whole scan open.** Each contract fetch is capped
  at the time left in the budget, so the concurrent scan stops when the budget says so
  instead of waiting for the slowest request.
- Two tests added: a typo is reported rather than failed, and the default scan fits the
  budget.

### 0.3 — reliability (F213, 2 Nov 2026; not yet built or published)

Input and output stay backward-compatible. Every existing input works as before, and every output field is unchanged. One optional input is new: `timeBudgetSecs`.

- **Results are delivered as they are found.** Each result is pushed and charged in the same call (`push_data(…, charged_event_name="result")`). Before, a run pushed and charged everything only at its very end, so a timeout, an abort or a crash late in the run lost all of it.
- **A time budget.** A run stops cleanly before `timeBudgetSecs` (default **240 s**, which fits an API caller waiting on `run-sync`'s 300 s) and before 90% of the platform timeout, keeping what it found. The status message says why it stopped.
- **One bad notice never ends the run.** In `supplierCui` mode a notice whose contracts can't be read is skipped and counted. A list page that can't be read after the retries ends the run with what was already delivered. The run fails only when **nothing** could be read (still never a silent empty success).
- **Faster `supplierCui` scans.** Up to 3 notices are checked at once (~1.5 requests/s to e-licitatie.ro), about 3× the notices per minute of before.
- **Retries cover more.** Every `requests` network error is retried (TLS, cut reads…, not only connection errors and timeouts), with jittered backoff (5 attempts, ≤20 s apart). `Retry-After` on 429 is honoured. A 403 that isn't the anti-hotlink answer is retried once on a fresh session. **e-licitatie.ro's outage page** (a redirect to `/assets/errors/internal-error.htm`, seen 2 Nov 2026) is retried twice and then reported as an outage.
- **Migration and abort are handled.** HTTP runs off the event loop. On `persistState` / `migrating` / `aborting` the run saves its place (page, notices seen, counts) in the key-value store (`RUN_STATE`). A migrated run resumes without repeating a result, and an aborted run stops cleanly.
- **Paging stops at e-licitatie.ro's 3,000-row listing cap** instead of erroring past it.
- **A status message during and at the end of the run** ("N results · M notices scanned · K skipped · stopped early: …"), plus a `RUN_SUMMARY` record in the key-value store.
- Tests: `tests/test_reliability.py` (14) next to `tests/test_supplier_cui.py` (7).

### 0.2

The supplier (winner) CUI filter, the consortium members, digits-only winner CUIs.
