# Changelog of France Tenders Monitor: BOAMP, TED & Marchés Publics (`lindenwerk/france-tender-matcher`) Actor

- **URL**: https://apify.com/lindenwerk/france-tender-matcher/changelog.md
- **Full Actor documentation**: https://apify.com/lindenwerk/france-tender-matcher.md

## Changelog

### README: cross-links and examples, no code or price change (2026-10-08, build 0.1.7)

- Product name in the README now matches the Store title ("France Tenders Monitor: BOAMP, TED & Marchés Publics").
- New Examples section linking the published example task "French IT tenders (BOAMP + TED) - last 7 days".
- "Other Lindenwerk Data Actors" lists all six sibling Actors by their current Store titles and Store URLs.
- No change to code, input schema, pricing, events, defaults or output.

All notable changes to the France Public Tenders Monitor (BOAMP + TED) by Lindenwerk Data.

### Data-quality update: awardedValue fix and deadline-window filter (2026-10-07)

- Fixed: `awardedValue` / `awardedCurrency` were filled on open tenders. TED's search API puts the estimated value in
  `total-value` on competition notices, and that showed up as an "awarded" value. Both fields are now null on every
  notice that is not an award (result) or contract-modification notice; `estimatedValue` is unchanged.
- New optional inputs `minDaysUntilDeadline` and `maxDaysUntilDeadline` (days from the run date to the deadline in
  `daysUntilDeadline`), e.g. `"maxDaysUntilDeadline": 21` for tenders due within three weeks. Not set = no filter, so
  existing inputs behave exactly as before. When either is set, notices without a published deadline are excluded.
  The filter runs before scoring and before anything is pushed, so filtered notices are never charged. Counts appear in
  `RUN_SUMMARY.excludedByFilter` (`deadlineTooSoon`, `deadlineTooFar`, `noDeadline`) and `RUN_SUMMARY.deadlineFilter`.
- Input help: CPV examples `4873` / `72212730` for security software, `7971` marked as guarding services (not IT);
  `keywords` / `minScore` explain that keywords raise the score, so a high `minScore` with few keywords can return nothing.
- README: warning that an API or AI-agent call without `mode` runs monitor mode (only new notices since the last run),
  with a one-off `backfill` example; the two new filters are documented.
- No change to pricing, events, defaults or output columns.

### Reliability update: long backfills keep finished days (2026-10-06, before the first public release)

- Fixed: the source time budget was a fixed 210 s and a timeout threw away every day that had already finished
  (BOAMP fell back to TED only). Long backfills at 512 MB (1/8 of a CPU core) could hit this.
- A source that runs out of time or breaks now keeps what it delivered (finished BOAMP days, TED pages fetched so far). The run
  says so: status message starting with **PARTIAL DATA**, a `PARTIAL SOURCE` log line with the missing days, and new
  `RUN_SUMMARY` fields `sourceStatus`, `partialSources`, `missingDays`, `sourceTimeBudgetSeconds`. Only delivered rows
  are charged.
- Time budget: 210 s for short monitor windows (unchanged), 60 s + 25 s per day for backfills (31 days: 835 s), always
  leaving at least 300 s before the run timeout.
- About 1.7x less CPU for the same output: compiled XPath look-ups, faster accent folding and a cheap upper-bound
  check before fuzzy title matching (identical rows on a 7-day test window). Dedup normalises each title once and
  reuses one matcher per TED notice. On Apify (512 MB) the 30-day IT backfill takes ~2.7 min with BOAMP and TED
  complete (was ~13 min before the fix, ~4.8 min with it); rows and scores identical.
- No change to prices, events, input fields, defaults or output fields.

### Listing quality update (2026-10-06, before the first public release)

- README: starts with an H2 "What is ...?" (Apify Store shows the Actor name as the page H1), new step-by-step
  "How to ..." section (first run, own profile, schedule, integrations, API) and a list of the other Lindenwerk Data Actors.
- Input form: fields grouped into sections (Competitor tracking, Date range and sources, Output, Advanced) and a "Quickest try" hint in the form description.
- No change to the code, prices, events, output fields or defaults.

### 0.1 (2026-10-05)

Pre-publication update (2026-10-05, later the same day): Store-test-proof defaults.

- The input form prefill is now a stateless 7-day `backfill` with `maxResults` 25 (IT profile unchanged). Apify's daily
  Store test runs the prefill, so it gets rows on any day. A monitor-mode prefill could return 0 rows on a quiet day,
  and 3 failed days in a row mark an Actor "under maintenance". API, task and schedule defaults are unchanged
  (`monitor`, `lookbackDays` 1, `maxResults` 200).
- New `firstRunBackfillDays` (default 7, 0 = off): a monitor run with no saved state whose window is empty looks back
  7 days once.
- More robust against outages: an unexpected error or a hang (210 s budget) in BOAMP or TED no longer crashes the run.
  The other source's results are delivered with a warning. If both are unreachable, the run fails cleanly with no
  charge and no state change. Empty runs end with a clear status message. `RUN_SUMMARY` gains `firstRun`,
  `firstRunFallbackUsed` and `failedSources`.

Initial release:

First public release.

- Sources: BOAMP open data (DILA, Licence Ouverte, Opendatasoft export API: one request per publication day) and the
  TED Search API v3 (buyer country FRA).
- Parses all three BOAMP formats: European eForms notices (`EFORMS`, rebuilt from BOAMP's JSON into eForms XML and read
  with the same parser as TED), national notices (`FNSimple`) and adapted procedures (`MAPA`). Older formats fall back to
  BOAMP's index fields.
- Cross-source deduplication of BOAMP and TED by eForms notice UUID, then by TED publication number, then a
  conservative fuzzy match.
- French-aware matching: accent-insensitive keywords, BOAMP descriptors (`descripteur_libelle`) count like title
  words, départements (from BOAMP's `code_departement` or the buyer's postcode, incl. 2A/2B and DROM), French regions
  (names, common abbreviations such as IDF/PACA/AURA, or NUTS prefixes) and market level (`eu`, `national`, `mapa`).
- Modes: `monitor` (only new since last run, per `stateKey`), `backfill` (up to 31 days) and `awards` (winners,
  awarded values, number of offers; for national notices extracted conservatively from text, legal entities only).
- GDPR: named contacts (M./Mme …), personal e-mail addresses and phone numbers are removed from contacts and free text.
  Sole-trader winners on MAPA notices (`PersonnePhysique`) are never output.
- Pay-per-event charging (`scored-match`, `notice`) with `maxResults` and maximum-total-charge stop.
- 103 offline pytest tests (sanitised real BOAMP fixtures with synthetic names), including pre-publish checks of the Actor definition and pricing.
