# Bron- en adapterarchitectuur ## 1. Adaptercontract Iedere adapter implementeert conceptueel: ```python class SourceAdapter(Protocol): parser_key: str parser_version: str def extract(self, content: str, *, url: str) -> ExtractionResult: ... ``` `ExtractionResult` bevat: - `jobs: list[ExtractedJob]`; - stabiele parser key/version; - confidence 0..1; - machine-/mensleesbare warnings. Adapters: - doen geen netwerkrequests; - schrijven niet naar ORM; - voeren geen scoring of bronpolicy uit; - werken deterministisch op aangeleverde fixtures; - bewaren alleen beperkte raw payload voor debugging; - leveren evidence per belangrijk veld. ## 2. Parservolgorde Voor HTML: 1. JSON-LD `JobPosting`; 2. gespecialiseerde adapter op expliciete source parser key; 3. generieke HTML-fallback; 4. geen vacature wanneer minimumvelden ontbreken. Voor XML/feed: 1. RSS/Atom; 2. sitemapdiscovery (backlog); 3. geen generieke HTMLparser. Voor e-mail: 1. MIME decode zonder attachments; 2. linkextractie met unsubscribe/privacyfilter; 3. bij een platformmailbox uitsluitend gelabelde HTTPS-links op het gekozen jobboarddomein aanvaarden; 4. externe tracking-, account-, help-, privacy- en uitschrijflinks verwerpen; 5. platformalias en provenance bewaren; 6. oorspronkelijke werkgever proberen te resolveren in een aparte begrensde taak. ## 3. Minimumvelden Een jobdraft is verwerkbaar wanneer minstens aanwezig: - titel; - HTTP(S)-URL of stabiele externe identiteit. Employer, locatie en beschrijving mogen tijdelijk ontbreken, maar verlagen confidence. Een generieke pagina zonder herkenbare titel wordt niet als job opgeslagen. ## 4. Confidence Richtwaarden: | Methode | Baseline | |---|---:| | JSON-LD geldig individueel JobPosting | 0,95 | | gespecialiseerde publieke ATS-adapter | 0,90 | | RSS/Atom | 0,82 | | vacaturemailanchor | 0,65–0,75 | | generieke HTMLselectors | 0,45–0,75 | Fieldconfidence en extractionconfidence zijn gescheiden. Een perfecte titel maakt een ontbrekende werkgever niet betrouwbaar. ## 5. Nieuwe adapter toevoegen 1. Maak `apps/sources/adapters/.py`. 2. Geef vaste parser key en semverachtige version. 3. Parse alleen de aangeleverde content. 4. Maak geanonimiseerde fixture onder `fixtures/`. 5. Test happy path, ontbrekende velden, gewijzigd markup en malafide HTML. 6. Registreer uitsluitend voor een expliciete source/parserdetectie. 7. Documenteer bronbeleid, rate limit en canonical URL-gedrag. 8. Voeg traceability toe. ## 6. ATS-strategie Gespecialiseerde adapters mogen publieke HTML of publieke, documenteerbare endpoints gebruiken wanneer: - geen login/token nodig is; - bronpolicy `allow` is; - gebruiksvoorwaarden/robots zijn beoordeeld; - het endpoint rechtstreeks vacatures van de werkgever publiceert; - rate limiting en identifiers stabiel zijn; - fallback naar HTML mogelijk is. Een endpoint mag technisch JSON zijn zonder een commerciële platform-API-integratie te vormen. De policybeslissing blijft per bron vereist. ## 7. Bronpromotie ```text candidate -> policy/robots/terms review -> trial (kleine frequentie, parserconfidence meten) -> active (voldoende succesvolle runs) -> quarantined (policy, security of herhaalde parsefout) -> trial/active na expliciet herstel ``` Automatische promotie naar `active` vereist minimaal: - meerdere succesvolle runs; - geen private/deny redirect; - voldoende extractieconfidence; - geen onverwachte volumepiek; - policy nog geldig. Juridische voorwaarden mogen niet door een taalmodel als definitief toegestaan worden verklaard. ## 8. Rate limiting Per eTLD+1/domein: - concurrency standaard 1; - minimum interval standaard 30 seconden; - respecteer `Retry-After`; - conditionele GET met ETag/Last-Modified; - exponentiële backoff bij fout; - geen retry op policyblokkade; - adaptieve lagere frequentie bij stabiele, zelden wijzigende bron. ## 9. Parserdrift Bronhealth detecteert: - leeg resultaat waar eerder vacatures waren; - sterke daling in velddichtheid/confidence; - nieuwe foutstatus/contenttype; - wijziging in jobvolume; - ontbrekende title/URL; - veel nieuwe canonical keys door URLtrackingwijziging. Automatisch herstel mag veilige selectorvarianten proberen, maar nieuwe logica moet eerst op opgeslagen fixture/snapshot draaien. Geen live agressieve exploratie. ## Publieke ATS-providers (VR-106) | Provider | Hostherkenning | Parserbenadering | Opmerking | Rate limit (advies) | |---|---|---|---|---| | Greenhouse | `*.greenhouse.io`, `*.boards.greenhouse.io` | gespecialiseerd endpoint/JSON + detailstructuren | gebruik alleen publieke boardpagina's/feeds, geen private/persoonlijke endpoints | 15 requests/min met conditional request indien beschikbaar | | Lever | `jobs.lever.co` | gespecialiseerd JSON/HTML listing+detail | alleen publieke postings, geen recruiter login flow | 60 requests/min per domein | | Recruitee | `*.recruitee.com` | gespecialiseerd listing+detail parser | parse alleen door werkgever geïntendeerde publieke vacature-URL’s | 60 requests/min per domein | | SmartRecruiters | `*.smartrecruiters.com` | gespecialiseerd listing+detail parser | geen interne API-auth nodig; stop bij CAPTCHA/anti-bot | 30 requests/min per domein | | Workable | `apply.workable.com` | gespecialiseerd listing+detail parser | alleen publieke vacaturepagina/JSON; geen private endpoints | 30 requests/min per domein | Gedeelde ATS-hosts zijn geen bronidentiteit: meerdere werkgevers mogen hetzelfde providerdomein gebruiken zolang iedere bron een unieke publieke `base_url` heeft. De scheduler blijft per origin coördineren, zodat deze extra bronrecords de providerlimiet niet omzeilen. Voor Corda Campus is een begrensde regionale listingadapter beschikbaar. Die leest uitsluitend de expliciete `/jobs/`-kaarten, bewaart de vermelde werkgever en weigert iedere joblink buiten `cordacampus.com`. Vacaturedetail en sollicitatie-URL's worden niet automatisch gevolgd. Voor elke provider-adapter: - `supports()` kijkt eerst op host/marker om andere pagina's niet te matchen; - listingpayloads produceren kandidaten zonder fetch van detail; - detailpayloads vullen een enkele vacaturestructuur; - elke veldafklaring krijgt veld-evidence met method-labels en confidence. ## Regionale Mol-adapter (VR-126) `MolRegionEmployerAdapter` verwerkt uitsluitend vijf vooraf gereviewde HTTPS-lijstroutes rond postcode 2400: SCK CEN, Cipal Schaubroeck, VanRoey, NTX/Netropolix en Thomas More. De adapter doet geen netwerkrequests en accepteert alleen same-host vacaturelinks. Cipal, NTX en Thomas More vereisen een expliciete lokale marker; bij Thomas More wordt bovendien de vaste publieke werkgever-GUID gecontroleerd. VanRoey-detailpaden zijn door robots uitgesloten en worden daarom nooit door VacatureRadar gefetcht; alleen de toegestane overzichtspagina dient als bron. ## Kempen-werkgeversadapter (VR-201) `KempenEmployerAdapter` voegt zeven gereviewde werkgeversroutes toe voor Ziekenhuis Geel, lokaal bestuur Geel, Stad Turnhout, Group Renotec, Ravago, Sanofi en DAF Trucks Westerlo. Iedere route heeft een vaste HTTPS-host en een toegestaan lijstpad; vacaturelinks moeten same-host zijn. Voor gedeelde of landelijke lijsten blijft alleen een kaart met de expliciete gereviewde vestiging over. Gemeente Mol wordt via de officiële same-host RSS-feed verwerkt. Interessante werkgevers zonder stabiele fail-closed parser blijven als niet-scanbare waaklijstbron zichtbaar.