Changelog
GitHubJede nennenswerte Änderung, Release für Release. Dieselbe Datei liegt im Repository und ist in alle 19 Sprachen übersetzt.
[1.253.0] — 2026-10-24
Zehn neue Job-Quellen stoßen zum Scanner — Atlassian, BWI, Computrabajo, Dover, Google, Kalibrr, Magneto365, Platsbanken, Tech in Asia und Zoho Recruit — die Registry liefert jetzt 120 Quellen (115 EN + 5 RU) mit 115 Adaptern, und der Role-Matcher hält Titel auseinander, die sich nur durch einen kurzen Großbuchstaben-Code unterscheiden.
Hinzugefügt
- 10 Quellen aus dem Parent 1.38.0 portiert, jeweils mit Fetch-Walk-Adapter und CI-isolierter Suite (fake fetchImpl, null Netzwerk): Atlassian (einzelner auth-freier JSON-Feed, iCIMS-URLs, https+icims.com-Allowlist), BWI (JSON-LD ItemList, JSON.parse zuerst), Computrabajo (LATAM, Entitäten-Dekodierung, spanisches „remoto“ liest remote), Dover (DRF-Paginierung pro Tenant,
Remote (International)-Rendering, UUID-validierte Boards), Google Careers (Erstseiten pro Phrase, Karten-Arbeitgeber gewinnt, Dedup über Abfragen), Kalibrr (SEA-Aggregator, Feed-ID als Zeilen-ID), Magneto365 (LATAM, festes Inter-Page-Tempo), Platsbanken (schwedische JobTech-API, Keyword-Fenster), Tech in Asia (Algolia-Schlüssel frisch aus dem Site-Build), Zoho Recruit (Hidden-Input-Board, begrenzte Detail-Anreicherung, Währung nur ISO-4217). - Registerzählung: 120 Quellen = 115 EN + 5 RU · 115 Adapter; die fünf Zähl-Gates aus der Live-Registry regeneriert;
#/scan-Dropdown-Fallback, cvstart.org-Quellenlinks und help §17 ×18 folgen.
Behoben
- Role-Matcher (#5688-Port): Titel, die sich nur durch einen kurzen Großbuchstaben-Code unterscheiden („HR BP“ vs. „HR GP“, „AWS“ vs. „GCP“, auch in gemischten Schriften), werden nicht mehr fuzzy als dieselbe Stelle gematcht; Codes werden aus dem Rohtitel ohne Noise-Wörter gelesen, der Guard sitzt direkt vor der Jaccard-Ratio.
- Die Ports überlebten ein Opus-5-5-Review maximaler Strenge (21 Befunde): Throw bei still-leeren Boards durch Markup-Drift (google/bwi), ein vorab abgebrochener Scan wirft, statt
[]zu liefern, Decode-then-strip-Reihenfolge in den LATAM-Scrapern (die Strip-then-decode-XSS-Form), https+Allowlist auf Atlassians Zeilen-URLs, ports-abweisende Host-Asserts, Late-Page-Parse im try (Partials überleben) und Test-Ehrlichkeit (Budgets exakt gezählt, Coercion-Verträge wahr fixiert). 13 Befunde behoben; der [spec]-Schwanz separat getrackt.
Hinweise
- Die fünf RU bleiben web-ui-only (keine Upstream-Adapter — mit Absicht); jede EN-Quelle hat ihren Adapter.
- Coverage-Ratchet: die sechs größten Ports auf ≥95 % Branch gehoben (27 neue Fälle); der restliche defensive Totcode ist je Datei dokumentiert.
- Neue Quellen werden mit
provider: <slug>in portals.yml gewählt — für bestehende Konfigurationen ändert sich nichts.
[1.252.1] — 2026-10-17
Das Release-Banner des Wikis wird jetzt von einem Skript geprüft statt von einem manuellen Durchlauf — die v1.252.0-Regression hat es nur von Hand erwischt — und die Locale-Direktive überschreibt explizit den language.output des Profils und die Sprache der Stellenanzeige.
Hinzugefügt
scripts/wiki-banner-check.mjs— das Gate, das die Regression forderte: es liest jedeHome*.mdin einem Wiki-Checkout und vergleicht das Banner mitpackage.jsonund den LIVE-Zählern aus dem Register (scanner adapters aus dem Portals-Register — 105, nicht die veralteten 109 des Banners; route modules ausserver/lib/routes/; UI locales aus dem CHANGELOG-Satz). Die Banner-Regex ist sprachagnostisch nach FORM: die lokalisierten Home-Seiten übersetzen Label und Zähler („Текущий релиз“, „Aktuel udgivelse“, „адаптеров сканера“, „enhedstests“), und ihre Zahlen waren weiter abgedriftet (5177) als die EN-Seite (5245).--tests Nübernimmt die Zahl, die ein frischestest:cigerade ausgegeben hat;--check-onlyverbietet den impliziten Clone für Offline-Nutzung. Offline-Fixture-Test intests/wiki-banner-check.test.mjs.
Behoben
- Das Wiki-Home-×18-Banner wird wieder in Sync gebracht (kommt mit diesem Release): der v1.252.0-Durchlauf tauschte Badges und Links, aber nie die Banner-Zeile, und nichts Mechanisches flaggte das. Das obige Gate macht daraus eine release-blockierende Prüfung.
- Die Locale-Direktive legt die Sprachpriorität fest (
buildLocaleDirective): „This UI locale overrides the profile’slanguage.outputsetting and the job posting’s own language“. Die eigene Regel 6 des Parents gebietet das Schreiben inlanguage.output— ohne den Prioritätssatz löste das Modell die beiden widersprüchlichen Anweisungen je Locale inkonsistent auf, genau die Streuung der Sprachspalte in v1.252.0. Verankert durchtests/locale-directive-language-output.test.mjsauf dem Direktivtext (17 Nicht-EN-Locales + leeres EN).
Hinweise
- FORM-Beobachtung bei ta evaluate (§3, beobachten): der erste Live-Lauf der v1.252.0 erzeugte ein 48 336-Zeichen-evaluate ohne Block G und ohne SCORE_SUMMARY — schleifenförmig, nicht sprachförmig (CAR-70 ist Sprache). Wiederholt es sich beim nächsten Release, bekommt es ein eigenes Linear-Issue, getrennt von CAR-70.
- Laut FIX-PROMPT: der Parent bleibt read-only; die Überschreibung lebt entirely in der UI-Direktive.
[1.252.0] — 2026-10-17
Der Parent-Fork ist mit career-ops-hq/career-ops synchronisiert — 264 Upstream-Commits, Parent 1.36.0 → 1.37.0 — beide Fork-Divergenzen verteidigt, und das Deployment pinnt den laufenden Parent wieder auf diese Basis.
Hinzugefügt
- Vietnamesischer (vi) Marktmodus aus dem Upstream (
modes/vi/,tuyen-dung/ung-tuyen): BHXH/BHYT/BHTN, thử việc, lương tháng 13, trợ cấp thôi việc — der 21. Markt in der Modustabelle des Parents. - Tech-in-Asia-Board-Provider (Upstream): liest den öffentlichen Algolia-Index
job_postings, den das Jobs-UI von techinasia.com selbst abfragt — ein SEA-Startup-Board, ~300 lebende Postings bei der Stichprobe, 273 davon indonesisch. Schlüssel werden bei jedem Lauf frisch aus dem Site-Build entdeckt, der eigene Bewerbungslink des Arbeitgebers schlägt die Board-Seite, und das Gehalt bleibt bewusst unangehängt (die Gehaltsfelder des Index sind Flags, kein Geld). - Acht neue Block-G-Dokumente zu Posting-Legitimitätssignalen (
modes/oferta-signals/08–17): Land in der Benefit-Terminologie, Standort-Tag einer Drittplattform, Agentur-Lizenzierung, überzogene Einwanderungsanforderungen, jurisdiktionsverbotener Inhalt, Pay-Transparency-Spannenbreite, Mindestlohn-Anwaltsfrage, AI-Screening-Offenlegung, Befristungsvertrag-Offenlegung, Kaufkraft bei der Relocation.
Geändert
- Härtungen auf Parent-Seite, die mit der Basis kommen: die Story-Provenance-Prüfung arbeitet nach Einheit und Satz und hält
user-cannot-confirm-Zahlen aus Verhandlung, ATS und Interview-Output heraus; der CV-Fact-Gate läuft auf jedem Renderer und liest umformulierte und strukturierte Aussagen; LaTeX verwirft abgelaufene extrahierte Slots vor dem Schreiben; der Launchd-Sweep der Follow-ups wird übersprungen, wenn Follow-ups aus sind; update-system hält seinen Scan lokaler Bearbeitungen unter der argv-Grenze. - Die Fork-Divergenzen überlebten den Merge:
providers/telegram.mjs(nur im Fork vorhandener Parser, ADR-0001) und das Unicode-LookaroundLOCATIONISH_REintelegram-channel.mjs(ADR-0002). Das Boards-Dokument listet nun die verbessertetelegram-channel-Zeile des Upstreams, die nur im Fork vorhandenetelegram-Zeile, Tech in Asia und den Tencent-Standardmax_pageserhöht auf 20 → 100.
Hinweise
- Keine SPA-Verhaltensänderung in diesem Release: es ist der Dokumentenzug plus das Neu-Pinnen des Parents. Das Deployment läuft jetzt auf Parent 1.37.0; die Tabelle der kanonischen Status wird vom Parent-Delta nicht berührt — die Tracker-Status rendern genau wie zuvor.
- Der Upstream-Merge löste zwei Konflikte:
docs/SUPPORTED_JOB_BOARDS.md(beide telegram-Zeilen erhalten) undtest-all.mjs(der walkTree-basiertecopyDirSyncdes Upstreams subsumiert den handgerollten.git-Guard des Forks für verschachtelte Checkouts; die Importliste ist die Vereinigung).
[1.251.1] — 2026-10-12
Der Spezialisierungsfilter matcht nur noch die DISTINCTIVEN Wörter der Rolle — Seniorität und generische Berufs-substantive matchen kein Posting mehr allein — und remote-qa trennt Form von Sprache, damit ein Drift A–G nie rot markiert.
Behoben
- Der Spezialisierungsfilter wirft Seniorität und generische Berufs-substantive aus seinem Matcher: «Senior PHP Developer» matchte «Senior Java Developer» über das gemeinsame Senior/Developer. Der Matcher behält nur unterscheidende Tokens (PHP, Go, Platform, SRE, Backend…); eine Rolle ohne unterscheidende Tokens («Software Engineer») fällt auf den Ganzsatz-Match zurück. Wortgrenzen bleiben: «Go» matcht nie «Golang» oder «Google».
- remote-qa trennt Form von Sprache: Das A–G-Flag wird nur anhand von Block/SCORE_SUMMARY/looping-Codes bewertet; eine
report not in <locale>-Drift geht in ein eigenes Flag und eine Warnings-Spalte (die Tabelle gewinnt die fehlende 4. Spalte; die Codes werden pro Zeile gespeichert). - Die Karten-Leiste ist ein echtes 50/50 bei 390 px (die 58/42-Aufteilung ließ die Suche bei 124 px und schnitt ihren Platzhalter ab), und die tote CSS-Regel
.job-map-job-selectist entfernt. - CodeQL 552 — die ungenutzte Variable
OVERLAYSim design-qa-Test entfernt.
Hinweise
- Meilen weiterhin vom Eigentümer verschoben. Der ta-Sprachdrift bleibt als DOKUMENTIerte Grenze des hermes-Backends (2–26 % Prosa über Läufe; A–G hält): remote-qa meldet sie als erwarteten Fund. Die generische Token-Liste ist bewusst minimal — Erweitern kostet eine Zeile pro Sprache.
[1.251.0] — 2026-10-12
Der Select der Karte filtert jetzt nach der SPEZIALISIERUNG aus den Einstellungen (config/profile.yml → target_roles.primary): «Product Manager» oder «Backend Engineer (Go / PHP)» wählen, und die Karte behält nur die Postings, deren Titel die Wörter dieser Rolle trägt. Der v1.250.0-Joblisten-Locator ist entfernt — das falsche Werkzeug (Korrektur des Eigentümers).
Hinzugefügt
- Spezialisierungsfilter: der Select listet die Spezialisierungen aus den Einstellungen (target_roles.primary); eine Wahl behält nur die Postings, deren Titel einen der unterscheidenden Tokens der Rolle trägt (Wortgrenzen, groß-/kleinschreibungslos — «Go» matcht nie «Google»); Reset stellt alles wieder her.
- Der v1.250.0-Joblisten-Locator ist entfernt — der Ersatz: Spezialisierungsfilter, keine Jobliste (Korrektur des Eigentümers).
Geändert
- Two mobile-map height specs: the floor moves 600 -> 540 px (the toolbar takes ~90 px of the viewport).
Hinweise
- Meilen weiterhin vom Eigentümer verschoben. Das Matching ist bewusst NUR Titel mit den Wörtern der Einstellungen: Firmen oder Beschreibungen würden fremde Postings in die Spezialisierung ziehen. Ein Posting außerhalb aller konfigurierten Rollen bleibt unter «All» sichtbar.
[1.250.0] — 2026-10-12
Die Karten Seite wird bedienbar: ein Stellen-Locator über der Karte fliegt zu jedem sichtbaren Job und öffnet seine Vorschaukarte (auch am Desktop), und eine Textsuche grenzt die Karte nach Firma oder Titel ein — beides übersteht ein Neuladen.
Hinzugefügt
- Stellen-Locator: ein Select über der Karte listet die sichtbaren Jobs nach Firma+Titel; die Wahl fliegt zum Kreis und öffnet die Vorschau — auch am Desktop.
- Textsuche: grenzt die Karte nach Firma+Titel ein, persisted in map-prefs, Reset löscht.
- Der FAB verdeckt die Attribution auf
#/mapin allen Breiten nicht mehr.
Behoben
- Zwei Mobile-Höhen-Specs: die Untergrenze geht auf 540 px (die Leiste nimmt ~90 px des Viewports).
Hinweise
- Meilen verschiebt der Eigentümer auf das nächste Release. Die 4 neuen i18n-Schlüssel kommen in alle 18 Wörterbücher und der zusammengesetzte Snapshot wird regeneriert. Der Locator öffnet immer die Vorschau (Wegnavigieren würde seinen Zweck zunichtemachen); der Kreis wird über den __job-Rückverweis gefunden — NICHT _point, das L.Circle bereits besitzt.
[1.249.0] — 2026-10-11
Die Stellenkarte ist jetzt relevanzorientiert: Eine ruhige Türkis-bis-Grau-Skala hebt die besten Stellen hervor, Bewertungs- und Fahrzeitfilter bleiben gespeichert, das Handy erhält eine Karte in voller nutzbarer Höhe mit Vorschau beim Antippen, und der Tracker teilt Fahrzeitfilter und -sortierung.
[1.248.8] — 2026-10-11
Parent-Parität: Fork und web-ui synchronisieren sich mit dem 115-Commit-Delta des Upstreams — eine neue PageUp-Quelle, Tracker-URL-Zellen werden als Markdown-Links geparst, der Assessment-State — plus die ta/ja-Spracharbeit: Looper-Berichte werden abgeschnitten und benannt, das Überschriftenskelett hält A–G stabil, und die Skript-Quote liest nur Prosa.
Hinzugefügt
Behoben
- Neue PageUp-Quelle (105 EN-Adapter / 110 Quellen); Tracker-URL-Zellen werden als Markdown-Links geparst (der href ist der Dedup-Schlüssel); der Assessment-State kommt über states.yml.
- Ein Looper-Bericht wird abgeschnitten und benannt (
report looping (block B repeated)), Überschriftenskelett## A) … ## G)für Nicht-Englisch. - Die Skript-Quote liest nur Prosa (Tabellen, SCORE_SUMMARY, Code und URLs ausgeschlossen) — ein japanischer Bericht mit lateinischen Tabellen schwankt nicht mehr.
- /api/evaluate warnt
report not in <locale>, und remote-qa druckt die Codes als Spalte.
-
[1.248.7] — 2026-10-11
Die Rolle aus dem Berichtskopf liest jetzt die tatsächliche Parent-Konvention — # Evaluation: {Company} — {Role} — statt # <Rolle> — <Firma> anzunehmen, sodass der Tracker nicht mehr «Evaluation:
Behoben
- Das lokalisierte Label wird zuerst entfernt (
Evaluation:,Оценка вакансии:und 16 weitere Varianten); nach dem LabelCompany — Role→ die Rolle ist die rechte Seite; ohne LabelRole — Company→ die linke; der Teil, der die Firma nennt, entscheidet; die Rolle ist nie die Firma noch ein Labelwort — der Fallback ist die gesäuberte Post-Zeile. - cleanup-plan summiert die N der Zeilen (
— N dirty-URL line(s)), zählt keine Dateien — eine Datei mit 5 dreckigen URLs ergab 1, nicht 5. - Ein Bericht in der falschen Sprache warnt: der Prompt öffnet UND schließt mit der verpflichtenden Sprachanweisung (der ganze Bericht, Tabellen eingeschlossen), und die Pipeline prüft die Sprache des gespeicherten Berichts — die Drift erscheint als Warnung
report not in <locale>. - Kein Modell-Geplänkel vor dem H1 — der Bericht beginnt bei seiner Überschriftenzeile.
Hinweise
- Nicht portiert und unangetastet (in der v1.248.6-Regression verifiziert): der Embed-Fetch, die Zeilenstruktur des Posts («Компания : X»), die «## Rejected»-Deduplikation, die Domain-Root-Ablehnung vor dem Fetch, die Recon/Link-Check-Ehrlichkeit und die Cleanup-Modi. Die Sprachprüfung ist eine Warnung — der Bericht (Inhalt bereits validiert A–G + Score) wird gespeichert; der Betreiber sieht die Drift und kann die Bewertung wiederholen.
[1.248.6] — 2026-10-10
Nach der Regression poliert: Ein echter Telegram-Post behält seine Zeilenstruktur (das Label «Компания : X» und die Rollenzeile werden dort gefunden, wo sie wirklich sind), die Tracker-Rolle kommt aus dem Berichtskopf statt aus einer rohen Post-Zeile, und die Betriebsoberflächen (recon, cleanup-plan, link-check) melden das, was sie zu melden behaupten.
Behoben
- Ein t.me-Post klebt nicht mehr in einer Zeile:
<br>wird vor dem Entfernen der Tags zum Zeilenumbruch, numerische HTML-Entities werden dekodiert; das Firmenlabel-Muster akzeptiert die Form mit Leerzeichen vor dem Doppelpunkt. - Die Tracker-Rolle kommt aus dem Berichtskopf (H1
# <Rolle> — <Firma>); die Fallback-Zeile wird gesäubert — keine Hashtags, keine Emoji, ≤80 Zeichen. ## Rejecteddupliziert sich nicht mehr: der Abschnitt wird einmal erzeugt; jede Ablehnung fügt ihre Zeile darin an.- Eine Domain-Root wird vor der Anfrage abgelehnt — eine Junk-URL kostet nichts mehr.
- Recon berechnet das Vertragssignal, statt es zu behaupten (
honours rejected:prüftmarkPipelineRejectedim deployten Code); der Mark-Zähler druckt «0» nicht mehr doppelt. - cleanup-plan meldet Zahlen je Kategorie (zu verschiebende t-role, dirty-URL-Zeilen, test-row-Zeilen) statt eines einzelnen
grep -c, der die eigene Erfolgszeile mitzählte. - link-check behandelt github.com-5xx als unbestätigt — hinter der Bot-Mauer, nicht als kaputte Links.
Hinweise
- Nicht portiert und unangetastet (in der v1.248.5-Regression verifiziert): der Embed-Fetch selbst, der Pipeline-Skip-Vertrag, die _setLookup-Offline-Tests, die uk-Ordinal-Akzeptanz, Wortgrenzen im Rollenfilter und der cleanup-apply-Modus. Die kategoriebezogene Zählung im cleanup-plan liest die Ausgabe-Labels des Bereinigungsskripts selbst — eine Umbenennung würde die Zähler stillschweigend brechen.
[1.248.5] — 2026-10-10
Nach der Regression gehärtet: Einzel-Post-Einträge von t.me holen jetzt den Posttext selbst (Embed-Form), ein abgelehnter Eintrag verlässt serverseitig den Pool des Eval-Timers, und die native Blocknummerierung eines ukrainischen Berichts wird akzeptiert — dazu ist ein Test, der heimlich das Live-Netzwerk nutzte, für immer offline.
Behoben
- t.me-Einträge werden endlich evaluiert: Eine Einzel-Post-Seite liefert im reinen HTML nur den Kanal-Header (~0,2 KB — «View in Telegram» und sonst nichts), das Modell erhielt eine leere Beschreibung, und jeder Telegram-Eintrag starb bei der Evaluierung. Der Fetch schreibt
t.me/<kanal>/<id>(und die Feed-Formt.me/s/…) jetzt in die Embed-Form um (?embed=1&mode=tme) — gleicher Host, SSRF-Validierung unverändert — und liest den Post aus.tgme_widget_message_text(Fallback:og:description). Kein Widget-Text → Ablehnung im Fetch-Schritt (telegram post has no text,rejected: true), bevor LLM-Tokens verbrannt werden. - Der Eval-Timer zahlt nicht mehr erneut für abgelehnte Einträge (serverseitiger Vertrag): Ein abgelehnter Eintrag wird jetzt in
data/pipeline.mdmarkiert — seine Zeile verlässt den URL-Zaun und landet in einem## Rejected-Abschnitt mit kurzem Grund (idempotent, nie dupliziert). Der Timer nimmt nacktes URL aus dem Zaun, also wird ein markierter Eintrag natürlich übersprungen — ohne Abhängigkeit von den Eingeweiden des Timer-Skripts. Recon drucktpipeline rejected marks: N+honours rejected: yes (server-side)statt das Skript zu durchsuchen. - Ukrainische Berichte bestehen die A–G-Validierung wieder: Ein uk-Modell nummeriert die Blöcke nach der Reihenfolge seines eigenen Alphabets (А Б В Г Д Є Ж), und der Lookalike-Durchgang zählte nur А/В/Е — genau die Blöcke C, D, F, G fehlten in der Produktion (2/2 Reproduktionen). Der Validator akzeptiert jetzt die ukrainische Ordinalsequenz, wenn die Überschriften der Reihe nach erscheinen (dieselbe Disziplin wie Abjad für ar und Devanagari für hi), und der Evaluierungs-Prompt verlangt für jede Locale jetzt explizit lateinische
A–G. - Das Rollen-Keyword-Tor springt bei Wörtern an, nicht bei Teilzeichenketten:
linkedin.com/- undx.com/-Roots kamen durchs Tor, weillead/itim UI-Text der Seite lebten. Keywords matchen jetzt an Unicode-Wortgrenzen, und eine Domain-Root (URL ohne Pfad) wird vor dem LLM-Aufruf abgelehnt. - Ein Unit-Test braucht das Live-Netzwerk nicht mehr: Der No-Hints-Test (
tests/auto-pipeline-jd-gate.test.mjs) ersetzte den HTTP-Transport, führte aber weiterhin eine echte DNS-Abfrage durch — in CI nur grün, weil der Runner Konnektivität hatte. safe-fetch erhielt einen_setLookup()-Hook (der Resolver selbst ist in Tests austauschbar; Produktionscode überschreibt ihn nie), und der Test löst offline auf.
Hinzugefügt
cleanup-plan/cleanup-applyWorkflow-Modi in deploy.yml: Die QA-Müllbereinigung in der Produktion (post-qa-cleanup) läuft serverseitig ohne SSH vom Laptop. Das Log trägt nur Zahlen — keine Dateinamen oder Zeilenvorschauen, denn das Log ist öffentlich und Dateinamen enthalten Firmennamen. Der Verify-Schritt wird für Cleanup-Modi übersprungen.
Hinweise
- Nicht portiert: der Wortgrenzen-Standort-Compiler und die USPS-Staaten-Tabelle des Parents (dokumentiert seit v1.248.1). Der Überspring-Vertrag für abgelehnte Einträge lebt serverseitig (Markierung in data/pipeline.md), nicht im Timer-Skript — dieses liegt außerhalb des Repos und wird bewusst nicht geändert.
[1.248.4] — 2026-10-10
Die Rollen-Auflösungskette für Telegram-Einträge: Ein kyrillischer Post mit echter Vakanz liefert jetzt einen benannten Bericht statt einer abgelehnten unknown-role-Datei — und der LLM-Aufruf feuert nur für Einträge, die eine Bewertung wert sind.
Behoben
- Die Rollen-Kette funktioniert für kyrillische Posts: Die Schlüsselwortliste bekam разработчик/программист/инженер/аналитик/менеджер/руководитель/тимлид/лид/дизайнер/тестировщик/QA/архитектор/специалист/директор/DevOps/SRE (die alte war nur EN: 0 von 40 echten Posts bekamen eine Rolle), der Berichtskopf wird konsultiert (
parseReportHeader— das Modell schreibt die Rolle in die H1), und die erste sinnvolle Zeile des Posts ist der letzte Ausweg. - Das Identitäts-Gate zog VOR den LLM-Aufruf: Ein Eintrag ohne Firma und ohne Rollen-Hinweis scheitert jetzt im Fetch-Schritt (
rejected: true) — keine Tokens für eine Bewertung, die ohnehin abgelehnt worden wäre. t.me/s/<Kanal>/<id>benennt die Firma nicht mehr „S“ — das Vorschau-Segment wird übersprungen; beide Linkformen lösen auf den Kanal auf.- „Компания: X“-Selbstetiketten in Telegram-Posts werden honoured (und „…в компании X“ mitten im Satz für Messenger-Hosts).
Hinzugefügt
- Auto-Pipeline-Fehler tragen
rejected: trueim SSE-Fehler — der serverseitige Eval-Timer nutzt das Flag, um den Pipeline-Eintrag zu überspringen statt ihn alle 2 Stunden zu wiederholen.
Hinweise
- Nicht portiert: der Parent-Compiler mit Wortgrenzen und die USPS-Tabelle (dokumentiert seit v1.248.1).
- Der README-„Translations“-Block wird aus einer kanonischen Liste generiert, mit einem Paritätstest in CI (17 Links je Datei, der eigene nie verlinkt) — ar/pl/uk bekamen den Block; die
|/·-Drift ist beseitigt.
[1.248.3] — 2026-10-10
Fortsetzung der Regressionsrunde: Der Eval-Timer kann namenlosen Telegram-Müll unter keinen Umständen mehr als Bericht ablegen, die QA-Bereinigung löscht keine echten Telegram-Vakanzen mehr, die Karten-Attribution ist wieder sichtbar, und die Dashboard-Chips lesen ≥ 4.5:1.
Behoben
- Das Auto-Pipeline validiert VOR dem Speichern: Ein Bericht ohne 0–5-Score oder ohne A–G-Blöcke beendet den Lauf im Evaluate-Schritt (
rejected: truein der SSE-Nutzlast) — keine Berichtsdatei, keine Tracker-Zeile. Die t.me-Einträge, die die Längen-Schwelle von v1.248.2 nahmen, erzeugten nach jener Version zwei weitere Müllberichte; jetzt werden namenlose Einträge mitcompany/role not identifiableabgelehnt. guessCompanyRoleerfindet keine Firma aus Messenger-Hosts mehr —t.me,telegram.me,vk.com,linkedin.com& Co. stehen in EMPTY_DOMAINS: Ein Telegram-Post fällt auf den Kanalnamen zurück, und ein namenloser Eintrag wird statt alsunknown-roleabgelegt abgelehnt.post-qa-cleanupliest zuerst den Kopf-Score: Das Auto-Pipeline entfernt den SCORE_SUMMARY-Block vor dem Speichern, damit las die alte Nur-Summary-Prüfung jede gespeicherte Telegram-Vakanz als Müll — in der Produktion tragen 8 von 41 einen Kopf-Score und--applyhätte sie gelöscht. Der Kopf-Score (viaparseReportHeader) ist jetzt das Primärsignal; der Block ist der Fallback. Der Dry-Run druckt den Grund je Datei.- Die Leaflet-Attribution ist auf #/map sichtbar — der Docs-FAB verdeckte sie (OSM-Lizenzverstoß); die Ecke ist um 96px angehoben.
- Dashboard-Chips erreichen 4.5:1 —
dash-chip-Labels und die--manual-Variante auf--foggy-strong(vorher 4.31:1).
Hinzugefügt
- Auto-Pipeline-Fehler tragen
rejected: trueim SSE-Fehler — der serverseitige Eval-Timer nutzt das Flag, um den Pipeline-Eintrag zu überspringen statt ihn alle 2 Stunden zu wiederholen.
Hinweise
- Die ar/ja-Abweichung wiederholte sich nicht beim erneuten Lauf (siehe v1.248.2).
- Die Produktionsbereinigung wartet weiterhin auf ein explizites Ja (CAR-61c); mit diesem Fix kann die Bereinigung die echten Telegram-Vakanzen nicht mehr löschen.
[1.248.2] — 2026-10-10
Regressionsrunde: Der Eval-Timer verwandelt Pipeline-Müll nicht mehr in „Last evaluation“, das SCORE-Parsing überlebt reale Formate, und der Tracker spricht endlich Ihre Sprache.
Behoben
- Leere-JD-Berichte werden nie mehr geschrieben. Die JD-Gate des Auto-Pipelines stieg vor dem LLM-Aufruf von 50 auf 200 Zeichen — Platzhalterseiten (
example.com/qa-v167-…) enden als SSE-Fehler + Konsolenwarnung statt als…-t-role-<ts>.md-Bericht, den das Dashboard als „Last evaluation“ zeigte (42 solche Dateien in der Produktion). - Das SCORE_SUMMARY-Parsing überlebt reale Formate: „4,2/5“, „4.2 / 5“, „4.2“, „SCORE :“ validieren; ein Komma mit 1–2 Ziffern ist dezimal, mit 3 ist es Tausender (der alte Code las „4,200“ als 4.2 und passierte die 0–5-Gate). Ein Fehlschlag loggt nur die
SCORE:-Zeile — nie den Berichtsinhalt (CI-Logs sind öffentlich). - Tracker-Status werden übersetzt: Stage-Tabs und Badges zeigen lokalisierte Labels in allen 18 Sprachen; der kanonische Wert in Daten, Filtern und URLs bleibt unberührt.
- Ehrliche ETA: „⏱ ~30s“ wurde zu „~2–4 Min“ in evaluate/advisor/Marktbericht (die echte Spanne: 86–285 s), und der Evaluate-Untertitel liest A–G.
- Barrierefreiheit: Der API-Keys-Zähler in config und der Pipeline-Zähler klarieren jetzt 4.5:1 (vorher 4.31:1); ein Facetten-Chip mit leerem Wert wird nicht mehr ohne zugänglichen Namen gerendert.
- UI-Politur: Der Delete-Knopf gespeicherter Suchen wirkt nur bei gewählter Suche destruktiv (sonst disabled); die PROFIL-E-Mail-Karte schneidet bei 1440 px nicht mehr ab; die Live-Evals-Pille bekam Icon/Text-Abstand inklusive RTL; der Docs-FAB überdeckt die Leaflet-Attribution nicht mehr; das Help-„?“ bricht nicht mehr unter den Stats-Tabs um; usage bekam Luft zwischen Untertitel und Range-Tabs; health sagt jetzt career-ops-ui.
Hinzugefügt
scripts/post-qa-cleanup.mjs: standardmäßig Dry-Run,--applymit automatischen Backups (*.bak-<ts>, Berichte wandern nachqa/cleanup-backup-<ts>/): ZZ-QA-TEST-Zeilen im Tracker,*-t-role-*-Berichte ohne gültigen SCORE,example.com-Pipeline-Einträge.
Hinweise
- Der ar/ja-Drift der Regressionsrunde wiederholte sich nicht beim erneuten Lauf (beide: lang ✓ + A–G ✓) — der ta-Eintrag im Language-Check und das gehärtete SCORE-Parsing gehen als Defense-in-Depth an Bord.
- Die Produktionsbereinigung wartet auf ein explizites Ja des Nutzers: zuerst der Dry-Run-Plan, dann
--apply.
[1.248.1] — 2026-10-09
location_filter erhält die Parent-Tiers always_allow und block_hard (CAR-60) — ein Mehrfach-Länder-String überlebt eine Geschwister-Sperrliste, und eine explizite harte Sperre kann nicht mehr gerettet werden.
Behoben
always_allowschlägtblock: mitblock: [romania, poland, croatia]undalways_allow: [united kingdom]passiert die workable-Zusammenführung „London, United Kingdom · Romania · Poland · Croatia“ — die Heimatregion ist eine Option (der Workable-Consumer-Fall #4806, jetzt in Parent-Form).block_hardschlägtalways_allow: länderbezogene Begriffe, die nie durch einen always_allow-Treffer anderswo im String gerettet werden dürfen.strict: truezähltblock_hardals einschränkenden Tier; derlocation_filter-Abschnitt in docs/help dokumentiert beide Tiers ×18.
Hinweise
- Bewusst diesmal nicht portiert: der Parent-Compiler für ortsbezogene Schlüssel mit Wortgrenzen und seine Tabelle der US-Bundesstaaten (web-ui behält Substring-Matching; die Tabelle dient US-zentrierten Konfigurationen — unter CAR-60 ablegen, falls nötig). Die Übergangs-Allow-List-Form des Workable-Consumer-Tests bleibt als zweites Urteil neben der neuen Parent-Form.
[1.248.0] — 2026-10-09
Parent-Parität: fünf Upstream-Fixes für Provider/Liveness portiert — Boards, die ihre API umgezogen haben, arbeiten weiter, und ein Closing-Banner in eigener Zeile wird nicht mehr übersehen.
Behoben
- Gupy folgt der API zu
portal.gupy.iound sendet die Filternamen, die die API wirklich liest (workplaceType/type) — die alten Pluralnamen wurden stillschweigend ignoriert;countrywird nicht mehr gesendet (ein konfiguriertes Land erzeugt eine Warnung mit dem Eintragsnamen). Vertrauliche Arbeitgeber fallen bei exakter Label-Übereinstimmung („Confidencial Seguros“ überlebt), undmax_pagesist auf 100 begrenzt — die API antwortet mit 400 ab Offset 10.000, und die Truncation-Warnung rät jetzt, Keywords einzugrenzen. - Workable behält alle Länder einer Mehrfach-Länder-Vakanz: Geschwister unter derselben Job-URL falten in eine Zeile („London, United Kingdom · Romania · Poland · Croatia“) statt dass das erste Land gewinnt.
- Liveness: Ein Banner in eigener Zeile ist nicht Teil der Zeile darüber — „Sign in if you want to apply“ / „Get notified when this position is filled“ unter einer Vakanz wird als Closing-Klausel gelesen, während ein echtes „until filled“ über Zeilen hinweg weiterhin aktiv liest.
- Radancy liest ein Geschwister-
job-location-Element im Legacy-Card-Markup (Barclays-Familien-Layouts legen den Ort nach</a>) — Zeilen ohne eigenen Ort bleiben leer statt den des Nachbarn zu leihen. - SmartRecruiters-Fallback-Links behalten den konfigurierten Slug — eine Vakanz ohne vertrauenswürdige
refsynthetisiert ihre öffentliche URL aus dem Tenant-Slug, nie aus dem Anzeigenamen.
Hinzugefügt
- Keine Ports, dokumentiert: der Upstream-
_http-Proxy-fetchImpl-Fix (web-ui hat keinen Proxy-Zweig — ein Port würde die Identitätsprüfung des DNS-Rebinding-Guards aushebeln und eine undici-Abhängigkeit hinzufügen, der das Repo bewusst ausweicht); Upstream-verify-ats(+226 Zeilen) undcv-facts(web-ui implementiert eigenes ATS-Scoring und CV-Parsing); das Parent-Dashboard Go TUI und CLI-Skripte; eine Teamtailor-Import-Umordnung (kosmetisch).
Hinweise
- Bekannte Lücke als Follow-up: der
always_allow-Tier des Parent-Standortfilters (der Workable-Consumer-Test überlebt dank ihm einen UK-Block) fehlt in web-ui; der Port drückt dasselbe Verbraucherurteil über einen Allow-List-Test aus. Als separate Änderung eingetragen.
[1.247.0] — 2026-10-09
Der Schwanz des Design-Sweeps CAR-49…58 ist abgeschlossen — alle übrigen Minor/Polish-Funde plus die dunkle Karte, die das vorige Release bewusst zurückstellte.
Behoben
- Die Karte rendert ein echtes Dunkelthema. Zoom/Ebenen/Attribution wurden in v1.246.0 dunkel; jetzt folgen die Kacheln: Der Dunkelmodus zeichnet den OSM-Layer mit CSS-Inversion (ohne Vendor-Keys), ein keyed Carto-dark-Preset ist über
MAP_TILE_DARK_URLverfügbar (dessen Host ist in der CSPimg-srcerlaubt). Der Layer wird bei Themenwechsel live neu montiert. - Gelbe Callouts haben dunkle Varianten (
config,batch, Hero-Warnung) über die Token-Mechanik von v1.246.0 — und nebenbei eine Regression erwischt:.callout aüberdeckte.btn-primary; jetzt auf:not(.btn)begrenzt. - Die URL in der Apply-Info-Karte bricht nicht mehr mitten im Wort.
- CV-Kopfschaltflächen nutzen ein einziges Icon-System (Text; der 📁/📄/💾-Emoji-Mix ist weg).
- Tracker-Status-Tabs in einer Schreibweise — Sentence case, mit lokalen Besonderheiten (CJK/ar/hi/ta ohne Genusformen).
- Topbar-Icons sind ein einzelner Stroke-SVG-Satz — der 🔔/🌙/🩺-Mix ist weg; der Themenknopf zeichnet Sonne/Mond per CSS.
- Die Pipeline-Karte des Dashboards verliert ihre rosa Hervorhebung (Ring nur bei Hover/Focus).
- Gespeicherte Suche löschen ist sichtbar destruktiv (
.btn-danger, kein neutraler Ghost). - Die Pipeline-Vorschau hat einen echten Leerzustand (Titel + Hinweis + CTA, Tracker-Muster).
- Zeit/Kosten-Meta ist ein Muster:
UI.pageMetazeichnet ⏱ + Kosten in eine Zeile unter den View-Titel (evaluate/deep/auto/mode-page/orientation). - Auto-pipeline verliert sein ✨ — die einzige dekorierte h1 kehrt an die gemeinsame linke Kante zurück.
- Batch-Docs-URL ist ein Inline-Link.
- interview-digest / orientation haben gestaltete Leerzustände (Titel + Hinweis + CTA).
- Career-Plan-Export ist ohne Plan deaktiviert, Aktionen in einer Reihe.
- stats: Die doppelte Überschrift „Market Report“ ist weg (Anmerkung: getestet), und der Regions-Platzhalter passt (kürzer ×18, feste Breite).
- Config leakt keine absoluten Pfade mehr — das serverseitige
displayPath()zeigt~/…oder den nackten Dateinamen. - Der LLM_PROVIDER-Helper sind drei kurze Zeilen (×18 +
pre-line), keine 150-Zeichen-Mono-Wand. - Die PROFIL-E-Mail-Karte hält Adressen in einer Zeile (Ellipsis; Umbruch nur ≤480px).
- Health-Text bricht keine Wörter mehr („Run buttons“ bleibt ganz; overflow-wrap anywhere).
- Inline-Code im Help überlebt RTL (LTR-isoliert, nowrap, scrollbar).
- Die Hero-Pille „Live evals“ kollidiert nicht mehr in ru/ar (Flex-Wrap, nicht schrumpfende Icons).
Hinzugefügt
- Dunkelkachel-Preset (
MAP_TILE_DARK_URL+ Attribution) im Code dokumentiert; die CSP-img-src-Erweiterung deckt den Carto-Host dafür ab.
Hinweise
- Der Dunkel-Kachel-Standard ist eine CSS-Inversion von OSM — keyless Carto hat seine Basemaps hinter einen API-Key gesperrt (per Request verifiziert); das keyed Preset ist Opt-in, nicht Standard.
[1.246.0] — 2026-10-09
Alle zehn Top-Funde des Senior-Design-Sweeps CAR-48 sind behoben — der Blocker und alle sieben Majors inklusive.
Behoben
- Apply dunkel: Der Link in der Info-Karte ist wieder sichtbar. Die Karte behielt ihre helle Fläche im Dunkelmodus; der Link ‚Need Playwright? See …‘ lag bei ≈1.0:1. Theme-aware
.callout--info(Surface/Border/Text/Link-Tokens) hebt den dunklen Link auf ≈7.3:1. - RTL dreht numerische Kompositionen nicht mehr. Score-Pills (‚1.5 / 5‘), Report-Bänder (‚≥ 4.5‘, ‚< 3.5‘) und das Dashboard-‚/ 5.0‘ bekommen Richtungsisolation (LTR-Spans); das ar-Wörterbuch formuliert Bereiche in Worten, wo Prosa es verlangt.
- CV-Markdown bleibt LTR. Textarea und Vorschau rendern in Arabisch mit
dir="ltr"; der Rahmen drumherum bleibt RTL. - Die DATE-Spalte der Reports bricht nie wieder ein ISO-Datum (nowrap + Tabellenziffern).
- Die Sidebar endet über dem USAGE-HUD (Scrollport-Reserve + Fade) — der letzte Nav-Punkt scrollt nicht mehr unter das Panel; für RTL gespiegelt.
- Mobiles Scan @390: ‚Save search‘ vollständig sichtbar — die Steuerreihe bricht um und schrumpft, statt am Rand abgeschnitten zu werden.
- Karten-Steuerungen folgen dem Dunkelthema (Zoom, Ebenen, Attribution). Die Kacheln bleiben bewusst, wie sie sind: Ihr Ursprung ist serverseitig per CSP
img-srcfixiert. - Der Chat-FAB überdeckt keinen interaktiven Inhalt mehr — das Scroll-Padding räumt ihn frei (Disable-Buttons in Portals, Score-Pill in Reports, Two-Pager-Eingabe, Leaflet-Attribution).
- Das Aktivitätslog spricht die UI-Sprache: 22 Action-Slugs in 18 Sprachen lokalisiert (Fallback auf die rohe ID für Unbekannte), Filter-Chips übersetzt; die TARGET-Spalte bleibt — auto-pipeline füllt sie.
- Assessments-Formulare bekommen echte Labels (Sentence case,
htmlFor/id-Bindung) statt reiner Placeholder.
Hinweise
- Nicht in diesem Release: dunkle Kartenkacheln (erfordert eine CSP-
img-src-Erweiterung — serverseitig, bewusst nicht clientseitig); dunkle Varianten der gelben Callouts (config,batch) — die Token-Mechanik steht für ein Follow-up bereit.
[1.245.0] — 2026-10-09
Tamil (தமிழ்) kommt als 18. UI-Sprache hinzu — die erste neue Sprache nach der 17-Sprachen-Welle, mit voller Schlüsselparität.
Hinzugefügt
- Tamil (
ta): 1449/1449 Schlüssel mit voller Parität zuen, keine verlorenen Platzhalter, hi-Stil Indic-Tech-Konventionen (Provider, Modellnamen, Pfade und Befehle bleiben lateinisch).detect()akzeptiertta,ta-IN,ta-LK; Tamil ist LTR (per Test fixiert). - Vollständiges docs/help/ta.md (33 H2 / 125 H3, Quellzähler gegen die Live-Registry geprüft, Hermes-Canary-Anker, wörtlicher YAML), dazu README.ta.md- und CHANGELOG.ta.md-Spiegel.
- Enumerations-Gates 17 → 18: LANGS, detect-Sonden, i18n-Audit, Changelog-Parität, RTL-Wächter, CI-
langs— zieht damit auchhinach, das bisher in der CI-Liste fehlte.
Hinweise
- Die cvstart.org-Landingpage bleibt vorerst bei 17 Sprachen — die Übersetzung ist eine eigene Aufgabe; der Site-Content-Sync liefert docs/help/ta.md und das ta-Changelog bereits aus.
report-header.jsREPORT_LABELS bleibt bei 17 — das Parsen tamilischer Berichte kommt mit der tamilischen Berichtsgenerierung im Parent-Repo.
[1.244.2] — 2026-10-08
Der Scan-Seiten-Polish: Launcher, Statusleiste, Reposts-Panel und Filter sind als konsistenter Chrome neu gestaltet — und die Ergebnistabelle kann nicht mehr durch Board-Daten gestreckt werden.
Behoben
- Die Gehaltszellen zeigen nur den Geldbereich. Manche Boards packen den ganzen Benefits-Text ins Gehaltsfeld — die sichtbare Zelle zeigt jetzt nur den Geldteil und verschiebt den Text in den Tooltip (vorher: eine 6-zeilige Zelle, die jede Reihe streckte).
- Reihen mit leerem Titel zeigen die Firma (danach einen neutralen Strich) statt einer riesigen leeren Zelle.
- Das Seniority-Badge bricht nie um in seiner festen Spaltenbreite.
- Nicht bewertete Reihen zeigen ein gedämpftes «◎ —» mit Tooltip, warum der Two-Pager-Vergleich keine Treffer fand (vorher: nichts, was neben bewerteten Reihen kaputt wirkte).
Hinzugefügt
- Chrome-Redesign: Launcher-Karte (ausgerichtete Steuerungsreihe, dominanter Primary-Button), Terminal-Statusleiste mit Statuspunkt (idle/running/done/error) reduced-motion-sicher, Reposts-Panel mit Höhenbegrenzung und Sticky-Header (ein 1,560-Cluster-Datensatz renderierte ein 155,000-px-Panel), und der Filterblock als gleichmäßiges responsives Raster mit ausgerichtetem Footer.
Anmerkungen
- Alles andere in #/scan ist identisch mit v1.244.1 — nur der Chrome-Polish und die vier Korrekturen oben haben sich geändert. Tests 5045 → 5068 unit.
[1.244.1] — 2026-10-08
Behoben
- Scan icons announce real values. The boost/fit/score icons’ accessible names leaked the raw dict templates (“Fit: {band}”, “Boosted by {by}”) —
{band}/{by}/{score}are now substituted (band word, booster keyword, numeric score). [HIGH a11y, caught live in the v1.244.0 regression round] - Title pipes split correctly. A 4-segment title like «C++ | Rust | Go Developer | Onsite» no longer lifts “Go Developer” out as a “country” — the split demands the segment resolves to a real country (conservative
Countries.detectCountry). - Meta dates render for every row.
daysSinceaccepted date-only ISO, but the API serves full timestamps for ~83% of dated rows — those rows showed no date segment.
Anmerkungen
- Alles andere in #/scan ist identisch mit v1.244.0 — nur die drei Korrekturen oben haben sich geändert.
- Bewusst nicht getan: Filterzustand im URL-Hash, Tastatur-Zeilennavigation und das Parsen von
5 lakhs INR(bestehende Nachzügler).
[1.244.0] — 2026-10-08
Die Scan-Ergebnisse lesen sich wie eine Liste, nicht wie eine Wand: eine Stelle = zwei Zeilen, die Signale sind Icons, und ein 700-Zeilen-Scan ist eine paginierte Tabelle statt einer 37,000-px-Seite.
Behoben
- Zeilenanatomie. Jede Stelle rendert als begrenzte Zwei-Zeilen-Zeile: Zeile 1 der Titel, Zeile 2
company · location · source · date · work-type. Das Boost-Badge, das Titel-Fit-Band und der Fit-Score wurden zu Icons mit lokalisierten zugänglichen Namen und Tooltips (Screenreader lesen „Passung: stark“ / „Fit: strong“, keine Wortwand); die Trust- und Umzugs-Badges wanderten in dieselbe Leiste. Seniorität und Gehalt bleiben zusätzliche Spalten, nur auf dem Desktop sichtbar. - Titelhygiene. Ein abschließendes
| Germany | Remote-Segment wird aus dem Titel in die Meta-Zeile ausgespalten — Unicode-bewusst, und nur wenn der Schwanz ein echtes Arbeitsform-Kennzeichen ist und das mittlere Segment nach einem Ort aussieht (legitime Pipes wie „C++ | Rust | Go Developer“ werden nie geteilt). - Kein horizontales Ausufern mehr. Festes Tabellen-Layout,
min-width: 0, einzeiliger Titel/Meta mit Auslassungspunkten und dem vollen Text im Tooltip: kein Seitenüberlauf und kein internes Tabellen-Scrollen bei 1440 px und 390 px (die alte Seite lief bei Telefonbreite um 675 px über). - Paginierung. Ergebnisse rendern 50 pro Seite (25/50/100/200 wählbar) — ein 700-Zeilen-Scan ist jetzt ~4,600 px hoch statt ~37,000 px, und das DOM hält ~1,000 Knoten statt 5,500+.
Hinzugefügt
- Layout-Contract-Tests: Die Zwei-Zeilen-Zeile, die zugänglichen Icon-Namen (en + ru), das Paginierungs-Knotenbudget und der Titel-Fit-Zähl- und Reihenfolge-Guard laufen in der Browser-Suite — das Redesign kann nicht mehr stillschweigend regressieren.
Anmerkungen
- Tests 5036 → 5045 Unit (die Browser-Suite bekam die Layout-Contracts), Coverage-Mittelwert 98.14 % Zeilen / 89.34 % Branch (Untergrenze 96/86).
- Was der Scanner findet, die Scoring-Formeln und
last-scan.jsonsind unberührt — der Titel-Fit bleibt eine Anmerkung. - Nicht in diesem Release: URL-Hash-Filterzustand und Tastatur-Zeilennavigation (Follow-up);
5 lakhs INRparst weiterhin als 5 (vorbestehend).
[1.243.2] — 2026-10-08
Behoben
- Deploybare Version als v1.243.2 geschnitten. Der Git-Tag v1.243.1 wurde nach der Veröffentlichung umgepunktet, und das
git fetch --tagsdes Servers verweigert korrekt das Überschreiben eines vorhandenen Tags — deshalb war v1.243.1 nie deploybar. Dieses Tag trägt den identischen Baum (der Window-Listener-Fix des Modals auf Basis von v1.243.0) plus diese Versionsanhebung. Sonst hat sich nichts geändert; das npm-1.243.1-Tarball enthielt den Fix bereits.
Anmerkungen
- Der App-Code ist identisch mit v1.243.1 — nur das deploybare Tag ist neu (das
git fetch --tagsdes Servers hat die umgepunktete v1.243.1 abgelehnt). - Bewusst nicht getan: das serverseitige Tag gewaltsam zu aktualisieren (veröffentlichte Tags werden nie verschoben).
[1.243.1] — 2026-10-08
Behoben
- Der „Ergebnis erfassen“-Modal schließt jetzt wirklich beim Navigieren. Der v1.243.0-Guard lauschte auf
hashchangeandocument, wo das Ereignis nie feuert — der Fix kam tot an und das Modal blockierte weiter die nächste Ansicht (live in der Produktion von der v1.243.1-Regressionsrunde gefunden). Der Guard lauscht anwindow; der Test dispatcht ebenfalls dorthin und wurde gegen den defekten Code rot geprüft.
[1.243.0] — 2026-10-08
Die Browser-Schicht bewahrt deine Arbeit und spricht deine Sprache: keine verlorenen ungespeicherten Änderungen, keine stillen Fehler, kein stilles Englisch in einer übersetzten Oberfläche, AA-Kontrast überall.
Behoben
- Ungespeicherte Änderungen überleben Neu-Renders. Der CV-Editor hält einen Dirty-Puffer und stellt ihn bei jedem Render wieder her — Abbrechen, ein Sprachwechsel oder die Navigation zum CV Studio fragt jetzt nach, statt zu verwerfen; der Karriereplan bewahrt seinen generierten Text auf dieselbe Weise. Ladefehler in Config/CV Studio/Karriereplan/Memory/Two-Pager zeigen einen Fehler an und deaktivieren Speichern, statt die Datei stillschweigend mit Leerwerten zu überschreiben.
- Das Modal „Ergebnis erfassen“ überlebt die Navigation nicht mehr. Es auf
#/trackerzu öffnen und dann eine andere Ansicht anzuklicken ließ es die ganze Seite blockieren (gefunden in der Zwei-Stand-Regression von v1.242.1) — es schließt jetzt beim Routenwechsel; seine Geschwister in der Scan-Ansicht stoppen ihre SSE-Streams und hängen ihre Listener bei der Navigation ebenfalls ab. - Gehalts- und Keyword-Parsing liest jede Schrift. Keyword-Chips verwenden Unicode-Wortgrenzen (Kyrillisch,
c++,.netfiltern korrekt, statt auf null Zeilen zu filtern); ein gemeinsamer Gehalts-Parser behebt$182.9K - $240K,от 100 000 руб,100 тыс. руб.,50 000 Kč(war 50M USD) und die Bereichssemantik ($100-150K); das Must-have „Deutschland oder Niederlande“ zählt nicht mehr gleichzeitig als erfüllt und als verletzt. - Streams legen sich genau einmal fest. Eine abgebrochene SSE-Verbindung lieferte den Fehler zweimal aus und konnte Generate dauerhaft deaktiviert lassen; jeder Endzustand (done/error/close/throw, eingeschlossen ein getöteter Kindprozess) legt genau einen Pfad fest und aktiviert die Schaltfläche wieder. Die Auto-Pipeline spült ihren letzten gepufferten Frame aus, statt ihn zu verwerfen.
- Privatsphäre und Korrektheit in den kleinen Pfaden. Der Bugreport-Body trägt nicht mehr die bewertete Job-URL oder Report-Slugs; die CV-Privatsphäre-Maskierung erfasst reine
linkedin.com/in/…-Links und Namen in Versalien und verstümmelt EU-Daten nicht mehr; die Länderauflösung nimmt den längsten Treffer und weigert sich zu raten, wenn zwei Länder genannt sind („Cambridge, MA“ nicht mehr → UK); Datei-Downloads behalten kyrillische Dateinamen. - Router und a11y. Der Skip-Link mündet nicht mehr in einen 404 auf
ontent; der aktive Navigationspunkt trägtaria-current="page"; ein<main>/ein<nav>; Escape schließt den mobilen Drawer (der im geschlossenen Zustand jetzt wirklich außerhalb der Tab-Reihenfolge liegt);scroll-padding-tophält den Fokus auch hinter Sticky-Bars sichtbar; das Sticky-Banner überdeckt nicht mehr die Topbar. - Der Kontrast ist in beiden Themes AA — dunkle Toasts waren Weiß-auf-Weiß (1.10:1): Toasts, Buttons, Banner und Links auf
--rauschlösen jetzt über themeninvariante Tokens auf, abgesichert durch berechnete WCAG-Contract-Tests (≥ 4.5:1). - Alles Übrige aus der Runde: evaluate rendert die Truncation-Warnung; memory/config/two-pager verweigern das Speichern nach einem fehlgeschlagenen Laden; scan verwaist seine SSE-Streams nicht mehr; die Scan-Ergebnisse klassifizieren API-gestützte Boards über das Server-Register statt über eine abgedriftete Regex; das „Proceed with Caution“ des Trackers ist eine Warnung, kein rotes Badge; stats faltet lokalisierte Statusse zusammen; Balkendiagramme lesen sich in RTL korrekt; das Docs-FAB verdeckt auf Telefonen keinen Inhalt mehr;
GET /app.jsantwortet mit 404 statt der Index-Shell;GET /api/runnerslistet seine Aktionen auf.
Hinzugefügt
- Dänisch kommt in die Auto-Erkennung — ein
da-DK-Browser erhält jetzt die dänische Oberfläche (das Wörterbuch war vollständig, nur die Erkennung übersprang es);zh-Hant*ordnet Traditionelles Chinesisch zu.cv-diagnostics— die Lebenslauf-Checkliste — ist vollständig lokalisiert (28diag.*-Schlüssel × 17). 26 Feldspezifikations-Labels ergänzt; 31 tote Schlüssel entfernt; Anbieterlisten behaupten nicht mehr, Hermes sei „zuletzt in der Auto-Reihenfolge“; die docs-fab/menu-aria-labels sind lokalisiert.
Anmerkungen
- Tests 4812 → 5036 Unit, Browser 118 + neue Navigations-/Kontrast-Contracts, Coverage-Mittelwert 98.15 % Zeilen / 89.34 % Branch (Untergrenze 96/86).
- Nicht in diesem Release: das
#/scan-Redesign (Phase 4 — v1.244.0, Spezifikation entworfen); ein eigenerexport.docxFailed-Dict-Schlüssel (der Fehlerfall nutztcommon.error);5 lakhs INRparst weiterhin als 5 (vorbestehend).
[1.242.1] — 2026-10-08
Behoben
- Das Release-Archiv enthält erneut keine
node_modules-Symlinks. Der Git-Tag v1.242.0 trug einennode_modules-Symlink im Root undsite/node_modules(ein pauschalesgit add -Aschlüpfte an den Ignore-Mustern mit abschließendem Schrägstrich vorbei), wodurch die tar-Entpackung des Server-Deploys scheiterte — der Deploy rollte sicher zurück, die Produktion blieb auf 1.241.1. Beide Symlinks sind aus der Verfolgung entfernt, die Ignore-Muster verbreitert; das npm-Tarball war nie betroffen (npm schließtnode_modulesbeim Packen aus).
Anmerkungen
- Der App-Code ist identisch mit v1.242.0 — nur Verpackung. Die Sources-Correctness-Inhalte kommen mit diesem Deploy in die Produktion (v1.242.0 erreichte die Produktion nie; sein Server-Deploy rollte nach dem tar-Fehler zurück).
- Bewusst nicht getan: v1.242.0 neu taggen (npm hat 1.242.0 bereits veröffentlicht; das Registry-Tarball ist sauber — npm schließt
node_modulesbeim Packen aus).
[1.242.0] — 2026-10-07
Die Datenschicht des Scanners ist wieder vertrauenswürdig: Tote Quellen leben wieder, eine fehlerhafte Antwort kann sich nicht mehr als leeres Board ausgeben, und eine feindliche Board-URL kann einen Scan nicht mehr umleiten.
Behoben
- Tote Quellen leben wieder. justjoin (der API-Envelope wurde auf
{data, meta.next.cursor}geändert) liest seinen gesamten Katalog über einen Cursor-Durchlauf mit den neuen Gehaltsfeldern; nofluffjobs antwortet ohnesalaryCurrencymit 400 — die vom Elternprojekt geforderte Abfrage wird gesendet und die Ergebnisse paginiert (pageTo). himalayas und jobicy sahen von über 100.000 Stellen nur die neuesten ~20 — beide durchlaufen jetzt ihre vollständigen Kataloge (Offset-/Cursor-Paginierung mit Seitenlimits). - Ein 200 mit falscher Struktur wirft auf Seite 1 eine Ausnahme. ~40 Quellen lasen fehlerhafte oder Challenge-Antworten als „0 Stellen“, und der Scan „gelang“ stillschweigend. Neue Guards in
server/lib/sources/_shape.mjs(requireArray/requireContainer/requireObject) machen den Fehler in jeder Familie laut; ein Fehler auf einer späteren Seite behält die bereits gesammelten Seiten; die Paginierung stoppt an der rohen Seitenlänge, nicht an der gefilterten Anzahl. - SSRF: Substring-Anbieterprüfungen durch geparste exakte Host-Pins ersetzt. lever (
clever.comenthältlever.co), greenhouse (5 Hosts + die Legacy-boards[.eu].greenhouse.io-Pfad-Slug-Hosts), ashby, smartrecruiters, workable, gem REST, ibm, arbeitsagentur, hecklerkoch (nur www), workingnomads (http://169.254.169.254war trotz DNS-Guard erreichbar), remoteok, remotive, rss, successfactors. Überallredirect:'error'; Stellen-URLs müssenhttps:auf dem gepinnten Host sein (javascript:/data:/ Off-Host wird verworfen). - Ein falsch konfigurierter Portals-Eintrag kann keinen ganzen Scan mehr abbrechen.
resolveAdapterfängt Adapter-Fehler ab,detectApiwird pro Firma gekapselt, werfende Adapter gebennullzurück; Fetch-Fehler von comeet schwärzen?token=, bevor sie in Logs oder Quarantäne-Einträgen landen. - Paginierung und Aktualität. workday durchläuft Tenants über 100 Stellen hinaus (Offset-Seiten,
MAX_PAGES); telegram behält die NEUESTEN Beiträge und paginiert?before=, statt stillschweigend die ältesten zu behalten; tencent läuft weiter, wennCountfehlt; taleo parst Tabellen mit französischen Spaltenüberschriften; avature stoppt an der rohen Seitenlänge; UTC-Datumsstempel (amazon Off-by-one östlich von UTC, torre, workingnomads, trudvsem); HTML-Entities dekodiert (geekjob, getmatch, hh); mycareersfuture liefert die vollständige Stellenstruktur mit 12 Feldern. - Remote-Erkennung verschärft. arbeitsagentur, vdab, rippling, teamtailor: Vertragsprozentsatz („Verpleegkundige (100%)“,
bundesweit) und das Wort „distributed“ („Distributed Systems Engineer“) markieren Vor-Ort-Rollen nicht mehr als Remote.
Hinzugefügt
tests/adapters-pin-coverage.test.mjsvervollständigt die Host-Pin-Branch-Abdeckung, die die Pins eingeführt haben.
Anmerkungen
- Unit-Tests 4419 → 4812, Browser 118. Coverage-Mittelwert 98.15 % Zeilen / 89.36 % Branch (Gate-Untergrenze 96 / 86).
- Nicht in diesem Release: die Client-Befunde aus derselben Review-Runde (Phase 3 — Views, Libs, CSS/a11y, i18n);
sources/habr.mjshat weiterhin keine eigene Testdatei (Ratchet-Ausnahme).
[1.241.1] — 2026-10-07
Live-Bewertungen funktionieren in jeder Sprache durchgängig, die Stellenkarte platziert lange Stellen mit mehreren Standorten, und ein Deploy beendet keinen laufenden Scan mehr.
Behoben
- Bewertungen sehen die gesamte Methode. Das Kontextbudget (144 KB) schnitt das Ende von
modes/oferta.mdweiterhin ab, sobald Lebenslauf und Profil eingebettet waren, sodass jeder Live-Bewertung die letzten Blöcke fehlten. Das Budget beträgt jetzt 192 KB und die weiche Prompt-Grenze 256 KB, einmalig inllm-dispatch.mjsdefiniert. - Berichte in der gewählten Sprache. Die Sprachanweisung steht jetzt sowohl am Anfang als auch am Ende des Prompts, und gewöhnliche Wörter (Rolle, Team, Design …) werden in der Sprache des Berichts geschrieben; nur Namen, Technologien und wörtliche Zitate bleiben englisch. Behebt Hindi- und Japanisch-Berichte, die überwiegend englisch waren.
- Langsame Bewertungen enden nicht mehr mit HTTP 502. Das Bewertungs-Timeout beträgt 300 s (zuvor der Provider-Standard von 180 s).
- Hindi-Blockbuchstaben. Ein Bericht, der die Buchstaben A–G in Devanagari schreibt (
## ए),## बी)…## जी)), wird erkannt, undSCORE:akzeptiert lokale Ziffern (३.८,٣٫٨,3.8) sowie ein Dezimalkomma. - Stellenkarte: lange Standorte. Eine Zelle wie „Berlin Office · Berlin · Germany · Bosnia · …“ (über 300 Zeichen) wurde mit HTTP 400 abgewiesen, und die Stelle erhielt keinen Marker. Jetzt wird der erste echte Ort verwendet (
·,;,|,/, Zeilenumbrüche; Segmente „Remote …“, „(HQ)“ und „Office“ werden übersprungen); abgewiesen wird nur noch eine Anfrage über 2000 Zeichen. - Deploy und stündlicher Scan. Der Deploy hält die Timer für Scan und Auto-Bewertung an, wartet auf einen laufenden Lauf (bis zu 15 Min.), startet neu und nimmt die Timer immer wieder auf — auch beim Rollback.
Hinzugefügt
- Stellenkarte auf cvstart.org in allen 17 Sprachen sowie in den englischen und russischen READMEs.
- Kartenscreenshots in jeder Sprache — einer pro Sprache (
scripts/capture-map-screenshots.mjs), in allen 17 READMEs, auf cvstart.org und in einem neuen Hilfe-Abschnitt „So nutzen Sie die Karte“ (§33, ×17).
Anmerkungen
- Tests 4404 → 4419, Browser 118. Remote-QA (alle Routen, Website, Links und Live-LLM × 17 Sprachen) in Produktion grün.
- Nicht in diesem Release: Streaming-Bewertungen — das 300-s-Timeout bleibt eine harte Grenze (ein langsames lokales Modell kann es weiterhin erreichen), und die Hermes-Bridge ruft weiterhin ohne Streaming auf.
[1.241.0] — 2026-10-07
Härtungs-Release nach einem Code-Review mit 28 Agenten (~190 verifizierte Befunde): Die Klassen Sicherheit, Prozessabsturz und Datenverlust sind geschlossen, dazu eine Stellenkarte, beigesteuert von @bullitt186.
Hinzugefügt
- Stellenkarte (
#/map) — Stellen aus der Pipeline, dem letzten Scan und dem Tracker auf einer OpenStreetMap-Karte, geokodiert über Nominatim mit identifizierendem User-Agent, einer Warteschlange mit 1 Anfrage/s, Back-off und einem Cache auf der Festplatte. Leaflet wird von diesem Server ausgeliefert (kein CDN); die CSP erhält lediglich die Tile-Origin inimg-src. Beigesteuert von @bullitt186 (#381). - Request-Guard — Der Server weist einen
Hostab, den er nicht bedient (DNS-Rebinding), sowie Cross-Site-Schreibzugriffe und GETs mit Nebenwirkungen (/api/stream/*,/api/run/*,/api/geocode). Ein Reverse-Proxy, der einen öffentlichen Namen weiterleitet, trägt ihn inALLOWED_HOSTSein; der Deploy-Workflow schreibt dieses systemd-Drop-in aus der öffentlichen URL. - Coverage-Gate —
npm run test:coverage:gate(scripts/coverage-report.mjs) gibt die Zeilen-/Branch-Abdeckung pro Datei aus und schlägt unterhalb einer nachziehenden Baseline fehl.
Behoben
- Eine fehlerhafte Anfrage stoppt den Server nicht mehr. Express 4 ignorierte abgelehnte asynchrone Handler, sodass ein Feld mit falschem Typ in
/api/deep,/api/auto-pipeline,/api/career-plan,/api/trackeroder ein NUL-Byte in/api/statsden Prozess beendete; jetzt sind das JSON-400-Antworten, und jede andere Ablehnung wird zu einer 500. - Ein abgebrochener oder fehlgeschlagener Scan löscht die letzten Ergebnisse nicht mehr.
last-scan.jsonbleibt erhalten, wenn ein Scan gestoppt wird oder jede Quelle fehlschlug, und ein Scan einer einzelnen Firma führt zusammen, statt zu ersetzen. Ein falsch konfigurierter Eintrag inportals.ymlbricht nicht mehr den ganzen Scan ab. - Bewertungen sehen die gesamte Methode. Modus-Dateien wurden bei 16 KB abgeschnitten, sodass
oferta.md(92 KB) die Blöcke C–G verlor; die Grenzen pro Datei liegen jetzt bei 128 KB für Modi und 64 KB für den Lebenslauf, und ein Abschneiden wird gemeldet. Leere oder abgeschnittene Antworten schlagen fehl, statt einen leeren Bericht zu schreiben. - SSRF: Redirect-Hops werden erneut auf private Ziele geprüft, jede DNS-Antwort wird geprüft, die Scan-Frist umfasst den Body,
*_BASE_URL-Schlüssel gehen nur an den Host des Anbieters (Hermes/Ollama: Loopback oder LAN), pandoc läuft mit--sandbox. - Event-Loop-Blockaden: zwei quadratische Regexe (HTML-Entfernung, Markdown-Bereinigung) durch lineare Scanner ersetzt.
- Datenverlust: Das Löschen einer Pipeline-Zeile behielt die Vergütung der anderen Zeilen;
$&in einer URL beschädigtepipeline.md; Speichern von Mock-Interview, Networking und Deep-Research überschrieb frühere Dateien; der Interview-Verlauf behielt die ersten 40 Züge statt der letzten. - LLM-Schicht: Auto-Pipeline und Deep-Research beachten
LLM_PROVIDERund jeden Provider über den gemeinsamen Dispatch, erfassen die Nutzung und melden Abschneiden; Gemini-evaluate/-test speichern keinen Bericht mehr; OpenAI verwendetmax_completion_tokens, und ein reines Codex-OPENAI_MODELwird alsgpt-5gesendet; die Stellenbeschreibung wird als nicht vertrauenswürdige Daten eingegrenzt. - Bericht-, Deep- und Inline-PDFs übergeben
--skip-fact-check(der Lebenslauf-Faktencheck blockierte jeden Bericht mit Zahlen); das Seitenformat des Profils wird nicht mehr überschrieben. - Scanner: Die Quarantäne wird nach URL geführt (behebt, dass eine URL erneut versucht wird), Deduplizierung innerhalb eines Laufs nach kanonischer URL, leere Listeneinträge passen nicht mehr auf alles, atomare Schreibvorgänge,
skipped_*-Verlaufszeilen gelten nicht als „gesehen“. - Prozesssteuerung: Die SIGKILL-Eskalation griff nie; Batch-Läufe beenden bei Verbindungsabbruch ihre gesamte Prozessgruppe.
- CI und Release: CI führt jetzt die Gates für Changelog-Parität, Workflows und i18n aus; Workflow-Berechtigungen nach dem Least-Privilege-Prinzip; Produktionsgeheimnisse nur unter der Umgebung
production; das npm-Paket liefert jetztdocs/help/mit. - Tests: Kein Test berührt mehr das Netzwerk oder das echte Elternprojekt (ein explizites
CAREER_OPS_ROOTwird immer beachtet); kopierte Algorithmen durch den Produktionscode ersetzt.
Anmerkungen
- Tests 4056 → 4404, Browser 118. Server-Coverage-Baseline: Zeilen 96,7 %, Branch 86,3 % (
scripts/coverage-baseline.json). - Spezifikationen liegen jetzt in einem Ordner,
docs/sdd/specs/; der Programmplan istdocs/sdd/PLAN.md, die Matrix Invariante→Gatedocs/sdd/TRACEABILITY.md. - Hinweis für Betreiber: Hinter einem Reverse-Proxy
ALLOWED_HOSTS=<öffentlicher Host>setzen; eine Ollama-/Hermes-Basis-URL auf einem öffentlichen Host wird jetzt abgelehnt.
[1.240.0] — 2026-10-06
Parent-Parität — career-ops main @ 62905981 (VERSION 1.35.0, 177 Upstream-Commits seit b39931e). Sechs neue Quellen (103 → 109; 104 EN + 5 RU), in acht bestehende Provider gespiegelte Fixes und das Checklisten-Pipeline-Format, beigesteuert von @bullitt186.
Hinzugefügt
- Sechs neue Quellen — 103 → 109 (104 EN + 5 RU), 98 → 104 EN-Adapter.
- ADP Workforce Now (
workforcenow.adp.commitcid+ccId) —$skip-Paginierung nach tatsächlich gelieferten Zeilen; eine Antwort ohnejobRequisitionsund ohnemetawirft einen Fehler, statt als leeres Board durchzugehen; Detailanreicherung als Opt-in (adpWorkforcenow.fetchDetails, Standard 25, Obergrenze 100). - Gupy (
employability-portal.gupy.io) — boardweiter Stichwort-Durchlauf, neueste zuerst mit vorzeitigem Abbruch beisince_days;workplaceTypewird als der singuläre pt-BR-String gelesen, sodass Hybrid-Stellen nicht verloren gehen; vertrauliche Karriereseiten werden verworfen. - JazzHR (
*.applytojob.com) — Karten-Parser mit Fallback auf nackte Anker, sodass ein teilweises Redesign trotzdem Stellen liefert; Stellenlinks ohne eine einzige geparste Karte werfen einen Fehler, ein wirklich leeres Board liefert[]. - Startup Jobs (
startup.jobs/feeds/jobs) — RSS, eine Anfrage; ein fehlerhafter, HTML- oder abgeschnittener Body wirft einen Fehler, statt[]zu liefern (der Review-Fix des Elternprojekts nach dem Merge,08fe5d06); CDATA und Kommentare werden vor dem Item-Abgleich maskiert. - Taleo (
<tenant>.taleo.net,tre.taleo.netwird abgelehnt) — Shell-GET, dannsearchjobs-POST; eine Shell ohne Portal-ID wirft einen Fehler als privates Board; Spalten werden anhand der Überschrift zugeordnet. - UKG Pro / UltiPro (
recruiting[N].ultipro.{com,ca}) —Top=50+Skip,totalCountwird nie als Abbruchbedingung herangezogen, Seitenobergrenze 100 (bei 1500 gekappt), Detailseiten als Opt-in, geparst mit einem stringbewussten Klammer-Durchlauf.
- ADP Workforce Now (
Behoben
- Pipeline im Checklisten-Format des Elternprojekts — das Upstream-career-ops schreibt
data/pipeline.mdals Abschnitte## Pending/## Processedmit Zeilen- [ ] url | …; die UI las das als 0 offen und schrieb neue URLs, indem sie am Dateiende einen Fence anhängte, wo das Elternprojekt nie nachsieht.parsePipeline,addPipelineUrlundremovePipelineUrllesen und schreiben jetzt diese Form (Dedup auch gegen verarbeitete Zeilen; Vergütung als beschriftetes Segmentnote: comp …); das Fence-Format bleibt unverändert. Beigesteuert von @bullitt186 (#380). - Land in den Standort übernommen — Ashby (primär
address.postalAddress), Breezy (country.name) und Recruitee (flacheslocation) hängen das Land an, sofern der Name es nicht schon als ganzes Wort enthält, sodass eine Remote-Stelle mit primärem UK- und sekundärem US-Standort nicht mehr von einem Standortfilter verworfen wird. - MokaHR — der regionale Host
hire-r1.mokahr.comwird akzeptiert (die Allowlist besteht genau ausapp.mokahr.com+hire-r1.mokahr.com), API-POST und Stellenlinks verwenden den eigenen Host des Boards, und ein Inband-success:falsewirft jetzt einen Fehler, statt als leeres Board zu gelten. - Avature —
article--jobs-Karten werden geparst (Titel aus dem Header-Anker, Standort aus demicon-address-Span). - SAP SuccessFactors — eine gespeicherte Such-URL
/go/<Category>/<id>[/<offset>]/wird zur Mandanten-Wurzel aufgelöst; sie traf früher…/tile-search-results/, das mit 200 und null Kacheln antwortet. - Deutsche Bahn — db.jobs leert jede Ergebnisseite, die eine nicht darstellbare Stelle enthält; der Durchlauf sortiert jetzt nach
pubExternalDate_tdtmit 1000 pro Seite (höchstens 5 Seiten), bricht anhand der rohen Zahl der Treffer-Anker ab und wirft einen Fehler bei fehlendem Ergebniskopf oder ungeparsten Stellenlinks, statt ein zu kurzes Board zu liefern. - HigherEdJobs — eine mit HTTP 200 beantwortete Incapsula-Challenge wurde als null Einträge geparst und als leeres Board gelesen; jetzt wirft sie einen Fehler.
- Ein Test las auf einem Entwicklerrechner das echte Elternprojekt —
tests/reports-list-cache.test.mjssetzteCAREER_OPS_ROOTauf ein temporäres Verzeichnis ohnecv.md/portals.yml, waspaths.mjsignoriert; lokal zählte er deshalb die Berichte des Elternprojekts (6 statt 3), während CI ohne Elternprojekt daneben grün durchlief.
Anmerkungen
- Tests 3782 → 4056. Neue Suiten:
sources-adp-workforcenow,sources-gupy,sources-jazzhr,sources-startup-jobs,sources-taleo,sources-ultipro,sources-location-country-fold,sources-deutschebahn-blanked-page,sources-higheredjobs-challenge-page, dazuparsers-pipeline-checklistaus #380. - Elternprojekt: Der Fork wurde auf Upstream-
maingebracht (144 → 0 Commits dahinter); die vier ADR-0002-Abweichungen sind als unversehrt verifiziert; Upstreams Revert des kyrillischenLOCATIONISH_REwurde nicht übernommen. - Bereits in v1.239.0 ausgeliefert, nicht wiederholt: AppliTrack, NEOGOV, Red Rover, SchoolSpring, OCC Mundial, eingebettete Greenhouse-Boards, Workday
myworkdaysite.com.
[1.239.4] — 2026-09-29
Patch aus der Live-Regression von v1.239.3.
Behoben
- Koreanische und traditionell-chinesische Bewertungen meldeten eine fehlende Bewertungszusammenfassung. Diese Berichte umfassen 10–11 Tausend Zeichen, und die Zusammenfassung steht am Ende, also lag sie jenseits der Grenze von 8192 Ausgabe-Tokens. Bewertungen (und die Auto-Pipeline) erlauben jetzt 16384 Tokens, und wenn ein Anbieter eine Antwort trotzdem abschneidet, sagt das die erste Warnung, statt fehlende Blöcke aufzuzählen.
- Der Live-Check hielt eine kurze deutsche Antwort für nicht deutsch (3 deutsche Stoppwörter, keine englischen). Eine listenförmige Antwort kann wenige enthalten; ganz ohne englische genügen jetzt drei.
- Tests 3778 → 3782.
Anmerkungen
- Die Routen für Tiefenrecherche und Modi behalten ihre Grenze von 8192 Tokens: Ihre Antworten erreichen sie in den Live-Läufen nicht.
[1.239.3] — 2026-09-29
Patch aus der Live-Regression von v1.239.2.
Behoben
- Arabische Bewertungen fielen weiterhin durch die A–G-Prüfung, nachdem v1.239.2 Japanisch und Hindi behoben hatte. Ein maskiertes Überschriften-Gerüst (kein Berichtstext im Log) zeigte den Grund: Der arabische Bericht schreibt den Blockbuchstaben auf Arabisch, in Abdschad-Reihenfolge, was immer der Prompt verlangt. Die Prüfung akzeptiert jetzt أ ب ج د هـ و ز als A–G, wenn der Buchstabe allein am Anfang einer Überschrift steht, wie sie schon kyrillische Doppelgänger akzeptiert; dieselben Buchstaben in einem arabischen Wort zählen nicht.
- Der Live-Check gibt dieses maskierte Gerüst aus, wenn die A–G-Prüfung scheitert, damit sich der nächste Fall aus dem Log diagnostizieren lässt.
- Tests 3776 → 3778.
Anmerkungen
- Die Notiz zu v1.239.2 sagte, die Prüfung werde nicht gelockert; für Arabisch ist sie es jetzt, weil drei Live-Läufe zeigten, dass der Prompt allein den lateinischen Buchstaben nicht hält.
[1.239.2] — 2026-09-29
Patch aus der Live-Regression von v1.239.1.
Behoben
- Eine arabische Bewertung übersetzte ihre Blockbuchstaben, eine japanische ließ die Bewertungszusammenfassung weg. v1.239.1 wiederholte in der letzten Prompt-Zeile nur die Sprache. Diese Zeile sagt jetzt auch, dass der lateinische Buchstabe A–G jeder Überschrift und der
---SCORE_SUMMARY----Block (Marker und Schlüssel auf Englisch) nicht übersetzt werden, und verlangt die eigene Schrift der Sprache. - Tests 3775 → 3776.
Anmerkungen
- Die Berichtsprüfung wird nicht gelockert, um übersetzte Buchstaben oder Marker anzunehmen: Das Format hält der Prompt stabil.
[1.239.1] — 2026-09-29
Patch aus der Live-Regression von v1.239.0.
Behoben
- Japanische und ukrainische Bewertungen kamen fast vollständig auf Englisch. Die Sprachanweisung stand am Anfang des Prompts, vor Dutzenden Kilobyte englischer Projektdateien und Stellenbeschreibung. Der Prompt wiederholt die Sprache jetzt in seiner letzten Zeile.
- Der Live-Check meldete einen fehlgeschlagenen Scan, wenn der Server bereits einen ausführte (den stündlichen Scan, direkt nach dem Deploy wiederholt). Dass der Server einen zweiten Scan ablehnt, ist korrekt; der Check wartet jetzt und versucht es erneut, bis zu 30 Minuten.
- Tests 3774 → 3775.
Anmerkungen
- Die Einzelscan-Sperre des Servers wird nicht gelockert: Ein zweiter Scan wird weiterhin mit
SCAN_BUSYabgelehnt. Geändert hat sich nur der Check.
[1.239.0] — 2026-09-29
Parität mit career-ops @ b39931e: fünf neue Quellen und drei übernommene Fixes.
Hinzugefügt
- Fünf neue Quellen (98 → 103; englische Adapter 93 → 98): AppliTrack (
applitrack.com/<Bezirk>), NEOGOV (SchoolJobs / GovernmentJobs), Red Rover (jobs.redroverk12.com/org/<id>) und SchoolSpring (<Bezirk>.schoolspring.com) für Stellen an Schulen und im öffentlichen Dienst, dazu OCC Mundial, Mexikos größte Jobbörse (explizitesprovider: occmit Suchbegriffen). Jede ist an ihren Host gebunden, nur HTTPS und lehnt Weiterleitungen ab. - Tests 3500 → 3774.
Behoben
- Eingebettete Greenhouse-Boards (
job-boards.greenhouse.io/embed/job_board?for=<Board>) zeigten auf ein Board namens „embed“ und schlugen fehl; das Board kommt jetzt aus?for=, und nur auf einem echten Greenhouse-Host. - Workday-Boards auf
myworkdaysite.com(/recruiting/<Tenant>/<Site>) wurden nicht erkannt; sie werden jetzt gescannt, mit Stellenlinks auf demselben Host. - Ein Berichtsname mit Klammern (
042-acme-(berlin)-….md) wurde im Tracker an der ersten)abgeschnitten; der ganze Name bleibt jetzt erhalten.
Anmerkungen
- Änderungen des Elternprojekts, die nur die CLI betreffen, werden nicht übernommen — PDF- und CV-Vorlagen, der Scaffolder, der Batch-Runner, der career-profile-Import, die ATS-Schlüsselwortabdeckung, die Übersetzungen der Interview-Modi, der CLI-Host
pi: web-ui führt sie nicht aus. - Einen solchen Bericht aus dem Tracker zu öffnen, erfordert noch, dass die Berichtsroute Klammern im Dateinamen akzeptiert; diese Route wird in diesem Release nicht geändert.
[1.238.3] — 2026-09-27
Seiten bleiben während eines Scans reaktionsfähig, und Remote-QA-Logs verraten den Produktionshost nicht mehr.
Behoben
- Dashboard und Berichte brauchten Sekunden zum Laden und liefen während eines Scans in Timeouts.
/api/reportsund/api/dashboardlasen und parsten bei jeder Anfrage alle Berichte — rund 3,7 s auf dem Produktionsserver, während alle anderen Anfragen warten mussten; lief ein Scan, erreichten Seitenaufrufe das 30-Sekunden-Timeout. Geparste Berichtsköpfe werden jetzt pro Datei zwischengespeichert und bei jeder Dateiänderung erneuert, sodass eine Liste nur eine Dateiprüfung pro Bericht kostet (500 Berichte: lokal 430 ms → 3 ms). - Ein Remote-QA-Befund konnte einen Teil des Produktionshosts in ein öffentliches Log schreiben. GitHub maskiert ein Secret nur, wenn es vollständig erscheint, und eine auf 180 Zeichen gekürzte Fehlermeldung endete mitten im Hostnamen. Befunde und ganze Berichte werden jetzt vor dem Kürzen geschwärzt.
- Jede KI-Bewertung wurde als fehlerhaft markiert. Der Prompt verlangte nie den
---SCORE_SUMMARY----Block, den die Berichtsprüfung voraussetzt (die Bewertungsskripte des Elternprojekts verlangen ihn), und ein übersetzter Bericht übersetzte auch das Wort “Block”, sodass die Prüfung seine Abschnitte nicht fand. Der Prompt verlangt jetzt die Zusammenfassung und den Blockbuchstaben in jeder Überschrift, die Prüfung akzeptiert ein übersetztes Wort vor dem Buchstaben, und der Zusammenfassungsblock wird entfernt, bevor der Bericht angezeigt oder gespeichert wird.
Hinzugefügt
- Tests 3488 → 3500.
Anmerkungen
- Der Scanner läuft weiterhin im Serverprozess; dieses Release beseitigt den anderen großen Blocker, verlagert den Scan aber nicht in einen Worker.
[1.238.2] — 2026-09-27
Fix für die 502, die der Produktionsserver während des stündlichen Scans noch zeigte.
Behoben
- Seiten und Dateien lieferten während eines Scans 502. Node schließt eine ruhende Keep-Alive-Verbindung nach 5 s; Caddy verwendet ruhende Verbindungen bis zu 2 Minuten weiter. Während ein Scan im Prozess den Server auslastete, schloss Node Sockets, auf denen Caddy gerade eine Anfrage gesendet hatte, und Caddy antwortete mit 502 connection reset by peer (62-mal während eines Scans, der Server selbst war gesund). Der Server hält ruhende Verbindungen jetzt 125 s — länger als der Proxy —, sodass immer der Proxy zuerst schließt.
Hinzugefügt
deploy.yml→verifyzeigt, warum der Proxy 502 geantwortet hat (Fehlerarten, Adressen maskiert), die 502 pro Minute seit dem letzten Neustart und das Ergebnis des stündlichen Scans.release.ymlkann beim manuellen Start ein fehlendes Tag anlegen, sodass sich ein Release auch dort erstellen lässt, wo keine Tags gepusht werden können.- Tests 3486 → 3488.
Anmerkungen
- Der Scan läuft weiterhin im Serverprozess und kann ihn sekundenlang auslasten; ihn in einen Worker zu verlagern, ist nicht Teil dieses Patches.
[1.238.1] — 2026-09-27
Stabilitäts-Patch nach der ersten Live-Regression des Produktionsservers: ein Scan zur Zeit und Website-Korrekturen.
Behoben
- Zwei Scans gleichzeitig brachten den Server zum Absturz. Jedes
GET /api/stream/scanstartete den vollständigen Scanner im Serverprozess ohne Schutz; ein über die UI gestarteter Scan überschnitt sich mit dem stündlichen Timer, und der Prozess starb an JavaScript heap out of memory (zweimal, dazwischen 502-Fehler). Jetzt läuft nur ein Scan zur Zeit: eine zweite Anfrage erhält ein SSE-errormit dem CodeSCAN_BUSY, und nichts startet. Der Deploy-Workflow hebt außerdem das Speicherlimit des Servers an (heap_mb, Standard 448 MB). /helpund/changelogauf cvstart.org scrollten auf Handys seitwärts (ja/zh-/changelogauch am Desktop): langer Code und das Inhaltsverzeichnis brechen jetzt um.- Hindi fehlte in den Sprachalternativen der Sitemap; der i18n-Check der Website vergleicht die Sitemap jetzt mit dem Sprachregister.
- Hilfe §19 verlangte, einen Schlüssel zu „allen 8“ Sprachdateien hinzuzufügen — es sind 17.
Hinzugefügt
- Remote-Regression (
.github/workflows/remote-qa.yml): jede Route × jede Sprache der ausgerollten App (nur lesend), jede Seite × Sprache × Handy/Desktop auf cvstart.org, jeder Link in den READMEs und im Wiki und auf Wunsch ein echter Scan plus Live-KI-Aufrufe in jeder Sprache. - Server-Deploy-Workflow (
.github/workflows/deploy.yml):recon,deploy(mit bytegenauem Rollback) undverify(Smoke-Test und Stabilitätsbericht). - Tests 3481 → 3486.
Anmerkungen
- Der Speicherverbrauch des Scanners selbst wird in dieser Version nicht gesenkt: der Schutz verhindert zwei gleichzeitige Scans, und das Drop-in hebt die Obergrenze an. Ein echter Scan über 98 Quellen lässt sich nur im Produktionsnetz profilieren.
- Der stündliche Timer
career-ops-scanwird nicht geändert;reconzeigt jetzt, welche Befehle er ausführt.
[1.238.0] — 2026-09-27
Parent-Parität — career-ops main @ 993085ce (VERSION 1.34.0, 182 Commits seit de2224a9). Vier neue Quellen, zehn gespiegelte Provider-Fixes, eine repo-weite SSRF-Lücke geschlossen und ein kostenloser Titel-Fit-Hinweis bei Scan-Ergebnissen.
Hinzugefügt
- Vier neue Quellen — 94 → 98 (93 EN + 5 RU), 89 → 93 EN-Adapter.
- Eploy (
provider: eploy) — liest die/live-jobs.xml-Sitemap des Mandanten auf jedem gebrandeten Host (nur HTTPS; IP-Literale,localhost,*.local,*.internalund einteilige Hosts werden abgelehnt). Eine Nicht-Sitemap-Antwort (eine Login-Seite) wirft einen Fehler, statt als null Jobs durchzugehen. Detailseiten sind Opt-in (eploy: { fetchDetails, detailLimit }). - HiringRoom (
*.hiringroom.com) — zuerst JSON-LD/portal/jobs, als Fallback die alten/jobs-Karten;http://-Posting-URLs im Payload werden auf HTTPS umgebaut. - PeopleSoft Candidate Gateway — erkannt am Pfad
/psc/{site}/EMPLOYEE/HRMS/c/HRS_HRAM_FL.HRS_CG_SEARCH_FL.GBLauf jedem gebrandeten Host. Er folgt PeopleSofts Same-Origin-Session-Redirects selbst (max. 10 Hops, jeder Hop erneut auf den Mandanten-Origin gepinnt und gegen private Adressen geprüft), spielt das „load more”-Formular erneut ab und markiert einen partiellen Durchlauf (peoplesoftIncomplete), statt ihn als vollständig zu melden. - PrevueAPS (
*.prevueaps.ca) — löst die Site-ID von der Jobs-Seite auf und liest dann die JSON-API.
- Eploy (
- Titel-Fit-Hinweis bei
#/scan-Zeilen. Jedes Ergebnis trägt einen groben strong / related / weak fit-Chip, der den Posting-Titel mit den Zielrollen ausconfig/profile.ymlvergleicht (target_roles.primary+ Archetyp-Namen). Es ist eine kostenlose Keyword-Schätzung: Sie filtert, sortiert oder verändert Anzahlen niemals — der Tooltip sagt das auch. Wird berechnet, wenn die Ergebnisse ausgeliefert werden, sodass eine Profiländerung erst beim nächsten Refresh sichtbar wird. ×17 übersetzt. Spiegelt Parent #3261.
Behoben
- Sicherheit — der DNS-Rebinding-Schutz lief bei einem echten Scan nie.
http-json.mjsprüfte aufgelöste Adressen nur, wenn ihm der nackte globalefetchübergeben wurde, aber beide Scanner injizierenmakeTimeoutFetch(), sodass jeder Scan die Prüfung übersprang. Eine Quelle, die auf einen Hostnamen zeigte, der zu10.xoder127.0.0.1auflöst, wurde abgerufen. Der Schutz läuft jetzt innerhalb vonmakeTimeoutFetchselbst, im Wettlauf mit dem Scan-Timeout, damit ein hängender Resolver einen Scan nicht blockieren kann. Gefunden im Review dieses Releases. - Sicherheit — IPv6-Adressen, die eine IPv4-Adresse einbetten, wurden nicht als privat erkannt.
http://[::ffff:127.0.0.1]/(das der URL-Parser zu[::ffff:7f00:1]umschreibt) bestandisValidJobUrl. Sowohl die gepunktete als auch die hexadezimale Form von::ffff:a.b.c.d/::a.b.c.dwerden jetzt anhand der eingebetteten Adresse klassifiziert. Parent_ip-guard.mjs. - Rippling wechselt zur v2-Board-API (
ats.rippling.com/api/v2/board/<slug>/jobs), mit Paginierung (Standard 10 Seiten, max. 50), einer Host-Sperre auf Job-Links und Slug-Validierung. Eine eingefügte API-URL liefert jetzt den echten Slug stattapi. - Recruitee — Angebote mit mehreren Standorten listen jeden Ort auf (
A · B),(Sample)-Demo-Postings und titellose Zeilen werden verworfen, und ein Angebot wird behalten, wenn eine seiner beiden URLs gültig ist. - Pinpoint — gesäte Demo-Postings (das doppelte Demo-Video) werden verworfen.
- Telegram-Kanäle — eine erste Zeile mit mehreren
|wird nicht mehr alsTitel | Firmagelesen, und mehr Rollenwörter werden erkannt. Der kyrillisch-bewussteLOCATIONISH_REdes Forks bleibt unangetastet (ADR-0002). - Workday — ein 422/401/403 auf der ersten Seite wird einmal geprüft; Workdays Wartungsseite oder Ausfall-Redirect markiert das Board als tot (im Strict-Modus unter Quarantäne). Die CXS- und Detail-Anfragen verweigern jetzt Redirects.
- BambooHR — ein umgeleitetes
/careers/listwird über den öffentlichen Embed-Feed triagiert: leer bedeutet ein lebendes Board ohne offene Stellen, alles andere ein totes Board. - Heckler & Koch / Rheinmetall — ein nacktes
heckler-koch.com/rheinmetall.comwird aufwww.umgeschrieben, damit der redirect-freie Transport es trotzdem erreicht. Ashby verweigert Redirects. beesite taktet Seiten mit dem abbrechbarendelay(). - Titelfilter falten Akzente auf beiden Seiten (EN- und RU-Scanner), sodass
Développeurvon einem Negativ-Matchdeveloppeurerfasst wird. - Der Cooldown-Firmenabgleich versteht nicht-lateinische Namen und Unternehmensformen (CJK-/koreanische Marker, türkisches punktiertes İ). CJK-Namen kollabierten früher zu einem leeren Key und trafen nie.
location_filter.strict: trueschlägt bei einem leeren Standort geschlossen fehl, wenn eine Allow- oder Block-Liste gesetzt ist.- Liveness — neue Public-API-Checks für Greenhouse-Embeds (
gh_jid), Arbeitsagentur und We Work Remotely; SmartRecruiters entscheidet jetzt anhand desactive-Flags des Postings. - PeopleSoft — eine relative „load more”-Formularaktion wird jetzt relativ zur Suchseite aufgelöst, nicht zur Origin-Wurzel.
Anmerkungen
- Tests 3210 → 3481. Neue Suiten:
sources-eploy,sources-hiringroom,sources-peoplesoft,sources-prevueaps,title-fit,fetch-timeout-dns-guard,cooldown-company-match-unicode,liveness-api-more-rungs,ru-scanner-title-accent-fold. - Nicht portiert, mit Begründung: Unicode-Wortgrenzen im Location-Filter (web-uis Filter ist substring-basiert, es gibt also keine Grenze zu reparieren — ein bewusster vollständiger Port des Parent-Location-Filters ist zukünftige Arbeit); Dedup gleichnamiger Requisitions (web-ui dedupliziert nur nach URL); Aggregator-Repost-Warnungen, Blacklist-Domain-Scope, unverifizierte-Null-Belege und
scan-runs.tsv-Migration (web-ui hat keine dieser Oberflächen); Workday-Facet-Split und Truncation-Tags (web-ui holt eine Seite pro Board); BambooHRenrichDate(kein Date-Enrichment-Hook); das Sleep-Refactoring von Parents_http.mjsüber ~20 Provider (web-ui nutzt bereits ein gemeinsames abbrechbaresdelay); Updater, Doctor, PDF, Tracker/Merge-Tracker,validate-profile,discover-new-companies, dieweb/-Next.js-App, GitHub-Bots — nur CLI oder weitergeleitet. - Bewusst anders als beim Parent gehalten: Heckler & Koch / Rheinmetall bleiben HTTPS-only (kein
http→https-Upgrade); ein totes Workday-Board liefert in einem normalen Scan weiterhin[], statt den Scan fehlschlagen zu lassen.
[1.237.1] — 2026-09-22
Patch aus einem externen QA-Durchlauf zu v1.237.0. Der Jobstreet-Fix wurde erstmals von außen bestätigt: Auf den AU-, NZ-, HK- und MY-Hosts liefert die alte Form /id/job/<id> einen sauberen 404, und die neue Form /job/<id> erreicht eine echte, Cloudflare-geschützte Route; auf id.jobstreet.com gilt das Umgekehrte. Die verlorenen Links waren real. Es wurden zwei Fehler gefunden, beide in dem, was v1.237.0 selbst hinzugefügt hatte.
Behoben
- Das Label des Buch-Links war lokalisiert, sein Ziel aber nicht. Alle 17 SPA-Locales und alle 17 cvstart.org-Locales verlinkten den Footer-Link zu Agentic Coding Design Patterns auf die englische Ausgabe, während das eine übersetzte Label — Russisch, «Паттерны агентного кодинга» — ein russisches Buch versprach. Ein neuer
data-i18n-href-Applier (gleiche Form wiedata-i18n-title, beschränkt auf absolutehttps://-Werte) und ein Schlüsselfooter.patternsUrlschickenruzur russischen Ausgabe und jedes andere Locale zur englischen. Der Regressionstest prüft die Invariante in beide Richtungen: Ein übersetzter Titel impliziert eine lokalisierte URL. - SEEK migriert Hosts, und der Transport verweigert Redirects.
www.seek.com.au→au.seek.comundwww.seek.co.nz→nz.seek.com: Die Detailseiten liefern bereits 301, und beide Host-Paare bedienen die v5-Such-API heute mit 200. Dahttp-json.mjsredirect:'error'setzt (ein SSRF-Schutz, den man behalten sollte), würde der Tag, an dem der API-Pfad beginnt umzuleiten, einen AU/NZ-Eintrag auf dem alten Host scheitern lassen — und mit nur denwww.-Hosts auf der Allowlist könnte niemand migrieren.au.seek.comundnz.seek.comstehen jetzt inALLOWED_JOBSTREET_HOSTS; die alten Hosts bleiben, sodass bestehende Konfiguration und bereits gebaute Links weiter funktionieren. Doppelgänger (au.seek.com.evil.com,au-seek.com) werden weiterhin abgelehnt. - Wiki: Die Zeile Agentic Practices in der Dokumentationsübersicht der Home-Seite war als einzige von zwölf
|innerhalb von[[…|…]]nicht escaped, sodass GitHub die Zelle abschnitt — toter Link, Beschreibung verborgen. Ein einziges Zeichen.
Anmerkungen
- Zahlen unverändert bei 94 Quellen (89 EN + 5 RU), 89 Adapter. Tests 3201 → 3210 (+5 Footer-Link-Locale, +4 SEEK-Hosts).
- Von außen nicht abschließbar, festgehalten statt behauptet: Oracle Cloud (Akamai blockierte beide REST-Sonden), der Personio-Pin und
appendWorkType(brauchen einenportals.yml-Eintrag, und die UI hat kein Löschen dafür), sowie die Liveness-String-Fälle (keine kontrollierbare Seite; 40 Live-Sonden ergaben 12 lebend, 28 unentschieden, 0 falsche Abläufe). - Hilfe ×17 listet jetzt beide Host-Formen für
AU-MainundNZ-Main. Überschriftenstruktur unverändert bei 32 H2 / 122 H3.
[1.237.0] — 2026-09-21
Eltern-Parität — career-ops main @ 93c4302b (VERSION 1.33.0, 75 aus career-ops-hq/main gezogene Commits). Diesmal keine neuen Quellen: vier gespiegelte Fixes, zwei davon verloren hier still Anzeigen.
Behoben
- Jobstreet / SEEK — jede australische, neuseeländische, singapurische, malaysische und Hongkonger Anzeige verlinkte auf eine tote Seite.
parseJobstreetItembaute die Detail-URL bedingungslos als${baseUrl}/id/job/${id}auf. Das Segment/id/ist kein Pfad — es ist das indonesische Locale-Präfix und existiert nur auf den indonesischen Hosts. Jeder andere SEEK-Plattform-Host antwortet mit 404 auf/id/job/<id>. web-ui hattewww.seek.com.au,www.seek.co.nz,sg.jobstreet.com,my.jobstreet.comundhk.jobsdb.combereits zuALLOWED_JOBSTREET_HOSTShinzugefügt, sodass der Scan diese Anzeigen anstandslos zurückgab — jede einzelne zeigte auf eine 404. Der Pfad wird jetzt über die neue HelferfunktionjobDetailPathhost-abhängig bestimmt:ID_LOCALE_HOSTS(id.jobstreet.com,www.jobstreet.co.id,jobstreet.co.id) behält/id/job/, alles andere erhält/job/, und ein nicht parsbarer Origin fällt auf den gemeinsamen Pfad zurück, statt zu werfen. Zwei eigene Tests hatten den Fehler festgeschrieben — sie behauptetenhk.jobsdb.com/id/job/…undwww.jobstreet.com/id/job/…— und wurden korrigiert. - Oracle Cloud — eine kurze Seite mitten in der Liste beendete den Durchlauf und ließ das Ende des Boards fallen. Die Abbruchbedingung lautete
listLen === 0 || listLen < PAGE_SIZE, doch ORC liefert mitten in einer Liste weniger Zeilen als das Limit, weil manche Zeilen serverseitig gefiltert werden. Upstream bei American Express gemessen:TotalJobsCount454, Seiten von 200 / 199 / 54 — die 199 wurde als Ende gelesen, und die letzten 54 Anzeigen, 12% des Boards, wurden nie abgerufen. Eine leere Seite beendet den Durchlauf jetzt immer; meldet der Tenant eine Gesamtzahl, läuft die Paginierung weiter, bisoffset + PAGE_SIZE >= total; nur ein Tenant ohne prüfbare Gesamtzahl endet auf einer kurzen Seite. Das folgt der breiteren Konvention, dass eine API „weniger Ergebnisse liefern darf, als angefragt wurden … auch wenn das Ende der Sammlung noch nicht erreicht ist“ (Google AIP-158).hasMorebleibt bewusst ignoriert — manche Tenants liefernhasMore:falseauf jeder Seite eines 7000-Stellen-Boards. Da kurze Seiten den Durchlauf nicht mehr beenden, istMAX_PAGESjetzt die einzige Absicherung gegen einen Tenant, der eine unglaubwürdige Gesamtzahl meldet, weshalb sie durch einen eigenen Test abgedeckt ist. - Liveness —
"This role is closed"wurde nicht erkannt, und ein falsch erkannter Ablauf ist dauerhaft. Das Muster war/this job (listing )?is closed/i, das die ebenso gängigen Formulierungenroleundpositionübersieht; auf einem Board maß man upstream 111 von 111 unsicheren Anzeigen mit"role". Erweitert zu/this (?:job|role|position)(?: listing)? is closed\b(?!-)/i. Das abschließende\b(?!-)ist eine Wache gegen zusammengesetzte Adjektive:\ballein weistclosedownzurück, matcht aber weiterhin echten Fließtext wie “This role is closed-loop control of the platform”, und erst der Lookahead verhindert das. - Liveness —
job expiredsitzt jetzt in einer weichen Stufe, die einen sichtbaren Apply-Button nicht mehr schlagen kann.HARD_EXPIRED_PATTERNSwird vor der Apply-Control-Prüfung geprüft, sodass alles, was dort steht, über eine lebende Anzeige gewinnt./\bjob expired\b/ilöste bei vier echten Live-Seiten-Formen fälschlich aus — einem Karussell-Eintrag"Similar jobs", einem Filter-Chip"Hide job expired", einem Footer-FAQ und der oben genannten closed-loop-Prosa —, weil der Klassifizierer den gesamten Seitentext erhält. Es steht jetzt inSOFT_EXPIRED_PATTERNS, geprüft nach der Apply-Control und vor der Listing-Seiten-Heuristik, und liefertexpired_body_soft; ein nacktes"JOB EXPIRED"-Banner ohne Apply-Control gilt weiterhin als abgelaufen. Diese Richtung ist wichtig, weil ein falschesexpiredim Scan-Verlauf alsskipped_expiredgeschrieben wird und danach einen echten Job aus jedem späteren Scan herausfiltert, auf unbestimmte Zeit, sofernscan_history.recheck_after_daysnicht gesetzt ist.
Hinzugefügt
- Personio — ein explizites
personio: <slug>pinnt den Tenant. Viele Unternehmen betten ihr Personio-Board als iframe in eine gebrandete Karriereseite ein, sodasscareers_urlauf die eigene Domain des Unternehmens zeigt, während der Feed unter<slug>.jobs.personio.deliegt; ohne Pin lösen sich solche Boards ins Leere auf. Der Slug ist über eine verankerte Allowlist (/^[a-z0-9][a-z0-9-]{0,62}$/i) zeichensatzbeschränkt, und die resultierende URL durchläuft weiterhin das bestehendenew URL()-Parsing, die HTTPS-Prüfung und dasPERSONIO_HOST_RE-Host-Gate — der Pin ist nie ein Weg zu einem anderen Host. Ein Slug mit.,/,@,:,.., Leerzeichen, einem führenden-oder mehr als 63 Zeichen (dem DNS-Label-Limit) fällt auf das heutigeapi/careers_url-Verhalten zurück. Anders als beim Elternprojekt ist der Host-Regex von web-ui groß-/kleinschreibungsabhängig, weshalb der Pin übernew URL()normalisiert wird —AcmeGrouplöst zuacmegroup.jobs.personio.deauf, statt einen Host zu bauen, den die eigene Allowlist anschließend ablehnen würde. - Jobstreet — das optionale
appendWorkTypehängt an den Titel die Arbeitstypen der Anzeige an, z. B.Strategy Consultant [Part time], sodass Titel-Filter und Triage die Anstellungsart schon beim Scan sehen. Standardmäßig aus; ein fehlendes oder leeresworkTypesbleibt wirkungslos.
Anmerkungen
- Quellen- und Adapterzahl bleiben unverändert: 94 Quellen (89 EN + 5 RU), 89 EN-Adapter. Dieses Release bewegt nur die Testzahl, 3164 → 3201 (+13 Jobstreet, +7 Oracle Cloud, +9 Liveness, +8 Personio).
- Das Elternprojekt löschte
providers/telegram.mjsund konsolidierte auftelegram-channel. web-ui registriert beide als nutzerseitige Quellen, weshalb die Löschung nicht übernommen wird: Sie würde die Registry von 94 auf 93 senken und stillschweigend jeden brechen, dertelegramkonfiguriert hat. Nur als bewusste, angekündigte Entfernung erneut aufgreifen. jd-skill-gap.mjserhielt für seine Überschrift eine unkontrahierte Form von"what we are looking for"sowie eine Bereinigung der Fettdruck-Markierungen. web-ui reicht dieses Skript weiter, statt es zu spiegeln, sodass der Fix mit dem ausgerollten Elternprojekt ankommt und hier keine Änderung nötig ist.- Nicht portiert, mit Begründung:
lib/latex-escape.mjs,browser-extract.mjs,openai-eval.mjs,update-system.mjsund der Scaffolder (web-ui spiegelt sie weder, noch shellt es in sie hinein); die Go-Dashboard-TUI und dasweb/-Konfigurationsformular (eigenständige Oberflächen);doctor.mjsundanalyze-patterns.mjs(read-only Relays, deren Fail-Soft die Änderung abfängt). - Die kyrillische
\p{L}-Abweichung des Forks inproviders/telegram-channel.mjsüberstand den Upstream-Merge unverändert und wurde byte-genau gegen eine Kopie von vor dem Merge geprüft.
[1.236.0] — 2026-09-18
Eltern-Parität — career-ops main @ 6a9c84c (VERSION 1.33.0, 128 Commits). Zwei neue Quellen, dazu drei gespiegelte Fixes — zwei davon verloren hier still Anzeigen.
Hinzugefügt
Zwei Scanner-Quellen, 92 → 94 (89 EN + 5 RU). Python.org Jobs (pythonorg) liest den öffentlichen Feed des Boards; die Arbeitgeber-Zuordnung ist verpflichtend, sodass eine Zeile, deren Titel keinen Arbeitgeber nennt, verworfen statt mit dem Namen des Portal-Eintrags nachgefüllt wird. Generalist World (generalist-world) scrapt die Karten des Boards und schlägt bei Markup, das es nicht parsen kann, laut fehl — eine veränderte Kartenform wirft, statt sich wie ein leeres Board zu lesen, was genau der Fehlermodus ist, der eine gescrapte Quelle unzuverlässig macht. Beide erscheinen im Quellen-Filter auf #/scan, beide sind host-gepinnt (verankerte Regex, nur HTTPS), und beide werden end-to-end von Fake-Transport-Suiten getrieben (43 bzw. 36 Fälle).
Behoben
Eine in mehreren Städten offene Workday-Stelle wurde von einem Standortfilter verworfen. Der List-Endpoint von Workday beantwortet eine Anzeige mit mehreren Standorten mit einer Anzahl, wo jede andere Anzeige einen Ort trägt: "53 Locations". Das ist kein Standort, und buildLocationFilter matcht per groß-/kleinschreibungsunabhängigem Teilstring — eine in Austin und 52 weiteren Städten offene Stelle traf also auf keinen allow: [austin]-Eintrag und wurde verworfen, während dieselbe Stelle, einzeln gelistet, durchkam. Upstream über drei Tenants gemessen: 53 von 291 Anzeigen waren Platzhalter — das ist damit der Normalfall, kein Randfall. Die echten Orte der Anzeige existieren nur im CXS-Detail-Dokument, weshalb jeder Platzhalter jetzt ein zusätzliches GET dagegen kostet, gedeckelt auf 200 pro Eintrag — jenseits der Obergrenze bleibt der Platzhalter sichtbar, statt vorgetäuscht zu werden, denn eine ungelöste Anzeige darf niemals als gelöste erscheinen. Ein fehlgeschlagenes oder ortloses Detail lässt die Anzeige exakt so, wie die Liste sie zurückgab: Ein sichtbares "53 Locations" ist ehrlich darüber, was nicht aufgelöst werden konnte, während ein geleerter Standort jeden Filter still passieren würde. Dasselbe GET trägt auch startDate, sodass eine angereicherte Anzeige exakt datiert wird statt über die relative Prosa des List-Endpoints ("Posted 30+ Days Ago" → undefined); alles, was kein reines YYYY-MM-DD ist, wird unangetastet gelassen, statt durch Date.parse gejagt zu werden, dessen Fallback für Nicht-ISO-Formate implementierungsabhängig ist.
Ein angegebenes Level lässt zwei Requisitions nicht mehr zu einem Repost zusammenfallen. "Insurance Specialist II" ist die eigene Aussage des Arbeitgebers, dass diese Requisition nicht die Schwester der anderen ist — ein anderes Gehaltsband, ein anderer Zuschnitt und eine andere Req-Nummer unter demselben Basistitel. Der Tokenizer konnte das nicht sehen: w.length > 3 verwirft jede römische Ziffer bis VIII und jede einstellige Zahl, sodass "Insurance Specialist" und "Insurance Specialist II" identisch tokenisiert wurden, ein perfektes Jaccard-Verhältnis erzielten, und der Repost-Detektor die zweite als Neuauflage der ersten markierte. Das role-matcher.mjs von web-ui hatte überhaupt keine Level-Behandlung, weshalb dies den gesamten Mechanismus des Elternprojekts landet: Level werden extrahiert, wobei römisch und arabisch auf eine Zahl gefaltet werden ("Nurse II" und "Nurse 2" sind dieselbe Aussage — und genau dieses Paar unterschied zuvor fälschlich), zwei angegebene Level müssen übereinstimmen, und ein Level nur auf einer Seite gilt als lockere Umformulierung, außer wenn jeder Titel zusätzlich ein Wort trägt, das dem anderen fehlt — was zwei Widersprüche statt einer bloßen Ausschmückung sind.
Ein auf einen Filter gepinntes Avature-Board durchlief trotzdem das gesamte globale Board. Die Such-UI von Avature erzeugt opake Facetten-Parameter (?42386=[812132] ist ein Länderfilter), und ein Betreiber, der ein eingeschränktes Board in portals.yml pinnt, erwartet, dass der Scan genau dieses durchläuft. resolveSearch baute die URL als ${origin}${path} neu auf und verwarf dabei den Query-String komplett — sodass jede Anfrage, die erste eingeschlossen, das gesamte Board des Tenants durchlief: mehr Seiten, mehr Anfragen und Anzeigen, die der Betreiber bewusst herausgefiltert hatte. Facetten reiten jetzt bei jeder paginierten Anfrage mit, wobei unser eigenes jobOffset zuletzt angewendet wird, sodass eine versehentlich gleichnamige Facette die Paginierung nie kapern kann.
Sicherheit
devalue 5.8.1 → 5.9.2 (transitiv über astro), schließt die eine offene Dependabot-Warnung — ein Denial of Service bei fehlerhafter Eingabe. Reine Site-Abhängigkeit; kein Laufzeit-Code geändert.
Anmerkungen
Nicht portiert, jeweils mit Begründung. Das Elternprojekt gab lever ein 30s- und manfred ein 25s-Timeout gegenüber seinem gemeinsamen 10s-Standard — ein No-Port: Der Scan-Transport von web-ui erlaubt bereits 60s (DEFAULT_SCAN_TIMEOUT_MS), beide Boards haben hier also mehr Spielraum, als der Fix dort gewährt. Auch das verdoppelte careers--Präfix von iCIMS kann hier nicht auftreten: web-ui bezieht den Tenant-Ursprung direkt aus api/careers_url und konstruiert niemals einen Host aus einem Tenant-Namen; dessen neuer Fallback-Host-Durchlauf ist ein Feature für die lockerere Konfiguration des Elternprojekts, kein Defekt-Fix. deutschebahn und manfred erhielten Retry-bei-transientem-Abbruch, was die 60s-Obergrenze von web-ui deutlich weniger dringlich macht — vermerkt statt portiert, damit ein künftiges Timeout bei langen Durchläufen einen bekannten ersten Ansatzpunkt hat. Alles andere im 128-Commits-Delta ist CLI, Verifier, Dashboard oder web/ (das eigene Frontend des Elternprojekts): liveness-browser, check-jd-archive, verify-ats/verify-portals/verify-cv-facts, update-system, set-status, audit-portals, discover-ats, scaffolder, test-all, Plugin-Manifeste.
Fork-Abweichungen nach dem Pull erneut geprüft: der kyrillische \p{L}-Fix in providers/telegram-channel.mjs, das eigene providers/telegram.mjs des Forks, hermes in web/src/lib/clis.ts. Upstream career-ops-hq/main ist vollständig gemergt (0 fehlende Commits). 3066 → 3164 Tests; die Hilfe nennt jetzt 94 Adapter (89 EN + 5 RU) in allen 17 Bundles, und die Überschriftenstruktur bleibt unverändert bei 32 H2 / 122 H3.
career-ops-ui v1.267.1 · page 2/10 · mirror of the project changelog for cvstart.org