# Profilvollständigkeit Framework

Ziel: Kandidatenprofile so vollständig und aktuell machen, dass anonymes Active Sourcing für Unternehmen effizient wird, ohne Kandidaten durch zu viele Fragen oder Datenschutzrisiken zu verlieren.

Grundprinzip: **100% vollständig heißt nicht "alles ausgefüllt", sondern "alle matchrelevanten Fragen sind beantwortet, frisch genug und für den jeweiligen Kontext nutzbar."** Eine explizite Negativantwort wie "keine Weiterbildung" ist genauso wertvoll wie ein ausgewählter Wert.

## Backend-Basis

Relevante Datenquellen im aktuellen Backend:

- `Applier`: Suchstatus, Präferenzen, Qualifikation, Standort, Matching-Signale.
- `User`: Identität, Kontaktbasis, Verifikation, `data_confidence_score`.
- `UserContact`: Telefonnummer, WhatsApp, E-Mail/SMS-Kontakte inklusive Verifikation.
- `UserFile`: Dokumenttypen wie Lebenslauf, Examensurkunde, Berufsurkunde, Sprachzertifikat.
- `WorkHistory`: Berufsstationen aus manuellem Eintrag oder Lebenslauf-Parsing.
- `ApplierHistory` / `UserOpenTasks`: interne CRM-Aufgaben, Due Dates und offene Bearbeitungen.

## Feldinventar

| Bereich | Backend-Felder | Einordnung | Aktualität | Matching-Must-have | Fit probability |
| --- | --- | --- | --- | --- | --- |
| Jobsuche-Status | `job_search_status`, `job_search_urgency`, `job_search_status_updated_at` | Präferenz / Status | Sehr häufig, alle 30-60 Tage | Produkt-Gate für Active Sourcing | Nein |
| Verfügbarkeit | `start_work_date`, `newest_registration_date`, `resubmission_at` | Präferenz / Timing | Häufig, alle 60-90 Tage | Produkt-Muss | Nein |
| Standort intern | `postcode`, `city`, `latitude`, `longitude` | Matching / Datenschutzsensibel | Mittel, alle 6 Monate oder bei Umzug | Code-Gate | Direkt: Distanz |
| Rolle / Zielposition | `position`, `position_other`, `position_verbatim` | Qualifikation + Präferenz | Mittel, alle 6-12 Monate | Produkt-Muss | Direkt: Positions-Similarity |
| Ausbildung | `education`, `education_other`, `education_verbatim_verbose`, `education_verbatim` | Qualifikation | Einmalig, nur bei Änderung | Code-Gate | Filter, nicht Formel |
| Berufserfahrung gesamt | `work_experience` | Qualifikation | Mittel, jährlich oder bei neuem Job | Produkt-Muss | Nein |
| Berufsstationen | `WorkHistory.company_name`, `position`, `start_date`, `end_date`, `is_current`, `duties` | Qualifikation / CV-Signal | Mittel, jährlich oder nach Jobwechsel | Produkt-Muss | Nein |
| Weiterbildungen | `continuing_education`, `continuing_education_other` | Qualifikation | Niedrig, bei neuer Weiterbildung | Code-Gate, wenn Job required CE hat | Direkt: required CE met |
| Sprache | `language_skill` | Qualifikation | Niedrig, alle 12 Monate oder bei Verbesserung | Code-Gate | Filter, nicht Formel |
| Führerschein | `driving_license` | Qualifikation / Mobilität | Niedrig, nur bei Änderung | Code-Gate bei Job-Anforderung | Filter, nicht Formel |
| Versorgungsart | `care_type` | Präferenz + Erfahrungskontext | Häufig, alle 60-90 Tage | Produkt-Muss | Direkt: Care-Type-Similarity |
| Arbeitsumfang | `working_type` | Präferenz | Häufig, alle 60-90 Tage | Code-Gate für Minijob-Logik | Direkt: Working-Type-Distanz |
| Dienstlänge | `shift_hour` | Präferenz | Häufig, alle 60-90 Tage | Produkt-Muss | Indirekt über `match_shift` |
| Schichtpräferenz | `shift_preferences` | Präferenz | Sehr häufig, alle 30-60 Tage | Produkt-Muss | Aktuell nicht gefunden |
| Gehaltsuntergrenze | `minimum_acceptable_salary` | Präferenz | Mittel, alle 3-6 Monate | Nein | Nein |
| Motivation / Wünsche | `motivation`, `others`, `note` | Soft Signal / Präferenz | Häufig, kontextbezogen | Nein | Nein |
| Dokumente | `UserFile.type`, `file_url`, `file_name`, `file_extention`, `is_removed` | Nachweis / Profilqualität | Bei Änderung, Upload bleibt gültig | Produkt-Muss für Qualität | Nein |
| Kontaktfähigkeit | `UserContact.contact_type`, `contact`, `verified_at`, `email_verified_at` | CRM / Trust | Sehr häufig bei Bounce oder fehlender Verifikation | Active-Sourcing-Gate | Nein |
| Kommunikationspräferenz | `mail_toggle`, `MessageUnsubscription` | CRM / Consent | Bei jeder Nachricht beachten | CRM-Gate | Nein |
| Matching-Score | `hiring_probability`, `predicted_revenue`, `xrevenue`, `data_confidence_score` | Intern / Scoring | Automatisch berechnet | Nein, abgeleitet | Nein, Ergebnis/abgeleitet |

