체인지로그

GitHub

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

[1.219.0] — 2026-08-26

추가 — Torre가 스캐너에 합류했고, 이제 모든 제공자 라벨이 올바릅니다.

추가

  • Torre(provider: torre) — torre.ai의 범-라틴아메리카 인재 마켓플레이스, Greenhouse/Lever/Ashby에 없는 원격 채용을 담은 무인증 공개 검색; 항목당 한 번의 제한된 요청. 레지스트리는 이제 83개 소스(영어 78 + 러시아어 5)를 제공합니다. 또한 온보딩 배너에 활성 제공자 모노그램.

수정

  • a16z Speedrun 스캔이 순환하는 보드에서 조기에 멈췄습니다(스윕 도중 짧은 페이지를 마지막으로 간주해 ~300개 중 149개로 잘림). 이제 피드의 total_pages를 신뢰합니다. 또 온보딩 배너가 4개 항목 맵에서 제공자를 해석해 나머지 14개는 원시 슬러그를 보였는데, 이제 18개 라벨+모노그램을 사용합니다. 설정의 제공자 안내가 7→18로 바뀝니다.

참고

  • Get on Board의 항목당 다중 카테고리 옵션은 아직 여기 없습니다(소스 수준 리팩터 필요). 테스트: 2768.

[1.218.0] — 2026-08-26

추가 — 제공자가 표시되는 모든 곳에 제공자 배지(와 올바른 이름).

추가

  • 대시보드 칩, 설정 “활성” 요약, ⚡ 평가 결과 헤더의 제공자 모노그램.

수정

  • 오래된 라벨: 세 화면이 5개 항목 맵에서 이름을 해석했고(평가 결과는 Anthropic이 아닌 모든 제공자를 “Gemini”로 잘못 표시), 이제 공유 ProviderStatus.label(18개)을 사용합니다. 키 개수가 “/ 7”로 표시되던 것을 18로 수정. LLM_PROVIDER 힌트 슬러그 목록에 17개 언어 모두 ark / arkcn 추가.

참고

  • 클라이언트 전용, CSP 안전. 서버 변경 없음. 스캔 소스: 82. 테스트: 2758.

[1.217.1] — 2026-08-26

테스트 강화 — 전체 제공자 목록의 엔드포인트 커버리지.

추가

  • Kimi, MiniMax, Mistral, Fireworks의 엔드포인트·기본 모델·Bearer 인증을 검증하는 매개변수화된 run<Provider> 테스트.

[1.217.0] — 2026-08-26

추가 — Ark가 LLM 목록에 합류(18개 제공자). ByteDance의 Doubao 모델인 BytePlus Ark와 Volcengine Ark가 이제 ⚡ 라이브 제공자입니다 — 동일한 OpenAI 호환 코어와 지역별 기본 URL을 사용합니다.

추가

  • BytePlus Ark(ARK_API_KEY, 국제)와 Volcengine Ark(ARK_CN_API_KEY, 중국) — runOpenAICompatible()을 통한 OpenAI 호환 Chat Completions. ARK_MODEL / ARK_CN_MODEL(Doubao 모델명 또는 ep-… 엔드포인트 id)을 설정하고, ARK_BASE_URL / ARK_CN_BASE_URL로 지역을 전환합니다. 둘 다 auto 순서(마지막)에 합류하며 설정과 사용량에 모노그램이 표시됩니다. 웹 UI는 이제 18개 라이브 제공자를 지원합니다.

참고

  • 나머지와 동일한 신뢰된 설정 + http(s) 스킴 검사 경로(채용 URL용 SSRF 검증기는 사용 안 함). 스캔 소스 변동 없음: 82. 테스트: 2755.

[1.216.0] — 2026-08-26

추가 — LLM 제공자 9개가 더해졌고, 각각 브랜드 타일이 붙습니다. 이제 ⚡ 라이브 평가를 DeepSeek, GLM (Z.ai), Kimi (Moonshot), MiniMax, Mistral, Grok (xAI), Together, Fireworks 또는 완전 로컬 Ollama로 실행할 수 있습니다 — 키 하나면 되고, 설정의 각 필드 옆에 모노그램이 표시됩니다.

추가

  • OpenAI 호환 제공자 9개. DeepSeek, GLM (Z.ai), Kimi (Moonshot), MiniMax, Mistral, Grok (xAI), Together AI, Fireworks AI, Ollama(완전 로컬, 키 불필요). 설정에서 키(또는 Ollama의 경우 OLLAMA_BASE_URL)를 지정하면 Hermes 다음의 auto 순서에 합류합니다. LLM_PROVIDER로 특정 제공자를 고정할 수 있습니다. Together는 Thinking Machines의 Inkling(thinkingmachines/Inkling)도 호스팅합니다. 웹 UI는 이제 16개 라이브 제공자를 지원합니다.
  • 제공자 모노그램 타일. CSP 안전한 브랜드 색상 이니셜 타일이 설정의 키 필드 옆과 사용량 페이지 행에 각 제공자를 표시합니다 — 인라인 SVG, 외부 로고 없음, 어떤 것도 기기를 떠나지 않습니다.

참고

  • 제공자 기본 URL은 runOpenAICompatible()을 통해 http(s) 스킴 검사(Ollama 루프백 허용)로 처리되는 신뢰된 설정이며, 채용 URL용 SSRF 검증기를 거치지 않습니다. 스캔 소스 변동 없음: 82. 테스트: 2752.

[1.215.0] — 2026-08-26

추가 — OpenWorker에서 구직 전체를 실행하세요. 새 coworker가 Andrew Ng의 오픈소스 AI coworker 앱에서 이 파이프라인을 구동하며, 이제 인앱 도움말이 이를 다뤄 “문서에 묻기” 어시스턴트가 안내할 수 있습니다.

추가

  • OpenWorker coworker. 코드 없는 OpenWorker coworker — Fighter90/career-ops-coworker — 가 구직 전체(스캔·평가·맞춤·추적·팔로업)를 실행하고 이 대시보드를 실행할 수 있으며, 모두 데스크톱에서 이뤄집니다. 17개 언어로 제공되는 한 파일짜리 페르소나로, OpenWorker 자체 로더로 설치 가능함이 검증됐습니다.
  • 도움말 §32 “Run it from OpenWorker”(17개 로케일)가 이를 문서화하여, 인앱 문서에 묻기 어시스턴트와 떠 있는 도움말 버튼이 이에 답할 수 있습니다. 랜딩 푸터, README, 위키, docs/integrations/openworker.md에도 링크됩니다.

참고

  • 문서 + 사이트만 — 앱 코드 변경 없음. 스캔 소스는 82개로 변동 없음. 테스트 스위트: 2742.

[1.214.2] — 2026-08-25

수정 — 공유 HTML→텍스트 파이프라인(Greenhouse / Recruitee 설명)이 속성 값에 > 문자가 있을 때 태그 속성을 설명에 흘리지 않습니다.

