체인지로그

GitHub

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

[1.92.0] — 2026-07-04

CV Studio (Epic 21). 새로운 #/cv-studio 페이지는 당신의 CV에 정직하고 대부분 로컬에서 동작하는 세 가지 도구를 제공합니다:

  • 이력서 진단 — 항목별 설명이 딸린 결정론적 0–100 점수(정량화된 임팩트, 약한 동사, 유행어, 길이, 핵심 섹션, 연락처 정보). 순수 클라이언트 측(window.CvDiagnostics) — LLM 없음, 지어낸 것 없음, 모든 발견 사항이 설명되어 당신이 무엇을 바꿀지 결정합니다.
  • 프라이버시 마스크 — CV를 샘플이나 스크린샷으로 공유하기 전에 개인정보(이메일, 전화번호, 링크/핸들, 도로명 주소, 그리고 선택적으로 이름 → 이니셜)를 가립니다. 전적으로 브라우저에서 실행되며(window.CvPrivacy), 정확히 무엇을 가렸는지 보고하고 원본은 절대 저장하지 않습니다.
  • 사람처럼 만들기 / 보이스 매칭 — 딱딱한 한 줄이나 문단을 붙여넣으면 당신의 목소리로 다시 쓰며, 서버 측에서 voice-dna.md 와 writing-samples/ 에 근거합니다. 엄격한 가드레일: 재배열, 다듬기, 재-보이싱은 가능하지만, 텍스트에 이미 없는 사실·지표·성과는 절대 새로 넣지 않습니다. 공유 제공자 캐스케이드를 통해 라이브로 실행되거나, 키 없이 복사-붙여넣기용 프롬프트를 돌려줍니다.

신규: server/lib/routes/cv-studio.mjs(20번째 라우트 모듈 — POST /api/cv-studio/humanize), public/js/views/cv-studio.js, public/js/lib/cv-diagnostics.js, public/js/lib/cv-privacy.js, PATHS.voiceDna + PATHS.writingSamplesDir. 16개 로케일 전체에 29개의 새 i18n 키. 테스트: tests/cv-diagnostics.test.mjs, tests/cv-studio-routes.test.mjs. (템플릿 갤러리, Word 내보내기, 채용공고 PDF 아카이브는 CV Studio 후속 작업으로 추적됩니다.)

[1.91.0] — 2026-07-04

네트워킹 및 심층 기업 리서치 (Epic 16). 새로운 #/networking 페이지는 기업 하나를 면접을 따내기 위한 실행 가능한 계획으로 바꿔주며, 당신의 CV·프로필·two-pager에 근거합니다:

  • 기업 도시에 — 기업이 무엇을 하는지, 인용할 만한 최근 신호, 그리고 당신의 실제 이력에서 끌어낸 “내가 적합한 이유” 훅을 담은 간결한 브리프.
  • 누구에게 연락할지 — 3–5명의 타깃 페르소나(채용 담당 매니저, 사내 리크루터, 팀의 시니어 IC, 따뜻한/동문 인맥)와 각자를 찾을 수 있는 구체적인 LinkedIn 검색 문자열. 실제 이름은 절대 지어내지 않습니다.
  • 가장 따뜻한 소개 경로 — 당신의 이력에 가장 현실적인 따뜻한 진입 경로(공통 고용주/학교/커뮤니티, 2촌 경로, 또는 신호가 강한 콜드 DM)와 그 이유.
  • 아웃리치 초안 — 상위 페르소나를 위한 짧고 구체적인 메시지로, 당신의 실제 증거 포인트에 근거합니다.
  • 라이브 또는 수동 — 아무 키로든 공유 제공자 캐스케이드를 통해 라이브로 실행되거나, 복사-붙여넣기용 프롬프트를 돌려줍니다(정직한 폴백, 지어낸 것 없음). 계획 저장은 완성된 계획을 사용자 계층(networking/net-{company}-{role}-{date}.md)에 보존하며, 페이지는 저장된 계획을 나열·열기·삭제합니다.

신규: server/lib/routes/networking.mjs(19번째 라우트 모듈), public/js/views/networking.js, PATHS.networkingDir. v1.90.0의 server/lib/llm-dispatch.mjs 캐스케이드를 재사용합니다. 16개 로케일 전체에 24개의 새 i18n 키. 테스트: tests/networking-routes.test.mjs.

[1.90.0] — 2026-07-04

Mock Interview 2.0 (Epic 15). 새로운 #/mock-interview 페이지가 이력서, 프로필, two-pager, 스토리 뱅크를 턴 바이 턴 면접 리허설로 바꿔 줍니다:

  • 대화형 연습 — 목표 직무(+ 선택적으로 회사 / JD)를 설정하면 면접관이 초점 있는 질문으로 시작합니다. 보내는 답변마다 구조화된 응답을 받습니다: Feedback(강점 + STAR+R 격차), Score(N/5), 그리고 직전 답변에서 가장 약한 부분을 파고드는 Next question. 서버 측에서 실제 자료에 근거하며 — 없는 경험을 지어내지 않습니다.
  • 스토리 뱅크 인식 — interview-prep/story-bank.md 가 프롬프트에 인라인으로 포함되므로(cv.md 와 동일한 신뢰 수준), 피드백이 당신의 가장 좋은 스토리를 가리켜 줄 수 있습니다.
  • 라이브 또는 수동 — 제공자 키가 있으면 해당 턴이 공유 제공자 캐스케이드(Anthropic → Gemini → OpenAI → Qwen → OpenRouter → GitHub Models)를 통해 라이브로 실행됩니다. 키가 없으면 바로 복사·붙여넣기할 수 있는 프롬프트를 받습니다(정직한 fallback, 지어낸 답변 없음).
  • 저장된 세션 — Save transcript 를 클릭하면 완료된 면접이 사용자 레이어(interview-prep/mock-{company}-{role}-{date}.md)에 보존됩니다. 페이지에서 저장된 세션을 나열, 열기, 삭제할 수 있습니다.

신규: server/lib/routes/interview.mjs(18번째 라우트 모듈), public/js/views/mock-interview.js, server/lib/llm-dispatch.mjs(공유 제공자 캐스케이드), PATHS.storyBank, bundleProjectContext({ extraFiles }). 16개 로케일 전체에 30개의 새 i18n 키. 테스트: tests/interview-routes.test.mjs.

[1.89.0] — 2026-07-04

후보자-시장 적합성 — two-pager (Epic 14). 새로운 #/two-pager 페이지에서 다음 직무에 대해 당신이 실제로 원하는 것을 담을 수 있습니다. Never Search Alone 의 “Mnookin two-pager” 를 본떴습니다:

  • 가이드 빌더 — 1인칭 “나는 누구인가” 서사, “목표 환경” 노트, 그리고 다섯 개의 칩 목록 편집기: loves, must-haves, hates, deal-breakers, non-negotiables. 상위 프로젝트의 사용자 레이어(config/two-pager.yml)에 PUT /api/two-pager 로 저장됩니다 — 시스템 업데이트로 덮어써지지 않습니다.
  • AI 채우기 도우미(POST /api/two-pager/draft) — 당신의 CV + 프로필이 인라인된, 바로 실행 가능한 Mnookin 프롬프트를 만들어 줍니다. 아무 LLM 에서 실행하고 결과를 다시 붙여넣으면 됩니다. 오직 당신 자신의 자료만 사용하며, 지어내는 것은 없습니다.
  • 원하는-것과의-적합도 배지 — #/scan 의 각 채용공고에 이제 ◎ N 적합도 점수(클라이언트 측, window.FitScore 경유)가 표시되어, 공고의 근무 형태, 국가, 최저 급여, 재배치를 당신의 two-pager 와 대조합니다. 설계상 정직합니다: 공고가 대조 가능한 신호를 주지 않으면 배지가 표시되지 않습니다(지어낸 숫자는 절대 없음). deal-breaker 위반은 가벼운 불호보다 더 무겁게 반영됩니다.
  • 모든 평가에 반영 — 저장된 two-pager 는 bundleProjectContext 에 인라인되므로, 이후의 모든 LLM 평가가 당신이 밝힌 선호와 CV-대-JD 매칭을 함께 반영합니다.

신규: server/lib/routes/two-pager.mjs, public/js/views/two-pager.js, public/js/lib/fit-score.js, PATHS.twoPager. 16개 로케일 전체에 27개의 새 i18n 키. 테스트: tests/two-pager-routes.test.mjs, tests/fit-score.test.mjs.

[1.88.0] — 2026-07-04

이슈 #29 마무리 — 스캔 i18n 공백 + API 위생.

  • 마지막까지 하드코딩되어 있던 스캔 문자열 현지화 (로드맵 v1.69.4): 소스 요약 알약(N개 신규 / M개 일치), N개 신규 채용공고 토스트, reloc 배지가 이제 t() 를 통과합니다 — 16개 로케일 전체에 4개의 새 키(scan.pillNew, scan.pillMatching, scan.newOffers, scan.relocBadge). 비영어 사용자는 핵심 스캔 흐름에서 더 이상 떠도는 영어를 보지 않습니다.
  • X-Powered-By 헤더 비활성화 (로드맵 v1.69.5): createApp() 내 app.disable('x-powered-by') — 서버가 더 이상 Express 를 광고하지 않습니다. (해당 에픽의 나머지는 이미 출시됨: parentVersion 이 release-please 주석을 제거하고, 라이트 모드 테마 토글, 라우트 변경 시 모달 닫힘, 보고서의 “Score”(rep.score) 현지화.)