Legende:

- **Code-Gate** = im aktuellen Matching-Code als Filter oder technische Voraussetzung gefunden.
- **Produkt-Muss** = für Active-Sourcing-Qualität notwendig, aber nicht zwingend technische Voraussetzung.
- **Fit probability direkt** = in `apps/match/match_probabilites.py` in der Formel.
- **Filter** = beeinflusst, ob ein Match entsteht, aber nicht den finalen Score.

Code-Befund:

Die aktuelle Fit-Probability-Formel nutzt direkt `care_type`, `position`, `match_shift`, `distance`, `working_type` und required `continuing_education`. `education`, `language_skill`, `driving_license` und required `continuing_education` wirken zusätzlich als Matching-Filter. `shift_preferences`, Gehalt, Dokumente und Job-Suchstatus habe ich aktuell nicht in der Fit-Probability-Formel gefunden.

## 100%-Framework V1

Vorschlag: Profilvollständigkeit wird als 100-Punkte-Modell geführt, aber mit Gates. Ein Profil kann z.B. 82% vollständig sein, aber trotzdem noch nicht active-sourcing-ready, wenn der Suchstatus veraltet ist oder kein Opt-in vorliegt.

| Modul | Punkte | Muss für Active Sourcing? | Kriterien |
| --- | ---: | --- | --- |
| Status & Aktualität | 20 | Ja | `job_search_status` gesetzt und jünger als 60 Tage; bei `active` zusätzlich `job_search_urgency` oder `start_work_date`. |
| Match-Präferenzen | 25 | Ja | `care_type`, `working_type`, `shift_preferences` oder `shift_hour`, Standort intern geocodiert. Optional: `minimum_acceptable_salary`. |
| Qualifikation | 25 | Ja | `position`, `education`, `work_experience`, `language_skill`, `driving_license` nicht `UNKNOWN`; Weiterbildung beantwortet. |
| Nachweise & Erfahrung | 15 | Nein, aber stark empfohlen | Lebenslauf vorhanden; Examensurkunde/Berufsurkunde vorhanden, wenn qualifikationsrelevant; mindestens eine `WorkHistory`-Station oder CV geparst. |
| Trust & Kontakt | 10 | Ja | verifizierter Kontaktkanal; keine relevante Abmeldung für geplanten Kanal; Kandidat ist erreichbar. |
| Active-Sourcing-Consent | 5 | Ja | Anonymes Profil explizit aktiviert, Preview-Version und Timestamp gespeichert. |

### Active-Sourcing-ready

Ein Kandidat ist active-sourcing-ready, wenn:

1. Score >= 80%.
2. Die Muss-Module Status, Match-Präferenzen, Qualifikation, Trust und Consent erfüllt sind.
3. Status ist `active` oder `open`; `passive` kann in einen Talent-Pool, aber nicht aktiv ausgespielt werden.
4. Datenschutzregel erfüllt ist: Unternehmen sehen keine exakte PLZ, keine Koordinaten, keinen Namen, keine Kontaktdaten, keine Dokumentdateien und keinen aktuellen Arbeitgeber.

## Qualifikation vs. Präferenz

### Qualifikation

Diese Felder ändern sich selten und sollten in CRM-Nachrichten nicht ständig erneut abgefragt werden:

- Ausbildung: `education`, `education_other`, `education_verbatim_verbose`.
- Weiterbildungen: `continuing_education`, `continuing_education_other`.
- Berufserfahrung: `work_experience`, `WorkHistory`.
- Position/Rolle: `position`, `position_other`, `position_verbatim`.
- Sprache: `language_skill`.
- Führerschein: `driving_license`.
- Dokumente: `UserFile` mit `RESUME`, `TRAINING_CERTIFICATE`, `PROFESSIONAL_LICENSE`, `LANGUAGE_CERTIFICATE`, `FURTHER_TRAINING`, `JOB_REFERENCE`, `POLICE_CLEARANCE`, `COVER_LETTER`.

CRM-Regel: erst fragen, wenn fehlt, offensichtlich widersprüchlich ist, aus Lebenslauf neu ableitbar ist oder seit 12 Monaten nicht bestätigt wurde.

### Präferenz

Diese Felder sollten häufiger aktualisiert werden, weil sie stark situativ sind:

