0% 5h quota used · resets in 2h live · 08/09/2026 12:05 CEST

de:wasserversorger-enrich

Enrich German water suppliers (Wasserversorger) with contact details and the published water-analysis URL, and append results to the campaign JSONL.

Runs 63
last 4h ago
Failed 0
no current streak
Median run $26.81
31m · de ceiling $100 per run
Spent in total $1,891
2,518 turns of agent work

Manifest

de/tasks.yaml
Address
de:wasserversorger-enrich — also de:wasserversorger-enrich
Runner
claude-code
Policy
autonomous writes only inside its own workspace, no outward action
Workspace
countries/de/
Schedule
30 2,7,12,17,22 * * * · next 08/09/2026 12:30
Model
sonnet
Budget
400 turns · 90 minutes
Triggerable
yes — free text can reach it
Notifies
every run — 🟢 on success, 🔴 on failure
Description (manifest)
Enrich German water suppliers (Wasserversorger) with contact details and the published water-analysis URL, and append results to the campaign JSONL.

Cost per run

oldest left · ceiling $100
de per-run ceiling $100 01/09/2026 08:02: $28.9401/09/2026 13:09: $21.1001/09/2026 18:00: $26.8001/09/2026 23:19: $49.9402/09/2026 03:01: $18.1302/09/2026 08:02: $14.6202/09/2026 13:09: $41.5102/09/2026 17:12: $22.3102/09/2026 18:08: $46.7902/09/2026 18:55: $34.6602/09/2026 23:15: $40.0903/09/2026 02:53: $23.4003/09/2026 08:00: $26.2003/09/2026 12:59: $29.2403/09/2026 16:25: $22.7303/09/2026 18:05: $39.8803/09/2026 23:06: $42.7104/09/2026 02:52: $54.3904/09/2026 07:57: $29.9504/09/2026 13:29: $97.6204/09/2026 17:57: $24.2304/09/2026 22:53: $19.9305/09/2026 03:05: $31.2605/09/2026 07:56: $21.5805/09/2026 13:01: $26.8105/09/2026 18:29: $30.1705/09/2026 23:10: $36.7206/09/2026 03:03: $47.2606/09/2026 08:06: $31.9306/09/2026 13:19: $52.8506/09/2026 18:10: $31.6006/09/2026 22:53: $17.7707/09/2026 03:07: $52.1207/09/2026 08:02: $46.2007/09/2026 12:54: $14.7907/09/2026 16:05: $28.3307/09/2026 18:10: $34.7007/09/2026 22:56: $19.2008/09/2026 02:50: $14.6608/09/2026 07:51: $37.58 01/0902/0902/0903/0903/0904/0905/0906/0907/0907/09
WhenWhat it reportedTurnsTookCost
08/09/2026 07:51Journal updated. The run is complete: batch of 45 water suppliers researched and merged, coverage stable, evidence trail intact, no forbidden actions taken. **Outcome:**…3221m$37.58ok
08/09/2026 02:50Journal entry appended. Final summary for the report. Enriched 45 more German water suppliers (wasserversorger campaign): 39/45 got a full contact set (email/phone/addre…3220m$14.66ok
07/09/2026 22:56Run complete. Enriched 45 water suppliers (23+22 shards, both validated and merged cleanly): 45/45 got address/phone/website, 41/45 email, 15/45 multi-zone, 237 fetches +…3526m$19.20ok
07/09/2026 18:10Journal updated. Final report: Enriched 45 German water suppliers (batch tag `20260907-1530`, 2 parallel subagents per shard cap) — phone/address/website 45/45, email 43…3340m$34.70ok
07/09/2026 16:05Both files clean. Run complete. **Outcome:** Enriched 45 more Wasserversorger (23+22 across two parallel research passes), bringing `enrichment.jsonl` to 2520 records (2…4131m$28.33ok
07/09/2026 12:54Journal entry appended. The run is complete. **Outcome:** Enriched 45 more German water suppliers (Wasserversorger) via 2 parallel subagents (23 + 22, population-priorit…2724m$14.79ok
07/09/2026 08:02This wasserversorger-enrich run completed cleanly: enriched 45 more water suppliers (2379→2424 of 3375 total, 976 pending), population coverage holds at 99.99%, and merge…192m$46.20ok
07/09/2026 03:07These are historical batch files from past runs (consistent with the campaign's convention of keeping them); today's own batch files were already removed. Everything is m…5137m$52.12ok
06/09/2026 22:53Run complete. Enriched 45 more water suppliers (2289 → 2334 distinct, `enrichment.jsonl` 2294 → 2339 lines), all verified as genuine active suppliers with clean shard mer…3723m$17.77ok
06/09/2026 18:10Journal updated. The run completed cleanly end-to-end. Processed a 45-company batch (23+22 shards) for the wasserversorger-enrich campaign: all verified as genuine drink…2940m$31.60ok
06/09/2026 13:19Journal entry appended. The run is complete — batch of 45 water suppliers enriched, merged, and verified. Enriched 45 more German water suppliers (23+22 shards, both for…2749m$52.85ok
06/09/2026 08:06Everything checks out. Final report: Enriched 45 more German water suppliers (23+22 split across two synchronous subagents), verified 45/45 distinct, zero collisions, an…3636m$31.93ok
06/09/2026 03:03Everything is clean — no leftover temp directories, all files updated and consistent. The run is complete. Batch of 45 German water suppliers enriched (contact details +…6133m$47.26ok
05/09/2026 23:10Everything checks out and the journal is up to date. Enriched 45 more German water suppliers (2064 → 2109 of 3375 on the roster; 1291 remain pending, none blocked on a m…4740m$36.72ok
05/09/2026 18:29Consistent with convention — batch input files are kept, not deleted. Nothing further to clean up. This run is complete. ## Summary Ran the wasserversorger-enrich sched…3259m$30.17ok
05/09/2026 13:01Everything checks out consistently. Not a git repo, so no commit needed — the data files are the persistence layer. ## Report Enriched 45 German water suppliers (23 + 2…3931m$26.81ok
05/09/2026 07:56Journal entry appended. Run complete. **Outcome:** Enriched 45 German water suppliers (2 parallel research passes, 23 + 22 companies) — all verified as genuine drinking-…3626m$21.58ok
05/09/2026 03:05Good, both entries are distinct and correctly placed. The run is complete. **Summary:** Ran the wasserversorger-enrich batch of 45 water suppliers (0 pending_domain in q…3735m$31.26ok
04/09/2026 22:53Everything is consistent and merged. Run complete. **Outcome:** Enriched 45 German water suppliers (23+22 via two parallel foreground subagents) covering contact fields,…4223m$19.93ok
04/09/2026 17:57Journal entry appended and the pipeline finished cleanly. Final report below. The scheduled 45-company batch (shard A: 23, shard B: 22) completed successfully — all comp…4227m$24.23ok
04/09/2026 13:29Everything checks out. The run is complete. **Summary:** Enriched a 45-company batch of German water suppliers (population-priority order, 0 pending_domain in queue). Tw…5159m$97.62ok
04/09/2026 07:57Journal entry appended. Run complete. **Outcome:** Enriched 45 German water suppliers (shard A: 23, shard B: 22), both researched directly by foreground subagents with n…3527m$29.95ok
04/09/2026 02:52Journal entry written. The run is complete: all 45 companies enriched, verified, merged, and the campaign pipeline re-run clean. Enriched 45 German water suppliers this…4422m$54.39ok
03/09/2026 23:06All state files are consistent and the campaign artifacts are up to date. Enriched all 45 batched Wasserversorger (population-priority order, 0 pending_domain), running…5536m$42.71ok
03/09/2026 18:05Journal updated. Run complete. Enriched a 45-company batch of German water suppliers (`enrichment.jsonl` 1574→1619, 1614 distinct), all researched via 2 synchronous suba…3035m$39.88ok
03/09/2026 16:25All done. Enrichment is complete and merged, the finish pipeline ran clean, and the journal is updated. **Report:** Enriched 45 German water suppliers (23+22 shards) — f…3827m$22.73ok
03/09/2026 12:59Journal entry appended. The run is complete. Enriched 45 water suppliers this pass (23+22 shards), all research-only with no contact made and no captchas touched: 38/45…2729m$29.24ok
03/09/2026 08:00Enriched 45 German water suppliers (wv-de-3792…wv-de-3816 range plus VG/Verband entries) — enrichment.jsonl grew 1439→1484, 272 new municipality links, 43/45 got an analy…3230m$26.20ok
03/09/2026 02:53Run complete. Final report: Enriched 45 more German water suppliers (23+22 shards, both verified clean against the roster with zero overlaps/malformed records), adding 4…2823m$23.40ok
02/09/2026 23:15Run complete: enriched 45 more German water suppliers (23+22 shards, 0 contact made, all Impressum-verified), merged cleanly into `enrichment.jsonl` (1349→1394 lines, 138…2545m$40.09ok
02/09/2026 18:55Journal updated. Final report: Enriched 45 more German water suppliers (wasserversorger campaign): all research-only, no contact attempted, no captchas touched. Combined…3040m$34.66ok
02/09/2026 18:08All shard evidence files are preserved as the campaign's convention requires, and the merge is clean. **Run complete.** Enriched 45 German water suppliers (research-only…3138m$46.79ok
02/09/2026 17:12Journal entry appended. This run enriched 45 more German water suppliers (23+22 shards, synchronous subagents, no contact made, no captchas touched): `enrichment.jsonl` n…4429m$22.31ok
02/09/2026 13:09Enriched 45 more German water suppliers (19 previously domain-blocked + 26 largest by population served): enrichment.jsonl grew from 1169→1214 records (1209 distinct comp…5539m$41.51ok
02/09/2026 08:02All files validated. Final report: Processed a 45-company batch of German water suppliers (Wasserversorger). Recognizing the batch came from the same WWA Traunstein/Ansb…6332m$14.62ok
02/09/2026 03:01Run complete. Enriched 45 German water-supplier companies (all `pending_domain` rows), appending to `enrichment.jsonl` (1060→1105 distinct companies) and 119 new municipa…5131m$18.13ok
01/09/2026 23:19Journal entry appended. Final report below. Enriched 45 more German water suppliers (23+22 split across two synchronous subagents), including all 9 remaining `pending_do…3149m$49.94ok
01/09/2026 18:00Enrichment run complete: 45 water-supplier companies researched (23+22 split across two synchronous subagents), all appended cleanly with no overlaps. `enrichment.jsonl`…2530m$26.80ok
01/09/2026 13:09Journal updated. Final report below. Ran the wasserversorger-enrich skill: researched a 45-company batch (5 pending_domain + 40 by population served, standing duplicate…3639m$21.10ok
01/09/2026 08:02Enriched 45 German water suppliers (23+22 split across two synchronous subagents), covering the 3 remaining `pending_domain` rows plus 42 by population served (34.4k–31.5…3232m$28.94ok
01/09/2026 03:16Enrichment run complete: processed a 45-company batch (3 pending_domain + 42 by population served, 39.8k–34.6k residents), all appended and validated (0 malformed, 0 miss…2946m$42.05ok
31/08/2026 22:48Run complete. Enriched 45 more water suppliers (13 `pending_domain` priority rows + 32 by population served), bringing distinct enriched companies to 835/3035. All 13 pen…3818m$11.17ok
31/08/2026 17:57Journal updated. Run complete. **Outcome:** Enriched 45 companies (2 shards, 23+22, dispatched as synchronous subagents per the skill's memory-cap rule) — 18 of 19 `pend…3627m$20.67ok
31/08/2026 13:27Everything checks out cleanly. Enriched a 45-company batch of German water suppliers (3 pending_domain rows prioritized, rest by population served, 6 known duplicates pr…6157m$31.56ok
31/08/2026 08:03Run complete. Enriched 45 more German water suppliers (23+22 via two parallel research agents), appending contact/analysis records to `enrichment.jsonl` (655→700 distinct…4033m$40.14ok
31/08/2026 03:26Everything checks out. All work completed synchronously within this run. **Outcome:** Enriched a 45-company batch (23+22 shards, split by pending-domain priority then po…5156m$50.72ok
30/08/2026 22:58Temp script cleaned up correctly. **Run complete.** Enriched 45 water suppliers (2 parallel research shards, 23+22, both 100% complete) — 610 companies now have full con…3528m$21.59ok
30/08/2026 17:58Journal updated. Final report: Ran the `wasserversorger-enrich` skill's 45-company batch (shards ee/ff, 23+22 companies split across two synchronous subagents), excludin…4428m$19.06ok
30/08/2026 13:07Everything checks out. Journal entry appended. **Summary:** Ran the wasserversorger-enrich skill for a 45-company batch (excluding the standing duplicate-pair backlog),…4337m$17.01ok
30/08/2026 08:10Enrichment run complete: 45 new companies enriched (430 → 475 of 2537, ~18.7%), population coverage nudged to 94.31% (+0.01pp), with the `pending_domain` priority row res…5338m$22.58ok
30/08/2026 02:59Run complete: enriched 45 German water suppliers (contact fields + published water-analysis locator + municipality links), catching and excluding one known duplicate (`wv…4129m$28.36ok
29/08/2026 23:10Run complete. Enriched 30 water suppliers (15 village-scale pending-domain candidates + 15 large city utilities like Stadtwerke Münster, badenova, ESWE Wiesbaden, ENTEGA)…4540m$24.42ok
29/08/2026 18:05Journal entry appended. Run complete. **Summary:** Enriched 30 more German water suppliers (13 `pending_domain` village-scale rows + 17 by population, including 15 large…3435m$34.31ok
29/08/2026 13:02Journal entry written. Final report: Enriched 30 more Wasserversorger companies (2 subagent shards of 15, both fully accounted for and validated before merge). The stand…4532m$22.89ok
29/08/2026 07:51Run complete: enriched 30 more pending_domain water suppliers (6 got a verified official domain, 13 published water-analysis URLs found), pushing population coverage from…3621m$15.19ok
29/08/2026 02:45Run complete. Enriched a 30-company `pending_domain` batch (2 synchronous subagents, 15/15 split): 5 official domains resolved, 25 stayed `pending_domain` with earned rea…3815m$8.90ok
28/08/2026 22:57Those are kept as the evidence trail, consistent with prior runs' convention. Everything is merged and the journal is updated. Ran the wasserversorger-enrich batch: 30 p…4427m$20.97ok
28/08/2026 17:56Journal updated. Run complete. **Outcome:** Enriched 30 pending_domain water suppliers (16+14 across two synchronous subagents), resolving 11 official domains and correc…4626m$19.09ok
28/08/2026 12:58Everything checks out cleanly. Final report: Enriched 30 `pending_domain` water suppliers (2×15 shards) in this run, resolving 12 new official domains and adding contact…4428m$22.12ok
28/08/2026 08:02Run complete. Enriched 30 water suppliers (all `pending_domain`-tier, population-priority order), resolved 7 official domains with zero collisions, and added 37 new munic…6232m$31.62ok
Read the skill

Wasserversorger enrichment

You are in the country workspace. Campaign files: campaigns/wasserversorger/. Plan and measured pilot results: _tasks/netzbetreiber-contact/german-water/PLAN.md.

This is the water counterpart of netzbetreiber-enrich. Same guards, same append-only discipline, two differences that matter: the data is mostly published (so outreach is a fallback, not the goal), and company identity is ours, not the register's.

Scope — this skill is Step 2, not Step 0

It enriches suppliers that are already identified. Finding out who exists is the separate discovery sweep: coverage.mjs ranks the unclaimed municipalities by population into residual-top.csv, a sweep resolves them into roster.part-*.jsonl + roster-links.part-*.jsonl, and only then do those companies become enrichment input. Do not start here on an empty roster.

Batch selection

  1. Read the roster (roster.part-*.jsonl, and seed.csv once it exists) plus campaigns/wasserversorger/enrichment.jsonl. A row is pending when its company_id has no JSONL record.
  2. Take the next 45 pending rows, unless requester instructions say otherwise (a different count, or specific company_ids to re-research). Water enrichment measured a median of 5 fetches per company against 1-2 for netzbetreiber, so 30 is the working batch — raise it only on measured evidence from the ledger, never on a guess.
  3. Order the batch by priority, not by file order: a. pending_domain rows first. A company with no official_domain cannot be deduped and cannot be imported at all — domain is the dedup key. 107 of the roster are in this state as of 2026-08-27 and discovery keeps producing more, because it finds delegated suppliers by name off a Verbandsgemeinde page without resolving their own site. Clearing these unblocks the load and surfaces domain collisions while they are still few. b. Then by population served (sum the bevoelkerung of the municipalities linked to that company_id in roster-links.jsonl — grep it, it is 3 MB+, never read it whole). The consumer is energiemarie's city→supplier finder, queried in proportion to population. c. Then the rest.
  4. Append-only. A re-research appends a new record; exports take the latest per company_id.

Identity — read before recording anything

company_id (wv-de-NNN) is our internal primary key. official_domain is the dedup/merge key (decided by Stefanie, 2026-08-12). Do not key records on handelsregister_nr, water_delegate_id, or any cities-api id. Mapping our companies onto cities-api rows is a separate late-binding pass run at import time.

Why not Handelsregister (measured 2026-08-12, pilot 3): only 16% of suppliers have a number at all — Zweckverbände are Körperschaften des öffentlichen Rechts and are not in the register.

That 16% is a Zweckverband rate, not a national one. The population-weighted roster sweep (2026-08-12, 19 big-city suppliers) found 68% with a number printed in the Impressum: the head of the country is GmbHs and AGs, which are registered. Handelsregister coverage therefore correlates with size, which is exactly what makes it useless as a key — it is present where dedup is easy and absent where dedup is hard. Domain does not have that skew.

Legal forms that justify a null handelsregister_nr are KdöR and AöR — Berliner Wasserbetriebe is an Anstalt des öffentlichen Rechts, not a Körperschaft. Record whichever the Impressum states. Worse, where a number exists it may belong to an operating subsidiary rather than the public-law body holding the supply relationship (Aggerverband has no entry; AV Aggerwasser GmbH is HRB 39328). Matching on it attaches the wrong company, undetectably.

Domain has full coverage instead (website_url was 100% in pilot 3) and the field already exists in Stefanie's analysis contract as official_domain.

Normalise before comparing: lowercase, strip scheme and www., reduce to the registrable domain (eTLD+1). https://WWW.Beispiel.de/wasser/beispiel.de.

Two failure modes to guard, not ignore:

  1. One domain, several suppliers. Small Eigenbetriebe live on the municipality's domain, and German Samtgemeinden / Verwaltungsgemeinschaften often share one website across several municipalities — so two genuinely distinct suppliers can present the same domain. Parent / operating-subsidiary pairs do the same. Never auto-merge on a domain collision. Flag any official_domain claimed by more than one company_id into a review queue and record both rows; a wrong merge here is the same undetectable error we rejected Handelsregister for.
  2. One supplier, several domains. Rebrands, .de/.com pairs, and old domains that redirect. Record the domain you verified via the Impressum and put observed alternates in notes, so a later rebrand appends an alias rather than creating a duplicate company.

Priority input: pending_domain rows

Discovery records a delegated supplier it could name but could not locate — official_domain: null with domain_status: "pending_domain" — because the VG page that names the supplier usually does not link its website. Those rows block the load: domain is the dedup key, so a row without one cannot be deduped or imported.

Take them before the normal batch. The company name is already verbatim in the row's notes along with the page that named it; the job is to find the official site, verify the Impressum names that entity, and set official_domain + domain_status: "resolved". If it genuinely has no website — small Zweckverbände sometimes publish only through their member municipalities — say so in reason and leave the status as is rather than attaching a member's domain, which would key the supplier to the wrong organisation.

Research each supplier

Spawn at most 2 parallel subagents (each is a separate multi-GB claude process; the container has a 6 GB memory cap — 4+ concurrent agents OOM the run). Work supplier by supplier.

Part 1 — contact and detail fields

Target fields: name, short_name, address, phone, address_opening_hours, phone_opening_hours, email, contact_form_url, website_url, emergency_phone_number, supply_area_municipalities.

  1. Official website. Verify the Impressum names this supplier. For a Zweckverband the Impressum usually also states the legal form (Körperschaft des öffentlichen Rechts) — record that verbatim when present, it is what justifies a null handelsregister_nr.
  2. Contact path. Kontakt / Kontaktformular. Record contact_form_url only if an actual form is present, not merely an address block.
  3. Emergency number. Look for Notdienst, Störung, Entstörung, Havarie, Bereitschaft — often a separate page, not the Impressum.
  4. Supply area. The member/Versorgungsgebiet list. This feeds the pivot; see Part 3.

supply_area_plz is not collected. Nobody publishes it (0/25 in pilot 3) and it is derivable: take the member municipalities and read plz from _tasks/netzbetreiber-contact/german-water/gemeinden_deutschland.csv. Compute it downstream; never scrape it, never email anyone for it.

Part 2 — the analysis locator

Port Stefanie's validated prompt contract verbatim — do not redesign it. Field list and her worked example (Stadtwerke Kempen) are in _tasks/netzbetreiber-contact/german-water/basecamp-todo-10101567321-find-prompt-analysis-url.md: official_domain, analysis_url, index_url, url_type, tier, year, multi_zone, zone_urls, zone_url_pattern, access_method, evidence_value, url_looks_dated, third_party_fallback, confidence, notes.

Published analyses are obligatory under TrinkwV §45/§46, so expect to find one — pilot 3 got 21/25, which is 21 of the 21 entities that actually supply drinking water; the 4 misses were all roster defects (flood-protection Verbände). multi_zone ran at 15/21 = 71% of real suppliers (60% of all 25 rows), so treat multiple supply zones as the normal case.

Useful side effect: "no analysis page anywhere + no Trinkwasser content" is a cheap automated filter for a non-supplier that has crept onto the roster.

Part 3 — municipality links

One record per published municipality↔company link in links.jsonl, carrying the verbatim published name. Normalisation and AGS resolution happen only in map_ags.mjs — never in the crawl. The crawl records what the supplier published; every normalisation decision stays in one reviewable file so the match rate is reproducible from raw evidence.

Classify each member with member_kind: gemeinde, ortsteil, landkreis, utility, unknown. Landkreise and utilities are legitimate members but have no AGS — they must not count against the match rate.

Set quartier when the supplier names a sub-area (Ortsteil / Versorgungszone) rather than the whole municipality, and type to aep for drinking water.

Hard rules

Country rules in ../../CLAUDE.md apply in full (never invent contact data; research only, never contact anyone; record a source per field group). Plus three the pilots proved:

  1. Every field carries its own source_url and confidence. Not one per record.
  2. A negative must be earned. Before recording not_published, follow the site's own labelled links. Pilot 3's single false negative was an emergency_phone_number marked not_published where the homepage linked "Notdienst" three times and the page was never opened. Use status: found / not_published (verified absent) / not_found (searched, inconclusive) / not_applicable (cannot exist for this legal form).
  3. Label inference as inference. A handelsregister_nr of not_applicable justified by an Impressum statement is a scraped fact; the same conclusion drawn from the legal form in the supplier's name is an inference. Say which in reason. 14 of 21 in pilot 3 were the latter — legally sound, but not evidence.

Memory safety

When searching saved HTML use fixed-string searches (grep -F -c 'g-recaptcha') or read the file directly — never wide-context regexes like .{0,150}pattern.{0,150} or unbounded .*pattern.* with -o. On minified single-line HTML these allocate gigabytes and have OOM-killed the host (2026-07-22).

Fetch politely: no parallel hammering of a single host. Known host traps, all measured:

  • bodensee-wasserversorgung.de serves an incomplete TLS chain — curl and WebFetch both fail cert verification; it needs a CA workaround or it reads as unreachable rather than absent.
  • 78.46.40.200 has hard-banned this IP after ~7 requests, and the ban is durable (still refusing an hour later, not a 429). Two campaign companies share that one host — reckenberg-gruppe.de and fernwasser-franken.de — so one ban took out two suppliers.
  • l.de (Leipziger Gruppe) returns 403 to both curl and WebFetch. Bot protection, not absence. Record it as unresolved; do not conclude the data is unpublished.
  • Impressum paths are not guessable. /impressum 404s on Stadtwerke Düsseldorf (/rechtliches/impressum/), Stadtwerke Karlsruhe (/de/impressum/) and N-ERGIE (a long portal URL). Fetch the homepage and read the footer link rather than guessing paths.
  • Normalise Unicode before matching. One site's heading Öffungszeiten (their typo) did not match a grep -F for the same string because the Ö was not the precomposed codepoint.
  • PDFs may be pure scans with no text layerpdftotext returned 6 bytes for a supplier whose 26 zone reports are all Konica-Minolta scans. Render and read, or OCR; a pdftotext-only pipeline scores that entire supplier as "publishes nothing".

Record

Append one JSON object per supplier (one line) to enrichment.jsonl. Each field is an object:

{"company_id": "wv-de-001", "name": "<from seed>",
 "fields": {
   "email": {"value": "info@…", "status": "found",
             "source_url": "https://…/impressum/", "confidence": "high", "reason": null},
   "handelsregister_nr": {"value": null, "status": "not_applicable", "source_url": "https://…",
             "confidence": "high", "reason": "Impressum states Körperschaft des öffentlichen Rechts"}
 },
 "analysis": { …Stefanie's 15 keys… },
 "fetches": 5, "searches": 1, "checked_at": "<today YYYY-MM-DD>", "notes": "… or null"}

Resumability

A fresh session must be able to continue mid-campaign without re-fetching completed work:

  1. Pending detection is company_id absent from enrichment.jsonl — nothing else.
  2. Append after each supplier, never batch at the end. An interrupted run keeps its completed work.
  3. Write raw crawl shards per subagent (enrichment.part-*.jsonl) and keep them as the evidence trail; map_ags.mjs merges.
  4. Do all work synchronously inside the run. Never spawn background agents and end your turn "waiting" — the run terminates the moment you produce your final message, killing anything still running (learned on netzbetreiber, 2026-08-04).

Finish

  1. Run node campaigns/wasserversorger/map_roster.mjs (merges the crawl shards into roster-links.jsonl and resolves each raw_name to an AGS), then node …/coverage.mjs (population coverage + the residual queue). Do not run map_ags.mjs / export_csv.mjs — those are pilot-3-era scripts that read the dead companies.part-a/b.jsonlcompanies.jsonl/links.jsonl/companies.csv pair (25 rows, frozen since 2026-08-12), not the live enrichment.jsonl/roster-links.jsonl data. Running them is harmless (they just regenerate the same stale 25-row files) but wastes a step — multiple runs since 2026-09-03 have done this out of habit before catching it. There is currently no CSV export from enrichment.jsonl for the team's Sheet; that script does not exist yet. Persisted campaign scripts are .mjs so any session can run them. python3 is available at /usr/bin/python3 and is fine for throwaway analysis, but do not add Python to the campaign's committed toolchain. Rerun build_ags_map.mjs whenever normName changesags-city-map.csv caches name_norm, and a stale cache silently depresses the exact-match rate.
  2. Journal entry (see country CLAUDE.md).
  3. Final message: batch size, per-field recovery counts, how many are complete vs needing outreach, municipalities newly claimed and population coverage (the metric that matters — not row count), plus anything odd worth a human's attention.