수정

  • 속성 안에 >가 있는 HTML 설명이 깔끔하게 제거됩니다. Greenhouse·Recruitee 설명 뒤의 태그 제거기는 첫 >에서 멈추는 단순 매칭을 써서, <a title="salary > 100k"> 같은 태그가 뒤쪽 속성 텍스트(100k">)를 설명에 남겼습니다. 이제 따옴표로 감싼 속성 값(작은/큰따옴표)을 존중하고, 엔티티를 디코딩하기 전에 마크업을 제거하며(속성 안의 인코딩된 따옴표가 거짓 구분자가 되지 않도록), 평문의 리터럴 <>는 그대로 보존합니다.

참고

  • 테스트 스위트: 2742. 스캔 소스는 82개로 변동 없음. 이번 라운드에 부모에서 새로 온 것은 없습니다: 나머지 변경은 읽기 전용으로 중계되거나(follow-up / weekly-digest / rejection-latency / salary-gap 수정은 web-ui 변경 불필요) web-ui 표면이 아닙니다(외부 X/Xquik BYO-key 플러그인, CLI/verify 수정). 선택형 항목 — SmartRecruiters 상세 설명, detect-reposts 애그리게이터 건너뛰기, stem: / word: 제목 접두사, DNS 리바인딩 방어 — 은 계속 대기 중입니다.

[1.214.1] — 2026-08-25

수정 — Ashby 공고 설명이 이제 Greenhouse·Recruitee와 같은 길이로 잘려, 콘텐츠 필터가 세 보드에서 동일하게 동작하고 한 보드가 스캔 캐시를 부풀리지 못합니다.

수정

  • Ashby 설명이 이제 다른 보드와 같은 길이 상한을 지킵니다. v1.214.0은 Ashby의 공고 본문을 콘텐츠 필터에 넣었지만, 4000자로 자르는 Greenhouse·Recruitee와 달리 Ashby는 상한 없이 그대로 매핑됐습니다(일부는 4배 더 길었음). 그래서 같은 content_filter 키워드가 텍스트의 위치에 따라 Ashby에서는 매칭되고 Greenhouse에서는 안 될 수 있었고, 한 보드가 #/scan이 불러오는 스캔 캐시에 수백 KB를 더할 수 있었습니다. Ashby도 이제 같은 4000자로 잘립니다(이미 평문이라 잘리기만 하고 태그가 제거되지는 않습니다).

참고

  • 테스트 스위트: 2738. 스캔 소스는 82개로 변동 없음.

[1.214.0] — 2026-08-25

수정 — Greenhouse·Ashby·Recruitee의 공고 설명이 이제 콘텐츠 필터로 들어가 키워드 / 국가 / 비자 필터가 이 보드들에 실제로 매칭되고, “Associate <시니어 직함>”(학술 직급 포함)이 더는 엔트리급으로 잘못 분류되지 않습니다.

수정

  • 콘텐츠 필터가 이제 Greenhouse·Ashby·Recruitee에서 작동합니다. 이 보드들은 이제 각 공고의 전체 본문을 설명으로 담으므로, 설명을 읽는 content_filter(또는 국가 / 비자 키워드 필터)가 보드를 그냥 통과시키지 않고 공고 본문에 매칭합니다. Ashby와 Recruitee는 목록 페이로드에 본문을 무료로 실어 주고, Greenhouse는 이미 그랬습니다. 셋 다 하나의 공유 HTML→텍스트 파이프라인으로 정리됩니다.
  • “Associate Director” / “Associate Professor”는 엔트리급이 아니라 시니어입니다. 시니어리티 분류기(skip_tiers:가 사용)는 “associate”가 시니어 직함을 수식할 때도 이를 엔트리급 표시로 취급했습니다. 이제 “Associate <시니어 명사>”—학술 직급(Associate Professor, Associate Dean) 포함—를 시니어로 읽고, 진짜 주니어 변형(Associate Attorney, Associate Editor)은 엔트리급으로 유지하며, 앞에 오는 “Intern, Associate Dean”은 여전히 인턴십으로 분류합니다.

참고

  • 테스트 스위트: 2736. 스캔 소스는 82개로 변동 없음. 후속으로 대기: 선택형 SmartRecruiters 설명 보강(공고별 상세 조회), detect-reposts의 aggregator: true 보드 건너뛰기, 앵커드 키워드 stem: / word: 제목 접두사 — 각각 기본 동작 변경이 아니라 선택형 설정입니다. DNS 리바인딩 강화는 별도 보안 릴리스로 여전히 대기 중입니다.

[1.213.0] — 2026-08-22

추가 — 싱가포르 국가 채용 은행 MyCareersFuture를 스캔 소스로 추가. 수정 — Greenhouse 공고가 이제 전체 본문을 담아 콘텐츠 필터가 작동하고, 원격 Ashby 공고가 도시만 표시된 위치 뒤에 더는 가려지지 않습니다.

추가

  • MyCareersFuture (싱가포르)(mycareersfuture.gov.sg) — Workforce Singapore가 운영하는 싱가포르 국가 공공 채용 은행을 위한 새 무토큰 스캔 소스입니다. #/scan의 소스 필터에서 고르거나, 선택적 keywords 목록과 함께 provider: mycareersfuture 회사를 추가하세요(Job Bank처럼 생략 시 프로필의 목표 직무로 대체). 공개 검색 API를 호스트 고정으로, 키 없이 읽습니다.

수정

  • Greenhouse 공고를 이제 콘텐츠로 필터링할 수 있습니다. Greenhouse 보드를 공고 전체 본문과 함께 가져와 공고 설명(description)으로 평문 디코딩합니다 — 그래서 설명을 읽는 content_filter(또는 국가/비자 키워드 필터)가 이제 Greenhouse 공고를 그냥 통과시키지 않고 실제로 매칭합니다.
  • 원격 Ashby 공고가 더는 도시 필터에 걸려 탈락하지 않습니다. Ashby는 근무 형태(Remote/Hybrid/Onsite)를 사무실 도시와 별개로 두므로, 완전 원격 공고도 예를 들어 “San Francisco”로 읽혔고 그 도시를 막는 위치 필터가 지원 가능한 공고를 숨겼습니다. 이제 원격일 때 위치에 “Remote”가 추가되며, 오래된 isRemote 플래그보다 workplaceType가 우선해 사무실 기반 하이브리드 공고를 잘못 라벨링하지 않습니다.

참고

  • 스캔 소스: 82개(영어 77 + 러시아어 5). 테스트 스위트: 2724. DNS 리바인딩 방어(연결 전에 호스트의 확인된 주소 검증)는 직접 이식이 아니라 web-ui 전용 설계가 필요해 전용 릴리스로 대기 중입니다.

[1.212.1] — 2026-08-21

수정 — cvstart.org 랜딩이 스캐너의 채용 소스를 적게 셌습니다(80으로 표시하며 Job Bank (캐나다)를 누락). 이제 앱의 81과 다시 일치하며, 둘이 어긋나면 사이트 빌드가 요란하게 실패합니다.

수정

  • 랜딩의 “채용 소스” 수가 앱과 다시 동기화됩니다. v1.212.0 이후 cvstart.org는 80개 보드를 보여주고 새 Job Bank (캐나다) 칩이 빠져 있었지만, 앱·스캔 드롭다운·도움말은 모두 81을 나열했습니다. 랜딩은 라이브 스캐너 레지스트리를 불러와 목록을 만드는데, 한 소스가 YAML 의존성을 가져오는 방식 때문에 그 빌드에서 로드에 실패해 조용히 누락됐습니다. 이제 Job Bank는 앱의 나머지가 스캔 시점에 하는 것과 같은 방식으로 그 의존성을 지연 로드하므로 항상 나타납니다.
  • 사이트 빌드는 이제 어긋난 소스 수를 배포하지 않습니다. 레지스트리가 디스크에 존재하는 것보다 적은 소스를 열거하면(로드 실패 소스의 특징) 빌드가 잘못된 숫자를 조용히 배포하는 대신 명확한 메시지와 함께 실패합니다.

참고

  • 앱 동작은 그대로입니다 — 스캐너에는 항상 81개 소스가 모두 있었고, 랜딩 페이지만 영향을 받았습니다. 스캔 소스: 81개(영어 76 + 러시아어 5) — 변동 없음. 테스트 스위트: 2687.

[1.212.0] — 2026-08-21

추가 — Job Bank (캐나다), 연방 국가 채용 보드. 제거 — EchoJobs(피드가 봇 차단됨). 수정 — Consider 기반 보드가 다시 결과를 반환하고, 다중 지역 Lever 공고가 더는 절반의 지역을 숨기지 않습니다.

추가

  • Job Bank (캐나다)(jobbank.gc.ca) — 어떤 애그리게이터도 잘 다루지 못하는 대용량 보드인 캐나다 연방 국가 고용 서비스를 위한 새 무토큰 스캔 소스입니다. #/scan의 소스 필터에서 고르거나, 선택적 keywords 목록과 함께 provider: jobbankca 회사를 추가하세요(생략 시 프로필의 목표 직무로 대체). 공개 ATOM 피드를 호스트 고정으로, 키 없이 읽습니다.

제거

  • EchoJobs — 폐기. 공개 피드가 이제 봇 차단 뒤에 있어 아무것도 반환하지 않으므로, 유지해봤자 스캔 슬롯만 낭비했습니다.

수정

  • Consider 기반 보드가 다시 결과를 반환합니다. 이제 Consider는 검색 요청을 받기 전에 익명 핸드셰이크(세션 쿠키 + CSRF 토큰을 심는 GET)를 요구합니다. 그것 없이는 요청이 조용히 거부되어 보드가 비어 보였습니다.
  • 다중 지역 Lever 공고가 더는 절반의 지역을 숨기지 않습니다. Lever는 기본 도시 하나를 location에, 나머지를 allLocations에 둡니다. 기본만 읽으면 바르셀로나 AND 몬테비데오에 열린 공고가 바르셀로나 전용으로 보였습니다(그리고 지역 필터에 잘못 걸려 탈락). 이제 둘을 병합합니다.

참고

  • 페이지 간 페이싱을 더 완만하게(150 → 250 ms) 조정해 단일 호스트 채용 사이트에 예의를 지킵니다. 스캔 소스: 81개(영어 76 + 러시아어 5) — 변동 없음(Job Bank 추가, EchoJobs 제거). 테스트 스위트: 2685.

[1.211.0] — 2026-08-19

추가 — 대만 테크 채용 보드 Yourator. 수정 — 제목·회사명의 악센트 엔티티가 이제 어디서나 디코딩되고, 이름에 악센트가 있는 회사가 더는 잘못 표시되지 않습니다.

추가

  • Yourator(yourator.co) — 대만 테크·디지털 채용 시장을 위한 새 무토큰 스캔 소스입니다. #/scan의 소스 필터에서 고르거나 provider: yourator 회사를 추가하세요. 공개 JSON API를 읽고(키·브라우저 없음), 보드의 모든 페이지를 순회하며, 각 공고의 실제 고용주 링크(자체 ATS)를 추적 파라미터를 제거해 내보냅니다.

수정

  • 악센트 명명 엔티티가 이제 어디서나 디코딩됩니다. 공유 HTML 디코더가 Latin-1 문자(&eacute; → é, &ccedil; → ç, …)를 갖췄으므로, D&eacute;veloppeur나 Fran&ccedil;ais를 쓰는 유럽 보드가 그 리터럴을 제목·트래커·생성 문서에 남기지 않습니다. (대문자는 대문자로 — &Eacute;는 É이지 é가 아님 — &constructor; 같은 조회는 이제 자기 자신으로 남습니다.)
  • 이름에 악센트가 있는 회사가 자체 도메인에 있다는 이유로 더는 잘못 표시되지 않습니다. “Işık”은 이제 “isik”으로 접혀 isik.com.tr과 일치하고, “Société Générale”은 societegenerale.com과 일치합니다. 이전 검사는 악센트 문자를 ASCII 기본형으로 접지 않고 지웠습니다.

참고

  • 스캔 소스: 81개(영어 76 + 러시아어 5). 테스트 스위트: 2667.

[1.210.1] — 2026-08-19

수정 — “&“나 따옴표가 든 Habr Career 공고 제목·회사명이 더 이상 깨져서 들어오지 않습니다.

수정

  • Habr Career 소스가 이제 제목과 회사명의 HTML 엔티티를 흘려보내기 전에 디코딩합니다. 서버 렌더링 카드는 이스케이프된 채로(“Changellenge >>”, “Demand Forecasting & Inventory Optimization”, “ООО "М-ТЕХ"”) 도착하므로, 디코딩되지 않은 “&“가 사용자의 “&” 제목 필터를 조용히 통과하지 못했고(직전 릴리스가 다섯 개 보드에서 닫은 바로 그 증상) 회사명이 트래커와 리포트에 깨진 채 도달했습니다. 이제 여섯 개 영향 소스 모두에서 엔티티 디코딩이 완결됩니다.

참고

  • 테스트 스위트: 2644.

[1.210.0] — 2026-08-19

추가 — 스캐너의 첫 아프리카 채용 보드 Senjob(세네갈); 다섯 개 보드의 제목 매칭 개선.

추가

  • Senjob(senjob.com) — 세네갈을 위한 새 무토큰 스캔 소스이자 스캐너의 첫 아프리카 보드입니다. #/scan의 소스 필터에서 고르거나 provider: senjob 회사를 추가하세요. 공개 목록을 평범한 HTTP로 읽고(키·브라우저 없음), 모든 요청을 senjob.com에 고정하며 — HTML을 파싱하므로 — 갑자기 아무것도 나오지 않는 목록을 “채용이 없는 나라”가 아니라 깨진 보드(드러난 오류)로 처리합니다.

수정

  • “&“가 든 제목이 다섯 보드에서 더는 공고를 떨어뜨리지 않습니다 — beesite, Cornerstone(csod), Hacker News “Who is hiring”, Phenom, TKMS는 제목이 HTML 이스케이프된 상태로 도착합니다. 그래서 “R&D Engineer” 같은 직무의 이스케이프된 “&“가 사용자의 “r&d” 키워드를 통과하지 못해 공고가 조용히 사라졌습니다(“sales & marketing” 부정 필터도 발동하지 않았습니다). 이제 제목 — 그리고 Phenom 위치 — 은 필터링 전에 디코딩됩니다.

참고

  • 스캔 소스: 80개(영어 75 + 러시아어 5). 테스트 스위트: 2643.

[1.209.0] — 2026-08-17

추가 — 인앱 도움말이 이제 지원 결과 기록 방법을 다루며, “도움말에 물어보기”가 그곳으로 안내합니다.

추가

  • 트래커 도움말(§11)에 17개 언어 모두 “결과 기록하기” 섹션이 추가되어 결과 버튼을 안내합니다: 무슨 일이 있었는지 선택(거절 / 오퍼 / 합격 / 거절 / 잠수 / 면접 진행)하고, 무엇을 할지 미리 본 뒤 기록하면 — 결과를 남기고, 제출한 CV와 커버레터를 보관하며, 행의 상태를 대신 동기화합니다. 떠다니는 “도움말에 물어보기” 도우미는 이 가이드를 읽으므로, 이제 상태를 직접 편집하라고만 하지 않고 그 버튼으로 안내합니다.

참고

  • 각 도움말 번들은 이제 31 H2 / 119 H3입니다(기존 118); 패리티 가드를 맞춰 올렸습니다. 문서만 변경 — 코드나 동작 변경 없음. 스위트: 2625.

[1.208.2] — 2026-08-16

수정 — 휴대폰에서 알림·테마 버튼이 더 이상 검색창 위에 겹치지 않습니다.

수정

  • v1.208.1은 상단 바 버튼이 페이지 제목을 가리는 문제를 없앴지만, 아주 좁지는 않은 좁은 화면에서 — 특히 버튼 라벨이 긴 언어에서 — 바 전체가 여전히 한 줄에 몰려서 🔔·🌙 버튼이 검색창 위에 놓일 수 있었습니다. 이제 동작 버튼(알림, 테마, 진단, Scan 열기)이 휴대폰에서는 항상 전체 너비의 둘째 줄로 내려가므로 검색창이 온전히 보이고 아무것도 겹치지 않습니다.

참고

  • 휴대폰에서 상단 바 동작 버튼이 전체 너비의 둘째 줄로 이동하여, 레이아웃이 남은 음수 공간을 겹침으로 분배하던 취약한 “거의 꽉 찬 줄” 구간을 없앴습니다. Playwright 가드가 이제 정확한 유발 조건 — 긴 라벨 로케일 × 565–640px 구간 — 을 재현하고 상단 바 컨트롤이 픽셀을 공유하지 않음을 확인합니다. 스위트: 2621.

[1.208.1] — 2026-08-16

수정 — 휴대폰에서 상단 바 버튼이 더 이상 페이지를 가리지 않습니다.

수정

  • v1.208.0에서 좁은 화면일 때 상단 바 동작 버튼(진단, Scan 열기, 알림, 테마)을 둘째 줄로 넘겼지만 바 높이가 고정이라, 넘어간 줄이 밖으로 삐져나와 페이지 제목 위에 겹쳤습니다. 이제 바가 줄에 맞춰 늘어나고 콘텐츠가 그 아래로 흐릅니다.

참고

  • 상단 바의 고정 height를 min-height로 바꿔 어떤 너비에서도 내용에 맞게 늘어납니다(데스크톱은 그대로). Playwright 가드가 이제 상단 바가 페이지 위로 삐져나오지 않는지도 확인합니다. 스위트: 2621.

[1.208.0] — 2026-08-16

수정 — 이제 앱이 휴대폰 화면에 맞습니다: 옆으로 스크롤되지 않습니다.

수정

  • 좁은 화면에서는 앱 전체가 옆으로 밀렸습니다 — 상단 바, 표, 도움말 문서, 설정 탭이 모두 오른쪽 가장자리를 넘었습니다. 이제 모든 페이지가 어떤 너비에도 맞습니다: 상단 바 버튼은 둘째 줄로 넘어가고, 넓은 표와 코드 블록은 자체 박스 안에서 스크롤되며, 도움말은 목차를 본문 위에 쌓고, 버튼·탭 줄은 줄바꿈되며, 긴 경로나 URL은 페이지를 늘리지 않고 줄바꿈됩니다.

참고

  • 근본 원인은 전형적인 flex/grid min-width: auto 함정과 감싸지지 않은 몇몇 넓은 요소였습니다. grid 항목의 min-width: 0, 마크다운·제목의 overflow-wrap, 스크롤 가능한 마크다운 표, 모바일 브레이크포인트에서의 도움말 그리드 쌓기로 해결했습니다. Playwright 가드가 주요 경로에서 375px에서 가로 오버플로 0을 검증합니다. tests/playwright-smoke.mjs. 스위트: 2621.

[1.207.2] — 2026-08-16

수정 — AI 계획과 커리어 오리엔테이션 프로필이 더 이상 원시 코드 덤프로 표시되지 않습니다.

수정

  • 일부 모델은 답변 전체를 markdown … 코드 펜스로 감쌉니다. 그럴 때 개발 계획과 오리엔테이션 프로필이 제목·목록이 있는 문서 대신 고정폭 코드 블록으로 나왔습니다. 이제 감싸는 펜스를 제거합니다 — 답변 전체를 감싸고 언어가 명시적으로 markdown/md일 때만, 그래서 진짜 python/js/언어 없는 ``` 코드 답변은 그대로 둡니다.

참고

  • 공용 LLM 정리 단계(cleanLlmMarkdown)에서 한 번에 처리하므로 모든 AI 경로가 혜택을 받고, 감싼 답변 안의 코드 블록은 보존됩니다. tests/llm-output.test.mjs (+3). 스위트: 2621.

[1.207.1] — 2026-08-16

수정 — 작은 휴대폰에서 랜딩 페이지가 더 이상 옆으로 넘치지 않습니다.

수정

  • 좁은 휴대폰에서 히어로 영역(제목, 소개 문장, 설치 터미널)이 오른쪽 가장자리에서 잘릴 수 있었습니다. 긴 설치 명령과 레이아웃 열이 화면에 맞게 줄어들지 않았기 때문입니다. 이제 어떤 너비에도 맞으며, 설치 명령은 자체 터미널 상자 안에서 스크롤됩니다.

참고

  • 또한 일시적인 리소스 404로 실패할 수 있던 불안정한 E2E 스모크 검사를 강화했습니다 — 이제 형제 검사처럼 무해한 네트워크 잡음(파비콘 / 연결 / 리소스 실패)을 무시하면서 실제 스크립트 오류는 계속 잡습니다. 애플리케이션 동작은 바뀌지 않았습니다. 스위트: 2618.

[1.207.0] — 2026-08-15

추가됨 — 트래커에서 바로 지원 결과를 기록합니다.

추가됨

  • 모든 트래커 행에 결과 동작이 생겼습니다: 무슨 일이 있었는지 고르고(불합격, 오퍼 수신, 입사, 오퍼 거절, 무응답, 면접 진행), 선택 메모를 추가하고, 결과 상태를 미리보기한 뒤 기록합니다. 기록하면 제출한 CV·커버레터 아티팩트를 보관하고 트래커를 표준 상태로 동기화합니다 — 손으로 편집하는 대신 결정적인 한 번의 동작입니다.

참고

  • 새 POST /api/outcome가 결과 CLI를 중계합니다: dryRun:true는 읽기 전용 미리보기(행을 찾아 결과 상태를 알려주며 아무것도 쓰지 않음)이고, 실제 호출은 기록합니다. 쓰기 안전성: 결과 유형은 알려진 집합으로 화이트리스트되고 모든 텍스트 필드는 셸 호출 전에 제어 문자를 거부합니다(배열 인자, spawn — 셸 없음). tests/outcome-route.test.mjs. 스위트: 2618.

[1.206.0] — 2026-08-15

문서 — 앱 내 도움말 가이드가 이제 최신 기능 다섯 가지를 17개 언어 모두로 다룹니다.

추가됨

  • 앱 내 도움말 가이드와 이를 근거로 답하는 «도움말에 질문» 도우미가 이제 최근에 추가된 다섯 기능을 문서화합니다: 설정 닥터(설정 — CV·프로필의 빠진 부분과 남은 예시 데이터를 점검), ATS 보드 찾기(포털 — 회사의 채용 보드를 자동으로 탐색), «아직 열려 있나요?» 확인(트래커 — 공고가 아직 열려 있는지), «이전 CV 재사용?» 힌트(CV 스튜디오 — 이미 맞춤한 CV가 새 공고에 맞는지 알림), 스킬 로그(분석 — 자가 평가 점수 기록). 새 하위 섹션 다섯 개를 17개 언어로 번역했습니다.

참고

  • 도움말 구조가 31개 H2 / 118개 H3로 늘었고 모든 언어에서 패리티가 보장됩니다. 참조 문서 갱신: docs/architecture/API.md가 이 기능들의 다섯 라우트를 문서화하고, CLAUDE.md와 docs/sdd/CONVENTIONS.md의 라우트/버전 수치가 최신입니다(라우트 모듈 36개). 스위트: 2610.

[1.205.0] — 2026-08-15

추가 — 시험/평가 결과를 기록하는 스킬 로그.

추가됨

  • 새 스킬 로그(분석 → 스킬 로그)로 자가 평가를 기록할 수 있습니다 — 회사, 플랫폼, 스킬, 점수 %, 선택 메모 — data/assessments.tsv에 추가되고 최신순 목록으로 표시됩니다. 토큰 없이 결정론적이며, 파일 형식은 부모 CLI가 담당합니다.

참고

  • 새 GET /api/assessments(assessment-log.mjs의 기본 JSON 목록 릴레이; 부드러운 실패 {available:false}) + POST /api/assessments(명시적 쓰기: 필드를 배열 인자로 assessment-log.mjs add에 전달). 쓰기 안전: 제어 문자가 든 텍스트 필드는 거부됩니다(TAB은 열을, 줄바꿈은 행을 깨뜨림) → 쓰기 전 400; 점수/기준은 0–100으로 제한, 길이 제한. tests/assessments-route.test.mjs. 스위트: 2610.

[1.204.0] — 2026-08-15

추가 — 설정에 “설정 닥터” 패널 추가, 불완전하거나 예시 데이터가 남은 CV/프로필을 표시합니다.

추가됨

  • 설정 → 설정 닥터가 이제 토큰 없이 cv.md와 config/profile.yml을 점검하여 차단 문제(누락 파일/필드)와 경고(남은 예시/자리표시 데이터, 하드코딩된 수치)를 나열합니다 — 불완전한 설정이 스캔·맞춤을 약화시키기 전에 잡습니다. 읽기 전용, 한 번 클릭으로 재실행.

참고

  • 새 읽기 전용 GET /api/cv-sync-check가 부모 프로젝트의 cv-sync-check.mjs를 릴레이합니다 — 이 스크립트는 사람이 읽는 텍스트 + 종료 코드를 출력하며 --json이 없습니다. 라우트는 안정적인 ERROR: / WARN: 줄을 가볍게 파싱해 {ok, errors[], warnings[]}로 만들며, 성공 여부는 종료 코드가 아니라 배너로 판단합니다. 독립 설치에서는 {available:false}로 부드럽게 실패합니다. tests/cv-sync-check-route.test.mjs. 스위트: 2602.

[1.203.0] — 2026-08-15

추가 — CV 스튜디오에 “이전 CV 재사용?” 힌트.

추가됨

  • CV 스튜디오에서 저장된 채용공고를 열면, 이제 앱이 저장해 둔 다른 공고들과 비교하여(결정론적 단어 겹침, 토큰 0) 가장 비슷한 것이 그 맞춤 CV를 재사용할 만한지, 수정 후 재사용할지, 새로 맞춤할지 알려줍니다 — 이미 노린 역할을 처음부터 다시 만들 필요가 없습니다.

참고

  • 새 읽기 전용 GET /api/jds/:name/reuse가 부모 프로젝트의 jd-similarity.mjs(자카드 겹침 + 시니어리티 가드; JSON {decision, score, reason})를 이전 공고마다 한 번씩 릴레이합니다(팬아웃 25 제한, 최적 매치 선택). 스크립트나 이전 공고가 없으면 {available:false}로 부드럽게 실패합니다. tests/jd-similarity-reuse-route.test.mjs. 스위트: 2594.

[1.202.0] — 2026-08-15

추가 — #/portals에서 회사의 ATS 채용 보드를 찾아 바로 추적을 시작하세요.

추가됨

  • #/portals에서 회사 이름을 입력하면 앱이 Greenhouse, Ashby, Lever에서 공개 채용 보드를 탐색하여 — LLM 0, 브라우저 없음 — 존재하고 현재 채용 ≥1건인 보드를 보여줍니다. 한 번 클릭하면 선택한 보드를 스캐너가 감시하는 회사에 추가합니다. 탐색은 읽기 전용이며, portals.yml 쓰기는 추가를 눌렀을 때만 일어납니다.

참고

  • 새 server/lib/discover-ats.mjs(고정 호스트·문자셋 검증 슬러그를 DNS 고정 safeGet으로 탐색, 요청당 ≤12회) + POST /api/portals/discover(읽기 전용) 및 POST /api/portals/track(명시적 쓰기: withFileLock + 텍스트 스플라이스 + 재파싱 가드 + 원자적 교체; 알려진 ATS 호스트만, 멱등). 스캐너의 어댑터 레지스트리를 재사용해 보드 확인·채용 수 계산. i18n ×17. tests/discover-ats-resolver.test.mjs + tests/discover-ats-route.test.mjs. 스위트: 2588.

[1.201.0] — 2026-08-15

수정 — 지역화되거나 변형된 열 머리글을 쓰는 트래커가 더 이상 빈칸으로 표시되지 않습니다.

수정됨

  • data/applications.md가 비영어 또는 변형 머리글(스페인어 empresa / puesto / estado / fecha / enlace, 또는 position / stage / link)을 쓰면, 트래커가 잘못된 키로 읽어 회사 / 직무 / 상태 / 날짜 / 링크 열이 빈칸으로 나왔습니다. 이제 이런 머리글이 표준 필드명으로 접혀 트래커가 올바르게 표시됩니다. 영어 트래커는 이전과 똑같이 처리됩니다.

참고

  • parseApplications(server/lib/parsers.mjs)에 새 HEADER_ALIASES 맵과 정규화 접힘 추가; 알 수 없거나 이미 표준인 머리글은 그대로 통과합니다. tests/tracker-header-aliases.test.mjs. 스위트: 2563.

[1.200.0] — 2026-08-15

추가 — 트래커에서 ATS 채용공고의 “아직 열려 있나요?“를 한 번에 확인.

추가됨

  • #/tracker에서 URL이 Greenhouse / Lever / Ashby / Workday / SmartRecruiters 공고인 지원 항목에 이제 “아직 열려 있나요?” 버튼이 표시됩니다. 한 번 누르면 ATS 자체의 공개 JSON을 조회하여 — 토큰 0, 브라우저 없음 — 열림 / 마감 / 알 수 없음을 보여주므로, 하나씩 열지 않고도 사라진 공고를 찾을 수 있습니다. 보수적 설계: 확실한 404/410만 마감으로 보고, 애매하면 알 수 없음으로 둡니다(잘못된 마감 없음).

참고

  • 새 server/lib/liveness-core.mjs + liveness-api.mjs와 읽기 전용 GET /api/liveness?url=(쓰기 없음, LLM 없음). SSRF 안전: URL은 isValidJobUrl로 검사한 뒤 ATS API는 고정 호스트와 문자셋 검증 경로로 DNS 고정 safeGet을 통해서만 접근합니다. tests/liveness-core.test.mjs + tests/liveness-route.test.mjs. 스위트: 2557.

[1.199.0] — 2026-08-15

수정 — 넓은 표가 잘리지 않고 가로로 스크롤됩니다.

수정됨

  • Scan 페이지(및 다른 모든 표 — 트래커, 통계, 사용량, 대시보드)에서 창보다 넓은 표가 스크롤바 없이 잘려 오른쪽 열에 접근할 수 없었습니다. 이제 넓은 표는 필요할 때 가로 스크롤바를 표시하므로 어떤 너비에서도 모든 열에 접근할 수 있습니다.

참고

  • public/css/components.css의 .table-wrap을 overflow: hidden에서 overflow-x: auto로 변경했습니다(기존 .reports-scroll 컨테이너와 동일). 둥근 테두리는 유지됩니다. tests/table-wrap-scroll.test.mjs. 스위트: 2540.

[1.198.0] — 2026-08-15

추가 — 스캔 재시도가 이제 지수 백오프, 지터를 사용하고 속도 제한의 Retry-After를 존중합니다.

추가

  • 스캔 도중 채용 보드가 잠깐 속도를 제한하거나 오류(HTTP 429 / 5xx)를 낼 때, 재시도가 고정된 짧은 지연 대신 지수 백오프 + 지터로 대기합니다 — 바쁜 보드를 같은 간격으로 계속 두드리지 않고, 동시 재시도가 한꺼번에 다시 충돌하지 않습니다. 보드가 보낸 Retry-After는 존중됩니다(단, 제한됨 — 악의적인 Retry-After: 86400이 전체 스캔을 멈추지 못하게). 영구 오류(404, 거부된 리디렉션)는 여전히 즉시 실패합니다 — 변경 없음.

참고

  • server/lib/http-json.mjs에 새 parseRetryAfterMs()와 순수 함수 computeRetryDelayMs(); fetchJson은 이제 비정상 응답에서 .retryAfter를 캡처하고 fetchJsonWithRetry는 선택적 maxDelayMs(기본 8000)를 받습니다. tests/http-json.test.mjs (+9). 스위트: 2536.

[1.197.0] — 2026-08-14

추가 — Getro VC 채용 보드를 careers_url 하나만으로 추적하고, 컬렉션 id는 자동으로 해석됩니다.

추가

  • 추적 중인 Getro 보드(b2venture, Earlybird, Point Nine, …)에 더 이상 손으로 찾은 숫자 getro_collection이 필요하지 않습니다. 보드 자체의 careers_url만 주면 첫 스캔 때 그 페이지에서 id가 자동으로 해석됩니다 — SSRF 안전 GET 한 번으로 페이지에 내장된 데이터에서 숫자 network.id를 바로 읽습니다. 명시적 getro_collection은 여전히 우선이며 가져오기를 통째로 건너뜁니다.

참고

  • server/lib/sources/getro.mjs에 새 httpsCareersUrl(), extractCollectionId(), 비동기 resolveCollectionId() 추가. 보드 페이지는 DNS 고정·크기 제한된 safeGet으로 가져오고, 해석된 id는 여전히 assertGetroUrl에 의해 호스트가 api.getro.com으로 고정됩니다. 이제 어댑터는 id가 없어도 https careers_url을 지닌 provider: getro 항목에 매칭됩니다. tests/sources-getro.test.mjs (+8). 스위트: 2527.

[1.196.0] — 2026-08-14

수정 (보안) — Workday 어댑터가 api 엔드포인트를 부분 문자열이 아닌 호스트명으로 검증합니다.

수정

  • portals.yml의 Workday api: 값은 이제 호스트명이 myworkdayjobs.com(또는 .myworkdayjobs.com 하위 도메인)일 때만 허용됩니다. 이전 검사는 부분 문자열 일치라서, 문자열을 포함하기만 한 URL — 예: https://example.com/?x=myworkdayjobs.com — 도 통과해 엔드포인트로 사용될 수 있었습니다. 실제 Workday 엔드포인트는 영향 없음. (CodeQL 보고, #443.)

참고

  • 새 isWorkdayApi()가 URL을 파싱해 호스트를 확인 (server/lib/portals/adapters/workday.mjs). tests/workday-adapter-endpoint.test.mjs (+1). 스위트: 2522.

[1.195.0] — 2026-08-14

성능 (스캐너) — 큰 스캔 이력에서도 재게시 감지가 빠르게 유지됩니다.

성능

  • 큰 scan-history.tsv에서 중복 공고 감지가 더 이상 O(N²)로 저하되지 않습니다. 회사별 제목 그룹화는 모든 쌍마다 전체 roleFuzzyMatch를 수행하는 중첩 루프였는데, 이제 역색인입니다 — 한 번의 패스로 정확한 제목별로 버킷팅한 뒤, 판별 토큰(비기본)을 공유하는 서로 다른 버킷에 대해서만 퍼지 매칭합니다. 출력은 동일 — 같은 재게시 클러스터 — 200개 이상의 무작위 이력에 대해 이전 알고리즘과의 차분 테스트로 증명.

참고

  • server/lib/detect-reposts.mjs의 groupRowsByTitle(차분 테스트를 위해 export). tests/detect-reposts-grouping.test.mjs (+2). 스위트: 2521.

[1.194.0] — 2026-08-14

수정 (스캐너) — 단일 세그먼트 URL의 Workday 채용 페이지가 이제 올바르게 스캔됩니다.

수정

  • Workday 어댑터가 이제 경로가 단일 세그먼트인 채용 URL을 파싱합니다 — 예: https://parsons.wd5.myworkdayjobs.com/Search, .../KBR_Careers, .../Careers. 이전에는 사이트가 External로 떨어져 잘못된 CXS 엔드포인트를 호출했고, 프로브가 정상처럼 보이지만 아무것도 반환하지 않을 수 있었습니다. 이제 경로의 첫 번째 비어 있지 않은 세그먼트를 사이트로 사용합니다(en-US 같은 로케일 접두사 제외); 문서화된 /en-US/External 케이스는 변하지 않습니다. (#255 보고.)

참고

  • server/lib/portals/adapters/workday.mjs의 구조적 경로 파싱. tests/workday-adapter-endpoint.test.mjs (+7). 스위트: 2519.

[1.193.0] — 2026-08-14

추가 (통계) — 챙길 만한 면접을 보여주는 “면접 후 무응답” 탭.

추가

  • #/stats의 면접 후 무응답 탭: 예의상 기간(기본 30일)을 넘겨 조용해진 면접을 진행 중 면접과 트래커를 합쳐 보여줍니다 — 각 면접이 얼마나 조용한지, 마지막 면접일, 이유와 함께. 부드러운 알림/마무리 목록이며 제안일 뿐, 불합격 주장이 아닙니다. 토큰 없음.

참고

  • 새 GET /api/stats/rejection-latency 릴레이(스크립트 부재 시 {available:false}). tests/stats-rejection-latency-route.test.mjs (+2). i18n 키 +10 ×17; #/stats help-hint 7→8 탭. 스위트: 2510.

[1.192.0] — 2026-08-14

추가 (cv-studio) — 실제로 없던 수치를 잡아내는 “CV 사실 확인” 게이트.

추가

  • #/cv-studio의 CV 사실 확인 카드: 맞춤 CV나 커버레터를 붙여넣어 모든 수치와 사실을 실제 CV·프로필·two-pager와 대조합니다. pass / warn / block 판정과 함께 지어낸 수치·근거 없는 사실·금지/주의 표현을 정확히 보여줍니다. LLM 없음, 아무것도 저장하지 않음.

참고

  • 새 POST /api/cv-studio/verify-facts 릴레이: 텍스트를 일회용 임시 파일에 써서 verify-cv-facts.mjs를 실행하며, 스크립트가 block에서 1로 종료해도 JSON 판정을 신뢰합니다. tests/cv-studio-verify-facts-route.test.mjs (+4). i18n 키 +15 ×17. 스위트: 2508.

[1.191.0] — 2026-08-14

추가 (통계) — 먼저 배울 스킬을 순위화하는 “다음에 배울 것” 탭.

추가

  • #/stats의 다음에 배울 것 탭: 트래커 전체 스킬 격차 집계 — 저적합을 가장 자주 초래한 부족 스킬을 가중치(모든 평가 리포트의 5−적합점수)와 Critical / High / Medium 티어로, 그리고 이미 CV/프로필이 다루는 스킬까지. 읽기 전용, 제안일 뿐, 토큰 없음.

참고

  • 새 GET /api/stats/upskill 릴레이(데이터 부족 시 { error } 필드; 스크립트 부재 시 {available:false}). tests/stats-upskill-route.test.mjs (+3). i18n 키 +15 ×17. 스위트: 2504.

[1.190.0] — 2026-08-14

추가 (트래커) — 어떤 회사가 실제로 응답하는지 알려주는 “회사 이력” 패널.

추가

  • #/tracker의 회사 이력 카드: 회사를 선택하면 읽기 전용 근거를 보여줍니다 — 그 회사가 당신에게 얼마나 응답했는지(무응답 / 혼합 / 이전에 응답함), 같은 공고가 계속 재게시되는지 — 트래커·후속 연락·스캔 이력을 결합. 토큰 없음; 스캐너를 호출하지 않습니다.

참고

  • 새 GET /api/stats/company-history[?company=] 릴레이(스크립트 부재 시 {available:false}). tests/stats-company-history-route.test.mjs (+3). i18n 키 +18 ×17. 스위트: 2501.

[1.189.0] — 2026-08-14

수정 (스캐너) — 로마 숫자로 쓴 연차 레벨이 비라틴 제목에서도 인식됩니다.

수정

  • skip_tiers 뒤의 티어 분류기가 이제 모든 문자 체계에서 직무 단어 뒤의 로마 숫자 레벨 접미사(I / II / III / IV / V)를 읽습니다 — “Инженер III”, “エンジニア I”, “Ingénieur IV” — ASCII 단어 뒤에서만이 아닙니다. 이전에는 비라틴 단어 뒤의 레벨 숫자가 무시되어 공고가 mid로 처리되었고, skip_tiers: [senior] 또는 [entry]가 그런 공고를 놓쳤습니다.

참고

  • server/lib/classify-tier.mjs의 문자 체계 독립 lookbehind; 죽은 중복 Sr. 매처 제거. tests/classify-tier.test.mjs (+1). 스위트: 2498.

[1.188.0] — 2026-08-14

수정 (UI) — 기본 액션 버튼이 더 이상 페이지 부제목에 딱 붙지 않습니다.

수정

  • 주간 인터뷰 다이제스트, 투자받은 회사, 포털, 커리어 플랜, 커리어 오리엔테이션 페이지의 기본 액션/컨트롤 행에 적절한 위쪽 여백이 생겨, 버튼이 부제목에 붙지 않고 아래에서 여유를 갖습니다.

참고

  • 회귀 가드 tests/lead-row-top-margin.test.mjs (+5). 스위트: 2497.

[1.187.0] — 2026-08-14

수정됨 (스캐너) — skip_tiers 설정이 다시 작동합니다: 연차로 건너뛰라고 한 공고가 제거됩니다.

수정됨

  • portals.yml의 skip_tiers: 목록(예: skip_tiers: [intern, entry])이 이제 스캔에서 반영됩니다. 각 공고의 제목이 연차 티어(intern / entry / mid / senior)로 분류되어 목록에 있으면 제거됩니다. 이전에는 스캔이 제목 / 위치 / 콘텐츠 / 신뢰 필터는 실행했지만 티어 필터가 없어 skip_tiers가 조용히 무시되었습니다. 레벨 단어가 없는 제목은 mid로 분류되며(따라서 skip_tiers: [mid]는 평범한 공고도 대부분 제거), 분류기는 가장 왼쪽 레벨 단어를 읽습니다.

참고

  • 새 순수 모듈 server/lib/classify-tier.mjs(classifyTier + buildTierFilter)를 EN·RU 스캐너 필터 체인에 연결. tests/classify-tier.test.mjs (+7). 전체: 2492.

[1.186.0] — 2026-08-14

추가됨 (CV 스튜디오) — “스킬 갭” 패널: 직무의 요구 스킬 중 CV가 명시/암시/누락한 것.

추가됨

  • CV 스튜디오의 새 스킬 갭 패널. 저장된 채용 공고를 선택하면 각 요구 스킬을 CV에 명시됨, CV에 암시됨, 누락으로 분류합니다 — LLM 없는 단어 비교, 아무것도 저장하지 않음. 공고에 명확한 요구사항 섹션이 없으면 신뢰도 낮음 안내가 표시됩니다.

참고

  • 새 GET /api/jds/:name/skill-gap(JD 이름은 경로 정화되고 jds/ 아래에 존재함을 확인한 뒤 인자로 사용; 스크립트가 없으면 {available:false}로 소프트 폴백). i18n 키 +13 ×17. 테스트: tests/jds-skill-gap-route.test.mjs (+4, 경로 순회 거부 포함). 전체: 2485.

[1.185.0] — 2026-08-14

추가됨 (통계) — “퍼널 및 속도” 탭: 당신의 퍼널이 시장과 어떻게 비교되고 단계 간 이동이 얼마나 빠른지.

추가됨

  • 통계의 새 퍼널 및 속도 탭은 시장 벤치마크 범위 옆에 당신의 응답률과 인터뷰율(소규모 표본 및 선택 편향 주의 포함), 일반적 첫 응답 창을 넘긴 진행 중 지원의 대기 목록, 단계별 중앙값 일수(지원 → 응답 → 인터뷰 → 오퍼)를 보여줍니다 — 느린 행은 우측 검열되어 중앙값을 왜곡하지 않습니다. 읽기 전용, 토큰 0; 당신의 트래커만 읽습니다.

참고

  • 새 GET /api/stats/funnel(스크립트가 없으면 {available:false}로 소프트 폴백). i18n 키 +18 ×17. 테스트: tests/stats-funnel-route.test.mjs (+2). 전체: 2481.

[1.184.0] — 2026-08-14

수정됨 (UI) — 대시보드의 빠른 작업 타일이 이제 균일한 그리드로 정렬됩니다.

수정됨

  • 대시보드(커맨드 센터)에서 3개짜리 타일 그룹이 4개짜리보다 넓게 렌더링되어 섹션의 오른쪽 가장자리가 들쭉날쭉했습니다. 이제 각 그룹이 동일한 너비의 열을 사용하며(넓은 화면에서 4열, 창이 좁아지면 3 / 2 / 1로 감소), 모든 타일 크기가 같고 오른쪽 가장자리가 정렬됩니다.

참고

  • CSS만 변경(.qa-grid: auto-fill 대신 고정 repeat(N, minmax(0,1fr))). tests/dashboard-grid-align.test.mjs로 보호(+2). 전체: 2479.

[1.183.0] — 2026-08-14

추가됨 (스캐너) — 더 똑똑한 중복 감지: 추적 링크로 다시 게시된 같은 채용이 더 이상 두 번 나타나지 않습니다.

추가됨

  • 스캐너가 이제 정규 URL 키로 공고를 인식하므로, 추적 매개변수(?utm_…, gclid 등), http 대 https, 또는 끝 슬래시 / #조각으로 다시 게시된 같은 채용을 하나의 공고로 취급합니다 — 스캔 결과나 파이프라인에 중복 행이 없고, 이미 본 채용에 대한 낭비된 평가도 없습니다. 진짜로 다른 공고(gh_jid 같은 유지된 기능적 id)는 여전히 별도로 계산됩니다.

참고

  • 새 server/lib/url-key.mjs를 두 스캐너의 dedup과 파이프라인 작성기에 연결. 의도적으로 덜 정규화 — 서로 다른 두 공고를 절대 병합하지 않습니다. 테스트: tests/url-key.test.mjs (+5), tests/parsers.test.mjs (+1). 전체: 2477 (+6).

[1.182.0] — 2026-08-14

수정됨 (스캐너) — 급여 범위가 이제 모든 언어에서 동일하게 표시됩니다.

수정됨

  • 스캔과 트래커 행의 급여 수치가 영어 단어 “from” / “up to”(비영어 UI에 번역되지 않고 노출됨) 대신 로케일 중립 기호 ≥, ≤(예: ≥ 120000 EUR, ≤ 90000)를 사용합니다. 한쪽 범위만 보고하는 모든 보드(Getro, Remotli, Manfred, Agentic Jobs, JustJoin, Jobicy)에 적용됩니다. 양쪽 범위(100000–150000 USD)는 이미 중립적이었습니다.

참고

  • 표시 전용 — 클라이언트 급여 필터는 접두어와 무관하게 숫자를 파싱하므로 필터링은 변하지 않습니다. 전체: 2471.

[1.181.0] — 2026-08-14

추가됨 (스캐너) — Getro 채용 보드가 이제 급여, 모든 위치, 원격 채용을 표시합니다.

추가됨

  • Getro 스캐너(펀드 인재 네트워크 보드)가 이제 각 채용에 급여 정보(연봉 범위 + 통화)를 표시하고, 첫 번째만이 아닌 모든 위치를 나열하며, 원격 채용을 표시합니다. 스캔과 트래커의 Getro 공고가 이제 다른 보드와 동일한 급여 + 위치 정보를 담습니다.

참고

  • 스캐너 전용; 새 의존성 없음, 라우트 / CSP / SSRF 변경 없음. 테스트: tests/sources-getro.test.mjs (+5). 전체: 2470 (+5).

[1.180.0] — 2026-08-14

수정됨 (중간, 리포트) — #/reports 목록이 이제 테이블이며, Machine Summary 플레이스홀더가 가리던 실제 점수를 복구합니다.

수정됨

  • #/reports 목록이 4카드 그리드가 아닌 테이블(리포트 · 날짜 · 신뢰도 · 점수)입니다. 긴 “점수 미감지” 칩이 제목 열을 거의 0으로 밀어붙였고, 카드 제목의 overflow-wrap: anywhere가 리포트 이름을 한 글자씩 줄바꿈했습니다. 이제 각 필드가 자체 열을 가지며, 이름 셀은 단어 단위로 줄바꿈되고, 좁은 화면에서는 테이블이 가로로 스크롤됩니다(새 .reports-scroll 컨테이너). 새 i18n 키 rep.colReport ×17.
  • 본문의 실제 점수(**Итоговый балл:** 1.8 / 5)가 더 이상 Machine Summary 플레이스홀더(score: —)에 가려지지 않습니다. ## Machine Summary 블록에 숫자가 아니거나 범위를 벗어난 점수가 있으면 파싱된 점수 자리를 차지해 볼드 값-형식 폴백을 막아, 본문에 실제 X / 5가 있어도 리포트가 “점수 미감지”로 표시되었습니다. 이제 parseReportHeader는 사용 가능한 숫자가 남지 않으면 본문의 값-형식을 복구합니다(4.5단계).

참고

  • 클라이언트 + 파서만; 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 테스트: tests/reports-table.test.mjs (+5), tests/report-header-locale.test.mjs (+2). 전체: 2465 (+7).

[1.179.0] — 2026-08-13

변경됨 (LOW, 스캐너) — 중복된 20개 HTML 엔티티 디코더를 공유 모듈로 통합 (패리티 후속, 워크리스트 종료).

변경됨

  • 20개 스크래핑 스캔 소스가 각자 decodeEntities/decodeXmlEntities(+ fromCodePoint 헬퍼)를 갖고 있었고, 이 복사본들은 드리프트했습니다(셋은 RangeError를 던질 수 있었고 v1.172.0에서 수정; 나머지는 NUL/C0를 허용하거나 &#1a2;를 잘못 파싱). 이제 모두 단일 server/lib/html-entities.mjs(XML 1.0 Char 안전 디코더)를 거치며 ~237줄의 중복을 제거했습니다. 8개 RSS 계열 소스는 &nbsp; 디코딩을 얻었고(이전엔 5개 엔티티만 처리), cryptocurrencyjobs의 의도적 이중 디코딩은 별칭으로 보존됩니다. hh는 자체 디코더 유지(&mdash;/&ndash; 처리, 공유 6개 밖). 새 가드 테스트는 어떤 소스든 로컬 디코더를 다시 만들면 실패합니다.

참고

  • 동작을 보존하는 리팩터링; 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 테스트: tests/decoder-consolidation.test.mjs (+2). 전체: 2458 (+2).

[1.178.0] — 2026-08-13

수정됨 (LOW, 상위 패리티) — 오래된 상수 두 개를 상위와 일치하도록 갱신 (PARENT-SYNC GAP #4 + #5).

수정됨

  • 브라우저 User-Agent (GAP #4) — BROWSER_LIKE_USER_AGENT(workable/workday/oraclecloud/a16z/eightfold가 WAF/봇 게이트 통과에 사용)를 Chrome 131에서 151로 올려 상위 user-agent.mjs와 일치시킴; 오래된 빌드는 차단되기 쉽습니다. Chrome major ≥ 151 테스트로 보호.
  • 트래커 상태 FALLBACK (GAP #5) — states.mjs의 최후 수단 FALLBACK(라이브 templates/states.yml을 읽을 수 없을 때만 사용 — 새 클론 / CI 격리 루트)에 상위의 터키어 상태 별칭(#2615) 추가: değerlendirildi, başvuruldu, yanıt verildi, mülakat, teklif, reddedildi, iptal edildi, uygun değil, kabul edildi/işe alındı. 프로덕션에서는 라이브 파일이 이미 제공했습니다.

참고

  • 상수 두 개뿐; 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 테스트: tests/http-json.test.mjs (+1) + tests/states.test.mjs (+1). 전체: 2456 (+2).

[1.177.0] — 2026-08-13

수정됨 (MEDIUM, 스캐너) — 검색 API를 세션 쿠키로 보호하는 테넌트에서 csod(Cornerstone)가 0건을 반환하던 문제 (parent #2769, PARENT-SYNC GAP #1).

수정됨

  • 일부 Cornerstone 테넌트는 부트스트랩 채용 사이트 홈에서 세션 쿠키를 설정하고, 그 쿠키가 익명 베어러 토큰과 함께 돌아오지 않으면 검색 API에서 401 CSOD Unauthorized를 반환합니다. sources/csod.mjs는 이제 새 fetchResponse 헬퍼로 부트스트랩을 읽어 Set-Cookie 값으로 Cookie 헤더를 만들고(cookieHeaderFrom — name=value만, jar 시맨틱) 검색 POST에 다시 보냅니다. 동일 출처 전용(호스트 고정 + redirect:'error')이라 세션 쿠키가 제3자에게 갈 수 없으며, 쿠키를 설정하지 않는 테넌트는 이전과 동일하게 동작합니다.

참고

  • 새 server/lib/http-json.mjs::fetchResponse(추가형; 기존 소스 영향 없음). 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 테스트: tests/sources-parity-v1118a.test.mjs (+1). 전체: 2454 (+1).

[1.176.0] — 2026-08-13

수정됨 (MEDIUM, 리포트) — RU 표에 없는 굵은 라벨 아래의 점수가 여전히 “Score not detected”로 표시됨 (FIND-5).

수정됨

  • 두 RU 리포트가 점수를 **Итоговый балл:** 1.8 / 5 / **Скор:** 1.8 / 5로 적었는데, REPORT_LABELS.ru가 열거하지 않는 굵은 라벨(“Оценка”/“Балл”만 앎)이라 점수가 파싱되지 않았습니다. 동의어 목록을 늘리는 대신 parseReportHeader는 이제 값 형태로 폴백합니다: 임의의 굵은 라벨 아래 /5 루브릭 분수. 언어 독립적이고, 제목에 면역이며(** 없음, /5 값 없음), 5/5/2026 같은 날짜를 거부합니다(분모에 대한 부정 전방탐색).

참고

  • 서버 파서만; 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 테스트: tests/report-header-locale.test.mjs (+2). 전체: 2453 (+2).

[1.175.0] — 2026-08-13

수정됨 (LOW, 견고화) — FIND-3 SEO 설명에 대한 회귀 가드 + 널 안전 정당성 스트립 (AI 리뷰 후속).

수정됨

  • SEO 설명 패리티 가드 — 각 언어의 meta.desc에서 하드코딩된 “~55”를 레지스트리 기반 {adapters} 플레이스홀더로 바꾼 v1.174.0 수정에는 테스트가 없어 다음 로케일 편집에서 조용히 회귀할 수 있었습니다. 새 CI 격리 tests/site-meta-desc-parity.test.mjs는 17개 site/src/i18n/*.json 중 하나라도 플레이스홀더를 잃거나 개수를 다시 하드코딩하거나, Landing.astro가 세 개의 설명 메타에 이를 보간하지 않으면 실패합니다.
  • 널 안전 정당성 스트립 — stripEmphasis는 널 입력에 대해 문자열 “undefined”가 아닌 ''를 반환합니다(필드는 문자열로 초기화되므로 심층 방어).

참고

  • 테스트 + 파서의 한 줄 가드; 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 테스트: tests/site-meta-desc-parity.test.mjs (+3). 전체: 2451 (+3).

[1.174.0] — 2026-08-13

수정됨 (HIGH, 리포트) — 현지화된 리포트가 “Score not detected”로 표시되고 SEO 설명이 오래됨.

수정됨

  • 점수 파싱 (FIND-1) — H1에 점수 라벨 단어가 포함된 비영어 리포트(# Оценка вакансии: <제목>)에서 그 제목을 점수로 오인하지 않습니다. parseReportHeader가 이제 현지화된 굵은 라벨(**Оценка:** 1.5 / 5)에 고정하고, 제목 줄을 건너뛰며, 라벨이 콜론에 인접하도록 요구합니다 — 그래서 “Score not detected”였던 RU 리포트가 실제 점수를 표시합니다.
  • 정당성 칩 (FIND-2) — 값에서 Markdown 강조가 제거되어 칩이 “** High Confidence”가 아닌 “High Confidence”로 표시됩니다.
  • 점수 오버플로 — 상태 텍스트가 뒤따르는 점수 줄(“1.8, Status: Evaluated, …”)은 점수만으로 압축됩니다; .score-pill에 줄바꿈 없음/오버플로 캡이 추가되고 제목 열이 줄어들 수 있어 색상 칩이 카드 밖으로 넘치지 않습니다.
  • SEO 설명 (FIND-3) — cvstart.org의 meta / OG / Twitter 설명(17개 언어 모두)이 “Scan ~55 job boards”로 고정된 반면 본문은 실제 레지스트리(“~75”)를 세었습니다. 이제 설명이 레지스트리 기반 개수를 삽입하여 다시 어긋나지 않습니다.

참고

  • 서버 파서 + 클라이언트 렌더/CSS + 사이트 i18n; 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 테스트: tests/report-header-locale.test.mjs (+4). 전체: 2448 (+4).

[1.173.0] — 2026-08-13

추가됨 (LOW, 설정) — Hermes가 감지되는 AI CLI 목록에 추가됨 (career-ops 동등성).

추가됨

  • #/config → “AI CLI 도구” 탭이 이제 Hermes(Nous Research), 상위 프로젝트가 새로 지원하는 에이전트 런타임(바이너리 hermes)을 탐지합니다. server/lib/routes/cli-detect.mjs의 고정 허용 목록이 10개에서 11개 도구로 늘어납니다; 탐지는 읽기 전용 PATH 스캔으로 유지됩니다(바이너리는 절대 실행되지 않음).

참고

  • i18n / 라우트 / CSP / SSRF / 상위 쓰기 변경 없음; 목록은 고정 허용 목록이며 입력이 아닙니다. 전체: 2444 (cli-detect 카나리 10 → 11 갱신).

[1.172.0] — 2026-08-13

수정됨 (MEDIUM, 스캐너) — 잘못된 HTML 엔티티가 스캔 소스를 중단시킬 수 있었음 (career-ops #2150 동등성).

수정됨

  • oraclecloud, gem, dassault 소스는 숫자 HTML 엔티티를 String.fromCodePoint 이전에 단순한 Number.isFinite 검사만으로 디코딩했습니다 — 0x10FFFF를 넘는 참조(예: 잘못되었거나 악의적인 피드의 &#99999999;)는 잡히지 않은 RangeError를 던져 해당 소스의 전체 파싱을 중단시켰습니다. 공유 모듈 server/lib/html-entities.mjs(상위 프로젝트의 _html-entities.mjs를 반영)가 이제 숫자 참조를 XML 1.0 §2.2 Char 집합으로 제한하여 String.fromCodePoint가 절대 던지지 않으며, 16진수와 10진수를 별도로 매칭하여 &#1a2;가 더 이상 잘못 파싱되지 않습니다. 세 소스가 이를 가져옵니다.

참고

  • 유효한 피드에는 변화 없음; JS / i18n / 라우트 / CSP / SSRF / 상위 쓰기 변경 없음. 소스에 남은 ~20개 디코더 사본 통합은 qa/PARENT-SYNC-WORKLIST-v1.26.0.md에 기록됨.
  • 테스트: tests/html-entities.test.mjs (+7). 전체: 2444 (+7).

[1.171.0] — 2026-08-13

변경 (LOW, 디자인 시스템) — 타입 스케일 + z-index 레이어 토큰 (D-4, 첫 단계). 크기와 쌓임이 컴포넌트별 리터럴이었습니다.

변경됨

  • z-index 레이어 — --z-* 토큰(--z-topbar … --z-skiplink)을 도입하고 모든 z-index 리터럴을 마이그레이션했습니다. 값이 보존되어 쌓임은 동일; 새 카나리가 새로운 매직 넘버를 금지합니다.
  • 타입 스케일 — --font-size-* 램프(xs 11 … 2xl 28, base = Inter 15px); 컴포넌트가 이미 쓰던 핵심 크기를 마이그레이션(시각 변화 없음). 램프 밖 값은 점진 마이그레이션(docs/UX-ROADMAP.md).

참고

  • CSS 토큰만; 동작/JS/i18n/라우트/CSP/SSRF/쓰기 변경 없음. 픽셀 변화 없음. tests/design-tokens-scale.test.mjs (+3). 총 2437 (+3).

[1.170.0] — 2026-08-13

추가 (LOW) — 긴 AI 생성에 정직한 ETA 힌트 (P4-ETA). 무거운 생성(커리어 플랜 ~40초, 오리엔테이션 / 마켓 / 네트워킹 ~30초, 투페이저 ~20초)이 기간 표시 없이 “생성 중…“만 보였습니다.

추가됨

  • 각 긴 생성 버튼 옆에 이제 ⏱ ~N초 힌트가 표시됩니다(#/auto ETA와 동일). 공유 .eta-hint 스타일 + 제네릭 키 2개(common.eta ~{n}s, common.etaTitle).

참고

  • 클라이언트만; 라우트/CSP/SSRF/쓰기 변경 없음. i18n 키 +2 ×17(스냅샷 1219 → 1221). tests/generation-eta-hint.test.mjs (+2). 총 2434 (+2).

[1.169.0] — 2026-08-13

추가 (LOW) — 인라인 PDF 미리보기 (D-5). GET /api/output/pdfs/:name가 Content-Disposition: attachment를 강제해 #/cv의 “열기” 링크조차 표시 대신 다운로드했습니다.

추가됨

  • **?inline=1**은 동일한 정제 파일을 Content-Disposition: inline으로 제공해 새 탭에서 👁 미리보기로 렌더링합니다. 기본값은 여전히 다운로드. 새 라우트 없음; 동일한 이름 가드 적용.
  • #/cv PDF 목록의 첫 버튼이 👁 미리보기(다운로드 옆). cv.openPdf “열기” → “미리보기” ×17.

참고

  • CSP/SSRF 변경 없음 — 동일한 sanitizePathName. 기존 i18n 키 1개 재작성 ×17(스냅샷 1219). tests/output-pdfs.test.mjs (+3). 총 2432 (+3).

[1.168.0] — 2026-08-13

수정 (LOW, a11y) — 체크박스 행이 이제 WCAG 2.5.8의 24×24 최소 타깃 크기를 충족합니다 (D-2). #/scan, #/config, #/evaluate, #/cv-studio의 체크박스/라디오 라벨이 ~22 px 밴드였습니다.

수정됨

  • 범위가 지정된 규칙 label:has(> input[type="checkbox"/"radio"]) { min-height: 24px }가 ≥24 px 밴드를 보장합니다. min-height만 — 라벨은 이미 flex이므로 이동 없음; .apply-checklist(32 px)는 이미 준수.

참고

  • CSS만; 동작/JS/i18n/라우트/CSP/SSRF/쓰기 변경 없음. tests/checkbox-target-size.test.mjs (+1). 총 2429 (+1).

[1.167.0] — 2026-08-13

수정 (LOW, 디자인 시스템) — 상승 표면이 이제 헤어라인과 구분됩니다 (D-3). --panel-2 / --surface-elev1이 헤어라인 --line / --border와 동일한 --slate로 해석되어 구분이 없었습니다.

수정됨

  • 전용 테마 인식 토큰 --elev(라이트 #eef1f6 / 다크 #1e232e, 두 테마 모두 --slate와 구분)가 상승 표면을 뒷받침하며 헤어라인은 --slate 유지. 나머지 항목(D-2, D-4, D-5, P4-ETA)은 docs/UX-ROADMAP.md에 백로그로 기재.

참고

  • CSS 토큰만; 동작/JS/i18n/라우트/CSP/SSRF/쓰기 변경 없음. tests/elevation-token.test.mjs (+2). 총 2428 (+2).

[1.166.0] — 2026-08-13

수정 (LOW) — 루브릭 용어가 이제 공식 문서와 일치합니다. career-ops.org/docs는 “다섯 차원과 종합 전체 점수”라고 하지만, 웹 UI·cvstart.org·위키는 모두 “6차원 루브릭”이라고 했습니다(5 + 1 = 6이지만 표현이 달랐습니다).

수정됨

  • 문서 표현 — “다섯 차원과 종합 전체 점수” — 을 README ×17, cvstart.org 사이트 ×17, 도움말 가이드 ×17, docs/career-ops-canonical.md, 위키(Home ×17 + Features)에 일관 적용.

참고

  • 문서/마케팅 문구만; 코드/i18n 키/라우트/CSP/SSRF/쓰기 변경 없음. tests/rubric-terminology.test.mjs (+2). 총 2426 (+2).

[1.165.0] — 2026-08-13

수정 (LOW) — “Two-pager” 용어가 이제 각 로케일 내에서 일관됩니다. 아랍어에서 사이드바는 라틴 “Two-pager”를 표시하는데 <h1>은 완전히 현지화되어 있었습니다 — RTL 내비게이션에서 유일한 라틴 문자열이었습니다.

수정됨

  • 결정 적용: 로케일별로 nav.twoPager와 twoPager.title이 용어를 일치시킵니다(둘 다 라틴 또는 둘 다 현지화). 아랍어만 갈라져 있었고, 이제 내비 라벨이 현지화(“الصفحتان”)되었습니다. 새 카나리가 다시 갈라지면 실패합니다.

참고

  • 문구만; 라우트/CSP/SSRF/쓰기 변경 없음. i18n 값 1개(ar) 변경; 새 키 없음(스냅샷 1219). tests/two-pager-term-consistency.test.mjs (+2). 총 2424 (+2).

[1.164.0] — 2026-08-13

수정 (LOW) — 상단 검색 플레이스홀더가 이제 어떤 로케일에서도 넘치지 않습니다. “Find a company, role or URL…“는 검색바가 줄어들면 잘렸고, “…or URL” 부분이 보이지 않았습니다.

수정됨

  • top.search (×17)가 이제 짧은 “검색 또는 URL 붙여넣기”(모든 로케일에서 ≤24자)로, 좁은 바에서도 맞고 URL 안내를 유지합니다. index.html 폴백이 일치하며 aria-label은 전체 설명을 유지합니다.

참고

  • 문구만; 라우트/CSP/SSRF/쓰기 변경 없음. 기존 i18n 키 1개 재작성 ×17(신규 없음; 스냅샷 1219). tests/search-placeholder-fit.test.mjs (+2). 총 2422 (+2).

[1.163.0] — 2026-08-13

수정 (LOW) — 인앱 “문서에 질문” 어시스턴트가 이제 리포트 PDF 내보내기를 다룹니다. #/reports/:slug에 📄 Generate PDF가 있는데도 가이드가 다루지 않는다고 답하곤 했습니다.

수정됨

  • 17개 도움말 번들 모두 §10 Reports 아래에 “리포트를 PDF로 내보내기” H3 추가(버튼 위치, 파일이 output/*.pdf에 저장, Playwright 필요, 전송 전 검토). 어시스턴트 검색이 이제 Reports 섹션을 노출합니다.

참고

  • 문서/도움말만; 코드/라우트/CSP/SSRF/쓰기 변경 없음. 도움말 게이트 112 → 113 H3(31 H2 유지). tests/help-reports-pdf-section.test.mjs (+2). 총 2420 (+2).

[1.162.0] — 2026-08-13

수정 (MEDIUM) — 도움말 “?“가 이제 ≥24×24 포인터 대상입니다 (WCAG 2.5.8). .help-hint는 모든 제목에서 padding:0의 18×18 px로 최소 기준 미만이었습니다.

수정됨

  • .help-hint 박스가 이제 24×24(측정 가능한 대상)이며 보이는 링은 18px 유지 — 가운데 정렬된 ::before로 그려져 글리프와 <h1> 기준선이 그대로입니다. hover/활성/포커스 상태는 링을 따라가고, 여백 6→3px로 간격 유지.

참고

  • CSS만; JS/i18n/라우트/CSP/SSRF/쓰기 변경 없음. tests/help-hint-target-size.test.mjs (+2). 총 2418 (+2).

[1.161.0] — 2026-08-13

수정 (MEDIUM) — #/reports가 빈 공간 대신 “점수 감지 안 됨” 칩을 표시합니다. v1.159.0 로케일 파서 이후에도 점수를 파싱할 수 없는 리포트는 빈 영역을 보여 실패와 구분되지 않았습니다.

수정됨

  • 점수 셀이 분기합니다: 점수 있음 → 톤 필; 점수 없음 → .score-muted 칩(“점수 감지 안 됨”, ×17) + “리포트를 열어…” 툴팁. 카드는 키보드로 조작 가능한 role="link"로 유지되고 날짜도 표시됩니다.
  • 기존 중립 토큰 재사용; 새 색상 없음.

참고

  • 클라이언트만; 라우트/CSP/SSRF/쓰기 변경 없음. i18n 키 +2 ×17 (스냅샷 1217 → 1219). 총 2416 (+3).

[1.160.0] — 2026-08-13

수정 (HIGH) — 제공자 안내 문구가 더 이상 7개 제공자 약속과 모순되지 않습니다. #/config는 라이브 평가가 “Anthropic 또는 Gemini 키를 사용”하고 OpenAI 키는 “웹 UI에서 사용되지 않는다”고 했고, 대시보드는 “Anthropic-first scoring”이라 표시했습니다 — 7개 제공자 캐스케이드(v1.157.0) 이후로 거짓.

수정됨

  • config.providerModelNote (×17): 이제 ⚡ 라이브 평가가 일곱 제공자 키(Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub Models · Hermes) 중 아무거나로 헤드리스 실행된다고 명시. 거짓 OpenAI 문장 제거.
  • dash.quick.evaluateSub (×17): 제공자 중립(“0–5 적합도 점수”). Keys: N / 5 → N / 7.

참고

  • 문구만; 라우트/CSP/SSRF/쓰기 변경 없음. 새 i18n 키 없음(스냅샷 1217). 총 2413 (+3).

[1.159.0] — 2026-08-13

수정 (HIGH) — 리포트 메타데이터가 더 이상 언어에 종속되지 않습니다. 영어가 아닌 로케일로 생성된 리포트는 #/reports에서 빈 메타데이터 영역(점수/날짜/정당성 없음)으로 표시되었습니다. parseReportHeader가 영어 굵은 라벨만 인식했기 때문입니다.

수정됨

  • parseReportHeader가 언어 불변 ## Machine Summary YAML 블록(score: / legitimacy: / date: — auto-pipeline이 이미 읽는 소스)을 파싱합니다. 우선순위: 영어 라벨 → Machine Summary → 로케일 라벨(REPORT_LABELS, 17개 로케일). 영어 리포트는 바이트 단위로 동일합니다.
  • 숫자 파싱 관대화(1.5/5, 1,5/5, 1.5 из 5, 4.5 out of 5); 본문에 날짜가 없으면 파일 mtime으로 대체.

참고

  • 읽기/파싱만; 라우트·CSP·SSRF·부모 쓰기 변경 없음. 새 i18n 키 없음. 총 2410 (+8).

[1.158.0] — 2026-08-12

수정 — 두 가지 표시상의 사소한 버그(탭 제목에 새어 나온 «?»와 랜딩의 잘못된 제공자 수). 표시 전용이며 동작·보안·데이터 흐름 변경 없음.

수정됨

  • HelpHint의 «?»가 더 이상 document.title에 새어 나오지 않습니다. 라우터가 가공되지 않은 h1.textContent에서 탭 제목을 만들어 «Vacancy search?»로 표시되었습니다. 이제 router.js::focusNewView가 제목을 복제하고 .help-hint를 제거한 뒤 텍스트를 읽습니다. 화면의 «?»는 그대로입니다.
  • cvstart.org가 «7» 대신 «17 AI providers»를 표시했습니다. Features.astro의 sub()가 카드별 치환 전에 모든 {n}을 로케일 수(17)로 바꿨습니다. 이제 {n}은 카드별로 해석됩니다(제공자 → 7, 언어 → 17).

참고

  • 서버·라우트·CSP·SSRF·i18n 키 변경 없음. 총 2402개 테스트(+1).

[1.157.0] — 2026-08-12

수정됨 — 라이브 평가가 이제 Anthropic/Gemini뿐 아니라 설정된 어떤 제공자로도 실행됩니다. OPENROUTER_API_KEY만 설정한 사용자가 잘못 수동 모드로 강제되었습니다.

수정됨

  • 근본 원인: 키 없는 LLM_PROVIDER 핀(예: init의 LLM_PROVIDER=claude)이 막다른 길로 갔습니다. 이제 설정된 제공자들 사이에서 auto 순서로 대체됩니다(selectActiveProvider + 두 디스패치 캐스케이드).
  • 클라이언트 게이팅(#/deep + mode-page 뷰)이 이제 오래된 Anthropic/Gemini 프로브 대신 window.ProviderStatus(/api/status/providers, 7개)를 사용; 문구 재작성(deep/eval × 17) + 대시보드 «라이브 평가» 배지 + config.llmProviderHint.

참고

  • 보안 변화 없음. 스위트: 2401개 테스트(+5).

[1.156.0] — 2026-08-12

리팩터 — scan.js를 파일 크기 한도 아래로 분할(P-16) + CodeQL 수정. scan.js는 906줄이었고, 동작 보존 팩토리 두 개를 추출해 648줄로. P-15/P-16 뷰 분할 쌍을 완성.

변경됨

  • 새 scan/runner.js(스캔 실행 엔진)와 scan/filters.js(필터 상태 머신)를 ctx/refs 백으로 추출; scan.js가 둘을 연결.

수정됨

  • CodeQL js/useless-assignment-to-local(#428) config/tab-controller.js: let n = i; → let n;.

참고

  • 순수 리팩터, 동작 변화 없음; 소스를 읽는 테스트 4개 재지정. 두 큰 뷰 모두 800 미만(P-15/P-16 완료). 스위트: 2396개 테스트.

[1.155.0] — 2026-08-12

리팩터 — config.js를 파일 크기 한도 아래로 분할(P-15). config.js는 1030줄(800줄 한도 초과)이었고, 동작을 보존하는 두 모듈을 추출해 783줄로 줄였습니다.

변경됨

  • 새 config/field-specs.js(필드 데이터 + 모델 목록)와 config/tab-controller.js(탭 바 팩토리) 추출; config.js가 이를 참조하며 렌더 로직은 그대로.

참고

  • 순수 리팩터, 동작 변화 없음; 소스를 읽는 테스트 6개를 재지정. scan.js(906)는 그대로 둠(이미 부분 분할됨; 코어가 너무 결합되어 깔끔한 기계적 분할이 어려움). 스위트: 2396개 테스트.

[1.154.0] — 2026-08-12

새 가이드 — “클라우드에서 전체 스택 실행.” career-ops에는 자체 클라우드/서버 안내가 없어 하나 추가했습니다: 부모 career-ops 파이프라인, 이 career-ops-ui 뷰어, AI 엔진(Claude Code를 통한 Claude 구독, 로컬 Hermes, 또는 API 키)을 항상 켜진 소형 서버에 올리는 단계별 레시피. 17개 언어의 도움말 §31, README 섹션, 위키 페이지로 제공됩니다.

추가됨

  • 도움말 §31 “클라우드에서 전체 스택 실행”(× 17) — 세 부분, 프로비저닝 + 설치, 엔진 선택, 안전한 노출(HTTPS 리버스 프록시 + 인증 + CSP/SSRF/XSS/비밀-없음 불변식). 도움말 번들이 31 H2 / 112 H3로 확장.
  • README — “클라우드에서 전체 스택 실행” 섹션(× 17) + 위키 Cloud-Deployment 페이지.

참고

  • 문서 전용 — 라우트·서버·클라이언트 변경 없음, 새 i18n 키 없음. 도움말 4개 테스트가 31 H2 / 112 H3 계약으로 이동. 스위트: 2396개 테스트(변화 없음).

[1.153.0] — 2026-08-12

Jobvite 스캐너가 공개 XML 피드로 이전(부모 동기화). 부모가 Jobvite JSON API를 폐기(이제 0건 반환)했고, web-ui source가 같은 죽은 엔드포인트를 써서 추적 중인 Jobvite 회사가 조용히 빈 결과로 스캔되었습니다. 부모 픽스(#2623)를 이식: 이제 companyEId로 키를 매기는 공개 테넌트별 XML 피드를 읽습니다.

수정됨

  • source가 폐기된 JSON API를 호출해 0건을 반환했습니다. 이제 https://app.jobvite.com/CompanyJobs/Xml.aspx?c={companyEId}를 호출하고 XML <result><job>…를 파싱합니다(CDATA + 엔터티, apply-url보다 detail-url 우선).

변경됨

  • companyEId 해석: (1) 포털의 company_eid:, (2) 명시적 api:의 c= 파라미터, (3) 보드 페이지 디스커버리. fetchText(http-json.mjs)가 non-ok 오류에 .location/.retryAfter를 부착(읽기 전용, 하위 호환).

참고

  • 보안 — 두 호스트(jobs.jobvite.com, app.jobvite.com)를 assertJobviteUrl로 매 요청 고정: https 전용, 엄격한 허용목록, 리디렉션 미추적. companyEId는 오직 ?c= 값이며, source 개수 변화 없음.
  • 스위트: 2396개 테스트(+4).

[1.152.0] — 2026-08-12

Hermes 제공자 — 배선 완료 + 문서 최신화. v1.151.0 Hermes 통합의 코드 리뷰에서 실제 결함 2건과 완성도 항목 4건을 발견해 모두 여기서 수정했으며, 앱 전체의 LLM 제공자 목록을 모든 문서 표면과 17개 언어에서 완전한 7개로 맞췄습니다.

수정됨

  • #/config에서 Hermes를 강제할 수 없었음 — LLM_PROVIDER 드롭다운이 6개 제공자만 나열해 HERMES_API_KEY는 설정해도 UI에서 Hermes를 강제할 수 없었습니다. 이제 hermes가 8번째 옵션이며, 새 패리티 테스트가 드롭다운이 LLM_PROVIDERS에서 다시 어긋나지 않도록 막습니다.
  • 짧은 자체 호스팅 키가 조용히 거부됨 — isUsableKey의 20자 하한은 클라우드 키 기준이었고, hasHermesKey는 이제 완화된 8자 하한을 씁니다(Hermes 문서 예시가 19자).

변경됨

  • 제공자 목록을 README(× 17), 인앱 도움말(× 17), config.llmProviderHint 사전(× 17), docs/sdd에서 완전한 7개로 정규화했고, hermesChatUrl이 경로 없는 호스트를 보완하며, 수동 폴백 문구가 Hermes를 명시합니다.

참고

  • 보안 변화 없음 — 새 라우트·SSRF/CSP 변경 없음; health/doctor에 HERMES_API_KEY 행이 추가됩니다.
  • 스위트: 2392개 테스트(+2).

[1.151.0] — 2026-08-12

Hermes가 이제 연결된 LLM 제공자입니다(Phase 5) — Phase 5 스코핑 스파이크에서 Nous Research의 Hermes가 OpenAI 호환 API Server(hermes gateway → POST /v1/chat/completions)를 제공함을 확인했고, 이제 career-ops-ui는 OpenAI/Qwen과 똑같이 로컬 Hermes를 통해 라이브 평가를 실행합니다. 앱 설정에서 HERMES_API_KEY를 지정하면 auto 순서에 합류합니다(마지막). 로드맵의 마지막 미해결 항목 — Phase 5, Shape A — 을 마무리합니다.

추가

  • Hermes LLM 제공자(Shape A) — 공유 runOpenAICompatible 클라이언트 위의 runHermes(server/lib/openai.mjs), 두 캐스케이드(llm-dispatch.mjs + routes/llm.mjs)에 게이트, auto 순서 꼬리 + LLM_PROVIDER=hermes 핀, /api/status/providers, llm-pricing.mjs. 사용자 설정 가능한 로컬 base URL(기본 http://127.0.0.1:8642/v1)에 Bearer 인증으로 접근 — 사용자 제공 채용 URL이 아니라 설정된 제공자 엔드포인트(OpenRouter/Qwen처럼)이므로 SSRF 가드를 거치지 않습니다.
  • #/config 필드 — HERMES_API_KEY(비밀) + HERMES_BASE_URL + HERMES_MODEL(기본 hermes-agent), 6개 새 i18n 키 × 17개 언어(스냅샷 1208 → 1214).

변경

  • 스코핑 스파이크가 해결됨: docs/integrations/HERMES.md, 앱 내 도움말 §30(× 17), README 예고(× 14), hermes-bridge 스킬, 로드맵이 “계획됨 / 아직 미연동”에서 연동됨(Shape A) 으로 전환. Shape B(맞춤형 에이전트 런타임 relay)는 불필요했습니다.

참고

  • 보안: 제공자 fetch는 설정된 엔드포인트로, 다른 OpenAI 호환 제공자와 동일한 범주 — 새 SSRF 표면 없음, CSP/새니타이저 변경 없음. HERMES_API_KEY는 SECRET_KEY(절대 노출되지 않음).
  • 테스트(CI 격리, 전송 스텁): tests/hermes-provider.test.mjs(+5); v1.146.0의 “Hermes 분기 없음” 캐너리를 연결됨을 확인하도록 반전; 제공자 표면 테스트를 7개 제공자 순서로 업데이트.
  • 스위트: 2390개 테스트(+5).

[1.150.0] — 2026-08-12

일관된 빈 상태 (Phase 4 다듬기) — 모든 “아직 없음” 패널이 이제 일부 뷰가 마법의 40px로 인라인 재선언하는 대신, 하나의 공유 .empty 스타일로 렌더링됩니다. 작은 시각적 일관성 수정; #/activity, #/cv-studio, #/stats, #/usage의 빈 상태가 이제 나머지 전부와 일치합니다(토큰화된 48px 패딩 + 점선 테두리).

변경

  • **#/activity, #/cv-studio, #/stats, #/usage**가 빈 패널의 인라인 style: { padding: '40px', textAlign: 'center', color: 'var(--foggy)' }를 제거했습니다 — 세 속성 모두 공유 .empty 클래스가 이미 제공합니다(--space-7 = 48px, 가운데 정렬, 흐린 색, 점선 테두리). 그래서 이 넷이 나머지 ~25개 .empty 패널과 동일하게 렌더링됩니다.
  • 뷰별 정당한 재정의(#/dashboard width:100%, #/pipeline border:none)는 그대로 — 순수 중복 재선언만 제거.

참고

  • 클라이언트 CSS 사용 정리뿐 — 라우트/서버/i18n 키/CSS 규칙 변경 없음(.empty 클래스 자체는 불변); 사전 스냅샷 1208. 브라우저 검증(#/usage 빈 패널이 48px 패딩 + 점선 테두리로 계산, 콘솔 오류 0).
  • 새 캐너리 tests/empty-state-consistency.test.mjs가 .empty를 유일한 진실 원천으로 유지합니다. Phase 5(Hermes 제공자)는 여전히 차단됨.
  • 스위트: 2385개 테스트(+2: tests/empty-state-consistency.test.mjs).

[1.149.0] — 2026-08-12

포털을 설정으로 이동 (Phase 4) — #/portals가 이제 Sourcing 아래가 아니라 앱 설정 옆의 Setup 내비게이션 그룹에 있습니다. v1.144.0부터 이것은 소싱 액션이 아니라 설정 화면(추적 회사 활성화/비활성화 + ATS 상태 점검)이므로, 여기가 제자리입니다. 내비게이션만 바뀌며 페이지와 라우트는 그대로입니다.

변경

  • #/portals 내비게이션 항목 → Setup 그룹 (public/index.html), 앱 설정 바로 뒤에 배치. Sourcing 그룹에서 제거(그룹은 Scan / Pipeline / Auto-pipeline / 자금 조달 기업을 유지). #/portals 라우트, 뷰, nav.portals 라벨은 그대로 — 사이드바 위치만 이동.

참고

  • 내비게이션 마크업만 — 라우트/뷰/i18n 키/서버 변경 없음. 브라우저 검증(콘솔 오류 0); tests/portals-nav-placement.test.mjs로 보호.
  • 스위트: 2383개 테스트(+2: tests/portals-nav-placement.test.mjs).

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