체인지로그

GitHub

릴리스별로 정리된 모든 주요 변경 사항입니다. 저장소에 실린 동일한 파일이며, 19개 언어 전체로 번역되어 있습니다.

[1.267.1] — 2026-11-04

수정

  • 릴리스 트레인 수리(v1.267.0의 CI 게이트) — PROJECT-CONTEXT의 테스트 카운트 체인이 더 이상 v1.267.0 헤드에서 끊어지지 않습니다(eval grader가 매칭하는 화살표 형식), QA 프롬프트가 재정리되고(릴리스당 라이브 프롬프트 1개, v1.266.0 아카이브), 파일별 커버리지가 98/90 ratchet 위로 올라갑니다: applicantpro salary 렌더러는 도달 불가능한 fallthrough을 잃고(salaryRangeOf는 증명 가능하게 undefined, {min}, {max} 또는 {min,max}만 산출), 일방향 ≥/≤ 분기가 fetch 흐름을 통해 고정되며, board-response-cache 스위트는 no-store 바이패스, 저장되지 않은 보드에 대한 호출자 제공 304, MAX_ENTRIES 축출을 고정합니다.
  • 테스트: 라이브 카운트는 7395입니다 (v1.266.0/1.267.0 웨이브 스위트가 이전 노트가 추적한 것보다 더 많은 스펙을 담고 있습니다 — 체인은 측정된 바닥값으로 다시 고정됩니다: 웨이브 후 7392, 이 패치에서 +3 게이트 테스트); 커버리지 98.51 라인 / 90.35 브랜치.

참고

  • v1.267.0 태그는 실패한 병합 커밋을 가리키는 채로 둡니다 — 재지정하지 않습니다(서버는 재지정된 태그를 거부합니다); v1.267.1이 배포 차량입니다.

  • v1.267.0 트레인의 패치 수리: CI 게이트는 녹색이고 코드 트리는 실질적으로 변하지 않았습니다.

[1.267.0] — 2026-11-04