테스트: tests/scan-i18n-gaps.test.mjs + tests/security-headers.test.mjs 의 X-Powered-By 부재 어서션.

[1.87.0] — 2026-07-04

인증 불필요 스캔 공급자 4종 추가 (상위 career-ops v1.16.0 동등성). 스캐너 레지스트리가 41 → 45개 어댑터(40 EN + 5 RU)로 늘어납니다 — 모두 공개, 무인증, 호스트 고정, redirect:'error'(SSRF 안전)이며 각각 CI 격리 테스트를 포함합니다:

  • Get on Board (getonbrd) — 포털 전체 공개 JSON:API(LATAM/원격 기술), 공급자 선택, 페이지네이션. server/lib/sources/getonbrd.mjs.
  • Amazon (amazon) — amazon.jobs 공개 검색 JSON, 호스트 감지 또는 provider: amazon, 오프셋 페이지네이션. server/lib/sources/amazon.mjs.
  • Avature (avature) — 테넌트별 *.avature.net ATS, HTML 파싱, 호스트 감지 또는 provider: avature. server/lib/sources/avature.mjs.
  • SAP SuccessFactors (successfactors) — 테넌트별 RMK 타일 목록(*.successfactors.eu/.com, jobs2web.com), HTML 파싱. server/lib/sources/successfactors.mjs.

