# Changelog of German & EU Tenders: Public Procurement Monitor (`lindenwerk/german-eu-tender-matcher`) Actor

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

## Changelog

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

- Product name in the README now matches the Store title ("German & EU Tenders: Public Procurement Monitor").
- New Examples section linking the published example task "IT security tenders in Germany - 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.

### 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 both sources (2026-10-06, before the first public release)

- Fixed: on Apify at 512 MB a 30-day `backfill` could drop the whole German source. Parsing the German eForms export
  is CPU-bound, 512 MB runs get 1/8 of a CPU core, and the source had a fixed 210 s budget; when it ran out, every
  finished day was thrown away and the run returned TED-only rows while still showing *Succeeded*.
- A source that runs out of time or breaks now keeps what it delivered (finished export 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 per source: 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 for dedup, scoring and pushing.
- About 3.6x less CPU for the same output (30-day window locally: 46.8 s -> 12.9 s): compiled XPath look-ups, notice
  types that were not requested are skipped before the XML is parsed, one parse thread instead of three competing for
  the GIL, faster accent folding and a cheap upper-bound check before fuzzy title matching. Output of a 30-day test
  window is identical row for row. Dedup normalises each title once and reuses one matcher per TED notice (same
  rows and scores). On Apify (512 MB) the 30-day IT backfill now takes ~2.5 min with both sources complete (German
  source 30/30 days fetched and parsed in ~108 s of an 810 s budget; was: German source dropped after 210 s).
- 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: readable titles for the "Sort by" options, a "Quickest try" hint in the form description and a short description for the Advanced section.
- No change to the code, prices, events, output fields or defaults.

### 0.1 (2026-10-04, updated 2026-10-05 before the first publication)

Pre-publication update (2026-10-05): 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` 500).
- 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 one source no longer crashes the run.
  The other source's results are delivered and a warning goes into the status message and `RUN_SUMMARY.warnings`. If
  every source is 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`, `failedSources` and `warnings`.
- `sources: ["oeffentlichevergabe"]` without DE in `countries` is now rejected as an invalid input instead of running
  with no source.

Initial release (2026-10-04):

- First public release.
- Sources: oeffentlichevergabe.de open-data eForms export (CC0) and TED search API v3.
- Cross-source deduplication (eForms notice id, TED reference, fuzzy fallback), plus collapse of identical
  re-publications under a new notice id (older ids in `relatedNoticeIds`).
- Rule-based profile scoring (CPV prefixes, DE/EN keywords with IDF, regions, contract types, value range,
  exclusions, open-only) with `relevanceScore` and `matchReasons`.
- Modes: `monitor` (only new notices, persistent state), `backfill` (up to 31 days), `awards` (competitor tracking).
- Pay-per-event events `notice` and `scored-match`; respects the run's maximum total charge.
- GDPR: personal contact names, personal e-mails and phone numbers are never output, including inside titles and
  descriptions (replaced with placeholders).
- Truncated CPV codes from some publishers are padded to 8 digits.
- Memory fixed at 512 MB (max 1024 MB).