추가

  • 네 개의 새 스캔 소스 — GermanTechJobs, Darwinbox, Keka, ApplicantPro/isolved (레지스트리 125 → 129 = 124 EN + 5 RU · 124 adapters) — career-ops @ 7d8feb6d 기준 두 번째 parent-sync 웨이브 (상류 커밋 87개). GermanTechJobs (#6346): germantechjobs.de/rss의 보드 전역 RSS (요청 한 번에 약 626개 항목); 고용주는 title의 마지막 " @ " 세그먼트입니다 — 이 세그먼트가 없는 title은 드롭되며 보드가 고용주로 기록되는 일은 없습니다; 명시적 provider: 선택만 지원합니다. Darwinbox (#6416): <tenant>.darwinbox.in 뒤의 무인증 alljobs JSON, 회사 id는 careers URL에서 나오며, POST 페이지네이션 워크 (50/페이지, max_pages 20/200) — 짧은 페이지에서의 정지는 소스 자체 행 개수에서 읽습니다. Keka (#6199): 두 번의 요청 — redirect:'manual' careers 셸의 스크립트가 피드 UUID를 밝혀주고 (알 수 없는 테넌트는 TenantNotFound.html로 302되어 이름 붙은 404로 읽힙니다), 이어서 열려 있는 전체 인벤토리를 하나의 embed-jobs 배열로 받습니다. ApplicantPro / isolved (#6252): 부트스트랩에서 숫자 domainId를 얻은 뒤 {success, data:{jobs, jobCount}}; jobCount > rows이면 목록이 불완전하다고 경고하고, 옵트인 applicantpro: { fetchDetails: true }는 250 ms 페이싱으로 JSON-LD에서 보강합니다.
  • ETag 보드 응답 재사용 (#6105) — 한 스캔 안에서 최근 15분 내에 가져온 보드 응답은 그대로 재사용되고, 더 오래된 응답은 If-None-Match로 재검증합니다 (304는 본문을 유지); 캐시 키는 method + URL + credential 헤더 + 본문을 커버하고, 겹치는 읽기는 진행 중인 프라미스로 접히며, CAREER_OPS_HTTP_CACHE=0 / _TTL_MS가 이를 오버라이드합니다. 스캔 런별 인메모리입니다 (web-ui에는 온-디스크 캐시 컨트랙트가 없습니다) — 기록된 편차입니다.

수정

  • 네 보드의 게시일 (#6274) — remoteok, remotive, workingnomads, IBM이 이제 게시일을 내보내 «Posted within» 필터가 그 보드에서 작동합니다; remotive의 naive 타임스탬프는 UTC로 정규화되고 (기계별로 틀어지는 나이 버그가 실제로 발생 중이었습니다), IBM의 _source는 이제 실제로 dcdate를 요청합니다.
  • portals.yml의 오타 키는 거부됩니다 (#6107) — EN 스캔은 어떤 fetch보다도 먼저 이름 붙은 에러를 throw합니다 (파일 + 키 + 가장 가까운 키 제안, Levenshtein ≤ 2); 조용히 titel_filter:를 무시하던 때와는 다릅니다.
  • getro는 오래된 featured 핀을 지나 계속 걷습니다 (#6406) — 핀된 오래된 공고는 여전히 드롭되지만 더 이상 페이지네이션을 끝내지 않습니다; 워크는 진짜 짧은 페이지에서 끝납니다.

참고

  • 의도적으로 하지 않음: RU 스캐너의 seen-필터는 언어를 인식하지 않고 RU 스캐너의 portals 파스 경로는 오타 키 게이트를 실행하지 않습니다 (웨이브 스펙에 핸드오프가 기록됨 — 아직 언어별 행을 내보내는 RU 보드는 없습니다); remoteok는 UTC 정규화 대신 오프셋이 붙은 날짜 문자열을 유지합니다 (라이브 형태가 결정적입니다). Tests 7096 → 7243; Playwright 173 pass / 0 fail / 1 opt-in skip; 레지스트리 129 = 124 EN + 5 RU · 124 adapters (모든 카운트 게이트를 살아있는 레지스트리에서 재생성).

[1.266.0] — 2026-11-01

추가

  • Personio 언어 버전들이 서로를 잡아먹는 일을 멈춥니다 (CAR-223: parent #4789 + #5044 + #5285) — 미뤄둔 스택을 자체 릴리스로 실행했습니다. 스캔 측 (#4789): scan_history.dedup_include_language: true (엄격한 옵트인 — OFF는 바이트 단위로 동일)이 seen-history 변형 메커니즘 (languageFormsFromUrl / recordUrlVariant / isUnseenLanguageVersion)을 통해 한 공고의 언어 버전들이 dedup에서 살아남게 만듭니다; 정준 키는 language/lang/locale 파라미터를 벗겨냅니다. preferred_languages (#5285): 공고의 버전 행을 선호 버전 우선으로 정렬합니다 (안정 정렬, 동순위에서는 provider 순서) — 스위치와는 독립적입니다 (OFF는 선호 버전을 유지하고, ON은 모든 버전을 그 순서로 유지합니다), validate-portals의 validatePreferredLanguages 포트가 함께 제공됩니다. personio fan-out (#5044): careers 페이지를 ?language=en 강제 없이 가져오고 (테넌트 자체의 ?language=xx는 존중됩니다) 언어 버전마다 language + ?language=<code> URL로 한 행씩 내보냅니다; dedupIncludeLanguage는 언어별 fetch를 추가합니다 (상한 5, 페이싱 적용); /xml과 job-link 폴백은 변함없습니다. recruitee는 같은 language 방출 + preferred_languages (opts 또는 엔트리)를 얻습니다. 공유 헬퍼들은 server/lib/language-preferences.mjs에 있습니다.

참고

  • 핸드오프는 기록됐고, 의도적으로 하지 않음: RU 스캐너의 seen-필터는 언어를 인식하지 않으며 아직 언어별 행을 내보내는 RU 보드는 없습니다; 클라이언트 url-key.js 미러와 portals-라우트 검증 연결은 열려 있습니다. Tests 7096 → 7203; 레지스트리 변경 없음 125 = 120 EN + 5 RU · 120 adapters.

[1.265.0] — 2026-11-01

추가

  • 두 개의 새 스캔 소스 — Apple, Bulldogjob (레지스트리 123 → 125 = 120 EN + 5 RU · 120 adapters) — career-ops @ fe857ecf 기준 parent-sync 웨이브. Apple (jobs.apple.com, #5496 + #6196): 사이트는 사용 가능한 JSON API가 없는 React Router SSR 앱이라 소스가 일반 검색 라우트의 하이드레이션 블롭(window.__staticRouterHydrationData)을 읽습니다; 발견은 엔트리 주도(apple.queries 관련도 정렬 텍스트 축 + apple.teams 슬러그 축, 기본값 없음 — 둘 다 없는 엔트리는 throw), 페이지는 max_pages(기본 2, 상한 50)로 제한되며 200 ms 페이싱, URL 세그먼트마다 서로게이트 안전 인코딩, Apple이 PIPE로 표시한 공고는 snippet에 «인재 파이프라인일 수 있음» 힌트를 실립니다 — 이를 기준으로 필터링하지 않습니다. Bulldogjob (bulldogjob.pl, #6253): 폴란드 테크 보드도 JSON API가 없습니다 — 목록 페이지가 __NEXT_DATA__(50/페이지, totalCount)를 싣습니다; 엔트리의 careers_url은 사용자가 사이트에서 만든 검색(필터는 경로에), 모든 요청에 명시적 /page,N이 붙고, 워크는 짧은 페이지, 보고된 total 또는 max_pages(기본 25, 상한 100)에서 멈추며 부분 보드에 잘림 태그를 붙입니다.
  • IBM 페이지네이션 리워크 (#6273) — 고정 600 레코드 워크가 max_pages(기본 20, 상한 300, 엔트리 오버라이드)로 바뀌고, 200 ms 인터페이스 페이싱과 fetchJsonWithRetry를 통한 페이지별 재시도; 첫 페이지 실패는 throw, 이후 페이지 실패는 수집분을 유지하며 페이지를 명명하고, 엔트리 자신의 상한에서 꽉 찬 페이지만 «raise max_pages» 경고를 냅니다.

수정

  • justjoin은 lastPublishedAt로 공고를 dating합니다 (#6254) — 후보 API의 publishedAt은 고용주가 공고를 새로 고칠 때마다 앞으로 이동해 보드 대부분이 오늘 날짜를 달고 있었고 max_posting_age_days가 몇 주 된 공고를 놓치지 않고 통과시켰습니다; 이제 더 이른 게시일이 이기며, 없거나 파스 불가하면 폴백합니다.
  • jobvite는 하나의 posting URL을 공유하는 행을 접습니다 (#6127) — 공개 피드는 하나의 detail URL에 도시별 <job> 행을 반환합니다; 파서는 첫 도시만 남기고 나머지를 중복으로 셌습니다. 이제 형제 행은 위치만 기여하며 feed 순서대로 ·로 연결되고, 첫 title/date가 유지되며, remote는 병합된 위치로 재평가됩니다.
  • Liveness: UltiPro 런그 (#6090) — UKG에는 공고별 JSON 엔드포인트가 없고 닫힌 공고에도 200으로 답해 57개의 닫힌 UltiPro 공고가 모두 uncertain으로 읽혔습니다. 상세 페이지는 레코드를 내장합니다: OpportunityIsClosed true/false → expired/active, «opportunity unavailable» 공지 → expired, 그 외는 브라우저에 맡깁니다. Built In의 «Sorry, this job was removed» 배너는 hard-expired (#6336) — 페이지가 전체 JD와 APPLY 컨트롤을 유지합니다; 이제 배너가 이기고, «sorry» 없는 형제 «Job removed» 배지는 매치되지 않습니다.

참고

  • 의도적으로 하지 않음: jobvite 수정의 parent 쪽 scan.mjs 위치 컴파일러 절반(web-ui는 USPS 컴파일러를 no-port로 문서화)과 Apple PIPE 힌트용 부모의 note 필드(web-ui job shape에는 note가 없음; snippet으로 이동). Tests 7096 → 7135 (+39); Playwright 173 pass / 0 fail / 1 opt-in skip; coverage gate ≥ 98/89; 레지스트리 125 = 120 EN + 5 RU · 120 adapters (모든 카운트 게이트를 살아있는 레지스트리에서 재생성).

[1.264.3] — 2026-11-01

수정

  • 레이트-리밋 강화 웨이브 (REVIEW-2026-10-31-v1.264.1의 리뷰 LOW №1–3) — writeRateLimit() (express-rate-limit@7.5.1, 60 req/min route×IP, 429 JSON + Retry-After)가 이제 47개 mutating 엔드포인트 전부를 커버합니다 (이전에는 2개): 잘못 스코프된 llmRateLimit 가드 9개를 교체하고 (/api/config — 바로 그 .env 재작성!, PUT /api/batch, /api/batch/merge, outcome, followup/seed, assessments, stats/snapshot, mock-interview/save, networking/save), 비용이 큰 경로에서 LLM 버킷 앞에 3개를 스택하고 (/api/evaluate, /api/deep, /api/auto-pipeline), bare 등록 33개를 연결했습니다 (CV/profile/modes 에디터, 모든 tracker 쓰기, pipeline POST/restore/DELETE, jds, 한 번의 루프 편집으로 잡히는 7개의 /api/run/*, posting-guard, Playwright-PDF 인라인, portals toggle/health/track, discover, reports, export/docx, two-pager, career-plan, memory, 그리고 DELETE 라우트 3개). /api/apply-helper는 allowlisted bare로 남습니다 (순수 로컬 계산, 고정됨); 16개 순수-LLM 라우트는 설계상 조건부 LLM 버킷을 유지합니다. 완전성 트립와이어 (tests/rate-limit-coverage.test.mjs, bare 27개 시점에서 red-first)는 앞으로 스로틀되지 않은 mutating 등록이 하나라도 등장하면 CI를 실패시킵니다.
  • TRUST_PROXY 옵트인 + 재무장된 검증 (리뷰 LOW №1–2) — app.set('trust proxy', N)는 TRUST_PROXY env 홉 카운트를 존중합니다 (불리언 true는 결코 아닙니다 — 그렇게 하면 공격자가 통제하는 왼쪽 XFF 끝을 키로 삼게 됩니다); 기본값은 미설정이므로, 루프백은 문서화된 소켓-키 기반 라우트별 열화를 그대로 유지합니다. validate: false는 표적화된 writeValidateConfig()가 됩니다: 옵트아웃되는 것은 xForwardedForHeader뿐이고 (trust proxy가 없으면 Caddy의 XFF는 설계상 무시됩니다), trustProxy를 포함한 나머지 모든 검증은 다시 시끄러워져서, 미래의 trust proxy: true 오설정이 침묵 대신 ERR_ERL_PERMISSIVE_TRUST_PROXY로 표면화됩니다. 커스텀 keyGenerator는 의도적으로 제거됐습니다: express-rate-limit는 프록시 검증을 기본 키 제너레이터에 묶어 두며, 라우트별 격리는 변함이 없습니다 (팩토리 호출마다 하나의 MemoryStore).
  • cvstart.org 카피 피드백이 2 s 후에 되돌려집니다 (CAR-141) — «Скопировано ✓» 상태는 이제 수용 기준인 2 s를 유지합니다 (이전에는 1.5 s), red-first 핀은 tests/site-copy-feedback.test.mjs에 있습니다. 이것으로 에이전트가 고칠 수 있는 마지막 CAR-141 기준이 닫혔습니다; 데모 비디오 새로 고침은 오너가 미룬 채 남아 있습니다 (결정 2026-11-01).

변경

  • Help 번들이 19개 언어를 말합니다 (×19) — §19 “Localizing the app”과 §32 “Run it from OpenWorker” 문장이 더 이상 두 로케일만큼 뒤처지지 않습니다: 모든 번들은 이제 19를 말하며 தமிழ்와 Tiếng Việt를 각자의 열거 스타일로 실어 나릅니다 (ar는 풀어 쓴 «التسع عشرة»를 씁니다), 그리고 vi.md §32의 «cả-17» 잔재도 함께 고쳐졌습니다. 구조 게이트 (33 H2 / 125+ H3)는 바이트-안정입니다.
  • 상태 위생 — AGENTS.md는 v1.264.2 / 7082 테스트를 말합니다 (이전에는 v1.86.x / 1566); PLAN.md는 릴리스 1–7을 Shipped로 표시하고 ship-train 단락은 docs ×19라고 말합니다; StatsBar.astro 코멘트는 이제 facts.adapters가 실제로 무엇을 담는지를 설명합니다 (123 = sources 총합, 소비자를 위한 역사적 키 이름은 유지); i18n.js 헤더는 19개 언어를 말합니다; v1.264.2 QA 프롬프트는 진짜 커버리지 (98.45/90.05)와 Playwright 산술 (176 = 174 harness + 1 opt-in 스킵 + 2 script-level 스위트)을 실어 나릅니다.

참고

  • 의도적으로 하지 않은 것: 기록된 포트-리스트에 따른 parent-sync 웨이브는 BACKLOG에 남아 있습니다 — 포크는 먼저 상류 a9476866을 지나 싱크해야 합니다 (포트-리스트 커밋 10개 중 7개가 아직 로컬 부모 트리에 없습니다; IBM/UltiPro/Bulldogjob/apple는 S–M 스코프, personio language-dedup은 자체 릴리스로 미뤄졌습니다). CAR-141의 데모 비디오 재녹화는 오너가 의식적으로 미뤘습니다 («видео не надо пока», 2026-11-01).
  • 테스트 7082 → 7096 (+9 커버리지 트립와이어 + 5 trust-proxy); Playwright 173 통과 / 0 실패 / 1 opt-in 스킵 (기존 케이스 안의 셰브론 핀 2개); 커버리지 98.46 라인 / 90.10 브랜치; evals 14/0/1skip; 레지스트리 변경 없음 123 = 118 EN + 5 RU · 118 adapters.

[1.264.2] — 2026-11-01

수정

  • 사이드바 그룹 헤더가 잘림 컨트랙트에 합류합니다 (CAR-219) — 그룹 토글 레이블 (.nav-v2-toggle > span)은 이제 말줄임표로 잘리고, CAR-203 내비 항목들과 같은 syncLabelTitles() 메커니즘을 통해 현지화된 툴팁을 실어 나릅니다; 셰브론은 줄어들지 않고, 토글은 클릭 가능하게 유지되며, RTL은 유지됩니다. red-first 핀은 tests/nav-v2.test.mjs에 있습니다 (+1 → 7082 테스트).

변경

  • cvstart.org 카피가 19개 언어 전부로 배포됩니다 (CAR-220) — meta.helpDesc, helpTeaser.c1.desc, faq.a2가 모든 사이트 로케일에서 번역됩니다 (v1.264.1에서는 설계상 EN 전용이었습니다); GitHub Pages가 업그레이드된 카피를 cvstart.org에 라이브로 배포했습니다.
  • 백로그 + 리뷰 위생 (CAR-221/222) — «상류에 병합되면 포트하기» 6개 항목이 닫혔습니다: 상류 PR 7개 전부가 병합됐고, 4개 항목은 이미 포트됐으며, 2개는 부모 측 몫입니다. docs/reviews/REVIEW-2026-10-31-v1.264.1.md가 반영됐고 (HIGH 0 / MED 0, LOW 후속 3개는 BACKLOG에 기록), parent-sync 포트-리스트가 기록됐습니다 (6 PORT / 3 VERIFY / apple-소스 갭; 상류 테일 118 커밋 @ VERSION 1.38.0).

참고

  • 변경 로그 미러-스탬프 수정 (92a05fcf)이 이 태그에 함께 탑승합니다: cvstart.org의 모든 페이지네이션 미러는 이제 생성 릴리스 버전을 실어 나르므로, site-build-after-fanout 그레이더가 마침내 스킵하는 대신 실제 로직을 실행합니다.
  • 의도적으로 하지 않은 것: 남은 mutating POST들의 스로틀링 (리뷰 LOW №3)과 trust-proxy 버킷 커플링 — 다음 강화 웨이브를 위해 BACKLOG에 기록됐습니다. CAR-141 (데모 비디오)은 오너의 몫으로 남아 있습니다.
  • 테스트 7081 → 7082 (+1 CAR-219 핀); Playwright 176; 레지스트리 변경 없음 123 = 118 EN + 5 RU · 118 adapters.

[1.264.1] — 2026-10-31

수정

  • 사이드바 필이 현지화된 긴 레이블을 우아하게 잘라냅니다 (CAR-203) — 내비 레이블 스팬은 이제 제대로 된 flex 시민입니다 (v2-nav.css의 min-width: 0 + overflow: hidden + text-overflow: ellipsis + nowrap), 그리고 nav-v2.js의 syncLabelTitles()는 같은 data-i18n 키를 통해 현지화된 레이블을 툴팁에 미러링합니다 (새 i18n 키 0개) — 긴 «Профинансированные компании»가 행의 overflow: hidden에 의해 하드-클립되는 대신 호버에서 다시 읽힙니다. 44 px 터치 타깃, 활성 페인트, RTL 미러는 그대로입니다; red-first 핀은 tests/nav-v2.test.mjs에 있습니다.
  • 모든 로케일에 하나의 480 s 평가 천장 (CAR-218) — remote-QA v1.264.0이 es와 zh-CN에서 일시적인 502 · 300 s · 0 ch 평가를 잡아냈습니다 (스팟 재실행은 통과했지만, 벽은 실재했습니다). EVAL_TIMEOUT_MS는 이제 480_000이고 evalTimeoutMs(lang)는 이를 무조건 반환합니다 — GEMINI_EVAL_TIMEOUT_MS와 auto-pipeline이 상속합니다. CAR-174의 로케일 인지 토큰 예산 (16384 / 32768)은 유일한 로케일별 분할로 남습니다.

변경

  • cvstart.org FAQ가 정확한 소스 카운트를 실어 나릅니다 (CAR-217) — {sources} job-board sources: {sourcesEn} international boards plus {sourcesRu} Russian ones가 라이브 레지스트리 (123 = 118 EN + 5 RU)에서 생성되는 facts.json에서 치환되므로, 숫자는 절대 드리프트할 수 없습니다; 여전히 이전 ~{adapters} 문구를 쓰는 로케일은 adaptersRounded를 통해 반올림을 유지합니다. EN 카피 업그레이드가 함께 탑승합니다 (meta.helpDesc, helpTeaser.c1.desc, faq.a2가 커맨드 메뉴, 되돌리기가 있는 그룹화된 파이프라인, 칸반 트래커를 이름으로 씁니다).

참고

  • 의도적으로 하지 않은 것: 사이트 i18n 카피 업그레이드 (meta.helpDesc / helpTeaser.c1.desc / faq.a2)는 이번 릴리스에서 EN 전용으로 남습니다 — 다른 로케일은 다음 docs 웨이브까지 정확하지만 더 둥근 문구를 유지합니다 (번역된 곳에서는 그들의 faq.a7가 정확-카운트 플레이스홀더를 얻습니다). CAR-141 (데모 비디오)은 오너의 몫으로 남아 있습니다; 대체된 car136/m141 브랜치는 아이디어가 Linear 태스크에 기록된 채로 삭제됐습니다 (태그 archive/car136-m141로 복구 가능).
  • 테스트 7041 → 7081 (CAR-203 핀 + CAR-218 핀 재작성); Playwright 176; 레지스트리 변경 없음 123 = 118 EN + 5 RU · 118 adapters.

[1.264.0] — 2026-10-31

추가

  • 인라인 트래커 상태 편집 — #/tracker의 모든 행 상태 배지가 정규 스테이지의 컴팩트한 <select>입니다 (GET /api/tracker/stages에서 오며, 하드코딩된 화이트리스트가 아닙니다). POST /api/tracker/status는 applications.md 락 아래에서 상위의 정규 set-status.mjs를 릴레이합니다 (states.mjs를 통한 상태 화이트리스트, --dry-run/--note 패스스루, 정화된 상세를 담은 fail-soft 502, argv-배열 스폰). 성공 시 행이 제자리에서 갱신되고 (톤 클래스 + 탭 카운트, 포커스 유지); 실패 시 되돌려지고 토스트합니다. 릴레이가 다룰 수 없는 행 — 손으로 편집된 알 수 없는 상태, 백필 센티널 # — 는 읽기 전용 배지를 유지합니다 (route-review LOW 수정; 리뷰어: HIGH/MED 발견 없음).
  • 모든 AI 페이지에 서버 경고 표시 — evaluate, deep, auto-pipeline (페이지 및 모달), career-plan, market, cv-studio (humanize + tailor)가 이제 서버의 warnings 배열 (출력 토큰 절단, 언어 드리프트, 날짜 수정)을 badge-warn 밴드 (role="status")로 렌더링합니다; auto-pipeline 모달은 done-프레임 경고를 더 이상 드롭하지 않습니다. 새 키 llm.truncatedNotice ×19.
  • 커버리지 웨이브 (+111 테스트) — routes/map.mjs 100/94.9, geocode.mjs 96.3/92.9, content.mjs 99/84.9, networking.mjs 96.4/89.1, sources/habr.mjs 100/98.2 (새 tests/cov-*-routes.test.mjs ×3, tests/sources-habr.test.mjs); 브랜치 하한은 ≥ 90 % 유지.
  • Linear CAR-191…216 클로저 번들 (docs/reviews/2026-10-30-linear-car191-216-closure-bundle.md) — CAR별 커밋 SHA와 증거 코멘트를 갖춘, 붙여넣기 준비가 끝난 오너 마무리 문서.

수정

  • Trudvsem 스캔 소스 — 빈 페이지 (results: {}, 「끝을 지났을」 때도 마찬가지)가 더 이상 스캔 전체를 죽이지 않습니다 (깨끗한 0행); API 오류 봉투 (status: 500을 담은 HTTP-200)는 API 자신의 텍스트로 throw합니다; 페이지네이션은 레코드 오프셋을 걷습니다 (이전에는 페이지-인덱스였습니다 — 페이지가 조용히 중복됐고, 수백 개 대신 ~52개 공고); 걷기는 API의 딥-오프셋 실패 구간 (offset × limit ≥ 10000) 바로 전에서 멈춥니다. 라이브 엔드-투-엔드 검증 완료.
  • habr — 제목/회사는 엔티티 디코딩 후에 태그가 제거됩니다 (hh.mjs 패리티): 카드 제목의 &lt;script&gt;가 살아있는 마크업으로 스캔 행에 도달할 수 없습니다; 제목의 정당한 >>는 살아남습니다.
  • PUT /api/profile (raw 경로) — 비어 있지 않은 full_name이 없는 candidate: 블록은 거부됩니다. 필드-병합 경로와 같은 라인입니다 (이전에는 full_name: null을 영속화했습니다).
  • 힌트의 정직함 — .doc이 CV 업로드 accept 목록과 cv.uploadHint ×19에서 제거됐습니다 (파서는 한 번도 지원하지 않았습니다); config.openaiModelHint ×19는 gpt-5를 이름으로 씁니다 (서버는 모든 *-codex id를 그것으로 매핑합니다); config.anthropicModelHint ×19와 Anthropic 기본값은 이제 상위의 티어를 반영합니다 — 기본 claude-sonnet-5-5, 프리미엄 claude-opus-5-5.
  • Claude Code 문서 링크 → code.claude.com (이전 docs.anthropic.com/en/docs/claude-code URL은 links 게이트에서 TypeError를 냅니다).
  • help ×19: 행-밀도 불릿은 사라졌고 (토글은 v1.263.0에서 UI를 떠났습니다) 18개 계열의 깨진 **bold** 스팬이 수리됐습니다 — 게다가 렌더러-누수 클래스가 ×19로 수정됐습니다 (테이블-뒤-단락마다의 두 번째 빈 줄; 볼드 셀에서 나가는 파이프 글리프): 모든 19개 help 번들에서 렌더 시 리터럴 ** 0개.

참고

  • Anthropic 기본 모델이 변경됐습니다 — claude-sonnet-5-5 (상위의 스탠다드 티어). 이전 조용한 기본값 (claude-sonnet-4-6)에 의존하던 배포는 자동으로 업그레이드됩니다 — 옵트아웃하려면 .env에 ANTHROPIC_MODEL을 고정하세요.
  • 의도적으로 하지 않은 것: 설계상 열어둔 두 LOW 리뷰 지적 (인라인 셀렉트는 칸반 movesReady 성능 저하를 미러링하지 않습니다 — 모든 실패에서 정직한 토스트가 선택된 동작입니다; code: 'timeout' 브랜치는 30 s 초과 테스트가 아니라 fake-writer 컨트랙트가 계속 커버합니다). CAR-141 (데모 비디오)은 오너의 몫으로 남아 있습니다.
  • help ×19는 v1.263.0 출시 후 main에서 릴리스-후 산문 패스 (밀도 불릿 제거, 렌더러-누수 클래스 수정)를 받았습니다 — 이 릴리스가 그것을 담는 첫 태그입니다.
  • 이 태그에 추가로: 증거 칩 파이프라인(리포트 파서 + 뷰 배지), fix-report-links 러너(POST /api/runners/report-links, #/health의 «복구» 카드), 사용량 기록이 포함된 배치 비용 추정/확인 — 브랜치 컷과 태그 사이에 main에 반영되었습니다.

Tests 6905 → 7041 (+136) · Playwright 176 (173 통과 / 1 opt-in 스킵) · 커버리지 98.5 % 라인 / 90.1 % 브랜치 · 레지스트리 변경 없음: 123 sources = 118 EN + 5 RU · 118 adapters.

[1.263.0] — 2026-10-30

추가

  • 모션 폴리시 레이어 — 새 motion.css(앱의 마지막 시트): 모달/팝오버 등장(scale .97 + 페이드, 130–180 ms), 토스트 스프링-인 + 소프트 퇴장 상태, 테마 토글 누름 스프링, 칩/탭 누름 scale(.97), 스태거 인프라(50 ms 스텝, 상한 12), .page-enter 라우트 전환 페이드(라우터), 칩/탭의 포커스-할로 전환. 모든 규칙은 prefers-reduced-motion 세이프입니다(움직임은 none으로 떨어지고, opacity/color는 유지).
  • 터치 정확성 — 68개의 호버-리프트 규칙이 무방비였습니다: 터치 기기는 탭 뒤에 들러붙은 “들어 올려진” 카드를 유지했습니다. 이제 모든 transform 호버는 @media (hover:hover)로 게이트되고(motion.css 뉴트럴라이저), 카드 리프트는 진짜 인터랙티브한 카드에만 적용됩니다(.card:has(a), a.card, button.card, [role=button].card) — 정적 카드의 가짜 어포던스는 끝입니다.
  • A11y: 모든 컨트롤이 프로그래매틱 이름을 갖습니다. 새 A11yDump 헬퍼가 16개 readonly LLM-출력 덤프 전부에 이름을 붙입니다; 플레이스홀더뿐이던 12개 필드(CV Studio ×6, config ×2, scan 검색, tracker 점수 셀렉트, outcome 노트, plan 에디터, auto URL, 버그 리포트)에 aria-label이 붙었습니다; a11y-form-wires 게이트가 이제 이를 클래스 전체로 강제합니다(이름 없는 사이트 26곳 → 이제 0 + 이유가 문서화된 allowlist).

수정

  • #/scan: 스티키 테이블 헤더가 스크롤로 아래로 들어간 뒤 탑바 검색 위에 더 이상 덧그리지 않습니다(z가 탑바와 같았고 뒤의 DOM이 이겼습니다 — 이제 토큰 파생으로 그 아래에); “Зарплата от/до” 입력이 정상 크기로 돌아왔습니다(.v2-scan-bar .input flex 규칙이 팝오버의 column flex로 새어 flex-basis 200 px를 HEIGHT로 설정했습니다 — 이제 > 스코프 + 팝오버 가드); 밀도 토글 버튼은 오너 결정으로 제거됐습니다(배관은 유지, i18n 키는 ×19 제거).
  • Deploy 워크플로 강화 (F1–F6): provider 입력이 어떤 셸 splice보다 먼저 문자셋 검증을 거칩니다; hermes 진단이 값을 redact합니다(세 싱크 모두에 대소문자 무시 키 필터 + 값 마스킹 + 80자 절단, ?token=도 마스킹); hermes 단계가 inputs.app_dir를 존중합니다; public_url은 https 게이트; 안티-다운그레이드 가드에 실행 가능한 핀 6개(기본값 없음, 가드가 Deploy보다 먼저, 엄격한 refs 정규식).
  • 리뷰 꼬리, red-first: CSV 가져오기 백슬래시 왕복(재가져오기가 다시 dedup됩니다), cv-voice 헤드-스트립 4827 ms → 13 ms, statedYearsMin 지연 계산, posting-guard 캡처 TOCTOU, MiniMax CN 와일드카드, deel 토큰 스킵, 가져오기 픽커가 읽기 전에 크기 가드. 가져오기 모달: nav 가드 무장, cancel에서 파일 입력 제거, 가드된 중복 읽기, reduce를 통한 num.
  • 오너 UX 배치: 대시보드 히어로 수직 리듬(인라인 레벨 자식이 하나의 라인 박스로 붙었습니다); help TOC −8 px 활성 행 시프트(행별 인라인 거터 + 숫자 열); 모바일 드로어(보이는 ✕ 닫기, scrollTop 리셋, 유령 –paper 페이드는 .has-hud로 게이트); docs-assistant 페이지가 답변 안의 긴 URL로 더 이상 넘치지 않습니다(그리드 min-width 체인 + overflow-wrap: anywhere); «Недавние»(최근 항목) 설명(힌트) + 지우기 가능; career-plan Save가 실패한 로드 뒤에 다시 무장됩니다; evaluate URL-프리필이 붙여넣기 플레이스홀더에 LLM 호출을 태우지 않습니다; auto-pipeline Enter가 IME 조합을 존중합니다.
  • 대비 (WCAG 감사, 296쌍): docs-fab 그라디언트 2.43 → 6.4:1, toast.success → ok-soft 쌍(1.75 → 다크 AA), 배너 btn-dark → --ink 플립, scan-logbtn/reposts-badge/fab-chip/close 라이트 변형, 그리고 폼 컨트롤 보더를 3:1로(--cv3-input-line, WCAG 1.4.11 — 이전 1.2:1).
  • 비주얼 스윕: tracker 빈 상태가 전체 폭 카드입니다(390에서 760 px 테이블 안쪽으로 잘려 있었습니다); scan 콘솔 라인이 반쯤 잘리는 대신 말줄임표를 칩니다; 플로팅 채팅 FAB가 390에서 콘텐츠를 더 이상 덮지 않습니다(44 px 타깃, inset 12); 대시보드 «Application statuses»(지원 상태) 빈 상태가 v2-empty 패턴을 따릅니다; stats «?» 힌트에 디바이더 + 44 px 타깃; career-plan Input 탭이 결과가 어디에 떨어지는지 설명합니다; help §1 마크다운(깨진 볼드 스팬 18개).

변경

  • 액센트 블루 → 크림슨 레드 (오너 요청, 로고와 일치, hue 15) — cv3 토큰 5개 × 테마 블록 3개; docs-assistant 채팅 버블 텍스트는 --accent-ink를 탑니다(리터럴 #fff였고 다크에서 2.39:1); funded 워시는 --accent-soft를 탑니다. Danger는 hue 25로 자체 처리를 유지합니다.
  • 문서: README 테스팅-테이블 카운트 ×17(v1.248 시대의 드리프트), help §1 + 마크다운 스팬 18개, help fr/it/ta «118», 벤더 가격 CHECKED_ON 2026-10-30, HANDOFF/CONTEXT/TRACEABILITY 사실.

참고

  • 의도적으로 하지 않은 것: 스켈레톤 스윕 재작업은 밖에 둡니다(car112 컨트랙트가 현재 페이드를 그대로 고정합니다); 테마 토글 아이콘 크로스페이드는 display 스왑으로 유지됩니다(테마 토글 Playwright 컨트랙트가 그것을 관찰합니다); .btn:active scale은 중복하지 않았습니다(app.css가 소유합니다); 모션 스태거는 인프라일 뿐입니다 — 대시보드 타일이 나중에 채택합니다; help ×19 산문(밀도 언급, §1 마크다운, «118»)은 다음 팬-아웃에 동승합니다.

  • 전체 remote-QA 증거: v1.262.4에서 ja 표적 그린(376 s / A–G ✓); en/it은 토큰 밀도 시대의 이른 시점에 그린.

  • 테스트 6869 → 6905 (+36) · Playwright 176 · 커버리지 98.4 % 라인 / 89.9 % 브랜치 · 레지스트리 변경 없음: 123 sources = 118 EN + 5 RU · 118 adapters.

[1.262.4] — 2026-10-30

수정

  • ja가 토큰 밀도가 높은 평가 로케일에 합류 (ja → 32768 토큰 천장 + 480 s 클록). prod v1.262.3의 5-로케일 표적 재실행이 그날 밤의 네 502 · 300 s · 0 ch 타임아웃을 일과성 원인과 결정론적 원인으로 갈랐습니다: ar(250 s), ko(291 s), zh-CN(259 s)은 재실행을 통과했고, ru의 일회성 언어 드리프트는 재발하지 않았습니다 — 하지만 ja는 출력 없이 표준 천장을 두 번 쳤습니다(전체 ×19 실행 40981578549와 표적 41012725839). it/pl과 같은 형태 클래스입니다. Red-first 핀: evalMaxTokens('ja') = 32768, evalTimeoutMs('ja') = 480_000, 나머지 로케일은 300 s를 유지합니다.

참고

  • 의도적으로 단일 목적의 패치입니다: ja 수정이 이 릴리스가 실리는 유일한 변경이며 — 그 외 아무것도 실지 않습니다. 그날 밤의 증거 기준으로 ar/ko/zh-CN은 표준 천장에 머뭅니다(통과; 그날 밤의 실패는 프로바이더 부하의 일과성 문제였습니다), ru의 드리프트는 일회성 생성 플레이크였습니다.

  • 테스트 6868 → 6869 (+1) · Playwright 176 · 레지스트리 변경 없음: 123 sources = 118 EN + 5 RU · 118 adapters.

[1.262.3] — 2026-10-30

수정

  • pl이 토큰 밀도가 높은 평가 로케일에 합류 (pl → 32768 토큰 천장 + 480 s 클록). prod v1.262.1의 전체 remote-QA가 폴란드어 평가가 표준 천장에서 죽는 것을 잡았습니다 — 502 · openrouter · 300 s · 0 ch — 그리고 v1.262.2와 함께 디스패치된 표적 재실행이 그것을 정확히 재현했습니다(실행 40960568766과 40968958062): 18/19 로케일이 A–G를 통과했고, it은 토큰 밀도 480 s 클록 안의 302 s로 끝냈으며, pl은 출력 없이 두 번 벽에 부딪혔습니다. 결정적 타임아웃이지 플레이크가 아닙니다 — v1.262.1에서 it을 옮긴 바로 그 형태 클래스입니다. Red-first 핀: evalMaxTokens('pl') = 32768, evalTimeoutMs('pl') = 480_000, 18개 표준 로케일은 300 s를 유지합니다.

참고

  • 의도적으로 단일 목적의 패치입니다: pl 수정이 이 릴리스가 실리는 유일한 변경입니다 — 그 외 아무것도 실지 않습니다; v1.262.2 리뷰-꼬리 콘텐츠는 별도로 나갔습니다.

  • 테스트 6867 → 6868 (+1) · Playwright 176 · 레지스트리 변경 없음: 123 sources = 118 EN + 5 RU · 118 adapters.

[1.262.2] — 2026-10-30

수정

  • 웨이브 리뷰 꼬리 — 낮은 심각도 발견 7건, 각각 red-first로. CSV 가져오기 백슬래시 왕복: 셀 안의 \가 쓰일 때 두 배로 적히고 읽을 때도 두 배로 읽혀, 같은 CSV를 다시 가져오면 company+role dedup이 빗나가 중복 행이 생길 수 있었습니다 — 리더가 이제 split 뒤에 \\를 언이스케이프합니다(쓰기→읽기 왕복은 무손실, 재가져오기는 dedup). cv-voice <head> 제거가 닫히지 않은 태그에서 이차 시간이 걸렸습니다 — 라우트가 상한을 둔 64 KB 입력에서 4827 ms → 13 ms. statedYearsMin는 years_min이 설정되지 않았어도 모든 소스의 모든 채용에 대해 연수 파서 전체를 돌렸습니다 — 이제 지연 실행되어 대형 스캔당 수 초의 CPU가 사라졌습니다. readdir과 statSync 사이에서 삭제된 캡처가 posting-guard 목록을 크래시시켰습니다(TOCTOU — 사라진 파일은 건너뜁니다). MiniMax CN 기본값이 api.minimax.cn과 정확히 일치할 때만 매칭됐습니다 — 이제 모든 *.minimax.cn 베이스 URL이 CN 모델을 받습니다. 16진수 ref가 아닌 Deel $ 토큰이 더 이상 리터럴로 스니펫에 새지 않습니다. 가져오기 픽커가 file.text() 전에 file.size를 검사합니다 — 512 KB 서버 상한이 브라우저가 어쩌면 거대한 파일을 파싱한 뒤에야 413으로 답했습니다 — 현지화된 import.tooLarge 토스트 ×19와 함께.
  • Deploy 워크플로 강화 (워크플로 리뷰 F1–F6). provider는 문자셋 검증 없이 원격 셸에 보간되는 유일한 입력이었습니다 — 값 안의 '가 인용을 탈출해 prod에서 임의 코드를 실행할 수 있었습니다; 이제 어떤 splice 전에 ^[A-Za-z0-9_-]+$ 화이트리스트를 거치고, 키 가드 grep이 더 이상 false-pass할 수 없습니다. hermes 진단 단계가 원본 journalctl + unit 덤프를 공개 로그에 출력했고, 필터가 대문자 *_API_KEY=를 놓쳐 아무것도 마스킹하지 않았습니다 — 이제 세 싱크 모두에 대소문자 무시 공유 redact + 값 마스킹 + 80자 절단이 적용되고, ?token= 쿼리 문자열도 마스킹됩니다. hermes 단계는 inputs.app_dir를 무시하고 /opt/career-ops/src/.env를 하드코딩했습니다. public_url은 https 검사 없이 curl에 도달했습니다(선두 -를 통한 옵션 인젝션). 그리고 안티-다운그레이드 가드 — v1.238.0 사건의 수정 — 는 실행 가능한 게이트가 없었습니다: ci-workflows에 핀 6개가 새로 생깁니다(refs에 기본값 없음, 가드가 Deploy보다 먼저 실행, 엄격한 refs 정규식).
  • 가져오기 모달 꼬리 (SPA 리뷰). 모달이 outcome 모달과 같은 nav 가드를 무장합니다 — 다른 곳으로 이동해도 더 이상 다른 뷰 위에 고아로 남지 않습니다. 숨은 파일 입력이 change뿐 아니라 cancel에서도 제거됩니다 — 반복적인 닫기가 더 이상 document.body에 입력을 새지 않습니다. plan.duplicates는 미리보기에서 가드된 형태로 읽힙니다. 트래커 num 할당이 Math.max(...spread) 대신 reduce를 사용합니다 — spread는 ~100k 행에서 인수 한도에 도달합니다.

변경

  • 문서: 도움말 가이드의 «118 EN 어댑터» 불릿이 fr/it/ta에서도 카운트를 실어 나릅니다(다른 15개 로케일은 이미 갖추고 있었습니다). cvstart.org의 벤더 가격이 공식 가격 페이지와 재검증됐습니다 — 넷 모두 불변, CHECKED_ON → 2026-10-30. 릴리스 후 드리프트 스윕이 README ×19, CONTEXT.md, CLAUDE.md, SDD 매뉴얼에 걸친 스테일 카운트/버전 라인 42건을 고쳤습니다(109/104 시대와 v1.262.0 시대의 잔여물).

참고

  • 의도적으로 하지 않은 것: cv-studio의 네이티브 window.confirm(CAR-179 시대)은 유지됩니다 — UI.confirm으로 바꾸는 것은 후속 작업이지 패치 항목이 아닙니다; <mark title> 상투 표현 툴팁은 툴팁 전용으로 유지됩니다(수용 가능한 a11y 바닥); remote-qa.yml은 cancel-in-progress: true를 유지합니다 — 재디스패치는 실행 중인 회귀를 의도적으로 대체합니다.
  • prod v1.262.1의 전체 remote-QA: routes ×19 ✓ · site ×19 ✓ · links ✓ · 라이브 LLM 18/19 로케일 A–G ✓ — pl이 단 한 번의 502 · 300 s · 0 ch 프로바이더 타임아웃을 맞았습니다(토큰 밀도 영역이 아님; 표적 재실행은 이 릴리스에 동승합니다).
  • 테스트 6841 → 6867(6864 pass / 0 fail / 3 skip, +26 vs v1.262.1) · Playwright 176 · 커버리지 98.42 % 라인 / 89.94 % 브랜치 · 레지스트리 변경 없음: 123 sources = 118 EN + 5 RU · 118 adapters.

[1.262.1] — 2026-10-29

이탈리아어 평가 패치: OpenRouter 시대에 it이 SCORE_SUMMARY 블록 바로 앞에서 보고서를 자르는 모습이 붙잡히면서, it이 토큰 밀도가 높은 로케일(32768 토큰 상한 + 480 s 클록)에 합류합니다 — 그리고 진단을 가능하게 만든 프로바이더 전환 도구가 deploy 워크플로에 실립니다.

수정

  • SCORE_SUMMARY 앞에서 잘린 it 평가 (CAR-190 꼬리): remote-QA 40928864980(크레딧 충전 후 첫 실행) — it 평가가 264 s를 달리고 16,384 상한에서 tok ~15635로 15,045자로 돌아왔습니다: A–G 블록은 온전, ---SCORE_SUMMARY---는 사라짐. v1.261.1(CAR-174)에서 ta에게 상한을 안겨준 바로 그 형태 클래스입니다. it이 TOKEN_DENSE_EVAL_LOCALES에 합류합니다(ta, hi → ta, hi, it): 32,768 토큰 예산과 480 s 클록이 모두 적용되며, /api/evaluate + auto-pipeline은 llm.mjs의 단일 진실 공급원에 머뭅니다.
  • 블록 없는 en 보고서 — 일회성 생성 플레이크: 같은 실행에서 단 하나의 en 평가가 언어는 맞으면서 A–G 블록이 하나도 없이 돌아왔습니다; 표적 재실행은 깨끗하게 통과했습니다(21,091자, A–G ✓). 토큰 밀도 집합에는 추가하지 않습니다 — 플레이크 한 번은 역사가 아닙니다.

추가

  • Deploy 워크플로: mode=hermes와 mode=provider (CAR-190 운영 꼬리): mode=hermes는 Hermes 게이트웨이를 재시작하고 전체 진단을 출력합니다(시크릿을 제거한 unit 파일, 전/후 리스너, sudo 저널, 포트별 프로브, 앱 자체의 HERMES_BASE_URL 뷰, OpenRouter 크레딧 잔액); mode=provider는 키 존재 가드를 갖추고 LLM_PROVIDER를 전환하며(대상 프로바이더의 *_API_KEY가 없으면 거절) 앱을 재시작합니다 — Hermes 크레딧이 도중에 바닥났을 때 prod가 이렇게 openrouter로 옮겨갔습니다. deploy-mode ref 가드는 mode=deploy에 대해서만 검증되므로 recon/verify 디스패치도 다시 동작합니다(v1.261.3 시대의 필수 입력 변경이 이들을 잠재웠습니다).

참고

  • 의도적으로 하지 않은 것: en은 토큰 밀도 집합에 추가되지 않습니다 — 블록 없는 보고서 한 건은 생성 플레이크이지 볼륨 역사가 아닙니다.
  • 의도적으로 하지 않은 것: ta 강화 언어 지시문은 it으로 확장되지 않습니다 — 이탈리아어에서 언어 드리프트는 관측되지 않았습니다.
  • 의도적으로 하지 않은 것: 새 UI-v2 데모 영상 촬영은 오너에게 남습니다(CAR-141) — 랜딩은 어차피 이제 youtube-nocookie.com으로 임베드합니다.
  • 테스트 6841 → 6841(릴리스 브랜치).

[1.262.0] — 2026-10-29

업스트림 기능 마이너: Deel 구직 보드가 스캐너에 합류하고(레지스트리 123), 다른 트래커의 CSV를 #/tracker로 바로 가져오며, posting-guard의 JD 무결성 검사와 CV 보이스 검사가 릴레이/린트로 들어오고, 스캐너가 경력 연수 필터를 익히며, Apply 버튼이 7개 언어 더에서 누락으로 읽히지 않습니다 — 더해 MiniMax의 은퇴한 기본 모델이 교체되고 리포스트가 소스 간에 조작되지 않습니다.

추가

  • Deel 구직 보드 소스 (CAR-184): 레지스트리 123 = 118 EN + 5 RU · 118 어댑터 — jobs.deel.com/<slug> 테넌트 보드를 자동 감지합니다(Klarna 등); Next.js flight 페이로드가 재고 전체를 한 요청에 실어 옵니다(제목, 위치, 근무 형태, 날짜, 설명), 허용되지 않은 /api/ 경로는 건드리지 않으며, 채용을 다른 곳에서 서빙하는 테넌트가 돌려주는 리다이렉트를 소스는 이름으로 거절합니다; 보상은 의도적으로 매핑하지 않습니다(지급 주기 없는 금액은 추측일 뿐입니다); 카운트 게이트 ×5 + 도움말 §17 ×19.
  • 트래커 CSV 가져오기 (CAR-181): dry-run 미리보기와 잠긴 커밋을 갖춘 POST /api/tracker/import-csv — RFC-4180 인용, ISO / 일-우선 / 월-이름 날짜 형식, 외부 트래커의 단계를 정준 상태로 접어 넣습니다(알 수 없는 단계는 Evaluated로 노트와 함께 들어오고 절대 버려지지 않습니다), markdown 링크 셀이나 Notes 열에서 읽은 정규화된 공고 URL로 dedup, 제한 512 KB / 2000행; #/tracker에 미리보기 모달이 있는 CSV 가져오기 버튼이 생기고, i18n ×19.
  • posting-guard 릴레이 (CAR-182): POST /api/runners/posting-guard — 부모 프로젝트의 결정론적 zero-LLM JD 무결성 게이트로, jds/ 캡처 접두사 규칙(패딩 여부와 무관하게 정수로 비교)이나 명시적으로 살균된 경로로 보고서 번호를 해석합니다; 자식은 argv만 받고, 30 s 상한과 fail-soft 계약을 갖습니다; #/reports/:slug의 «Posting guard» 액션이 결과를 렌더링하고, i18n ×19.
  • CV 보이스 검사 (CAR-188): 상투 이력서 문구 27개(passionate about, proven track record, leveraged, spearheaded, delve…)와 대시 휴리스틱(2022–2024 같은 연도 범위는 신호가 아니고, 20–30%는 신호입니다)을 읽기 전용 POST /api/cv-studio/voice-check와 정확한 일치를 하이라이트하는 #/cv-studio 카드로 제공; Tailor 체크리스트는 6개 불변식으로 늘어나고, i18n ×19.
  • 경력 연수 스캔 필터 (CAR-180): jd-years 파서(65가지 표현, 확실하지 않으면 절대 떨어뜨리지 않음)가 공고 설명에 명시된 최소 연수를 읽고, portals의 새 옵션 years_min:은 자격 미달 공고를 skipped_experience로 건너뜁니다(URL을 고정하지 않는 관찰형 히스토리 상태입니다), yearsMin은 미래 UI를 위해 스캔 스냅샷을 타고 갑니다.
  • 7개 언어 더의 Apply 컨트롤 (CAR-185/189): 베트남어 Nộp đơn ứng tuyển, 불가리아어 Кандидатствай/Кандидатстване/Външно кандидатстване(명사를 라벨 시작에 고정 — 조언 기사 링크는 Apply가 아닙니다), 네덜란드어 Solliciteer/Solliciteren, 스페인어 ¡Apúntame!/Postularse(동사가 아니라 라벨 — FAQ 링크는 더 이상 문제가 되지 않습니다), 중국어 申请职位/投递(컨트롤 전체에 고정) — 이 보드들의 살아 있는 공고는 더 이상 no_apply_control로 읽히지 않습니다.

수정

  • MiniMax 기본 모델 (CAR-187): 은퇴한 MiniMax-Text-01 기본값은 api.minimax.io에서는 MiniMax-M3.1-Flash로, api.minimax.cn에서는 MiniMax-M3로 교체됩니다; 베이스 URL은 벤더 핀(비벤더 호스트는 런타임에 거절되고 키가 그곳에 도달하지 않습니다); 설정 힌트 ×19가 새 기본값을 이름으로 밝힙니다.
  • 리포스트는 같은 소스를 요구 (CAR-186): LinkedIn과 고용주의 Greenhouse에 각각 올라온 한 역할이 더 이상 리포스트로 보고되지 않습니다 — 클러스터가 이제 sourceHost()로 갈라지며, 이는 알려진 사이트에서만 지역 서브도메인을 접고(de.linkedin.com ≡ www.linkedin.com, 그러나 ca.example.com은 따로 유지), Greenhouse 호스트 별칭을 한 소스로 접고, 뒤따르는 점을 제거합니다.
  • 타밀어 평가 언어 드리프트 (CAR-183): 강화된 타밀어 지시문이 이제 볼륨 예산 옆(CAR-174 슬롯)의 휘발성 프롬프트 꼬리에 앉습니다 — v1.261.2에서 보였던 간헐적 드리프트 이후 remote-QA 보고서가 다시 타밀어로 착지합니다.

참고

  • 의도적으로 하지 않은 것: posting-guard 릴레이는 fail-soft로 출시됩니다 — 포크에는 아직 부모 프로젝트의 posting-guard.mjs가 들어 있지 않습니다(업스트림 PR #6146가 아직 열려 있음). 부모 동기화가 이를 가져올 때까지 엔드포인트는 {available:false, reason:"script-not-found"}로 응답합니다.
  • 의도적으로 하지 않은 것: yearsMin은 이번 릴리스에서 #/scan UI 칩으로 노출되지 않습니다 — 필드는 스냅샷에 실리고, 필터는 portals 설정 전용입니다.
  • 의도적으로 하지 않은 것: 업스트림 modes/reports의 Machine Summary 변경(#6161/#6160/#6157)은 포트되지 않았습니다 — web-ui 미러가 아니라 배포된 부모 프로젝트와 함께 도착합니다.
  • 테스트 6734 → 6841(릴리스 브랜치); 스캐너의 dedup 키는 그대로입니다(URL 정규화 동작은 바뀌지 않았습니다).

[1.261.3] — 2026-10-28

디자인 완성 패치: Lovable 디자인 3단계가 밀도(테이블 행 32, 버튼 30, 칩 24px)·코랄 잔여물 퇴출·애니메이션 --ease-out·빈 TOKEN_HANDOFF로 마무리되고, Trakstar Hire 소스가 레지스트리를 122 = 117 EN + 5 RU · 117 어댑터로 밀어올리며(CAR-177), 스캔이 호스트-풀로 한 번에 최대 32개 회사(호스트당 10개)를 걷고(CAR-178), 업스트림 프로바이더 위치 수정 6건과 http-json spent-timeout 재시도 금지가 들어오며(CAR-176), CV Studio가 되돌리기 + 미저장 가드를 ×19로 얻고(CAR-179), 최종 회귀 P3가 지도 중간-잉크 AA 4.68:1과 skip-link 악센트 링을 닫습니다 — 더해 문서 패리티 잔여 정리.

수정

  • 업스트림 프로바이더 위치 수정 6건 (CAR-176, #580): workday가 Fully Remote에 · Remote 접미사를 붙이고, workable의 telecommuting이 전달되며, successfactors가 trailing comma를 남기지 않고, lever가 remote를 단어 경계로 매칭하며, icims가 Location 레이블을 읽습니다 — 여섯 곳의 위치 파싱이 모두 바로잡혀 원격/멀티로케이션 게시물이 필터에서 잘못 떨어지지 않습니다.
  • http-json — spent-timeout 재시도 금지 (CAR-176, #580): 요청이 이미 타임아웃 예산을 소진해 실패했다면 더 이상 재시도하지 않습니다.
  • 최종 회귀 P3 (#585/#586): 지도의 related 라벨이 라이트 테마에서 4.68:1로 AA를 통과하고(다크의 4.98:1은 불변), skip-link가 악센트 링을 얻습니다.
  • CodeQL 노트-레벨 정리 (#575): 캡처·테스트 도구의 미사용 변수(capture-car140, car136-test)와 two-pager의 죽은 가드를 제거 (#671 #670 #669).

추가

  • Lovable 디자인 통합 — 3단계: 밀도 + 잔여물 (#584): 테이블 행 ≈32px, 버튼 30px, 칩 24px의 밀도 토큰; 크롬 어디에도 코랄 rgba가 남지 않고(.tab-btn.is-active는 악센트 페어로 승격), 애니메이션이 --ease-out으로 통일되며, TOKEN_HANDOFF는 비어 있는 상태로 끝납니다.
  • Trakstar Hire 소스 (CAR-177, #583): client.hire.trakstar.com 보드를 새 스캔 소스로 추가 — 레지스트리 122 = 117 EN + 5 RU · 117 어댑터.
  • 스캔 호스트-풀 (CAR-178, #579): 한 번의 스캔이 최대 32개 회사(호스트당 10개)를 걷습니다 — data/last-scan.json 스냅샷은 portals.yml 순서를 그대로 유지하며, 중복이나 행 손실은 없습니다.
  • CV Studio 되돌리기 + 미저장 가드 (CAR-179, #582): 되돌리기 버튼 + Cmd/Ctrl+Z(RU 자판에서는 Cmd+Я)가 이전 결과를 복원하고, 미저장 변경이 있는 채로 떠나면 confirm을 묻으며, 거절하면 작업이 그대로 돌아옵니다 — i18n ×19.
  • 문서 패리티 · 팩트 재동기화 (docs, #576/#577/#578/#587): 아키텍처 문서를 v1.261.2 팩트로 갱신(39 라우트, 19 로케일, v2 CSS 구조), CAR-139 로케일 카운터 18→19(HANDOFF ×19), CAR-138 사이트 콘텐츠 페이지가 facts.json에서 카운트를 읽고 faq.a8에 베트남어 항목 추가, 그리고 HANDOFF/CONVENTIONS/README에 남은 17/18-시대 카운트 정리(uk pt-BR es ja).

참고

  • 의도적으로 하지 않은 것: .tab-btn의 밀도(별도 44px 계약); logo-PNG는 코랄로 남깁니다(소유자 결정); 업스트림 #6154(years-of-experience)와 #6170(CSV import)은 Linear에 있습니다(CAR-180/181).
  • 게이트: npm run test:ci exit 0 · changelog 패리티 exit 0 · npm run test:e2e:browser 176 pass / 1 opt-in skip.
  • 테스트 6734(릴리스 최종: 6691 pass / 6 env-skip); 레지스트리 122 = 117 EN + 5 RU · 117 어댑터.

[1.261.2] — 2026-10-28

안정성 + 폴리싱 패치: 지오코드 캐시가 쓰기 도중 크래시를 견뎌내고(CAR-173), UI.md가 불릿 연속 줄과 중첩 리스트를 렌더링하며, 도움말 번들에서 렌더링 텍스트로 새어 나온 키 누출 ×17건이 닫히고, evaluate가 ta용 로케일별 토큰 상한을 얻으며(CAR-174), RTL 고정 UI가 논리 속성으로 미러링되고(85건 발견), 지도 캡처가 ×19 재구도되며 지도 클러스터가 다크 테마 대비를 통과합니다 — 더해 Lovable 디자인 통합의 첫 단계(패시브 tokens-v3.css 시트), 문서 백필(릴리스 리뷰 + 스펙), 그리고 CI-node 커버리지 면제.

수정

  • 지오코드 캐시 부분-쓰기 손실 (CAR-173, #562): 두 플러시 경로(디바운스된 insert-flush, exit-flush)가 모두 .cache/geocode.json을 베어 writeFileSync로 썼습니다 — truncate-then-write — 그래서 쓰기 도중의 크래시, OOM 또는 SIGKILL이 찢어진 파일을 남겼고 다음 부팅의 JSON.parse가 예외를 던져, 하루치만이 아니라 영구 캐시 전체(수 주분량의 Nominatim 예산)가 조용히 폐기되었습니다. 두 쓰기 지점은 이제 file-lock.mjs::atomicWrite를 거칩니다(같은 디렉터리의 임시 파일 → rename → 실패 시 unlink) — FULL-R1/FULL-M1이 pipeline.md, cv.md, profile.yml, career-plan, 배치 입력, 메모리에 적용한 바로 그 계약입니다. CAR-142 플러시 기계 자체(디바운스된 insert-flush, exit-flush, 경계가 잡힌 셧다운)는 red-first로 온전함이 검증되었습니다: 테스트 (a)/(b)는 insert가 GEOCODE_SAVE_MS 창 안에 파일에 도달하고 유효한 JSON으로 남는 것을 고정하고; (c)는 메커니즘 게이트와 두 플러시 경로의 동작 실행으로 임시 파일 찌꺼기가 없음과 파싱 가능한 파일을 단언합니다.
  • UI.md 목록 연속 줄 (#563): 목록 핸들러가 연속된 - /* 줄만 소비했으므로, 불릿 뒤의 들여쓰기된 연속 줄은 내보낸 <ul> 밖으로 떨어지고 단락 패스가 그 청크를 건너뛰었습니다 — 연속 줄이 리터럴 **/백틱 마커를 그대로 단 채 날텍스트로 새어 나왔습니다(도움말 번들 전체에서 로케일당 매달린 텍스트 런 ~95개; en.md 95, ru.md 104). 이제 연속 줄 — 선행 공백이 있고 새 블록이 아닌 줄(헤딩, 불릿, NUL-감싼 펜스 플레이스홀더, 빈 줄) — 은 이전 <li>에 <br/>로 연결된 텍스트로 합류하며, 강조 마커가 합류 지점을 가로질러 쌍을 이루지 않도록 줄마다 inline()이 실행됩니다; 펜스-플레이스홀더 가드는 베어 토큰 매치가 아니라 NUL-첫문자 검사입니다. 중첩 - 불릿, 번호 목록, 목록 안의 펜스와 목록 안의 헤딩은 문서화된 별개 갭으로 남습니다(다음 docs 물결).
  • 도움말 렌더링 텍스트 키 누출 ×17 (#561): remote-QA가 렌더링된 도움말에서 원시 i18n 키 85개를 발견했습니다(근본 원인 재현 입증: 같은 UI.md 단일-줄-불릿 소비 — 줄바꿈된 연속 줄이 원시 텍스트로 내보내지며 백틱을 리터럴로 유지; v1.260/261 도움말 섹션들이 다중-줄 불릿을 썼습니다). 163개 불릿이 17개 로케일에서 한 줄로 펼쳐졌습니다 — 공백만 손대고 번역된 단어는 하나도 만지지 않았습니다(이전-대-이후 검증). 새 게이트 tests/help-rendered-keys.test.mjs는 모든 번들을 실제 체인(stripGithubOnlyBlocks + vm로드 UI.md)으로 렌더링하고 텍스트에 1,711개 키 중 하나라도 나타나면 실패합니다; red-first 17 로케일 → 그린. 재현 도구도 저장소에 함께 실립니다: scripts/remote-qa/help-render-scan.mjs (vm) + help-render-crosscheck.mjs (실제 DOM).
  • Remote-QA Ctrl+K가 CAR-122 팔레트 계약을 단언 (#561): prod-regression.mjs는 이제 Ctrl+K 뒤에 커맨드 팔레트(.nav-v2-palette[role=dialog] + nav-v2-input의 포커스) 또는 폴백으로 레거시 검색-포커스를 기대하며, 어느 쪽이든 조용히 받아들이지 않습니다; 유닛 tests/remote-qa-ctrl-k.test.mjs가 모의 DOM 위에서 6개 시나리오를 고정합니다 — red-first 0/6 → 6/6.
  • 지도 캡처 구도 ×19 (#560): images/job-map-en.png에 대한 소유자 보고 — 상단에 탑바 검색 절반 + 잘린 h1, 오른쪽 가장자리에 사이드 리스트의 회색 조각, 런던 근처에서 프레임에 잘린 마커 클러스터, 그리고 지도의 ~2/3 높이를 가로지르는 흰 가로선. capture-map-screenshots.mjs는 이제 지오코드 큐가 비워지기를 기다리고(located + noLoc === total, N/N 카운터가 아님 — Remote 행은 구조적으로 위치가 없어 기존 대기는 결코 완료될 수 없었습니다), 촬영 시 header.topbar와 #notif-drawer를 숨기고, 정확한 .job-map-page 박스로 클립하며(패널이 프레임 가장자리에 잘리지 않음), 클러스터가 절반으로 잘리지 않도록 두 단계 줌 아웃하고, 지도 높이를 고정한 채 일반 뷰포트 스크린샷을 찍으며(captureBeyondViewport는 합성된 타일 레이어를 리샘플링해 타일-행 경계에 흰 띠를 칠합니다), 지도 컨테이너를 정수 정렬합니다. 19개 job-map 캡처 전부 재촬영되었습니다.
  • 지도 클러스터 버블 — 다크 테마 대비 + 라벨 맞춤 (#572): 소유자 스윕 P2/P3 — 다크 테마의 클러스터 링이 고정된 흰 채움 위에 놓여 있었습니다(1.83:1); 링은 이제 var(--paper)를 배경으로 삼고 스코어-팔레트 숫자를 얹습니다(측정 4.98:1+, 채움 9.53:1; 가장 낮은 밴드는 다크에서 수학적으로 착색 불가능하며 정직한 data-dim 경로로 6.1:1을 유지합니다), 그리고 related 라벨이 자기 버블 안에 맞습니다(rAF 폰트-맞춤, 숫자는 결코 축소되지 않습니다).
  • #/scan 언어 전환 — 스켈레톤 재활성 (#534 후속, 에이전트 진행 중, 태그 전 반영): v1.261.0 소유자 수정(#534)의 후속 — 언어 전환 리페인트에서 재활성된 스켈레톤이 완료된 재페치를 더 이상 살아남지 못합니다.
  • 문서 드리프트 (CAR-139, 에이전트 진행 중, 태그 전 반영): README ×19 카운터, HANDOFF ×19, CONVENTIONS 팩트가 라이브 레지스트리/테스트 상태와 재동기화되었습니다.

추가

  • 릴리스 리뷰 + 스펙 백필 (문서, #559/#558): docs/reviews/REVIEW-2026-10-28-v1.261.1.md (v1.261.0/v1.261.1 물결을 스펙 대조로 리뷰)와 같은 물결의 SDD 스펙 백필 — BACKLOG 꼬리는 이제 남은 갭의 이름을 밝힙니다(sources/habr.mjs의 전용 테스트).
  • RTL 딥 스윕 (#571): AR 하니스가 39개 라우트 × LTR-대-RTL을 걷습니다(X-미러, 오버플로, 클리핑, text-align 시그니처) — 85건 발견 수정: 고정 UI 요소(토스트, 사이드바, 드로어 + 그 닫기 ×, docs-FAB, usage-HUD, skip-link)가 inset-inline-*/border-inline-*으로 옮겨지고, 테이블은 17개 뷰 파일 + 2개 라이브러리에 걸쳐 text-align: start/end로, 일괄 [dir=rtl] text-align:right은 제거되며(스캔 별-열 센터링과 충돌했습니다), CAR-58 점수-열 격리는 ltrNum 스팬 위로 옮겨졌습니다.
  • 키보드 a11y 하니스 (#570): tests/playwright-a11y-keyboard.mjs가 11개 라우트를 걷습니다(탭 순서/함정, 포커스 표시기, Escape 오버레이, Enter/Space) — 프로덕션 결함 0건; 회귀 커버리지는 새 것입니다.
  • i18n 미번역 스윕 (#565): 영어와 동일한 36개 값이 ×9 로케일에서 번역되었습니다(*.rolePh, 설정 키, ta 선언문) + 닫힌 허용목록 스윕 가드; 사이트(cvstart.org) i18n 감사(#568): 20개 키 × 18 로케일 번역(전체 demo.*/stats.t.*/showcase.tab* 블록이 어디서나 영어였습니다), 죽은 키 1개 ×19에서 제거.
  • Remote-QA 관측성 (CAR-175, #567): 요약이 실패하는 스캔 소스를 이름으로 지목하고(stderr 샘플, 잘라내기 전 재식별 정보 제거) 프로바이더 사용량에서 tok ~N을 출력합니다; 로직은 드리프트 게이트가 있는 순수 summary-parts.mjs에 삽니다.
  • UI.md 중첩 마커 목록 (한 단계, #564): - 불릿이 중첩 <ul>로 렌더링됩니다(펜스-플레이스홀더-안전) — 로케일당 마지막 2개의 매달린 텍스트 런이 사라졌습니다.
  • Lovable 디자인 통합 — 1단계 (#566) / 2단계 (브랜치, 태그 전 반영): public/css/tokens-v3.css가 Lovable 컨셉을 그대로 이식합니다(oklch 표면 0–3, AA 주석 ink 쌍, 악센트 패밀리, ok/warn/danger/info/neutral 소프트+ink, 스코어 밴드 A–G ×2 테마, 밀도 + 포커스 + 그림자); 2단계는 대비/역할/다크 게이트를 갱신하면서 기존 토큰 이름 위에서 이를 활성화합니다(악센트 분할 blue/danger, 스코어 밴드 + 콜아웃이 cv3 토큰 위로).
  • evaluate 로케일별 토큰 상한 (CAR-174, #569): evalMaxTokens(lang) — ta/hi는 32768을 요청합니다(세 SDK 클라이언트가 16384에서 재클램프; 두 층 모두 수정), 더해 프롬프트 안의 타밀어 분량 예산(블록당 ≤5 문장, ≤900 단어) — v1.261.1 remote-QA ta 보고서가 출력-토큰 한도에서 잘렸었습니다(Block B–G + SCORE_SUMMARY 누락).
  • CodeQL 실제-수정 후속: 새 QA 렌더 도구가 손말린 엔티티 처리를 구조화된 분할 패스 + htmlToText({cap: Infinity})로 대체; 재식별 정보 제거 단언이 정확히 마스킹된 출력을 고정; RTL 하니스의 미사용 상수 제거.
  • CI-node 분산을 위한 커버리지 면제 정책 (habr #558, states #561): sources/habr.mjs(CI node-매트릭스 브랜치 87.23 vs 로컬 ≥89)와 states.mjs(CI 86.49 vs 로컬 ≥89)가 scripts/coverage-baseline.json의 면제 목록에 합류합니다 — 노드-버전 분산 때문에 CI에서 측정된 브랜치 커버리지가 구조적으로 로컬 바닥 아래인 파일은 게이트 전체를 낮추는 대신 면제됩니다.

참고

  • Ctrl+K 계약 (CAR-122): 키 조합은 커맨드 팔레트를 엽니다 — 검색 상자를 직접 포커스하지 않습니다; 검색은 팔레트에서 포커스됩니다. remote-QA 프로덕션 회귀는 이제 정확히 이것을 단언합니다 (#561).
  • CodeQL FP-기각 ×5: main의 js/missing-rate-limiting 알림들은 security_events 토큰을 기다립니다 — 운영자 조치이지 코드 변경이 아닙니다.
  • 브랜치 컷 이후 반영: #561/#562/#563/#564/#565/#566/#567/#569/#570/#571/#572는 모두 main에 병합되었습니다; #534-스켈레톤과 CAR-139 에이전트는 태그 전에 병합됩니다. 한 단계를 넘는 중첩 마커 목록과 인라인-무거운 뷰 지오메트리는 문서화된 갭으로 남습니다.
  • 테스트 6609 (릴리스 브랜치 기준; v1.261.1 이후 +15; 병합 시 #561에서 ~13 더 — 새 게이트 파일 2개); 팬아웃 전 유일한 빨간 테스트는 CHANGELOG ×19 패리티 게이트입니다(로케일 팬아웃은 오케스트레이터의 다음 단계); 스캐너 소스는 건드리지 않았습니다(레지스트리 121 = 116 EN + 5 RU · 116 어댑터).

[1.261.1] — 2026-10-28

프로덕션-QA 클라이언트 패치: #/two-pager의 P1 크래시 수정, 비-QWERTY 레이아웃에서 작동하는 Ctrl+K, 390px 모바일 물결(docs-assistant 오버플로, 지도 칩 겹침, 39-라우트 하니스), CAR-137 완료(구체적 오류, v2empty ×5, 힌트 ×13 + 모드 페이지), 그리고 모든 표면에 실제 v2 스크린샷을 반영한 CAR-140.

수정

  • two-pager TDZ (CAR-170, P1, #554): Cannot access 'previewTimer' before initialization — 뷰 구성 중에 저장 태그 편집기가 복원된 칩마다 onChange를 발화했고, 그 구성 루프는 어휘상 let previewTimer 위에 자리 잡고 있었으므로 #/two-pager 렌더 전체가 크래시했습니다(h1 없음, ×19 로케일). 스케줄러 + 바인딩은 이제 루프 위로 옮겨졌습니다; 동작 테스트는 실제 뷰 모듈을 실행합니다 — 수정 전에는 정확히 그 프로덕션 오류, 후에는 렌더된 페이지 — 그리고 어휘-순서 드리프트 게이트도 더합니다.
  • 비-QWERTY 레이아웃에서의 Ctrl+K (CAR-172, #554): 두 키 조합(chord) 핸들러가 e.key만 검사했습니다 — 러시아어 레이아웃에서는 조합이 key='л' code='KeyK'로 도착해 아무 일도 일어나지 않았습니다. nav-v2.js와 app.js가 이제 e.code === 'KeyK'로 매칭합니다; 합성 ‘л’/KeyK 조합으로 실제 브라우저에서 재현했습니다. CAR-122 계약이 재진술됩니다(참고 섹션 참조).
  • docs-assistant 390px 오버플로 (CAR-172, #554): CAR-136 시작 칩들이 .btn(nowrap, flex-shrink: 0)이었습니다 — 줄바꿈할 수 없는 418px 항목 하나가 #/docs-assistant를 390px 뷰포트 너머 56px까지 밀었습니다; CAR-146 축소 패턴(v2-llm.css 안, 800-LOC CSS 게이트는 그대로)이 이를 0으로 되돌립니다.
  • 지도 칩 겹침 @390 (#552): 390px 뷰포트에서 지도 메타 칩들이 CTA 행과 겹쳤습니다(map.css).
  • CAR-137 — 구체적 오류 + v2empty ×5 (#550): 다섯 개의 일반적인 오류 폴백(‘Error’ / ‘failed’)이 구체적이고 실행 가능한 키가 되었습니다 — evaluate POST 실패, 터미널 이벤트 없는 auto-pipeline 스트림, scan→pipeline 추가, 재게시 클러스터 로드, 파이프라인 되돌리기; 마지막 다섯 개의 평면적인 빈 상태(#/reports, #/usage, #/portals, #/activity, #/dashboard)가 CAR-124 v2empty 패턴에 합류합니다(설명 + aria-hidden 태그 예시 + 미리 연결된 액션; 신규 v2empty.* 키 15개, 죽은 한 줄 키 4개 제거) — 전부 ×19 사전, 스냅샷 재생성.
  • CAR-137 — HelpHint ×13 + mode-page 키트 (#550): 문맥적 «?» 힌트가 이제 남은 모든 페이지 H1(pipeline, reports, dashboard, cv, portals, usage, activity, assessments, map, health, docs-assistant, settings)과 키트를 통한 8개 모드 페이지에 자리합니다(각 MODES cfg가 hintKey를 실으며, HelpHint.title을 통해 렌더됩니다).

추가

  • 모바일-390 하니스 (#552): 390×844에서 모든 SPA 라우트(정규 31개 + 모드 페이지 8개 = 39개 해시)를 걷는 Playwright 스위프(시드된 코퍼스 포함) — 라우트별 가로 오버플로 검사(scrollWidth vs clientWidth, 1px 허용 오차, 가장 넓은 위반 항목을 지목)와 CTA 적중 검사(sticky 바/드로어/FAB가 프라이머리 CTA를 가리지 않음); 옵트인, 브라우저 바이너리가 없으면 우아하게 건너뜁니다, 네트워크 없음.
  • 새 v2 캡처 ×48 (CAR-140 phase A, #551): 시드된 CI-격리 픽스처에서 나온 실제 v2-UI 스크린샷 — dashboard ×19 + job-map ×19 (job-map-ta 첫 등장, job-map-vi 신규) + scan/pipeline/tracker/reports × en/ru — README, 도움말, 랜딩에 들어갑니다.
  • cvstart.org 실제 스크린샷 (CAR-140 phase B, #553): CAR-140 자리표시자 슬롯이 사라졌습니다 — 랜딩이 실제 v2 캡처를 보여줍니다(job-map 쇼케이스 + 스크린샷 행, ×19 랜딩 로케일).
  • CAR-137 힌트 (#550): 신규 번역 키 21개 ×19 (help.hint.* ×20 + 모드 페이지 hintKey들) — 모든 페이지가 이제 그 자리에서 스스로를 설명합니다.

참고

  • Ctrl+K 계약 (CAR-122): 키 조합은 커맨드 팔레트를 엽니다 — 검색 상자를 직접 포커스하지 않습니다; 검색은 팔레트에서 포커스됩니다. v1.261.0 QA 체크리스트의 «Ctrl+K фокусит поиск» 줄은 대체되었습니다; v1.261.1 QA 프롬프트가 갱신된 검사를 담고 있습니다.
  • CodeQL 실제 수정 (#547): 4개 알림이 닫혔습니다(content.mjs 리터럴-키 반복의 구조화, 린트 스윕) — 이 패치 전에 릴리스 후 v1.261.0 main에 반영되었습니다; 완전성을 위해 여기에 기록합니다.
  • 테스트 6582 (+51); 브라우저 스위트 171–172 통과 / 0 실패 / 1 옵트인 스킵; 모바일-390 하니스는 단독 실행됩니다(node tests/playwright-mobile-390.mjs). 스캐너 소스는 건드리지 않았습니다(레지스트리 121 = 116 EN + 5 RU · 116 어댑터).

[1.261.0] — 2026-10-28

전면 리뷰 물결 완료(HIGH + MEDIUM 발견 전부 반영), 소유자가 보고한 언어 전환 수정, 평가 품질 수정, 도움말 ×18 팬아웃, cvstart.org 랜딩 v2 — 그리고 이 물결을 기록하는 SDD/OpenSpec 스펙 4건. 모든 수정은 적-녹(red→green) fix-forward와 새 컨텍스트 GLM-5.3-Flash 리뷰를 거쳐 반영되었습니다.

보안

  • FULL-R1 — CSRF acting-GET 게이트 + 잠긴 원자적 파이프라인 쓰기 (#519): /api/stream/*, /api/run/*, /api/geocode에 Sec-Fetch-Site/Origin/Referer 게이트(내보낸 ACTING_GET, 정적 커버리지 게이트); data/pipeline.md / data/last-scan.json의 모든 읽기-수정-쓰기가 withFileLock + 새 atomicWrite(같은 디렉터리 임시 파일 → rename → 실패 시 unlink) 아래에서 이루어집니다.
  • FULL-R2 — 소스의 SSRF 클러스터 (#514): rss.mjs https + 사설 주소 거부; 날 fetchImpl 소스 ~25개 → 가드된 전송 계층; 미러 고정; habr/hh/geekjob/getmatch의 리디렉션 검증; 공유 delay(ms, signal).
  • FULL-R4 — stripDangerousMarkdown 산문 훼손 (#513/#518): 스킴 제거는 이제 링크/속성 컨텍스트에서만; 엔티티는 감지 전용 복사본에서 디코딩됩니다(«JavaScript: React, Node»와 «R&D»는 살아남습니다).
  • FULL-M2 — 전송 계층 강화 (#520): safeGet은 호출자의 signal과 함께 timeoutMs를 존중합니다(브리지 컨트롤러); guardResolvedHost는 DNS 오류에서 fail-closed로 실패합니다. #525는 cross-origin 리디렉션에서 자격증명 헤더 제거, 모든 종료 경로에서의 전송-리스너 분리, 그리고 이미 중단된 signal에서의 크래시 수정을 더합니다.
  • FULL-M4 — 로고/배치 (#523): 로고는 모든 경로에서 magic bytes를 고정합니다(거짓말하는 image/png는 SVG를 실어 나를 수 없습니다), 저장 시점에 content-type을 image/*로 제한하고, 사설 캐시 + nosniff; batch: --parallel은 1–8로 제한, --min-score는 검증, BATCH_BUSY single-flight, config/batch 변경에는 llmRateLimit.

추가

  • CAR-147 — 로케일 인식 평가 상한 (#524): evalTimeoutMs(lang) — ta/hi는 300 s 대신 480 s를 받습니다(인도계 문자는 음절당 여러 토큰으로 토큰화됩니다; 기존 상한선은 502 + 빈 리포트를 낳았습니다); remote-QA 클라이언트는 540 s를 기다립니다.
  • CAR-133 — cvstart.org 랜딩 v2 (#532): reduced-motion으로 게이트된 패럴랙스 히어로, scan→evaluate→tracker 흐름의 명확히 표시된 SIMULATED 데모, 실시간 검증된 카운트가 있는 scroll-reveal 섹션, CAR-140 스크린샷 자리표시자 슬롯.
  • CAR-135 — UI v2를 위한 도움말 재작성, ×18 팬아웃 완료 (#533, #540, #541, #542): 19개 도움말 파일 전부가 v2 셸을 설명합니다(접을 수 있는 nav, Cmd/Ctrl+K 팔레트, 스켈레톤, 예시+액션 빈 상태, scan v2 인터페이스, 파이프라인 그룹화+되돌리기, 리포트 v2, 칸반); UI 라벨은 자신의 i18n 키를 인용합니다; 121-소스 문장은 레지스트리에서 직접 삽니다.
  • 스펙 (SDD/OpenSpec) (#535): 10월 27일 wave-two 백필 + 향후 작업을 위한 스펙 세 개(도움말 팬아웃, 쓰기 핸드오프, 언어 전환 불변식).

수정

  • 언어 전환이 #/scan을 더 이상 텅 비우지 않습니다 (#534) — 소유자 보고: 뷰-로컬 스캔 상태(결과, 패싯 칩)는 이제 모듈 수준입니다; 4 s 지연 재페치를 갖춘 동작 Playwright 하니스(이전 모양에서 적색).
  • CAR-148/149 — ru 리포트 «Block G 누락» (#530): 프롬프트는 서로 일치하지 않는 세 개의 A–G 맵을 가르치고 있었습니다; 스켈레톤 + 목록 + 예시가 이제 oferta.md와 일치합니다(G = Posting Legitimacy); 동작 드리프트 게이트.
  • CAR-150 — 파이프라인 되돌리기가 comp 메타데이터를 유지 (#537): DELETE는 comp로 답하고, POST는 게이트된 선택적 comp를 받아들이며, 뷰는 모든 폴백 분기를 통해 {token, comp}를 운반합니다.
  • FULL-M3 — 죽은 소스가 정직한 보드를 보고 (#522): getro/mycareersfuture/meituan/mokahr/glints는 형식이 어긋난 page-1에서 throw합니다(죽은 API가 더 이상 «건강함, 공고 0개»로 읽히지 않습니다); pinnedOverride는 18개 어댑터의 company.api 오버라이드를 고정합니다.
  • FULL-M5 — 단일 제공자 캐스케이드 (#521): routes/llm.mjs가 runActiveProvider를 거칩니다(+cachePrefix, signal); Gemini 서브프로세스 가로채기; 응답 계약은 바이트-동일합니다.
  • FULL-M1 — 공유 파일 쓰기 잠김 (#526, #539): runTrackedScript + TRACKER_MUTATING 명부; outcome/followup 스폰은 잠금을 보유합니다; tracker.mjs는 atomicWrite로 씁니다; PUT 저장은 잠김+원자적으로 이루어집니다.
  • FULL-M6 — 서버 + 클라이언트 통합 (#531, #536, #525): dotenv null-프로토타입 객체 + __proto__ 키 거부; isPrivateOrLoopback이 NAT64/6to4/benchmark/예약 대역을 커버; env-config mtime 캐시; file-lock 실제 슬롯 GC; safeGet timeout+signal; DNS 가드 fail-closed; SSE ×4 → API.postStream; 포커스/라벨/상태 라이브러리; clipboard 쓰기 await 처리; 대시보드 Hired 색조.
  • R3 — CSS 토큰 드리프트 (#527): --accent 해결(지도 포커스 링이 조용히 죽어 있었습니다); 토큰 밖 duration 11개 흡수; 드리프트 게이트.
  • R4 — 로딩 잔여 항목 (#538): assessments/mode-page/파이프라인 미리보기가 CAR-112 스켈레톤을 렌더링합니다.
  • CodeQL + CI 일괄 처리 (#517, #539 수정): 쓰이지 않는 임포트 정리, ACTING_GET 게이트가 실제 정규식을 임포트하도록 수정, 라이프사이클 슬라이스 재고정.

참고

  • 의도적으로 하지 않은 것: 연결 시점 DNS 고정(undici 필요 — 의존성 밖, 잔여분은 문서화됨); CAR-140 스크린샷(배포된 v2 UI 필요); CAR-137/138/141(다음 문서 차례); js/missing-rate-limiting CodeQL 클래스는 문서화된 거짓 양성입니다(커스텀 리미터가 인정받지 못함).
  • 도움말 ×18 팬아웃: es/de wave-1이 안내 노트와 Active Companies 카드 델타를 건너뛰었습니다(de는 자체 안내를 반영했고, es는 한 줄 후속을 받습니다).

[1.260.0] — 2026-10-26

리뷰 강화 트레인 — v1.259.0 플릿에 대한 opus-5-5 및 AI-리뷰 라운드에서 나온 여덟 개의 머지(#491–#498): 급여 정규화, 리포트 날짜 범위 한정, 지오코드 견고성, 번들 캐시 버스팅 r2+r3, deep 오버플로 수정 — 그리고 두 개의 UI v2(파이프라인, 리포트). 모든 수정은 적-녹(red→green) fix-forward로 출시되었습니다.

수정

  • 급여 비교 정규화 (CAR-143 rev-2): 기간 표지는 이제 그것이 속한 수치 바로 근처에서만 인식됩니다 — «6 month contract, $120k»는 더 이상 ×12로 연간화되지 않고, 숫자에 붙은 RU/ES 기간 형태(«за месяц»가 숫자 바로 옆)가 부수적인 단어를 이깁니다; $/¥ 기호 계열은 인식되지만 같은 줄에 명시적 ISO 코드가 없으면 매핑되지 않습니다(AUD/CA/SG 달러는 더 이상 USD로 청구되지 않고, ¥ RMB 형태는 ~20× 어긋난 JPY 변환 대신 명시적 주석을 받습니다); 단독 19xx/20xx 숫자는 항상 연도입니다; 범위 수치는 짝의 기간을 물려받습니다; 프로필의 minimum과 target_range는 하나의 범위로 이어 붙여지지 않습니다.
  • fixReportDate 범위 한정 (CAR-144 rev-2): 서버 고정 날짜 교정은 첫 ## 제목 앞의 프리앰블에만 적용됩니다(H2 제목 리포트는 보수적인 no-op을 받습니다 — 낡은 날짜는 용인되지만 지어낸 날짜는 아닙니다); 프리앰블 안에서: 유일한 후보는 교정되고, 아니면 유일한 굵은 **Date:**, 그것도 아니면 아무것도 바뀌지 않습니다(굵은 후보 둘 또는 평범한 후보 둘은 아무것도 재작성하지 않습니다). FIX-5 PDF 라벨 게이트는 이제 실제 조립된 사전을 읽습니다 — 누락된 로케일은 영어 폴백으로 조용히 통과하는 대신 throw합니다.
  • Habr 위치 칩 + 지오코드 견고성 (CAR-142 rev-2): Habr 메타 칩은 구조적 증거(도시 필터 링크 또는 위치 아이콘)가 있을 때만 도시가 됩니다 — 이전 안내(«Возможна релокация»)와 고용 형태는 절대 위치로 가장하지 않습니다; 지오코드 캐시는 이제 재시작을 살아남습니다(beforeExit/exit/SIGTERM+SIGINT에서 재-raise와 함께 flush, 더티 플래그는 쓰기가 끝난 뒤에만 해제), 종료는 범위가 한정되고(exit-loop 래치, 세대 카운터, recency LRU), 소진된 지오코드 예산은 일반 502 대신 503 + Retry-After로 답합니다.
  • 번들 캐시 버스팅 (CAR-145 r2+r3): 셸은 /js/bundle.js?v=<content-hash>를 인용합니다 — 매치 시 immutable, 불일치 시 no-store — 새 셸이 캐시된 오래된 번들과 짝을 이루던 5분 배포 창을 없앱니다. 해시와 스트림은 하나의 파일 디스크립터를 공유합니다(찢어진 읽기 없음); 짧은 읽기는 전체 배포를 오염시킬 수 있는 stat-폴백 해시 대신 null로 답합니다; make-bundle은 점-접두 임시 파일 + rename으로 아이덴티티와 .br/.gz 컴패니언을 쓰므로, 죽은 빌드는 반쯤 쓰인 바이트를 제공하거나 임시 파일을 누출할 수 없습니다.
  • Deep 오버플로 (CAR-146): 파일명만으로도 휴대폰 뷰포트를 초과하는 저장된 조사 브리프는 더 이상 #/deep을 390px 너머로 밀 수 없습니다 — 카드는 자기 행에 갇히고, 제목은 줄바꿈되며, 날짜는 한 줄에 머뭅니다; 좁은 뷰포트 스위프는 이제 과도하게 긴 시드 픽스처와 함께 #/deep을 걷습니다(두 원격 QA 실행의 유일한 발견이었으며, 이제 해결되었습니다).
  • 파이프라인 v2 강화 (CAR-116 리뷰): 삭제는 확인과 되돌리기를 갖춥니다(정직한 되돌리기 문구와 함께), 카드에 호스트-회사 날조 없음, FNV-1a 행 id, 진짜 벌크 카운트, 벌크 작업에 대한 재진입 가드.

추가

  • 파이프라인 v2 (CAR-116): 풍부해진 카드, 그룹 뷰, 되돌리기가 있는 벌크 폐기; 신규 pipe.* 키 17개가 19개 사전 전체에서 번역되어 출시되었습니다.
  • 리포트 v2 (CAR-118): 점수 밴드/합법성/회사/날짜 필터가 있는 실시간 검색, 정렬 가능한 헤더(aria-sort), 밴드별 액션 버튼이 있는 영속 카드 뷰, 클릭으로 필터링하는 점수 히스토그램, 그리고 통합 «Company — Role» / «x.x/5» 렌더링(window.ReportOps); 신규 rep2.* 키 31개가 19개 사전 전체에서 번역되어 출시되었습니다.

참고

  • 모든 수정은 적-녹(red→green) 증거를 갖춘 리뷰 라운드를 거쳤습니다; 첫 번째 번들 r2에 대한 두 AI-리뷰 HIGH 발견(짧은 읽기 해시, 컴패니언 원자성)은 r3로 닫혔고, CAR-143/CAR-144/CAR-142 라운드는 각각 전임자의 미해결 발견을 재검증했습니다.
  • 의도적으로 하지 않은 것: 지도 클러스터 뷰는 CAR-125에 머뭅니다(대기 중); 서버 측 파이프라인 메타데이터 복원은 별도 티켓입니다; rev-2 이전 v1.259.0 상태로 저장된 콘텐츠에 대한 소급 수정 없음.
  • 테스트 5988 (+165); 브라우저 스위트 152 통과 / 0 실패 / 1 옵트인 스킵. 스캐너 소스는 건드리지 않았습니다(레지스트리 121 = 116 EN + 5 RU · 116 어댑터); 신규 i18n 키 48개는 기능 PR 그 자체 안에서 ×19 팬아웃과 함께 출시되었습니다.

[1.259.0] — 2026-10-25

에이전트 플릿 두 번째 트레인, 여섯 가지 수정: 리포트 날짜는 서버에서 고정되고, 지도 지오코더는 캐시하며 RU 도시를 읽고, 도움말 ×18은 현지화된 버튼 라벨을 인용하며, UI 문장은 언어 섞임을 멈추고, 급여 비교는 서버 측에서 계산됩니다 — 그리고 JS 번들은 사전 압축되어 출시됩니다(콜드 로드 3.1 MB → 598 KB).

수정

  • 지어낸 리포트 날짜 (CAR-101): 평가 프롬프트는 이제 오늘의 날짜를 서버 측에서 고정하고(“the Date: field MUST be exactly — copy verbatim; never invent”), fixReportDate는 리포트가 수락되는 모든 지점(Anthropic 분기, Gemini 서브프로세스, OpenAI 호환 꼬리, /api/deep, auto-pipeline)에서 어긋난 날짜를 오늘로 재작성합니다 — 경고 + 활동 기록과 함께. 이것은 교정이며 결코 거절이 아닙니다. 프로덕션 리포트의 349/789가 지어낸 날짜를 갖고 있었습니다; 새 리포트는 더 이상 그럴 수 없습니다.
  • 지도 지오코딩 (CAR-105): hh.ru/Habr 행의 키릴 도시 문자열이 이제 지오코더에 도달합니다; 영속적인 크기 제한 캐시(data/geocode-cache.json, withFileLock) 덕분에 한 번 확인된 도시는 공급자를 두 번 다시 치지 않습니다; 지오코드 호출은 요청당 예산이 매겨져 지도가 13.9초 동안 막히는 대신 점진적으로 채워집니다.
  • 언어가 섞인 UI (CAR-108): 19개 사전 중 17개에 걸쳐 키가 지정된 누수 23건(85개 값)이 수정되었습니다 — «все matching», «только remote», «Открыть Scan», 퍼널 «Evaluated → Applied…» 등이 각 로케일 자체의 제품 어휘를 재사용하는 완전한 현지화 문장이 되었습니다. i18n-no-latin-leaks는 조각 테이블 + 퍼널 프로브로 확장되었습니다 — 적-녹(red→green).
  • 통화 판정 뒤집힘 (CAR-109): 급여 비교는 이제 comp-compare.mjs에서 서버 측에서 계산되어 정규 형태로 삽입됩니다(Candidate minimum: … per YEAR (raw: …) vs JD offer: …); 월급 ×12, k/к/тыс/萬 및 쉼표 소수 형태는 비교 전에 정규화됩니다; 불확실한 변환은 그 자체로 판정을 뒤집을 수 없습니다(zh-TW «€80–100k ниже минимума» 케이스는 이제 죽었습니다). 모델은 절대 곱셈을 하지 않습니다.
  • 도움말 ×18 버튼 라벨 (CAR-107): 현지화된 도움말 번들이 UI가 더 이상 보여주지 않는 영어 라벨들(«⚡ Run live», «Save», «Scan now»)을 인용하고 있었습니다 — 뷰 소스에서 매핑한 18개 로케일에 걸친 263건의 교체; docs-assistant는 이 수정을 그대로 물려받습니다.

추가

  • 사전 압축 번들 + 캐시 가능한 콜드 로드 (CAR-104): make-bundle.mjs는 릴리스 시점에 bundle.js.br(brotli q11) + .gz를 생성합니다; 번들 범위 미들웨어는 Vary: Accept-Encoding, public, max-age=300, must-revalidate + ETag/304로 br > gz를 협상합니다; 신선도 가드가 적용되고(컴패니언 아티팩트는 절대 아이덴티티 번들보다 오래된 것을 제공하지 않습니다), 직접 아티팩트 페치는 404를 돌려주며, CSP는 바이트-동일합니다. 콜드 로드: 3.1 MB → ~0.6 MB 와이어(−80%); 5분 이내의 반복 방문은 ≈0바이트를 전송합니다.

참고

  • 모든 수정은 적-녹(red→green) 증거(각 스위트에 걸친 신규 케이스 13 + 28 + 1 + 30 + 7)와 리뷰 라운드를 갖춰 에이전트 플릿에서 나왔습니다(CodeQL #579/#580/#584는 코드에서 수정 — 스텁 URL은 new URL().host로 파싱, 죽은 프롬프트 문자열 하나 제거).
  • v1.258.0 점수 백필 수치는 이제 CAR-129에 기록되어 있습니다(updated: 22, reportScoreless: 159).
  • 의도적으로 하지 않은 것: 349개 과거 리포트에 대한 소급 날짜 수정 없음(백필은 소유자 승인 운영 작업이 될 것입니다), 지도 클러스터 뷰 없음(CAR-125는 이 지오코드 수정 뒤에 계속 대기 중입니다).
  • 테스트 5823 (+74). 스캐너 소스는 건드리지 않았습니다(레지스트리 121 = 116 EN + 5 RU · 116 어댑터); i18n 키 없음(CAR-108은 기존 키의 값만 편집합니다).

[1.258.0] — 2026-10-25

에이전트 플릿에서 나온 여섯 가지 수정, 하나의 릴리스 트레인: #/scan 회사 카운터가 우주를 섞는 것을 멈추고, 심층 조사는 출처를 요구하며, 폼 오류는 필드를 밝히고, 로딩 상태는 스켈레톤과 즉시 h1을 얻으며, 평가는 37k 토큰 프롬프트 접두사를 캐시합니다.

수정

  • #/scan «활성 회사» 카운터 (CAR-100): 분자는 모든 결과 행에 걸친 고유 회사 수를 세고 있었고(프로덕션에서 1554 — hh.ru만으로도 수백 개가 나옵니다) 분모는 API 기반 추적 회사 수(~90)를 세고 있었습니다 — 하나의 분수 안에 두 개의 우주였습니다. 짝 (a): 분자는 이제 API 기반 추적 스캔 자체가 가져온 행들 안에서 회사를 셉니다(_entry ?? company로 매칭; hh.ru/telegram 고용주 행은 탈락); 스캔 전 폴백도 같은 우주 안으로 옮겨졌습니다(«104/90»을 읽을 수 있었습니다).
  • 심층 조사 날조 (CAR-110): deep 프롬프트는 이제 강제 계약을 실고 갑니다 — 모든 사실 주장은 [source: domain.com]으로 끝나야 하고(이름을 대지 못하면 진술하지 않습니다; 마커를 지어내는 것은 “unknown”을 쓰는 것보다 더 나쁩니다), 알 수 없는 사실은 unknown으로 쓰이며, 리포트는 반드시 ## Data freshness 섹션으로 끝나야 합니다. 신선도 앵커: 오늘의 UTC 날짜 + POST /api/deep의 선택적 회사 url(범위가 한정되고, 서버 측에서 절대 페치하지 않습니다).
  • 폼 오류가 필드를 밝힘 (CAR-130): 실패한 POST는 첫 번째 폼 필드에 aria-invalid + .is-error를 표시하고 그곳으로 포커스를 옮깁니다(WCAG 3.3.1); 편집 시 해제됩니다. 네트워크 오류 힌트는 배포 상태를 인지합니다 — 루프백은 개발 명령 힌트를 유지하고, 배포된 오리진은 bash web-ui/bin/start.md 조언 대신 «서버가 응답하지 않습니다»를 받습니다(연결 배너도 같은 하드코딩을 갖고 있었습니다).
  • 로딩 상태 (CAR-112): #/map과 #/tracker는 h1을 즉시 렌더링하고(라우터 포커스 대상은 데이터가 도착한 뒤에만 존재했습니다) 공유 스켈레톤 헬퍼(Skeleton.rows, role="status" 영역 안의 aria-hidden 반짝임 행으로 common.loading을 재사용)가 트래커, 스캔 결과, 지도 셸의 맨 «로딩 중…» 텍스트를 대체합니다; prefers-reduced-motion은 펄스를 멈춥니다.
  • 평가 프롬프트 캐싱 (CAR-111): buildEvaluationPromptParts는 ~37k 토큰 프롬프트를 바이트-안정 접두사(CV + 프로필 + 모드 파일 + 지시문)와 JD 꼬리로 나눕니다; Anthropic 경로는 [접두사 + cache_control: ephemeral] + [JD]를 보냅니다 — 접두사는 첫 호출 이후의 모든 호출에서 캐시-읽기 요율(≈10× 저렴)로 청구됩니다. 누락/불일치 접두사는 바이트-동일한 기존 요청으로 실패-오픈합니다; OpenAI 호환 캐스케이드는 그대로입니다(그쪽은 서버 측 캐싱).

참고

  • 여섯 수정 모두 적-녹(red→green) 증거(신규 케이스 5 + 7 + 7 + 7 + 8)와 리뷰 라운드를 갖춰 에이전트 플릿에서 나왔습니다; 첫 CAR-111 CodeQL 경고(#579/#580)는 코드에서 수정되었습니다 — 테스트 스텁은 부분 문자열 매칭 대신 URL 호스트를 파싱하고, 죽은 전체-프롬프트 문자열은 제거되었습니다.
  • 점수 백필이 프로덕션에서 실행됐습니다 (v1.256.0 엔드포인트, 소유자 승인): updated: 22, missingReport: 0, reportScoreless: 159 — 점수 없는 159개 행은 머신 블록 이전 헤더를 가진 리포트나 파싱 불가능한 점수 형식을 실은 리포트를 가리킵니다; 재평가나 더 넓은 파서 패스로만 복구 가능합니다(CAR-129에 기록).
  • 의도적으로 하지 않은 것: 통근 컬럼의 null 맨-뒤 정렬 변경 없음(기존의 내림차순 Infinity 특이점), 과거 사용량 레코드의 소급 재가격 책정 없음, 스캔 필터 폼에 대한 .is-error 스타일 하이라이팅 없음(CAR-130은 공유 mode-page 제출 헬퍼 + api.js 힌트로 범위 한정).
  • 테스트 5749 (+36). 스캐너 소스는 건드리지 않았습니다(레지스트리 121 = 116 EN + 5 RU · 116 어댑터); i18n 키 없음(CAR-130 힌트는 conn.down을 재사용; 스켈레톤 영역은 common.loading을 재사용).

[1.257.0] — 2026-10-25

에이전트 플릿에서 나온 두 가지 수정: 인앱 도움말 어시스턴트가 데바나가리와 타밀 문자를 이해하며, 사용량 페이지가 Hermes의 실제 비용을 보여줍니다.

수정

  • hi/ta 도움말 어시스턴트 검색 (CAR-103): docs-assistant 키워드 토크나이저의 단어 클래스가 [\p{L}\p{N}\u30fc]+였습니다 — 결합 기호(combining marks)가 없었습니다. 데바나가리 마트라(ा ै ्…)와 타밀 모음 기호(ே ை ்…)가 단어를 조각으로 잘라냈습니다; 길이 규칙은 짧은 조각을 버렸고(힌디어 질의가 0개 섹션에 매칭), 더 긴 단어들은 머리를 잘려 관련 없는 섹션과 충돌하는 깨진 토큰이 되었습니다. 이제 클래스는 \p{M}을 유지합니다; CJK 바이그램 경로는 그대로입니다. hi «मैं स्कैन कैसे चलाऊं?»와 ta «ஸ்கேன் எப்படி இயக்குவது?»는 출시된 번들에서 올바른 섹션을 검색합니다.
  • Hermes 사용량 비용 (CAR-106): normalizeUsage()은 공급자 사용량을 {in, out}으로 압축하면서 게이트웨이가 보고한 cost를 조용히 버렸습니다 — #/usage는 9.5 M Hermes 토큰 동안 $0.0000을 보여주었습니다(가격표의 해당 행이 0/0이기 때문입니다). 사용량 레코드 형태는 하위 호환되는 선택적 cost?를 얻습니다; 애그리게이터는 레코드별로 공급자가 보고한 비용을 우선하고, 없을 때만 가격표 추정치로 폴백합니다; 쓰레기/음수 값은 버려지며 절대 조작되지 않습니다.

참고

  • 두 수정 모두 적-녹(red→green) 증거와 함께 에이전트 플릿에서 나왔습니다(신규 케이스 3 + 5; 14/14 및 10/10 그린; 인접 테스트 78/78 및 61/61). 첫 PR들(#470/#471)은 스쿼시-병합된 v1.256.0과 충돌하는 낡은 워크트리 베이스를 실고 있었습니다 — 깨끗한 체리-픽(#472/#473)으로 대체되었습니다.
  • 의도적으로 하지 않은 것: 과거 data/llm-usage.jsonl 줄들의 소급 재가격 책정 없음(비용 필드가 없습니다; 신규 레코드만 혜택을 봅니다).
  • 테스트 5713 (+8: docs-assistant ×3, usage ×5). 스캐너 소스는 건드리지 않았습니다(레지스트리 121 = 116 EN + 5 RU · 116 어댑터); i18n 키 없음.

[1.256.0] — 2026-10-25

트래커 데이터 품질: 역할이 회사로 기록되는 일은 이제 없고, 채용 보드가 고용주로 기록되는 일도 없으며, 점수 없는 행은 더 이상 맨 앞에 뜨지 않습니다 — 그리고 리포트 자체에서 누락된 점수를 채워 넣는 일회성 엔드포인트가 추가됩니다.

추가

  • 점수 백필 (CAR-129): POST /api/tracker/backfill-scores는 링크된 리포트에 파싱 가능한 점수가 담긴 행의 점수 셀을 채웁니다(평가된 181개 행이 리포트는 있었지만 점수는 없었습니다). 점수가 이미 있는 행, 명시적 센티널(N/A), 리포트 없는 행, 점수 없는 리포트는 절대 건드리지 않습니다; 파일 락 하에서 점수 셀만 재작성됩니다; 점수 형태는 쓰기 지점에서 검증되어 트래커의 X.X/5 규약으로 정규화됩니다; 실행은 멱등적이며 {ok, updated, missingReport, reportScoreless}를 반환합니다.

수정

  • 역할은 결코 회사가 아닙니다 (CAR-128): guessCompanyRole은 대시 헤더 줄의 왼쪽을 회사로 취급했지만, 보드는 두 순서 모두로 씁니다 — «Senior Frontend Engineer - Grafana Labs»는 역할을 회사로 기록했습니다(13행: Grafana, CircleCI…). 이제 단어 경계가 적용된 역할 키워드 휴리스틱이 역할 쪽을 고르고, 고용 형태는 결코 고용주가 되지 않습니다(«Senior Engineer - Remote (EU)»는 «Remote (EU)»로 기록하는 대신 아무것도 기록하지 않습니다).
  • 채용 보드는 결코 고용주가 아닙니다 (CAR-127): 도메인-슬러그 폴백이 hh.ru 행을 «hh»로 기록했습니다(120행). hh/habr 슬러그가 고용주-아님 목록에 합류하고, 문장 중간 고용주 추론(«в компании X», «X ищет»)이 이제 채용 보드 호스트(hh.ru/kz/uz/by, habr.com)에서도 실행됩니다 — 그 페이지 텍스트가 실제 고용주를 밝히는 곳이기 때문입니다. 원인은 작업 중에 발견됐습니다: 이 추론 패턴들의 ASCII \b/\w는 키릴 문자와는 전혀 일치하지 않았습니다 — v1.248.4 이후 조용히 죽어 있었던 것입니다; 유니코드 룩비하인드와 \p{L} 클래스가 그 자리를 대신합니다.
  • 점수 없는 행은 맨 뒤로 정렬 (CAR-129): 트래커의 점수 정렬은 -1 센티널을 사용했기 때문에, 기본 오름차순 정렬에서는 점수 없는 모든 행이 점수 있는 행보다 먼저 나타났습니다. 이제 null은 양방향 모두에서 맨 뒤로 정렬됩니다(점수 FILTER는 -1을 유지합니다 — “점수 없는 행은 ≥4 필터에서 탈락”이라는 의미는 그 자리에서 올바르기 때문입니다).

참고

  • 과거 행은 이 릴리스에서 마이그레이션하지 않습니다: 파서 수정은 새 평가에 적용됩니다; 점수는 백필 엔드포인트로 복구할 수 있고, 120개 «hh» 고용주 셀은 재작성이 아니라 재평가가 필요합니다.
  • opus-5-5 리뷰 라운드가 이번 릴리스에 접어 들어갑니다: 앵커 없던 «X ищет» 패턴은 출시 전에 제거되었고(동사 앞의 대문자 어구는 무엇이든 고용주로 기록될 뻔했습니다 — 조작 금지 불변식), 문장 첫 «В…» 앵커가 추가되었으며, 백필 라이터는 대상 셀을 재확인하고(수동 편집 후에는 중복 # 행이 존재할 수 있음), 손으로 망가진 짧은 행의 컬럼 수를 유지하며, 테이블이 아닌 첫 줄에서 멈춥니다. 점이 찍힌 이름(«Сбер. IT») 안의 점은 «в компании» 앵커에게 문장 마침표와 구별되지 않습니다 — 보수적인 접두사(«Сбер»)가 기록됩니다; 동사에 앵커된 패턴이 전체 형태를 잡습니다.
  • 의도적으로 하지 않은 것: tracker.js에서 비교자를 유닛 테스트 가능한 라이브러리로 추출하지 않음(소스-스태틱 게이트는 유지; CAR-129에 후속 과제로 기록), 통근 컬럼에 null 맨-뒤 정렬 변경 없음(기존의 내림차순 Infinity 특이점), «hh» 행에 대한 데이터 마이그레이션 없음.
  • 테스트 5705 (+10: auto-pipeline-jd-gate의 identity 5개, CAR-128 2개, CAR-129 정렬 게이트, 적대적 경로 핀을 포함한 백필 4개 — 대체된 v1.248.x 케이스 차감). 스캐너 소스는 건드리지 않았습니다(레지스트리 121 = 116 EN + 5 RU · 116 어댑터); i18n 키 없음.

[1.255.2] — 2026-10-24

라이브 회귀와 프로덕션 크래시 리포트에서 나온 사용자에게 보이는 세 가지 수정: #/scan 렌더 크래시, 눈먼 베트남어 언어 검사, 그리고 반복되던 zh-TW 요약 블록 유실.

수정

  • #/scan 크래시 (CAR-99): v1.253.0 이후 카운트-게이트 재생성이 FALLBACK_SOURCES 리터럴의 모든 항목 뒤에 이중 쉼표를 붙여 썼습니다 — 배열의 빈 슬롯(elision hole) 120개. 스캔 뷰의 new Set(FALLBACK_SOURCES.map((s) => s.value))는 그래서 undefined를 담았고, 회사 분류기의 id.length는 모든 페이지 렌더에서 TypeError: Cannot read properties of undefined (reading 'length')를 던졌습니다(프로덕션에서 logbuf가 포착). 구멍은 제거되었고 새 게이트(스펙 4개, 적-녹 검증 완료)가 파일을 실행해 어댑터-id Set 안의 어떤 구멍, 사용 불가 항목, undefined든 실패시킵니다 — 텍스트 전용 드리프트 게이트는 이 클래스를 볼 수 없었습니다.
  • 눈먼 vi 언어 검사 (CAR-97): 베트남어는 라틴 문자라 라이브-QA 불용어 테이블에 항목이 없었고, 그래서 “vi 불용어”가 영어 히트를 세어 베트남어 답변이 report not in vi로 읽혔습니다(라이브 회귀: vi 2, en 2). 베트남어는 이제 자체 10단어 기능어 테이블을 갖습니다 — 순수 vi 리포트는 통과하고 영어 리포트는 여전히 실패합니다.
  • zh-TW가 SCORE_SUMMARY 블록을 잃음 (CAR-98): 마무리 언어 리마인더는 기계 형식을 번역하지 말라고만 말했고, 요약 블록이 존재해야 한다는 말은 없었습니다 — 짧은 zh-TW 리포트(5,983자)는 A–G 블록을 실었지만 그 블록을 아예 생략했습니다(v1.239.2급 재발). 이제 여는 지시문과 마무리 리마인더가 블록이 필수이며 없으면 리포트가 무효라고 명시합니다.

참고

  • 이 릴리스는 어떤 스캐너 소스, 레지스트리 수(121 = 116 EN + 5 RU · 116 어댑터), 공급자 배선도 의도적으로 바꾸지 않습니다 — 수정 전용 릴리스이므로 카운트 게이트는 그대로입니다.
  • 테스트 5695 (+9: car99-fallback-holes ×4, car97-vi-lang-check ×4, CAR-98 프롬프트 불변식 ×1). 브라우저 143/143. vi/zh-TW 라이브 재검사는 배포 후 회귀에 실려 갑니다.

[1.255.1] — 2026-10-24

두 차례의 리뷰 수정 라운드: 베트남어 로캘에 대한 Opus 리뷰(발견 9건)와 CAR-91/92 수정의 2차 리뷰(필수 5건) — 둘 다 적-녹(red→green) 테스트로 마감했습니다.

수정

  • vi 로캘 리뷰 라운드 (CAR-88 후속): project.subtitle / project.roleHint로 유출된 한글 글리프(신)를 수정(độ khớp tín hiệu) — EN 사전에 없는 CJK/가나 글리프가 vi 값에 있으면 실패하는 게이트를 함께 추가; docs/help/vi.md의 공백 포함 마크다운 URL 6개(what-is career-ops, apply for a job, set up playwright 외 3개)를 실제 가이드 슬러그로 하이픈 처리 — URL 내 공백 금지 게이트 추가.
  • 평가 헤딩 경계 (CAR-92 r2 H1): 블록 글자의 왼쪽 경계에서 &, ., /, '가 이제 단어 문자로 취급 — ## Q&A, ## Plan B, ## Ph.D. requirement, ## E-commerce는 더 이상 블록 A/B/D/E로 등록되지 않습니다; 행 끝에 홀로 놓인 터미네이터는 블록 단어 토큰(Block/блок/블록/…) 뒤에서만 허용되며, ASCII -는 철자 터미네이터 클래스에서 빠져 E-commerce/ई-मेल 헤딩이 살아남습니다.
  • 우크라이나어/러시아어 숫자 리포트 (CAR-92 r2 H2): А Б В Г Д Є Ж 시퀀스 감지기가 공유 헬퍼가 됩니다; 어떤 헤딩 레벨에서 3개 이상의 시퀀스 문자를 발견하면 그 레벨이 커터의 기준 레벨이 됩니다 — 문자가 붙은 ### A. 하위 섹션이 더 이상 블록 Б에서 리포트를 잘라내지 않습니다.
  • 이월 행 수명 주기 (CAR-92 r2 M1): 실패한 소스가 다음 스캔으로 이월하는 행은 carriedSince 스탬프를 받고, 30일 경과 또는 5회 연속 이월 시 제거되며, 신규 행 뒤에 추가됩니다 — 더 이상 영원히 조용히 재축적되지 않습니다.
  • Gemini synthesis 분모 + legitimacy (CAR-92 r2 M2) 및 서브프로세스 본문 점수 경로 (M3): 발견 파일에서 읽어 적-녹 테스트로 수정.
  • 콜드-로드 번들 드리프트 게이트가 vi 사전에 맞춰 갱신(스크립트 태그 90 / 번들 가능 87)되고 미사용 테스트 바인딩이 제거되었습니다 (CodeQL 572).

참고

  • 리뷰가 지적한 README.vi ‘최신 릴리스’ 배너의 어댑터 수 표현은 이전 버전 배너에 있으며 이미 EN README와 바이트 단위로 동일합니다 — EN↔vi 패리티 유지를 위해 손대지 않았습니다 (커밋 본문에 문서화).
  • 테스트 5686 (+26: eval-validate-opus-r2, scanner-carryover-r2, vi-게이트 단정). 레지스트리 변경 없음 — 121 소스 (116 EN + 5 RU) · 116 어댑터.

career-ops-ui v1.267.1 · page 1/9 · mirror of the project changelog for cvstart.org