feat: ship premium IT-focused vacancy radar
deploy / deploy (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-22 15:16:15 +02:00
parent 7d3d026d1d
commit 029df89265
23 changed files with 838 additions and 53 deletions
+44
View File
@@ -1444,6 +1444,50 @@ tasks:
note: GeoNames België postcodedataset 2026-04-03 onder CC BY 4.0 gereviewd, checksums vastgelegd, 2781 unieke
rijen gevalideerd en 5562 lookuprecords geïmporteerd; 2400 Mol opgelost naar 51.1919, 5.1166 en actieve profielscores
herberekend.
- id: VR-206
title: Premium Stitch-cockpit en deterministische IT-focus
status: done
priority: P0
requirement_ids:
- PR-040
- PR-050
- PR-051
- NFR-004
- NFR-008
depends_on:
- VR-119
- VR-120
- VR-205
summary: Audit de volledige gebruikersflow in code en browser, voorkom dat niet-IT-vacatures de dagelijkse radar
vervuilen en benut de Stitch-compositie veel dominanter op mobiel, desktop en widescreen.
acceptance_criteria:
- Een deterministische en uitlegbare IT-relevantieregel heeft voorrang op scoring en AI; niet-IT-vacatures zijn
standaard afwezig uit Vandaag en de vacaturefeed maar blijven via een expliciete archiefkeuze inspecteerbaar.
- De vacaturefeed gebruikt echte score-evidence, bruikbare relevantie-/match-/sorteerfilters en toont standaard
de meest relevante IT-kansen in plaats van louter de nieuwste bronrecords.
- Dashboard, vacaturelijst en vacaturedetail nemen de bruikbare Stitch-panelhiërarchie, informatiedichtheid en
actiepatronen over zonder fictieve metrics, quick-apply of externe frontendafhankelijkheden.
- De shell en kernlayouts schalen inhoudelijk tussen 320 en 2560 px; widescreen voegt bruikbare kolommen/panelen
toe en laat de inhoud niet op een smalle 1280px-strook staan.
- Focus, headings, labels, lege toestanden, dark/light contrast, mobiele navigatie en reduced motion blijven toegankelijk;
kernroutes hebben geen horizontale paginaoverflow of browserconsolefouten.
verification:
- uv run pytest tests/unit/test_relevance.py tests/unit/test_scoring.py tests/integration/test_views.py tests/e2e/test_vr114_browser_and_accessibility.py
- ./scripts/codex_verify.sh
- Browsermatrix op 2560x1440, 1920x1080, 1440x900, 1024x768, 390x844 en 320x800
primary_paths:
- apps/jobs/services/relevance.py
- apps/jobs/services/scoring.py
- apps/jobs/views.py
- templates/dashboard/today.html
- templates/jobs/list.html
- templates/jobs/detail.html
- static/css/app.css
- docs/design/STITCH_INTEGRATION_PLAN.md
result:
completed_at: '2026-07-22'
note: 'Volledige code- en browseraudit: deterministische IT-focus, laatste gebruikersscore, premium Stitch-feed/detail
en adaptieve 320-2560px-layout; 225 tests groen en browsermatrix geslaagd.'
- id: VR-301
title: Multi-tenant SaaS, teams en billing
status: deferred
+17 -1
View File
@@ -1,7 +1,7 @@
# Projectstatus
- Laatst bijgewerkt: 2026-07-22
- Repositoryversie: 0.2.10 Kempen-werkgeversradar en GeoNames
- Repositoryversie: 0.2.11 premium IT-radar en ultrawide Stitch-cockpit
- Uitvoeringsmodus: autonome backlog
- Eerste uitvoerbare taak: controleer met `python scripts/backlog.py next`
@@ -131,6 +131,15 @@ VR-201/VR-205-herverificatie op 2026-07-22:
- de fetcher valideert het werkelijk verbonden publieke peer-IP, zodat legitieme CDN-rotatie van DAF en Xplore Group veilig wordt ondersteund;
- taakledger bevat 44 taken: 38 `done`, 3 `blocked-external`, 3 `deferred` en 0 `ready`.
VR-206-herverificatie op 2026-07-22:
- 225 geslaagde tests en 2 optioneel overgeslagen pytest-Playwrightvarianten; HTML/a11y-fallback geslaagd;
- 84,53% branch-aware codedekking; Ruff, Django system check, migratiecontrole, taakledger en repositoryvalidatie geslaagd via `scripts/codex_verify.sh`;
- de standaardradar gebruikt een deterministische titelgestuurde IT-grens en laat verborgen/hard uitgesloten matches weg, met expliciete archiefopties voor volledige inspectie;
- de lokale auditinventaris telt 336 actieve records: 108 aantoonbare IT-titels, 228 gefilterde niet-IT-records en 102 kaarten in de persoonlijke radarselectie;
- de Stitch-cockpit benut maximaal 2240 px met tweekoloms matchfeeds op ultrawide; de zes kernroutes zijn handmatig gecontroleerd op 320, 390, 1024, 1440, 1920 en 2560 px zonder horizontale paginaoverflow;
- dark/light, gelabelde velden, mobiele drawer, score-/confidencecopy, archieffilters en inline bewaren zijn in de geïntegreerde browser gecontroleerd.
## Deploymentstatus
- `main` is via de bestaande Gitea-SSH-sleutel naar `NuklearRabbit/VacatureRadar` gepusht.
@@ -141,6 +150,13 @@ VR-201/VR-205-herverificatie op 2026-07-22:
## Laatste uitgevoerde backlogtaak
- 2026-07-22: `VR-206` voltooid.
- Niet-IT-vacatures en commercieel/recruitmentmisleidende IT-titels zijn standaard uit Vandaag en de vacaturefeed verwijderd zonder bronhistoriek te wissen.
- Matchsortering, radar-/archieffilters, score-evidence en gebruikersgescopeerde dashboardscores maken de dagelijkse selectie beslisbaar.
- De Stitch-dominante shell, feeds, details en context-rails schalen van 320 tot 2560 px en gebruiken op ultrawide twee resultaatkolommen.
- Een open redirect in feedbackterugkeer, 320 px bronoverflow en de ontbrekende faviconfallback zijn als auditbevindingen mee opgelost.
- Volgende taak: geen uitvoerbare `ready` taak; `VR-202`, `VR-203` en `VR-204` blijven `blocked-external`.
- 2026-07-22: `VR-201` en `VR-205` voltooid.
- De officiële GeoNames-postcodedataset lost postcode 2400 op naar Mol en levert indicatieve postcodecentrumafstanden; licentie, versie, reviewdatum en twee SHA-256-checksums zijn vastgelegd.
- Gemeente Mol, Ziekenhuis Geel, lokaal bestuur Geel, Stad Turnhout, Group Renotec, Ravago, Sanofi Geel en DAF Trucks Westerlo zijn na fixtures, negatieve routechecks en twee livecycli actief.
+3 -2
View File
@@ -52,11 +52,12 @@ mag in een afgeschermde browsercontext uitvallen zonder de bediening te breken.
## 4. Spacing en layout
- 4/8px-gebaseerde schaal;
- contentcontainer maximaal 1600px zodat de cockpit op brede schermen bruikbaar blijft;
- contentcontainer maximaal 2240px; vanaf 1850px gebruikt de cockpit extra resultaatkolommen en
contextpanelen in plaats van alleen langere tekstregels;
- kaarten met consistente padding 2024px desktop, 16px mobiel;
- minimaal 44×44px touch targets voor primaire interactie;
- vaste zijbalk van 272px boven 900px, toegankelijke uitschuifnavigatie daaronder;
- grids breken onder 1180px, 900px en 680px logisch af;
- grids breken rond 1850px, 1350px, 1180px, 900px en 680px logisch af;
- vaste/sticky elementen mogen content niet bedekken.
## 5. Componenten
+12 -1
View File
@@ -19,6 +19,17 @@ Na productfeedback is de integratie aangescherpt: Stitch bepaalt nu niet alleen
De bedoelde “circa 90% Stitch” slaat hiermee op overgenomen visuele structuur en interactiehiërarchie, niet op het kopiëren van fictieve content, externe assets, auto-apply of routes waarvoor het domeinmodel geen betrouwbare data bezit.
## VR-206: premium radar en ultrawide compositie
De volledige audit maakt de Stitch-compositie ook functioneel dominant. De vacaturefeed start met
een brede commandofilter, toont beslisbare score-evidence en krijgt op breed desktop een vaste
radarcontext. Vanaf 1850 px worden matchfeeds tweekoloms; de shell benut maximaal 2240 px in plaats
van de vroegere 1280 px. Op mobiel worden filters, sectiekoppen en acties lineair gestapeld.
De visuele hiërarchie is gekoppeld aan een deterministische IT-grens: niet-IT en hard uitgesloten
matches zijn standaard afwezig. Het volledige archief en verborgen scores blijven expliciet
inspecteerbaar, zodat premium rust niet ten koste gaat van auditbaarheid.
## Visuele richting
- Donkere cockpit als standaard: diep marineblauw, tonale panelen, fijne slate-randen, signaalcyaan voor primaire interactie, emerald voor aantoonbaar gezonde status en koraal/amber voor fouten of aandacht.
@@ -58,7 +69,7 @@ De bedoelde “circa 90% Stitch” slaat hiermee op overgenomen visuele structuu
## Responsive en toegankelijkheid
- Validatiebreedtes: 1440×900, 1280×800, 1024×768, 768×1024, 390×844 en 360×800.
- Validatiebreedtes: 2560×1440, 1920×1080, 1440×900, 1024×768, 390×844 en 320×800.
- Eén `h1` per pagina, semantische landmarks, zichtbare focus, toetsenbordbediening, 44px touchdoelen waar praktisch, tekstlabels naast statuskleur en een robuuste skiplink.
- Tabellen schakelen op smalle schermen naar kaartachtige rijen of blijven gecontroleerd horizontaal scrolbaar wanneer kolomvergelijking essentieel is.
- Animatie is subtiel en volledig uitgeschakeld bij `prefers-reduced-motion`.
+6
View File
@@ -56,6 +56,8 @@ Regels:
Filters:
- vrije tekst;
- relevantiefocus: standaard aantoonbare IT-titels, met expliciet volledig bronarchief;
- radarselectie: standaard geen harde uitsluitingen of verborgen score, met aparte inspectiefilters;
- status;
- recommendation/scoreband;
- werkvorm;
@@ -65,6 +67,10 @@ Filters:
Desktop gebruikt een compacte filterbalk; mobiel een disclosurepanel. Filters blijven in querystring zodat bookmarks en back navigation werken.
De standaardvolgorde is persoonlijke matchscore, niet importtijd. Niet-IT-inhoud wordt niet
verwijderd: ze blijft uitsluitend via `Volledig archief` bereikbaar. Een beschrijvingshit alleen
mag nooit een niet-IT-titel promoveren.
Tabel of kaarten kiezen op breedte. Geen horizontale scroll voor kernvelden.
## 5. Vacaturedetail
+65
View File
@@ -0,0 +1,65 @@
# VR-206 volledige UX-, logica- en browseraudit
Datum: 2026-07-22
## Uitgangssituatie
De productiecontrole toonde 242 actieve vacatures. De standaardlijst sorteerde hoofdzakelijk op
`first_seen` en liet elke actieve vacature zien. Daardoor stonden onder meer facilitaire techniek,
logopedie, vroedkunde en niet-IT-onderzoek tussen de persoonlijke IT-resultaten. Op 2560 px bleef
de inhoud bovendien op ongeveer 1280 px begrensd, waardoor de Stitch-cockpit een brede lege zone
overliet.
## Bevindingen en oplossing
| Prioriteit | Bevinding | Oplossing |
| --- | --- | --- |
| P0 | Niet-IT-vacatures bereikten Vandaag en de standaardfeed. | Titelgestuurde, deterministische IT-classificatie vóór score/AI; niet-IT blijft alleen in het expliciete volledige archief. |
| P0 | Vandaag gebruikte niet overal de laatste gebruikersgescopeerde score en open dossiercijfers waren niet volledig gebruikersgescopeerd. | Laatste score per vacature/profiel via subquery; profiel, scores en dossiers zijn aan de aangemelde gebruiker gebonden. |
| P1 | Verborgen/hard uitgesloten IT-vacatures stonden hoog door hun ruwe score. | Nieuwe standaardkeuze `Radarselectie` laat verborgen resultaten weg; `Verborgen` en `Alle IT-scores` blijven bewuste inspectiekeuzes. |
| P1 | De lijst sorteerde op bronrecency in plaats van persoonlijke besliswaarde. | Standaard matchscore aflopend, met alternatieven voor nieuwste en sluitingsdatum. |
| P1 | Stitch werd visueel gebruikt, maar ultrawide bleef een smalle strook. | Adaptieve contentbreedte tot 2240 px, tweekoloms feeds vanaf 1850 px en context-/actierails op breed desktop. |
| P1 | Vacaturekaarten boden weinig scorebewijs. | Score, datakwaliteit, eerste pluspunt, eerste aandachtspunt, status, werkmodel en directe actie staan in de feed. |
| P2 | Confidencewaarden wisselden tussen breuken en percentages. | Alle dagelijkse score- en extractiekwaliteit wordt als percentage getoond. |
| P2 | Inline feedback accepteerde een willekeurige `next`-URL. | Terugkeer alleen naar dezelfde host, met negatieve regressietest. |
| P2 | De bronpagina liep op 320 px uit door statuschips naast lange sectiekoppen. | Sectiekoppen stapelen onder 680 px; de 320 px matrix heeft geen horizontale overflow. |
| P2 | Sommige browsers vroegen `/favicon.ico` en kregen een 404. | Lokale permanente fallback naar het bestaande SVG-favicon. |
## Relevantieregel
De classifier is bewust conservatief en titelgestuurd. Een beschrijving die toevallig “digitale
systemen” vermeldt, maakt een zorg- of facilitaire functie niet relevant. Duidelijke IT-families
zoals infrastructuur, netwerk, cloud, security, software, data, applicatiebeheer en workplace
worden toegelaten. Commerciële of recruitmentcontext die IT-woorden bevat, wordt expliciet
afgewezen. Dezelfde regexcontracten worden in Python en in de databasequery gebruikt.
Op de lokale auditdataset met 336 actieve records bleven 108 titelmatig aantoonbare IT-vacatures
over en werden 228 niet-IT-records uit de standaardfocus gehouden. Na uitsluiting van verborgen
matches bevatte de persoonlijke radarselectie 102 kaarten. Het volledige archief blijft 336
records bevatten: er wordt geen bronhistoriek gewist.
## Stitch-vertaling
- Dashboard: KPI-strip, beslisbare matchfeed en vaste radar-/actierail.
- Vacaturelijst: brede commandofilter, compacte evidencekaarten, scorehiërarchie en radarcontext.
- Detail: scorehero, intelligence-strip, bronhistorie en menselijke actierail.
- Shell: vaste desktopnavigatie, compacte utilitybar, lokale iconen en mobiele drawer.
- Ultrawide: twee inhoudskolommen plus context; geen kunstmatig uitgerekte tekstregels of fictieve grafieken.
Alle cijfers en signalen komen uit de database. Quick apply, automatische sollicitatie,
platformloginbots, fictieve trendgrafieken en externe frontendassets blijven bewust afwezig.
## Browsermatrix
Handmatig gecontroleerd in de geïntegreerde browser op 320×800, 390×844, 1024×768, 1440×900,
1920×1080 en 2560×1440. De zes kernroutes (`/`, `/jobs/`, `/jobs/applications/`, `/profiles/`,
`/sources/`, `/system/`) hebben één `h1`, gelabelde formuliervelden en geen horizontale
paginaoverflow. De vacaturefeed schakelt van één kolom naar twee kolommen op ultrawide en gebruikt
daar 2240 px van de beschikbare ruimte. Mobiele navigatie opent en sluit met correcte
`aria-expanded`-status.
## Resterend operationeel punt
Na deployment moet de bestaande productie-inventaris opnieuw gescoord worden zodat opgeslagen
score-evidence eveneens het nieuwe `it_relevance`-blok bevat. De viewfilter werkt onmiddellijk en
onafhankelijk daarvan; het herscoren verbetert de uitleg en blijvende `hidden`-status.
+1
View File
@@ -80,6 +80,7 @@ Django/Celery
| T-38 | Cross-user mailboxbeheer, dubbele polls of IMAP naar private host | credentialmisbruik, duplicaten, toegang tot interne diensten | alle views scopen op gebruiker en vereisen CSRF; unieke user+platformkoppeling; transactionele lease; hostvorm en publieke DNS/IP-validatie vóór TLS-IMAP | `tests/integration/test_mailbox_connections.py`, `tests/unit/test_mailbox_credentials.py`, `apps/sources/services/imap_import.py`, `VR-123` |
| T-39 | Mol-/Kempenbron vermengt een andere werkgever, campus of vestiging | foutieve afstand, phishing of buitenregionale resultaten | exacte HTTPS-host/pad/GUID-routes; same-host details; expliciete plaatsfilters voor regionale en gedeelde lijsten; postcodecentra blijven als benadering herkenbaar; waaklijstbronnen worden niet gescand | `tests/unit/test_regional_adapters.py`, `tests/integration/test_tasks_and_more_views.py`, `VR-126/201` |
| T-40 | Verouderde lokale CA-store of ongeldige serverketen lokt een TLS-bypass uit | onderschepte vacature- of mailboxdata en credentialdiefstal | expliciet gepinde `certifi`-CA-bundel voor HTTP, robots en IMAP; hostnaamcontrole en `CERT_REQUIRED`; nooit `verify=False`; dependency- en negatieve TLS-tests bij update | `tests/unit/test_tls.py`, `tests/security/test_robots.py`, `apps/sources/services/tls.py`, `VR-201` |
| T-41 | Gemodificeerde `next`-parameter na vacaturefeedback | open redirect naar phishingdomein na een geldige gebruikersactie | terugkeer-URL moet dezelfde host en het actuele HTTPS-beleid respecteren; anders veilige detailfallback | `tests/integration/test_tasks_and_more_views.py`, `VR-206` |
## Misbruikscenario's
+3 -3
View File
@@ -32,14 +32,14 @@ Legenda: **I** = geïmplementeerd en getest in de huidige MVP; **P** = gedeeltel
| PR-031 | I | conservatieve fuzzy dedupe | `tests/unit/test_dedupe_lifecycle_feedback.py`, `scripts/benchmark.py`, `tests/benchmark/test_benchmark.py` | VR-116 benchmark |
| PR-032 | I | direct-sourcepreferentie/alias | pipeline- en dedupetests | VR-108 resolver |
| PR-033 | I | canonical constraints/transacties | replaytests | — |
| PR-040 | I | features/scoring | scoringtests | |
| PR-040 | I | features/scoring + deterministische IT-relevantie vóór AI | `tests/unit/test_scoring.py`, `tests/unit/test_relevance.py`, viewtests | VR-206 |
| PR-041 | I | scorecomponenten | `tests/unit/test_features_distance.py`, `tests/unit/test_scoring.py` | VR-105 afgerond |
| PR-042 | I | unknown/confidencegedrag | `tests/unit/test_features_distance.py`, `tests/unit/test_scoring.py` | VR-105 |
| PR-043 | I | `ScoreRun` | pipeline/scoringtests | — |
| PR-044 | I | profiel/modelversievelden | scoring/profiletests | VR-109 promptversie |
| PR-045 | I | optionele Ollamaservice/fallback | `test_ai.py` | VR-109 |
| PR-046 | I | geen tools + guardrails | AI/securitytests | VR-109/115 evaluaties |
| PR-050 | I | dashboard view/template + Stitch-dominante cockpitshell/feed/rail | viewtests, VR-119 browsermatrix | VR-114/118/119 e2e/a11y |
| PR-050 | I | dashboard/list views + Stitch-dominante cockpitshell/feed/rail en ultrawide compositie | viewtests, VR-119/206 browsermatrix | VR-114/118/119/206 e2e/a11y |
| PR-051 | I | detailview/template/provenance + scorehero/intelligence-strip | viewtests, VR-119 screenshots | VR-114/118/119 |
| PR-052 | I | feedbackendpoint/UI | feedback/viewtests | — |
| PR-053 | I | applicationmodel/serviceflow | `tests/integration/test_dedupe_lifecycle_feedback.py`, `tests/integration/test_applications.py` | VR-113 uitgebreid dossier |
@@ -98,5 +98,5 @@ Iedere backlogtaak die een requirement wijzigt, werkt in dezelfde commit deze ma
- VR-126 herankert profiel en broncatalogus op postcode 2400 Mol en 40 km. De host-, pad- en locatiefilters voor SCK CEN, Cipal Schaubroeck, VanRoey, NTX en Thomas More worden gedekt door regionale adaptertests, source-viewtests, live bronruns en de bijgewerkte threat T-39.
- VR-201 activeert Gemeente Mol en zeven bijkomende Kempen-werkgevers na menselijke voorwaardenreview, fixtures, fail-closed locatie-/routefilters en twee livecycli. Twaalf interessante werkgevers zonder stabiele actieve ingang staan afzonderlijk en niet-scanbaar op de UI-waaklijst. Bewijs staat in de livecatalogus, seeddata, `KempenEmployerAdapter`, bronruns en adapter-/seed-/viewtests.
- VR-205 laadt de officiële Belgische GeoNames-postcodedataset 2026-04-03 onder CC BY 4.0, documenteert bron- en afgeleide checksums en lost 2400 Mol op naar het benaderende postcodecentrum. Bewijs staat in `config-data/geodata/README.local.md`, het importlog, geocodingtests en de opnieuw berekende profiel-vacaturescores.
- VR-206 voegt een titelgestuurde IT-relevantiegrens vóór scoring/AI toe, maakt de persoonlijke radarselectie de standaard en benut Stitch-panelen dynamisch tot 2560 px. Bewijs staat in `apps/jobs/services/relevance.py`, de score-/viewtests en `docs/design/validation/VR-206_AUDIT.md`.