- Suchstatus: `job_search_status`, `job_search_urgency`.
- Startdatum: `start_work_date`.
- Versorgungsart-Wunsch: `care_type`.
- Arbeitsumfang: `working_type`.
- Schichtlänge: `shift_hour`.
- Schichtpräferenz: `shift_preferences`.
- Gehalt: `minimum_acceptable_salary`.
- Sonstige Wünsche: `motivation`, `others`, ggf. Bewerbungskontext aus Experiment 7.1.

CRM-Regel: alle 30-90 Tage aktualisieren, abhängig von Aktivität. Wer aktiv sucht, wird häufiger gefragt; wer passiv ist, bekommt eher leichte Confirm-Flows.

## Nachrichtensystem

Das CRM sollte nicht "Profil vervollständigen" allgemein senden, sondern immer **eine konkrete fehlende oder veraltete Signalgruppe** abfragen.

Priorität:

1. **Status bestätigen**: "Bist du aktuell aktiv auf Jobsuche, offen für Angebote oder gerade nicht suchend?"
2. **Timing klären**: "Falls es passt: Wann könntest du starten?"
3. **Schichten aktualisieren**: "Welche Schichten passen aktuell für dich?"
4. **Versorgungsart aktualisieren**: "Welche Versorgungsarten kommen für dich infrage?"
5. **Arbeitsumfang / Gehalt**: "Welcher Umfang und welche Gehaltsuntergrenze wären für dich passend?"
6. **Qualifikation schließen**: "Welche Ausbildung / Weiterbildung sollen wir in deinem Profil ergänzen?"
7. **Nachweis anfordern**: "Du kannst deine Examensurkunde oder Berufsurkunde ergänzen, damit Einrichtungen schneller entscheiden können."
8. **Berufserfahrung ergänzen**: "Möchtest du deine letzte Station ergänzen oder deinen Lebenslauf hochladen?"

Empfohlene Cadence:

- Aktiv suchend: maximal 1 Profilfrage pro Woche, Status alle 30 Tage.
- Offen/passiv: maximal 1 Profilfrage pro Monat, Status alle 60-90 Tage.
- Nicht suchend: kein Active-Sourcing-Push; höchstens alle 90 Tage "Status hat sich geändert?".
- Nach jeder Bewerbung: nur kontextbezogene Mini-Frage wie Experiment 7.1.

## Produkt-/Backend-Gaps

Für ein wirklich sauberes 100%-Framework fehlen oder sind aktuell nur indirekt vorhanden:

1. **Feld-Level-Aktualität**: Es gibt `job_search_status_updated_at`, aber keine `*_updated_at` pro Präferenz/Qualifikation. `ApplierHistory` enthält Diffs, ist aber für CRM-Staleness unhandlich.
2. **Explizite Negativantworten**: Leere `continuing_education` bedeutet aktuell "unbekannt" oder "keine Weiterbildung". Für Vollständigkeit braucht es "keine Weiterbildung bestätigt am ...".
3. **Spezialisierungs-Erfahrungsjahre**: Experiment 05 ist fachlich wichtig, aber im aktuellen `Applier` nicht als eigenes aktuelles Feld sichtbar. `area_of_experience` wurde entfernt; `WorkHistory` ist dafür zu grob.
4. **Dokumenten-Verifikation**: `UserFile` speichert Typ und Datei, aber keine sichtbare Verifikation wie `verified_at`, `verified_by`, `rejected_reason`.
5. **Active-Sourcing-Consent**: Das anonyme Active-Sourcing-Profil braucht persistente Felder für Opt-in, Preview-Version und sichtbare Feldgruppen.
6. **Anonymitätsregel für Entfernung**: Standortdaten existieren exakt; Company-Ansicht sollte daraus nur Buckets ableiten und bei geringer Kandidatendichte ausblenden.

## Empfohlene neue Datenstruktur

Minimal für V1:

- `ProfileSignalStatus`
  - `user_id`
  - `signal_key` z.B. `job_search_status`, `shift_preferences`, `education`, `documents.resume`
  - `status`: `missing`, `answered`, `confirmed_none`, `stale`, `verified`
  - `last_answered_at`
  - `last_prompted_at`
  - `source`: `registration`, `profile`, `crm_whatsapp`, `post_apply_chat`, `resume_parser`, `agent`

- `AnonymousActiveSourcingConsent`
  - `user_id`
  - `enabled_at`
  - `disabled_at`
  - `preview_version`
  - `visible_field_groups`
  - `source`

- `ApplierSpecializationExperience`
  - `applier_id`
  - `area`
  - `years`
  - `source`
  - `confirmed_at`

Damit kann das CRM sauber entscheiden: nicht "Profil unvollständig", sondern "Schichtpräferenz ist 74 Tage alt", "Weiterbildungen unbekannt", "Lebenslauf fehlt", "Consent fehlt".