각 항목은 sources/<slug>.mjs(자동 발견 meta → #/scan 드롭다운) 및 ALL_ADAPTERS 내의 portals/adapters/<slug>.mjs(두 레지스트리 규칙) + tests/sources-<slug>.test.mjs 를 제공합니다. ALL_ADAPTERS 개수와 정렬된 id 및 /api/scan/sources EN 세트 어서션이 36→40 으로 올랐고, GET /api/scan/sources 는 이제 45개를 나열합니다.

[1.86.0] — 2026-07-03

목표 직무별 통계 (#/stats) — 당신의 목표 직무에 대한 시장 채용공고 및 급여 통계. 새 분석 페이지가 프로필의 목표 직무(config/profile.yml → 하드코딩 아님)와 최신 스캔의 공고를 읽어, 직무 및 국가별로 다음을 표시합니다:

  • 국가별 채용공고 수와 국가별 급여 중앙값(USD) — 스캐너가 이미 수집하는 희소한 데이터로부터 클라이언트 측에서 집계됩니다(public/js/lib/role-stats.js, window.Countries 재사용). 임의 통화의 급여는 명시적으로 근사치인 FX 표를 통해 USD로 정규화되며, 표본 크기 주의 사항이 함께 표시됩니다 — 결코 조작되지 않습니다.
  • 직무 및 국가 필터와 직접 작성한 인라인 SVG 막대·추세 차트(새 의존성 없음, CSP 안전 — addEventListener 전용).
  • 스냅샷 저장(POST /api/stats/snapshot)은 현재 집계를 data/role-stats.jsonl 에 저장하며, 추세 차트(GET /api/stats/trend)는 시간 경과에 따른 채용공고 수를 추적합니다 — “다이내믹스” 뷰. 정직한 하이브리드: 스냅샷은 로컬 스캔 데이터에서 나오며 요청 시 새로 고쳐집니다.
  • 16개 로케일 전체에 완전 현지화(신규 i18n 키 26개).

신규: server/lib/routes/stats.mjs(16번째 라우트 모듈), public/js/lib/role-stats.js, public/js/views/stats.js, PATHS.roleStats; 테스트 role-stats.test.mjs(7) + stats-routes.test.mjs(5).

[1.85.0] — 2026-07-03

독일어, 이탈리아어, 터키어 로케일 (상위 career-ops v1.16.0 로케일 패리티). 이제 UI가 16개 언어로 제공됩니다 — 기존 13개에 de 🇩🇪, it 🇮🇹, tr 🇹🇷 가 추가되었습니다.

  • 전체 UI 번역 — public/js/lib/locales/i18n-dict.{de,it,tr}.js 에서 730개 i18n 키 모두 번역; 언어 전환기에 Deutsch / Italiano / Türkçe 가 표시되고 브라우저 언어 자동 감지가 de/it/tr 를 인식합니다(public/js/lib/i18n.js).
  • 인앱 도움말 가이드 — docs/help/{de,it,tr}.md 번역(전체 19 H2 / 75 H3 구조), GET /api/help/:lang 로 제공됩니다.
  • 문서 — README.{de,it,tr}.md 및 CHANGELOG.{de,it,tr}.md 추가; CHANGELOG 로케일 패리티 게이트가 이제 15개 비 EN 로케일을 커버합니다.
  • 프롬프트 스캐폴딩 — server/lib/prompts.mjs(LOCALE_NAMES + SCAFFOLD_STRINGS)가 세 개의 새 로케일에 맞게 현지화되어, LLM 출력이 UI 언어를 따릅니다.

모든 패리티 게이트(i18n-locale-files, i18n-coverage, check-changelog-parity, lang-switcher-rtl)가 16개 로케일 세트로 확장되었습니다.

[1.84.0] — 2026-06-30

재적용 쿨다운 + pipeline.md 내 보상 정보 (상위 career-ops v1.15.0 패리티). 두 가지 스캐너 업그레이드:

  • 재적용 쿨다운 (#1201): EN 스캔이 최근 지원한 회사의 역할을 건너뛰어 결과가 새로운 채용 공고에만 집중됩니다. config/profile.yml의 re_apply_windows: 아래에서 회사별 기간을 설정할 수 있습니다(last_apply_date, same_role_days, applied_to: [roles], 선택적 cross_role_bucket); 회사 매칭은 구두점 비민감 + 단어 경계 방식입니다(server/lib/cooldown.mjs). 키가 없으면 비활성화; 스캔 로그에 Cooldown skipped: N이 표시됩니다.
  • pipeline.md 내 보상 정보 (#1017): 스캔된 채용 공고가 이제 선택적 후행 열로 급여를 data/pipeline.md에 저장합니다(url | <salary>). URL은 중복 제거 키로 유지되고(| comp 열은 읽을 때 제거됨), 셀은 정제되며(행/열 주입 없음, 수식 선두 중화), 일반 URL 파이프라인은 하위 호환성을 유지합니다.

tests/cooldown.test.mjs 및 pipeline 보상 테스트 포함. 소스 수는 41개 유지 — 두 기능 모두 스캔 로직 업그레이드이며, 새 보드가 아닙니다.

[1.83.0] — 2026-06-30

재게시 / 유령 공고 감지기 (상위 career-ops v1.15.0 패리티). #/scan에 새로운 🔁 재게시 / 유령 공고 패널이 추가되어, 90일 롤링 윈도우 내에서 다른 URL로 재게재된 회사+직무 클러스터를 표시합니다 — 오래된 채용 파이프라인과 유령 공고의 신호입니다. 퍼지 직무 제목 매처(server/lib/role-matcher.mjs)와 data/scan-history.tsv에 대한 읽기 전용 감지기(server/lib/detect-reposts.mjs)를 기반으로 하며, GET /api/scan/reposts를 통해 노출됩니다. 또한: /api/health의 parentVersion이 이제 순수 semver만 반환합니다(릴리스-플리즈 # x-release-please-version 주석이 제거됨). tests/detect-reposts.test.mjs 포함. 소스 수는 41개 유지 — 재게시는 분석 기능으로 새 보드가 아닙니다.

[1.82.0] — 2026-06-30

NoDesk 스캔 소스 (상위 career-ops v1.15.0 패리티). 보드 전체 NoDesk 원격 채용 RSS 피드가 이제 1급 스캔 소스입니다 — provider: nodesk 항목을 추가하면 #/scan의 Source 드롭다운에 표시됩니다(총 41개 어댑터: EN 36 + RU 5). 호스트는 nodesk.co로 고정되고 redirect:'error'(SSRF 방지); 제목은 Role at Company로 분리됩니다(NoDesk에는 위치 태그가 없어 위치는 빈 채로 유지); 모든 항목은 원격. CI 격리 tests/sources-nodesk.test.mjs 스위트 포함; 전체 유닛 테스트 스위트 1523개 통과.

[1.81.0] — 2026-06-29

상위 career-ops 패리티 — 13개의 새로운 채용 보드 스캔 소스. Fighter90/career-ops main의 최신 프로바이더 배치를 인프로세스 스캐너에 이식합니다. 보드 전체 공개 API(프로바이더 선택): Arbeitnow, Himalayas, Jobicy, Landing.jobs, 4 Day Week, The Muse, The Hub, Jobspresso(RSS), 그리고 Hacker News “Who is hiring?”(Algolia 2단계). 폴란드 보드(호스트 또는 provider: 감지): JustJoin.it 및 NoFluffJobs(POST 검색). 테넌트별 ATS(careers_url에서 자동 감지): Pinpoint(<slug>.pinpointhq.com/postings.json) 및 Rippling(ats.rippling.com/<slug> → api.rippling.com 보드). 모든 소스는 redirect:'error'로 호스트 고정(SSRF 방지)되며 #/scan Source 드롭다운에서 선택 가능합니다 — 레지스트리는 이제 40개 스캐너 어댑터(EN 35 + RU 5)를 제공합니다. 소스별 CI 격리 테스트 스위트 13개 추가; 전체 유닛 테스트 스위트 1513개 통과.

[1.80.0] — 2026-06-28

스캔 5가지 개선(job-crawler 아이디어 재구현). (1) Teamtailor 소스 — <slug>.teamtailor.com 테넌트 채용 사이트를 공개 /jobs.rss 피드로, careers_url에서 자동 감지(호스트 고정 + redirect:'error'); 레지스트리는 이제 27개 어댑터. (2) 소스 격리 — 영구 404/410을 반환한 소스를 data/scan-quarantine.json에 기록하고 이후 스캔에서 건너뜀(자가 치유: 14일 후 재시도). (3) 소스당 최대 — #/scan의 선택 필드로 보드당 채용 수 제한(기본 ∞). (4) 게시 기간 — 클라이언트 측 기간 필터(24시간 / 7일 / 30일). (5) 저장된 검색 + ★ 즐겨찾기 — 필터 세트를 이름 지어 재사용하고 채용을 별표; localStorage에 방어적 검증과 함께 저장(손상된 캐시는 깔끔히 초기화); 결과 캐시는 스캔 전에 초기화되고 실시간으로 다시 채워집니다.

[1.79.0] — 2026-06-28

WeWorkRemotely 스캔 소스 (상위 career-ops v1.14.0 패리티). We Work Remotely의 보드 전체 원격 채용 RSS 피드가 이제 1급 스캔 소스입니다 — provider: weworkremotely 항목을 추가하면 #/scan의 Source 드롭다운에 표시됩니다(총 26개 어댑터). 호스트는 weworkremotely.com으로 고정되고 redirect:'error'(SSRF 방지); 제목은 Company: Role로 분리됩니다. 또한 title_filter 키워드는 길이 검사 전에 트림됩니다(parent #1261).

[1.78.2] — 2026-06-27

i18n·UX 보강 (v1.78.1 후속 수정). 로고의 접근성 이름이 13개 언어 전체로 현지화되었습니다(nav.logoHome). 이미 #/scan에 있는 상태에서의 전역 검색 Enter는 강제 리렌더링하여 미리 채운 검색어를 잃지 않습니다(동일 경로 가드). health.title가 폴란드어(Kondycja)와 덴마크어(Systemtilstand)로 번역되었습니다(이전엔 영어). 테스트 1235 → 1238.

[1.78.1] — 2026-06-27

Scan UX 수정. #/scan 결과 테이블이 이제 스캔 중 실시간으로, 그리고 완료 후 한 번 더 자동 새로고침됩니다(수동 새로고침 불필요). 상단바 전역 검색은 Enter 힌트를 표시하고, URL이 아닌 검색어에서는 검색창이 채워진 채로 #/scan으로 이동합니다(이전엔 #/tracker). 로고는 이제 대시보드(홈)로 연결됩니다.

[1.78.0] — 2026-06-27

Scan 페이지에 지역 필터 — 국가별(국기 포함)로 채용 결과 필터링. #/scan의 새 국가 드롭다운은 결과에서 감지된 각 국가(국기 이모지 + 개수)를 나열하여 특정 국가에 연결된 공고만 남길 수 있습니다. Remote/Hybrid/Onsite 근무 형태 필터와 함께 사용해 국가 기반·원격 모두 검색할 수 있습니다. 새 countries.js 헬퍼가 공고의 자유 텍스트 위치(국가명·별칭·주요 도시 ~100개)를 ISO 국가 + 국기로 매핑하며, 감지는 보수적이라 추측하지 않습니다.

[1.77.0] — 2026-06-27

덴마크어(Dansk)를 13번째 인터페이스 언어로 추가. UI, 인앱 도움말 가이드(19 H2 / 75 H3), README, CHANGELOG의 완전한 덴마크어 번역. 덴마크어가 국기 언어 선택기에 추가되었고, i18n 체계(어셈블러, 감사, 패리티 게이트, 스냅샷)가 이제 13개 로케일을 포함합니다.

[1.76.0] — 2026-06-26

상위 career-ops v1.13.0 패리티 — 신규 소스 6종, 스캐너 강화, 결과 테이블 상한 제거.

추가

  • 테넌트별 ATS 소스 6종 — BambooHR, Breezy HR, Comeet, Personio, Recruitee, SolidJobs. careers_url 호스트로 자동 감지(Comeet은 전체 api: 필요). 각 호스트는 앵커 정규식 + redirect:'error'로 고정(SSRF 방지). #/scan의 Source 드롭다운에서 선택 가능 — 레지스트리는 이제 25개 어댑터(EN 20 + RU 5). Personio XML 피드용 fetchText 헬퍼 추가.
  • trust_filter — 선택적 신뢰 점수(0–100, 레벨 high/medium/low, 플래그). 주석만. high 미만 행은 #/scan에 언어 중립 ⚠ 배지 표시. 채용공고는 절대 제거하지 않음.
  • Arbeitsagentur remoteMatch + remoteMaxPages — 설정 기반 원격 감지: title, filter(서버측 homeoffice=nv_true + 페이지네이션), off.

변경

  • 스캔 결과 상한 없음. 표시 상한 MAX_STORED_RESULTS(2000) 제거 — 일치하는 모든 공고를 저장하고 #/scan 테이블이 페이지로 표시(200/페이지).
  • 제목 필터 견고성 — 짧은 약어(COO, SDR…)는 단어 경계로 매칭; 잘못된 title_filter 설정이 스캔을 중단시키지 않음. ATS·지역 스캐너 모두.

테스트

  • +32 케이스(1190 → 1222): sources-ats-providers, title-filter, arbeitsagentur-remote, trust-validator, 재작성된 scan-result-cap “상한 없음” 가드.

[1.75.2] — 2026-06-19

docs: v1.75.0 스캐너 집계기에 대한 12개 로케일 전체의 완전한 문서 패리티. 코드 변경 없음 — v1.75.0에 들어온 일곱 개 소스에 맞춰 사용자 대상 문서를 정렬합니다:

  • 도움말 가이드(12개 로케일). §5에 content_filter 블록(설명/스니펫 키워드 게이팅, location_filter의 형제)과 집계기 노트가 추가됩니다. §7은 원클릭 스캔 스윕과 전체 Source 드롭다운 열거에 일곱 개의 새 소스를 나열합니다. §17의 어댑터 수가 오래된 “11 adapters”에서 “19 adapters — 14 English + 5 Russian”으로 수정됩니다. ##/### 헤딩이 추가되지 않았으므로 게이팅된 19 H2 / 75 H3 구조는 변경되지 않습니다.
  • README(9개 전체 로케일). 스캔 소스 아래에 새 “Aggregator boards (v1.75.0)” 항목이 추가되고, 릴리스 배지가 v1.75.2로 갱신됩니다. (축약된 pl/uk/ar README에는 소스별 목록이 없으며 그 부분은 의도적으로 그대로 둡니다.)
  • 참조 문서. docs/portals-examples.md에 일곱 개 모두에 대한 정확한 provider: / <provider>: 설정 블록을 담은 복사-붙여넣기용 “Aggregator boards” 섹션이 추가됩니다. docs/PROJECT.md는 19 adapters로 갱신됩니다. docs/sdd/CONVENTIONS.md는 두 레지스트리 구분(드롭다운용 sources/registry.mjs 대 페칭용 portals/registry.mjs), opts.company로 전달되는 provider: 기반 집계기 선택, 스캔 쓰기 새니타이저(scan-sanitize.mjs), 그리고 v1.75.1 테스트 수(1190)를 문서화합니다.
  • QA. qa/QA-REGRESSION-PROMPT-v1.75.2-FULL.md 추가 — v1.75.x 스캔 집계기 사이클에 맞춰 갱신된 전체 표면 릴리스 게이트 드라이버.

[1.75.1] — 2026-06-19

fix(scan): v1.75.0의 구성 기반 소스에 대한 견고성 다듬기. 릴리스 후 검토에서 나온 세 가지 작은 강화 수정입니다(정상 스캔에서는 동작 변화 없음):

  • Abort 인지 페이지네이션 지연. Glints(300 ms) 및 Jobstreet/SEEK(200 ms) 페이지 간 예의 대기는 이제 server/lib/http-json.mjs의 새 delay(ms, signal) 헬퍼를 통해 스캔의 AbortSignal이 발생하면 즉시 해제됩니다. 따라서 연결이 끊긴 클라이언트가 페이지네이션 중인 스캔을 추가 대기 시간 동안 붙잡아 둘 수 없습니다.
  • 설명적인 비JSON 오류. fetchJson은 이제 비JSON 2xx 본문(예: 상태 200으로 제공되는 HTML 유지보수 페이지)을 노출되는 SyntaxError 대신 non-JSON 2xx response from <url>로 래핑합니다. 따라서 스캐너의 소스별 오류 로그가 오작동하는 엔드포인트를 명시합니다.
  • 더 강력한 스캔 쓰기 정규화. normalizeScanScalar은 이제 \r \n \t에 더해 수직 탭, 폼 피드, 그리고 유니코드 줄/문단 구분자(\v \f U+2028 U+2029)까지 축약합니다 — 엄격한 상위 집합이므로, 스프레드시트나 뷰어가 인식할 수 있는 어떤 레코드/줄 구분자도 scan-history.tsv에 살아남지 않습니다.

[1.75.0] — 2026-06-19

feat(scan): 상위 career-ops v1.12.0 패리티 이식 — 신규 채용 소스 7종, 콘텐츠 필터링, 보안/품질 수정. web-ui는 자체 인프로세스 스캐너를 실행하며(상위의 scan.mjs로 셸 아웃하지 않음) 따라서 상위 v1.12.0의 프로바이더 및 스캔 변경 사항은 자동으로 전파되지 않습니다 — 이번 릴리스는 적용 가능한 변경 사항을 web-ui의 어댑터 계약에 맞춰 재구현합니다.

  • 신규 스캐너 소스 7종. 보드 전반 원격 집계기 3종 — RemoteOK, Remotive, Working Nomads — 은 자동 탐색되는 server/lib/sources/*.mjs 패턴에 들어맞습니다(provider: remoteok / remotive / workingnomads 로 선택). 설정 기반 지역 집계기 4종 — IBM careers, Arbeitsagentur(독일 연방고용청), Glints(동남아시아), Jobstreet / SEEK — 은 항목별 <provider>: 설정 블록을 읽으며, en-scanner는 이제 해결된 회사 항목을 각 페처까지 전달하여 페처가 이를 읽을 수 있게 합니다. 일곱 개 모두 #/scan 소스 드롭다운에 자동으로 표시됩니다.
  • content_filter (상위 #974). 게시물을 설명/스니펫 텍스트로 게이팅하는 선택적 portals.yml 블록(positive / negative 키워드 목록) — location_filter 시맨틱을 따르며, 설명이 없는 게시물은 항상 통과합니다. EN 및 RU 스캐너 모두에 연결되었습니다.
  • 스캔 쓰기 강화 (상위 #1098). 외부 피드 메타데이터는 이제 data/scan-history.tsv 및 data/pipeline.md에 기록되기 전에 정제됩니다: 제어 문자가 축약되고(회사/직함의 줄바꿈이 더 이상 TSV 행을 주입할 수 없음) 선행 = + - @ 는 스프레드시트 수식 주입에 대비해 무력화됩니다.
  • Ashby secondaryLocations (상위 #1073). Ashby 소스는 이제 각 보조 위치의 지역 레이블과 우편 addressLocality / addressCountry 를 위치 문자열에 접어 넣으므로(중복 제거), 기본 레이블이 예컨대 “Canada”로 표시되는 EU 자격 직무가 location_filter에 노출됩니다.
  • 평가 보고서 형태 검증 (상위 #819). /api/evaluate의 인프로세스 프로바이더(Anthropic / OpenAI / Qwen / OpenRouter / GitHub Models)는 이제 형식이 잘못된 A–G / SCORE_SUMMARY 보고서를 치명적이지 않은 warnings 배열로 플래그합니다; Gemini 평가 경로는 이미 상위 gemini-eval.mjs로부터 해당 가드를 상속합니다.
  • docs: Antigravity CLI를 12개 README 전반의 지원 어시스턴트 목록에 추가(Gemini 프로바이더에 매핑).

상위의 git pull에서 무상으로 상속됨(web-ui가 이들로 셸 아웃함): 일본어 CJK PDF 폰트 폴백(#1053), ATS 안전 PDF 폰트(#1074), LaTeX CJK 가드(#1054), tracker/merge/followup/dashboard 수정, 그리고 modes/zh 중국어 모드(web-ui는 모드를 동적으로 나열함).


[1.74.3] — 2026-06-18

docs(parent-source): 부모 career-ops 저장소를 Fighter90/career-ops 포크로 변경. web-ui는 이제 실제 소스로 쓰이는 모든 곳에서 메인테이너의 포크를 부모 프로젝트로 참조합니다: bin/setup.sh 설치 스크립트의 CAREER_OPS_REPO 클론 기본값, 12개 README의 모든 git clone / “위에 올라가는” / 온보딩 링크, 그리고 에이전트 문서(CLAUDE.md, AGENTS.md, GEMINI.md, .github/copilot-instructions.md, docs/). 작성자 santifer 크레딧(및 비공식 UI 고지)은 변경되지 않았으며 — 소스/클론 URL만 이동했습니다. tests/sh-files.test.mjs는 이제 설치 스크립트가 포크를 클론하는지 검증합니다.


[1.74.2] — 2026-06-17

fix(health): #/health 및 /api/status/providers에서 GITHUB_MODELS_API_KEY를 선택적 점검 항목으로 노출. v1.74.0의 GitHub Models 프로바이더는 #/config에서 설정할 수 있었지만 Health 페이지에 행이 없었고 keysConfigured 프로바이더 표면에서 누락되어 있었습니다. 선택적 점검 항목(다른 다섯 개 라이브 평가 프로바이더와 동일한 “set / unset (manual mode)” 문구)과 github(+ 해당 GITHUB_MODELS_MODEL)를 /api/status/providers에 추가하여, 활성 프로바이더 라우팅과 Health 페이지가 이제 여섯 개 모두를 반영합니다. tests/api.test.mjs 헬스 행 테스트를 여섯 개 프로바이더 모두로 확장했습니다.


[1.74.1] — 2026-06-17

docs + test: README “AI 어시스턴트 설치” 섹션; Gemini 커넥터의 전체 브랜치 커버리지. README에 설치/로그인 표 추가 — Claude Code / Gemini CLI / Codex / Qwen Code / OpenCode / GitHub Copilot CLI 설치 링크 + 각각의 #/config 공급자 매핑 + “계속하기 전에 로그인” (career-ops.org/docs 빠른 시작 반영; web-ui가 CLI 없이도 사용 가능한 독립형 대안임을 명확히 설명). 새로운 tests/gemini-connector.test.mjs (8개 케이스)는 runGemini의 모든 브랜치를 다룸 — 키 없음, 성공, API 오류, 빈/차단된 완료, 잘못된 형식의 본문, 타임아웃, 네트워크 오류, hasGeminiKey — server/lib/gemini.mjs를 구문 100%로 끌어올림. 전체 커버리지: 96% 라인 / 88% 브랜치 / 96% 함수. 스위트 1126 → 1134.


[1.74.0] — 2026-06-17

feat(llm): GitHub Models (Copilot)을 6번째 프로바이더로 추가 + 6개 어시스턴트 표준 정렬. career-ops.org/docs에는 여섯 가지 AI 코딩 어시스턴트가 나열되어 있습니다 — Claude Code, Gemini CLI, Codex, Qwen Code, OpenCode, GitHub Copilot CLI. 이제 web-ui는 여섯 가지 모두를 지원합니다: 다섯 개는 기존 라이브 프로바이더(Anthropic / Gemini / OpenAI / Qwen / OpenRouter)에 매핑되고, GitHub Copilot CLI는 전용 GitHub Models 커넥터 — runGitHubModels (OpenAI-compatible; models 스코프를 가진 GitHub PAT)를 갖게 됩니다. 이는 #/config에서 (GITHUB_MODELS_API_KEY + GITHUB_MODELS_MODEL)로 설정하고 LLM_PROVIDER=github를 통해 선택할 수 있으며 auto 순서에서 6번째입니다. 도움말 번들과 README는 이제 표준 여섯 가지를 나열하고(Qwen CLI→Qwen Code로 이름 변경; Gemini CLI + GitHub Copilot CLI 추가), README에는 모든 기능이 상위 프로젝트로 추적될 수 있도록 전체 모드 참조 및 포털 어댑터 링크 테이블이 career-ops.org/docs에 추가됩니다. tests/llm-provider-context.test.mjs는 여섯 프로바이더 모두로 페치 경계 매트릭스를 확장하고(cv.md + profile.yml 인라인 + 아티팩트 반환), 새로운 GITHUB_MODELS_* 키가 12개의 모든 로케일 사전에 추가됩니다. 스위트 1125 → 1126.


[1.73.0] — 2026-06-17

feat(llm): 범용 Gemini 커넥터 + 모든 공급자에 걸쳐 검증된 CV/프로필 컨텍스트. server/lib/gemini.mjs (runGemini)를 추가했습니다 — Anthropic / OpenAI 호환 클라이언트와 동일한 {markdown, usage, error} 형태를 반환하는 외부 의존성 없는 Gemini generateContent 클라이언트입니다. 수정 사항: /api/mode/:slug와 /api/deep이 이전에는 채용 공고 전용인 gemini-eval.mjs를 통해 프롬프트를 라우팅하여, Gemini Run live가 요청된 결과물(자기소개서, 아웃리치, 브리프) 대신 평가를 반환했습니다. 이제 bundleProjectContext와 함께 runGemini를 호출하므로 cv.md + config/profile.yml이 다른 모든 공급자와 동일하게 Gemini에도 인라인으로 삽입되어 편지와 브리프가 상세하고 개인화됩니다. 새로운 tests/llm-provider-context.test.mjs는 각 공급자의 HTTP 경계를 목킹하고 다섯 개 공급자(Anthropic / Gemini / OpenAI / Qwen / OpenRouter) 모두 cv.md + profile.yml을 인라인으로 삽입하고 결과물을 반환함을 검증합니다(mode + deep + evaluate 매트릭스, 9개 케이스). /api/evaluate는 채용 공고 전용 gemini-eval.mjs를 유지합니다. Suite 1116 → 1125.


[1.72.0] — 2026-06-17

feat(modes): Run live 가 이제 최종 결과물을 직접 반환합니다 (단일 실행 출력 계약). 상위 modes/<slug>.md 템플릿은 Claude Code 대화형 세션을 위해 작성되었으며, 일부(cover, contacto 등)는 결과를 생성하기 전에 확인 질문을 위해 일시 중지합니다. 이로 인해 웹 UI의 Run live 가 결과물 대신 설문지를 출력했습니다. buildModePrompt 는 이제 모든 모드를 비대화형 출력 계약으로 감쌉니다: 분석(JD 분석, 회사 노트, ATS 키워드, 프로필↔JD 간극, 톤/방향 선택)을 자동으로 수행하고, 템플릿이 보통 물어볼 내용에 대해 cv.md / config/profile.yml 에서 합리적인 기본값을 선택하며, 최종 결과물만 출력합니다 — 모드별 «output ONLY {the cover letter / outreach message / …}» 알림으로 마무리됩니다. 이제 #/cover 에서 Run live 를 클릭하면 커버 레터 자체가 반환됩니다. 동일한 수정이 모든 일반 모드(cover, contacto, interview-prep, project, training, followup, patterns)의 12개 로케일 전체에 적용됩니다(결과물은 로케일 지시어를 통해 UI 언어로 작성됩니다). Suite 1103 → 1116.


[1.71.2] — 2026-06-17

docs(i18n): 문서 일관성 검토 결과를 게시합니다. 각 README의 “Translations of this guide” 블록에 이제 11개의 형제 언어가 모두 나열되며(이전에는 일부가 English/Français를 생략하거나 자기 자신에 링크), 섹션 구분 앞의 빈 줄이 복원되었습니다. 전체 QA 회귀 프롬프트가 현재 버전으로 이름이 변경되었고, 문서(CLAUDE.md, CONVENTIONS, LOCALIZATION, PROJECT-CONTEXT)가 현재 버전 및 테스트 수(1103)와 동기화되었습니다. 코드 또는 동작 변경 없음 — 문서만 변경되었으므로 도움말/UI 번역 및 1.70.0–1.71.1의 모든 기능은 변경되지 않습니다.


[1.71.1] — 2026-06-17

fix(i18n): 앱 내 도움말 가이드가 이제 12개 언어 모두에서 완전히 번역되었습니다. docs/help/{pl,uk,ar}.md를 추가하여 (각 파일은 검증된 19 H2 / 75 H3 구조를 포함) #/help가 영어로 폴백하는 대신 폴란드어, 우크라이나어, 아랍어 네이티브 번들을 제공합니다 — GET /api/help/{pl,uk,ar}가 이제 해당 로케일을 반환합니다. 모든 도움말 게이트(help-ui, help.test, help-ru-config-section, canonical-docs-coverage)에 연결되었습니다. 또한 12개 언어 번역 목록을 모두 완성했습니다: README의 «Translations of this guide» 블록(9개 README), 로컬화된 CHANGELOG의 «Translations:» 헤더(8개 파일), 그리고 오래된 문서 수치를 업데이트했습니다. Suite 1100 → 1103.


[1.71.0] — 2026-06-16

feat(cover): #/cover에서 바로 자기소개서 PDF를 생성합니다. v1.70.0에 추가된 cover 모드는 편지 텍스트를 생성하며, 이제 결과 화면에 Generate PDF 버튼이 제공되어 공유 인라인 markdown→PDF 파이프라인(POST /api/stream/pdf/inline → generate-pdf.mjs)을 통해 렌더링됩니다. 이는 interview-prep이 사용하는 것과 동일한 경로입니다. 이제 SPA를 벗어나지 않고도 편지를 작성하고 PDF를 제출할 수 있습니다.

test/docs: v1.70.0 검토 강화. cover 모드(허용 목록 + 프롬프트 조립), 국기 <select> 스위처 + 아랍어 RTL(dirFor/<html dir>), 모든 로케일의 top.langLabel, 자기소개서 PDF 연결, prompts.mjs 로케일 지시문 + fr/pl/uk/ar 스캐폴딩에 대한 CI-격리 커버리지를 추가했습니다. docs/sdd/CONVENTIONS.md 및 전체 프로젝트 QA 회귀 프롬프트에서 오래된 «8개 전체» → 12개 로케일 참조를 업데이트했습니다.


[1.70.0] — 2026-06-16

feat(i18n): 세 가지 새로운 UI 언어 — 폴란드어(pl), 우크라이나어(uk), 아랍어(ar, 완전한 RTL 지원 포함) — 를 추가하여 SPA를 12개 로케일로 확장하고, 상위 career-ops README의 모든 언어와 일치시킵니다. 각 새 로케일은 697개 키로 구성된 완전한 사전(public/js/lib/locales/i18n-dict.{pl,uk,ar}.js)을 제공하며, 기존의 패리티 / 커버리지 / 라틴 누락 방지 / 개인 데이터 없음 테스트 스위트로 검증됩니다. 아랍어는 진정한 오른쪽에서 왼쪽 지원을 추가합니다: i18n.js는 RTL 로케일에 대해 <html dir="rtl">을 설정하고, app.css의 범위가 지정된 [dir="rtl"] 블록이 크롬(사이드바, 알림 드로어, 마크다운 테이블/블록 인용, 인라인 간격)을 미러링합니다 — LTR 로케일은 바이트 단위로 변경되지 않습니다. 새로운 top.langLabel 키(×12)가 스크린 리더를 위해 선택기 이름을 지정합니다.

feat(ui): 플래그 아이콘 <select> 언어 전환기가 래핑 버튼 행을 대체합니다. 12개 로케일에서 이전 .lang-btn 행은 사이드바에서 세 줄로 줄 바꿈되었습니다. 네이티브 <select>(각 옵션 앞에 국기 이모지)는 깔끔하게 확장되고, 기본적으로 키보드 및 스크린 리더 친화적이며, CSP에 안전합니다(addEventListener를 통한 변경 핸들러, 인라인 JS 없음). 플랫폼에 국기 글리프가 없는 경우 국기는 지역 문자로 대체되므로 언어 레이블이 항상 핵심 식별자입니다.

feat(cover): 상위 프로젝트의 자기소개서 모드(career-ops v1.10.0 + v1.11.0 인사말)를 SPA로 이식합니다. 지원 탐색 그룹 아래에 새로운 #/cover 페이지를 추가하고, 범용 모드 러너 위에 구축합니다: 채용 공고 설명 + 회사/역할 + 선택적 인사말 → cv.md / modes/_profile.md에서 생성된 맞춤형 편지. 서버 MODE_ALLOWLIST에 cover를 추가하고 cover.* i18n 블록(×12 로케일)을 추가했습니다.

chore(compat): 상위 career-ops v1.11.0을 추적합니다. 읽기/쓰기 계약이 그대로 유지됨을 확인했습니다 — data/applications.md는 마크다운 진실 소스로 유지되며(v1.11.0 SQLite 트래커 인덱스는 파생 캐시), 트래커 열은 여전히 헤더 매핑됩니다. parentVersion은 이제 1.11.0을 보고합니다.

fix(i18n): 프랑스어(v1.61.0에 추가)가 server/lib/prompts.mjs의 LOCALE_NAMES 및 SCAFFOLD_STRINGS에 누락된 잠재적 결함을 수정합니다 — 프랑스어 LLM 호출이 자동으로 영어 출력 및 영어 스캐폴딩으로 대체되었습니다. fr/pl/uk/ar가 이제 모두 프롬프트 로케일 경로에 연결되었습니다.

알려진 후속 작업: 앱 내 도움말 가이드(docs/help/)는 pl/uk/ar의 경우 영어로 대체됩니다(UI 크롬 자체는 완전히 현지화됨); 상위 프로젝트의 대화형 인터뷰 온보딩, 역방향 ATS 검색, 새로운 스캔 제공업체는 아직 SPA에 노출되지 않았습니다.


[1.69.2] — 2026-06-12

fix(test): npm test가 실제 config/profile.yml과 data/scan-history.tsv를 덮어쓰던 테스트 격리 누수를 수정합니다. tests/critical-fixes.test.mjs가 파일 상단에서 prompts.mjs(→ paths.mjs)를 가져왔기 때문에, before()가 CAREER_OPS_ROOT를 임시 디렉터리로 설정하기 전에 PROJECT_ROOT가 실제 부모로 해석되어 PUT /api/profile가 매 실행마다 “Acceptance Test” 픽스처를 실제 프로필에 기록했습니다. 수정: prompts.mjs를 before() 안에서 동적 import()로 로드. 새 tests/test-root-isolation.test.mjs(2개 케이스)가 전체 스위트를 이 패턴으로부터 보호합니다. 프로덕션 코드 변경 없음. 스위트 1084 → 1086.


[1.69.1] — 2026-06-12

fix(scan): #/scan 가 대규모 지역 스윕을 조용히 잘라내지 않습니다. 리전별 표시 집합이 500개로 고정되어 있었습니다(실제 RU 스캔에서 일치 1352개 중 500개만 표시, 852개 숨김 — “2000개 스캔, 약 600개 표시” 증상). 두 스캐너 모두 공유 및 환경 변수로 재정의 가능한 상수 MAX_STORED_RESULTS(기본 2000, SCAN_MAX_RESULTS로 재정의)를 사용합니다. 표시 전용 — pipeline.md / scan-history.tsv 추가는 이미 잘리지 않은 집합을 사용했습니다. fix(health/ui): #/health 점검 카드가 더 이상 넘치지 않습니다. 긴 이름/값이 Fix → 버튼 및 상태 배지와 충돌했으나, 이제 .health-check-row로 축소·줄바꿈됩니다. 새 테스트 scan-result-cap + health-card-overflow. 스위트 1079 → 1084.


[1.69.0] — 2026-06-12

feat(scan): 스캐너 어댑터 자동 발견 (P-14) — server/lib/sources/ 에 .mjs 파일을 두기만 하면 새로운 소스가 등록됩니다. v1.69 이전에는 server/lib/sources/registry.mjs 의 소스 목록이 손으로 유지되는 정적 배열이어서, 어댑터를 추가하려면 <id>.mjs 와 registry.mjs 를 모두 수정해야 했습니다. 로드맵 항목 P-14 (docs/ROADMAP.md) 의 남은 절반을 마무리합니다. 이제 server/lib/sources/ 의 모든 *.mjs 가 모듈 부팅 시 동적으로 로드되며, 각 어댑터는 자기 기술적인 블록 export const meta = { value, label, region, configKey? } 로 자신의 정체성을 선언합니다. 동봉된 12개의 어댑터(ashby / greenhouse / lever / rss / smartrecruiters / workable / workday + geekjob / getmatch / habr / hh / trudvsem) 에 meta 가 추가되었고, registry.mjs 는 readdirSync + 동적 import() 를 top-level await 로 해석합니다 (Node 18+ ESM 표준). 공개 API (SOURCES, SOURCES_BY_REGION, RU_CONFIG_KEYS, getRegionalSources) 는 변경되지 않습니다 — 기존 임포트는 그대로 동작합니다. 잘못된 meta 는 거부되고 파일마다 console.warn 가 한 번씩 기록됩니다. tests/sources-registry-discovery.test.mjs 에 14개의 케이스를 추가했습니다. 스위트 1065 → 1079.


[1.68.2] — 2026-06-07

fix(bin): npx / npm link로 실행하는 CLI 동사가 깨져 있었음 — 이제 bin 경로를 심볼릭 링크를 따라 해석함. npm과 npx는 career-ops-ui를 node_modules/.bin/ 아래의 심볼릭 링크로 노출하는데, 기존 dirname "${BASH_SOURCE[0]}"가 패키지 루트가 아니라 .bin을 가리켜서 npx career-ops-ui init이 node node_modules/scripts/init.mjs를 실행하다 MODULE_NOT_FOUND로 죽었습니다(로컬 npm install 실행은 영향을 받지 않아 버그가 숨겨졌습니다). 이제 bin/career-ops-ui.sh와 bin/start.sh는 심볼릭 링크 체인을 따라 SCRIPT_DIR를 정규화하므로(readlink 루프 + cd -P) 모든 동사가 저장소에서, npm link로, 그리고 npx로 동작합니다. .bin 스타일 심볼릭 링크로 동사를 실행하는 회귀 방지 테스트를 tests/sh-files.test.mjs에 추가했습니다. 스위트 1065/1065.


[1.68.1] — 2026-05-29

fix(scan): 소스별 fetch 타임아웃 10s → 60s로 상향. v1.67.1의 10s fail-fast는 시간이 조금 더 필요할 뿐인 “느리지만 살아있는” Ashby 보드까지 끊어냈습니다. 기본값을 1분으로 올려 그런 보드가 응답하도록 합니다. 트레이드오프: 정말로 죽거나 멈춘 소스는 이제 60s 내내 동시성 슬롯을 점유하며(최악의 경우 스캔이 느려짐), 만성적으로 멈추는 곳(Perplexity, Supabase, Resend 등)은 여전히 타임아웃될 가능성이 큽니다 — 제대로 고치려면 소스별 / Ashby 동시성 축소가 필요합니다. SCAN_FETCH_TIMEOUT_MS로 재정의 가능. 스위트 1063/1063.


[1.68.0] — 2026-05-29

feat(scan): 결과 필터 패널 개편 — 라벨 필드, 적용 버튼, 사무실 근무 옵션, 그리고 실제로 동작하는 급여 필터. #/scan의 모든 필터가 라벨 필드가 되었습니다(플레이스홀더가 아니라 컨트롤 위에 라벨): 검색 · 근무 형태 · 급여 최소 · 급여 최대 · 소스 · 범위. 명시적 적용 버튼(및 초기화, 각 필드에서 Enter)이 필터를 재실행하고, 페이지의 힌트가 사용법을 설명합니다. 이제 급여 범위가 실제로 필터링합니다 — 최소/최대를 설정하면 급여가 범위를 벗어난 공고 및 급여가 없는 공고가 제거됩니다(범위 겹침, 통화 무시). 근무 형태 필터에 원격 / 하이브리드 / 재배치와 함께 사무실 근무 옵션 추가. 신규 i18n 키 ×9, salaryInRange 엄격화, 스위트 1063/1063.


[1.67.1] — 2026-05-29

fix(scan): 소스별 fetch 타임아웃 30s → 10s로 단축(fail-fast). v1.67.0의 30s 상향은 느린 Ashby 보드의 절반 정도만 회복했고, 나머지(Perplexity, Supabase, Resend, DeepL, Ramp 등)는 마감과 무관하게 멈추므로 긴 타임아웃은 죽은 슬롯을 기다리며 전체 스캔을 지연시킬 뿐이었습니다. 10s는 만성 행에 즉시 실패하고 스캔 반응성을 유지합니다. SCAN_FETCH_TIMEOUT_MS로 재정의 가능. 스위트 1060/1060.


[1.67.0] — 2026-05-29

feat(scan): #/scan에 급여 범위(최소/최대) 필터 추가 및 소스별 fetch 타임아웃 연장. 결과 테이블에 텍스트·원격 필터 옆으로 숫자 입력 두 개(급여 최소 / 최대)가 추가됩니다. 각 행의 자유 텍스트 급여(от 100 000 до 200 000 ₽, 120000-150000 USD, $120K–$150K 등)를 숫자 범위로 파싱해 범위 겹침 방식으로 매칭합니다. 급여가 없는 행은 유지하므로 필터가 목록을 비우지 않고 좁힙니다(통화는 구분하지 않음 — 환율 변환 없음). 또한 소스별 스캔 fetch 타임아웃을 15s → 30s로 상향(SCAN_FETCH_TIMEOUT_MS로 재정의 가능). Ashby의 includeCompensation 페이로드는 ×8 동시성에서 흔히 15s를 초과해 매 스캔마다 ~30개 Ashby 보드가 타임아웃되었습니다. 신규 window.Skills.parseSalaryRange/salaryInRange + i18n ×9, 테스트 13개 추가, 스위트 1060/1060.


[1.66.0] — 2026-05-28

feat(scan): RU 소스가 첫 페이지뿐 아니라 모든 결과 페이지를 순회합니다. hh.ru, Habr Career, Trudvsem은 쿼리당 처음 ~50건만 가져왔지만, 이제 끝까지 페이지를 따라갑니다 — hh.ru/Habr는 &page=N, Trudvsem은 offset/meta.total — 페이지 간 중복 제거 및 새 항목이 없으면(또는 50페이지 안전 상한에서) 중단. “Backend разработчик” 같은 쿼리가 한 페이지가 아니라 전체 결과를 반환합니다(예: hh.ru PHP 17 → 3페이지에 55건 이상; Trudvsem 72건 전부). 페이지별 요청은 기존 타임아웃 + AbortSignal 유지. 테스트 4개 추가; 스위트 1045/1045.


[1.65.0] — 2026-05-28

feat(scan): hh.ru를 JSON API 대신 공개 웹사이트에서 스크랩 — 어떤 IP에서도 프록시 없이 작동. api.hh.ru가 IP나 User-Agent와 무관하게 모든 프로그램 클라이언트에 403 forbidden을 반환하기 시작했습니다(엣지 안티봇 차단). 반면 웹사이트(hh.ru/search/vacancy)는 브라우저류 클라이언트에 전체 결과를 제공하므로, 어댑터가 이제 그 HTML을 파싱합니다(Habr Career처럼). 1.64.0의 HH_PROXY 변수와 undici 의존성을 제거 — 프록시·키·User-Agent 설정이 필요 없습니다. HTML 파서용으로 테스트 재작성; 스위트 1041/1041.


[1.64.0] — 2026-05-27

feat(scan): HH_PROXY로 hh.ru 요청을 러시아 프록시로 라우팅. hh.ru는 API를 User-Agent가 아니라 IP로 차단합니다 — 따라서 HH_USER_AGENT만으로는 러시아 외 출구 노드의 403을 해제할 수 없었습니다. HH_PROXY에 러시아 HTTP/HTTPS 프록시 URL(예: http://user:pass@ru-host:port)을 지정하면 hh.ru 요청만 그 프록시를 통과하고 나머지 소스는 직접 연결을 유지합니다. undici의 ProxyAgent 기반(런타임 의존성 추가); HH_PROXY가 없으면 dispatcher는 전혀 추가되지 않습니다. 테스트 3개 추가; 스위트 1041/1041.


[1.63.2] — 2026-05-27

feat(scan): #/scan 콘솔에 실시간 % 진행률 + 소스별 상세. 진행 표시줄이 확정형이 되었습니다 — 스캐너가 진행 이벤트(EN: 회사별, RU: 쿼리별)를 SSE로 보내고, “Scanning… NN%” 라벨과 함께 막대가 채워집니다(애니메이션 줄무늬는 첫 이벤트까지만). 각 소스의 첫 실패(타임아웃 / 403 / 네트워크)는 콘솔에 상세히 기록되고 이후 반복은 억제됩니다. 신규 테스트 1개, 스위트 1040/1040.


[1.63.1] — 2026-05-27

style(scan): #/scan 진행 표시줄을 더 눈에 띄게. 진행 표시기에 보이는 “Scanning…” 라벨을 추가하고 막대를 8px(기존 4px)로 키워 스캔 중 분명히 보이게 했습니다. 동작 변경 없음.


[1.63.0] — 2026-05-27

feat(scan): 요청별 타임아웃 + #/scan 진행 표시줄. 소스 요청에 마감 시간이 없어, 멈춘 업스트림(예: 차단된 IP에서의 api.hh.ru)이 스캔 전체를 멈출 수 있었습니다. 새 server/lib/fetch-timeout.mjs 가 스캐너의 fetchImpl 을 감싸(makeTimeoutFetch, 기본 15초, SCAN_FETCH_TIMEOUT_MS 로 변경) 각 요청에 엄격한 마감을 부여합니다. 타임아웃된 소스는 비치명적 오류로 기록되고 스캔은 계속됩니다. #/scan 은 스캔 중 진행 표시줄을 보여줍니다(9개 로케일 scan.progress). 신규 테스트 7개, 스위트 1039/1039.


[1.62.3] — 2026-05-27

docs: 설치 명확화(career-ops-ui 는 career-ops/web-ui/ 안에서 실행)+ init 문제 해결, 9개 로케일 전체. 설치 섹션을 Option 1(원커맨드 curl)/ Option 2(기존 career-ops 프로젝트 안에 web-ui 로 UI 클론)+ CLI 명령어 + 공급자 설정 + Troubleshooting init 블록으로 재작성. 중첩 구조 안내를 /help §1 Setup 에도 추가하고, README 하이라이트에서 v1.62.* 라인 전체를 요약. 문서만 변경, 코드 변경 없음.


[1.62.2] — 2026-05-27

fix(help): #/help 필터가 이제 전문 검색입니다(RSS 같은 H3 하위 섹션도 검색됨). 도움말 페이지의 검색/TOC 필터는 이전에 H2 섹션 제목만 일치시켰기 때문에 v1.62.x의 RSS 문서(§5 Portals & sources 아래 H3)를 찾을 수 없었습니다. 이제 각 섹션의 본문이 필터에 인덱싱되어 예를 들어 “RSS”를 검색하면 §5가 표시됩니다. 클라이언트 측만 변경, API 변경 없음.


[1.62.1] — 2026-05-27

feat(scan): 소스 필터에 RSS 추가 + RSS 위치 수정. #/scan 소스 필터 드롭다운에 이제 RSS 가 표시됩니다(server/lib/sources/registry.mjs 와 SPA 폴백 목록에 추가). 따라서 RSS 보드(LaraJobs, WeWorkRemotely 등) 결과도 다른 ATS 소스처럼 필터링됩니다. RSS 어댑터는 더 이상 피드의 <category> 태그를 location 에 매핑하지 않습니다 — 위치가 아닌 태그가 location_filter 로 하여금 원격 직무를 잘못 제외하게 만들었기 때문입니다. 이제 location 은 비어 있어 피드가 위치 필터를 통과합니다. 스캔 버튼 툴팁/레이블과 소스 목록 i18n 문자열을 9개 로케일 전체에서 업데이트(Workable / SmartRecruiters / Workday / RSS). i18n 스냅샷과 소스 엔드포인트 테스트(EN 6 → 7) 업데이트.


[1.62.0] — 2026-05-27

feat(scan): 비-ATS 채용 보드를 위한 범용 RSS 어댑터. 새 rss 어댑터(server/lib/portals/adapters/rss.mjs + server/lib/sources/rss.mjs)를 통해 스캐너가 모든 RSS 피드(LaraJobs, WeWorkRemotely, RemoteOK, golangprojects 등 Greenhouse/Ashby/Lever 외 보드)에서 채용 공고를 가져올 수 있습니다. 새 의존성 없음: 피드 파싱은 정규식 기반이며 CDATA와 HTML 엔티티를 지원합니다(제목/회사명 태그 제거, astral 코드포인트 안전 디코딩). portals.yml의 provider: rss / rss: / feed_url:로 회사별 활성화되어 ATS에 이미 매칭된 회사를 가로채지 않습니다. ALL_ADAPTERS가 6 → 7로 증가. 신규 테스트 29개; 9개 README 로케일에 문서화.


[1.61.1] — 2026-05-22

fix(i18n): 테마 토글의 title + aria-label을 9개 로케일 전체에서 현지화 (MINOR-001). 다크/라이트 테마 버튼(#theme-toggle)이 index.html에 title="Toggle theme"과 aria-label="Toggle theme"을 하드코딩하여 — 모든 로케일에서 툴팁과 스크린리더 텍스트가 번역되지 않았습니다. 새 top.themeToggle 키 + applyI18n()의 data-i18n-title 핸들러(v1.58.15 검색 aria-label 수정과 동일 패턴)가 부팅 시와 언어 전환 시마다 두 속성을 현지화합니다. tests/playwright-theme-toggle-i18n.mjs(9개 로케일 + 런타임 전환)와 정적 가드 2개로 잠금. v1.61.0 프랑스어 사인오프의 유일한 LOW 항목. (MINOR-001)


[1.61.0] — 2026-05-22

feat(i18n): 프랑스어를 9번째 UI 언어로 추가. 새 로케일별 사전 public/js/lib/locales/i18n-dict.fr.js(window.__I18N_DICT_FR)는 영어와 668개 키 완전 동등성을 가지며, 새 도움말 번들 docs/help/fr.md(19 H2 / 73 H3, en과 정확한 구조 동등성). fr는 언어 전환기와 브라우저 자동 감지(i18n.js), 어셈블러(i18n-dict.js), index.html(어셈블러 앞의 <script> 태그), 테스트 스냅샷, 모든 테스트 로케일 목록에 등록됨. 초기 번역 표는 PR #9(커뮤니티 기여)에서 가져옴. 로직 변경 없음: t()와 모든 뷰는 그대로. 단위 테스트 1001 / 1001; Playwright 로케일 스윕은 9개 서브테스트로 확장. (FR-LOCALE)


[1.60.0] — 2026-05-22

refactor(i18n): 8개 언어 통합 파일을 로케일별 파일로 분리 (I18N-SPLIT). 번역 사전이 하나의 public/js/lib/i18n-dict.js에 있었으나, 이제 public/js/lib/locales/ 아래 언어당 한 파일과 공용 i18n-dict.aliases.js로 나뉘어 번역가가 한 언어만 독립적으로 편집할 수 있습니다(i18next / OpenWA 방식). i18n-dict.js는 동일한 window.__I18N_DICT를 다시 조립하는 조립기가 되어 t()와 모든 화면은 그대로입니다. <script src>로 동기 로드 — 빌드·fetch 없음. 스냅샷으로 무손실 마이그레이션 검증(678개 키). 도구와 ~25개 테스트를 분리 인식하도록 수정; 신규 tests/i18n-locale-files.test.mjs, tests/playwright-locale-sweep.mjs(모든 페이지 × 8개 로케일, 실제 Chromium). 994 → 1000 단위 · 62 → 70 Playwright. 동작 변화 없음. (I18N-SPLIT)


[1.59.13] — 2026-05-21

fix(i18n): @alias 로 진짜 중복 키 통합 + 개인정보 최종 정리. 메인테이너 실명을 테스트 픽스처/QA 리포트에서 제거(→ Jane Doe), LICENSE/package.json 을 Fighter90 핸들로 변경. @alias 로 8개 로케일 동일한 10개 키 통합. nav.config/config.title 은 스페인어에서 달라 통합 안 함. 991 → 994 테스트. (I18N-CL3)


[1.59.12] — 2026-05-21

fix(i18n): i18n-dict.js 정리 — fr 로케일 이전 (I18N-CL1, I18N-CL2, I18N-CL4). training.coursePh 의 개인 데이터를 일반 플레이스홀더로 교체, followup.lastPh 를 고정 날짜에서 형식 힌트로 변경, npm run audit:i18n 추가. 중복 값 그룹은 의도적(서로 다른 UI 역할) — 사전 헤더 참고. (I18N-CL1, I18N-CL2, I18N-CL4)


[1.59.11] — 2026-05-21

fix(test): v1.59.11 — e2e-comprehensive 스위트가 23/23 통과 (이전 11/23). Playwright 의 page.goto 가 해시만 바뀌는 경우 no-op 이라서 발생한 문제. 새 goRoute(hash) 헬퍼가 about:blank 를 거쳐 실제 내비게이션을 강제합니다. (e2e-harness-r1)


[1.59.10] — 2026-05-21

fix(api): NEW-F1-sub-r1 (v1.59.10) — 원시 .. 가드를 모든 /api 라우트 등록 위로 끌어올렸습니다. v1.59.8 버전은 app.all 뒤에 있어 발화되지 않았습니다. Express 정규화 이전에 실행됩니다. (NEW-F1-sub-r1)


[1.59.9] — 2026-05-21

fix(ux): UX-A5-r4 (v1.59.9) — Help TOC 스크롤 스파이에 data-toc-spy="active" 디버그 마커 + 동작 기반 락 테스트. 6번째 사이클. 동기 초기 페인트 + 이중 rAF 재계산 + resize 리스너 + hashchange 정리. (UX-A5-r4)


[1.59.8] — 2026-05-21

fix(ux+api): v1.59.8 — UX-A5-r3 + NEW-F1-sub (HIGH + LOW 묶음). FINAL-REGRESSION-v1.59.7 권고에 따른 doctrine 예외. UX-A5-r3: #/help 가 IntersectionObserver 를 scroll 리스너 + rAF 쓰로틀링으로 교체. NEW-F1-sub: /api/* 의 가공되지 않은 .. 를 404 JSON 으로 거부. (UX-A5-r3 · NEW-F1-sub)


[1.59.7] — 2026-05-20

fix(api): NEW-D3-cache (v1.59.7) — GET /api/cv 가 Cache-Control: no-store 전송. CV 는 사용자 핵심 아티팩트이므로 항상 재검증. (NEW-D3-cache)


[1.59.6] — 2026-05-20

feat(a11y): NEW-D2-motion (v1.59.6) — prefers-reduced-motion: reduce 존중. 새 @media 블록이 애니메이션·트랜지션·scroll-behavior 를 비활성화. (NEW-D2-motion)


[1.59.5] — 2026-05-20

fix(api): NEW-F1 (v1.59.5) — 알 수 없는 /api/* 가 모든 HTTP 동사에서 JSON 404 반환. app.get → app.all. (NEW-F1)


[1.59.4] — 2026-05-20

fix(ui): NEW-OR1 (v1.59.4) — #/config Active/Keys 칩 경쟁 조건 제거. atomic replaceChildren + 인-플라이트 토큰 + last-good 캐시. (NEW-OR1)


[1.59.3] — 2026-05-20

fix(ux): UX-A5-r2 (v1.59.3) — #/help 스크롤 스파이 강화. rootMargin 가시 영역을 10 %에서 25 %로 확대 + 마운트 시 초기 상태 계산 추가. (UX-A5-r2)


[1.59.2] — 2026-05-20

fix(ui): v1.59.2 — Active/Keys 칩: 카운트 정확화, 공급자 이름 대문자화, 겹침 제거. (post-v1.59.1 hotfix)


[1.59.1] — 2026-05-20

fix(test): v1.59.1 — NEW-D1 가드가 UX-A11 다듬어진 ES 복사를 허용합니다. (v1.59.1)


[1.59.0] — 2026-05-20

feat(ui): UX-A14 (v1.59.0) — 모바일 (≤ 420 px) 감사 패스. 새 @media (max-width: 420px) 블록에 5 가지 수정. (UX-A14)


[1.58.65] — 2026-05-20

test(ui): UX-A2 (v1.58.65) — Modes 구조화된 필드 폼 리그레션 락 테스트. v1.54.3 구현을 회귀로부터 보호합니다. (UX-A2)


[1.58.64] — 2026-05-20

fix(i18n): UX-A11 (v1.58.64) — es/pt-BR 사본 다듬기. 영어 차용어를 모국어 동등 표현으로 교체. (UX-A11)


[1.58.63] — 2026-05-20

fix(ui): UX-A15 (v1.58.63) — Dashboard Pipeline 타일에 시각적 주요 강조. Pipeline 타일은 이제 강조 테두리, 더 큰 아이콘, 굵은 라벨로 눈에 띄게 표시됩니다. (UX-A15)


[1.58.62] — 2026-05-20

feat(ui): UX-A9 (v1.58.62) — API keys 탭 상단의 sticky 요약 칩. #/config → API keys 탭 상단에 활성 공급자와 설정된 키 수를 표시하는 sticky 칩이 추가되었습니다. (UX-A9)


[1.58.61] — 2026-05-20

docs(readme): UX-A8 (v1.58.61) — 8 개 README 모두에 첫 실행 정리 섹션 추가. 첫 스캔 전 두 개의 QA 픽스처 URL 을 정리하는 make clean-test-fixtures 단계를 문서화했습니다. (UX-A8)


[1.58.60] — 2026-05-20

feat(ui): UX-A12 (v1.58.60) — 알림 드로어에 모두 지우기 + 항목별 닫기. 알림 패널에 전체 지우기 버튼과 항목별 × 버튼이 추가되었습니다. (UX-A12)


[1.58.59] — 2026-05-20

feat(ui): UX-A13 (v1.58.59) — #/health 실패 행에 실행 가능한 “Fix →” CTA. FAIL/OPTIONAL 행은 이제 해당 설정 탭으로 바로 이동하는 고스트 버튼을 표시합니다. (UX-A13)


[1.58.58] — 2026-05-20

fix(ux): UX-A10 (v1.58.58) — #/cv 의 저장되지 않은 편집 손실 방지. 브라우저 종료(beforeunload)와 SPA 내부 이동(hashchange) 시 더티 버퍼가 있으면 현지화된 확인을 표시합니다. (UX-A10)


[1.58.57] — 2026-05-20

test(ui): UX-A7 (v1.58.57) — cost-line 자동 갱신 계약에 대한 리그레션 락 테스트. providers-changed 이벤트의 디스패치/구독/모든 어드바이저 뷰 호출을 정적으로 보장합니다. (UX-A7)


[1.58.56] — 2026-05-20

fix(a11y): UX-A4 (v1.58.56) — .lang-btn 이 WCAG 2.5.8 최소 터치 타깃 크기를 충족. 이전에 언어 버튼은 높이 23–25 px 로 24×24 px 기준 미달이었으나, 이제 min-height: 28px + min-width: 28px 로 WCAG 2.2 AA 를 충족합니다. (UX-A4)


[1.58.55] — 2026-05-20

feat(ui): UX-A3 (v1.58.55) — Dashboard 활성 공급자 칩. #/dashboard 히어로에 현재 활성 LLM 공급자가 표시됩니다(⚡ Live evals: Anthropic claude-sonnet-4-6 또는 📋 Manual prompt mode). #/config 에서 LLM_PROVIDER 변경 또는 탭 포커스 복원 시 자동 갱신. (UX-A3)


[1.58.54] — 2026-05-20

fix(ux): UX-A1 (v1.58.54) — Deep 브리프 구조 방어 경고. 저장된 브리프에 표준 6개 섹션(Company snapshot / Engineering culture / Recent news / Glassdoor / Interview process / Negotiation leverage) 중 3개 미만이 포함되면 public/js/views/deep.js가 비차단 경고와 레퍼런스 링크를 앞에 표시합니다. UI 가드레일이며, 프롬프트 단계 수정은 상위 프로젝트에서 진행됩니다. (UX-A1)


[1.58.53] — 2026-05-20

fix(ux): UX-A6 — 모든 saved-card 가 단일 renderSavedCard() 헬퍼를 통과. 모든 렌더 경로에서 <span>+<time> 구조 보장. 948 → 949 유닛. (UX-A6)



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