変更履歴
GitHubすべての注目すべき変更をリリースごとに記録しています。同じファイルがリポジトリに収録されており、17言語すべてに翻訳されています。
変更履歴
career-ops-ui の主要な変更履歴。形式は Keep a Changelog、バージョンは SemVer に準拠します。
翻訳: 🇬🇧 English · 🇪🇸 Español · 🇧🇷 Português · 🇰🇷 한국어 · 🇷🇺 Русский · 🇨🇳 简体中文 · 🇹🇼 繁體中文 · 🇫🇷 Français · 🇵🇱 Polski · 🇺🇦 Українська · 🇩🇰 Dansk · 🇸🇦 العربية · 🇩🇪 Deutsch · 🇮🇹 Italiano · 🇹🇷 Türkçe · 🇮🇳 हिन्दी
翻訳ノート — このファイルは全エントリが日本語本文で完全に翻訳済みです。コードブロック、コミットメッセージ、ファイルパス、URL、環境変数、コマンド、リンクラベルは原文のまま保持しています。
[1.236.0] — 2026-09-18
親パリティ — career-ops main @ 6a9c84c(VERSION は 1.33.0、128 コミット)。新規ソース 2 件と、ミラー修正 3 件——うち 2 件はここで求人情報を静かに失わせていました。
追加
スキャナーソース 2 件、92 → 94(89 EN + 5 RU)。 Python.org Jobs(pythonorg)はそのボードの公開フィードを読み取ります。雇用主の帰属表示は必須のため、タイトルに雇用主名が含まれない行は、ポータル側のエントリ名で埋め合わせるのではなく除外されます。Generalist World(generalist-world)はそのボードのカードをスクレイピングし、パースできないマークアップに対して大きな音を立てて失敗します——カードの形が変わると、空のボードとして読めるのではなく throw します。これはスクレイピング由来のソースを信頼できなくする、まさにその故障モードです。どちらも #/scan の Source フィルターに現れ、どちらもホストに固定されており(アンカー付き正規表現、HTTPS 限定)、どちらもフェイクトランスポートのスイートでエンドツーエンド駆動されます(43 件と 36 件のケース)。
修正
複数都市で募集している Workday の求人が、ロケーションフィルターによって除外されていました。 Workday の一覧エンドポイントは、他のすべての求人が地名を持つのに対し、複数拠点の求人には件数で応答します:"53 Locations"。これは地名ではありませんが、buildLocationFilter は大文字小文字を区別しない部分一致でマッチさせるため、Austin と 他の 52 都市で募集している求人は allow: [austin] のどのエントリにもマッチせず除外され、一方同じ求人が単独で列挙されていれば通過していました。3 テナントにわたって上流で計測したところ、291 件中 53 件の求人がプレースホルダーであり、これは例外ではなく通常のケースです。求人の実際の所在地は CXS の詳細ドキュメントにしか存在しないため、プレースホルダーごとに追加の GET リクエストが 1 回発生するようになり、1 エントリあたり上限 200 件に抑えられています——上限を超えるとプレースホルダーは偽装されずそのまま可視のままにされます。未解決の求人が解決済みの求人として読めてしまうことは、決してあってはならないからです。詳細取得が失敗するか所在地が空の場合、求人は一覧が返したままの状態に留まります:可視の "53 Locations" は解決できなかったことに正直であり、空欄にされたロケーションはすべてのフィルターを静かに通過してしまいます。同じ GET は startDate も運ぶため、拡充された求人は一覧エンドポイントの相対的な文言("Posted 30+ Days Ago" → undefined)ではなく正確に日付が付きます。素の YYYY-MM-DD でないものはそのまま残され、非 ISO のフォールバックが実装依存である Date.parse には通しません。
明記されたレベルが、2 件の求人票を 1 件の再掲載へ折り畳んでしまうことはもうありません。 "Insurance Specialist II" は、この求人票がその「兄弟」ではないという雇用主自身の意思表示です——同じ基本タイトルの下にある、異なる給与帯・職務範囲・求人番号だからです。トークナイザーはそれを見分けられませんでした: w.length > 3 は VIII までのすべてのローマ数字と一桁の数字をすべて捨ててしまうため、"Insurance Specialist" と "Insurance Specialist II" は同一にトークン化され、完璧な Jaccard 比を記録し、再掲載検出器は後者を前者の再出品として検知していました。web-ui の role-matcher.mjs はレベルの扱いをまったく持っていなかったため、今回は親のメカニズム全体が着地します:レベルはローマ数字とアラビア数字を 1 つの数値に畳んで抽出され("Nurse II" と "Nurse 2" は同一の表明です——そしてその組み合わせこそ、以前は誤って別物と判定されていました)、明記された 2 つのレベルは一致していなければならず、片方だけにレベルがある場合は緩い書き直しとして扱われます。ただし双方のタイトルが相手にはない単語を含んでいる場合は例外で、これは装飾ではなく 2 件の不一致とみなされます。
フィルターに固定されているはずの Avature ボードが、それでもグローバルボード全体を巡回していました。 Avature の検索 UI は不透明なファセットパラメータを生成します(?42386=[812132] は国別フィルターです)。オペレーターが portals.yml で絞り込んだボードを固定した場合、スキャンはそのボードを巡回することを期待します。ところが resolveSearch は URL を ${origin}${path} として再構築し、クエリ文字列をまるごと落としていました——そのため、最初のリクエストを含むすべてのリクエストがテナントのボード全体を巡回してしまい、ページ数もリクエスト数も増え、オペレーターが意図的に除外していた求人まで含まれていました。いまはファセットがページ送りのすべてのリクエストに乗り、自前の jobOffset を最後に適用するため、たまたま同名のファセットがページ送りを乗っ取ることは決してありません。
セキュリティ
devalue 5.8.1 → 5.9.2(astro 経由の間接依存)。開いていた Dependabot アラートを 1 件解消しました——不正な入力によるサービス拒否です。サイト限定の依存関係で、ランタイムのコードに変更はありません。
備考
理由付きで未移植のもの。 親プロジェクトは共有の既定 10 秒に対し、lever に 30 秒、manfred に 25 秒のタイムアウトを与えましたが、これは移植なしです:web-ui のスキャントランスポートはすでに60 秒(DEFAULT_SCAN_TIMEOUT_MS)を許容しており、両ボードとも向こうでの修正が与える余裕よりこちらの方が大きいからです。iCIMS の二重 careers- プレフィックスも、ここでは起こり得ません:web-ui はテナントのオリジンを api/careers_url から直接取得し、テナント名からホストを組み立てることは一切ないためです。その新しいフォールバックホスト巡回は、親側のより緩い設定のための機能であって、不具合修正ではありません。deutschebahn と manfred は一時的な中断時のリトライを得ましたが、web-ui の 60 秒という上限がある以上その必要性はずっと小さく、移植ではなく記録に留めました。将来ロングウォークのタイムアウトが起きたとき、最初に試すべきことが分かるようにするためです。128 コミットのデルタの残りはすべて CLI、検証器、ダッシュボード、または web/(親自身のフロントエンド)です: liveness-browser、check-jd-archive、verify-ats/verify-portals/verify-cv-facts、update-system、set-status、audit-portals、discover-ats、scaffolder、test-all、プラグインマニフェスト。
取得後に再確認したフォークの分岐点: providers/telegram-channel.mjs のキリル文字 \p{L} 修正、フォーク独自の providers/telegram.mjs、web/src/lib/clis.ts の hermes。上流の career-ops-hq/main は完全にマージ済みです(欠落コミット 0 件)。3066 → 3164 件。 ヘルプはいま全 17 バンドルで 94 アダプター(89 EN + 5 RU)と記載されており、見出し構造は 32 H2 / 122 H3 のまま変わりません。
[1.235.0] — 2026-09-16
親パリティ — career-ops main @ 68e6b94(VERSION は 1.33.0、42 コミット)。ミラー修正 2 件、うち 1 件はここで MokaHR の求人情報を静かに失わせていました。
修正
同じ MokaHR ボードの 2 件目の求人が、追加されるはずが消えていました。 url-key.mjs は求人 URL を比較用キーへ縮約しますが、フラグメントを無条件に切り捨てていました —— ほとんどのフラグメントは表示専用に過ぎない(#apply、#section-2)ので、それ自体は妥当な判断です。MokaHR はこのレジストリ内での例外で、その求人 URL は …/social-recruitment/{tenant}/{id}#/job/{n} の形をしており、同じテナントの求人はすべて同じパスを共有し、求人 ID はフラグメントの中にしか存在しません。そのため、すべてが同一のキーへ正規化されていました。修正前に計測すると、異なる 2 件の MokaHR 求人をパイプラインに投入しても結果は1 行——エラーもなければ警告もなく、気づく手がかりが一切ありません。スキャナーの鮮度フィルター(en-scanner.mjs)も同じキーを参照するため、そのテナントの求人が一度スキャン履歴に入ると、残り全部が「既知」として捨てられていました。normalizeUrl はいま、フラグメントを落とす前に、認識できる #/job/{id} または #/jobs/{id} というルートを比較専用のクエリキーへ昇格させます —— MokaHR は既存の mokahr_job_id という名前をそのまま使うので過去に書き込まれたキーとの比較可能性を保ち、それ以外のホストには内部用の _career_ops_fragment_job_id が割り当てられます。昇格させた ID は追加であって上書きではありません:すでにそのパラメータを持つ URL は別の求人の ID を保持しており、それを上書きすればキーが分離しようとしている当の 2 つを、まさに統合してしまうからです。認識されないフラグメントは引き続き切り捨てられ、これはすでに正しかった半分です。
accept-encoding はすべてのソースリクエストで固定されるようになりました。 未設定のままだと、Node は自身の undici ビルドが提供するものをそのまま交渉します。Node 23 以降ではそこに zstd が含まれ、amazon.jobs の zstd レスポンスはちょうど 1024 バイトで、しかもステータス 200 のまま切り詰められて返ってきます —— そのため障害は Unterminated string in JSON at position 1024 という、トランスポートの不具合というよりは壊れた API であるかのような形で現れていました。すべてのリクエストはいま gzip, deflate, br を送信します。私たちのランタイム上ではこれは修復ではなく予防です:CI マトリクスとサーバー(22.22.1)である Node 18/20/22 は gzip, deflate を送信しており(推測ではなく直接確認済み)、ここでは何も切り詰められていませんでした。今回変わるのは br を提示するようになった点で、これらのバージョンは既定では要求しないものの正しくデコードできます。この既定値は大文字小文字を区別せずに適用され、これがレビューに値する部分です:ヘッダー名は大文字小文字を区別せず、fetch は同名の 2 エントリを 1 つのカンマ区切り値へ結合するため、呼び出し側が Accept-Encoding: identity を渡した場合、そうしなければ gzip, deflate, br, identity が送られてしまっていたはずです。
備考
fetchImpl の境界では、ヘッダーはプレーンオブジェクトのままです。親プロジェクトはそこで fetch に Headers インスタンスを渡しています。これは見落としではなく意図的な分岐です:web-ui のフェッチヘルパーは注入可能な fetchImpl を受け取り、45 個のソーススイートが捕捉した内容から opts.headers['User-Agent'] を読み取っています —— Headers インスタンスにはブラケットアクセスがないため、移植すべきは実装ではなく挙動(大文字小文字を区別しない上書き、呼び出し側が勝つ)の方だからです。呼び出し側のオブジェクトはコピーされるだけで決して変更されません。複数のソースが 1 つのヘッダー定数を共有しているためです。親の対になる修正 —— 既定のユーザーエージェントを大文字小文字を区別せず適用する件 —— は移植なしです:web-ui は既定の UA を一切注入しておらず、各ソースがそのボードが要求するものを個別に設定するため、呼び出し側の User-Agent と衝突しうる既定値がそもそも存在しません。
新規スイート 2 本と新規ケース 3 件がこの組を固定します: tests/http-accept-encoding.test.mjs(固定された値そのもの、4 つのフェッチヘルパーそれぞれ、そして 3 通りの大文字小文字表記での呼び出し側による上書き —— 単一の accept-encoding キーだけが残ることをアサートしており、これはマージのバグがまさに 2 つ生み出していたものです)と tests/parsers-pipeline-hash-route.test.mjs で、こちらはキーではなく利用者から見える継ぎ目そのものを検証します:2 件の MokaHR 求人が両方ともパイプラインへ到達すること、同じ求人を 4 通りの綴りで貼り直しても 1 行に収束すること、装飾的なフラグメントが ID になってしまわないこと。すべて移植前に失敗することを確認済みです。 このピン留めは、これらのヘルパーだけでなくトランスポートにあります。92 のソースのうち 25 は注入された fetchImpl を直接呼び、ヘルパーを一切通りません。そのため、両スキャナーが実際に注入するラッパーである makeTimeoutFetch が適用します — すべてのソースが共有する唯一の通過点です。(親に相当物は不要です。providers/_http.mjs が唯一のトランスポートだからです。)新しいソースが渡されたトランスポートではなく fetch を直接呼ぶと構造ガードが CI を落とし、振る舞いのケースでは 25 のうちの 1 つである greenhouse をラッパー経由で動かし、ヘッダーが実際に送出されることを確かめます。
理由付きで未移植: liveness-browser.mjs(ヘッド付き Playwright の再利用ガード —— web-ui はヘッド付きブラウザを操作しません)、check-jd-archive.mjs(web-ui からの中継なし)、test-all.mjs(親のハーネス。フォーク独自の vpFixtureEnv の分岐はそのまま)、web/(親自身のフロントエンドでありミラーしません)、プラグインマニフェストと SIGNATURES.md(リポジトリ限定)。batch/batch-runner.sh(sed への補間前にアンパサンドをエスケープ)と jd-skill-gap.mjs(cv.md をデータルートに対して解決)は読み取り専用で中継されています —— web-ui は親のコピーへシェルアウトするだけなので、どちらもここではコード変更なしで着地しますが、jd-skill-gap の方は /api/jds/:id/skill-gap の中継をより正確にします: cv.md はいま、プロセスがどのディレクトリで起動されたかに関わらず、データルートから解決されます。取得後に再確認したフォークの分岐点: providers/telegram-channel.mjs のキリル文字 \p{L} 修正、フォーク独自の providers/telegram.mjs、web/src/lib/clis.ts の hermes。ソース数は 92(EN 87 + RU 5)のまま変わらず、ヘルプも無変更です。3042 → 3066 件。
[1.234.0] — 2026-09-15
親パリティ — career-ops main @ 56cce8f(VERSION はまだ 1.32.0、1.32.0 から 58 コミット先)。15 ソースにまたがるミラー修正 1 件、加えて再発を防ぐガード。
修正
不正な求人 ID がひとつあるだけで、ページ全体が空になることはもうありません。 encodeURIComponent は独りぼっちの UTF-16 サロゲートに対して URIError を投げ、JSON ペイロードはそれを運び得ます — JSON.parse('"\\uD800"') はそれを保持します。15 個のソースは各求人の URL を、パースループの内側でホスト管理下の id またはスラッグから組み立てていたため、不正な求人ひとつがループを中断させ、そのページの求人がすべて失われていました。企業ごとの catch に報告すべき内容が届く前に例外が巻き戻るため、ソース障害として記録されることすらありませんでした。親プロジェクトはこれを providers/_safe-url.mjs で修正し(safeEncodeURIComponent は URIError で null を返し、それ以外は再送出)、各ループの id をそこへ通して null のときはその 1 件だけを落とすようにしました。そのヘルパーはいま server/lib/sources/_safe-url.mjs としてあり、同じ変更がミラーされた 13 ソース — alibaba、arbeitsagentur、bamboohr、feishu-jobs、garena、jibeapply、manfred、meituan、mokahr、phenom、thehub、tkms、vdab — に加え、新しいガードが初回実行で洗い出した web-ui 専用の 2 ソース、jobstreet と trudvsemにも適用されました。どちらも同じループの形と同じ潜在的な throw を持っていました。範囲は意図的なもので、ヘルパーを通すのは API 由来の id だけです。設定由来の値は厳格なエンコードのままとし、失敗時ははっきりと壊れます —— garena の office(portals.yml のセグメント)は throw する urlSegment のままにし、その API の id は null を返す idUrlSegment を得ます。csod の corpName と 4dayweek の SLUG_RE で検証済みのスラッグは、理由付きで名前によって許可リストに入っています。
rheinmetall のタイトル・フォールバックは、不正な href で throw し得ました。 カードに見出しが無いとき、タイトルは decodeURIComponent を介して URL のスラッグから再構成されますが、これはスクレイピングした href 中の不正なパーセントエンコード列に対して throw します — デコード側での、同じ「1 行の不正がページ全体を殺す」形です。いまは防御的にデコードし、失敗時は生のスラッグへフォールバックします。
ソースレジストリは _ 始まりのファイルを飛ばすようになりました。 自動探索は server/lib/sources/ 内のすべての .mjs を自身を除いてインポートし、export const meta を持たないものには警告していました —— そのためこのヘルパーは起動時警告を生んでいました。親プロジェクトの慣行を採用しました: 先頭のアンダースコアはソースではないことを意味し、そうしたファイルはインポートも警告もされません。meta を欠く未宣言のファイルは引き続き警告され、探索テストはいまや両半分をアサートします(旧フィルターに対して赤になることを確認済み)。 サイトビルドの sync-assets.mjs もディスク上のアダプターファイルを同じ規則で数えるため、ヘルパーが無言ドロップ検出ガードに引っかかることはなくなりました。
備考
tests/sources-url-encoding-surrogate.test.mjs は親のスイートを 3 部構成でミラーします: ヘルパーの契約(サロゲートペア —— 普通の絵文字 —— は引き続きエンコードされ、値自身の toString からの throw は呼び出し側のバグとして伝播する)、15 ソース全部の個別挙動(不正な id 1 件 + 正常な id 1 件 → throw なし、正常は残り不正は落ちる、それぞれをソースがエクスポートする純粋なパーサー経由で、thehub についてはフェイクのトランスポートを使うフェッチャー経由で駆動)、そしてソース単位のガード: 変換済みの全ファイルはヘルパーを import し、かつ 呼び出さなければならず、import せずに参照するファイルは許されず、未レビューのファイルが素の encodeURIComponent で求人 URL を組み立てることも許されません —— 旧来の形をした新規ソースは、問題の行番号つきで CI が落とします。15 ソース分のケースはすべて、移植前に失敗することを確認済みです。
親デルタの残りは理由つきで未移植です: batch/batch-runner.sh --cli は CLI 専用フラグ(web-ui はバッチランナーへシェルアウトしません); invite-match.mjs はいま set-status.mjs をコードルートから解決しますが、web-ui はそれを中継していません; company-funded.mjs は内部で同じ safe-url の変更を得ましたが、/api/company-funded がパースするその --json 出力形状は変わっていません; test-all.mjs はタイムアウト予算を動かしただけです(フォーク固有の vpFixtureEnv の分岐はそのまま); web/ は親自身のフロントエンドでありミラーしません; AGENTS.md/SIGNATURES.md はリポジトリ限定です。取得後に再確認したフォークの分岐点: providers/telegram-channel.mjs のキリル文字 \p{L} 修正、フォーク独自の providers/telegram.mjs、web/src/lib/clis.ts の hermes。レジストリは変更なし: 92 ソース(EN 87 + RU 5)。ヘルプも無変更です。3022 → 3042 件。
[1.233.2] — 2026-09-10
修正 — 重大度「高」の CodeQL js/remote-property-injection 2 件を、既にあった不変条件を主張ではなく構造にすることで解消しました。
修正
POST /api/config は再起動なしで効くように保存値を process.env へ写しますが、そのループは要求本文から組み立てた Object.entries(safe) を回り、内部に __proto__ / constructor / prototype への明示的なガードを置いていました。コード自体は安全です——safe は 20 行上で KNOWN_KEYS を辿って該当するものだけを写して作られるため、プロパティ名が利用者の手に落ちることはありません。しかしループ単体では要求本文から作ったオブジェクト由来の名前を持つプロパティへ代入すると読め、CodeQL は 2 つのループにまたがる制約を追えません。いまは定数配列を直接回して safe から値を引くので、プロパティ名はモジュール階層のリテラルであることが証明できます。プロトタイプ・ガードも一緒に消えました——大文字スネークの固定リストにそれが現れることはなく、起こり得ない場合への実行時チェックは雑音だからです。挙動は同一で、警告の日付が 9 月 9 日なのは v1.232/v1.233 が行番号を動かしたためにすぎません。
備考
2 件を誤検知として却下する道もあり、本リポジトリにはその慣行もあります。ここでは修正を選びました——コメント内の主張は、解析器と次の読み手が 20 行の制約を追わずに検証できる「形」より値打ちが低いからです。
旧ガードを固定していた検査は、k === '__proto__' || … という字面をソースから grep するもので、文字列の存在を示すだけで маршрут の安全は示しません。より強い 2 つに置き換えました: process.env[k] へ到達する経路が KNOWN_KEYS の反復であること(かつ要求由来のオブジェクトを回していないこと)を見る構造検査——旧形で赤くなることを確認済み——と、config-endpoint.test.mjs の挙動検査で、__proto__・constructor・prototype・未登録名を生の JSONとして送ります。オブジェクトリテラルでは駄目で、ソース中の { __proto__: … } はプロパティを作らずプロトタイプを設定するため、JSON.stringify は {} を送り、検査は何も証明しなかったでしょう。3021 → 3022 件。
[1.233.1] — 2026-09-10
修正 — v1.233.0 が追加した操作要素が案内に一切書かれておらず、同じ節には 200 リリース前に正しさを失った指示が残っていました。
修正
DOC-1 — 追加された操作要素がどこにも説明されていませんでした。 各ドロップダウンは「既定値を使う(…)」で始まり、これまで UI に表しようのなかった状態、すなわちキーが未設定であることを表します。下の一覧から同じ値を選ぶのとの違いは目に見えず、しかも重大です——「既定値を使う(llama3.2)」は何も書かず、プロジェクトが既定値を動かせば新しい値が届き続けます。一方 llama3.2 を明示的に選ぶと OLLAMA_MODEL=llama3.2 が書かれ、次に既定値が変わったとき黙って古い値に取り残されます。CONFIG-2 と CONFIG-3 が扱ったのはまさにこの静かなずれで、仕組みはコードで直したのに、それを読む場所がありませんでした。既存の ### Behavior に 3 項目を 17 言語すべてで追加しています。見出しゲートには捉えられません——数えるのは H2/H3 であって、網羅は数ではないからです。
DOC-2 — 表の一行がキーを名乗らず、唯一の具体的な指示も誤っていました。 Key 欄には (server uses default UA) という括弧書きの注記が入り、v1.19.0 が KNOWN_KEYS と UI から削除した HH_USER_AGENT を説明していました。さらに dev.hh.ru でアプリを登録し UA 文字列を使えと案内していましたが、v1.65.0 で hh.ru アダプターが自前のブラウザー UA で公開検索サイトを読む方式に変わって以来、それは何の役にも立ちません。同様に古い記述があと 2 か所——v1.28.1 で削除された health 行を説明する「任意チェック」の項目と、dev.hh.ru の助言を繰り返すトラブルシューティング行。3 か所とも 17 言語すべてから除きました。トラブルシューティング行は削除ではなく書き換えです。半分は今も正しいからで、2026 年 7 月以降 hh.ru はロシア国外の IP に HTTP 451 を返すため、ロシアの家庭用 IP は実際に必要です。誤っていたのは登録の手順と、存在しないキーへの言及でした。
キーの表が、自分が何であるかを述べるようになりました。 ### Recognized keys は 45 個のうち 6 個を挙げるだけで、見出しは登録簿を約束しています。プロバイダー系列は同じ節の本文で扱われているので、抜粋であること、および完全な一覧はグループ分けされて #/config にあることを一文で 17 言語に明記しました。キーは増やしていません——表を膨らませるのではなく、曖昧さを解消しています。
備考
tests/help-recognized-keys.test.mjs が、欠けていた門番です。実際にキーを列挙している表の第 1 列を生きた KNOWN_KEYS と突き合わせ、dev.hh.ru に触れる言語があれば落ち、「既定値を使う」の説明を欠く言語があっても落ちます。範囲の絞り込みが肝心で、書き出しの表の PDF のように第 1 列がバッククォート付き大文字語である別の表もあるため、自分の行の少なくとも一つが本物のキーを名乗る表だけを対象にします。修正前の言語ファイルで 3 件とも赤になることを確認済みです。3018 → 3021 件。すべて既存の見出しの内側に収めたので、ヘルプは 17 言語すべてで 32 H2 / 122 H3 のままです。
[1.233.0] — 2026-09-10
修正 — #/config の保存経路で 3 件。CONFIG-3 は v1.232.1 の修正そのものが招いたものです。
修正
CONFIG-3 — 仕込まれた値とたまたま一致した意図的な選択が「触れていない」として捨てられていました。 v1.232.1 の私のコメントは「表示用の既定値と、同じ値をあえて選んだ利用者とを見分けられる」と述べていましたが、実際には見分けていません。initial は 2 つの由来を混同します: .env からの仕込み(一致は未編集を意味し、飛ばすのが正しい)と defaultValue からの仕込み(一致は曖昧——開いていないか、開いて同意したか)。HOST が無いときコントロールは 127.0.0.1 を表示しますが、まさにその値を打って保存すると {} が送られ、キーは現れません。固定する唯一の道は誤った値を入れて保存し、正しい値に戻すこと——誰も見つけない袋小路でした。initial の隣に seededFromFile を記録して解消。ファイル由来なら従来どおり、既定値由来なら dirty が有効な第二の手掛かりになります。
キーの削除が「· 0」と表示されていました。 サーバーは書いたものしか返さないため、削除には数えるものが無く、実際には削除されているのに「何も起きなかった」と読めました。POST /api/config は removed も返すようになり、トーストは両方を数えます。
セレクトは設定できても解除できませんでした。 18 個のどれにも空の選択肢が無く、フォームは '' を送れませんでした。いまは各セレクトが**「既定値を使う(…)」**で始まり、未設定のキーは既定値そのものではなくその項目を選びます。v1.57.1 の意図は残しつつ、選択のふりをやめて明示するようにしました。select-remote は replaceChildren が項目を消すため再生成しています。i18n キー 1 件 ×17。
備考
この機会に確認し、あえてそのままにしたもの: lang は前方一致規則ではなくリテラルの許可リスト(langx・lang_evil・LANG はいずれも 400)。facts.json は設計どおりデプロイ先に無い——22 ファイルがビルド時に取り込み、値は HTML に埋め込まれます。拡張子なしのアップロードをテキスト扱いするのは文書化された意図的な仕様で、書き込みも実行もせず、.exe/.zip は明示的に拒否されます。
ユニットの基準値は 3018 のまま。ゲートはブラウザー側で、playwright-config-save-scope.mjs が 6 件から 11 件へ(ブラウザー計 111 → 116)。新しい 5 件はすべて旧コードで失敗することを先に確認しました。うち 1 件は CONFIG-2 の再発防止専用です。
[1.232.1] — 2026-09-10
修正 — #/config の保存が、利用者の触れていない項目まで書き込んでいました。
修正
CONFIG-2. v1.57.1 は未設定のコントロールに defaultValue を仕込み、サーバーが実際に使う値を項目に表示させます(これは正しく、そのままです)。一方 save() は dirty を秘密項目にしか問い合わせず、それ以外はすべて毎回送っていました。つまり表示のために置かれた値が、利用者の選択として送られていたのです。設定済みキーが 2 つのフィクスチャで測ると、ドロップダウンを 1 つ変えるだけで 28 キーが送られ、誰も開いていない 18 キーが固定されました。何も変えずに保存しても 27 キーです。当日の挙動は変わりません——書かれた値は既定値そのものだからです。変わるのは各設定の状態で、「未設定、プロジェクトの既定に従う」から「.env に固定」へ移ります。curated な一覧はリリース間で動くため、次に既定を変えたとき、一度でも保存した人には黙って届かなくなります。
備考
見どころは比較の基準です。 読み込んだ cfg.values との比較——最初に試した明白な修正——では足りません。未設定の select は defaultValue を表示し、保存値は '' なので、両者は正当に食い違い、17 個のモデル選択はやはり書かれます。送信本文は 27 キーから 19 キーに減っただけで、1 にはなりませんでした。いまは各コントロールが何で初期化されたかと比べます。これだけが「触れていない」を、表示のために置かれた値と、同じ値をあえて選んだ利用者とに分けられる表現です。dirty も基準になりません——値を入力して元に戻した場合にも立つからです。
あわせて修正: トーストのキー数は t() のフォールバック引数の中にあり、config.saved は 17 言語すべてに存在するため、数字は一度も描画されていませんでした。12 キーの保存が 1 キーの保存と同じ見た目だったわけです。
他は動かしていません。v1.57.1 の事前入力はそのまま、秘密項目は触れたときだけ送られ、v1.232.0 のサーバー側の猶予はフォームを通らない直接 API 呼び出しの守りとして残ります。修正はクライアント側なのでテスト基準値は 3018 のまま。門番は tests/playwright-config-save-scope.mjs の 6 件で、test:e2e:browser に登録済み(105 → 111)、6 件すべて旧コードで失敗することを先に確認しています。
[1.232.0] — 2026-09-09
修正 — ブラウザー QA で見つかった 2 件。いずれも v1.231.5 由来ではありません。
修正
PROFILE-1 — #/profile がページ全体を横スクロールさせていました。 値はインラインのフォント指定しか持たない無名の <div> で、算出後の overflow-wrap は normal のままでした。LinkedIn の URL は CSS に正当な改行点を与えないため、要素の min-content 幅は文字列全体になります。グリッド項目は既定で min-width: auto を持ち、それ未満には縮まないので、カードはトラックを、続いて行・主カラム・文書を押し広げました(1280 px で実測 23 px の横スクロール)。共有クラス .card-value に overflow-wrap: anywhere を与え、.card-row > * { min-width: 0 } を控えに置いて解消。break-word ではなく anywhere です——min-content の算出に数えられるのは anywhere だけで、break-word では折り返しても列幅は変わりません。
CONFIG-1 — POST /api/config が select 項目にどんな値でも通していました。 {"LLM_PROVIDER":"not-a-provider"} が 200 を返し、そのまま .env に書かれていました。以後も何も失敗しません——リゾルバーが黙ってフォールバックし、利用者が選んだ設定は効かないまま健全に見えます。その 19 個の値そのものを持つ LLM_PROVIDERS は、ずっと env-config.mjs から公開されていました。検証側が参照していなかっただけです。
備考
検証するのは LLM_PROVIDER だけで、これは報告に対する意図的な絞り込みです。 モデル一覧の実際のドメインはベンダーのカタログにあり、当方のリリースとは無関係に動きます。curated な一覧を強制すれば、提供側が既に配信しているモデルを選べなくなります——実際、本リポジトリの検査自体が curated から外れた gemini-2.0-flash を設定しています。害の性質も同じ方向を示します: 誤ったモデルは呼び出し時に大きな音を立てて失敗し、黙って失敗するのは провайдер名だけです。
既に保存済みの値は大目に見ます。厳格版が使い物にならないことをブラウザー検査が示したからです。 public/js/views/config.js は保存のたびに非秘密の項目をすべて送り直すため、既存の不正値を拒むとページ全体が凍りつき、触れてもいない項目を直すまで何も変更できなくなります。いまは変更のみを拒みます。
あわせて解消: OLLAMA_API_KEY は API 経由では書けるのに #/config に項目がなく、認証プロキシ配下の Ollama を使う人はアプリから設定できませんでした。ディスクリプターを追加(秘密扱い、i18n キー 2 件 ×17)し、サーバーの 45 キーと UI の 45 記述子が一致し続けることを検査で保証します。3013 → 3018 件。
[1.231.5] — 2026-09-09
セキュリティ — 7 件の勧告を解消。うち 1 件は重大な遠隔コード実行です。 ソースファイルの変更はありません。
セキュリティ
Dependabot から取り込み: multer 2.2.0 → 2.3.0(高 — 中断時のファイル記述子リークによるサービス拒否)と、サイトの svgo 4.0.2 → 4.1.0(高および中 — removeScripts が名前空間と内容の扱いを通じて実行可能なリンクを許し、foreignObject 内の実行可能 HTML の無害化も不完全でした)。
続いて、その取り込みが引き起こした再スキャンでさらに 5 件。 影響範囲はいずれも既存の ^ 指定で満たされていたため、マニフェストの編集ではなく更新で足りました: サイトの astro 7.1.0 → 7.3.2 ——重大、AVIF 画像最適化を経由した遠隔コード実行(7.2.8 で修正)、あわせて設定 base を除去する際のパスセグメント境界チェック欠如による認可バイパス(7.2.4);sharp 0.35.3 → 0.35.4 ——高、libheif の脆弱性 2 件;両方のマニフェストで js-yaml 4.3.1 → 4.3.2 ——高、maxTotalMergeKeys が空のマージ元に対して CPU 使用を制限しませんでした。
備考
ここでの「依存関係のみ」は「無リスク」と同義ではないので、監査結果だけを根拠には出していません。js-yaml はアプリが読むすべての設定を解析し、multer はすべてのアップロードを受け取り、Astro はマイナーバージョンを跨ぎました。確認済み: npm run test:ci 3013/3013、ブラウザー検査 4/4、そしてサイトの完全再ビルドで同じ 86 ページ・17 すべてのチェンジログ・ミラー・正しい facts.json。npm audit は両マニフェストで脆弱性 0 件。ソースが変わっていないため、テスト基準値も据え置きです。
[1.231.4] — 2026-09-07
修正 — v1.231.3 は Doctor クリックの「後始末」を直しただけで、その最中は直していませんでした。モバイルとデスクトップの双方から再度の報告。
修正
withSpinner はリクエスト実行中も依然としてボタンを書き換えていました。 v1.231.3 で子ノードを控えて finally で戻すようにし、確かに戻ってはいました——しかし実行中の合図は相変わらず button.replaceChildren(document.createTextNode('⏳ ' + original)) を実行していたのです。そのため doctor.mjs の実行中ずっと、36 px の正方形は ⏳ 🩺Doctor というテキストを抱えた幅広のピルとなり、行を崩していました。デスクトップでは ⏳🩺Doctor と詰まって見えました。子を戻すことは後始末を直しただけで、その瞬間を直してはおらず、終了状態だけを見る検査はこれを通してしまいます。子要素を持つボタンは、もう一切書き換えません。 合図は .is-loading クラスが担い、これがスタイルシート側でアイコンを砂時計に差し替えるので、寸法は変わりようがありません: 320 px において待機 36 px、実行中 36 px、終了後 36 px。テキストのみのボタン——24 か所の呼び出しのうち 23——は従来どおり砂時計を前置します。
備考
教訓はコードではなく検査の側にあります。v1.231.3 のゲートは Doctor をクリックし、aria-busy が消えるのを待ってから測っていました——構造上、finally が既に修復した状態しか見えません。新しいブラウザー検査は自らプロミスを開いたまま保持し、実リクエストと競走せずに飛行中の値を採り、幅が変わらないこと・両方の <span> が残ること・はみ出しがないことを要求します。最初の草案は実クリック後に採り、リクエストが先に終わると検査を飛ばしていました——それは運で通る錠前です。3012 → 3013 件。 新しい 2 つの検査はいずれも、v1.231.3 のコードに対して失敗することを確認済みです。
[1.231.3] — 2026-09-07
修正 — Doctor をタップするとモバイルの上部バーが崩れていました。v1.231.2 公開当日に実機から報告。
修正
UI.withSpinner が、自分の装飾している当のボタンの子要素を潰していました。 実行中の合図を textContent 経由で出していたのです——button.textContent を保存し、'⏳ ' + original を書き、また書き戻す。textContent への代入はすべての子を 1 つのテキストノードで置き換えます。そして v1.231.2 は、まさに上部バーの各操作を .btn-ico と .btn-label に分割し、ラベルを max-width: 900px で隠したばかりでした。そのため Doctor への最初のタップが両方の <span> を永久に破壊し、モバイル用の規則は掛かる相手を失い、ラベルは素のテキストとして戻り、36 px の正方形はテーマ切り替えに覆いかぶさる 🩺Doctor のピルへ膨らみました。自然には直らず、再読み込みだけがマークアップを戻せました。修正では子ノードを控え、replaceChildren で復元します。テキストだけのボタン——24 か所の呼び出しの大半——は従来とまったく同じ挙動です。
- FIND-3 — 320 px の端末で検索欄が 8 px まで潰れていました。 上部バーを 1 行にまとめた結果、searchbar が唯一の伸縮要素として残り、前回のチェンジログ自身がこう書いていました——「何もなくなるまで縮んでよい」。実際に何もなくなりました: 320 px で入力欄 8 px、340 px で 28 px——21 文字のプレースホルダーのうち 1 文字です。クリックも入力もできますが、もはや欄として読めません。
min-widthでは解決できません。320 px のバーは余白 32 px、メニュー 40 px、操作群 162 px を抱えており、120 px を割く余地がないからです。そこで 420 px 未満——入力欄が自力で 100 px を超える幅——では虫眼鏡の後ろに隠れ、タップでバー全体へ展開します: 320 px で 182 px、360 px で 222 px、390 px で 252 px、どの幅でもはみ出しはありません。開閉ボタンはアクセシブル名を 1 つに保ち、状態はaria-expandedで伝えます。🔍/✕ の切り替えは純粋な CSS です——このボタンに JS は書き込みません。同じリリースで Doctor を壊したのが、まさにその書き込みでした。420 px を超えると何も変わりません。実ビルドに対するブラウザー回帰で発見。
備考
これは v1.231.2 で書き留めた applyI18n() の罠と同じ欠陥種別——子要素を持つようになった要素に textContent を代入するコード——を、別の呼び出し元から踏んだものです。教訓は一般化します: 要素の中身を子 <span> に分割した時点で、その要素に対する既存の textContent 書き込みはすべて潜在的な不具合になります。しかもその書き手は、いま編集しているファイルにいるとは限りません。
二重に固定しました。node:vm 上で本物の withSpinner を偽ボタンに対して動かし、320 px の実ブラウザーで Doctor をクリックします。いずれも旧コードでは失敗することを確認済みです。3009 → 3012 件(ブラウザーの 1 件はオプトインで test:ci には含みません)。
[1.231.2] — 2026-09-06
修正 — モバイルの上部バーが 2 行に折り返していた件、サーバーが古い親で動いていた件、そして cvstart.ru のリダイレクトが既に言語を含むパスすべてで 404 になっていた件。
修正
- モバイルの上部バーが再び 1 行になり、操作はアイコンのみになりました。 操作ボタンは全幅の 2 行目に強制されており、スマートフォンでは
[☰ · 検索]の下に[🔔 🌙 Doctor Scan を開く]という幅広のピル 2 つが独立した行を占めていました。現在はベルとテーマ切り替えの隣に 36 px の正方形(🩺 / ⚡)が並び、320 px でもすべてが 1 行に収まります。目視ではなく実測です: ドキュメント幅は 320・360・390・430 px、および以前ボタンが検索欄に重なっていた 560〜760 px の帯全体でビューポートと一致します。ロケールはモバイルのレイアウトにもはや一切影響しません — ラベルは非表示で、ボタンは固定サイズの正方形だからです。900 px を超えるとラベルはそのまま戻ります。アクセシブル名はaria-labelから取ります。display:noneのラベルは名前の計算から外れるためです。 - リリースは親 1.32.0 を前提に作られたのに、サーバーは親 1.31.0 で動いていました。 resumecraft.ru の
/api/healthがparentVersion: 1.31.0を返していました。ランタイム 95 ファイルを同期し、2 ファイルを削除しました。本番面に対するブラウザー QA が発見したものです。 cvstart.ruはパスの中身を見ずに/ru/を前置していました。/ru/helpは/ru/ru/helpに、/en/helpは/ru/en/helpになり、どちらも 404 でした。前者は本サイトから URL をコピーすると起きます。後者はより深刻で、英語話者が英語版にたどり着く手段を完全に塞いでいました。今は 16 個のロケール接頭辞のいずれかを既に含むパスはそのまま通り、enは取り除かれ(cvstart.org は英語をルートで配信し/en/ディレクトリを持ちません)、それ以外は従来どおり/ru/を受け取ります。ロケール判定はアンカー付きなので、/enterprise/がenと誤認されることはありません。
変更
- 旧版の QA プロンプト 98 件を
qa/archive/superseded-prompts/へ移動しました。qa/README.mdは v1.137.0 以降「最上位に残すのは現行リリースのプロンプトだけ」と定めていましたが、この規則は 95 リリースにわたり適用されていませんでした。 - さらに 5 つのプロンプトが最上位から外れました — いずれも自らを最新と称していましたが、そうではありませんでした。
REGRESSION-FINAL.mdは「唯一の権威ある、バージョン非依存の回帰プロンプト」を名乗っていましたが、その §§11–15 は閉じた 5 つのサイクル台帳(v1.55.x → v1.59.7)であり、掲げるヘルプ基準は 16 ロケールで 28 H2 / 103 H3 ——今日の 32 / 122 と 17 に対して。これに従う QA エージェントは §9 の最初の一歩で落ちていたはずです。QA-REGRESSION-PROMPT.mdはバージョンなしの名前に v1.226.0 の中身でした。QA-REGRESSION-PROMPT-v1.76.0-FULL.mdは 155 リリース前に固定され、REGRESSION-PROMPT-FINAL.mdは v1.58.52 → v1.59.10 のサイクルを閉じるもの、QA-FULL-REGRESSION.mdはそれらすべてを v1.131.1 基準で重複させていました。PARENT-SYNC-WORKLIST-v1.26.0.md(親はすでに 1.32.0)も同行し、日付入りのUX-AUDIT-2026-07-06.mdはreports/へ移りました。残るドライバーは 4 つ、その基準値は v1.231.2 の実測に更新済みです。qa/README.mdは実在するフォルダーを説明するよう書き直しました。
備考
- 上部バーの修正で踏んだ詳細度の罠 2 件を
app.cssにコメントしました: デスクトップ用の規則はmax-width: 900pxブロックより下で宣言されているため、詳細度が同じなら後勝ちになります。モバイルのgapは黙って 24 px のままで(これが 320 px をあふれさせていました)、アイコンは非表示のまま(ボタンが空の正方形になっていました)でした。 applyI18n()はel.textContentに代入するため、i18n キーは内側の<span>へ移しました。ボタンに置いたままでは言語切り替えのたびにアイコンが消えます。- 親の最初の rsync は、ローカルにもう存在しないファイルで転送の途中で中断し、それでも成功を報告しました。親のデプロイは終了コードではなくチェックサムで検証します。
[1.231.1] — 2026-09-06
修正 — v1.231.0 のカウント一括置換が、すべてのリンクのアカウント名を書き換えていました。
修正
- すべての
Fighter90リンクがFighter92になっていました。 v1.231.0 のソース数の一括置換は、数字境界の保護のもとで90を92に置き換えます。この保護は290や901を正しく除外しますが、Fighter90の中の90には一致します。直前が英字で、直後はトークンが終わるだけだからです。17 の README すべて、17 のヘルプ一式すべて、サイトのヘルプミラーにわたる 562 のリンクと、ウィキの 138 件が存在しないアカウントを指しており、providers.pngのバナーと「GitHub で編集」のリンクはすべて 404 でした。画像が表示されないことに気づいた利用者からの報告です。 - 過去のリリース参照 5 件がずれました。 同じリリースの
85 → 87置換がv1.85.0をv1.87.0に変え、存在しないリリースを作り出していました。
備考
- 数字境界だけでは足りません。隣接する数字からは守りますが、識別子やバージョン文字列の一部であることからは守りません。カウント自体は正しく、変わっていません: 92 ソース、EN アダプター 87、テスト 3009。
- 親リポジトリは無傷です。カウントの一括置換を行っていません。
[1.231.0] — 2026-09-05
追加 — 親 career-ops v1.32.0 からのスキャンソース 2 件(Collage、Telegram 厳格)、Gem の REST モード、そして移植が親自身のコードに見つけた欠陥 1 件。
追加
- 新しいソース 2 件、90 → 92(EN 87 + RU 5)、
ALL_ADAPTERS85 → 87。どちらもトークン不要。- Collage(
provider: collage)— Collage HR の公開求人サイト API。パスの区間はテナント自身が選んだサイトのアドレスであって会社名から作った slug ではないため、api:かsecure.collage.co/jobs/<アドレス>の careers URL から読み取り、決して推測しません。推測したアドレスは他人のボードを走査することになります。 - Telegram(厳格)(
provider: telegram-channel)— 既存のtelegramソースと同じt.me/s/<チャンネル>のプレビューを読みますが、取引は正反対です。投稿が行になるのは雇用主を名指しし、かつ求人ページへリンクしているときだけ。ダイジェスト、雇用主を伏せた投稿、連絡先のみの投稿は落とします。網羅率が低いのは設計どおりで、上流の計測では HR 運営チャンネルの 32〜77% の投稿が通過します。そのかわり、どの行も実在の雇用主と実在のリンクを持ちます。親と同じく両方を提供します。
- Collage(
- Gem に任意の REST モードが加わりました(GraphQL 経路と併存、
api: https://api.gem.com/job_board/v0/<board>/job_posts)。ボード ID から導出することは決してなく、GraphQL 経路が既定のままです。親 #3783 から移植。
修正
telegram-channelの所在地フィルターはキリル文字を認識できず、都市名を雇用主として返していました。 親のLOCATIONISH_REは ASCII 限定の\bを使っており、キリル文字の隣では決して発火しません。そのため中のロシア語の候補(москва、спб、офис、удалёнк*)はすべて到達不能で、Senior Engineer | Москваは**「Москва」を雇用主として**返していました。これは帰属ポリシー全体が防ごうとしている、まさにその「誤ったフィールドを事実として書く」動作であり、しかもこのプロバイダーの主要な利用者層を直撃します。ロシア語チャンネルは所在地をキリル文字で書くからです。web-ui の移植では Unicode の判定(\p{L}/\p{N})を使います。これは親が 1 つ下のROLE_WORD_REで既に行っていることと同じです。ASCII 形式では失敗するテストで固定しました。上流では未修正です。
備考
- 親の Workday ファセット復旧(#3851、#3874)は移植不要でした。 web-ui は Workday をページングもファセット分割もしません(
offset: 0の POST が 1 回だけ)。v1.229.0 と同じ結論を、前提とせず再確認しました。 providers/_types.jsの変更はドキュメントのみです(省略可能なJob.salary)。- テスト: 2962 → 3009(+47)。
[1.230.0] — 2026-09-05
変更 — 親 career-ops v1.32.0 からのプロバイダー修正 3 件と、移植そのものが浮かび上がらせた欠陥 1 件。
追加
- Welcome to the Jungle がサーバー側の Algolia
filters式を受け取ります(wttj: { filters: "offices.country_code:FR AND contract_type:full_time" })。キーワードだけでは全世界規模のボードは絞れません。どのmax_hits件が返るかを決めるのはスキャナーの絞り込みではなく Algolia の関連度順であり、こちらの絞り込みは届いたものに対して後から効くため、上限を超えたものはどれほど適合していても見えません。フィルターは並べ替えではなく結果集合そのものを縮めるので、クエリごとの上限は 200 から 1000 に上がります。フィルターがあればqueriesは任意になり、空のクエリは「フィルターを通過するすべて」を意味します。親 #3757 から移植。
修正
- Radancy は、テナントが自身のバナーの主張より単に少ない求人しか返していないだけの場合に「truncated」と警告しなくなりました。 親プロジェクトは
data-total-resultsを実在の 9 テナントと突き合わせました。5 件は一致しましたが、4 件は — 小規模なものも巨大なものも含めて — 重複行が 1 行もないまま、公称より 10〜56% 少ない一意の求人しか返しませんでした。バナーはテナント自身の検索インデックスが実際に返すものを過大に示すため、蓄積件数と比較する方式は過半数のボードで誤検知になっていました。警告はこちら側のmax_jobs/max_pagesが走査を止めた場合にのみ出ます。呼び出し側が対処できるのはその場合だけだからです。totalResultsは文脈としてメッセージに残りますが、判断には一切関与しません。#3839 から移植。 - Radancy は JSON フラグメント要求のたびにキャッシュを回避します。 この経路の手前にあるキャッシュ層は一部のテナントで古い応答を返します。同じ求人が数ページ後に再び現れる一方、別の求人は一度も返らない — 上流で 9 回中 9 回再現しました。
Cache-Control/Pragmaヘッダーでは何も変わらず、要求ごとのランダムなパラメーターで解決しました。これは呼び出しごとに一意でなければならず、ページ番号から導いてはなりません。決定的なパラメーターではキャッシュに安定した鍵を与えてしまい、同じ安定した誤った対応が残るからです。#3839 から移植。 - Greenhouse のオフィス一覧は決定的な順序になりました。 Greenhouse は応答間で
offices[]の順序を保証せず、/officesツリーから復元した集合はその走査順に従います。どちらも求人のlocationフィールドに届くため、ボードがオフィスを並べ替えるとこの文字列が書き換わり、何も変わっていない求人が新規として読まれていました。親は集合を修正しましたが(#3839)、web-ui には親には存在しない同じ欠陥の 2 例目がありました — 求人自身が所在地を持たないときに使うoffices[0]のフォールバックです。現在は両方とも並べ替えています。
備考
- 親の iCIMS 修正(#3728)は移植不要でした。 これは詳細ページの JSON-LD から
jobLocationの全項目を読みます。一部のテナントが実際の住所の前にすべてUNAVAILABLEの項目を出すためです。web-ui の iCIMS は一覧ページの HTML から所在地を取り、詳細ページを開くことはありません。 - 親の
_http.mjsの変更も移植不要でした。isRefusedRedirectErrorを切り出して 2 番目の利用者と判断を共有するためのものですが、web-ui にその 2 番目の利用者はなく、isRetryableErrorの挙動は変わりません。 - 所在地を考慮した重複排除(#3751)は反映していません — 親 CLI の
scan.mjsパイプラインにあるものです。 - レジストリは変更なし: 90 ソース(EN 85 + RU 5)。テスト: 2956 → 2962(+6)。
[1.229.0] — 2026-09-03
追加 — 親 career-ops v1.31.0 から 4 つのスキャンソース: Built In、Feishu Jobs、Garena、MokaHR。
追加
- 新規ソース 4 件、86 → 90(EN 85 + RU 5)、
ALL_ADAPTERS81 → 85。いずれもトークン不要です。Built In は米国のテック求人ボード 9 市場を横断しますが、既定のクエリを持ちません:queries:もcategories:もない設定は何もスキャンせず、その旨を告げます —— 共有ソースが誰か一人の検索語を抱えてはならないからです。許可リストにない市場は拒否され、黙って全国版に差し替えられることはありません。Feishu Jobs が受け付けるホスト形式は 2 つだけで、求人ページのパスが互いに異なります。Garena は単一企業で、officeはリンクを形づくり、一覧には影響しません。MokaHR は暗号化された応答(鍵が隣に届く AES-128-CBC のエンベロープ)を返します —— 難読化であって保護ではありませんが、復号しなければ何も読めません。
修正
htmlToTextが不完全なタグ開始文字をテキストに漏らしうる問題。safe <img src=x onerror=1がsafe <img src=x onerror=1として出ていました。今は各デコードのあとに除去が走り、末尾の開始文字は山括弧を失います。親から移植(#3491)。
備考
- 親の Workday 修正からは何も移植していません。 あれは CXS バックエンドがオフセット 2000 でページングを打ち切るテナントを救うものですが、web-ui はそもそも Workday をページングしません(
offset: 0の POST 1 回、100 行)。 verify-cv-factsとmerge-trackerの修正は移植不要でした: web-ui はこれらを親のチェックアウトから実行します。- テスト: 2909 → 2956(+47)。
[1.228.5] — 2026-08-30
修正 — 報告されるバージョンは、実行中のコードではなくディスク上のファイルを表していました。
修正
-
/api/healthが毎リクエストでpackage.jsonを読み直していたため、古いプロセスが自分では実行していないバージョンを報告していました。 QA は、version: 1.228.4と答えながら 1.228.3 で追加された/api/pingに 404 を返すローカルインスタンスを見つけました。デプロイ前に起動し、古いコードを提供しつつ、新しいファイルのバージョンを報告していたのです。ファイルをコピーしても再起動しないデプロイは、まさにこうして成功に見えます —— 運用者が確認する唯一の文字列が、その問題を見られない文字列なのです。v1.228.1 のシンボリックリンクと同じ種類で、あのときも rsync は転送の途中で中断しながら成功を報告していました。バージョンはモジュール読み込み時に取得され、実行中のコードを表します。古いプロセスは古いバージョンを報告します —— それが真実であり、最初のリクエストで見えます。
parentVersionは意図的にリクエストごとのままです。親チェックアウトを表すもので、このプロセスは読み込まず、稼働中のサーバーの下で正当に変わりうるからです。
[1.228.4] — 2026-08-30
修正 — 認証なしの死活監視が毎リクエストでディスク処理をしていました。
修正
GET /api/pingが毎回package.jsonを読み込んで解析していました。 認証なしで到達できる唯一のエンドポイントであり、リクエストごとのディスク読み取りと JSON 解析は誰にでも手渡されたサービス拒否のレバーです —— CodeQL はレート制限の欠如として指摘しました。バージョンはルート登録時に一度だけ読み込みます。再起動なしに変わることはないので、ハンドラーは I/O を一切行いません。レート制限は被害に上限をかけますが、処理を取り除けばレバーそのものが消えます。- ループバック外でのプロフィール所有者の実名マスクがテストで固定されました。
/api/healthはすでにhiddenに置き換えていましたが、それはその行がプロジェクトルートとhidden ?? valueのガードを共有しているからにすぎません。何もそれを検証しておらず、その行に独自の値を与える変更があれば、実在する人物の名前が誰にも気づかれずに LAN へ漏れていたはずです。
[1.228.3] — 2026-08-30
Fixed — the docs assistant could not answer in English, Telegram company names arrived truncated, and a monitor had no way to see the service was up.
Fixed
-
The docs assistant answered “no matching help sections were found” in English while the same question worked in Russian. Retrieval was fine; the context builder was not.
buildAskPromptdidif (ctx.length + sec.body.length > MAX_CONTEXT) break— abreak, so one oversized section ended the loop and left the context empty, and the assistant truthfully reported it had nothing. The English §5 had grown to 16 081 bytes against a 14 336-byte budget (partly because v1.228.0 addedtelegram_channelsto it); the Russian §5 was 13 009 and fit. Same ranking in both languages — §5 was first — only the size differed.Two changes: a section past 6 KB is now split at its
###boundaries, sotelegram_channelsis its own retrievable chunk (1 340 bytes) that ranks first in both languages; and an oversized section is skipped or truncated rather than ending the loop. The guide went from 32 chunks to 75. -
A Telegram company label yielded the sentence after it, not the name.
Компания: FinCore Technology (продуктовая разработка, высоконагруженные системы)becameFinCore Technology (продуктовая разработка, высоконагруженны— capped at 60 characters, cut mid-word. Three of 141 rows. The name now ends at the first separator that introduces a description — a spaced dash (soCoca-Colasurvives), a comma, semicolon, pipe or opening parenthesis — and any remaining cap falls on a word boundary. This source attributes a company as fact; a truncated sentence is as wrong as a guessed name, only less obviously so.
Added
GET /api/ping— a liveness probe that leaks nothing.{ ok, version }and no more./api/healthreports absolute paths, the profile owner’s real name and which API keys are set: legitimate for an authenticated operator, never for the public internet. A monitor behind a Basic-auth gate previously had no way to see the service was up at all.
[1.228.2] — 2026-08-30
Fixed — a two-word search in /api/scan-results matched the phrase, not the words, and under-reported badly.
Fixed
-
?q=now matches every term independently instead of the literal phrase. The query was a singleincludes(q)over title + company + location, soпродакт менеджерfound only rows carrying those two words in that exact adjacency — 32 of the 162 that carry both.Менеджер продуктаandМенеджер по развитию продуктаwere invisible to it: the same words, reordered or with filler between them.This is how a narrow match becomes a wrong answer. The Telegram assistant asked for all Продуктовый менеджер reported 9 — faithfully, because 9 was what the endpoint said. The count it quoted was honest; the endpoint’s was not. Word order and the words between are not something a person typing two words intended to pin.
Matching is AND across terms, so widening precision costs nothing: a row must still carry every term.
менеджер разработчикreturns zero rather than the union.
[1.228.1] — 2026-08-29
Fixed — a dangling symlink in the repo that silently truncated deploys.
Fixed
-
.claude/skills/refero-designwas a tracked symlink to a path that has never existed here. It pointed at../../.agents/skills/refero-design, which resolves to.agents/at the repo root; there is no such directory, and the link arrived in the v1.118.0 parity pack from a machine where the surrounding folders nested differently. Anyone cloning the repo has been getting a broken link ever since.It was not inert. rsync aborts on it mid-transfer — a deploy today reported success while the server kept running the previous version, and the mismatch only surfaced because the version was checked afterwards. The parent project’s
node test-all.mjsalso crashes outright when a web-ui checkout sits inside its tree, because its fixture copy walks the working directory and cannot stat the link. Nothing referenced it: the onlyreferomatches in the repo are the Polish word preferowany.
[1.228.0] — 2026-08-29
追加 — スキャンソースとしての Telegram チャンネルと、/api/scan-results のページング付きクエリモード。
追加
-
Telegram チャンネルがスキャン対象になりました。
portals.ymlのtelegram_channels:ブロックに列挙し、各チャンネルは公開ウェブプレビューhttps://t.me/s/<チャンネル>から読みます。レジストリ: 85 → 86 ソース(EN 81 + RU 5)、ALL_ADAPTERS80 → 81。Telegram は RSS を publish せず、Bot API はボットが管理していないチャンネルを読めません。つまり自明な経路は両方とも塞がっています。
/s/プレビューはサーバー生成の素の HTML で、認証も JS も Cookie も不要です。16 チャンネルで検証: 15 が素の GET で 20 件ずつ返しました。channel:は書き方を問いません。パーサーは Telegram の再設計が最後まで動かさないであろう 3 点 ——data-post、本文コンテナ、ISO 日付<time datetime>—— に依拠します。 -
GET /api/scan-resultsにページング付きクエリモードを追加。?q=<テキスト>&limit=50[®ion=ru|en][&set=filtered|fresh][&offset=N]が{ total, returned, offset, limit, set, rows }を返します。totalはページング前の件数です。パラメータなしなら従来どおりスナップショット全体を返します ——#/scanのテーブルがその形に依存しているためです。
修正
- Telegram アシスタントはデータの一部だけで答えていました。 スキルはすでに
/api/scan-resultsを指していましたが、このルートはスナップショット全体しか返せず、実機で約 2 MB —— エージェントのコンテキストに入りません。「Продуктовый менеджер を全部探して」に対し、スナップショットに数百件ある中で 9 件と答え、「限定的なローカルインデックスで動いている」と述べました。それは本当で、原因はエンドポイントが見えても消費できないことでした。ページングモードなら同じ質問が 2 KB で済み、正直なtotalを返します。スキルも書き直しました: 新着の投稿を要約せよと書かれており、filtered(残った全件)ではなくfresh(直近実行分のみ)を指していたためです。
備考
- チャンネルの投稿は散文であり、求人レコードではありません。 構造化フィールドは存在しません: 見出しは最初の実質的な行から、会社・勤務地・給与は明示ラベルから取り、本文は説明に入れます。本物の求人を広告やダイジェストと分ける役目は既存の
title_filter—— 他のソース向けに調整済みの、まさにそれ —— に委ねています。これは手がかりであって一覧ではありません。 - 会社名は決して推測しません。 ラベルが無ければ行はチャンネル(
@rabotaphp)に帰属させます。誤った雇用主はトラッカーに事実として入ってしまうからです。 - 解析結果ゼロはエラーであり、「求人なし」ではありません。 非公開・存在しないチャンネルに t.me はリダイレクトを返します。ここでゼロ件を返せば静かな一日と読め、設定のタイプミスを永久に隠します。元の一覧の
jobGeeksがまさにこれ(HTTP 302)で、意図的に設定していません。 - リモート判定は
\bではなく Unicode の単語境界を使います。\bは ASCII 専用で、キリル文字の前の空白は境界にならないためです —— v1.227.3 でタイトルフィルターが踏んだのと同じ罠で、今回はリリース前にテストが捕まえました。 - 15 チャンネルを設定し実データで検証: 299 投稿、15/15 チャンネル。新規テスト 28 件。テスト: 2865 → 2895。
- The README test counts jumped by hundreds, and no, this release did not add them. The localized READMEs carried unit-test totals frozen at 1856 / 1945 / 1955 / 549 / 419 / 284 / 2527 depending on the file — each stale since a different release, and several contradicting the badge at the top of the same page. They now all read 2895, verified by running the suite rather than by copying a badge. Playwright (70 / 90 / 12 → 101), e2e smoke (20 → 21), route modules (12 / 32 → 37), smoke flows (12 → 22) and the test-file count (218 → 329) were stale in the same way. The
Totalrow also repeated the unit count, so it under-summed the three suites below it; it now reads the real sum (3040).
[1.227.5] — 2026-08-29
修正 — 地域スキャンがサーバーのメモリを食い潰し、Playwright スイートが自身の teardown で不安定に落ちていました。
修正
- 地域スキャンが OOM でサーバーを落としていました。
ru-scannerは全クエリの生ヒットをすべてため込み、重複排除とフィルタリングを最後にまとめて行っていました。クエリ一覧は意図的に類義語だらけ(Golang、Go разработчик、Golang разработчик、Senior Go…)なので、同じ求人がクエリごとに返り、配列はユニーク件数の数倍を保持していました —— しかも求人オブジェクトの大半を占める説明文つきで、その説明文はフィルタでほぼすべて捨てられます。実測: 21 クエリの実行でヒープ 742 MB。サーバーは Node のヒープを 490 MB に制限しており(V8 がマシンの 956 MB の RAM から算出)、スキャンはFATAL ERROR: Reached heap limitでサービスを落としました —— 1 日に 4 回。現在は重複排除とフィルタリングをクエリごとに行い、同じ実行が 177 MB に収まりました(76% 減)。報告される件数は不変です: ユニーク合計は URL 文字列のSetが保持します。 net::ERR_SOCKET_NOT_CONNECTEDが CI をランダムに落としていました。 3 つの Playwright スイートは、コンソールエラーが無いことを検証する前にwindow.stop()で進行中のリクエストを止めます。Chromium はこの 1 つの意図的な操作を、ソケットの状態に応じて 3 通りの名前で報告します: 取り消し時はERR_ABORTED、飛行中はERR_EMPTY_RESPONSE、ソケットが既に消えていればERR_SOCKET_NOT_CONNECTED。共有フィルターは最初の 2 つしか知りませんでした。ロケール走査のtrで表面化しましたが、同じくwindow.stop()を呼ぶplaywright-formsとplaywright-smokeも、不運な 1 回で同じ結果になるところでした。
備考
- なぜ今になって落ち始めたか。 プロダクトマネージャー系の検索を足した際にクエリが 14 個から 21 個になりました。旧実装のメモリは総ヒット数に比例したため、クエリ +50% でマシンの上限を超えました。新しいクエリに問題はありません —— 長らく設定変更ひとつで破綻し得た確保パターンを露呈させただけです。
- 重複排除の変更は挙動を保ちます。主張ではなくテストで担保しています。
tests/ru-scanner-dedup-order.test.mjsは厄介な方向 —— 後から来た重複がフィルタに落ちる場合、先に通っていた方を追い出さねばならない —— を固定し、旧実装との等価性を 200 回のランダム試行で検証します。 - コンソールフィルターは狭いままです。 無害なのは取り消し系のみ。接続拒否、DNS 失敗、リセット、あらゆる 5xx、未捕捉の JS 例外は引き続き検証を落とします。
- 2 つの仮説は検証のうえ破棄し、リリースには含めていません: Caddy のタイムアウト(設定はクリーンで、SSE 用の
flush_interval -1も正しい)と Node 既定の 5 分requestTimeout(縮小再現により、長いストリーミング応答を切断しないと判明)。実際の証拠はジャーナルのReached heap limitでした。テスト: 2860 → 2865。
[1.227.4] — 2026-08-29
修正 — word: / stem: は content_filter では依然として無視されていました。v1.227.3 が直したのはタイトルフィルターだけでした。
修正
content_filterがプレフィックスを尊重していませんでした。 v1.227.3 はtitle_filterに親のword:/stem:プレフィックスを教えましたが、content_filterは独自の素のlower.includes(k)のままで、プレフィックス付きエントリはそこでも文字列そのもの"word:java"として照合されていました —— 同じ静かな no-op が、フィルター一つ隣に残っていたわけです。content_filterは求人の説明文を読み、その既定は常に単純な部分一致でした。だからこそ素の否定javaは「JavaScript」に触れているだけの求人まで除外します。プレフィックスは、その既定から特定の 1 エントリだけを外す手段です。親のcompileContentKeywordを移植しました。プレフィックスの仕組みは共有しつつ、短いキーワードの自動アンカーは意図的に行いません —— タイトルフィルターが 2〜3 文字の略語を固定するのは「Coordinator」の中の「COO」が常に誤りだからですが、説明文の段落中の短い並びは通常は意図されたもの(aws、gcp、sql、go)だからです。
備考
- v1.227.3 の PR に対する AI レビューで発見されました。 コードが自身のコメントの主張を満たしていない —— コメントは「
title_filter/content_filter」と書きながら、配線されていたのはタイトル側だけ —— という指摘です。現在のコメントは明示的に分けています: プレフィックスは両方のフィルターで有効、略語の自動アンカーはタイトルのみ。 - 同じレビューから検証済みで、変更なし: 2〜3 文字の略語ルールを
\bから Unicode 境界へ移した件は ASCII では挙動が同一で、必要な箇所でのみ厳格になります ——COO_leadとcoo1は引き続き保持(アンダースコアと数字はどちらでも単語文字)、Coordinatorも保持、「Директор COO」と「VP «Продукт»」は正しく除外。14 の境界ケースを確認しました。 - プレフィックスの無い
content_filterエントリは従来の一致をバイト単位で維持し、空・欠落した説明文は引き続き通過し、中身のないword:/stem:は全部ではなく何にも一致しません。tests/content-filter.test.mjsに 6 ケース追加。テスト: 2852 → 2858。
[1.227.3] — 2026-08-29
修正 — タイトルフィルターの word: / stem: プレフィックスが黙って無視され、その行が実質無効になっていました。
修正
- web-ui は親プロジェクトの
word:/stem:プレフィックスを実装していませんでした。title_filter(およびcontent_filter)は既定で大文字小文字を区別しない部分一致であり、そのため素の否定internは「International Product Manager」や「Internal Tools Engineer」まで除外します。この既定を反転させると設定済みの全インストールが壊れるため、親はエントリ単位で精度をオプトインにしました:word:internは単語全体のみに一致し、stem:agentは単語の先頭に一致して続きは自由(「Agentforce」と「Reagents」を区別)。web-ui にはどちらも無く、エントリを文字列そのもの"word:intern"として照合していました —— そんな文字列を含む求人タイトルは存在しません。結果としてその行は静かな no-op となり、同じportals.ymlが CLI では正しく効き、ここでは全く効かず、除外したはずのインターン求人がすべて#/scanの表に届いていました。親のtitle-keywords.mjsから移植し、\b(ASCII 専用で、アクセント付きやキリル文字のタイトルでは語中に一致していました)ではなく Unicode 対応の語境界(\p{L}\p{M}\p{N}_)も併せて取り込みました。
備考
- 実際の
portals.ymlの確認中に発見。 そこでは素のinternが「International Product Manager」を落とさないよう、まさにこのためにword:internを導入したばかりでした。CLI ではプレフィックスが効き、web-ui は黙ってインターン求人を残していました。現在は挙動が一致します。 - 既定は変わりません。 プレフィックスの無いキーワードは従来どおりの一致方法を保ちます: フレーズや非英字を含むもの(
.NET、SAP、L&D)は部分一致、2〜3 文字の略語は語境界固定。中身のないword:/stem:はタイポとみなし何にも一致しません —— 否定として空パターンは全タイトルに一致し、コロン一つでスキャン全体を無効にしてしまうためです。 - 2〜3 文字の略語ルールも同じ Unicode 境界に統一しました。従来は
\bを使っており、非 ASCII のタイトルでcooとword:cooが食い違っていました —— 一つの規則に二つの書き方があった状態です。 tests/title-filter.test.mjsに 6 ケース追加。テスト: 2846 → 2852。
[1.227.2] — 2026-08-29
修正 — 幅の狭い端末で #/config が横にはみ出していました。
修正
- MOBILE-1: 幅 352 px 以下のビューポートで
#/configが横方向にあふれていました。 select 系のフィールドはすべてインラインのmin-width: 300pxを持っていました。インラインスタイルはスタイルシートに優先するため、コントロールはコンテナに合わせて縮むことができず、320 px のビューポートでは select の右端が 353 px に達し、ページに横スクロールが生じてコントロールが右端の外へ押し出されていました。対象はプロバイダー/モデルの select 18 個すべて(cfg-llm-providerと全cfg-*-model)。現在はmin-width: min(300px, 100%)とmax-width: 100%により、余裕がある場合にのみ 300 px の快適幅が適用されます。しきい値以下の端末: iPhone SE(第 1 世代、320 px)、旧 Android、Galaxy Fold の外側画面。
備考
- これまでのモバイル確認がすべて見逃した理由。 360 px と 375 px にはあふれを隠すだけの余裕がちょうどあり、Playwright のスイートは幅 1280 px のウィンドウで動きます。CI で 375 px より狭いビューポートを測定したものは一つもありませんでした。新しい
tests/playwright-narrow-viewport.mjsはフォームを持つ各ルートを 320 px で走査し、横方向のあふれがあれば失敗し、最も右に張り出した要素をメッセージに示します。2 つ目のケースは#/configのどの select もビューポートを超えないことを固定します。まずバグを再現することを確認済みです(scrollWidth 353 > clientWidth 320、右端 353 の select が 18 個)。Playwright: 99 → 101。 - 既知かつ意図的にまだゲート対象外: 280 px(Galaxy Fold の外側画面)は約 5 px のあふれが残りますが、原因は別です — 設定カードの
<details>ブロック内の長いコンテンツと、280 px のビューポートに対する 256 px のサイドバーであり、本リリースが修正したインラインmin-widthではありません。主要な端末が申告しない幅に合わせた再レイアウトは別作業で、スイートのWIDTHSリストは対応済みです。 - v1.227.1 の QA プロンプトも修正しました。フォローアップのルートを
#/modes/followupと記載していましたが、mode ページはスラッグをそのまま登録するため(Router.register(cfg.slug))、正しくは#/followupで、#/modes/followupは正しく 404 になります。ドキュメントのみ。 - ユニットスイートは 2846 のまま(今回の追加はブラウザーレベルの網羅です)。
[1.227.1] — 2026-08-29
修正 — ヘルプのソース内訳が自身と食い違い、GitHub 用バナーがアプリで生のマークアップとして表示されていました。
修正
- §17 の合計と内訳が合っていませんでした。 どのヘルプバンドルも「
server/lib/sources/レジストリは 85 個のアダプタを同梱 — 英語圏 78 個 + ロシア系 5 個」と記載していました。78 + 5 = 83 で 85 ではなく、ja/ko はさらにずれて 77(= 82)でした。新しいソースを追加するたび合計だけを更新し、内訳は据え置いたため、両者は複数リリースにわたって乖離していました。内訳は全 17 ロケールで EN 80 + RU 5 になりました。これは体裁の問題にとどまりません。docs/help/<lang>.mdは「Ask the docs」が根拠にする唯一のコーパスであり、対応ソース数を尋ねたユーザーは一つの文から矛盾する二つの数字を受け取っていました。 - 同じ文が古いバージョンアンカーを固定していました。 「v1.213.0 時点でレジストリは …」が 16 ロケールに残る一方、カウンタは二度動いています(v1.219.0 で 83、v1.226.0 で 85)。このアンカーは v1.210.1 で一度手作業で直されており、再び流れました。バージョンを含まない表現に置き換えて削除しました — カウンタ自体は生きたレジストリと突き合わせて検証され、古くなりようがありません。
- プロバイダーバナーが
#/helpの先頭にエスケープ済みマークアップとして表示されていました。 v1.225.0/v1.225.1 は「UI.md()に画像サポートがないためアプリ側では除去される」と記録していました。これは二つの別物を混同しています。UI.md()はエスケープ優先であり、markdown 変換の前にソースの全バイトをエスケープします。これは XSS 境界として正しい設計ですが、除去とは逆です。誰も除去を試みなかったため誰も除去せず、17 ロケールすべてが#/helpを 268 文字の生の<p align="center"><img src="https://…で開いていました。新しい共有入口(server/lib/help-markdown.mjs::stripGithubOnlyBlocks)が読み込み時に単独の HTML 画像ブロックを取り除きます —GET /api/help/:langと docs-assistant のコーパスの両方で。ファイル側にはバナーが残るため、GitHub のショーケースはこれまで通り表示されます。 - ケイデンスボードの「#」列は v1.117.0 以来空でした。
entry.appNumを読んでいましたが、followup-cadence.mjsが返すのはnumで、appNumはペイロードに一度も存在しませんでした。現在は両方を読みます。v1.227.0 の「Next up」行も同じ誤りを引き継いでおり、あわせて修正しました。
備考
- 4 件とも、ゲートが無かったために見過ごされてきました。 ヘルプのゲート(
canonical-docs-coverage,help-ui,help-ru-config-section)は見出しを数えるもので — 32 H2 / 121 H3 — 段落内の数値を読んだテストも、ヘルプ入口が実際に何を返すかを見たテストも存在しませんでした。新しい 2 つのスイートがこれを塞ぎます:tests/help-source-counts.test.mjs(記載された合計がSOURCES.lengthと一致し、内訳が生の EN/RU 分割と一致して合計になり、段落がバージョンを固定しないこと)とtests/help-banner-strip.test.mjs(バナーは GitHub 用にディスク上へ残り、strip 後は決して残らず、各バンドルの最初の描画行が見出しであり、strip が狭いままで、両方の利用側がそこを通ること)。いずれもコミット前に、まさにこれらのバグに対して赤になることを確認済みです。テストスイート: 2837 → 2846。 - レジストリ・スキャナー・ルート契約に変更はありません。ソースは 85(EN 80 + RU 5)、
ALL_ADAPTERSは 80 のままです。
[1.227.0] — 2026-08-28
追加 — SEEK 香港(JobsDB)、フォローアップの要対応のみ絞り込み、そして非公開の雇用主が仲介エージェンシーを表示。
追加
- 既存の Jobstreet/SEEK ソース経由の JobsDB 香港。 香港は JobsDB ブランドで運営されていますが、同じ v5 エンドポイントの背後にある同一の SEEK プラットフォーム上にあるため、別ソースは不要です —— ホスト許可リストへの
hk.jobsdb.comとsiteKey: HK-Mainだけで足ります。レジストリは据え置きで 85 ソース(EN 80 + RU 5)、ALL_ADAPTERS80 —— 既存アダプターに加わった新しい市場であり、新規アダプターではありません。 - ケイデンスボードの要対応のみ絞り込み(
#/modes/followup)。新しいチェックボックスで、いま対応が必要なものだけに絞れます。要対応かどうかは親プロジェクトのfollowup-cadence.mjsが算出済みのurgency(urgent/overdue)から読み取り、トラッカーのstatusは決して使いません —— そちらはapplied/responded/interviewであり、何にも一致しないためです。要対応が無いときは、空表示のかわりに直近の予定フォローアップを提示します。ロジックは親のfollowup-view.mjsを写した純粋モジュールpublic/js/lib/followup-view.jsにあります。 - 非公開の雇用主は、仲介したエージェンシーとして表示されます。 雇用主が伏せられた求人はトラッカーに
?として記録され、リクルーターはVia列に入ります。#/trackerは素の?を出すだけだったため、複数の非公開行を区別できず、ロゴ解決も手がかりを持てませんでした。今後そうした行は 「非公開 · <エージェンシー> 経由」 と表示され、ロゴはエージェンシー名から解決され、検索欄でもその名前で見つかります。表示のみの変更です —— 正本のcompanyフィールドをリクルーターで上書きすることはありません。親のcompany-presentation.mjsを写した新しい純粋モジュールpublic/js/lib/company-presentation.js。
修正
- Jobstreet/SEEK が廃止済みエンドポイントを呼んでいました。 ソースは SEEK が終了した chalice-search v4(
/api/chalice-search/v4/search)を指したままでした。親は/api/jobsearch/v5/searchへ移行済みでしたが、web-ui は追随していませんでした。今後すべての Jobstreet/SEEK エントリが v5 を照会します。v5 のアイテム形状をそのまま読み(idから URL を生成、locations[0].label、advertiser.description、salaryLabel)、v4 専用の射影パラメータsolrFieldsは削除、portals.ymlが死んだ v4 パスを指したままのエントリも、市場のホスト名は保持しつつパスだけ v5 に組み直されます —— 利用者の編集は不要です。
備考
- career-ops 1.30.0 パリティ(親 HEAD
8d64f65→6cc46a4; 親のVERSIONはまだ 1.30.0 —— release-please がmainより遅れています)。未移植とその理由: iCIMS の JSON-LD 位置補完 —— web-ui の iCIMS ソースにはenrichDate()詳細ページフックが存在せず(一覧ページのみ)、直すべき経路が無い; 非致命的な CLI stderr(#1974) —— web-ui のランナーは終了コードのみで失敗を判定し、そのヒューリスティックを持たない; Turbopack のパストレース —— web-ui にバンドラーは無い;cv-sync-checkの修正 —— 読み取り専用のフェイルソフト中継;scan.mjsの Playwright 逐次化、Block H 回答、updater/doctor/update-systemのガード、再帰的な構文チェック、Go 製ダッシュボード、jd-skill-gap/verify-cv-factsの自己テスト追加 —— web-ui が呼ばない CLI 専用の面、または挙動を変えない自己テスト。テストスイート: 2818 → 2837。
[1.226.0] — 2026-08-27
追加 — ベトナムの求人ソース2つ: ITviec と CareerViet。
追加
- ITviec + CareerViet(ベトナム)。 ホスト固定のベトナム系ボード2つが
provider: itviec/provider: careervietエントリでスキャナーに加わります: ITviec(itviec.com、技術系)と CareerViet(careerviet.vn、広範)。いずれもトークン不要の HTML スクレイパーで、SSRF ホスト固定・HTTPS 限定・会社ごとのフェイルソフト・ページ上限を備えます。レジストリは 85 ソース(80 EN + 5 RU)、ALL_ADAPTERS80 —— 両方が#/scanのソースフィルターに表示されます。
備考
- career-ops 1.30.0 パリティ(parentVersion 1.29.0 → 1.30.0)。未移植(CLI/コミュニティ専用または既対応): Hired Wall、
/calibrate、clean-markers、テンプレートパック、doctor、pdf/LaTeX、web-ui が呼ばない eval/tracker/scan CLI 群;_html-to-text修正と detect-repostsroleFuzzyMatchは既存;title-filter stem:プレフィックスは保留。テストスイート: 2818。
[1.225.1] — 2026-08-27
修正 — プロバイダーロゴのバナーが README とヘルプで表示されるようになりました。
修正
- v1.225.0 のバナーが GitHub で表示されませんでした: 単体の
images/providers.svgのfont-familyに入れ子の二重引用符(GitHub が拒否)があり、<img>がリポジトリ相対のsrc(GitHub は raw HTML<img>の相対パスを解決しません)を使い、<p>ブロックに末尾の空行が無く次の行と結合していました。現在は絶対 raw URL でレンダリング済みのimages/providers.pngを指し、空行で区切られています。SVG も正しい XML に修正しました。
備考
- ドキュメントのみ; コード/テストの変更なし。cvstart.org のショーケースとアプリのタイルは既に正常でした(インライン SVG、このファイルではない)。 2784
[1.225.0] — 2026-08-27
追加 — README とヘルプガイドにプロバイダーロゴのバナー。
追加
- プロバイダーロゴのバナー。 単一の自己完結型
images/providers.svg—— 18 の LLM プロバイダーを、アイコンを公開する11は実際のブランドロゴ(simple-icons、CC0)、残る7はブランドカラーのモノグラムで —— が README(×17)とアプリ内ヘルプガイドの先頭に表示されます。(アプリは実際のロゴタイルを自前で描画します。バナーは GitHub で表示され、アプリの XSS 安全なレンダラーが取り除きます。)
備考
- ドキュメントのみ —— コード/テストの変更なし; SVG はインライン(リモートアセットなし)。スキャンソースは変更なし: 83。テストスイート: 2784。
[1.224.0] — 2026-08-26
変更 — LLM プロバイダーのタイルが実際のブランドロゴを表示するようになりました。
変更
- 実際のプロバイダーロゴ。 プロバイダータイル —— 設定の API キー欄、使用状況の行、ダッシュボードのチップ、設定の「アクティブ」サマリー、⚡ 評価結果、オンボーディングバナー —— が、オープンソースのアイコンを公開している11のプロバイダー(Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub · DeepSeek · Kimi · MiniMax · Mistral · Ollama; simple-icons の単一パス SVG、CC0)について実際のブランドロゴを表示します。公開アイコンのない7つ(Hermes · GLM/Z.ai · Grok · Together · Fireworks · BytePlus Ark · Volcengine Ark)はブランドカラーのモノグラムのままです。依然として構造的に CSP 安全: ロゴパスはインラインの静的定数 —— リモートアセットなし、
innerHTMLなし。
備考
- ルートや動作の変更なし; 同じ
ProviderLogo.el(slug)API。provider-logo.jsは大きくなりました(インラインのロゴパス)。スキャンソースは変更なし: 83。テストスイート: 2784。
[1.223.0] — 2026-08-26
修正 — 成功した ⚡ ライブ評価がエラーとして着色されていた問題。さらに OpenWorker コワーカーを GitHub URL または .zip から直接インストール。
修正
- ⚡ 評価結果バッジの色。 成功したライブ評価がもう失敗として着色されません。インプロセスのプロバイダー(OpenRouter、DeepSeek、…)はサブプロセスの終了
codeを返さないため、厳密なcode === 0が偽になり、すべての成功が(“exit 0” と表示しつつ)赤でした。色は結果に従うようになりました — 非ゼロで終了したサブプロセスのみ赤、コードのないライブ結果はbadge-ok、“· exit N” の接尾辞は実際の終了コードがある場合のみ表示されます。
変更
- OpenWorker コワーカーを GitHub URL / .zip からインストール。 コワーカーのドキュメントに、ワンコマンドのインストーラー(
curl … | bash、冪等 — 既存の career-ops / web-ui を再利用)と OpenWorker の3つのインストール経路(GitHub URL ·.zip· 単一ファイルのインポート)がdocs/integrations/openworker.md、アプリ内ヘルプ §32(×17)、README に記載されました。
備考
- 長年のカバレッジの穴を塞ぎました: Recruitee ソースに v1.214.2 の引用符付き山括弧の修正(タグ属性内の
>が説明に漏れてはならない)のエンドツーエンドテストを追加。スキャンソースは変更なし: 83。テストスイート: 2783。
[1.222.0] — 2026-08-26
変更 — 拡張プロバイダー(DeepSeek … Volcengine Ark)のフィールドヒントが 17 言語すべてにローカライズされました。
変更
- プロバイダーのフィールドヒントをローカライズ。 v1.216.0–v1.217.0 で追加された 11 の OpenAI 互換プロバイダー(DeepSeek · GLM/Z.ai · Kimi · MiniMax · Mistral · Grok · Together · Fireworks · Ollama · BytePlus Ark · Volcengine Ark)の各
config.<slug>Hint—— 設定 → API キーの API キー・モデル・ベース URL のヒント —— が、英語へのフォールバックではなく 17 ロケールすべてに翻訳されました。登録 URL、モデル id、⚡ ライブ評価マーカーはそのまま保持されます。
備考
- 動作の変更なし ——
field-specs.jsの英語hintFallbackはキー欠落時に依然として保護し、新しい契約テストがすべてのフィールド記述子hintKeyが 17 ロケールで解決されることを検証します。スキャンソースは変更なし: 83。テストスイート: 2779。
[1.221.0] — 2026-08-26
セキュリティ — スキャナー fetch 経路の DNS リバインディング多層防御。
セキュリティ
- スキャナーの HTTP コア(
server/lib/http-json.mjsのfetchJson/fetchText)は、実ネットワーク経路で各ソースのホストを解決し、プライベート/ループバック/リンクローカル/CGNAT/クラウドメタデータのアドレスに解決される場合は接続を拒否するようになりました。スキャナーのホストは既に公開ドメインに固定されているため多層防御であり、ユーザー URL 経路には既により強い接続固定(safe-fetch.mjs)があります。ガードは実際のfetchトランスポートでのみ動作し、注入されたテスト用fetchImplは解決されません。
備考
- 正常なスキャンでは挙動変化なし。スキャンソースは 83 で変更なし。テスト: 2775。
[1.220.0] — 2026-08-26
追加 — 1 つのエントリーで複数の Get on Board カテゴリーをスキャン。
追加
getonbrdエントリーでprogrammingフィードだけでなくcategories: [programming, operations-management, machine-learning-ai](または単一のcategory:)を指定できます。求人はカテゴリー間で URL により重複排除され、上限は 12 件。既存エントリーはそのまま(既定はprogramming)。
備考
- サーバーのみ。SSRF ホストピン(www.getonbrd.com)と
redirect:'error'は不変で、不正なカテゴリースラッグはリクエスト前に拒否されます。スキャンソースは 83 で変更なし。テスト: 2771。
[1.219.0] — 2026-08-26
追加 — Torre がスキャナーに加わり、すべてのプロバイダーラベルが正しくなりました。
追加
- Torre(
provider: torre)— torre.ai の汎ラテンアメリカ人材マーケットプレイス。Greenhouse/Lever/Ashby に載らないリモート求人を含む認証不要の公開検索で、エントリーごとに 1 回の上限付きリクエスト。レジストリは 83 ソース(英語 78 + ロシア語 5)に。さらにオンボーディングバナーにアクティブなプロバイダーのモノグラム。
修正
- a16z Speedrun のスキャンが、ローテーションするボードで早期に停止していました(スイープ中の短いページを最後のページとみなし、約 300 件中 149 件で打ち切り)。今はフィードの
total_pagesを信頼します。またオンボーディングバナーは 4 項目のマップからプロバイダーを解決し、残り 14 は生のスラッグを表示していましたが、18 のラベル+モノグラムを使うようになりました。設定のプロバイダー注記は 7→18 に。
備考
- Get on Board のエントリーごとの複数カテゴリー指定はまだ未対応です(ソース側のリファクタが必要)。テスト: 2768。
[1.218.0] — 2026-08-26
追加 — プロバイダーが表示されるあらゆる場所にバッジ(と正しい名称)。
追加
- ダッシュボードのチップ、設定の「アクティブ」概要、⚡ 評価結果ヘッダーのプロバイダーモノグラム。
修正
- 古いラベル: 3 つの画面が 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順(最後)に加わり、設定と使用状況にモノグラムが表示されます。Web 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 で実行できるようになりました。キー 1 つで使え、設定の各フィールド横にモノグラムが表示されます。
追加
- 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で任意の 1 つに固定できます。Together は Thinking Machines の Inkling(thinkingmachines/Inkling)もホストします。Web 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 アプリからこのパイプラインを動かし、アプリ内ヘルプがそれをカバーするようになったので、Ask-the-docs アシスタントが案内できます。
追加
- OpenWorker coworker。 コード不要の OpenWorker coworker — Fighter90/career-ops-coworker — が求職活動のすべて(スキャン・スコアリング・テーラリング・トラッキング・フォローアップ)を実行し、このダッシュボードを起動でき、すべてデスクトップで完結します。17 言語で提供される 1 ファイルのペルソナで、OpenWorker 自身のローダーでインストール可能と検証済みです。
- ヘルプ §32「Run it from OpenWorker」(17 ロケール全部)がこれを説明するので、アプリ内の Ask the docs アシスタントとフローティングのヘルプボタンが質問に答えられます。ランディングのフッター、README、Wiki、
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 と同じ長さで打ち切られるようになり、コンテンツフィルターが 3 ボードで同一に動作し、1 つのボードがスキャンキャッシュを肥大化させられなくなりました。
修正
- Ashby の説明が他ボードと同じ長さ上限を守るようになりました。 v1.214.0 は Ashby の求人本文をコンテンツフィルターに渡していましたが、4000 文字で打ち切る Greenhouse・Recruitee と違い、Ashby は上限なしでそのままマッピングされていました(一部は 4 倍長い)。そのため同じ
content_filterのキーワードが、テキスト内の位置だけで Ashby ではマッチし Greenhouse ではしないことがあり、1 つのボードが#/scanの読み込むスキャンキャッシュに数百 KB を追加し得ました。Ashby も同じ 4000 文字で打ち切られます(すでにプレーンテキストなので切り詰めるだけで、タグ除去はしません)。
備考
- テストスイート: 2738。スキャンソースは 82 で変更なし。
[1.214.0] — 2026-08-25
修正 — Greenhouse・Ashby・Recruitee の求人説明がコンテンツフィルターに渡るようになり、キーワード / 国 / ビザのフィルターがこれらのボードに実際にマッチします。また「Associate <シニア職名>」(学術ランクを含む)がエントリー級と誤分類されなくなりました。
修正
- コンテンツフィルターが Greenhouse・Ashby・Recruitee で機能します。 これらのボードは各求人の本文全体を説明として持つようになったため、説明を読む
content_filter(や国 / ビザのキーワードフィルター)が、ボードを素通しにせず求人本文にマッチします。Ashby と Recruitee は一覧ペイロードに本文を無料で含め、Greenhouse はすでにそうでした。3 つとも 1 つの共有 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 は検索リクエストを受ける前に匿名ハンドシェイク(セッション Cookie + CSRF トークンを仕込む GET)を要求するようになりました。それなしではリクエストが静かに拒否され、ボードが空に見えていました。
- 複数勤務地の Lever 求人が勤務地の半分を隠さなくなりました。 Lever は主要都市 1 つを
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 の文字(
é→ é、ç→ ç、…)を備えたため、DéveloppeurやFrançaisと書くヨーロッパのボードが、その文字列をタイトル・トラッカー・生成文書にそのまま残さなくなりました。(大文字は大文字のまま —Éは É であり é ではない —&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”、“ООО "М-ТЕХ"”)届くため、未デコードの「&」が自分の「&」タイトルフィルターを静かに外し(前リリースが他 5 ボードで閉じたのと同じ症状)、会社名が文字化けしたままトラッカーやレポートに届いていました。エンティティのデコードが対象 6 ソースすべてで完了しました。
備考
- テストスイート: 2644。
[1.210.0] — 2026-08-19
追加 — スキャナー初のアフリカ求人ボード Senjob(セネガル)。さらに 5 ボードでタイトル照合を改善。
追加
- Senjob(senjob.com)— セネガル向けの新しいトークン不要スキャンソースで、スキャナー初のアフリカのボードです。
#/scanの ソース フィルターで選ぶか、provider: senjobの会社を追加します。公開一覧を素の HTTP で読み(キー不要・ブラウザ不要)、すべてのリクエストを senjob.com に固定し、HTML を解析するため、急に何も取れなくなった一覧を「求人ゼロの国」ではなく壊れたボード(表に出るエラー)として扱います。
修正
- 「&」を含むタイトルが 5 つのボードで求人を落とさなくなりました — 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 で上部バーのボタンがページ見出しに重なる問題は解消しましたが、極端に狭くはない狭い画面 — 特にボタンのラベルが長い言語 — では、バー全体がなお 1 段に詰め込まれ、🔔・🌙 ボタンが検索ボックスの上に載ることがありました。アクションボタン(通知・テーマ・診断・Scan を開く)は、スマホでは常に幅いっぱいの 2 段目に下りるようになり、検索ボックスは全体が見えて何も重なりません。
備考
- スマホでは上部バーのアクションボタンが幅いっぱいの 2 段目へ移り、レイアウトが余った負の空きを重なりとして配っていた脆い「ほぼ満杯の段」帯を解消しました。Playwright ガードが正確な誘発条件 — ラベルの長いロケール × 565–640px の帯 — を再現し、上部バーのコントロールがピクセルを共有しないことを確認します。スイート: 2621。
[1.208.1] — 2026-08-16
修正 — スマホで上部バーのボタンがページに重ならなくなりました。
修正
- v1.208.0 は狭い画面で上部バーのアクションボタン(診断・Scan を開く・通知・テーマ)を 2 段目に折り返しましたが、バーの高さが固定だったため、折り返した段がはみ出してページ見出しの上に重なっていました。バーは段に合わせて伸びるようになり、コンテンツはその下に流れます。
備考
- 上部バーの固定
heightをmin-heightにしたので、どの幅でも内容に合わせて伸びます(デスクトップは不変)。Playwright ガードが上部バーのはみ出しも確認します。スイート: 2621。
[1.208.0] — 2026-08-16
修正 — アプリがスマホ画面に収まるようになりました: 横スクロールが解消。
修正
- 狭い画面ではアプリ全体が横にはみ出していました — トップバー、表、ヘルプ記事、設定タブが右端を越えていました。どの幅でも各ページが収まります: トップバーのボタンは 2 段目に折り返し、幅の広い表やコードブロックは自身のボックス内でスクロール、ヘルプは目次を記事の上に積み、ボタン/タブの行は折り返し、長いパスや 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 スモークチェックを強化しました。姉妹チェックと同様に無害なネットワークノイズ(favicon/接続/リソース失敗)を無視しつつ、実際のスクリプトエラーは引き続き検出します。アプリの挙動は変わりません。スイート: 2618。
[1.207.0] — 2026-08-15
追加 — トラッカーから応募の結果を直接記録できます。
追加
- すべてのトラッカー行に 結果 アクションが付きました: 何が起きたか(不採用・オファー受領・採用・オファー辞退・返信なし・面接に進んだ)を選び、任意のメモを添え、結果のステータスをプレビューしてから記録します。記録すると提出した CV・カバーレターの成果物を保管し、トラッカーを正規の状態に同期します — 手作業の編集ではなく決定論的な 1 アクションです。
備考
- 新しい
POST /api/outcomeが結果 CLI を中継します:dryRun:trueは読み取り専用プレビュー(行を照合し結果ステータスを報告、何も書き込まない)、実呼び出しは記録します。書き込み安全性: 結果タイプは既知の集合にホワイトリストし、すべてのテキストフィールドはシェル呼び出し前に制御文字を拒否します(配列引数、spawn — シェルなし)。tests/outcome-route.test.mjs。スイート: 2618。
[1.206.0] — 2026-08-15
ドキュメント — アプリ内ヘルプガイドが最新の 5 機能を 17 言語すべてでカバーしました。
追加
- アプリ内ヘルプガイドと、それを根拠に回答する「ヘルプに質問」アシスタントが、最近追加された 5 つの機能を説明するようになりました: セットアップドクター(設定 — CV とプロフィールの不足やサンプルデータの残りを点検)、ATS ボードを探す(ポータル — 企業の採用ボードを自動検出)、「まだ募集中?」チェック(トラッカー — 求人がまだ開いているか)、「過去の CV を再利用?」ヒント(CV スタジオ — 調整済みの CV が新しい求人に合うかを通知)、スキルログ(分析 — 自己評価スコアを記録)。新しいサブセクション 5 つを 17 言語に翻訳しました。
備考
- ヘルプ構成は 31 個の H2 / 118 個の H3 に増え、全ロケールでパリティが保証されます。参照ドキュメントを更新:
docs/architecture/API.mdがこれら機能の 5 ルートを記載し、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 スタジオで保存済みの求人を開くと、アプリが他の保存済み求人と比較し(決定的な語の重なり・トークンゼロ)、最も近いものがその調整済み CV を再利用できるほど似ているか、修正して再利用か、新規に調整すべきかを教えます — すでに狙った職種をゼロから作り直さずに済みます。
備考
- 新しい読み取り専用
GET /api/jds/:name/reuseが親プロジェクトのjd-similarity.mjs(Jaccard の重なり + シニオリティガード、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 ゼロ・ブラウザなし — 存在して現在 1 件以上掲載しているボードを表示します。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 の掲載である応募に 「まだ募集中?」 ボタンが表示されます。1 クリックで ATS 自身の公開 JSON を照会し — トークン 0・ブラウザなし — 募集中 / 終了 / 不明 を表示するので、1 件ずつ開かずに消えた求人を見つけられます。保守的設計:明確な 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は非 ok 応答で.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の Workdayapi:値は、ホスト名が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(+5)。スイート: 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;#/statshelp-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の文字体系非依存の後読み。死んでいた重複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の違い、末尾スラッシュ /#フラグメントで再掲載された同じ求人を、本来の1件として扱います — スキャン結果やパイプラインに重複行が出ず、既に見た求人の評価を無駄にしません。本当に異なる求人(gh_jidのような保持された機能的 id)は引き続き別個に数えます。
備考
- 新しい
server/lib/url-key.mjsを両スキャナーの dedup とパイプライン書き込みに接続。意図的に控えめに正規化し、異なる2件を決して統合しません。テスト: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 スキャナー(ファンドのタレントネットワークボード)が、各求人に給与(年額レンジ + 通貨)を表示し、最初の1件だけでなく全ロケーションを一覧化し、リモート求人にタグを付けるようになりました。スキャンとトラッカーの Getro 求人が、他のボードと同じ給与 + ロケーション情報を持つようになります。
備考
- スキャナーのみ。新しい依存関係なし、ルート / CSP / SSRF の変更なし。テスト:
tests/sources-getro.test.mjs(+5)。スイート: 2470 (+5)。
[1.180.0] — 2026-08-14
修正 (中, レポート) — #/reports 一覧がテーブルになり、Machine Summary のプレースホルダーが隠していた実スコアを復元します。
修正
#/reports一覧は 4カードのグリッドではなくテーブル(レポート · 日付 · 信頼性 · スコア)になりました。 長い「スコア未検出」チップがタイトル列をほぼゼロまで押し縮め、カードタイトルのoverflow-wrap: anywhereがレポート名を1文字ずつ改行していました。各フィールドが独自の列を持ち、名前セルは単語単位で折り返し、狭い画面ではテーブルが横スクロールします(新しい.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ヘルパー)を持っており、これらのコピーはドリフトしていました(3 つはRangeErrorを投げ得た、v1.172.0 で修正済み。他は NUL/C0 を許容、またはa2;を誤解析)。今はすべて単一のserver/lib/html-entities.mjs(XML 1.0 Char 安全なデコーダー)を通り、~237 行の重複を削除しました。8 つの RSS 系ソースは のデコードを獲得(以前は 5 種のみ)、cryptocurrencyjobs の意図的な二重デコードは別名で保持。hhは自前デコーダーを維持(—/–を扱う、共有 6 種の外)。新しいガードテストは、いずれかのソースがローカルデコーダーを再生成すると失敗します。
備考
- 動作保存のリファクタリング。ルート / CSP / SSRF / 親書き込みの変更なし。テスト:
tests/decoder-consolidation.test.mjs(+2)。スイート: 2458 (+2)。
[1.178.0] — 2026-08-13
修正 (LOW, 親パリティ) — 古い定数 2 つを親に合わせて更新(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ı。本番ではライブファイルが既に提供していました。
備考
- 定数 2 つのみ。ルート / 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')なので、セッションクッキーが第三者に届くことはなく、クッキーを設定しないテナントは従来どおり動作します。
備考
- 新規
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)。
修正
- 2 つの 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 説明の回帰ガード + null 安全な正当性ストリップ(AI レビューのフォローアップ)。
修正
- SEO 説明のパリティガード — 各言語の
meta.descのハードコード “~55” をレジストリ由来の{adapters}プレースホルダーに置き換えた v1.174.0 の修正にはテストがなく、次のロケール編集で静かに退行し得ました。新しい CI 分離テストtests/site-meta-desc-parity.test.mjsは、17 個のsite/src/i18n/*.jsonのいずれかがプレースホルダーを失う/件数を再ハードコードする、またはLanding.astroが 3 つの説明メタへの差し込みをやめると失敗します。 - null 安全な正当性ストリップ —
stripEmphasisは null 入力に対して文字列 “undefined” ではなく''を返します(フィールドは文字列で初期化されるため多層防御)。
備考
- テスト + パーサーの 1 行ガード。ルート / 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を超える参照(不正・敵対的なフィードの�など)は捕捉されないRangeErrorを投げ、そのソースの解析全体を中断させました。共有モジュールserver/lib/html-entities.mjs(親プロジェクトの_html-entities.mjsを反映)が数値参照を XML 1.0 §2.2 Char 集合に制限し、String.fromCodePointが決して投げないようにし、16進と10進を別々にマッチするためa2;が誤解析されなくなりました。3つのソースがこれをインポートします。
備考
- 有効なフィードでは動作変更なし。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 リテラルを移行。値は保持され、重なりは同一; 新しいカナリアがベアな 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 は「5 つの評価次元に加えて総合的なグローバルスコア」と述べていますが、Web UI・cvstart.org・wiki はいずれも「6 次元ルーブリック」と表記していました(5 + 1 = 6 だが用語が不一致)。
修正
- ドキュメントの表現 — 「5 つの評価次元 + 総合スコア」 — を README ×17、cvstart.org サイト ×17、ヘルプガイド ×17、
docs/career-ops-canonical.md、wiki(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 キーは「Web UI では使われない」と記載し、ダッシュボードは「Anthropic-first scoring」と表示していました — 7 プロバイダーカスケード(v1.157.0)以降は誤り。
修正
config.providerModelNote(×17): ⚡ ライブ評価は 7 つのプロバイダーキー(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 SummaryYAML ブロック(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
修正 — 表示上の軽微なバグ2件(タブタイトルに漏れる «?» と、ランディングの誤ったプロバイダー数)。 表示のみで、動作・セキュリティ・データフローの変更なし。
修正
- 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+ 2 つのディスパッチ)。 - クライアントのゲーティング(
#/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 行でした。挙動を保つ 2 ファクトリを抽出し 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 テストを付け替え。大きい 2 ビューとも 800 未満に(P-15/P-16 完了)。スイート:2396 件のテスト。
[1.155.0] — 2026-08-12
リファクタ — config.js をサイズ上限内に分割(P-15)。 config.js は 1030 行(800 行上限超過)でした。挙動を保つ 2 モジュールを抽出し 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 セクション、Wiki ページとして提供。
追加
- ヘルプ §31「スタック全体をクラウドで動かす」(× 17)—— 3 つの構成要素、プロビジョニング + インストール、エンジン選択、安全な公開(HTTPS リバースプロキシ + 認証 + CSP/SSRF/XSS/秘密を残さない不変条件)。ヘルプは 31 H2 / 112 H3 に拡大。
- README — 「スタック全体をクラウドで動かす」節(× 17)+ Wiki の 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を付与(読み取り専用・後方互換)。
備考
- セキュリティ — 2 ホスト(
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)' }を削除 — 3つのプロパティはすべて共有の.emptyクラスが既に提供します(--space-7= 48px、中央揃え、淡色、破線ボーダー)。これでこの4つは他の約25個の.emptyパネルと同一に描画されます。- ビュー固有の正当な上書き(
#/dashboardwidth:100%、#/pipelineborder: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)。
[1.148.0] — 2026-08-12
見やすくなったスキャンフィルター (Phase 4) — フィルターパネルが整然としたグリッドに — #/scan のフィルターパネルが、幅バラバラの箱による不揃いなflex-wrapからレスポンシブグリッドになり、適用 / リセットのアクションは右寄せの独立した行に配置されました。フィルターも挙動も同じ — 読みやすさだけ向上。デザインの手直し(parent-syncなし)。
変更
#/scanフィルターパネル → レスポンシブグリッド —.scan-filtersはrepeat(auto-fill, minmax(180px, 1fr))の列と均一な間隔を持つdisplay: gridになり、ラベル付きの11個のフィルターがどの幅でも不揃いに折り返さず整った列に揃います。- 適用 / リセットのアクションはグリッド全幅の独立した行を占め、細い罫線で区切られ右寄せに。
scan.jsの旧・隠しラベルのハック + 内部flexラッパーを削除。
補足
- CSS + わずかなDOM整理のみ — フィルターのid(
#scan-filter-*、#scan-apply)とSR.render()の配線は不変なので、Playwrightのフローは無傷。新しいi18nキーなし。 - ブラウザ検証済み(コンソールエラー0);
tests/scan-filters-grid.test.mjsで保護。 - スイート:2381件のテスト(+3:
tests/scan-filters-grid.test.mjs)。
[1.147.0] — 2026-08-12
Hermes & Telegram — アプリ内ヘルプセクション + cvstart.org 面(Phase 5b・第2部) — Hermesドキュメント作業の2つ目にして最後の部分です:手順が17言語すべてでアプリ自身のヘルプガイド内に収まり、アプリ内ドキュメントアシスタントがそれを基にHermesの質問に答えます。引き続きドキュメントのみ — HermesのLLMプロバイダー経路は 計画中 / 未接続 のままです(Phase 5)。
追加
- アプリ内ヘルプ §30「Hermes & Telegram」× 17言語 — 新しいガイドセクション(Hermesとは + 2つの統合形態;クラウドサーバーでの実行;Hermes経由のTelegram + 「公開してはならないもの」ルール)。
#/helpからアクセス可能。docs-assistant/DocsFabのグラウンディングが自動的に取り込みます(どちらもdocs/help/<lang>.mdを読むため)。 - cvstart.org — Hermesガイドへのリンク。GitHubのドキュメントへ誘導。
変更
- ヘルプバンドルのゲートを 29 → 30 H2 / 105 → 108 H3 に引き上げ(
canonical-docs-coverage、help-ui、help-ru-config-section);§30がH3を3つ追加。
補足
- まだHermesを呼び出す処理はありません。 新しいカナリア
tests/help-hermes-section.test.mjsが、すべての言語に§30が言語非依存のアンカー(docs/integrations/HERMES.md、hermes-bridge、#/help、127.0.0.1、Telegram)とともに存在することを検証します。プロバイダーはPhase 5のAPI契約スパイクによって引き続きブロックされています。 - これでPhase 5bのドキュメント + スキル成果物が完了します;プロバイダー統合(Phase 5)は別個のブロック中の項目として残ります。
- スイート:2378件のテスト(+2:
tests/help-hermes-section.test.mjs)。
[1.146.0] — 2026-08-12
Hermesエージェント + Telegram — 統合ガイドとスキル(Phase 5b・第1部) — career-ops-uiをクラウドサーバーで動かし、そのイベント(完了したスキャン、新しいレポート、緊急のフォローアップ)をNous ResearchのHermesエージェント経由でTelegramに橋渡しできます。このリリースでは設計+デプロイのドキュメントとhermes-bridgeスキルを提供します。HermesのLLMプロバイダー経路は依然として計画中/未接続のままです(Phase 5のAPI契約スパイクに阻まれています)。意図的にコードより先にドキュメントを整えています。
追加
docs/integrations/HERMES.md— 詳細解説:2つの統合形態(OpenAI互換エンドポイント vs. エージェントランタイム)、クラウドサーバーへのデプロイ(reverse proxy+HTTPS+systemd、ヘッドレス機での読み取り専用のparent契約)、Hermes経由のTelegram連携、そして脅威モデルの「絶対に公開してはいけないもの」リスト(CV/給与/レポート本文/キーをチャンネルに出さない)。- READMEの
## Hermes agent + Telegramティーザー — 英語版READMEに短い案内とリンクを追加し、完全に翻訳された各言語のREADMEにも反映。 - ガイドを実行可能にする**
hermes-bridgeスキル**(.claude/skills/hermes-bridge/) — 前提条件+スコープゲートの確認(Node ≥ 18、キーの有無、SSRF安全な経路でのエンドポイント到達性)、秘密情報をディスク/ログに書き込まない、Hermesのエンドポイントを勝手に作り出したりプロバイダーが接続済みだと主張したりしない。 docs/architecture/OVERVIEW.mdのIntegrationsセクションがガイドにリンク。
補足
- まだHermesを呼び出す処理はありません。 カナリアテスト(
tests/hermes-docs.test.mjs)が「計画中/未接続」という正直な表示と、llm-dispatch.mjsにHermes/Nousの分岐が存在しないことを検証しています — つまり将来プロバイダーを接続する際は、同じ変更でドキュメント+ロードマップも更新する必要があります。 - v1.147.0へ延期(Phase 5b・第2部):アプリ内ヘルプの「Hermes & Telegram」H2 × 17言語、およびcvstart.orgのマーケティング面。
- スイート:2376件のテスト(+4:
tests/hermes-docs.test.mjs)。
[1.145.0] — 2026-08-12
役立つ統計(続き):再構築できるグラフ — #/stats の「目標職種トレンド」タブに グラフを作成 ウィジェットが追加:指標 × ディメンションを選ぶとライブで描き直します。ユーザーからのUX要望(parent-syncなし)。
追加
- 指標 × ディメンションの再構築グラフ — 指標(求人数 / 給与の中央値 / 平均給与)と ディメンション(国別 / 職種別)を選ぶと棒グラフが即座に描き直されます。給与系の指標は通貨+年/月切替に従い、求人数は単純なカウントです。
- 8個の新しいi18nキー × 17ロケール;スナップショット 1200 → 1208。
補足
- ブラウザで確認(コンソールエラー0)。スイート:2372 テスト(+2)。
[1.144.0] — 2026-08-12
設定とフィルター(フェーズ4・第1部):追跡ポータルの有効化/無効化 — #/portals から監視中の企業をオン/オフでき、スキャナーがそれを反映します。ユーザーからのUX要望(parent-syncなし)。
追加
#/portalsの企業ごとの有効化/無効化ボタン — ワンクリックでポータルをオフにでき(ENスキャナーは既にenabled: falseの企業をスキップするため、無効なポータルは以降のすべてのスキャンから外れます)、また再度オンにできます。楽観的トースト付き。POST /api/portals/toggle—portals.ymlの企業のenabledフラグを外科的に(パース検証付きで)切り替える明示的なユーザー書き込み(コメント・順序・その他のフィールドは保持)。5個の新しいi18nキー × 17ロケール;スナップショット 1195 → 1200。
補足
- スキャナーの変更はゼロ —
en-scanner.mjsは既にenabled !== falseでフィルタリング済み。スイート:2370 テスト(+3)。
[1.143.0] — 2026-08-12
わかりやすく(続き):主要ワークフロー画面の ? ヒント — ヘルプ ? が9つの主要な操作ページを全言語でカバーするようになりました。ユーザー報告のUX改善(parent-syncなし)。
追加
- さらに9つの画面タイトルに
?ヘルプヒント —#/scan,#/evaluate,#/cv-studio,#/tracker,#/config,#/deep,#/batch,#/auto,#/applyにインラインの?(HelpHint.title)が付き、翻訳済みの「何を/どう使う/何が得られる」ポップオーバーを開きます — v1.139.0 の CSP 安全なコンポーネントと同じです。 - 9個の新しいi18nキー × 17ロケール(
help.hint.scan/…/apply);スナップショット 1186 → 1195。
補足
- ブラウザで確認(コンソールエラー0)。スイート:2365 テスト(+1)。
[1.142.0] — 2026-08-12
修正:「Unknown」キャリア・アーキタイプの解消 — #/orientation が、時折「Unknown」と答えてそれに「注力せよ」と勧める代わりに、常に名前付きの8つのキャリアベクトルからランク付けするようになりました。ユーザー報告の修正(parent-syncなし)。
修正
#/orientation— AIプロンプトが集合外のアーキタイプを禁止。 モデルは正確に8つの名前付きベクトルから上位3つを選ぶ必要があり、「Unknown」「N/A」「データ不足」と答えたり新しいラベルを作ったりできません。履歴書が薄い場合でも、最も近い3つを低い確信度で挙げ、欠けている根拠を示します。
補足
- サーバープロンプトのみの変更(
buildOrientationPrompt)。i18n/スキーマ変更なし。スイート:2364 テスト(+1)。
[1.141.0] — 2026-08-12
役立つ統計(続き):資金調達企業の情報強化 — #/funded がより視覚的になりました:企業ロゴ、調達額チャート、ラウンド/金額/発見スコア/推奨アクションを含むカード。ユーザー報告のUX改善(parent-syncなし)。
変更
#/funded— 平坦な表 → カードグリッド。 直近に資金調達した各企業が、ロゴ(企業名から導出、失敗時は文字アバター)、ラウンド+金額チップ、親プロジェクトの発見スコアと推奨アクション、そして資金調達ニュースのリンク・日付を持つカードになりました。- 調達額の可視化 — 開示額による上位企業の横棒グラフ。“$120M”/“€1.5B” のような自由記述の金額を新しい
parseAmountで大きさに変換します。3個の新しいi18nキー × 17ロケール。
補足
- 依然として
GET /api/company-funded上の読み取り専用です。説明と給与レンジは資金調達フィードにありません。スイート:2363 テスト(+2)。
[1.140.0] — 2026-08-12
役立つ統計:より豊かな給与数値 — #/stats の「マイパイプライン」給与内訳に、(中央値だけでなく)平均、年間 ⇄ 月間切替、国別の最小・平均・中央値・最大表を追加しました。Phase 3 の最初の一片。ユーザー報告のUX改善(parent-syncなし)。
追加
- 平均給与 —
RoleStats.salaryStatsがminUsd/medianUsd/maxUsdに加えてavgUsdを返すようになりました。中央値は外れ値に強く、平均は偏りを示すため、両者は分布として読めます。 - 給与セクションの年間 ⇄ 月間切替と、国別の最小・平均・中央値・最大の表(通貨・期間セレクター連動)。8個の新しいi18nキー × 17ロケール。
補足
- 数値は依然として給与を解析できる求人からのみ算出され、USDに正規化されます(参考値)。スイート:2361 テスト(+1)。
[1.139.0] — 2026-08-12
わかりやすく:? ヘルプヒント — クリックすると「何をするか / どう動くか / 何が得られるか」をあなたの言語で説明する、再利用可能でCSP安全な ? ボタン。ユーザー報告のUX改善(parent-syncなし)。
追加
?ヘルプヒントのポップオーバー(window.HelpHint)— 見出し横の丸い?をクリックすると、テーマ対応でRTLミラーされた軽量ポップオーバーが開き、UI.md()で翻訳済みの説明を表示します。アクセシブル(role="tooltip"、aria-expanded、Escape/外側クリックで閉じ、フォーカス復帰)かつCSP安全です。#/statsの5つのタブと 8つのAI/分析ビューのタイトル(career-plan、オリエンテーション、two-pager、ネットワーキング、模擬面接、メモリ、funded、週次ダイジェスト)に?を追加 — 14個の新しいi18nキー × 17ロケール。
補足
- すべてのビューには既に1行の説明がありました。
?はより深い説明をオンデマンドで加え、空の状態も自己説明的にします。スイート:2360 テスト(+4)。
[1.138.0] — 2026-08-12
インターフェース言語での生成 — すべての AI 生成が UI で選択した言語で返るようになりました。加えてレビュー由来のテスト強化。ユーザー報告の UX 改善(parent-sync なし)。
変更
- AI 生成が UI 言語に従うようになりました。 インターフェースをロシア語・スペイン語・日本語などに設定すると、生成テキストが常に英語ではなくその言語で返ります。出力言語ディレクティブがすべての生成エンドポイント — キャリアプラン、オリエンテーション、市場レポート、模擬面接、ネットワーキングプラン、「ドキュメントに質問」、メモリノート提案、two-pager 下書き — に伝わります。コードと識別子は英語のまま(例:two-pager の YAML キー)で、散文・見出し・箇条書きのみ翻訳されます。
修正
- CSS カラーロール・ガード(
tests/css-role-tokens.test.mjs)— v1.137.0 のダークモード別名トークンが役割を反転しないことを検証する静的カナリア:テキスト役トークン(--fg/--danger/--ok/…)はbackgroundに、サーフェス役トークン(--card/--panel/--line/…)はテキストのcolorに決して使われない(全 CSS と SPA のインラインスタイル)。 UI.md()XSS ローダーの自己プローブ —api.jsからmd()を読み込むテストが抽出直後にmd('<script>…')を検査し、エスケープが欠けていれば例外を投げるため、将来の切り出しミスがセキュリティスイートを緑にせず大きく失敗します。#/career-planのスクロールガード — 生成後のscrollIntoViewはプレビューがまだドキュメントに接続されている場合にのみ実行されます。
補足
docs/UX-ROADMAP.mdを更新:?ヘルプヒント+ページ説明+空状態の作業は v1.139.0 へ。Nous Research / Hermes プロバイダ(クラウドサーバー+Telegram デプロイガイドと Hermes スキルを含む)は Phase 5 / 5b として追跡。- スイート:2356 テスト(+5)。
[1.137.0] — 2026-08-11
可読性・レンダリングの修正 — ダークモードのコントラスト、チャートのラベル、キャリアプランに関する修正です。ユーザー報告に基づく UX 対応です(parent-sync ではありません)。
修正
- 多数の画面でダークモードの白地に白文字・黒地に黒文字が発生する問題 — 複数のビューが参照していた15個の CSS カスタムプロパティ(
--fg、--panel、--panel-2、--ok、--danger、--card、…)が一度も宣言されておらず、ハードコードされたライト/ブラック値にフォールバックしていました:ライトモードでは問題ありませんでしたが、ダークモードでは判読不能でした(#/pipelineの概要チップ、#/statsのアクティブタブ、#/configの “Active / Keys” と “✓ set”、#/two-pagerの各セクション、#/mock-interviewの質問バブル、エラーテキスト)。現在は実際のテーマ対応トークンにエイリアスされ、テーマに自動追従するようになりました — 自動監査ツールによる検証で全29ビューにわたり WCAG-AA コントラスト違反ゼロ、#/configのアクティブタブは判読しやすいティント配色に変更されました。リグレッションガード(tests/dark-theme-tokens.test.mjs)がエイリアスを維持します。 #/statsのチャートラベルが単語の途中で切れていた問題(「Senior Backend Engineer」→「…Enginee」)— 現在は省略記号で切り詰められ、完全なラベルはホバー時のツールチップで確認できます。#/career-planが生成されたプランを Markdown の生テキストのまま表示していた問題 — 現在は整形済みの読みやすいテキストとして自動的にレンダリングされるようになりました(編集可能な Markdown はテキストエリアに残り、Preview で切り替えられます)。
補足
#/career-plan、#/two-pager、#/stats、週次面接ダイジェストは壊れているわけではありません — プランを生成する / データが揃うまでは空の状態を表示します。次はページ上のガイダンスと?ヘルプヒントをより明確にする予定です(docs/UX-ROADMAP.md)。
[1.136.0] — 2026-08-11
親 career-ops v1.26.x パリティ(v1.26.0 以降のメインライン)— 1つの新しい認証不要スキャンソースに加え、web-ui のミラーに向けた品質・堅牢性の改善が一連続きます。レジストリは 79 ソース = 74 EN + 5 RU(ALL_ADAPTERS 74)になりました。
追加
eightfold(Eightfold AI、#2684)— 認証不要のhttps://<tenant>.eightfold.ai/api/apply/v2/jobsAPI を介したタレントアクイジションボードで、*.eightfold.aiにホスト固定されています(ブランド化されたcareers.<company>.comの CNAME は意図的に拒否されます)。安全上限付きのページネーション、停止ボードの例外化、URL 重複排除を備えます。ソース + アダプター + CI分離型のテストスイートを追加し、#/scanの Source フィルターとランディングページに表示されます。
修正
- Unicode 対応の重複排除・役職キー(#2569 / #2587 / #2667)— 新しい共通
normalizeTextKey(NFKC、任意のスクリプトの文字・記号・数字を保持)がこれまでの ASCII 限定キーを置き換えました:detect-repostsは全角/句読点の企業表記の差異(「Acme, Inc.」≡「Acme Inc」)をクラスタリングできるようになり、非ラテン文字の異なる企業を誤って統合することもなくなりました。role-matcherは全角の職種名を正規化し、非ラテン文字の役職トークンを消去せずに保持するようになりました。 fetchJsonWithRetryが拒否されたリダイレクトをリトライしなくなりました(#2657)—redirect:'error'ガードが 3xx に遭遇するのは決定的な事象であるため、これはリトライ不可として扱われ、リトライ予算を消費せずに即座に失敗するようになりました。title_filter.positiveの AND グループ(#2552)— positive エントリ内の空白区切りの+は、順序を問わずすべての語がタイトルに含まれることを要求するようになりました。oraclecloudが番号付きテナントのアペックスドメインを受け付けるようになりましたoraclecloud1.com … oraclecloud99.com(#2683)— 範囲が定められたファミリー(先頭ゼロなし、2桁以下)であり、ワイルドカードのアペックスではありません。workableが堅牢化されました(#2675)— Cloudflare フロントのホストに対するリトライ、ブラウザ風ヘッダー、リクエストの直列化を追加しました。personioは XML フィードが無効化されている場合、何も返さない代わりに HTML スクレイピングにフォールバックするようになりました。statesの FALLBACK エイリアスが親と再同期されました(#2615)。
補足
- 未移植(web-ui にミラーされていない、または CLI 専用のため):reply-matcher(#2672)、jd-similarity(#2661)、jd-skill-gap(#2686)、スキャンの環境変数パス(#2568)/
--flag=valueパース(#2589)、および cover-letter / CV テンプレート / doctor / ollama / generate-pdf の変更。Web 側のjs-yaml/nanoidの HIGH アドバイザリは、web-ui v1.135.0 で既に修正済みです。
[1.135.0] — 2026-08-11
親 career-ops v1.26.0 パリティ — 5つの新しい認証不要スキャンソースに加え、web-ui がすでに持つ4つのボードに対する正確性の修正です。レジストリは 78 ソース = 73 EN + 5 RU(ALL_ADAPTERS 73)になりました。
追加
- 5つの新しいスキャンソース(それぞれソース + アダプター + CI分離型のテストスイートを備え、
#/scanの Source フィルターと cvstart.org のランディングページに表示されます):join(JOIN)—join.com/companies/<slug>の Next.js__NEXT_DATA__から企業の JOIN ボードを取得します(ホスト固定、ページ数上限あり)。getro(Getro)— 公開のapi.getro.comPOST API を通じて VC の「タレントネットワーク」ポートフォリオボードを取得し、新着順にページネーションされます。各求人はファンドではなくポートフォリオ企業に帰属します。consider(Consider)— 同一オリジンの POST による getconsider.com の VC ポートフォリオボードです。設定駆動のホストは構造的な SSRF ガードで固定されています(公開 HTTPS ホストのみ)。joinup(JOINUP)— スイスのボード joinup.ch から SSR された最新ページを読み取ります。スクレイパーが壊れた場合はフェイルクローズします。remotli(Remotli)— remotli.ch、スイス企業のリモート職(CHF 建て給与)です。企業自身の ATS 応募 URL を出力するため、クロス掲載が重複排除されます。
修正
- a16z Speedrun が一時的な不具合でボード全体を中断しなくなりました — ページ取得は共通の
fetchJsonWithRetryを経由するようになり(一時的な 429/5xx/タイムアウトのみを対象に上限付きリトライを行い、恒久的な 4xx ではリトライしません)、ページ予算も50件ページに合わせて再調整されました。 - arbeitsagentur が v6 Jobsuche API(
/pc/v6/jobs)に移行しました — 旧 v4 エンドポイントは 404 を返すようになっています。レスポンス形状の項目名が変更され、リモートフィルタリングはサーバー側で絞り込まれるようになりました。 - thehub が v2 の
jobsandfeaturedAPI に移行しました。行に掲載日が含まれないため、経過日数フィルターの対象外となります。 - hackernews は、自由テキスト検索の代わりに
author_whoishiringアカウントタグで Algolia の検索を絞り込むことで、毎月の「Who is hiring?」スレッドを確実に見つけられるようになりました。
補足
- 未移植(web-ui はすでに安全、リレー側で吸収済み、または CLI 専用のため):Unicode 対応の役職重複排除 / 企業マッチングキー(web-ui の再掲グルーピングはすでに単純な小文字化で企業をキー化しているため、非ラテン文字の異なる企業が誤って統合されることはありません);followup の却下レイテンシーシグナル + company-funded の微修正(読み取り専用でリレーされ、フェイルソフトです);スキャンの環境変数上書き可能なパスと
--flag=valueパース(web-ui はスキャナーをインプロセスで実行します);User-Agent 統合のリファクタリング(web-ui はすでに一元化済みです);および CLI 専用の項目(信頼できないコンテンツの一覧、oferta/offer-prep、doctor、カバーレター/CV テンプレートの変更)。
[1.134.1] — 2026-08-05
検証の堅牢化 — プロジェクト全体の監査で見つかった修正です。
修正
successfactorsがスキャン中の失敗で収集済みの求人を破棄しなくなりました(v1.134.0 のデッドボード例外化の移植で生じた回帰)— ページネーションループにtry/catchがなく、2ページ目以降(1ページ目は成功済み)での失敗が例外を投げてしまい、すでに収集済みのものすべてが失われていました。さらに、その失敗が404(範囲外のstartrowによる)だった場合、en-scannerは稼働中のテナントを何日も停止扱いとして隔離してしまっていました。今後はphenom/radancyと同様の挙動になります。0ページ目での失敗は今も例外を投げます(ボード停止)が、それ以降のページでの失敗は部分結果を保持します。#/scanのフィルターチップがキーボードで操作できるようになりました(WCAG 2.1.1)— ファセットチップ(および「クリア」チップ)はクリックハンドラーを持つ span 要素でしたが、tabindexや role がなく、キーボードユーザーやスクリーンリーダーユーザーが到達・切り替えできませんでした。現在はrole="button"、tabindex="0"、aria-pressedを備え、Enter/Space キーでも操作できます。- 3つのハードコードされた英語文字列がローカライズされました—
#/scanの信頼バッジのツールチップ、#/scanの移転列ヘッダー、#/dashboardのスコア列ヘッダーは、キーとして存在しない裸のリテラルだったため i18n パリティゲートから見えず、英語以外のすべてのロケールで英語のまま残っていました。現在はscan.trustTip+scan.col.reloc(新規2キー)と、既存のtrack.col.scoreの再利用になり、ソース静的ガード(tests/scan-chip-a11y.test.mjs)で固定されています。 - +3 テスト → スイート 2187。
[1.134.0] — 2026-08-05
親 career-ops v1.25.0 パリティ。
追加
- 新しいスキャンソース: getManfred(
manfred)— 給与が公開されているスペイン/EU圏のテック職を横断的に集めるボード全体フィードで、www.getmanfred.com/api/v2/public/offersから取得します(認証不要、ホスト固定 + HTTPS 限定、単一リクエストで全カタログを取得)。ソース + アダプター + CI 分離型のテストスイート(tests/sources-manfred.test.mjs)を追加。レジストリは 73 ソース = 68 EN + 5 RU(ALL_ADAPTERS68)になりました。#/scanの Source フィルターと cvstart.org のランディングページに表示されます。
修正
- a16z Speedrun フィードが50件で静かに打ち切られていた問題(#2404)— このフィードは1ページあたり50件が上限ですが、ソース側は
PER_PAGE = 100を要求していたため、1ページ目でページネーションが停止していました。50に修正しました。 - 稼働していないボードが「稼働しているが空」として読み取られる問題を修正(#2379)—
cryptocurrencyjobs、phenom、radancy、successfactors:リクエストが一件も解決しなかったフェッチ失敗はこれまで空のリストとして飲み込まれていましたが、今後は例外を投げるようになり(#/portalsのヘルスチェックとスキャンが実際の失敗として記録します)、1件以上成功した後のスキャン途中の失敗は部分結果を保持します。 - workable が公開ウィジェットAPIを使うようになりました(#5ab8425)—
apply.workable.com/api/v1/widget/accounts/<slug>に切り替えたことで、大規模アカウントの求人一覧を1リクエストで全件取得できるようになり、大規模アカウントが打ち切られなくなりました。
補足
- 未移植(CLI専用、または web-ui にミラーされていないもの):
detect-repostsの #2389 タイトルバケット化によるパフォーマンス改善;Unicode 対応の会社キー修正(web-ui 独自のトラッカー重複排除はすでに非ラテン文字に対応済みのため不要);scan --since;cv-facts;CV テンプレート/PDF 監査パス;doctor;モードの信頼できないコンテンツディレクティブ。
[1.133.1] — 2026-08-02
修正
#/funded(資金調達企業)が結果を表示するようになりました — 二つのバグにより、親のcompany-funded.mjsが完全な一覧を返しているときでも、テーブルには常に「資金調達企業なし」と表示されていました。(1) ビューは結果をres.candidatesから読み取っていましたが、親はcompaniesの下にそれらを送出します(各項目は{ company, amount, round, funding: { sources: [{ source, url, observed_date }] } }という形です)。クライアントは正しいキーを読み取り、実際のエビデンス形状にマッピングするようになりました。(2) 結果テーブルはセルを可変長引数としてUI.el('tr', {}, …)に渡していましたが、UI.el(tag, attrs, children)はchildrenを単一ノードまたは配列として受け取るため、最初の列(Company)しか描画されていませんでした — セルは配列として渡されるようになりました。実機ブラウザで検証済みです:4つのフィード全体で11社が Company / Funding signal / Source / Date の各列とともに描画され、エビデンスリンクも機能し、コンソールエラーはゼロです。結果が空のときも、ソースごとの診断情報が表示されるようになったため、静かなニュースの日とブロックされたフィードとを見分けられるようになりました。tests/parity-routes-v1133.test.mjsにリグレッションガードを追加しました:偽の親スクリプトが実際のcompanies出力形状を送出するようになりました(元のフィクスチャは誤ったcandidates形状を反映しており、これこそがこのバグがグリーンのまま出荷された理由です)。加えて、funded.jsがres.companiesを読み取り(res.candidatesは決して読み取らない)、テーブル行を配列の children で構築していることを検証するソース静的カナリアも追加しました(+1 → 2144)。
[1.133.0] — 2026-08-01
追加
- 資金調達企業ディスカバリー(
#/funded、親パリティ #2117) — 親 career-ops のcompany-funded.mjsをGET /api/company-funded経由でリレーする新しい読み取り専用ビューです。公開されホスト固定された資金調達フィード(TechCrunch、PR Newswire、The Guardian、Hacker News)から発見された、最近資金調達を行った企業のレビューファーストな一覧を表示します。このリレーはスクリプトを--json --dry-runで実行し(JSON を標準出力へ、ファイル書き込みなし)、ユーザー入力を--sourcesへ決して引き渡さず、レートリミットを備え、ユーザートリガー式です(マウント時ではなく Discover ボタンによって起動)。新しいルートモジュールserver/lib/routes/funded.mjs+public/js/views/funded.jsが Sourcing 配下に追加されました。 - 週次面接ダイジェスト(
#/interview-digest、親パリティ #2129/#2130) — 親のゼロ LLMweekly-digest.mjsをGET /api/interview/weekly-digest経由でリレーする新しい読み取り専用ビューです。面接セッションのメモを機械的に集約し、今週どの企業・どのラウンドで面接を行ったか、繰り返し登場するコンピテンシー、ベストエフォートで抽出した未解決のギャップを示します。オプションの?from=&to=範囲は両方とも有効なYYYY-MM-DDの場合にのみ引き渡され、範囲が空でも有効なavailable:trueダイジェストになります。server/lib/routes/interview.mjs+public/js/views/interview-digest.jsに追加され、Analytics 配下に置かれます。 - 両リレーとも、親スクリプトが存在しない場合(CI、スタンドアロンインストール)には既存の fail-soft な
available:falseコントラクトに従います。新規 i18n キー 26 個 ×17 ロケール、CI 分離型スイートtests/parity-routes-v1133.test.mjs(+5 → 2143)。
補足
- 親 career-ops は v1.24.0 以降、Next.js の web/ アプリに Follow-up Tracker ページ(#1422)とバックエンド PDF レンダリング(#2182)を追加しましたが、これらは移植していません — web-ui にはすでに独自のフォローアップリレーと PDF ランナーがあり、基盤となる
followup-cadence.mjsの堅牢化はシェルアウトリレー経由で自動的に取り込まれます。set-status.mjs/tracker-utils.mjsの変更は CLI 内部のものであり、ミラーしません。
[1.132.0] — 2026-07-31
変更
#/scanの結果描画サブシステムをpublic/js/lib/scan-results.jsに抽出(ファイルサイズ規約の負債解消 —public/js/views/scan.jsは約1254 LOCまで肥大化していました)。renderResults、buildChipRow、行/ファセットビルダー群、オプションペインター群、FALLBACK_SOURCESレジストリのミラーからなるこのサブシステムは、ビュー側が渡すコンテキストオブジェクトをクロージャで保持するwindow.ScanResults.create(ctx)ファクトリへ移動しました。挙動の変更は一切ありません — 各関数はそのまま移動し、クロージャ変数をctx.*に付け替えただけです。scan.jsは現在約906 LOCです(800-LOC目標に向けた2回目の抽出パスを予定)。- 新しいブラウザ内リグレッションゲート —
tests/playwright-scan-filters.mjsが既定のdata/last-scan.jsonをシードし、#/scanの全フィルターを駆動して行数を厳密に検証するため、この抽出は実ブラウザの挙動に対して検証されています。 - README バナーをスリム化 — バージョンごとの長大な「最新リリース」ナラティブの壁を廃止し、1行の要約 + 完全な変更履歴(本ファイル)へのリンクに置き換えました。
[1.131.2] — 2026-07-31
変更
app.cssを3つの順序付きスタイルシートに分割(ファイルサイズ規約の負債解消 — 単一ファイルが約1990 LOCまで肥大化しており、800-LOCのハード目標を大きく超えていました)。現在はapp.css(約672 LOC — アクセシビリティ、デザイントークン/テーマ、サイドバー、メイン、ボタン、コンテンツシェル)、components.css(約595 LOC — カード、グリッド、ページネーター、バッジ、テーブル、フォーム、ログ/コンソール、Markdown、言語スイッチャー、チップフィルター、接続バナー)、overlays.css(約737 LOC — トースト、通知ドロワー、モーダル、その他/レスポンシブ、[dir="rtl"]ミラー、docs-fab、usage-hud)の3つとなり、いずれもハード上限に収まっています。- 分割は連続的かつ順序どおりに行われているため、カスケードは分割前のファイルとバイト単位で完全に同一です。
index.htmlはこの3つを順序どおりの<link>として読み込みます。挙動・マークアップ・i18nの変更は一切ありません。 - CSSを検証するテストは、共有ヘルパー
tests/helpers/css.mjs::loadAppCss()経由で連結結果を読み込むようになりました。新規のtests/css-modularization.test.mjsが分割を固定します(ファイルの存在・各ファイルが800 LOC以下であること・index.htmlのリンク順序) → スイート 2138。ブラウザ上での検証も実施済みです: 3つのスタイルシートすべてが正しくパースされ、そのルールが適用されます。
- 分割は連続的かつ順序どおりに行われているため、カスケードは分割前のファイルとバイト単位で完全に同一です。
[1.131.1] — 2026-07-31
修正
- v1.130.0 の2つのソースにおけるアダプターのホスト固定の一貫性強化(コードレビュー対応、多層防御。有効な入力に対する挙動変更はありません):
a16z-speedrun-talentアダプターがapi:/a16z-speedrun-talent:のオーバーライドをbuildEndpointで再検証するようになりました(HTTPS 限定、厳密なホストspeedrun-talent-network.com)。検証に失敗した場合は正規フィードへフォールバックします —cryptocurrencyjobsアダプターとのパリティであり、ホスト外の値がフェッチ処理に到達することはもうありません(以前はフェッチ時点のassertSpeedrunUrlガードのみに依存していました)。厳密ホストチェックは、ガードとアダプターで共有される単一のエクスポート済みSPEEDRUN_TALENT_HOST_REになりました。cryptocurrencyjobsパーサー —cleanUrlがassertCryptocurrencyJobsUrlおよびアダプターのオーバーライドと同じ厳密一致のホストガードを使うようになりました(以前はendsWithで、サブドメインも許容していました)。パーサーが SSRF ガードより緩くなることはありません:sub.cryptocurrencyjobs.coのアイテムリンクは破棄されます。- +2 テスト → スイート 2135。
[1.131.0] — 2026-07-31
追加
#/trackerの CRM ステージタブボード(親 Web アプリの/pipelineビューを移植)。トラッカーのファネルチップバー+ステータスドロップダウンがステージタブ帯に置き換わりました。All タブに加え正規ステータスごとに 1 つずつのタブ — Evaluated · Applied · Responded · Interview · Offer · Rejected · Discarded · SKIP · Hired — が並び、それぞれ全履歴のライブ件数を表示します。ゼロ件のステージも含むため、ファネル全体が常に見渡せます(CRM らしい見た目)。アクティブなタブがフィルタを駆動し、同じタブを再度クリックすると All に戻ります。行はスコアトーン・正当性・PDF・レポートの操作性をそのまま保持し、ロゴを有効にすると企業セルにブランドロゴが表示されるようになりました(デフォルトはオフ → 追加リクエストはゼロ)。- 新しい読み取り専用ルート
GET /api/tracker/stagesが、正規のファネル(順序付きラベル)+エイリアス畳み込みマップを返します。ソースはserver/lib/states.mjs(templates/states.yml、組み込みフォールバック付き)— これによりクライアントはステータスのホワイトリストを一切ハードコードしません。パラメータなしの従来のGET /api/trackerレスポンスは変更なし({ rows }のみ)。 - 新しい純粋関数でユニットテスト済みのクライアントライブラリ
public/js/lib/tracker-stages.jsが、行をサーバーのステージに振り分けます。余分な markdown 太字やローカライズ済みエイリアス(例:aplicado→Applied)も許容します。タブはアクセシブルです(role tablist/tab、aria-selected、44px 以上のヒット領域、各タブのアクセシブルネームに件数を含む)。新規 i18n キーなし。スイート 2133。
- 新しい読み取り専用ルート
[1.130.0] — 2026-07-31
追加
- 親 career-ops v1.24.0 から新スキャンソース2件を移植(インプロセス、新規依存なし;いずれも
#/scanの Source フィルターと cvstart.org のランディングページに表示されます):- a16z Speedrun(
a16z-speedrun-talent、#2231)— a16z Speedrun の タレントネットワーク ボード全体 JSON フィード。speedrun-talent-network.comにホスト固定、HTTPS 限定、0 始まりのページネーション(上限あり)、企業別のq/設定引き回し、fail-soft。 - Cryptocurrency Jobs(
cryptocurrencyjobs)— Web3 求人ボードcryptocurrencyjobs.coを、公開 RSS 2.0 フィード経由で取り込み(認証不要)。二段階の XML エンティティデコード、リモート限定の求人、タイトル末尾の"… at <Company>"から雇用主を抽出。 - レジストリ合計は 72 ソース(67 EN + 5 RU) になりました(
ALL_ADAPTERS= 英語圏ポータルアダプタ 67 件)。
- a16z Speedrun(
修正
echojobs— ハイブリッド勤務がリモートと区別されるように(親 #2258 に準拠)。大文字小文字を区別しないhybridマーカーが検出されると、Remoteに丸め込まれる代わりに"<City> · Hybrid"(都市名がない場合は単にHybrid)とworkplaceType: 'Hybrid'を返すようになりました。radancy— レガシー TalentBrew マークアップ + JSON 結果フラグメント転送(親 a3e6df9 に準拠)、注入可能なopts.fetchJsonでゲート。
補足
- 未移植 — CLI 専用の親機能。 career-ops v1.24.0 の大規模な CLI/モード面は web-ui の対象外のままです。web-ui はビューア+薄い書き込みスルーであり、モードホストではありません:コンプライアンス/管轄区域テーブル、連絡先電話帳 + vCard、面接トランスクリプトのデブリーフ/通話プラットフォーム検出、台帳の set-status、結果記録、二段階トリアージ、jd-similarity、バージョン管理された応募先 CV アーティファクトのスキーマ、doctor の Playwright-MCP 検出、
portals/fix-slugs.mjs。親のscan.mjsにあるスキャンオーケストレーションの変更 — Interamt.de の Playwright スキャナー、iCIMS リバース ATS のフルスイープ、国籍適格性によるリモートフィルター、DNS ルックアップのペーシング、StepStone のrltr重複排除、スキャン履歴の正規化された会社カラム — も対象外です:web-ui は EN/RU スキャナーをインプロセスで実行しており、scan.mjsへシェルアウトしません。 - すでに対応済み。
role-matcherのアクセント畳み込み修正(#2209)は v1.127.0 で移植済みのため、ここでは no-op です。
[1.129.1] — 2026-07-29
修正
- v1.128/v1.129 の web 移植への AI レビュー対応(すべて助言、根本修正):
job-facets.jsのレベル優先順位(明示的な修飾語がマネジメント語に勝つ —Senior Engineering Manager→senior、以前はlead);states.mjsのフォールバックがピン留めされなくなった(成功読み取りはメモ化、フォールバックはキャッシュせず返す — 起動時に一時的に不在の親は次回再読込)+ 存在するが不正なファイルでconsole.warn;score-tone.js— スコアなしの行は中立(muted)で赤ではない;domainFromName()は/api/logoの前に非 ASCII スラッグをスキップ;+tests/states.test.mjsの分離ガード。+4 テスト → 2073。
[1.129.0] — 2026-07-29
追加
#/scanレベルファセット + 経過カラム — v1.128.0 のjob-facets.jsライブラリがスキャン UI に接続されました(以前はロジックのみ)。新しい レベル ドロップダウンが各求人タイトルを lead/staff/senior/mid/junior/intern に分類(JobFacets.seniorityFromTitle)し、結果に実在するものから自動生成します(Country ファセットと同様);レベル語のないタイトルは常に通過。保存検索・リセット・適用に保持されます。結果テーブルに レベル バッジ列と無トークンの 経過 列(今日/N日、JobFacets.daysSince)が追加されます。i18n キー 12 個 ×17、+3 テスト → 2069。
[1.128.0] — 2026-07-29
追加
- 親プロジェクト自身の Web アプリ(
../web/, Next.js)から 4 つの解決策を移植 — バニラ JS/ESM で再実装、新規依存なし:(1)server/lib/states.mjsがtemplates/states.ymlをトラッカーのステータス語彙の唯一の情報源としてライブ読み取り(CI フォールバック付き)— 毎リリースの手動ホワイトリスト再同期を廃止;POST はエイリアス(スペイン語/レガシー)を正規ラベルへ畳み込み、GET のファネルは正規ステータスでバケット;(2)ATS ホスト行の会社ロゴ —domainFromName()(約 90 ブランド→ドメイン);(3)score-tone.js— 4 段階スコアトーン(≥4.2/3.8/3.0 + 文字フォールバック);(4)job-facets.js— seniority/source/days ファセット。+21 テスト。
補足
- 未移植(概念のみ):親のエージェント的アクション層(
actions/registry.ts+api/assistant/route.ts)—docs-fabをコパイロット化する際の設計図。新規ソースなし(レジストリ 70)、i18n/help 変更なし。
[1.127.0] — 2026-07-29
追加
- 3 つの新スキャンソース(career-ops v1.23.0 パリティ) — レジストリは 70 アダプター(65 EN + 5 RU) に: Flowxtra(無認証の全体アグリゲーター)、VDAB(フランダース公共雇用サービスのキーワード API)、iCIMS(
careers-<tenant>.icims.comポータル、jibeapplyとは別)。さらに Cursor が CLI ロスターに復帰(parent #2115):cli-detectがcursorも検出(10 ツール)し、help/README/config ×17 にロスターを復元。
修正
- agenticjobs を HTML スクレイピングから REST API へ(#2167);Greenhouse は
location.nameが勤務形態のみのとき/officesから都市を復元(#2104);role-matcher パリティ(#1933/#2164/#2009: MTS 接頭辞、productベースライン、アクセント畳み込み、サブベースライン不一致)。
補足
- 未移植。 v1.23.0 の大半は web-ui が使わない CLI/ダッシュボード面(batch-tailor、discover-ats、NL/PT モード、PDF テーマ、Go ダッシュボード、updater/doctor);リレースクリプトは変更不要。親 VERSION → 1.23.0。
[1.126.1] — 2026-07-25
修正
- v1.126.0 の再同期が見落とした CLI ロスターのドリフト 2 箇所 — (1)
#/configの API keys タブ導入文(config.providerModelNote、i18n ×17)が 7 CLI しか列挙していなかった — Antigravity と Grok Build を OpenCode の後に挿入; (2) ヘルプガイド(×17)の 2 つ目の比較表の行と CI ビルドのサイト help がまだInside Claude Code / Codex / Cursor / Gemini CLI(旧 Cursor 入り)だった — 全ロスターに修正。どちらも v1.126.0 スイープのパターンが対象外だったスラッシュ/中黒区切り。i18n スナップショット再生成、スイートは 1969 維持。
[1.126.0] — 2026-07-25
追加
- AI CLI ツールタブが career-ops の 8 つの一級 CLI をすべて検出 —
#/configのロスターを親のdocs/SUPPORTED_CLIS.mdと同期:server/lib/routes/cli-detect.mjsに Grok Build CLI(grok)と Kimi CLI(kimi)を追加し、Antigravity は正規バイナリagyを優先的に探索します。読み取り専用の PATH スキャンは 9 ツール を報告するようになり、見つかったバイナリを実行しない点は変わりません。
変更
- career-ops.org/docs とのドキュメント再同期 — すべてのドキュメント面を親のライブページ(全 31 ページを読了)と突き合わせました。正規の AI アシスタントロスター(help ×17 + README ×17)は、8 つの一級 CLI — Claude Code、Codex、OpenCode、Antigravity CLI、Grok Build CLI、Qwen Code、Kimi、GitHub Copilot CLI — と Gemini CLI(レガシーラッパー)を列挙します。ヘルプバンドルは 29 H2 / 105 H3 構造を維持します。
[1.125.4] — 2026-07-23
変更
- site の依存関係(dependabot #151–#153)—
site/のsharp0.34.5→0.35.3、svgo4.0.1→4.0.2、fast-uri(dev)3.1.3→3.1.4。Astro ビルドはグリーン、SPA/サーバーへの影響なし。
補足
- 親プロジェクトのパリティスイープ(career-ops
37d17ec..254764a、v1.22.0 以降) — 移植対象なし:set-statusの誤行ガード(#2108)は CLI 専用(web-ui のトラッカー行は UI で明示的に選択され、どのルートもset-status.mjsを呼ばない)、ローカライズ済みモードの Risk Summary(#2109)は web-ui が読まないmodes/<lang>/ファイル(読むのはトップレベルのmodes/*.mdのみ)、update-systemのマニフェスト検証(#2111)はアップデーター専用、残りは親のドキュメント(トルコ語 README、SIGNATURES ×4、SCRIPTS.md、es のアクセント)。親の VERSION は 1.22.0 のまま —parentVersionに変更なし。
[1.125.3] — 2026-07-23
修正
- デンマーク語・ヒンディー語の LLM プロンプトが英語で応答していた問題(ユーザー報告)—
server/lib/prompts.mjsのLOCALE_NAMESと 5 つのSCAFFOLD_STRINGSブロックがda・hiに拡張されておらず、resolveLocale()がenにフォールバックし、ディープリサーチ(ライブ・手動)、モード実行、評価、面接、ネットワーキング、CV Studio のすべての AI プロンプトから# Output language指示が失われていました。両ロケールを正式サポート: ロケール指示 + ローカライズ済みスキャフォールディング。tests/locale-scaffold.test.mjsの回帰ゲートはハードコードされた 12 件ではなく正規の 17 ロケール一覧を走査し、新しい構造パリティゲートは英語にフォールバックするスキャフォールドキーを失敗させます — 今後prompts.mjsを見落としたロケールは出荷できません(+12 テスト、スイートは 1969 件に)。
[1.125.2] — 2026-07-22
修正
- Gemini でのディープリサーチ:HTTP 502(
MALFORMED_FUNCTION_CALL) (#145、@Alien10140 の貢献) — ライブの/api/deepプロンプトはモデルに「Use WebFetch / WebSearch」とファイル保存を指示していましたが、ヘッドレス API プロバイダにはツールチャネルがなく、Gemini はテキストではなく関数呼び出しで応答し、空の HTTP 502 として現れていました。buildDeepPromptとbundleProjectContextにheadlessフラグを追加:ライブ実行(Anthropic/Gemini/フォールバックカスケード)はインライン化されたコンテキストからブリーフを書くツールなしプロンプトを受け取り、Claude Code 用のコピー&ペーストプロンプトはツール指示を保持します。tests/critical-fixes.test.mjsにテスト +1。
変更
- 非推奨の
gemini-2.0-flashを超えて Gemini デフォルトを更新 (#144、@Alien10140 の貢献) — 設定ドロップダウン、gemini.mjsのサーバーフォールバック(ヒントと静かに食い違っていた値)、OpenRouter フォールバックチェーン、config.geminiModelHint×17、ヘルプガイド ×17 がすべてgemini-3.6-flashを指すようになりました。新しいドリフトゲートtests/gemini-default-model.test.mjs(+5 テスト)がすべての面を同一リテラルに固定します — スイートは 1957 テストになりました。
[1.125.1] — 2026-07-21
修正
- SuccessFactors:マルチブランドの RMK テナントがブランドパスを維持(親 #2099、v1.22.0 以降)— 一つの共有 RMK インスタンス上で複数の買収ブランドを運用するホールディングカンパニーは、パスセグメントでブランドを区別します(
careers.nemetschek.com/Bluebeam/vs…/Vectorworks/)。アダプタは以前、設定された URL を origin まで切り詰めてしまい、親ブランドの求人だけを黙ってスキャンしていました。エンドポイントは今やブランドプレフィックスを保持し、末尾の/search/または/tile-search-results/セグメントのみを取り除くため、二重スキャンが発生することはありません。単一ドメインのテナントはバイト単位で変更されません。新しくエクスポートされたresolveTenantBaseヘルパー +tests/sources-successfactors.test.mjsに移植したテストブロック 1 件。
[1.125.0] — 2026-07-21
追加
- cvstart.org:「求人ソース」ランディングセクション — スクリーンショットと比較表の間に新しいセクションが追加され、67 件のスキャナーソースすべてをクリック可能なチップ(英語圏の求人ボード/ATS 62 件 + 独自の見出しの下にまとめたロシア語圏のボード 5 件)として一覧表示します。各チップはそのソースの公開サイトへリンクします。この一覧はビルド時に実際のアダプタレジストリ(
sync-assets.mjs→facts.sources)から同期されるため、アプリの実態と乖離することはありません。Sources.astro内の厳選されたリンクマップは、新設のtests/site-sources.test.mjsによって保証されます。ヘッダーナビゲーションに Sources アンカーが追加され、サイトの i18n キーが 4 件 × 17 言語分新設されました。あわせて、ランディングページの JSON-LD のinLanguage一覧にhiが抜けていた問題も修正しました。
[1.124.0] — 2026-07-21
追加
- 5 件のスキャンソース(親 v1.22.0 パリティ、#1808/#1572/#2024/#2055)— Welcome to the Jungle(ボード全体の JSON API)、Agentic Engineering Jobs(エージェンティック/AI エンジニアリング専門ボード)、Jobvite(ゼロ認証のテナント単位 ATS)、Gem(テナント単位 ATS)、Alibaba Group(キャリア JSON API、Meituan/Tencent パターン)。いずれもホスト固定・CI 分離されたソース + アダプタのペアで、レジストリは 67 個のアダプタ(英語圏 62 個 + ロシア系 5 個) を同梱するようになりました。
#/scanの Source ドロップダウンのフォールバックとそのドリフトゲートも更新済みです。新しいテストスイートtests/sources-{wttj,agenticjobs,jobvite,gem,alibaba}.test.mjsを 5 本追加。
修正
- Arbeitsagentur:全国リモート判定は
homeofficetypがVOLLSTAENDIGの場合のみ(親 #1981)—homeoffice=nv_trueクエリはハイブリッド求人も返してしまうため、リモート判定は各ヒットを求人詳細エンドポイントに対して小さなバッチで確認するようになりました。照会がエラーになった場合はフェイルクローズし、求人の実際の勤務都市を保持するため、勤務地フィルタは引き続き適用されます。 - SmartRecruiters:公開求人 URL に
/postings/が付与されずに生成される問題(親 #2047)— リンクは、公開サイトがこのセグメントを省略しているテナントで 404 になる代わりに、公開求人ページへ正しく到達するようになりました。
補足
- 親 v1.22.0 では、Web UI がシェルアウトしていない、またはすでにカバーしている CLI 側の変更も出荷されています:zh-CN 向け CV テンプレート + PDF タイポグラフィ、
/expandモード、プロバイダのプロンプトキャッシュ調整(Gemini/OpenAI/Ollama)、ステップ単位のトークン内訳(Web UI は独自の使用量メーターを保有)、トラッカーのライターロックによる直列化(Web UI は v1.21 以降withFileLockで書き込みを経路化)、スキャンのvisa_filter+ 絶対投稿日 CLI フラグ(Web UI は独自の「投稿からの経過日数」フィルタを保有)、そして seen-sources 重複排除のシード投入(Web UI スキャナーは独自のスキャン履歴による重複排除を保持)です。
[1.123.0] — 2026-07-17
追加
- Oracle Recruiting Cloud スキャンソース(親 v1.21.0 パリティ、#1929)— Oracle Fusion/ORC 系のキャリアサイト(JPMorgan Chase、Oracle、BNY Mellon、American Express、Honeywell など)が使うゼロ認証の
recruitingCEJobRequisitionsREST API:*.fa[.<region>][.ocs].oraclecloud.comにホスト固定し、サイト番号は各トラッキング対象企業のcareers_urlから解決、オフセットページネーションはハード上限付き、WAF を意識したブラウザ風ヘッダーを使用します。レジストリは 62 個のアダプタ(英語圏 57 個 + ロシア系 5 個) を同梱するようになりました。#/scanの Source ドロップダウンのフォールバックとそのドリフトゲートも更新済みです。CI 分離された新しいテストスイートtests/sources-oraclecloud.test.mjsを追加。
修正
- repost 検出器:ベースタイトルを特化サフィックス違いの兄弟求人と区別(親 #1922)— 「Senior Analytics Engineer」が「Senior Analytics Engineer, People Analytics」とクラスタリングされなくなりました:一方のタイトルのトークンが他方の厳密な部分集合であり、余分なトークンが(基本語ではなく)実際の専門分化を示す場合、両者は別々に応募可能な求人として扱われます。再掲載の注記(「(Repost)」、「relisted」)はメタ的なノイズとしてストップワード化されました。
tests/detect-reposts.test.mjsに +2 アサーション。
補足
- 親 v1.21.0 では、Web UI がシェルアウトしていない、またはすでにカバーしている CLI 側の変更も出荷されています:同一企業への再応募警告(Web UI は v1.84.0 以降、再応募クールダウンを保有)、カバーレターの
--format/--reportフラグ、面接のレッドフラグ / パネルインテル / 無断欠席メールのプロンプトモード、スキャンの信頼シグナル & ポータルヘルスの永続化(Web UI は独自のインプロセススキャナーでtrust-validatorと Portals health ページを運用)、そして stats/salary-gap の拡張(読み取り専用かつフェイルソフトで中継)。
[1.122.0] — 2026-07-16
追加
- ヒンディー語(हिन्दी)— 17番目の言語 — 完全な UI 辞書(約 1,110 キー)、完全なアプリ内ヘルプガイド(29 の H2 / 105 の H3 パリティ)、
README.hi.md、新しいCHANGELOG.hi.md(de/it/tr の前例に倣い v1.122.0 から開始)、cvstart.org のランディング + メソドロジー/ライセンス/変更履歴/ヘルプページ、言語スイッチャー(🇮🇳)、ブラウザ言語の自動検出、ローカライズされたダッシュボードのスクリーンショットを追加しました。×16 のパリティゲートはすべて ×17 で実行されるようになりました:i18n 辞書パリティ + スナップショット、ヘルプ H2/H3 ゲート、変更履歴パリティ、サイトのcheck-i18n、そして Playwright のロケール一巡テストです。
[1.121.0] — 2026-07-16
追加
- cvstart.org: メソドロジー、ライセンス、変更履歴ページ — ランディングに、既存の Compare ブロックの隣に全 16 言語で 3 つの新セクションが追加されました: /methodology/(6 次元の 0.0–5.0 スコアリングルーブリック、4.0 の応募しきい値、絶対にしないことのルール — career-ops.org/methodology のローカライズされた要約)、/license/(NOTICE.md へのリンク付きの正規 MIT 全文)、/changelog/(このファイルを、リポジトリの 16 の翻訳済み CHANGELOG からロケールごとにレンダリングしたもの)。ヘッダーに新しい Methodology エントリ、フッターの Resources にもリンクを追加。
sync-assets.mjsはビルド時に CHANGELOG ×16 と LICENSE をサイトへ同期するようになり、ページがリポジトリから乖離することはなくなりました。 - ドキュメント全体へのメソドロジーリンク — README(全 16)、アプリ内ヘルプガイド §1 の正規ガイドリスト(全 16)、そして wiki が、既存の career-ops.org/docs ガイドと並んで career-ops.org/methodology(FAQ・用語集にも)にリンクするようになりました。
変更
- README のリリースバナーとバッジを更新(テスト 1850、リリース v1.121.0)— バナーはまだ v1.119.5 を告知したままでした。
[1.120.0] — 2026-07-16
追加
- CareerOps マニフェスト(親 v1.20.0 パリティ)— 親プロジェクトが CareerOps マニフェスト(
MANIFESTO.md· career-ops.org/manifesto)を公開し、README・アップデーター・Go ダッシュボードから案内するようになりました。Web UI もこれに追随します:サイドバーフッターの新しいリンクからマニフェストのページが開くようになり(全 16 ロケールに新しい i18n キーfooter.manifestoを追加)、アプリ内ヘルプガイドには全 16 言語で §29「CareerOps マニフェスト」が追加され、README はマニフェストとは何か・どう署名するかを説明し、cvstart.org のランディングフッターにもリンクが張られました。
備考
- 親 v1.20.0 では、
upskillのターゲットモードにおける既知スキルの抑制も修正され、scan --jsonの標準出力がパース可能であり続けるよう dotenv を静音化し、役職見出しがその箇条書きから分離しないよう HTML CV テンプレートを修正しました — いずれも Web UI がシェル経由で呼び出さない CLI 側の挙動であり、Web UI 側のコード変更は不要でした。
[1.119.5] — 2026-07-13
修正
- ランディングの言語ボタンが折り返さなくなりました — v1.119.2 の国旗追加により、ヘッダーのスイッチャーラベル(例:「🇷🇺 Русский」)が狭いデスクトップ幅で最大 3 行に折り返すことがありました。スイッチャーラベルとドロップダウンの全項目を
whitespace-nowrapにし、国旗 + 言語名が常に 1 行に収まります。 フッターの言語リストは固定の 2 列グリッドから 1 行アイテムの折り返し行に変更 —「🇧🇷 Português (Brasil)」も名前の途中で折れなくなりました。
[1.119.4] — 2026-07-13
変更
- LICENSE に作者を明記 — 著作権行は Sergei Emelianov (Fighter90) https://sergey-cv.com and career-ops-ui contributors になりました(MIT の正規本文は無変更)。新しい NOTICE.md がライセンスを詳細に解説:著作権者は誰か、MIT 許諾が正確に何をカバーするか(コード、ドキュメント、翻訳、ランディング、wiki)、何をカバーしないか(実行時のあなたのデータ、親プロジェクト、求人ボードのコンテンツ、商標)、サードパーティ構成要素の表(express/js-yaml — MIT、Astro/Tailwind — MIT、フォント Figtree と JetBrains Mono — SIL OFL 1.1、sharp — Apache-2.0)、任意の帰属表示の一文。
[1.119.3] — 2026-07-13
追加
- SECURITY.md — CONTRIBUTING が指していたセキュリティポリシーがついに実在します:サポート対象バージョン、非公開報告フロー(リポジトリで GitHub の private vulnerability reporting を有効化 — Security タブ →「Report a vulnerability」)、localhost バインドの単一ユーザーアプリの脅威モデル(スコープ内:敵対的な求人経由の XSS / SSRF / パストラバーサル / シークレット漏えい / CSP の弱体化。スコープ外:自分の localhost への DoS と親プロジェクトの問題)、レビュアー向けのハードニング基準。
[1.119.2] — 2026-07-13
追加
- CONTRIBUTING.md — ランディングと README がずっとリンクしていたコントリビューターガイドがついに実在します:セットアップ、プロジェクトマップ、セキュリティ/ノービルドの厳格ルール、テスト階層、スキャンソース追加の「2 つのレジストリ」ウォークスルー、×16 i18n 契約、コミット/PR 規約、リリースプロセス。
- ランディングの言語フラグ — cvstart.org の言語スイッチャー、フッターの言語グリッド、「あなたの言語で読む」バナーに、各ロケールの国旗をエンドニムの横に表示するようになりました(アプリの言語
<select>と同じ地域指示子セット。国旗グリフのない環境では地域文字に退化)。 - ランディングのフッター修正 — 機能が有効化されていないためリンク切れだった Discussions は、プロジェクトの wiki を指すようになりました。フッターには作者のクレジットを追加:Sergei Emelianov(sergey-cv.com · LinkedIn)。
[1.119.1] — 2026-07-13
修正
#/scanのソースフィルターがレジストリに追いつきました — Source ドロップダウンの背後にある静的なFALLBACK_SOURCESリスト(GET /api/scan/sourcesに到達できない場合のみ使用)は v1.87.0 以降、静かに遅れていました:オフラインフォールバックに 20 のプロバイダー(Amazon、Avature、SAP SuccessFactors、Get on Board、Dassault Systèmes、beesite、HigherEdJobs、JibeApply (iCIMS)、softgarden、Cornerstone、Phenom、Radancy、Deutsche Bahn、EchoJobs、TKMS、Heckler & Koch、Rheinmetall、LaraJobs、そして新しい Meituan / Tencent)が欠けていました。全 61 と同期し、クライアントのリストがサーバーレジストリから乖離すると CI を失敗させるドリフトテスト(値もラベルも)で保護されるようになりました。+1 テスト(1845)。
[1.119.0] — 2026-07-13
親 career-ops v1.19.0 パリティ + cvstart.org ランディングの刷新。
追加
- 2 つの新スキャンプロバイダー — Meituan(
zhaopin.meituan.com)と Tencent(careers.tencent.com):中国テック系ボードの認証不要な公開 JSON API。ホストから自動検出、または明示的なprovider:で選択。キーワードごとのサーバーサイド検索・ページネーション・URL による重複排除つき — これで 61 アダプタ(英語圏 56 + ロシア系 5)。+20 テスト(1844)。 - ランディングのコントリビューターブロック — cvstart.org にコードを提供した全員のアバターを表示(ビルド時に GitHub
/contributorsAPI から取得、ボットは除外)。16 言語すべてにローカライズされ、コントリビューターグラフ全体へのリンクつき。 - ランディングのライブ GitHub スター数 — ヘッダーのバッジは訪問のたびにクライアント側で GitHub API から更新(ビルド時のスナップショットはフォールバック)。週次スケジュールの Pages 再ビルドがスナップショットとコントリビューター一覧を新鮮に保ち、CI の API 呼び出しはトークン認証になりました。
修正
- Workday CXS リクエストがブラウザ風ヘッダーを送信(親 #1813)— Cloudflare 保護テナント(実例:geico)は通常の UA/
accept-language/origin/refererを欠くリクエストに 500 を返します。フェッチャーは origin とサイトスラッグを CXS URL 自体から導出するようになりました。Glints リクエストにも同じブラウザ UA + origin/referer を追加 — どちらもhttp-json.mjsの共有定数BROWSER_LIKE_USER_AGENTから。
[1.118.4] — 2026-07-10
修正
- ロシアの IP から hh.ru スキャンが 0 件を返していた(地域サブドメインのリンク) — ロシアの住宅用 IP からだと hh.ru は検索を地域サブドメイン(
sochi.hh.ru、spb.hh.ruなど)へ 302 リダイレクトし、求人リンクをそのサブドメインで返します。パーサーはタイトルリンクを固定ホストhttps://hh.ru/vacancy/で探しており、地域リンクを 1 件もマッチできず、完全に動作するスキャンが黙って 0 件を記録していました。現在は任意の*.hh.ruホストを受け入れ(adsrv.hh.ru/click?…の広告は引き続き除外 —/vacancy/<id>パスを持たない)、各結果 URL をhttps://hh.ru/vacancy/<id>に正規化します。実地検証済み:以前 0 件だったsochi.hh.ruページから 17 件の実求人が解析されます。+1 テスト(1824)。
[1.118.3] — 2026-07-10
修正
- hh.ru がエラーなしで 0 件を返していた(VPN チェックの中間ページ) — hh.ru は VPN/プロキシと判定したネットワーク(データセンター IP)を
/vpncheeck中間ページ(「VPN мешает работе сайта」)へ 302 リダイレクトするようになり、このページは求人カードを 1 枚も含まない HTTP 200 を返すため、スキャンはエラーなしで 0 件を報告していました。スキャナーはレスポンスの最終 URL からリダイレクトを検出し、実行の残りの間 hh.ru を無効化し、正直なヒントを出力します:トラフィックは本当に住宅用 IP から出ている必要があります — ブラウザのトグルをオフにしてもシステム全体の VPN/プロキシは有効なままのことがあります。+1 テスト(1823)。
[1.118.2] — 2026-07-10
メンテナンス
- ランディングのフォローアップ(#118) —
site/README.mdを Astro 7(#116 のセキュリティアップグレード)に合わせて修正し、未使用 import を削除、ランディングのビルドスクリプトに実行可能なガードを 4 件追加: i18n パリティゲートが壊れた辞書で確実に失敗することを証明し、sync-assetsはsite/の外に書き込まないことを保証 — スイート 1822。CodeQL アラート 2 件を解決(1 件はソース修正、1 件は意図されたビルド時動作として却下)。
[1.118.1] — 2026-07-10
修正
- ロシア国外からの hh.ru スキャン — hh.ru が公開検索ページでロシア国外の IP に HTTP 451(地域的な法的ブロック)を返すようになりました。スキャナーは 451 を 403 と同様に扱います: 最初のブロック後、実行の残りで hh.ru を無効化し、ロシア IP / VPN 出口を案内する正直なログを残すため、残りのクエリや他の RU ソースを無駄にしません。ヘルプ §7 を全 16 言語で更新。テスト +1(1818)。
[1.118.0] — 2026-07-09
親 career-ops v1.18.0 パリティパック。
追加
- 9 つの新しいスキャンプロバイダー — Cornerstone OnDemand (
csod), Phenom (phenom), Radancy (radancy), Deutsche Bahn (deutschebahn), EchoJobs (echojobs), TKMS (tkms), Heckler & Koch (hecklerkoch), Rheinmetall (rheinmetall), LaraJobs (larajobs) — これでアダプターは 54 個。Lever アダプターは EU テナンシーのボード(jobs.eu.lever.co)も検出します。 - トラッカーの
Hiredステータス(親のstates.ymlパリティ): 受諾したオファーは独自の正規ステータス、お祝いバッジ、#/trackerの内定獲得バナーを持ちます。ファネルとコンバージョンのチャートは全ステージを通過したものとして数えます。 #/statsの累計タブ — 親のstats.mjs(累計トラッカー集計、累積ファネル率、スキャナー合計、ポータルカバレッジ)とsalary-gap.mjsの報酬観測(希望 vs 求人提示 vs 実際、応募ごと)を読み取り専用で中継します。新ルートGET /api/stats/lifetimeとGET /api/stats/salary-gap— ゼロトークンのシェルアウトで、親プロジェクトがなければ{available:false}に安全にフォールバックします。- 全 16 ロケールに 28 個の新しい i18n キー。ヘルプガイド §14/§26 を全言語で更新。
テスト
- ユニットテスト +38(プロバイダーパリティ 3 スイート + リレー/ステータスルート) — 合計 1817。
[1.117.2] — 2026-07-06
パリティ・シェルアウトの空トラッカー修正。 トラッカーに応募がまだ無い場合、親スクリプトはコード1と構造化された {error} JSONで終了します。フォローアップボードと不採用パターンタブはこれを「script-error」と表示していました。両ルートはこれを健全な空状態(available:true, empty:true)として中継し、UIは正直な「まだ何もない」メッセージを表示します。実際の親でライブ検証済み。
新規: なし。
[1.117.1] — 2026-07-06
v1.117.0 のハードニング(CodeQLトリアージ)。 3つのシェルアウト・エンドポイント(GET /api/followup、POST /api/followup/seed、GET /api/stats/patterns)に共有のIP別リミッターを適用(リクエストごとに子プロセスを生成; ループバックではno-op)。CVに追加のURLテキスト抽出は、タグを不動点まで除去した後、残った </> をすべて削除 — LLMプロンプト用テキストとして証明可能に完全なサニタイズ。有効な入力の挙動は不変。
新規: なし。
[1.117.0] — 2026-07-06
親パリティパック — 親 career-ops の6つの機能をUIへ。 (1) #/followup のフォローアップ・ケイデンスボード: followup-cadence.mjs による応募ごとの緊急度(🔴/🟠/🟡/🔵)+ フォローアップ日をシードボタン(followup-seed.mjs --backfill)。(2) 不採用パターン: 統計の4つ目のタブが analyze-patterns.mjs(読み取り専用)を実行 — 結果の内訳、推奨、ATSベンダー別前進率。(3) CVに追加: CV StudioのカードがURLまたは貼り付けテキストを、その出典のみに基づくATS箇条書きに変換(提案のみ、書き込みなし; URLフェッチはSSRF保護)。(4) 新スキャンプロバイダ4つ — beesite、HigherEdJobs(RSS)、JibeApply(iCIMS)、softgarden — レジストリは50アダプタ(45 EN + 5 RU)になり、すべてScanのドロップダウンに表示。(5) Applyチェックリストに失格要件の事前スキャンステップ。(6) reconcileランナー(/api/run/reconcile)。シェルアウトのルートは親スクリプトがなければ正直に「利用不可」を表示。
- 新ルートモジュール
server/lib/routes/followup.mjs(31番目)+ 新ルート + ソース/アダプタ8ファイル。テスト: 6 + 7 追加; スイート 1737 → 1750。新しいi18nキー41件×16。ヘルプ §13/§17/§24/§26 を×16拡張。
新規: GET /api/followup、POST /api/followup/seed、GET /api/stats/patterns、POST /api/cv-studio/add-entry、POST /api/run/reconcile。
[1.116.0] — 2026-07-06
使用量メーターの作り直し + 初のE2Eウィジェットテスト。 AI使用量メーター(v1.114.0)を正しく固定しました:左サイドバーの下部に固定され(サイドバー全幅、同じ表面)、自身の高さ分だけサイドバー下部に余白を確保してメニューが決して隠れません — ナビとバージョンフッターは常にその上をスクロールします。リアルタイム更新(15秒間隔 + タブフォーカス + ルート変更)で、各ウィンドウ行は常に100%の「割合」ではなく実際の**<トークン> · <推定コスト>(バーは30日ウィンドウ基準)を表示します。さらに、CVインポーターの恒久的な typeof バリアが、繰り返されるCodeQLの型混同の誤検知を根本で閉じ、新しいPlaywrightのE2Eテスト**が2つの常駐ウィジェットを実ブラウザで検証します。
public/js/lib/usage-hud.js+app.css、server/lib/cv-import.mjs。テスト:tests/playwright-widgets.mjs(2 E2E)+tests/usage-hud.test.mjs(10)。ヘルプ§6を×16拡張。
新規: なし。
[1.115.0] — 2026-07-06
デザインの磨き込み(保守的、コーラルのブランドは維持)。 共有デザインシステムへの軽い洗練パス — 構造変更なし、パレット変更なし。ダッシュボードのメトリクスカードはホバー時にわずかに浮き上がり、コーラルの枠線を帯びます(クイックアクションタイルと同じ)。コンテンツカードもわずかに浮きます。primary / dark / danger ボタンは静止時の影と穏やかなホバーリフトで奥行きを得ます。大きな数字は tabular-nums で揃います。インタラクティブなコントロールは2pxのキーボードリングの背後に柔らかなコーラルのフォーカスハローを受け取ります。すべてのモーションは prefers-reduced-motion を尊重し、ハローはコントロールに限定されます — グローバルな *:focus-visible ではありません。
- CSSのみ(
public/css/app.css)。マークアップ・i18n・ルート・CSPの変更なし。テスト:tests/design-polish-v1115.test.mjs(5)。Playwrightでライブ検証。
新規: なし。
[1.114.0] — 2026-07-06
サイドバーのAI使用量・コストメーター(左下)。 コンパクトな使用量セクションが、すべてのページのサイドバー下部に表示されます(サイドバーがなければ左下の固定カード、RTLでは右下)。24h / 7d / 30d の各ウィンドウのLLMトークン使用量を <トークン> · <割合%>(全期間に対する割合)で緑のメーターバーとともに表示し、24時間の推定コストのフッターを添えます。データは data/llm-usage.jsonl の読み取り専用 GET /api/usage 集計(ローカルのみ)で、#/usage ページと同じソースです。コストは推定であり、手動モードの実行は無料でカウントされません。折りたたみ可能 — ヘッダーで切り替え、状態は保持されます。
- 新しいクライアントウィジェット
public/js/lib/usage-hud.jsをindex.htmlから読み込み、バージョンフッターの上のサイドバーにマウント(固定コーナーのフォールバック)。CSP安全、テーマ対応 + RTLミラー。新しいサーバールートなし。テスト:tests/usage-hud.test.mjs(8)。新しいi18nキー3件×16。
新規: なし。
[1.113.0] — 2026-07-06
すべてのページに浮かぶ「ヘルプに質問」アシスタント。 グラデーションのロボットチャットボタンが、すべてのページの右下(RTLでは左下)に浮かびます。クリックすると、あなたの言語のアプリ内ヘルプガイドのみに基づいて使い方の質問に答えるコンパクトなチャットが開きます — #/docs-assistant ページと同じエンドポイント(POST /api/docs-assistant/ask)なので、CV・プロフィール・トラッカーは読みません。LLMキーがあればライブ、なければすぐ実行できるプロンプト。ヘッダーにはロボットアバター + オンライン状態、スターターチップでよくある質問を入力、Escまたは外側クリックで閉じ、#/docs-assistant ページでは非表示。
- 新しいクライアントウィジェット
public/js/lib/docs-fab.jsをindex.htmlからグローバルにマウント。CSP安全。app.cssにテーマ対応 + RTLミラーのスタイル。新しいサーバールートなし。テスト:tests/docs-fab.test.mjs(8)。新しいi18nキー6件×16。ヘルプ§1をその場で拡張。
新規: なし。
[1.112.0] — 2026-07-06
ドキュメントとQAの統合。 ユーザーに見えるコード変更なし。SDD規約ドキュメント(docs/sdd/CONVENTIONS.md)を現在の30ルートモジュール(以前は24)と現在のテスト基準に更新し、全プロジェクト確定QAプロンプト(qa/QA-REGRESSION-PROMPT.md)を統合しました — リリース手順を最新化(v1.111、parentVersion 1.17.0、リリーストリガー公開)、§14追加表を修正(Scan除外をv1.109.0に再ラベル)し、v1.111のCodeQLクローズアウトを追加 — 全機能の単一リグレッションプロンプトとして単独で成立します。過大アップロード分岐のカバレッジテストを1件追加。
新規: なし。
[1.111.0] — 2026-07-06
セキュリティ — CodeQLバックログの解消。 残る静的解析の指摘を握りつぶすのではなく原因箇所で閉じる、多層防御の強化3件。stripDangerousMarkdown は 途中で切れた 危険タグの開始(<script/<iframe/… で終わるペイロード)の < をエスケープするようになり、出力に生きた危険タグが残らないことを保証します。CVインポートはアップロードされたバッファのサイズを明示的な Number() 変換で読み取ります — 型混同のバリア。モードのロール行は、保存された関数ではなく String.replace で補間されるテンプレート文字列になり、動的ディスパッチ呼び出しを完全に排除しました。ユーザーに見える挙動の変化はありません。
server/lib/security.mjs、server/lib/cv-import.mjs、server/lib/prompts.mjs。テスト:tests/security-hardening-v1111.test.mjs(7)+ v1108ガードテスト更新。i18n/ヘルプ/ルート変更なし。
新規: なし。
[1.110.0] — 2026-07-06
ドキュメント & QA 更新(全言語)。 コード変更なし。プロジェクト全体のQAプロンプトがv1.109.0に更新され、v1.98→v1.109を扱う新§14が追加、UX監査とデザインエクスポートのプロンプトが現在の画面群を反映。v1.100–v1.109で追加された全ヘルプ段落が全16言語に翻訳されました。
新規: なし.
[1.109.0] — 2026-07-06
Scanの除外フィルター + パイプライン概要(Webレイアウトパリティ)。 #/scanで検索ボックスがカンマをORとして扱い(「探す役割」)、新しい除外フィールドは会社/役割/場所にカンマ区切りの語(例: senior, staff)を含む行を隠します。両方とも保存済み検索に記憶されます。#/pipelineにはコンパクトな概要ストリップがパイプラインを一目で表示 — 受信 N、追跡 N、トラッカーの Applied / Responded / Interview / Offer の数、各チップは #/tracker にリンク。
- クライアントのみ(新ルート/書き込みなし)。
public/js/views/scan.js+public/js/views/pipeline.js。テスト:tests/scan-pipeline-ui-v1109.test.mjs(2)。新しいi18nキー4件×16。ヘルプ§7 + §8をその場で拡張。
新規: なし。
[1.108.0] — 2026-07-06
セキュリティ強化(CodeQLトリアージ第2弾)。 さらに軽微な3件を修正: モードプロンプトビルダーがロケールの役割行を自身のキー + typeof === functionで解決し、改ざんされたロケールがプロトタイプメソッドへディスパッチできないようにし(unvalidated-dynamic-method-call);PDFファイル名スラッグを正規表現の前に200文字に制限し、全ハイフン入力がバックトラックしないようにし(多項式ReDoS);ドキュメントインポートが配列のfilename(繰り返しヘッダー)を文字列に強制変換します(type-confusion)。有効な入力に対する挙動変化なし。
server/lib/prompts.mjs、server/lib/routes/runners.mjs、server/lib/cv-import.mjs+tests/security-hardening-v1108.test.mjs(3)。v1.106–v1.108で静的解析のバックログは167から約14に減り、真にセキュリティ関連の指摘はすべて修正、残り(ガード/サニタイズ済みの誤検知 + ノートレベルのlint)は根拠付きで却下。
新規: なし。
[1.107.0] — 2026-07-06
サニタイザー強化(保存時XSSの多層防御)。 stripDangerousMarkdown — 保存済みの履歴書/求人マークダウン内の危険なHTMLを無害化し、描画時エスケープのクライアントを迂回する消費者も安全にする — が、タグ除去を不動点まで(変化がなくなるまで反復)実行し、除去がペイロードを再構成する場合(例: <scr<script></script>ipt>)を捕捉し、末尾にゴミが付いたscript/styleなどの終了タグ(</script foo>)と閉じられていない実行オープナー(<script …>)を除去します。有効なマークダウンの挙動は変わらず、より多く除去するだけです。
server/lib/security.mjs: 不動点ループ(8回制限)+[^>]*>終了タグパターン + 未クローズオープナー除去。tests/cv-xss-bypasses.test.mjsに回帰ケース+3。権威あるXSS境界は依然として出力エスケープ(UI.md)であり、これは保存時保証を強化し、対応するCodeQLの指摘を解消します。
新規: なし。
[1.106.0] — 2026-07-06
セキュリティ強化(CodeQLトリアージ)。 静的解析のバックログを精査し、実際の(軽微だが)3件を修正しました: ルート描画のエラー経路がエラーメッセージをDOMに到達する前にエスケープするようになり(サーバーエラーがユーザー入力を反映しうるため、信頼できないものとして扱う — XSS境界)、プロフィール/設定のプロパティ書き込みが**__proto__ / constructor / prototype キーを拒否**します(念のためのプロトタイプ汚染ガード — キーは固定フィールド仕様から来るもので、生のリクエスト入力ではない)。残りのアラートの大半は、スキャナーの正当な data/* の読み書きや、既に独自レートリミッターを備えたルートに対する誤検知で、根拠付きで却下しました。
public/js/router.jsはinnerHTMLの前にUI.escapeHtmlでerr.messageをエスケープ;server/lib/routes/content.mjsとserver/lib/routes/config.mjsはプロトタイプキーをガード。有効な入力に対する挙動変化なし。テスト:tests/security-hardening-v1106.test.mjs(3)。新しいi18nキーなし。
新規: なし。
[1.105.0] — 2026-07-06
AI使用量とコストのページ。 新しいAI使用量ページ(サイドバー、ヘルスの隣)は、ライブのAI生成 — 評価・レポート・チャット — に費やしたトークンを、過去24時間・7日・30日・全期間にわたってプロバイダーごとに表示し、推定USDコストを併記します。すべてのライブ呼び出しは小さな {provider, in, out} レコードを data/llm-usage.jsonl に追加します(どこにも送信されません)。キーなしの実行(手動モード)はコストがかからず、記録されません。
- 新ルートモジュール(30番目)
server/lib/routes/usage.mjs—GET /api/usage(読み取り専用の集計) +server/lib/llm-usage.mjs(recordUsageがAnthropic/OpenAI/Geminiの使用量形式を正規化してbest-effortで追加、readUsage/aggregateが24h/7d/30d/全期間の窓 × プロバイダーで集計) +server/lib/llm-pricing.mjs(編集可能なプロバイダーごとの$/1Mトークン価格表 — トークンは正確、ドルは概算定価でプランに合わせて修正可能、請求されない)。記録はディスパッチ地点(runActiveProvider+routes/llm.mjs)にフックされます。 - 新ビュー
public/js/views/usage.js(#/usage、期間タブ)。テスト:tests/usage-routes.test.mjs。新しいi18nキー17件×16(usage.*+nav.usage)。ヘルプ§6をその場で拡張。
新規: server/lib/routes/usage.mjs; server/lib/llm-usage.mjs; server/lib/llm-pricing.mjs; public/js/views/usage.js.
[1.104.0] — 2026-07-06
スキャン表の会社ロゴ(プライバシー保護)。 アプリ設定の新しい外観トグル — スキャン表に会社ロゴを表示(既定オフ) — が #/scan で会社名の隣にロゴを描画します。ロゴは会社自身のドメインから取得したファビコンで、サーバー側でプロキシされます(GET /api/logo)。そのためサードパーティのロゴサービスがあなたの見ている雇用主を知ることはありません。共有求人ボード(Greenhouse、Lever、Ashby など)の掲載はボードのアイコンではなく色付きの文字バッジを表示し、読み込みに失敗したロゴも同じバッジにフォールバックします。
- 新ルートモジュール(29番目)
server/lib/routes/logos.mjs—GET /api/logo?domain=。ドメインを検証し(スキーム/パス/ループバックなし)、SSRF安全なsafeGet(新しいbinaryモードが生バイト + content-type を返す。DNSピン留め・リダイレクト検証・サイズ上限は不変)で/favicon.icoを取得し、HTMLエラーページを画像として提供しないよう画像マジックのスニッフィングを行い、ヒットとミスをメモリLRUにキャッシュし、ディスクには何も書き込みません。 - 新クライアントライブラリ
public/js/lib/company-logo.js(window.CompanyLogo): localStorageフラグで既定オフ。共有ATSホストはスキップして決定的な文字アバターを使用。CSP安全なimg.onerrorフォールバック。テスト:tests/logo-routes.test.mjs。新しいi18nキー5件×16(appear.*)。ヘルプ§2をその場で拡張。
新規: server/lib/routes/logos.mjs; public/js/lib/company-logo.js.
[1.103.0] — 2026-07-06
設定: 「AI CLIツール」 — 何がインストールされているか。 career-opsはClaude Codeベースですが、オープンなスキル標準の任意のエージェントCLIで動作します。アプリ設定(#/config)の新しいAI CLIツールタブは、Claude Code、Codex、Gemini CLI、OpenCode、GitHub Copilot CLI、Qwen、Antigravityのうち、サーバーを実行しているマシンに何がインストールされ、そのパスがどこかを表示します。これは読み取り専用のPATHスキャンです: 各バイナリの存在を確認するだけで決して実行せず(--versionなし、実行なし)、何も書き込まず、ユーザーデータに触れません。
- 新ルートモジュール(28番目)
server/lib/routes/cli-detect.mjs—GET /api/cli-detect。検出は固定7件の許可リストからprocess.env.PATHを通じてバイナリのパスを解決します(Windowsの.cmd/.exe/.batシム、POSIXの実行ビット)。PATH上の悪意あるファイルがこのルートで実行されることは決してありません。 public/js/views/config.jsの新しい「AI CLIツール」タブ(遅延読み込み、#/config?tab=cliでディープリンク)。テスト:tests/cli-detect-routes.test.mjs。新しいi18nキー8件×16(cli.*+config.tabCli)。ヘルプ§2をその場で拡張。
新規: server/lib/routes/cli-detect.mjs.
[1.102.0] — 2026-07-05
「ガイドに質問」 — アプリ内ヘルプガイドに基づくチャット。 新しいガイドに質問 💬ページ(サイドバー、ヘルプの下): 「求人ポータルはどうスキャンしますか?」のような質問を入力すると、あなたの言語のアプリ内ヘルプガイドのみから回答が得られます — 使用したセクションを表示し、あなたの履歴書・プロフィール・求職活動は一切読み取りません。あなたのことではなく、アプリの使い方についてです。LLMキーがあればライブ回答、なければ関連するヘルプセクションが既に入ったすぐ実行できるプロンプトを渡します。
- 新ルートモジュール(27番目)
server/lib/routes/docs-assistant.mjs—POST /api/docs-assistant/ask。依存関係なしの検索: あなたの言語のヘルプ文書を##セクションに分割し、質問とのキーワード重複でスコア付け — 上位セクションをインライン化し、モデルはそこから回答するかガイドが扱っていないと述べます(機能/ルートの捏造なし)。共有プロバイダーカスケード、手動フォールバック、レート制限、書き込みなし、ユーザーデータを読みません。 - 新ビュー
public/js/views/docs-assistant.js。テスト:tests/docs-assistant-routes.test.mjs。新しいi18nキー14件×16(docs.*+nav.docsAssistant)。ヘルプ§1をその場で拡張。
新規: server/lib/routes/docs-assistant.mjs; public/js/views/docs-assistant.js.
[1.101.0] — 2026-07-05
CV Studio: 特定の求人に合わせて履歴書を最適化し、カバーレターを作成 — リクルーターのチェックリストでゲート。 #/cv-studioに新しい求人に合わせて最適化カード: 求人内容(任意で目標の役割/ヘッドライン)を貼り付けると、CV Studioがその求人に合わせた履歴書と対応するカバーレターを作成し、渡す前に両方をチェックリストゲートに通します — errorはブロック(結果を見る前に修正)、warnは助言。この仕組みはキャリアコーチングの実践を汎用ルールに蒸留したものです — リクルーターは数秒で読むため、関連経験は上に、ヘッドラインは求人の役割に一致、成果には具体的な数値、カバーレターは「要件↔あなたの合致する事実」という単一のブリッジを持つ短いティーザー。あなた自身の履歴書・プロフィール・ツーページャーのみに基づき決して捏造しません — 企業・役割・経歴をハードコードしません。
- 新エンドポイント
POST /api/cv-studio/tailor(既存のcv-studioモジュールを拡張 — 27番目のモジュールではない):buildTailorPrompt+ 汎用のTAILOR_INSTRUCTIONSゲート、bundleProjectContextに基づき、共有プロバイダーカスケード、キーがなければ手動プロンプト、レート制限、書き込みなし。結果は共有のreport-export.jsバーでMarkdown / PDF / DOCX にエクスポート。 - テスト:
tests/cv-studio-routes.test.mjsに+3。新しいi18nキー10件×16(cvs.tailor*)。汎用リファレンスdocs/prompts/resume-cover.md。ヘルプ§24をその場で拡張。
新規: docs/prompts/resume-cover.md.
[1.100.0] — 2026-07-05
ツーページャー: 履歴書からのAI自動入力+プレビュー+PDF/DOCX/Markdownエクスポート。 ツーページャー(#/two-pager)は次の役割に本当に望むものを記録しますが、これまでは各項目を手入力するかプロンプトを別ツールにコピーする必要がありました。今回、✨ AI入力アシスタントが設定済みプロバイダーでライブ実行されます — 履歴書+プロフィールのみを読み込み(bundleProjectContext、捏造なし)、すべての項目(私は誰か/好きなこと/必須条件/嫌いなこと/絶対NG/譲れない点/目標環境)を下書きしてフォームに入力し、確認・編集・保存できます。APIキーがなければ従来どおりプロンプトコピーのモーダルにフォールバックします。新しい**👁 プレビューとエクスポートボタンはツーページャーを整形済み文書として描画し、.mdをダウンロード/PDFで保存/DOCXで保存/コピー**のバーを表示します。
- 依存関係なしの
.docxエクスポート。 新規server/lib/docx.mjsが最小限だが有効なOffice Open XML.docx(4つのOOXMLパートのDEFLATE ZIP、エントリごとにCRC-32)を生成します — 新しいランタイム依存なし(依存はexpress+js-yamlのまま)。新ルートPOST /api/export/docx(server/lib/routes/export.mjs、26番目のルートモジュール。ステートレス、200 KB制限、書き込み・LLM・URL fetchなし)。共有のpublic/js/lib/report-export.jsに統合したため、市場レポート、キャリアプラン、キャリアオリエンテーションのレポートもDOCXエクスポートに対応します。 - ライブ自動入力は共有プロバイダーカスケード(
runActiveProvider/providerAvailable)を使用し、返されたYAMLはパースされて制限付きツーページャー形状(parseYamlFields+normalizeTwoPager)に強制変換されます — 不明なキーは破棄、配列/文字列は上限あり。手動モードは維持。 - テスト:
tests/export-routes.test.mjs。新しいi18nキー4件×16(export.saveDocx、twoPager.preview、twoPager.aiFilling、twoPager.aiFilled)。
新規: server/lib/docx.mjs; server/lib/routes/export.mjs.
[1.99.0] — 2026-07-05
ポータル状態ページ(#/portals)。スキャナは portals.yml の会社群を監視します。ATS スラッグが静かに壊れると、その雇用主は以降のすべてのスキャンから静かに消えます。新しい Portals ページは監視中の各社を一覧し、Check portal health で各 careers_url を DNS ピン留めの safeGet(SSRF 安全)で探査し、死んだもの(404 = 静かに除外)を印付けします — 読み取り専用。またレビューに従い v1.98.0 のバグレポーターを強化:エラーリングバッファがネットワークの fetch 失敗を捕捉し、スクラバがラベルなしのプロバイダキーを伏字にします。
新規: server/lib/routes/portals.mjs; public/js/views/portals.js.
[1.98.0] — 2026-07-05
アプリ内バグレポーター(親プロジェクトの web-v0.2.0 Web とのパリティ)。通知ドロワーの 🐞 Report a bug ボタンが、プライバシー下限を守る診断スナップショットを収集します — バージョン、現在の画面、ブラウザ、/api/health のチェック要約、新しいクライアント側リングバッファからの直近 20 件のエラー — さらに決定的な重複排除フィンガープリント(co-web-<base36>)を付け、正確な Markdown を確認させてから、事前入力済みの GitHub Issue を開きます。自動送信は一切なく、CV・プロフィール・回答・求人 URL・キーを決して含みません。新ライブラリ logbuf.js + bug-report.js;11 個の i18n キー ×16;tests/bug-report.test.mjs。
新規: public/js/lib/logbuf.js; public/js/lib/bug-report.js.
[1.97.1] — 2026-07-05
レビュー起点の堅牢化とドキュメントのパリティ(v1.97.0 のフォローアップ)。 AI レビューログを一巡したところ、実際の修正が浮かび上がりました:
fit-score.js(スキャン◎適合バッジ)。salaryFloor()は年額未満のレートをもう偽の年額下限に格上げしません — 「at least 500 EUR/day」「$80/hr」「6000 monthly」は 500k/80k のディールブレーカーではなくnullを返すようになりました。国のマッチングが単語単位(\b…\b)になり、「Germany」が形容詞「German」(あるいは「Nigerian」内の「Nigeria」)にマッチして誤った必須条件-他地域違反を発火させることがなくなりました。tests/fit-score.test.mjsにテスト +3。- ドキュメントのパリティ。 すべてのローカライズ済み README が 16 ロケール を一貫して表示するようになりました — Help 行の件数/一覧(×13)とローカライズセクションの本文 + 「N 個すべてのファイルにキーを追加」ノート(×8)が、まだ v1.85 以前の件数(8/9)のままでした。アプリ内ヘルプ §17 のアダプタ件数は、16 バンドル全体で 46 アダプタ — 英語 41 + ロシア語 5 に修正されました。
適合バッジのヒューリスティック以外に動作変更なし。新しいルート、キー、i18n の追加なし。
[1.97.0] — 2026-07-05
Dassault Systèmes スキャナソース + 3方面の品質スイープ。
- 新しいスキャンソース — Dassault Systèmes(親 career-ops パリティ、#1498)。
server/lib/sources/dassault.mjs+server/lib/portals/adapters/dassault.mjsは、親プロジェクトのゼロトークン Exalead「カード検索」プロバイダ(3ds.com/careers/jobsの背後にある公開フィード)をそのまま反映します。単一のグローバルエンドポイントなので、プロバイダ選択(provider: dassault)または3ds.comホストからの自動検出で、redirect:'error'とともにwww.3ds.comに SSRF ホスト固定されます。XML は DOM なし(<Hit>ごとの<Meta>マップ)で解析され、都市/国はローカライズされたカテゴリ文字列から取得され、求人はその公開 URL が*.3ds.com上にある場合のみ保持されます。レジストリは 46 アダプタ(EN 41 + RU 5)を提供するようになりました。ALL_ADAPTERSの件数、ソート済み id、/api/scan/sourcesの EN セットアサーションが 40 → 41 に引き上げられました。スイートtests/sources-dassault.test.mjs(10 ケース)。 - 親プロジェクトの堅牢性修正を移植。 Avature パーサは 2 つのライブテナントのマークアップ変種(位置インデックスの接尾辞付き
article--result+ クラスなしの JobDetail タイトルアンカー、#1541)を許容するようになりました。Get on Board は0/負のpublished_atを防御します(もう誤った 1970 年の日付はありません)。SuccessFactors は最終ページに上限を設け、MAX_JOBSを超えないようにします(#1528)。 - サーバ監査の修正。
safe-fetchは上限超過のレスポンスでもうハングしません — サイズ上限のパスが、破棄されたストリームが決して発火しない'end'イベントを待つ代わりに、Promise を直接解決するようになりました(大きなページの/api/pipeline/preview+ 自動パイプラインのフェッチを修正)。SSEstream.*のアクティビティログが再び到達可能になりました(/api/stream/チェックが包括的な「GET をスキップ」ガードより上に移動)。 - SPA 監査の修正。
#/statsタブスイッチャーが非同期レンダーの競合を防御します — 遅いタブの結果が、ユーザーがすでに切り替えた新しいタブを上書きできなくなりました。mock interview とネットワーキングの削除確認は、適切なタイトル + 本文を渡すようになりました(もう本文が空のダイアログはありません)。 - 翻訳の修正。 未翻訳の辞書値を修正 — ウクライナ語
config.modes*(Adaptive Framing / Exit Narrative / Location Policy)、ロシア語eval.jdLbl(「Job Description」)、イタリア語dash.quick.contactoSub(「referral」→「segnalazione」)— さらに ru/uk/ja/ko/zh-CN/zh-TW の CHANGELOG で英語の 16 ロケール の定型文をローカライズしました。
新規: server/lib/sources/dassault.mjs; server/lib/portals/adapters/dassault.mjs.
[1.96.0] — 2026-07-04
キャリアの方向性(Epic 27)。 新しい #/orientation ページは「どの方向が実際に自分に合うのか?」に答えます — 職業適性検査で得られるような読み解きですが、アンケートではなく、あなた自身の CV とプロフィールから推測します。プロフィールを生成 をクリックすると、モデルはあなたの 最も適したキャリアベクトル(8 つのアーキタイプ — 機能主義者、管理者、コミュニケーター、スペシャリスト、アナリスト、イノベーター、マネージャー、アントレプレナー — のうちどれが合うか、根拠とともに)、キャリアタイプの傾向、おすすめの職種、CV に結び付いた職業上の強み、働き方の傾向、そして開発の推奨事項を返します。これは あなたの CV がどう読めるかについての AI による振り返りであり — 心理検査ではありません:実績を捏造することはなく、数値スコアを測定されたものであるかのように報告することもありません。Markdown または PDF にエクスポートでき、ディスクには何も書き込まれません。
- 新しいルート
server/lib/routes/orientation.mjs(24 番目のルートモジュール)—POST /api/orientation/generateは共有プロバイダカスケードを通じて CV+プロフィール+ツーページャー+メモリからプロフィールのプロンプトを構築し、コピー&ペーストの手動フォールバックと ファイル書き込みなし を提供します。 - Growth ナビゲーショングループの下で、Markdown/PDF/コピーのために
report-export.jsを再利用します。 - テスト:
tests/orientation-routes.test.mjs(振り返りのフレーミング / 捏造されたスコアなし、CV/プロフィールをシードにした手動モード)。新しい i18n キー 7 個 ×16 ロケール、ヘルプ §28 ×16。
新規:#/orientation;server/lib/routes/orientation.mjs。
[1.95.0] — 2026-07-04
キャリアプラン(Epic 26)。 新しい #/career-plan ページは、あなたの CV とプロフィールを、具体的でパーソナライズされた成長プランに変えます。期間(6/12/24 か月)と任意の フォーカス を選ぶと、モデルは — あなたの CV、プロフィール、two-pager、メモを読んで — 出発点のスナップショット、強み/成長の SWOT、SMART / OKR / WOOP としての目標、代替の進路、ハード/ソフトスキルの計画、月ごとのロードマップ、進捗管理の方法、落とし穴、そしてサポートの一手を書き出します。あなたの資料が実際に示していることから先を計画し、あなたの経歴について事実を捏造することは決してありません。インラインで編集し、ユーザーレイヤー(config/career-plan.md)に 保存 し、Markdown または PDF に エクスポート できます。
- 新しいルート
server/lib/routes/career-plan.mjs(23 番目のルートモジュール)—GET/PUT /api/career-plan(config/career-plan.mdに書き込み)+POST /api/career-plan/generate(共有プロバイダーカスケード、手動フォールバック、捏造なし)。PATHS.careerPlan。 - 共有ヘルパー
report-export.js(v1.94.0)を Markdown/PDF/コピーに再利用し、新しい 成長 ナビゲーショングループを追加。 - テスト:
tests/career-plan-routes.test.mjs(範囲、GET/PUT のラウンドトリップ、期間を踏まえた CV/プロフィールをシードしたプロンプト)。16 ロケール すべてに 20 個の新規 i18n キー、ヘルプ §27 16 言語すべて。
新規: #/career-plan、server/lib/routes/career-plan.mjs、PATHS.careerPlan。
[1.94.0] — 2026-07-04
統計、刷新(Epic 25)。 #/stats ページは、実際のグラフと大幅に増えたデータを備えた、3 つのタブからなる 統計 セクションになりました。新しい 市場レポート タブは、あなたが選んだ地域と通貨で、あなたのターゲット職種の給与および労働市場分析をモデルに求めます — エグゼクティブサマリー、P10/P25/P75/P90 パーセンタイル付きのグレード別給与、上位の雇用主、需要のあるスキル一覧、福利厚生の頻度、オフィス/ハイブリッド/リモートの内訳、12〜24 か月のトレンド、そして交渉ガイダンス。すべての数値は、スクレイピングしたデータとしてではなく、モデルの知識に基づく方向性の推定値 として明示されます。新しい マイパイプライン タブは、あなた自身のトラッカーをチャート化します: スコア分布、ステータスファネル、上位の企業と職種、時系列の応募、そしてコンバージョン率。元のターゲット職種ビュー(国別の求人/給与 + 保存したスナップショットのトレンド)は 3 つ目のタブに移り、通貨セレクター と 職種別の求人 概要を備えるようになりました。
- どのレポートもエクスポート — 共有ヘルパー
report-export.jsを介して、Markdown または PDF にエクスポートするか、コピーします(Markdown blob のダウンロード。PDF は既存のインライン PDF ランナー経由)。 - 新しいルート
server/lib/routes/market.mjs(22 番目のルートモジュール)—POST /api/stats/marketは、あなたの CV/プロフィール(そのためあなたのターゲット職種を把握しています)、地域、通貨から市場分析プロンプトを構築し、共有プロバイダーカスケードを通じて実行し、キーがなければコピー&ペースト用のプロンプトにフォールバックします。ディスクへの書き込みなし。 - テスト:
tests/market-routes.test.mjs(地域/通貨の範囲、正直さを明示したプロンプト、CV/プロフィールをシードした手動モード)。16 ロケール すべてに 36 個の新規 i18n キー、ヘルプ §26 16 言語すべて。
新規: タブに刷新された #/stats、server/lib/routes/market.mjs、public/js/lib/report-export.js。
[1.93.0] — 2026-07-04
メモリレイヤー(Epic 24)。 新しい #/memory ページには、アシスタントが すべての タスクで念頭に置く、短く編集可能な「私についてこれを覚えておいて」というメモが保存されます:
- 一つのメモをどこでも —
bundleProjectContextにインライン展開されるため、メモはすべての AI リクエスト(評価、模擬面接、ネットワーキング、CV Studio)に すべての プロバイダーを横断して自動的に届きます。一度書けば、すべてを方向づけます。 - 事実ではなく方向づけ — あなたの好みや働き方(トーン、フォーマット、deal-breaker、ペース)を捉えるものであり、あなたの経歴に関する新しい事実の主張は決して含みません — それらは今も CV、プロフィール、two-pager にのみ存在します。ユーザーレイヤーの
config/memory.mdに保存され、更新で上書きされることはありません。 - あなたのデータから提案 —
POST /api/memory/suggestはあなた自身の応募トラッカーから行動パターンを抽出し、あなたがレビューして編集するための箇条書きを下書きします。トラッカーを読むだけで、事実を捏造することは決してなく、ライブ呼び出しも行いません。
新規: server/lib/routes/memory.mjs(21 番目のルートモジュール — GET/PUT /api/memory + POST /api/memory/suggest)、public/js/views/memory.js、PATHS.memory、そして bundleProjectContext に追加された config/memory.md ブロック。16 ロケール すべてに 11 個の新規 i18n キー。テスト: tests/memory-routes.test.mjs。
[1.92.0] — 2026-07-04
CV Studio(Epic 21)。 新しい #/cv-studio ページは、あなたの CV に正直で、ほとんどローカルで動く 3 つのツールを提供します:
- 履歴書診断 — 決定論的な 0–100 のスコアと、各チェックの説明(定量化されたインパクト、弱い動詞、バズワード、長さ、コアセクション、連絡先情報)。純粋なクライアントサイド(
window.CvDiagnostics)— LLM なし、捏造なし、すべての指摘が説明されるので、何を変えるかはあなたが決めます。 - プライバシーマスク — CV をサンプルやスクリーンショットとして共有する前に、PII(メール、電話番号、リンク/ハンドル、住所、任意で名前 → イニシャル)を伏せ字にします。すべてブラウザ内で実行され(
window.CvPrivacy)、何を伏せ字にしたかを正確に報告し、原文を保存することは決してありません。 - Make it human / voice match — 硬い一文や段落を貼り付けると、
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 ページは、あなたの CV・プロフィール・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)経由でライブ実行されます。キーがなければ、そのままコピー&ペーストできるプロンプトが得られます(誠実なフォールバック、捏造された回答なし)。
- 保存されたセッション — 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」をモデルにしています:
- ガイド付きビルダー — 一人称の「私は誰か」ナラティブ、「ターゲット環境」ノート、そして 5 つのチップリストエディタ: 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
Issue #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.netATS、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(2つのレジストリのルール)+ 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)を 3 つの新規ロケール向けにローカライズし、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 パリティ)。 スキャナーの 2 件のアップグレード:
- 再応募クールダウン (#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): スキャンされた求人が給与をオプションの末尾カラム(
url | <salary>)としてdata/pipeline.mdに保存されます。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 がセマバーのみを返すようになりました(リリース-プリーズ # x-release-please-version コメントを除去)。tests/detect-reposts.test.mjs を同梱。ソース数は 41 のまま — 再掲載は分析機能であり、新しいボードではありません。
[1.82.0] — 2026-06-30
NoDesk スキャンソース(親 career-ops v1.15.0 パリティ)。 ボード全体の NoDesk リモート求人 RSS フィードが第一級のスキャンソースになりました — 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 フィードが第一級のスキャンソースになりました — 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 言語として追加。 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 で着地した7件のソースにユーザー向けドキュメントを揃えます:
- ヘルプガイド(12ロケール)。 §5 に
content_filterブロック(説明/スニペットのキーワードゲーティング、location_filterの兄弟)と集約サイトに関するノートが追加されます。§7 はワンクリックスキャンの一巡と Source ドロップダウンの完全な列挙に7件の新規ソースを記載します。§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に7件すべての正確なprovider:/<provider>:設定ブロックを備えたコピー&ペースト用 “Aggregator boards” セクションが追加されます。docs/PROJECT.mdは 19 adapters に更新されます。docs/sdd/CONVENTIONS.mdは、2つのレジストリの区別(ドロップダウン用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 の構成駆動ソースに対する堅牢性の仕上げ。 リリース後レビューから得られた 3 つの小さな強化修正です(正常なスキャンでは動作変更なし):
- 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に加えて、垂直タブ、フォームフィード、および Unicode の行/段落区切り(\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 は解決済みの企業エントリを各フェッチャーまで通すようになり、フェッチャーがそれを読めるようになりました。7件すべてが#/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 プロバイダーサーフェスからも欠落していました。オプションのチェック項目(他の5つのライブ評価プロバイダーと同じ “set / unset (manual mode)” の文言)と github(+ その GITHUB_MODELS_MODEL)を /api/status/providers に追加したため、アクティブプロバイダーのルーティングと Health ページが6つすべてを反映するようになりました。tests/api.test.mjs のヘルス行テストを6つのプロバイダーすべてに拡張しました。
[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 には、6つのAIコーディングアシスタントが記載されています — Claude Code, Gemini CLI, Codex, Qwen Code, OpenCode, GitHub Copilot CLI。web-ui はこれら6つすべてをサポートするようになりました: 5つは既存のライブプロバイダー (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 は標準の6つをリストするようになり (Qwen CLI→Qwen Code に改名; Gemini CLI + GitHub Copilot CLI を追加)、README にはすべての機能が親プロジェクトに追跡できるよう、完全なモード参照とポータルアダプターのリンクテーブルが career-ops.org/docs に追加されます。tests/llm-provider-context.test.mjs はフェッチ境界マトリックスを6つのプロバイダーすべてに拡張し (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境界をモックし、5つのプロバイダー(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など)は結果を生成する前に確認の質問のために一時停止します。これにより、WebUIの 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対応)の3言語を追加し、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 行がサイドバーで3行に折り返されていました。ネイティブの <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 は引き続きMarkdownの信頼できる情報源です(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 を 1 回出力します。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): ソース別フェッチのタイムアウトを 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): ソース別フェッチのタイムアウトを 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 に給与レンジ(下限/上限)フィルタを追加し、ソース別フェッチのタイムアウトを延長。 結果テーブルに、テキスト・リモートのフィルタの隣に2つの数値入力(給与 下限 / 上限)が加わりました。各行のフリーテキスト給与(от 100 000 до 200 000 ₽、120000-150000 USD、$120K–$150K など)を数値レンジに解析し、レンジの重なり判定でマッチします。給与未掲載の行は残すため、フィルタは一覧を絞り込むだけでゼロにはしません(通貨は区別しません=為替換算なし)。さらに ソース別スキャンのフェッチタイムアウトを 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 разработчик」のようなクエリが 1 ページではなく全件を返します(例: 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 の進捗バー。 ソースのリクエストに期限がなく、停止した upstream(例:ブロックされた 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/ 配下のロケールごとに 1 ファイルと共通の i18n-dict.aliases.js に分割し、翻訳者が 1 言語だけを独立して編集できるようにしました(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 で承認されたドクトリン例外。UX-A5-r3: #/help の IntersectionObserver を rAF スロットル付き scroll リスナーに置換。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 + in-flight トークン + 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 すべてに初回起動クリーンアップセクションを追加。 初回スキャン前に 2 つの 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)
[1.58.52] — 2026-05-20
fix(ux): UX-A5 — #/help TOC スクロールスパイが正しく発火するように。 v1.58.45 の setTimeout(0) は router マウント前に発火していた。fix: headings の直接参照 + 二重 requestAnimationFrame。947 → 948 ユニット。(UX-A5)
[1.58.51] — 2026-05-20
chore(docs): v1.58.51 — v1.58.37 → v1.58.50 サイクル(14 releases)の最終ハウスキーピング。 コード変更なし。qa/ を整理し、すべてのバージョン固定ドキュメントを archive/v158-cycle/ へ移動;6 つの perennial がルートに残る。REGRESSION-FINAL §13 が v1.58.37→.50 のすべての不変条件を文書化。ベースライン不変(947/947)。(housekeeping)
[1.58.50] — 2026-05-20
docs: DOC-1 — qa/REGRESSION-FINAL.md に §5a を追加(サーバエラーボディの英語固定ポリシーを明文化)。 NEW-D4 を not-a-finding として close。FIX-PROMPT-FINAL-EXHAUSTIVE.md の v1.58.37 → v1.58.50 キューを完了(14 リリース)。 946 → 947 ユニット。(DOC-1)
[1.58.49] — 2026-05-20
chore(tooling): TOOL-1 — make clean-test-fixtures と親プロジェクト data/pipeline.md から example.com 行を削除するスクリプトを追加。 --dry-run 対応。CI-isolated テスト 4 件。942 → 946 ユニット。(TOOL-1)
[1.58.48] — 2026-05-20
fix(ux/onboarding): UX-D-B — プロフィールがデフォルトテンプレートのままなら #/dashboard に警告バナーを表示。 /api/health で Profile customized: false を検出すると .hero-banner--warning を表示。新 i18n キー onboarding.fixtureWarning + onboarding.fixProfile × 8。941 → 942 ユニット。(UX-D-B)
[1.58.47] — 2026-05-20
fix(ux/naming): UX-D-C — トップバーの「Quick scan」を Scan を開く に改名(動作はナビゲーションのみで実際にスキャンは開始しないため)。 8 言語で更新。940 → 941 ユニット。(UX-D-C)
[1.58.46] — 2026-05-20
fix(ux): UX-D-D — #/apply チェックリストの {company}-{role} を URL/JD 由来のスラグに置換。 以前はプレースホルダがそのまま表示されていた。新 extractSlugs + substitutePlaceholders が Greenhouse/Lever/Ashby/Workable/SmartRecruiters/Workday を認識。フォールバックは [company]/[role]。939 → 940 ユニット。(UX-D-D)
[1.58.45] — 2026-05-20
fix(ux): UX-D-K — #/help の TOC スクロールスパイで現在のセクションをハイライト。 IntersectionObserver が現在表示中の H2 に対応する TOC リンクに .toc-current を付与。938 → 939 ユニット。(UX-D-K)
[1.58.44] — 2026-05-20
fix(ux): UX-D-L — #/deep で開いた Saved-research の brief にインライン × クローズボタンを追加。 以前はスクロール・ナビゲーション以外で閉じる手段がなかった。新キー deep.closeBrief × 8。937 → 938 ユニット。(UX-D-L)
[1.58.43] — 2026-05-20
fix(ux): UX-D-F — #/evaluate で空送信時に専用のローカライズトーストを表示。 以前は「短すぎる」と同じメッセージ。新キー eval.emptyJd × 8。936 → 937 ユニット。(UX-D-F)
[1.58.42] — 2026-05-20
fix(ux): UX-D-J — すべての advisor ページで ETA チップを統一。 以前は #/auto のみ「⏱ ~1–2 min」表示。#/evaluate・#/deep・5 つの mode ページにも ⏱ ~30s を追加(新 advisor.eta キー × 8)。935 → 936 ユニット。(UX-D-J)
[1.58.41] — 2026-05-20
fix(ux/truthfulness): UX-D-I — コストヒントがタブ復帰時 + providers-changed イベントで再取得。 以前は 1 回のみ取得していたため、別タブでプロバイダを変更すると古い値が表示されていた。934 → 935 ユニット。(UX-D-I)
[1.58.40] — 2026-05-20
fix(ux/docs): UX-D-H — career-ops.org/docs/... 深層リンクがクリック可能であることを保証する regression-lock。 新しい tests/external-doc-links.test.mjs が views/.js と docs/help/.md を検査。932 → 934 ユニット。(UX-D-H)
[1.58.39] — 2026-05-20
fix(ux): NEW-D2 — ダッシュボードヘッダの Refresh ボタンが明示的なフィードバックを提供。 ページリロードなしで再フェッチ + 再レンダリング。2 つの新 i18n キー。931 → 932 ユニット。(NEW-D2)
[1.58.38] — 2026-05-20
fix(a11y): NEW-D3 (WCAG 4.1.2) — #/tracker 検索入力にプレースホルダとは異なるローカライズ aria-label を追加。 以前は placeholder のみで SR には目的が伝わらなかった。新 i18n キー track.searchAria × 8 言語、placeholder と異なる文字列。930 → 931 ユニット。(NEW-D3)
[1.58.37] — 2026-05-20
fix(i18n): NEW-D1 — #/pipeline の H1 を es/pt-BR/ru でローカライズ + 2 件の RU タイトル漏れを修正。 新しい tests/i18n-no-latin-leaks.test.mjs が contacto.title / health.title の RU の漏れも検出して同時修正。928 → 930 ユニット。(NEW-D1)
[1.58.36] — 2026-05-20
chore(docs): v1.58.36 — v1.58.x サイクル終了時の完全ハウスキーピング sweep。 コード変更なし。(1) qa/: 3 つのバージョン固定スナップショット(REGRESSION-END-TO-END-v1.58.16/33/35.md)を qa/archive/v158-cycle/ に移動。(2) REGRESSION-FINAL.md に §12 を追加(v1.58.4 → v1.58.35 全不変条件)。(3) UX-AUDIT-PROMPT.md に 30 行追加。(4) docs/architecture/ 更新(FRONTEND drawer、TESTING 合計 928/62/20/23)。(5) CLAUDE.md に「v1.58.x サイクルの教訓」セクション追加。(6) README ×8 に「通知 🔔」行追加 + テスト数の古い値を修正。ベースライン変更なし。(housekeeping)
[1.58.35] — 2026-05-20
fix(ui): v1.58.35 — 通知ドロワーが自動オープンしないように修正 + ヘルプに §18「通知」を追加(ユーザー報告)。 v1.58.34 のバグ: .notif-drawer { display: flex } が UA の [hidden] { display: none } に勝っていた。.notif-drawer[hidden] { display: none } を明示。ドロワーは鈴アイコンのクリックでのみ開きます。8 言語のヘルプに §18 を追加(カテゴリ表 + キーボード操作)。927 → 928 ユニット。(ユーザー報告)
[1.58.34] — 2026-05-20
feat(ui): v1.58.34 — 通知ドロワー(U-13 完全クローズ)。 v1.58.33 のキャプチャ上に: 新 API UI.onToast(fn)、トップバーのベル 🔔 + 未読バッジ、右スライド <aside role="dialog"> でローカライズされたタイトル / 空状態 / エントリ (notif.* × 8)。Esc + 閉じる + ベル再クリックで閉じる。926 → 927 ユニット。(U-13 follow-up)
[1.58.33] — 2026-05-20
fix(ux): U-13 + U-14 + U-15 — トーストジャーナル (50 件キャップ + UI.getToastHistory()) + .page-header h1 + p 安全弁ルール + #/cv の未保存変更インジケータ。 v1.58.x シリーズ完了。新規 i18n キー cv.unsaved × 8 言語。925 → 926 ユニット。(U-13/U-14/U-15)
[1.58.32] — 2026-05-20
fix(ux): U-12 — #/help TOC のフィルタ入力に min-width: 16ch を設定。 KO/JA のプレースホルダが切れないよう .help-toc__filter クラスを追加。924 → 925 ユニット。(U-12)
[1.58.31] — 2026-05-20
fix(ux): U-11 — Tracker の Legitimacy 列ヘッダにローカライズされた情報チップ ⓘ + ツールチップで High/Caution/Suspicious スケールを説明。 新規 i18n キー track.col.legitimacy.help × 8 言語。923 → 924 ユニット。(U-11)
[1.58.30] — 2026-05-20
fix(ux): U-10 — Tracker の Normalize / Dedup / Merge ボタンが data/applications.md 空のとき無効化。 ローカライズされたツールチップ (track.fixEmpty × 8 言語) が理由を表示。922 → 923 ユニット。(U-10)
[1.58.29] — 2026-05-20
fix(ux): U-9 — #/pipeline のカウンタ ↔ フィルタ行が狭幅で縦積みに。 新しい .pipeline-controls クラスと @media (max-width: 720px) でフィルタを横幅 100% に拡張。921 → 922 ユニット。(U-9)
[1.58.28] — 2026-05-20
fix(ux): U-8 — 7 つのモードページで生成プロンプトブロックがデフォルト折り畳み。 <details class="prompt-block"> でラップし、サマリは「Show prompt (N lines)」をローカライズ表示(prompt.show / prompt.lines × 8)。Copy + Run-live は引き続き上部に。920 → 921 ユニット。(U-8)
[1.58.27] — 2026-05-20
fix(ux): U-7 — verify-pipeline.mjs の === ASCII 区切りをモーダルから除去。 ハンドラ内で ^={10,}$ 正規表現により事前にストリップ。919 → 920 ユニット。(U-7)
[1.58.26] — 2026-05-20
fix(ux): U-6 — #/scan の “Active companies N/M” チップにツールチップ + aria-label で N と M の意味を提示。 新規 i18n キー scan.activeCo.help × 8 言語。918 → 919 ユニット。(U-6)
[1.58.25] — 2026-05-20
fix(ux/ia): U-5 — Dashboard の CTA を整理(ヘッダの Open Pipeline ボタンと Quick-action の Scan all sources タイルを削除)。 サイドバーとヒーロが既に両ルートをカバーしており、v1.58.3 QA の 4× Pipeline / 4× Scan を 2× ずつに減らした。917 → 918 ユニット。(U-5)
[1.58.24] — 2026-05-20
fix(ux): U-4 — エラートーストの “(METHOD /path · HTTP NNN)” 末尾を折りたたみ <details> に格納。 技術的詳細は DOM 内に保持(BUG-006 不変条件)、ヘッドラインは人間向けの文だけが残る。新規 i18n キー toast.details × 8 言語。916 → 917 ユニット。(U-4)
[1.58.23] — 2026-05-20
fix(ux): U-3 — #/followup の lastContact プレースホルダを今日 − 14 日に動的算出。 固定 2026-04-21 は時間経過で陳腐化していた。new Date() ベースで setDate(getDate() - 14) し、ISO YYYY-MM-DD を生成。915 → 916 ユニット。(U-3)
[1.58.22] — 2026-05-20
fix(ux): U-2 — #/auto の H1 が先頭の ✨ のせいで 2 行に折り返さないようにした。 auto.title から ✨ を分離し、<span class="page-icon" aria-hidden="true"> に移動。.page-header--icon は grid レイアウトで絵文字専用列を確保。914 → 915 ユニット。(U-2)
[1.58.21] — 2026-05-20
fix(ux): U-1 — #/cv の H1 + サブタイトルが他ページと統一(v1.56.0 UX-9 のチップを設計上撤回)。 .cv-breadcrumb チップを削除し、<h1 class="page-title"> + <p class="page-subtitle"> を復活。シングル <h1> 不変条件は維持。913 → 914 ユニット。(U-1)
[1.58.20] — 2026-05-20
fix(i18n/platform): I-6 — フッターのショートカットは Mac で ⌘K、それ以外で Ctrl+K、動詞はローカライズ。 修正前はプラットフォーム/言語に関わらず CTRL+K — search のリテラルが表示されていた。top.langhint に {hotkey} — 検索 形式を導入し、applyFooterHotkey() が navigator.platform に応じて {hotkey} を置換。915 → 916 ユニット。(I-6)
[1.58.19] — 2026-05-20
fix(i18n): I-4 — ロシア語 #/followup のラテン語 cadence / follow-up 流出を排除。 RU の followup 関連文字列(H1、ヒント)に cadence、follow-up、scope、timeline が混じっていたが、ロシア語ネイティブ表現に置換。914 → 915 ユニット。(I-4)
[1.58.18] — 2026-05-20
fix(i18n): I-3 — ヘルプ TOC 項目 2/5/13/14 の非ラテンロケールでの英語残りを排除。 修正前、複数のロケールヘルプ束で ## 2. App settings & API keys、## 5. Portals & Sources、## 13. Mode prompts、## 14. Apply checklist が混在していた(ru/ja/ko/zh-CN/zh-TW)。今は 8 言語全てで完全にローカライズ。913 → 914 ユニット。(I-3)
[1.58.17] — 2026-05-20
fix(i18n): I-2 — Saved-research の日付ラベルを Intl.RelativeTimeFormat でロケール化。 public/js/views/deep.js の formatRelative() は英語の today / 1d ago / Nd ago をハードコードしていた。Intl.RelativeTimeFormat(I18n.getLang(), { numeric: 'auto' }) に置換 — ブラウザネイティブで「今日/昨日」「сегодня/вчера」等のローカライズ表現を取得。7 日以上前は Intl.DateTimeFormat(locale, { dateStyle: 'medium' }) にフォールバック。912 → 913 ユニット。(I-2)
[1.58.16] — 2026-05-20
fix(ui): ブランドボタンのホバーちらつき(ユーザー報告)。 原因:.btn-primary / .btn-danger のデフォルト背景は linear-gradient(...)、:hover は単色 var(--rausch-dark)。CSS はグラデーション↔単色を補間できず、180ms の transition: background がスナップして白〜ピンクのちらつきが見えていた。修正は public/css/app.css:ホバーでもグラデーションを維持し filter: brightness(0.92) で減光 — filter はあらゆるブラウザでスムーズに補間される。.btn の transition 一覧に filter var(--transition) を追加して減光がアニメーションする。911 → 912 ユニット。(ユーザー報告)
[1.58.15] — 2026-05-20
fix(a11y/i18n): I-1 — トップバー検索の aria-label と非表示 <label> を i18n 化。 従来は全 8 言語で英文の aria-label がスクリーンリーダーに読まれていた。public/js/app.js に汎用 data-i18n-aria-label フックを追加 — applyI18n() が data-i18n / data-i18n-placeholder と同様に言語変更時に aria-label を差し替える。新規 i18n キー 2 つ(top.search.aria、top.search.label)を 8 言語に追加。任意の将来のコントロールでも再利用可。910 → 911 ユニット。(I-1)
[1.58.14] — 2026-05-20
fix(ux): M-9 — 接続バナーの「更新」ボタンにフィードバックを追加(従来は無音リロード)。 v1.58.13 以前はクリックで即 location.reload() を呼んでいた。今は「更新中…」トーストを即表示し、sessionStorage['refreshedToast'] をセット、ボタンを disabled に(連打で重ねない)、リロードを 200ms 遅延させてトーストを描画。次回ブートで app.js がフラグを検出し、成功トースト「更新しました」を出す。新規 i18n キー 2 つ(common.refreshing、common.refreshed)を 8 言語で追加。909 → 910 ユニット。(M-9)
[1.58.13] — 2026-05-20
fix(ux): M-8 — #/apply のチェックリストがインタラクティブに。 v1.58.13 まで「▶ チェックリスト生成」は項目 0〜7 を等幅 <pre> で表示するだけでチェックできなかった。今は各項目を本物の <input type="checkbox"> として描画し、<label> でラップ(クリック領域は行全体・WCAG 2.5.5)。状態は localStorage['applyChecklist:'+slug] に URL ごとに永続化 — 3 つチェック → リロード → 3 つとも残る。ボタン:未チェック項目をコピー(未消化の項目を - markdown で書き出し)と リセット。新規 i18n キー 5 つ(apply.checklist.copyUnchecked, resetBtn, copied, copyFailed, reset)を 8 言語で追加。パーサが項目を見つけなかった場合の防御的フォールバックあり。908 → 909 ユニット。(M-8)
[1.58.12] — 2026-05-20
fix(ux): M-7 — コストヒントがアクティブプロバイダに追従(OpenRouter で誤った固定額が出ない)。 UI.providerCostHint() は既に /api/status/providers 経由でプロバイダ対応していたが、public/js/api.js のマップに anthropic/gemini/openai/qwen のみ。v1.57.0 で OpenRouter が 5 番目のプロバイダとして加わってからも汎用フォールバック 0.03 に落ち、表示名も小文字 openrouter のままだった。今回 EST に openrouter: null を追加(ルーターがモデルを選ぶためコストは可変)、=== null 分岐でローカライズされた「cost varies (router picks)」を出力。NAME には openrouter: 'OpenRouter'。新規 i18n キー cost.varies を 8 言語で追加。907 → 908 ユニット。(M-7)
[1.58.11] — 2026-05-20
fix(ux): M-4 — 保存済みリサーチカードのタイトル↔日付の間隔を構造的 CSS に(従来はインライン margin)。 v1.58.3 の MASTER リグレッションで、一部のカードに software-engineer-generaltoday(タイトルと日付の間に空白なし)が確認された。原因は 2 つの裸の <span> の間の style="margin-left: 8px" インラインに依存しており、特定エントリで崩れていたこと。public/js/views/deep.js を修正 — 2 つの <span> を .saved-card__title + セマンティックな <time class="saved-card__date" datetime="…"> に置き換え、フレックスコンテナ .saved-card でラップ。間隔は gap: var(--space-2, 8px) で制御するため崩れず、<time> で a11y/SEO の意味付けも得られる。906 → 907 ユニット。(M-4)
[1.58.10] — 2026-05-20
fix(ux): M-2 — 結果モーダル表示前に進捗トーストを排水。 #/cv の sync-check クリック時、「Running cv-sync-check.mjs…」トーストが右下に残ったまま結果モーダルが開き、注目を奪い合い狭い画面では視覚的に重なっていた。Health ページの Doctor / verify-pipeline ボタンは UI.modal() 前に UI.dismissToast() を明示呼び出ししていたが、cv.js の sync-check のみ抜けていた。public/js/api.js で UI.modal() の最初の実行文として dismissToast() を呼ぶよう変更(境界での多層防御)。あわせて cv.js のハードコード英語文字列を t('cv.syncCheckRunning') / t('cv.syncCheck') に置換し、BUG-008 不変条件(モーダルタイトル == ローカライズボタンラベル)も満たす。新規 i18n キー 2 つを 8 言語で追加。905 → 906 ユニット。(M-2)
[1.58.9] — 2026-05-20
fix(a11y): M-1 — フォームフィールドに可視 :focus-visible リングを復元(WCAG 2.4.7 Level AA)。 v1.58.3 MASTER リグレッションで getComputedStyle(focusedInput) が outline: rgb(255,255,255) none 1.5px を返し、none キーワードがリング幅を 0 px に潰していたことを確認。根本原因:.input, .textarea, .select { outline: none } と .searchbar input { outline: none } のベース指定がグローバル *:focus-visible より優先度が高く、ページあたり 88 個のフォーカス可能要素のキーボードフォーカスリングを静かに消していた。修正は public/css/app.css — 明示的に .input:focus-visible/.textarea:focus-visible/.select:focus-visible と .searchbar input:focus-visible を追加し outline: 2px solid var(--rausch) + 半透明 box-shadow を付与。マウスフォーカス(:focus)は従来通りクリーン。904 → 905 ユニット(静的契約ガード);Playwright 60 → 61(Tab トラバーサル)。(M-1)
[1.58.8] — 2026-05-20
feat(health): OPENAI_API_KEY / QWEN_API_KEY / OPENROUTER_API_KEY を #/health に表示(GEMINI_API_KEY と同様)。 v1.57.0 で OpenRouter が 5 番目のヘッドレス live-eval プロバイダとして追加され、v1.55.3(UX-2)で 4 プロバイダのオンボーディングも表示されるようになった。しかし #/health ページでは GEMINI_API_KEY と ANTHROPIC_API_KEY のみ表示され、残り 3 つはルーティング対象でありながら見えない状態だった。ユーザー要望:「set / unset (manual mode)」行のパターンを全ヘッドレスプロバイダに拡張。server/lib/routes/health.mjs に 3 つの任意チェック行を追加(/api/status/providers と同じ isUsableKey ゲートに接続)。hasOpenAIKey() / hasQwenKey() / hasOpenRouterKey() は既にインポート済みで未使用だった。Health ビューは body.checks を反復するため 8 言語の文字列追加は不要。903 → 904 ユニット。(ユーザー要望)
[1.58.7] — 2026-05-20
fix(security): NEW-2 — isValidJobUrl がペアのテンプレートプレースホルダ構文(${…}、{{…}})を拒否するよう修正、エラーメッセージと整合化。 POST /api/pipeline の 400 エラーは 「contain no script or template characters」 と謳っていたが、v1.58.3 MASTER リグレッションで実際には ASP/EJS 形式の <%…%> のみが [<>"'\\s]ガードの副作用で阻止されており、JS テンプレートリテラル${TEST}と Mustache/Handlebars{{TEST}}は素通りだったと確認。fix-prompt の Option A(正規表現をメッセージに合わせる)で、[server/lib/security.mjs](https://github.com/Fighter90/career-ops-ui/blob/main/server/lib/security.mjs) に新規TEMPLATE_PATTERNS 配列(/${[^}]}/、/{{[^}]}}/)を追加し、new URL(…)の前にhasTemplatePlaceholder(url) でチェック。**ペア** のみを拒否({normal}` のような正当な ATS パスはそのまま許可)。901 → 903 ユニット。(NEW-2)
[1.58.6] — 2026-05-20
fix(a11y/i18n): BUG-008-tb — トップバーの Doctor モーダルタイトルがローカライズ済みボタンラベルと一致するよう修正。 v1.58.0 でクローズした台帳 BUG-008(「モーダルタイトル == ローカライズ済みボタンラベル」)は Health ページのエントリーには適用されていたものの、v1.58.3 の MASTER リグレッションで トップバー のエントリーが不変条件に違反していることが判明:UI 言語に関わらず、トップバーの Doctor をクリックすると常に doctor(英語小文字)というモーダルタイトルが表示されていました。修正は public/js/app.js:118 — リテラル 'doctor' を I18n.t('top.doctor', 'Doctor') に置換。top.doctor キーは 8 言語すべてに既に存在(EN Doctor、ES/pt-BR Diagnóstico、KO 진단、JA 診断、RU Диагностика、zh-CN 诊断、zh-TW 診斷)し、ボタンが data-i18n="top.doctor" で宣言しているキーと同一です。tests/qa-report-fixes.test.mjs に静的契約ガードを追加。900 → 901 ユニット; Playwright 60/60。(BUG-008-tb)
[1.58.5] — 2026-05-20
fix(ui): NEW-3 — #/followup Run-live 二重 POST は 再現不能 と判定、Playwright リグレッションガードで施錠。 v1.58.3 の MASTER リグレッションでは(monkey-patch した window.fetch で計測)、#/followup の Run live を一度クリックしただけで /api/mode/followup への同一 POST が約 2 秒以内に 2 件 観測されました(company/role/notes 入力済み、日付は空欄)。fix-prompt の「まず再現せよ」原則に従い、public/js/views/mode-page.js::submit() を精査した結果:(a) Run live と Generate prompt はいずれも <button> 要素で onClick を 1 つだけ持ち、親 <form> も addEventListener('submit') もないため二重発火が成立しない、(b) UI.withSpinner()(FIX-L1)がリクエスト発火中は button.disabled = true を設定し、二度目の物理クリックを根本でブロック、の 2 点が確認できました。tests/playwright-smoke.mjs に新規テストを追加し、リグレッションの再現レシピを忠実に踏みます — company/role/notes を入力し、日付を意図的に空欄のままにし、Run live と同じ submit() を呼ぶ手動ボタンを 1 回クリックして、3 秒間で POST /api/mode/followup が 正確に 1 件 であることを検証します。ロケール非依存のセレクタ(8 言語すべてで ▶ グリフは同一)を採用し、同一ブラウザコンテキスト内で先行する言語切替テストの影響を防ぐため addInitScript で career-ops-ui:lang=en を事前注入。Playwright 59 → 60。元の QA 観測はレシピとして記録するに留め、製品コード変更は不要。(NEW-3)
[1.58.4] — 2026-05-19
fix(security): NEW-1 — すべてのレスポンスに Content-Security-Policy を付与(従来はループバック時に欠落)。 v1.58.4 以前は isPubliclyExposed() が真(HOST がループバック外)のときだけ CSP ヘッダーを付与していたため、127.0.0.1 上では / も /api/health も CSP なし で応答し、UI.md() のエスケープ優先契約が唯一の XSS 防御でした。v1.58.3 の MASTER リグレッション(§5)がこれを stop-ship 不変条件として指摘。CSP は 無条件 となり、バインドアドレスに関係なく全レスポンスで同一です:default-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'。script-src は 'unsafe-inline'/'unsafe-eval' を一切許可しません。ディレクティブ集合は従来の公開時専用ポリシーから変更なし(SPA に最適化済み — Inter 用に Google Fonts を許可リスト化)のため、視覚・機能のリグレッションはありません。tests/security-headers.test.mjs を書き直し、Playwright のルート巡回(en/ru/ja/zh-TW × 7 ルート)で CSP 違反 0 を検証。ユニット 900 · Playwright 58→59 · e2e 20/20+23/23。後続の fix-prompt 項目はプロジェクト方針に従い one-fix リリースとして順次公開します。(NEW-1)
[1.58.3] — 2026-05-19
fix(deep): R-2 / FIX-C1 — 調査出力から孤立/不均衡なエージェント足場タグを除去。 v1.58.0 の cleanLlmMarkdown は対ブロックと末尾の開きタグのみ除去。v1.58.2 の深い回帰で、開きの無い孤立 </tool_response>(</thinking>)がそのまま保存ブリーフに出ていた。最終の保守的スイープで、単独の足場トークン(開閉・均衡不問)、Anthropic ツール XML(<invoke>/<parameter>/antml:*)、tool_* フェンスを除去。純粋・冪等。実 <https://…> 自動リンク/コードは保持。FIX-C2 は再現せず(i18n.js は既に <html lang> 設定+navigator.language 検出)。両者を回帰ガードで固定。896 → 900 ユニット · Playwright 58/58。v1.58.3 fix-prompt の残りは one-fix ship でキュー(バッチ不可)。
[1.58.2] — 2026-05-19
fix(i18n): I18N-011 — #/help の目次を非 EN 7 ロケールでローカライズ。 TOC は docs/help/<lang>.md の ## 見出しから生成。3/4/6/7/8/9/10/11/12 章が es/pt-BR/ko/ja/ru/zh-CN/zh-TW で 英語 タイトルのままで、サイドバーは翻訳済みなのに TOC は英語でした。各見出しをサイドバーの nav.* キーと同一の語にローカライズ(単一の真実源 — TOC とサイドバーが一致)、章番号と (#/route …) は原文維持。EN は不変。v1.58 QA 唯一の i18n バックログを解消。ドキュメントのみ;896/896 ユニット · 33/33 help · Playwright 58/58。
[1.58.1] — 2026-05-19
fix(test): CI 隔離の checkProfileCustomized ガード(v1.58.0 へのパッチ)。 v1.58.0 は助言的 pre-commit は通過したが ci.yml(Node 18/20/22)で失敗:テストが cache-bust 動的 import + PATHS 書換を使用したが paths.mjs はプロジェクトルートをプロセスごとに一度解決するため無効だった。堅牢な静的ガード(allow-list + ^(…)$/i アンカー済み正規表現;“test” を含む実名は誤検知しない)に置換。プロダクションコード変更なし;publish-package.yml も解除。896/896 ユニット · Playwright 58/58。qa/v158-regression/ 参照。
[1.58.0] — 2026-05-19
fix(qa): 外部 QA レポートのバグ一掃 + 整形済みの調査出力。 修正: BUG-001 #/followup の任意の日付をクライアントで ISO YYYY-MM-DD 検証; BUG-003 ブロック引用内でも **太字**/`code`/リンクが描画(全 Help ページ); BUG-005 重複 URL は「すでにキューにあります — スキップ」; BUG-006 無効 URL メッセージを人間向けに((POST /api/pipeline · HTTP 400) 文脈は意図的に維持); BUG-007/008 「Running doctor.mjs…」トーストをモーダル前に消す(新 UI.dismissToast())、モーダル題はボタンのローカライズ済みラベル; BUG-010 #/reports 空状態にサブタイトル; BUG-002/UX-032 checkProfileCustomized() がテストフィクスチャ名を「未カスタマイズ」と判定(親の profile.yml/cv.md は不変 — ルール #1); I18N-012/013 ロシア語 Deep research を実翻訳。新規: cleanLlmMarkdown() がエージェント足場(<tool_call>{…}</tool_call>, <tool_response>, <thinking> …)を #/deep・保存済み調査から除去(全プロバイダ+保存済みファイル配信時); #/outreach→#/contacto エイリアス(BUG-004); クライアントのネットワークエラーを I18n.t() で多言語化(8 ロケール; サーバの details は意図的に英語の診断)。テスト: 新規 tests/qa-report-fixes.test.mjs(10)・tests/llm-output.test.mjs(5)、881 → 896 ユニット、Playwright 58/58。未変更(理由付き): BUG-009(#/cv H1 は設計上、WCAG single-h1)、親データ(parent-owned)、minor i18n/UX のロングテールはバックログ。詳細は CHANGELOG.md。
[1.57.2] — 2026-05-19
fix(config): /#/config「validation failed」の真の原因 — SPA が注入する lang フィールド。 public/js/api.js は すべての JSON POST ボディに lang を自動付与します(LLM ルートが UI ロケールを拾うため)。/api/config は LLM ルートではなく lang は設定キーでもないため、validateConfig の(正しく、セキュリティ上重要な)未知キー拒否が 毎回の保存 に 400 を返していました:validation failed — lang: not a known config key。ブラウザ限定の症状で、curl/インプロセスの再現は lang を送らないため v1.57.0/.1 はメッセージを改善しても原因は残っていました。設定ルートは検証前にトランスポート用の lang を除去するように。KNOWN_KEYS 書き込みフィルタは依然として真に未知のキーを破棄 — インジェクション対策は不変。実際の保存ボタンを押す新しい Playwright フォーム巡回で発見。テスト: 新規 tests/playwright-forms.mjs(26、npm run test:e2e:browser に組込)で全フォームを巡回、config-endpoint にブラウザ等価ケース。879 → 881 ユニット、Playwright 32 → 58。詳細は CHANGELOG.md。
[1.57.1] — 2026-05-19
fix(ux): すべての API エラーが「何が失敗し、どこで、なぜ」を表示。入力エラー文も最大限に説明的に。 サーバは以前から { error, details: ["フィールド: 理由", …] } を返していましたが、各フォームはトップ行(「validation failed」)しか表示せず、/#/config(および全画面)でどのフィールドが不正か分かりませんでした。api.js がフィールド単位の details をメッセージにサイト全体で畳み込み(1 箇所の修正で全フォームが恩恵)、リクエスト文脈 (メソッド /パス · HTTP NNN)(どこで)を付与、非 JSON レスポンスは生本文の断片を表示、ネットワークエラーにもメソッド+パスを付与。err.details も公開。validateConfig のメッセージは最大限に説明的(何が悪く、どう直すか)に。シークレットキーは入力値を一切エコーしません(文字数のみ)—実キーの打ち間違いがトースト/ログに漏れません。PORT の範囲も実際に検証(99999 は拒否)。/#/config の PORT/HOST は実デフォルト(4317 / 127.0.0.1)を初期表示。エラートーストは長め(9–20 秒)に表示し、折り返し/スクロールして見切れません。テスト: 新規 tests/config-validation-detail.test.mjs(12)、874 → 879。詳細は CHANGELOG.md。
[1.57.0] — 2026-05-19
feat(provider): OpenRouter を 5 番目のヘッドレス・ライブ評価プロバイダとして追加 + fix(config): どの API キーを保存しても「validation failed」になる不具合を修正。 貼り付けたキーは末尾の改行や空白を伴うことが多く(OS のクリップボード、プロバイダコンソールの「コピー」ボタン)、1.57 以前は すべての プロバイダで改行ガードに引っかかり、さらに $ 終端アンカーの ANTHROPIC_API_KEY 正規表現が本物の Anthropic キーを誤って弾いていました。validateConfig は検証の 前 に各値を正規化(トリム)し、ルートはトリム後の値を保存(ランタイムで認証成功、\n による .env 破損なし)、Anthropic チェックは堅牢な sk-ant- プレフィックス + 長さに変更(共通の isUsableKey() ≥ 20 文字が本当の「実在キーか」判定)。内部改行は引き続き拒否(.env インジェクション対策)。OpenRouter が一級プロバイダに:/#/config の OPENROUTER_API_KEY 一つで 300 以上のモデルにアクセス。auto 順序の 最後(Anthropic → Gemini → OpenAI → Qwen → OpenRouter)なので既存設定が黙ってリルートされることはなく、LLM_PROVIDER=openrouter で固定可能。OpenAI/Qwen と同じ _tailProvider() 経路で /api/evaluate・/api/deep・/api/mode/:slug に接続、/api/status/providers と Health ダッシュボードに表示。OpenAI 互換クライアント(新規依存なし — 直接 fetch、AbortController タイムアウト、キーは記録しない)で推奨の HTTP-Referer/X-Title ヘッダ付き。モデルのドロップダウンはライブ:OPENROUTER_MODEL は GET /api/openrouter/models(OpenRouter 公開カタログのサーバ側プロキシ — CSP connect-src 'self' を維持)から取得し、取得失敗時は厳選フォールバック、10 分間のメモリキャッシュ。8 ロケールに新 i18n キー(config.openrouter*)。テスト: 新規 tests/openrouter-route.test.mjs・tests/openrouter-model-selector.test.mjs、env-config/openai/provider-selector を拡張。831 → 855。詳細は CHANGELOG.md。
[1.56.4] — 2026-05-19
feat(ui): UX-N2 — グローバル検索入力にプラットフォーム対応の可視 ⌘K / Ctrl K ヒント。 Cmd/Ctrl+K(検索にフォーカス)ショートカットは aria-label/ソースにしか無く、晴眼ユーザーは発見できずアプリが実際より遅く感じられた。控えめな <kbd class="kbd-shortcut"> が検索ピル末尾に表示され、起動時にプラットフォーム判定(navigator.platform/userAgent)で data-mac/data-other から埋められる:macOS/iOS は ⌘K、その他は Ctrl K。aria-hidden="true"(既存の aria-label が AT に伝えるため二重通知を避ける)かつ pointer-events:none(装飾)。既存の Cmd/Ctrl+K キーバインドは不変。新しい i18n キーなし(グリフは共通);バッジは既存 .searchbar の flex 子(ラッパー/絶対配置不要 — input は既に flex:1)。テスト: 新しい CI 隔離のソース静的スイート tests/cmdk-hint-visible.test.mjs(5):.searchbar 内に <kbd class="kbd-shortcut">;aria-hidden="true" + data-mac/data-other 両方;app.js が navigator 判定で埋める;(e.ctrlKey||e.metaKey)&&e.key==='k' → search.focus() バインド健在(リグレッションガード);app.css が .kbd-shortcut を整形し display:none にしない。826 → 831。feat(ui) · test: tests/cmdk-hint-visible.test.mjs。詳細は CHANGELOG.md。
[1.56.3] — 2026-05-19
fix(reliability): プロバイダーのキー検出が空文字だけでなくプレースホルダー / 短すぎる値も拒否。 親 .env のプレースホルダー GEMINI_API_KEY が「✓ set」と表示され、かつ有効な ANTHROPIC_API_KEY を差し置いてアクティブプロバイダーに選ばれていた。effectiveEnv() は undefined/'' しか弾かないため、10 文字のゴミが本物のキー扱いに:オンボーディングバナーが GEMINI ✓ set、GET /api/status/providers が activeProvider: "gemini" を返し、有効な 108 文字の Anthropic キーを無視して死んだキーで全ライブ ⚡ 評価が無言で失敗していた。新しい純粋関数 isUsableKey()(env-config.mjs)はシークレットを「設定済み」とみなす条件を ≥ 20 文字(どの対応プロバイダーのキーもこれより短くない — Gemini AIza… ≈ 39、Anthropic sk-ant-… ≈ 100+、OpenAI ≥ 40、Qwen ≈ 35)かつ既知のプレースホルダー(your_*_here、changeme、placeholder、<…>、同一文字の繰り返し…)でないこと、に限定。hasAnthropicKey()/hasGeminiKey()(anthropic.mjs)、hasOpenAIKey()/hasQwenKey()(openai.mjs)、GET /api/health の GEMINI_API_KEY/ANTHROPIC_API_KEY 行(生 process.env から同じ effective+plausible ビューへ移行)に一律適用 — health ページ・プロバイダーエンドポイント・OR ルーターが常に一致。selectActiveProvider() は不変(正しい keysConfigured を受け取るだけ)。テスト: 新しい CI 隔離スイート tests/key-detection-rejects-placeholder.test.mjs(5):isUsableKey 単体 + in-process createApp() で報告シナリオを再現(一時 .env に 10 文字 GEMINI_API_KEY + 実 ANTHROPIC_API_KEY)— gemini は keysConfigured になく、activeProvider === "anthropic"、/api/health 行も一致。既存の effective-env 階層テスト 4 件は短すぎるスタブを現実的な長さに延長(契約は不変)。821 → 826。fix(reliability) · test: tests/key-detection-rejects-placeholder.test.mjs。詳細は CHANGELOG.md。
[1.56.2] — 2026-05-19
feat(a11y): UX-N1 — ルートごとのロケール対応 document.title(マルチタブの識別 + スクリーンリーダーのページ変更通知)。 修正前は 24 ルートすべてが index.html の静的 <title>(「career-ops — command center」)のままで、タブ名が同一・ブックマークが汎用・「ページが変わりました」通知も毎回同じだった。public/js/router.js の focusNewView() がビュー自身のローカライズ済み <h1 class="page-title"> からタイトルを導出 — 「ビュー — career-ops」— するため、自動翻訳(新しい i18n キー不要)かつルートごとに一意。初回ペイントの guard より前に設定するので最初のタブにもタイトルが付く(v1.56.0 UX-12 の tabindex 設定と同じ順序)。見出しが無いビューは career-ops — command center にフォールバック。テスト: 新しい CI 隔離のソース静的スイート tests/document-title-per-route.test.mjs(4):focusNewView が document.title を代入;タイトルは <h1> 由来(ルートごと + ローカライズ、単一リテラルでない);代入は !firstPaintDone より前;製品デフォルトあり。817 → 821。feat(a11y) · test: tests/document-title-per-route.test.mjs。詳細は CHANGELOG.md。
[1.56.1] — 2026-05-19
fix(a11y): ルーター管理の tabindex="-1" 見出しフォーカスで出ていた偽のブランドフォーカスリングを抑制。 public/js/router.js は各クライアント遷移で遷移先ビューの見出しに tabindex="-1" を付与し .focus() する(スクリーンリーダーが新ページを読み上げるため)。tabindex="-1" 要素はキーボードで到達不能だが、Chromium の :focus-visible ヒューリスティックが依然グローバルのブランドリング(*:focus-visible { outline: 2px solid var(--rausch) })を描き、遷移ごとに ページ見出しの周りに赤い矩形(例:#/dashboard の「Command Center」)を表示 — それが images/dashboard-*.png のヒーロー画像にも焼き込まれていた。修正は 1 つのスコープ付きルール [tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none }(WAI-ARIA APG の管理フォーカス・パターン)。実際のキーボードフォーカスはグローバル *:focus-visible リングを維持(WCAG 2.4.7 維持);skip-link のリングは無影響(<a> であり tabindex="-1" ではなく、より高い詳細度)。8 つの images/dashboard-*.png を再生成 — 赤い枠は消えた。テスト: 新しい CI 隔離のソース静的スイート tests/managed-focus-no-ring.test.mjs(4):グローバル *:focus-visible リングが定義されたまま(WCAG 2.4.7 非後退);[tabindex="-1"]:focus,:focus-visible ⇒ outline:none;抑制ルールはグローバルの後(カスケード安全);修正はスコープ付き(全体 *:focus{outline:none} なし)。tests/dashboard-initial-focus.test.mjs と対。813 → 817。fix(a11y) · test: tests/managed-focus-no-ring.test.mjs。詳細は CHANGELOG.md。
[1.56.0] — 2026-05-19
feat(ux): LOW 仕上げバンドル — UX-9 / UX-10 / UX-11 / UX-12(1 つのグループ化マイナーリリース)。 UX-9 #/cv:ページタイトルを控えめな .cv-breadcrumb チップに降格し、うるさいサブタイトルは <h1> の title ツールチップへ移動 — ユーザーの CV(プレビューの名前)が視覚的優先権を持つ。F-V54-A 不変は維持 — 依然 正確に 1 つの <h1>、依然 .page-title。UX-10 新しい共有ヘルパー UI.providerCostHint(t) を #/auto・#/evaluate・#/deep・各 #/<mode> の ⚡ ライブ実行の隣に表示;GET /api/status/providers(v1.55.3)を再利用:キーありで 「推定コスト: OpenAI gpt-5-codex · ~$0.04/eval」(桁レベル、“~”)、キーなしで ⚡ は手動プロンプトをコピー(API コストなし)と明示;fail-soft。UX-11 #/help:TOC フィルタが ちょうど 1 つ のセクションに絞られたら 300ms アイドル後にそこへスクロール(デバウンス;0 や 2 以上では発火しない)。UX-12 #/dashboard:初回ペイントで <h1> をフォーカス可能(tabindex="-1")にし、#content は aria-live="polite" を維持(起動時に読み上げ)— フォーカスは 奪わない(skip-link との競合回避、v1.41.0 の決定)。新 i18n キー cost.estimate、cost.manual ×8;新しい .cv-breadcrumb/.cost-hint CSS。テスト: 4 つの新しいソース静的 CI 隔離スイート(cv-breadcrumb 3、run-cost-line 4、help-toc-autoscroll 4、dashboard-initial-focus 3);既存 cv-single-h1/help-nav-a11y ロック更新(不変保持)。800 → 813。4 つのライブ Playwright プローブ、コンソールエラー 0。feat(ux) · 4 test suites。詳細は CHANGELOG.md。
[1.55.8] — 2026-05-19
feat(tracker): サーバーサイド・ページネーション + クリック可能なファネルチップ(UX-8)。 サーバー: GET /api/tracker に任意の ?page / ?pageSize / ?status クエリパラメータを追加。無しなら応答は従来の { rows: [...] } とバイト単位で同一(既存の呼び出し元/テストは無影響)。有りなら { rows: slice, total, page, pageSize, funnel } を返す — pageSize は [1,500]、page は ≥1 にクランプ、status は rows+total をフィルタ、funnel は全履歴のステータス→件数内訳(ページ/フィルタに依存しないのでチップが常に正確)。#/tracker: 上部に新しいクリック可能なファネルチップバー — 「すべてのステータス · N · Applied · N · Interview · N …」(順序 Applied → Responded → Interview → Offer → Rejected → Discarded → Evaluated → SKIP)。チップのクリックで Status フィルタを設定(アクティブなチップの再クリックで解除);アクティブチップは aria-pressed + ハイライト。8 ロケールの新 i18n キー track.funnelAria;新しい .tracker-funnel/.tracker-chip/.tracker-chip--active CSS。test: tests/tracker-server-paged.test.mjs(新規、7 ケース、CI 隔離、エフェメラルポートのインプロセス Express + 一時 CAREER_OPS_ROOT applications.md — CLAUDE.md #2/#8):back-compat(パラメータ無しで厳密に {rows});?page&pageSize スライス + total/page/pageSize/funnel 合計 N;最後の部分ページで重複なし;範囲外ページ ⇒ 空 rows + 有効 total;?status= が total/rows をフィルタしつつ funnel は全履歴;pageSize キャップ;+ チップバーのソース静的ロック。793 → 800。feat(tracker) · test: tests/tracker-server-paged.test.mjs。詳細は CHANGELOG.md。
[1.55.7] — 2026-05-19
feat(pipeline): >1000 行で vanilla-JS の行仮想化(UX-7)。 #/pipeline は全行を描画(filtered.forEach(list.appendChild(urlRow)))していた — スキャンはキューを数千の URL で満たすため、数千の行ノード(各々 flex div + <a> + ボタン 2 個)がフィルターのキー入力ごとに同期構築され、DOM とアクセシビリティツリーを溢れさせた。新しい vanilla-JS 仮想化(react-window 相当、依存なし):VIRTUALIZE_THRESHOLD = 1000 超で #/pipeline は固定高(70vh)のスクロールビューポートになり、縮まないスペーサー(flex:0 0 auto、height = 行数 × 56px)がリスト全体の実スクロールバーを保ち、rAF スロットルのスクロールリスナーがビューポート ± 5 行バッファのみ描画(一度に N ではなく ~16–19 ノード)。閾値以下では元の単純描画をバイト単位で維持するため、典型的なパイプラインと既存テスト/e2e は影響なし。各仮想化行は URL で曖昧性を解消した ▶/✕ aria-label を維持(F-V54-B 回帰ロック)。ウィンドウ計算は純粋な computeWindow() ヘルパー。test: tests/pipeline-virtualize.test.mjs(新規、5 ケース、CI 隔離、ソース静的):~1000 の数値閾値;≤閾値分岐は forEach→appendChild 維持;>閾値分岐は rAF スクロールリスナー + スペーサーで slice(start,end) 描画;computeWindow() が [0,total] ± バッファでクランプ;行が ▶/✕ aria-label 維持。788 → 793。ライブ Playwright プローブ(1200-URL フィクスチャ):scrollHeight≈67248、DOM に ~16–19 ノードのみ、ウィンドウがスクロールを端から端まで追従、コンソールエラー 0。feat(pipeline) · test: tests/pipeline-virtualize.test.mjs。詳細は CHANGELOG.md。
[1.55.6] — 2026-05-19
feat(scan): 副次フィルターを「詳細フィルター」ディスクロージャの背後へ整理(UX-4)。 #/scan は全フィルター — フリーテキスト、リモート/ハイブリッド/オンサイト、スコープ、ソース、スキャン後の stack/level/dynamic ファセットチップ — を同じ重みで積み上げ、コントロールの壁だった。今や日常フィルターは可視のまま(フリーテキスト + リモート/ハイブリッド/オンサイト;🌐 スキャンボタンは既にコントロールカードに別置)、副次は <details class="scan-advanced"><summary>詳細フィルター</summary> の背後に折り畳み:スコープ + ソースのセレクト、そして別途ファセットチップ群(新しい結果集合がチップの壁ではなくテーブルで始まり、チップ行が 1 つ以上ある時のみ描画)。8 ロケールの新 i18n キー scan.advancedFilters;新しい .scan-advanced サマリースタイル(控えめな ⚙ アフォーダンス、マーカーなし、開時に太字)。test: tests/scan-advanced-disclosure.test.mjs(新規、6 ケース、CI 隔離、ソース静的):.scan-advanced フック + scan.advancedFilters ラベルの <details>/<summary> が存在;フリーテキスト + リモートは可視のまま;スコープ + ソースはディスクロージャ内;chipsContainer が <details>;.scan-advanced summary がスタイル付き;scan.advancedFilters ×8。782 → 788。feat(scan) · test: tests/scan-advanced-disclosure.test.mjs。詳細は CHANGELOG.md。
[1.55.5] — 2026-05-19
feat(dashboard): 2 つの P0 CTA + 焦点となる最近のアクティビティヒントのヒーロー(UX-3)。 #/dashboard は同じ重みの約 30 ノードで開き、「次に何をすべきか」が不明瞭だった。新しい .dash-hero ブロックがページヘッダー直下に配置:2 つの P0 ジャーニー — ✨ URL を自動パイプライン、🌐 今すぐスキャン — を大きな .btn-hero ボタンに昇格し、単一の 焦点となる最近のアクティビティヒント(「最新の評価: <スコア> — <タイトル>」、レポートへリンク;コールドスタートでは dash.heroNoEval の案内空状態)が、再訪ユーザーには中断地点を、新規ユーザーには重要な唯一のアクションを示す。2 つの主要ボタンはヘッダーから削除(副次的な「📋 パイプラインを開く」のみ残置)しアクションの重複を回避。ステータスバケットは目立つ .badge から控えめな .dash-chip ピルへ降格。8 ロケールの新 i18n キー dash.lastEval、dash.heroNoEval;新しい .dash-hero/.btn-hero/.dash-chip CSS。test: tests/dashboard-hero.test.mjs(新規、5 ケース、CI 隔離、ソース静的):.dash-hero が存在し Quick-actions グリッドに先行;両 P0 CTA が /auto+/scan ルートの .btn-hero;焦点 dash.lastEval + 空状態 dash.heroNoEval;バケットが .dash-chip;CSS 存在;dash.lastEval+dash.heroNoEval ×8。777 → 782。feat(dashboard) · test: tests/dashboard-hero.test.mjs。詳細は CHANGELOG.md。
[1.55.4] — 2026-05-19
feat(ux): Run の隣に正直な auto-pipeline ETA + スキャン中の目立つ Stop(UX-6)。 #/auto:新しい .auto-eta ヒント — “⏱ 約1〜2分”(キー auto.eta、title は auto.etaTitle)— が Run ボタンの隣に表示され、ワンクリックの約束が所要時間について コミット前に 正直になる;文言は career-ops.org/docs(「URL を貼る → 1〜2 分で完全レポート」)と一致。#/scan:数分のクロールが実行中(aria-busy)の間、Stop を低コントラストのゴーストボタンから目立つ破壊的ボタンへ昇格(新しい .btn-danger — 塗りつぶし、高コントラストの白文字 on コーラル、太さ 600)。setScanRunning(running) が scan-stop-btn を btn-danger(実行中)と btn-ghost(アイドル、どのみち非表示)で切り替え、負荷時でもユーザーが Stop を見つけ信頼できるようにする。8 ロケールの新 i18n キー auto.eta、auto.etaTitle;新しい .btn-danger/.auto-eta CSS。test: tests/auto-eta-stop.test.mjs(新規、4 ケース、CI 隔離、ソース静的):#/auto が runBtn の隣に .auto-eta クラスで t('auto.eta') を描画;auto.eta ×8;setScanRunning(running) が Stop を btn-danger に昇格;.btn-danger が高コントラストの白文字で存在。773 → 777。feat(ux) · test: tests/auto-eta-stop.test.mjs。詳細は CHANGELOG.md。
[1.55.3] — 2026-05-19
feat(onboarding): 画面上の 4 プロバイダ OR ステータス — コールドスタート・バナー + アクティブ・プロバイダ・チップ(UX-2、HIGH)。 新しい読み取り専用エンドポイント GET /api/status/providers → { activeProvider, activeModel, keysConfigured }。keysConfigured は llm.mjs のゲートと同じ実効 env ビュー(process.env ∨ 親 .env);activeProvider は OR ルーターが実際に選ぶもの — env-config.mjs の新しい純粋ヘルパー selectActiveProvider() が providerOrder() を走査(キーのない LLM_PROVIDER ピンは null)。秘密は返さない — プロバイダ名 + モデル id のみ。SPA シェルがグローバルなオンボーディング領域(#onboarding-banner、app.js が描画、CSP 安全な DOM のみ)を表示:0 キー → 赤いバナー + #/config?tab=api-keys への CTA;≥1 キー → 控えめなチップ にアクティブなプロバイダ+モデル。看板の差別化要因(「Anthropic / Gemini / OpenAI / Qwen のいずれかが自動順で動く」)を試行錯誤ではなく画面上で発見可能にする。8 ロケールの onboarding.* i18n キー;新しい .onboarding-warn/.onboarding-ok CSS。test: tests/onboarding-key-banner.test.mjs(新規、9 ケース、CI 隔離):selectActiveProvider の意味論;GET /api/status/providers のインプロセス(エフェメラルポート + 一時 CAREER_OPS_ROOT .env で実際の親キーを決して読まない — CLAUDE.md #2/#8);静的 SPA 配線 + onboarding.* ×8 カバレッジ。764 → 773。feat(onboarding) · test: tests/onboarding-key-banner.test.mjs。詳細は CHANGELOG.md。
[1.55.2] — 2026-05-18
fix(cv): #/cv マークダウンエディタに記述的で自己完結したアクセシブル名を付与(F-V55-H / UX-5)。 #/cv の主エディタ <textarea id="cv-editor"> は今や新キー cv.editorAria 経由で記述的な aria-label — “CV マークダウンエディタ — マークダウン形式のプロフェッショナル履歴書” — を持ち、可視の「Markdown」セクション見出しから継いでいた簡素な名前を置き換える。注:F-V55-H の症状(aria-label/labels のみ検査)と異なり、このフィールドは名前が無かったわけではない — v1.47.0(WS2 #16)が既に aria-labelledby → <h3 id="cv-md-heading">Markdown</h3> で結び付けており、スクリーンリーダーは「Markdown、編集、複数行」と読み上げていた。v1.55.2 はその簡素な「Markdown」を自己完結ラベルへ改善する。冗長な aria-labelledby は削除(残れば死んだマークアップ — ARIA 優先順位で aria-label が勝つ);可視の <h3>Markdown</h3> は晴眼ユーザー向けに残る。WCAG 1.3.1 + 4.1.2;v1.54.5 の batch-tsv 修正(F-V54-C)と並行。test: tests/cv-editor-a11y.test.mjs(新規、3 ケース、CI 隔離、auto-stepper-prerender.test.mjs 流のソース静的):#cv-editor は空でないフォールバックと共に t('cv.editorAria', …) で自己命名;cv.editorAria は 8 ロケール全てに存在し空でない;要素に冗長な aria-labelledby なし。761 → 764。fix(cv) · test: tests/cv-editor-a11y.test.mjs。詳細は CHANGELOG.md。
[1.55.1] — 2026-05-18
fix(auto): #/auto マウント時に 5 段階パイプラインのステッパーを事前レンダリング(F-V55-E / UX-1、シニア観察 S-4 再オープン)。 #/auto は今や、文書化された 5 段階の概要 — 検証 → 取得 → 評価 → レポート保存 → トラッカー追加 — を画面マウントの瞬間に表示する。以前は最初の SSE イベントまで空白だった。従来は <ol class="auto-stepper"> が display:none で生成され、renderStepper() は setStep() / run() からのみ到達したため、コールドスタートのユーザーは Run をクリックする前にドキュメントが約束するパイプラインを決して見られなかった。ステッパーは今やマウント時に 5 段階すべてが pending 状態で可視となり、aria-label(auto.stepperAria)を持つため支援技術がその領域を読み上げる。F-V55-E(a11y/静的保証レンズ)と UX-1(約束忠実度レンズ)をクローズ — 同一修正、両レンズ。test: tests/auto-stepper-prerender.test.mjs(新規、4 ケース、CI 隔離、router.test.mjs 流のソース静的):STEPS 配列は正確に 5 つの正準段階が順序どおり;stepperEl はマウント時に display:none ではなく auto.stepperAria を持つ;マウントスコープの renderStepper() 呼び出しが function setStep( に先行する;auto.stepperAria は 8 ロケール全てに存在。757 → 761。fix(auto) · test: tests/auto-stepper-prerender.test.mjs。詳細は CHANGELOG.md。
[1.55.0] — 2026-05-18
feat(llm): ヘッドレス・ライブ評価が “OR” で動作 — Anthropic | Gemini | OpenAI | Qwen、どのキーが設定されているかで自動選択。 ユーザー要望により、web-ui の ⚡ ライブ評価は Anthropic/Gemini だけでなく設定されているどの API キーでも動作するようになった。LLM_PROVIDER に openai と qwen を追加;auto(既定)はキーが存在する最初のプロバイダを使い、Anthropic → Gemini → OpenAI → Qwen の順で優先する。明示値は 1 つを固定する;キーなしで強制されたプロバイダは依然として手動プロンプト経路へフォールバックする。新しい server/lib/openai.mjs — 依存ゼロの OpenAI 互換 Chat Completions クライアント(anthropic.mjs と同じ安全な直接 HTTPS パターン:AbortController タイムアウト、キーは決してログ出力しない、effectiveEnv() キー解決により親 .env のキーが再起動なしで動作)。1 つのコア(runOpenAICompatible)が runOpenAI(api.openai.com)と runQwen(Alibaba DashScope の OpenAI 互換モード;中国本土ホストは raw .env で QWEN_BASE_URL によりエンドポイントを上書き)を支える。SDK なし、任意の CLI 実行なし — 親プロジェクトは CLI 非依存のまま(Claude Code · Codex · Gemini · OpenCode · Qwen · Copilot · Kimi);これはヘッドレスな API キー経路のみを拡張する。OpenAI/Qwen のテールは全評価サーフェスに配線済み:/api/evaluate、/api/deep、/api/mode/:slug、/api/auto-pipeline SSE — auto の優先を保持するため Anthropic(インライン)+ Gemini(サブプロセス)分岐の後で参照され、Anthropic が使うのと同じバンドル済みコンテキストのインライン化を行う。env-config.mjs:QWEN_API_KEY(秘匿)+ QWEN_MODEL(非秘匿)を KNOWN_KEYS/KEY_GROUPS.core に追加;LLM_PROVIDERS と providerOrder() を拡張;OPENAI_API_KEY は今や第一級のヘッドレス・プロバイダ・キー(以前は保存のみ)。#/config API キータブ:LLM_PROVIDER セレクトに openai/qwen を追加;新しい QWEN_API_KEY + QWEN_MODEL フィールド(キュレートされた qwen-max/qwen-plus/qwen-turbo/qwen2.5-* リスト);タブ先頭の新しいノートが CLI 非依存の親 vs web-ui のヘッドレス評価と OR 順を説明する。8 ロケール全体に新しい i18n キー。test: tests/openai.test.mjs(新規、9 ケース、CI 隔離):OpenAI/Qwen 成功 + ブロック配列コンテンツ、Bearer 認証、既定および QWEN_BASE_URL 上書きエンドポイント、4xx/5xx/不正形式、max_tokens クランプ、タイムアウト、effectiveEnv キー検出、キー無漏洩カナリア。tests/provider-selector.test.mjs は v1.55.0 の providerOrder/LLM_PROVIDERS/SECRET サーフェス + OpenAI/Qwen テール配線向けに更新。748 → 757。feat(llm) · test: tests/openai.test.mjs · test: tests/provider-selector.test.mjs。詳細は CHANGELOG.md。
[1.54.10] — 2026-05-18
fix(auto-pipeline): SSE クライアント切断の衛生 — 不安定な Playwright e2e ジョブを潰す。 Playwright e2e ジョブが断続的に赤になっていた(個別テスト 32/32 通過するが not ok 2 - tests/playwright-smoke.mjs):#/auto SSE ストリームが進行中にページを閉じると、サーバーの次の res.write() が EPIPE/"aborted" で拒否され、—レスポンスに 'error' リスナがないため— Node がそれを uncaughtException に昇格させ、node:test が “asynchronous activity after the test ended” として報告した。auto-pipeline.mjs の openSse() は今や no-op の res.on('error') を登録し、send() を res.writableEnded || res.destroyed で保護する(try/catch で包む)— 消えたクライアントは例外ではなく想定内である。これは単なるテスト修正ではなく、正しい本番 SSE 衛生である。tests/playwright-smoke.mjs:Cmd+K テストは実際の外向き URL(https://example.com/jobs/123)を使っていたがモーダル出現を待つだけだったため、closePage() がテスト終了後にサーバーの進行中の safeGet() を中断していた。今やパイプラインが終端状態に達するまで待つ(閉じる前に fetch が正常に解決するように)。共有 closePage() ヘルパ(window.stop() してから閉じる)と after フックの server.closeAllConnections() は多層防御として残る。検証:連続 8/8 グリーン実行(6× node --test + 2× browser-smoke)、以前は ~2 回に 1 回赤。tests/auto-pipeline.test.mjs +1 静的ケースで openSse 切断衛生コントラクトを固定(res.on('error') リスナ + writableEnded||destroyed ガード + try で包んだ書き込み)。747 → 748。fix(auto-pipeline) · test: tests/auto-pipeline.test.mjs。詳細は CHANGELOG.md。
[1.54.9] — 2026-05-18
fix(llm): リクエスト時に親 .env の LLM キーを尊重する — 古い/無効なプロバイダへの誤ルーティングを停止。 ANTHROPIC_API_KEY が構成済みプロバイダであっても、ライブ評価が “Gemini API error: API key not valid” で失敗することがあった。根本原因: hasAnthropicKey() / hasGeminiKey()(および runAnthropic のキー/モデル検索)が起動時の process.env スナップショットのみを読んでいた。サーバー起動後に Anthropic キーが親 .env に追加されても、実行中プロセスはそれを決して見ず → Anthropic 検出が false になり、評価は process.env に実際にあった古いキー(しばしば古く無効な GEMINI_API_KEY)へフォールバックした。Gemini 実行パス(親 Node サブプロセス)は既に親のライブ .env を読んでいたため、両プロバイダがキーを一貫しない形で解決していた。env-config.mjs の新しい effectiveEnv(key, envFilePath): 空でない process.env 値が優先(シェル export と POST /api/config のライブ適用をカバー)、そうでなければ現在の親 .env ファイルを参照する。anthropic.mjs は今や ANTHROPIC_API_KEY、ANTHROPIC_MODEL、Gemini キーチェックをこれ経由で解決するため、親 .env に設定されたキーがサーバー再起動なしで尊重され、キー検出がリクエストが実際に送るキーと常に一致する。プロバイダ順序は不変(auto → Anthropic-次に-Gemini);これは検出のみを修正する。キーは決してログ出力も返却もされない(REVIEW-B4 無漏洩テストは引き続き通過)。tests/anthropic.test.mjs は CI 隔離向けに書き直し(temp CAREER_OPS_ROOT、動的 import)、正確なバグを再現する 2 つの新ケースを含む(キーが親 .env のみ → 検出される;process.env が未設定のとき runAnthropic が親 .env のキー + モデルを送信)。tests/env-config.test.mjs +3 effectiveEnv ケース(process.env の優先、空文字列を未設定として扱う .env フォールバック、ファイル欠如 / キー欠如 / パスなし → undefined)— 新分岐の 100%。742 → 747。fix(llm) · test: tests/anthropic.test.mjs · test: tests/env-config.test.mjs。詳細は CHANGELOG.md。
[1.54.8] — 2026-05-18
feat(config): Modes フィールドフォームが(空/スタブのファイルでも)常に career-ops.org のフィールドガイダンス付きで正規スキーマをレンダリングする。 v1.54.3 の Modes フィールドフォームは既存の ## セクションのフィールドのみをレンダリングしていた — そのため新規・空・スキーマ非準拠の modes/_profile.md(例:よくある 1 行スタブ)では “No ## sections found — use the raw editor below.” にフォールバックし、ユーザーはフィールドを得られなかった。ユーザー要望(“разбей по полям … описание полей возьми из career-ops.org/docs”)により、フォームは今や常に文書化された順序で 5 つの正規フィールドをレンダリングする(Target Roles, Adaptive Framing, Exit Narrative, Comp Targets, Location Policy)。ファイルにあれば事前入力され、なければ空だが編集可能 — そのため真新しいプロファイルもフォームだけで完全に記入できる。各フィールドは career-ops.org 正規 Quick Start §Step-5 由来の説明(Target Roles / Adaptive Framing / Exit Narrative / Comp Targets / Location Policy に何を入れるか)を表示し、スクリーンリーダー向けに aria-describedby で配線される。見出しバリアント許容:テンプレートの ## Your Target Roles(等)は ## Target Roles と同じ正規フィールドにマップされるため、テンプレートもサーバースキャフォールド慣習もフォームを壊さない。collect() は今やタグ付きペイロードである:レンダリングされた見出しがファイルの既存の見出しと正確に一致するときの非破壊的な { sections } マージ(前文 + 触れていない + カスタムセクションがバイト安定で残る)、またはファイルにスキーマが欠けていたときにスキーマ準拠ドキュメントをブートストラップ/正規化する { markdown } 全ファイル再構築。再構築パスは config.js で確認ゲート付き(親ファイルを置き換える — WS2 #4 破壊的保存不変条件)、既存の前文(または文書化されたデフォルト)を保持し、非正規セクションを verbatim で維持する。8 ロケール全体に 6 つの新 i18n キー(config.modesDescTargetRoles … config.modesDescLocationPolicy + config.modesFormRebuildBody)。tests/modes-form.test.mjs は v1.54.8 契約向けに書き直し:スキーマ + 正規順序、config.js のペイロード/確認配線、8 ロケールでの各フィールドの文書由来説明の存在、canonicalKey “Your X” 許容、リストのラウンドトリップ安定性、ブートストラップ常時レンダリング保証、そしてデータ安全性を備えたタグ付き sections-vs-markdown の collect()。実際の親スタブファイルに対してライブ検証済み(5 フィールド + 説明が表示、コンソールエラー 0)と隔離スタブフィクスチャ(記入 → 確認ゲート保存 → 5 つの正規セクションすべて永続化)。feat(config) · test: tests/modes-form.test.mjs。詳細は CHANGELOG.md。
[1.54.7] — 2026-05-18
fix: W-001 — コード/スタイルのアセット + SPA シェルが Cache-Control: no-store で配信されていた(デプロイ衛生)。 SPA は api.js / router.js / 各ビューをバージョンクエリ文字列なしの素の <script src> でロードし、ビルドステップがない(コンテンツハッシュなし)ため、デプロイ後にブラウザがキャッシュされた古いバンドルを数時間配信し続けることがあった → クエリ文字列ルートで stale-cache 404(v1.29.2 回帰中にライブ観測;回帰ラン W-001)。server/index.mjs は今や express.static の setHeaders フック経由で .js / .mjs / .css / .html に Cache-Control: no-store を設定し、SPA シェルの catch-all(sendFile を使い setHeaders を回避する)にも明示的に設定するため、ブラウザはルーティングを駆動するコードを常に再検証する。非コードの静的アセットは express.static のデフォルトキャッシングを維持する。セキュリティヘッダー(CSP / nosniff / frame-deny / referrer-policy)は不変 — 既存の security-headers スイート(8 ケース)が新テストと並んでグリーンで実行され検証済み。テストファイル tests/asset-cache-control.test.mjs を +1 — 4 ケース(JS アセット no-store、CSS no-store、静的 index.html no-store、SPA catch-all のディープルートシェル no-store)、隔離された CAREER_OPS_ROOT に対して実アプリをブート。さらに tests/playwright-smoke.mjs のフレーキーな teardown 修正(別の test(e2e) コミット):auto-pipeline の SSE スモークテストが今や finally でリーダーをキャンセル + fetch を中断し、after フックが残存ソケットを強制クローズして、v1.54.6 の Playwright e2e ジョブを赤くしていた teardown 後の “Error: aborted” を排除。738 → 742。fix · test: tests/asset-cache-control.test.mjs。詳細は CHANGELOG.md。
[1.54.6] — 2026-05-18
fix(a11y): S-7 — #/help の back-to-top ボタンが正規のセレクタークラス back-to-top を持つ。 #/help のフローティング back-to-top ボタンは正しく動作していたが(ライブ検証済み)、そのクラスリスト(btn btn-primary help-back-top)は spec §2 #28 テストが狙う .back-to-top セレクター規約の外にあった — より厳格なセレクターならフレーキーだったはず(回帰ラン S-7、“イージーウィン”)。ボタンは今や正規の back-to-top クラスも持つ。純粋に加算的で CSS no-op:help-back-top(既存の CSS フック)は変わらず、back-to-top には CSS ルールがない — 安定したテスト/自動化ハンドルにすぎない。ライブ検証済み:document.querySelector('.back-to-top') がボタンを解決、aria-label 維持、コンソールエラー 0。tests/help-nav-a11y.test.mjs の既存 #12 ケースを、back-to-top ボタンのクラスリストが正規 back-to-top セレクターを含むというアサーションで拡張(新ファイルなし)。fix(a11y) · test: tests/help-nav-a11y.test.mjs。詳細は CHANGELOG.md。
[1.54.5] — 2026-05-18
fix(a11y): F-V54-C — #/batch の TSV エディターにアクセシブルな名前がある。 #/batch の TSV <textarea> は aria-describedby 経由で配線されたヒントはあったがアクセシブルな名前がなかった — <label htmlFor> なし、aria-label/aria-labelledby なし(回帰ラン F-V54-C; WCAG 1.3.1 Info & Relationships / 4.1.2 Name, Role, Value)。aria-describedby は名前ではなく説明を供給するため、スクリーンリーダーはラベルのない “edit text” を読み上げた。textarea は今や新しい i18n キー batch.tsvAria 経由の aria-label を持ち、すでに *Aria キーを使う兄弟のラン制御入力と一貫する;既存の describedby ヒントは保持される。ライブ検証済み:aria-label 存在 + ローカライズ、aria-describedby 維持、コンソールエラー 0。新しい i18n キー batch.tsvAria を 8 ロケール全体に追加。テストファイル tests/batch-tsv-accessible-name.test.mjs(2 ケース:batch-tsv ブロックが describedby ヒントを保ちつつ t(batch.tsvAria) 経由の aria-label を持つ;batch.tsvAria が 8 ロケールで定義)を +1;736 → 738。fix(a11y) · test: tests/batch-tsv-accessible-name.test.mjs。詳細は CHANGELOG.md。
[1.54.4] — 2026-05-18
fix(a11y): F-V54-B — #/pipeline の行アクションボタンにアクセシブルな名前がある。 #/pipeline の行ごとの ▶(評価)および ✕(削除)ボタンは title 属性のみのアイコン専用だった(回帰ラン F-V54-B; WCAG 4.1.2 Name, Role, Value)。title は信頼できるアクセシブルな名前ではないため、スクリーンリーダーユーザーは区別のつかない “button” の長い連なりを聞き、削除がどの行に当たるか判別できなかった。両ボタンは今や、新しい shortUrl() ヘルパー経由のコンパクトな URL(host + …/ + 末尾 2 つのパスセグメント;パース不能な入力には末尾スライスのフォールバック)で曖昧さを解消した明示的な aria-label を持ち、a11y ツリーは例えば “Delete: hh.ru/…/vacancy/12345” と読み上げる。新しい i18n キーなし — common.delete / pipe.evaluateBtn + URL を再利用。ライブ検証済み:1385 行、各ボタン名が行ごとに一意、コンソールエラー 0。テストファイル tests/pipeline-row-action-names.test.mjs(4 ケース:両ボタンが shortUrl(url) で配線 + ちょうど 2 つのそのラベル、shortUrl が使用前に宣言、同一ホスト別求人の URL は collapse しない、ベアホスト / パース不能 / 空のフォールバック)を +1;732 → 736。fix(a11y) · test: tests/pipeline-row-action-names.test.mjs。詳細は CHANGELOG.md。
[1.54.3] — 2026-05-18
feat(config): #/config の “Modes” タブ向け構造化フィールドフォーム(もう生の markdown ではない)。 “Modes” タブは modes/_profile.md を ## セクションごとに 1 つの生の <textarea> として編集していた(v1.36.0 のセクション単位の粒度)。ユーザー要望により、今や文書化されたスキーマから派生した構造化フィールドフォームをレンダリングする(career-ops.org Quick Start §Step-5): Target Roles / Adaptive Framing / Comp Targets → 追加/削除可能な反復ラベル付きライン入力(フィールドごとにロール/アングル/comp 1 行、+ Add line / 行ごとに aria-label 付き ✕); Exit Narrative / Location Policy → 単一のラベル付き散文 <textarea>。各フィールドは i18n セクション名を持つ <label htmlFor> で紐付けられた実コントロール。新しい public/js/lib/modes-form.js(window.ModesForm)が parse → render → collect() のロジックを保持する;これは既存の PUT /api/modes/_profile { sections } マージ経路へ供給されるため、前文、順序、およびフォームが触れないすべてのセクションがバイト安定で残る(マージ-非置換、サーバー強制)。データ安全性: 本文が純粋な箇条書きでない正規リストセクション(ユーザーがそこに散文を入れた)および非正規 ## セクションは、説明ノート付きのラベル付きそのままの <textarea> にフォールバックする — 任意のコンテンツはそのまま round-trip し、決して暗黙に再構成されたり失われたりしない。Round-trip 安定性を実証:serialise(parse(body)) が同一に再パースされる。全ファイル生 markdown エディターは、セクションの追加/削除と前文編集のための確認ゲート付き Advanced ディスクロージャーとして残る(WS2 #4 破壊的保存ゲートは変更なし)。8 ロケール全体で新しい i18n キー 10 個(config.modesTargetRoles … config.modesUnknownNote)。テストファイル tests/modes-form.test.mjs(7 ケース)を +1;725 → 732。隔離された CAREER_OPS_ROOT フィクスチャに対してライブ検証済み:正規セクション 5 個がフィールドとしてレンダリング + カスタムセクション 1 個がラベル付きフォールバックとして、編集して保存の round-trip が前文 + カスタムセクションを保持、コンソールエラー 0。feat(config) · test: tests/modes-form.test.mjs。詳細は CHANGELOG.md。
[1.54.2] — 2026-05-18
feat(config): #/config の OpenAI / Codex モデルセレクター。 #/config には OpenAI / Codex モデルを選ぶ手段がなかった — OPENAI_API_KEY は親のマルチ CLI(Codex / OpenCode)フロー用に既に公開されていたにもかかわらず、ドロップダウンは ANTHROPIC_MODEL と GEMINI_MODEL にしかなかった。今や OPENAI_MODEL はファーストクラスの環境キー:env-config.mjs の KNOWN_KEYS(OPENAI_API_KEY の直後に並べる)と core キーグループに追加し、SECRET_KEYS には意図的に含めない — 資格情報ではなくモデル id なので、決してマスクされない。config.js はキュレートされた OPENAI_MODELS リスト(デフォルト gpt-5-codex、続いて gpt-5 / gpt-5-mini / gpt-4.1 / o4-mini / o3)と、OpenAI キーの直後にレンダリングされる OPENAI_MODEL <select> フィールドを追加し、Anthropic/Gemini のモデルフィールドを正確に踏襲する。8 ロケール全体で新しい i18n キー config.openaiModel + config.openaiModelHint。テストファイル tests/openai-model-selector.test.mjs(4 ケース)を +1;721 → 725。ライブ検証済み:#/config → 6 オプションの OPENAI_MODEL select、デフォルト gpt-5-codex、ラベル紐付け済み、コンソールエラー 0。feat(config) · test: tests/openai-model-selector.test.mjs。詳細は CHANGELOG.md。
[1.54.1] — 2026-05-18
fix(a11y): F-V54-A — #/cv を単一の <h1> に。 CV markdown 自身の # Name が、ページタイトルの <h1>CV</h1> の隣に 2 つ目 のトップレベル <h1> としてレンダリングされていた(回帰実行 F-V54-A;WCAG 1.3.1 情報と関係性 / 2.4.6 見出し)。cv.js は今や CV プレビューの全注入ポイント(初期レンダー、ファイルインポート時の更新、エディタのライブ同期)を、見出しを 1 レベル下げる(h1→h2 … h6→role="heading" aria-level="7")スコープ付きの cvMd() を通すようになり、ページは正確に 1 つの <h1> を保つ。cv.js に意図的にスコープ — UI.md は help/reports/deep/evaluate で共有され、それぞれが見出しを独自に管理するため。テストファイル tests/cv-single-h1.test.mjs(4 ケース)を +1;717 → 721。ライブ検証済み:#/cv → <h1> 1 個、ユーザーの # Name は今や <h2>、コンソールエラー 0。fix(a11y): F-V54-A · test: tests/cv-single-h1.test.mjs。詳細は CHANGELOG.md。
[1.54.0] — 2026-05-18
WS10 — canonical-docs 再検証 + help バンドルの H3 パリティ(最終の収束リリース)。 CHANGELOG/構造の CI ゲートは H2 しか検査していなかったため、docs/help/en.md は静かに 70 個の H3 サブセクションへ漂流した一方、7 つのローカライズ済みバンドルは 68 のままだった — ギャップは §17(「Reference adapters」テーブル + 「Common pitfalls」リスト、英語のみ)。両方が今や 7 言語すべてに翻訳され(アダプタのファイル名 / リンク / 識別子はバイト同一に保持);8 バンドルすべてが今 17 H2 / 70 H3。help-ru-config-section.test.mjs の新しい H3 パリティゲートがこれをロックする(716 → 717)。canonical-docs-coverage.test.mjs 7/7 が、help が依然として career-ops.org/docs の 5 ガイドすべてを反映していることを確認;WS2 の UX 監査(v1.41→v1.52 の 40 件)が各画面を docs と照合 — 乖離なし。docs/sdd/CONVENTIONS.md を v1.54.0 に更新(テスト合計、H3 パリティゲート、ファイルサイズの外れ値、新しいアクセシビリティ規約セクション)。WS0–WS10 完了;残るは WS11 のみ。fix(docs): WS10 canonical re-validation + H3 parity · test(help): H3-parity gate。詳細は CHANGELOG.md。
[1.53.0] — 2026-05-18
WS9 — シェル表面のテストピラミッド(最後の未テスト層)。 4 つの bin/*.sh スクリプトと .githooks/pre-commit フックはカバレッジがゼロだった;新しい tests/sh-files.test.mjs が 10 件を追加し、bash -n/sh -n 構文、shebang + 実行ビット、そして他のワークストリームが依存する振る舞いの契約を固定する:career-ops-ui.sh — help は 0 で終了し shell-source の漏れがない(v1.40.0 リグレッションガード)、未知の verb は 2 で終了、usage() は heredoc;start.sh — NO_OPEN を尊重、Node ≥ 18 を要求、ブラウザの前面化を scripts/open-dashboard.mjs に委譲(v1.43.0 ガード);setup.sh — strict モード、SKIP_START、両リポジトリを clone;run_all.sh — --quick/--no-e2e のパースと 4 スイート;.githooks/pre-commit は WS7 のレビュアを exec し、いかなるシェルファイルも git --no-verify を呼ばない(CLAUDE.md ハードルール #7 ガード);install-hooks.mjs が core.hooksPath を配線する。docs/architecture/TESTING.md — ピラミッド図にシェル表面のベース層を追加 + v1.53.0 の合計ノート(716 node --test ケース / 90 ファイル + 4 E2E サーフェス)。706 → 716。詳細は CHANGELOG.md。
[1.52.0] — 2026-05-18
WS2 LOWs #33–#40 — バッチ磨き上げ一掃(UX 監査キューを締める)。 低重大度の 8 件。fix(a11y/i18n): WS2 LOW batch — #33:#/dashboard — ヘッダーの 3 つの CTA が不揃いだった(2 つだけ先頭アイコンあり);「Open Pipeline」が今や 📋 を持ち、3 つすべてが揃う。#34:#/profile — アーキタイプの fit/level が曖昧な 2 つのチップとして描画されていた;今は接頭辞付き(Fit: / Level:)で対応する aria-label を持つ。#35:#/health — Run-doctor / verify の toast が doctor.mjs の生文字列を表示していた;今は i18n キー化。#36:#/health — チェック結果はフラットな <div> の連なりだった;今は role=list の <ul>/<li> で、ステータスバッジは aria-label="<check>: <status>" を持つ。#37:#/reports — レポートカードはマウス専用の <div onClick> だった;今は role=link + tabindex + Enter/Space ハンドラ + aria-label。#38:#/activity — ページネータのコメントは「200」と言うがコードは 500 を要求していた;CAP 定数へ整合させ、500 上限が古い履歴を切り詰めると role=note の通知が現れる。#39:#/batch — プレースホルダ文言が英語ハードコードだった一方で aria-label は localized だった;4 つが今は i18n キー化。#40:モードページは非同期プローブ後に主ボタンを黙ってラベル変更していた;今は丁寧な role=status 領域がそれをアナウンスする。新規 i18n キー 10 件 × 8 ロケール({n} 保持);テスト +9:test: tests/low-sweep.test.mjs。697 → 706。WS2 の UX 監査キュー(v1.41→v1.52 の #1–#40)を締める;次は WS9 → WS10 → WS11。詳細は CHANGELOG.md。
[1.51.0] — 2026-05-18
WS2 #13 + #14 + #18 + #19 + #20 — #/auto と #/evaluate の feedback/i18n 一掃。 UX 監査の 5 件の指摘。fix(a11y/ux): auto+evaluate — busy state, actionable HTTP errors, clipboard fallback, aria-live result, spinner-guarded submit — #13:#/auto の Run ボタンは今や単に無効化されるのではなくビジー状態(is-loading + aria-busy +「Running…」)を表示する。#14:失敗した HTTP リクエストは今やステップ上の操作可能な i18n メッセージと toast({n} 付きの auto.httpFail)を提示する(以前は素っ気ない「HTTP 500」だった)。#18:手動モードの「Copy prompt」は今や非同期 Clipboard API を execCommand フォールバック付きで使い、偽の「Copied」ではなく実際の失敗を toast する。#19:evaluate の結果コンテナは今や role=status aria-live=polite であり、長い LLM 呼び出しがスクリーンリーダーへアナウンスされる。#20:Evaluate ボタンは UI.withSpinner でラップされた(以前は素の onClick: run で重複送信を許していた)。新規 i18n キー 3 件 × 8 ロケール;テスト +6:691 → 697。さらにテストのみの修正(コミット 7f8e250):e2e pipeline-delete のティアダウンが v1.48 以前のネイティブ confirm 経路上にあった;API DELETE へ切り替え(fix(test): … — CI の Playwright-e2e が赤だった;製品の回帰ではない)。詳細は CHANGELOG.md。
[1.50.0] — 2026-05-18
WS2 #12 + #27 + #28 — help ナビゲーションのアクセシビリティ。 17 セクション・90+ 見出しのガイドにおける #/help の UX 監査の 3 件の指摘を help.js で修正。fix(a11y): help — single h1, labelled+filterable TOC, focus-on-anchor, back-to-top — #28:ドキュメントの markdown が独自の # Title で始まり、すでにヘッダーが正規の h1 を供給しているページに 2 つ目の <h1> を生んでいた;今は記事のすべての <h1> が除去され、h1 はちょうど 1 つで、階層は <h2> セクションから綺麗に始まる。#27:TOC の <nav> は名前の無いランドマークだった(ページ上にラベルの無い <nav> が 2 つ);今は aria-label(help.toc)を持ち、TOC エントリをクリックするとビューポートのスクロールだけでなくフォーカスがセクション見出しへ移る(tabindex=-1 + focus())。#12:長いドキュメント内で何かを見つける手段が無かった;TOC の上の type=search フィルタが見出しテキストでエントリをライブに絞り込み、スクロール後に aria-label 付きのフローティング「Back to top」ボタンが現れ、先頭に戻りページの <h1> にフォーカスを戻す;その scroll リスナーは #/help から離れる hashchange で除去される。新規 i18n キー 2 件 × 8 ロケール — help.tocFilter、help.backToTop;テスト +6:test: tests/help-nav-a11y.test.mjs。685 → 691。詳細は CHANGELOG.md。
[1.49.0] — 2026-05-18
WS2 #10 + #11 + #25 + #26 — tracker テーブルのアクセシビリティとソート。 #/tracker における UX 監査の 4 件の指摘を tracker.js で修正。fix(a11y): tracker headers, sortable table, localized fix labels, empty state — #10:アクション列のヘッダーは空文字列で、行ごとの Report ボタンには文脈が無かった;今はすべての <th> が scope=col を持ち、アクションヘッダーと Score/PDF ヘッダーは i18n キー化され(空またはハードコードされた英語だった)、Report ボタンは会社名付きの aria-label(<report> — <company>)を得る。#11:ソート手段の無い tracker;Date / Score / Status ヘッダーは今や <th> 内のキーボード操作可能なソートボタンで aria-sort(none/ascending/descending)を持つ;sorted() コンパレータ(score は数値、date/status はロケール比較)がページネーションの前に走り、クリックで方向をトグルしページャをリセットする。#25:track.normalize/dedup/merge は最もリスクの高い破壊的コントロールであるにもかかわらず 8 ロケールすべてで同一の英語だった(data/applications.md をその場で書き換える)— 今は適切にローカライズされ、さらに title ツールチップを追加。#26:ゼロ行の初回実行が過剰フィルタのリストと同じ「no match」メッセージを表示していた;rows.length === 0 は今や独立した空状態(タイトル + 本文 +「Open pipeline」CTA)をレンダリングする。新規 i18n キー 7 件 × 8 ロケール + 3 件を再ローカライズ;テスト +6:test: tests/tracker-a11y-sort.test.mjs。677 → 683。詳細は CHANGELOG.md。
[1.48.0] — 2026-05-18
WS2 #8 + #22 — pipeline:フォーカストラップされた確認 + プレビューのアクセシビリティ。 #/pipeline における UX 監査の 2 件の指摘を pipeline.js で修正。fix(a11y): pipeline UI.confirm() + live preview region — #8:#/pipeline の 3 つのアクションはネイティブ confirm() を使用していた(フォーカストラップされない):プレビューペインの Delete、行ごとの ✕ 削除、「Evaluate first」。いずれもフォーカストラップされる UI.confirm()(v1.44.0 のインフラ)を経由するようになった — 2 つの削除は danger:true(Cancel がデフォルト)、「Evaluate first」は danger:false;pipeline.js にネイティブ confirm() はもう残っていない。#22:previewPane にはライブロールが無く、fetch 失敗が previewBody に詰め込まれて誤解を招く <pre>「preview」としてレンダリングされていた;今は aria-label 付きの role=region aria-live=polite となり、失敗時は別の previewError を設定して独立した role=alert ブロックとしてレンダリングする((再)選択時およびアクティブ行の削除時にクリア)。新規 i18n キー 4 件 × 8 ロケール;テスト +5:test: tests/pipeline-confirm-preview.test.mjs。672 → 677。詳細は CHANGELOG.md。
[1.47.0] — 2026-05-18
WS2 #7 + #30 + #31 + #16 — 未バインドラベルのアクセシビリティ一掃。 フォームコントロールにプログラム的ラベルが無かった UX 監査の 4 件の指摘(WCAG 1.3.1 / 3.3.2 / 4.1.2)を、すべてバインド。fix(a11y): bind every swept form control to an accessible name — #7 scan.js:dry-run チェックボックスと company-select ドロップダウンには for の無いラベルがあった;既存の id に合わせて htmlFor を追加。#30 deep.js:company / role 入力に未バインドのラベルがあった;id + htmlFor を追加(deep-company、deep-role)。#31 apply.js:url / jd に未バインドのラベルがあった;id + htmlFor を追加(apply-url、apply-jd)。#16 cv.js:主要な markdown <textarea> にアクセシブル名が無かった;表示されている「Markdown」見出しへ aria-labelledby でバインド — スクリーンリーダー名と画面上の見出しが一致、新規 i18n キーなし。batch.js / mode-page.js で既に標準の明示的 label[for]↔control[id] パターンを使用;新規 i18n キーなし;挙動の変更ゼロ。テスト +5:test: 667 → 672。詳細は CHANGELOG.md。
[1.46.0] — 2026-05-18
WS2 #5 + #6 + #21 + #24 — scan SSE のアクセシビリティ。 #/scan における UX 監査の 4 件の指摘を scan.js で修正。fix(a11y): scan SSE — live-log region, Stop, run-state, error banner — #5:ストリーミングコンソールは今や role=log aria-live=polite(+ aria-label、tabindex=0、キーボードでスクロール可能)で、別の視覚的に隠された assertive な role=status 領域がターミナルイベント(完了 / 失敗 / 停止)をアナウンスする。#6:Stop ボタンが進行中の EventSource を閉じ(es.close())、結果ポーリングをキャンセルし、状態をリセットする。Stop は scan 実行中のみ表示。#21:scan 実行中は Scan ボタンを無効化 + aria-busy を付与し、Stop を表示する。両方のストリームパス(単一フェーズの streamTo と多フェーズの runScanAll — 後者は終端 done、final !== false でのみ実行を終える)で適用。#24:SSE 障害はもはや 3.5 秒のトーストだけではなく、永続的な role=alert バナーがエラーを再試行アクション付きで表示(直近の実行関数を再呼び出し)し、次回の実行でクリアされる。i18n キー 8 件を新規追加 × 8 ロケール;テスト +7:test: tests/scan-sse-a11y.test.mjs。660 → 667。詳細は CHANGELOG.md。
[1.45.0] — 2026-05-18
WS2 #3 — #/config タブ:完全な WAI-ARIA Tabs パターン。 #/config の 3 つのタブ(API keys / Profile / Modes)はクリックのみで起動する素の <button class="tab-btn"> で、role も aria-selected もキーボードモデルもなかった(UX-audit HIGH #3、WCAG 4.1.2 / 2.1.1)。fix(a11y): config.js tabs implement role=tablist/tab/tabpanel — 今は aria-label 付きの role=tablist コンテナ;各タブは role=tab + id + aria-controls + aria-selected(activate() 内で同期)+ ローテーション tabindex(アクティブ 0 / その他 -1);パネルは role=tabpanel + tabindex=0 + アクティブタブを追う aria-labelledby。完全なキーボードナビ:←/→/↑/↓(ラップ)+ Home/End がフォーカス移動と起動の両方を行う。レガシーの .tab-btn.is-active CSS フックは保持。i18n キー +1 × 8 ロケール(config.tablistLabel);テスト +7:test: tests/config-tabs-aria.test.mjs。さらにテストのみの修正:fix(test): retarget 2 stale auto-pipeline smoke tests — v1.34 以前の Playwright-e2e smoke テスト 2 件が、ダッシュボードの「Auto-pipeline」ボタンが v1.34.0 で開かなくなった一時モーダル(→ Router.go('/auto'))をアサートしており、別の Playwright-e2e CI ジョブで赤のままだった。#/auto 画面に再ターゲット。653 → 660。詳細は CHANGELOG.md。
[1.44.0] — 2026-05-18
WS2 #4 + #9 — 親プロジェクトファイルの破壊的上書き前にフォーカストラップ付き確認。 UX 監査の HIGH が 2 件、いずれもデータ消失: (#4) config.js の saveProfileRaw/saveModesRaw は親の config/profile.yml / _profile.md を確認なしでまるごと置換していた; (#9) tracker.js の Normalize/Dedup/Merge は親の data/applications.md を確認なしでその場で書き換えていた。fix(a11y/safety): UI.confirm() gate before whole-file parent overwrites — public/js/api.js に新しい UI.confirm() を追加。既存の WAI-ARIA モーダル基盤を再利用したフォーカストラップ付きダイアログで(_onClose フックにより Esc / backdrop / × / Cancel のすべての解除経路が false を解決;フォーカスは既定で Cancel;Promise<boolean> を返す;ネイティブ confirm() ではない)、3 つの破壊的呼び出しはすべて書き込み前にゲートされる。新規 i18n キー 8 件 × 8 ロケール({op} プレースホルダは逐語的に保持);テスト +8:test: tests/confirm-gate.test.mjs、644 → 652。詳細は CHANGELOG.md。
[1.43.0] — 2026-05-18
ユーザー要望 — career-ops-ui open + autostart によるブラウザ前面化。 setup/run の後、ブラウザが既に起動しているとむき出しの open/xdg-open ではダッシュボードのタブが背面に残り、ユーザーが探す羽目になっていた。feat(cli): career-ops-ui open — open AND raise the dashboard tab — 新しい scripts/open-dashboard.mjs が HOST/PORT から URL を構築し(0.0.0.0 バインドを loopback に書き換え)、必要なら /api/health を待ち、既定ブラウザを開いてから強制的に前面化する — macOS は osascript で起動中の Chrome/Brave/Edge/Safari/Arc/Firefox のいずれかをアクティブ化、Linux は xdg-open+wmctrl、Windows は start。career-ops-ui open 動詞として公開(エイリアス dash、focus)。bin/start.sh の autostart はこれに委譲し、タブが自動的に前面化される;NO_OPEN=1 は headless/CI 起動で auto-open を無効化する。README ×8 + help §1 ×8 を更新;テスト +8:test: tests/open-dashboard.test.mjs、636 → 644。詳細は CHANGELOG.md。
[1.42.0] — 2026-05-18
WS2 修正 #2 — デッドルート #/portals → config ディープリンク。 #/portals は未登録ルートで 404 ビューを描画していたが、ポータルソース管理用にブックマーク/手入力されうる妥当な URL だった(UX 監査 HIGH #2)。fix(router): #/portals 404 → alias to config + Regional-sources deep-link — router.js の ALIASES に portals: 'config' を追加(settings→profile と同じブックマーク安定化パターン)、これで config ビューに解決され config ナビ項目がアクティブになる。Regional-sources グループが存在する場合、ビュー(config.js)が #/portals ハッシュを検出し、その <details> グループを強制展開・スクロール表示し、その summary にフォーカスを移動(既定の h1 フォーカスを上書き)、ユーザーはポータルソース操作部にちょうど着地する;エイリアス単独で空の地域グループを描画することはない。help-bundle §5 × 8 にショートカット注記を追加;router テスト +1:test(router): portals→config alias guarantee を router.test.mjs に追加、635 → 636。詳細は CHANGELOG.md。
[1.41.0] — 2026-05-18
WS2 — シニア UX/ユーザビリティ監査 + 横断的フォーカス管理修正。 10 年以上のヒューリスティック監査(Nielsen × WCAG 2.2 AA × プロジェクト規約)で全 17 ルートを精査し、重大度順の 40 件キューを生成(.planning/.../UX-AUDIT.md);HIGH→MEDIUM→LOW をリリースごとに 1 修正ずつ出荷。本リリースは横断的 HIGH の第 1 位に着地。修正: fix(a11y): move focus to the new view on every route change — router.js render() は hashchange ごとに #content を差し替えるがフォーカスを移動しなかったため、キーボード/スクリーンリーダー利用者が破棄されたノードに取り残され位置を失っていた(WCAG 2.4.3 Focus Order / 4.1.3 Status Messages — 横断的で全 17 画面に影響);新しい focusNewView(content) が新ビュー先頭の h1/.page-title にフォーカス(簡潔な SR アナウンス + 正しいフォーカス順)、必要なら見出しをフォーカス可能化(tabindex=-1)し #content にフォールバック;skip-link と競合しないよう初回描画はスキップ;成功・エラー両レンダパスに配線;ライブ検証済み:遷移後 document.activeElement は新ビューの H1.page-title。テスト: test(router): focus-management static guarantees — router.test.mjs に 4 ケース(ヘルパ定義、見出しターゲット + content フォールバック、初回描画スキップガード、≥2 呼び出し箇所);631 → 635。詳細は CHANGELOG.md。
[1.40.0] — 2026-05-18
WS8.3 — docs 実態化スイープ + career-ops-ui help 修正 + askSecret 強化。 修正: fix(cli): career-ops-ui help no longer leaks shell source — ディスパッチャはヘッダコメントを sed -n '2,12p' で出力していたが、12 行目(set -euo pipefail)はコメントではなくコードのため、career-ops-ui help(および未知動詞の使用法テキスト)が余分な set -euo pipefail 行で終わっていた;help と *) の両ケースを 2,11p(コメントブロック)に絞り込み;help は exit 0、未知動詞は exit 2 — 検証済み。fix(cli): scripts/init.mjs key entry never echoes — v1.39.0 のフォローアップで装飾的な readline 上書きマスクを実際の raw モードリーダに置換: setRawMode(true) + バッファ行により、入力/貼り付けされたキーのバイトが端末に一切届かない(scrollback / tmux / 画面共有への漏洩なし);完全な VT エスケープ FSM が CSI/SS3/OSC/DCS/SOS/PM/APC の全シーケンスを消費し、矢印・ファンクションキーが秘密を破損しない;stdin は依存性注入されるため、非 TTY フォールバックはグローバルを触らず単体テスト;AI レビュー LGTM までクリーンに反復。ドキュメント: README ×8 — 旧「ワンコマンドインストール」セクションを目立つ 「ワンコマンドで起動と初期化」 セクションに置換(curl ワンライナーに加え明示的な career-ops-ui CLI チェーン: clone → npm link → setup → init → doctor → run → help、プロバイダウィザード説明、CI 形式 --provider --anthropic-key --yes、LLM_PROVIDER ノート);8 つの README バッジを v1.22–v1.24 / tests-461–474 から v1.40.0 / tests-631 に実態化(e2e バッジは捏造カウント回避のため非数値化);help-bundle ×8 §1 — クイックスタート手引きの先頭(「A. Setup」の前)に「ワンコマンド起動 & init」コールアウトを 8 ロケール全てに追加;H2 セクションのパリティ維持(各 17 — CI ゲート緑)。テスト: test(init): non-TTY askSecret fallback — provider-selector.test.mjs に DI-stdin ケースを追加し、askSecret が非 TTY で共有グローバルを変更せず素の ask() に委譲(trim パリティ)することを検証;629 → 631。詳細は CHANGELOG.md。
[1.39.0] — 2026-05-18
WS8.2 — LLM プロバイダ選択 + OpenAI/Codex キー + 対話型 init ウィザード。 env-config に LLM_PROVIDER(auto|claude|gemini)+OPENAI_API_KEY(秘匿)。llm.mjs の 6 ゲートが _provGate() 経由で providerOrder() を参照;auto は挙動不変。#/config に select+フィールド。scripts/init.mjs は実ウィザード(検証済みパスで parent .env 書込)。7 テスト。622 → 629。README ×8/正規ドキュメント fold = WS8.3/WS10。詳細は CHANGELOG.md。
[1.38.0] — 2026-05-17
WS8.1 — 統合 CLI ディスパッチャ + doctor 動詞。 bin/career-ops-ui.sh が setup/run/doctor/init/help をルーティング。scripts/doctor.mjs は /api/health エンジンをそのまま再利用(createApp インプロセス → ターミナルレポート);必須チェック全通過時のみ exit 0。docs/sdd + help §1 ×8。6 テスト。616 → 622。README ×8 = WS8.3。詳細は CHANGELOG.md。
[1.37.0] — 2026-05-17
WS7 — git ワークフローの pre-commit AI レビュー。 決定論フロア(fail-HARD):staged .env/シークレット、diff 内キーパターン、staged ビューの .also(、node --check 失敗をブロック。AI レイヤ(fail-SOFT):CLI があり AI_REVIEW != off なら claude -p。.githooks/pre-commit + prepare で core.hooksPath を配線。--no-verify 禁止。docs/sdd。6 テスト。610 → 616。詳細は CHANGELOG.md。
[1.36.0] — 2026-05-17
WS6.3 — Modes タブ:生の塊 → セクション別エディタ。WS6 完了。 modes/_profile.md を ## セクション単位で編集(見出しごとに折りたたみ textarea)。サーバ splitProfileSections はバイト厳密;PUT { sections } は指定セクションのみマージ — プリアンブル・他セクション・順序をバイト単位で保持。未知見出し → 400。raw 経路は不変。i18n 5 キー ×8。help §2 ×8。新規テスト 6。604 → 610。WS6 完了。詳細は CHANGELOG.md。
[1.35.0] — 2026-05-17
WS6.4 — Profile 配列エディタ + WS6.2 API-keys 監査。 PUT /api/profile が { arrays } を受理({ fields } と併用可):Target roles/Superpowers(リスト)、Archetypes(name/level/fit)、Proof points(name/url/hero-metric)。同一 merge-not-replace;空行破棄;空リストはキー削除。#/config に add/remove エディタ 4 つ。i18n 6 キー ×8。監査:KNOWN_KEYS ≡ FIELDS、ギャップなし。新規テスト 7。597 → 604。詳細は CHANGELOG.md。
[1.34.0] — 2026-05-17
WS5 — ワンクリック Auto-pipeline 画面(#/auto)。 モーダルを専用・リンク可能ページへ昇格。ワンクリックで検証→取得→評価→レポート保存→トラッカー(SSE)。アクセシブルなステッパー、ディープリンク、キーなし手動モード、#/auto?url=…&go=1 リンク可。サイドバー項目;ダッシュボード ✨ ボタンはここへ。i18n 14 キー ×8。help §1 ×8 + README ×8。新規テスト 8。589 → 597。詳細は CHANGELOG.md。
[1.33.0] — 2026-05-17
WS4 — career-ops 1.8.0 パリティ監査 + location_filter。 親 scan.mjs に location_filter(#570)追加;web-ui の in-process スキャナは委譲しないため流れず。新 server/lib/location-filter.mjs がセマンティクスを忠実に複製、両スキャナに配線。help §5 ×8。新規テスト 8。581 → 589。詳細は CHANGELOG.md。
[1.32.0] — 2026-05-17
#/config Profile タブ — 生 YAML 塊 → 項目別フォーム (WS1)。 折りたたみ 3 セクション(候補者/ナラティブ/報酬)、14 スカラーパス。項目保存は config/profile.yml に マージ — アーキタイプ・プルーフポイント・独自キーをそのまま保持。Advanced に raw-YAML エスケープハッチ維持(コメント保持)。i18n 23 キー ×8。新規テスト 7。574 → 581。詳細は CHANGELOG.md。
[1.31.0] — 2026-05-17
career-ops 1.8.0 同期 — #/batch が --model + --start-from を公開。 親プロジェクトが 1.7.1 → 1.8.0 に。batch-runner.sh に --model NAME(#504)+ --start-from N 追加。web-ui は #/batch で公開(モデル・開始 # 入力)+ サーバ側 defense-in-depth 検証。i18n ×8。新規テスト 7。567 → 574。詳細は CHANGELOG.md。
[1.30.0] — 2026-05-14
#/scan 結果ページネータ — v1.12 の「最初の 200 件を表示 / 合計 N」トランケーションを置き換え。
v1.30 以前は、スキャン結果テーブルがフィルタ後の最初の 200 行で切り詰められ、「Showing first 200 of N」のフッターが表示されていました。201..N 行は UI から到達不能。v1.30.0 はキャップを UI.paginate に置換します(#/tracker / #/reports / #/activity と同じヘルパー)。PAGE_SIZE = 200 で従来の視覚密度を維持;ブースト先頭の並びがページ間で安定(全体をソート後にページ分割);フィルタ変更時にページ 1 に自動リセット。未使用の scan.shownTop i18n キーを削除(8 ロケール)。tests/scan-paginator.test.mjs に 9 件の新規ケース(静的カナリア 7 件 + 6 つの境界ケースを持つ純ロジックテーブル 1 件 + サマリ計算 1 件)。558 → 567 unit + acceptance(+9)。完全な詳細は CHANGELOG.md を参照。
[1.29.2] — 2026-05-14
ホットフィックス:🌐 Scan を source=both で実行しても EN フェーズのみ動作。RU フェーズが静かに破棄されていました。
SSE クライアント(public/js/api.js:156)が最初の done イベントで EventSource をクローズしていましたが、サーバは source=both でフェーズごとに 1 つずつ送出します。RU フェーズは開始直後にキャンセルされていました。修正:サーバは各 done に final: true|false を付与し、クライアントは final !== false の場合のみクローズします。後方互換 — final を設定しない単一フェーズの送出側は従来通りクローズ。547 → 558 unit + acceptance(+11 新規)。完全な詳細は CHANGELOG.md を参照。
[1.29.1] — 2026-05-14
5 つの RU ポータルを構成するための詳細なユーザガイドを help-bundle §5 の 8 ロケール全てに追加。
§5(Portals & sources)内に新規 ### サブセクション「ロシア系ポータルの構成 — 詳細セットアップガイド」を追加:認証・地域制限を含む 5 ソースのインベントリ表、portals.yml の場所と編集手順、5 ソースの YAML 例、negative リストの衝突と修正例、1 ソースを一時無効化する方法、🌐 Scan + SSE ログによる検証方法。§17(v1.29.0 で出荷)は開発者向けフロー、§5 v1.29.1 はエンドユーザ向けフロー。540 → 547 unit + acceptance(+7 新規)。完全な詳細は CHANGELOG.md を参照。
[1.29.0] — 2026-05-14
ロシア系ポータルスキャナが 2 → 5 ソースに拡張。レジストリ + 動的ドロップダウン。新 §17「新規ポータルの追加方法」。
- 3 つの新 RU アダプタ:
Trudvsem(政府オープンデータ API、認証なし、ジオゲートなし)、GetMatchおよびGeekJob(防御的な HTML スクレイプ — パース失敗時は[]、健全な 200 で throw しない)。 - ソースレジストリ
server/lib/sources/registry.mjs— ディスパッチャ + エンドポイント + ドロップダウンが消費する単一情報源。v1.29 以前は 3 箇所にハードコードされていました。 - 新エンドポイント
GET /api/scan/sources(Cache-Control: max-age=60) — SPA は#/scanマウント時にソースフィルタのドロップダウンを動的に再構築。 - 新規 §17 8 ロケール全てに「新しい求人ポータルソースを追加する方法」(アダプタテンプレート、レジストリエントリ、ディスパッチャ、モックテスト、
portals.yml)。 russian_portals.sourcesのデフォルトが["hh", "habr"]から 5 ソースに変更。portals.ymlでsources:を明示的に列挙している場合は、新規 3 つを手動で追加してください。- テスト: 520 → 540(+20)。完全な詳細は
CHANGELOG.mdを参照。
[1.28.1] — 2026-05-14
ホットフィックス: ?query 付きハッシュでの router 404。health から HH_USER_AGENT 行を削除。
v1.28.1 以前は Router.go('/evaluate?url=…') が最初の split('/') セグメントとして "evaluate?url=…" リテラルを生成し、登録済みルートに一致せず __not_found__(404)に落ちていました。1 行修正: 名前分割の前に hash.split('?')[0]。報告された 2 つのクリックを両方カバーします:#/pipeline → ▶ と「App settings → Modes」。オプションの HH_USER_AGENT 行は /api/health から削除(ロシア外 403 ゲートのヒントは help-bundle §16 に残り、スキャン時に stderr でも依然として出力される)。515 → 520 unit + acceptance(+5 新規)。完全な詳細は CHANGELOG.md を参照。
[1.28.0] — 2026-05-14
ドキュメント整合 + #/batch の --max-retries N コントロール。qa/QA-PROMPT-docs-vs-app.md から提起された 2 件のオープン issue を解消します。
- Issue #2 —
#/batchに数値入力「Max retries」(1–10) を追加。「Retry failed」がチェックされた場合のみ有効化。サーバ側でparseIntと 1≤N≤10 のレンジ検証を行い、範囲外は静かに破棄、--retry-failedなしで--max-retriesは付与されません。tests/batch-max-retries.test.mjsの 7 ケース。新規 i18n キー 2 個 × 8 ロケール。 - Issue #1 — 8 個の help-bundle と 8 個の README の AI CLI リストを career-ops.org/docs の正典(Claude Code・Codex・OpenCode・Qwen CLI)に揃え、ローカライズされた一文を追記:「同じスラッシュコマンド・サーフェスで他の Claude 互換 CLI も動作します」。README の「Multi-CLI」項目(web-ui 自身の shim ファイル説明)はそのまま(別 surface)。
tests/canonical-docs-coverage.test.mjsに新規カナリア 2 個。 - 506 → 515 unit + acceptance(+9 新規)。Playwright 32/32 変更なし。完全な詳細は
CHANGELOG.mdを参照。
[1.27.0] — 2026-05-14
外観 + アクセシビリティ調整: サイドバーの #/dashboard エントリ重複排除。
サイドバーで、ブランドロゴ(<a class="logo" href="#/dashboard">)と最初のナビ項目が同一ルートを指していました。スクリーンリーダーは「Dashboard」を二回読み上げ、キーボード操作で無意味なタブストップが発生していました。ブランドブロックは通常の <div class="logo"> になり、ナビ項目のみが #/dashboard への唯一のリンクとなります。506 / 506 unit + 32 / 32 Playwright — 変更なし。詳細は CHANGELOG.md を参照。
[1.26.1] — 2026-05-14
ホットフィックス WCAG 2.5.5 — .btn 最小高さ 44 px の復元.
v1.26.0 で .btn の min-height: 44px 宣言が失われ、ヘッダーボタンが 39-41 px で描画されていました (WCAG 2.5.5 違反)。v1.26.1 で 44 px のフロアと flex-shrink: 0 + line-height: 1.2 を復元。502 → 506 unit、Playwright 32/32 変更なし。詳細は CHANGELOG.md を参照。
[1.26.0] — 2026-05-14
テストピラミッド + 行カバレッジ ≥ 93 %.
v1.25 バックログに従い 4 階層テストピラミッド (unit → functional → acceptance → e2e) を導入。v1.25 で最大だったカバレッジギャップを埋める 22 件の新テスト (jds.mjs 61.64 % → 100 %、auto-pipeline 拒否経路)。マルチエンドポイントのユーザージャーニーテスト用に tests/acceptance/ ディレクトリを新設。480 → 502 unit + acceptance、Playwright 32/32 変更なし。詳細は CHANGELOG.md と docs/architecture/TESTING.md を参照。
[1.25.0] — 2026-05-14
自動パイプラインの手動ショートサーキット + ダッシュボードのコスメティック修正 + CHANGELOG パリティのバックフィル。 G-014 (自動パイプラインが mode: 'manual' を無視していた問題)、G-012 (CHANGELOG パリティのドリフト — 6 ロケールが 2 リリース遅れていた)、およびダッシュボードの ✨ ✨ 二重グリフのコスメティック問題をクローズします。G-003 (README.cn.md のリネーム) は実質的に既にクローズ済み — リポジトリには README.zh-CN.md しか存在しません。G-005 (A-G → A-F レポートブロックの再整列) は親プロジェクトとの協調コミットが必要で、引き続き延期となります。
🛡️ G-014 — 自動パイプライン mode: 'manual' ショートサーキット
fix(auto-pipeline): G-014 — honour mode:'manual' short-circuit(server/lib/routes/auto-pipeline.mjs:158-195) — v1.25 以前は、このルートは常に LLM を呼び出していました。mode: 'manual'を渡す (v1.10.2 以降の/api/evaluateのミラー) は黙って無視され、リクエストは Anthropic で 1〜3 分ハングしていました。新ハンドラの動作は以下の通りです:- 後方互換のため
modeとevalModeの両方を受け付けます。いずれかが'manual'であればショートサーキットが発動します。 - 5 ステージ全ての SSE を
status: 'done'/status: 'skipped'で送出します。fetch なし、LLM 呼び出しなし、リクエストあたり $0.05 のコストなし。 doneペイロードは{ mode: 'manual', prompt: <buildEvaluationPrompt scaffold>, message }を含み、SPA は既存の/api/evaluate手動プロンプトカードと同様にレンダリングできます。
- 後方互換のため
HOST=0.0.0.0における DoS リスクをクローズ: 以前はllmRateLimitが 10 req/60s/IP で上限を設けていたとしても、攻撃者 10 名 × 各 10 リクエスト = 1 分あたり $50 の Anthropic 課金が発生していました。ショートサーキットは実呼び出しのレート制限カウントが減る前に発火します。- テスト —
tests/auto-pipeline-manual-mode.test.mjs: 3 件のテストで以下を確認します — (1)mode: 'manual'が 5 ステップキー全てを伴って 2 秒以内に返ること、(2)ANTHROPIC_API_KEYがセットされていてもショートサーキットが発火すること (元の症状)、(3) レガシーなevalMode: 'manual'呼び出し元が引き続き動作すること。
📝 G-012 — CHANGELOG パリティのバックフィル (6 ロケール × 欠落 2 リリース)
docs(changelog): backfill v1.23.0, v1.24.0, v1.24.1, v1.25.0 in 6 lagging locales— v1.25 以前は EN のみが v1.23-v1.24 を持ち、RU は 1 リリース遅れ、その他 6 ロケールは 2 リリース遅れでした。v1.25 では並列翻訳エージェント (v1.23 と同じパターン) をディスパッチし、4 エントリ全てをCHANGELOG.{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.mdに投入します。RU は v1.24.0 + v1.24.1 + v1.25.0 を受け取ります (v1.23.0 は v1.23 サイクルで既に翻訳済み)。feat(ci): scripts/check-changelog-parity.mjs gate— いずれかのロケール CHANGELOG の最新エントリが EN 正本より古い場合、ビルドを失敗させます。npm run test:ciに組み込み済み。既存の G-012 ドリフトは、EN の境界を越えた瞬間に自身を検出できていたはずです。
✨ コスメティック — ダッシュボードの二重グリフ重複排除
fix(dashboard): dedup ✨ glyph in auto-pipeline button label(public/js/lib/i18n-dict.js:219) —dash.autoPipelineは全ロケール文字列の先頭に✨を含んでおり、加えてpublic/js/views/dashboard.js:58がビュー側でさらに✨を前置していました。結果、ボタンは✨ ✨ Auto-pipeline …とレンダリングされていました。v1.25 では各ロケール DICT エントリの先頭グリフを削除し、ビューの前置を唯一のソースとします。同じ監査パスで残りの i18n バンドルもスイープしましたが、他に二重グリフのパターンは見つかりませんでした。
🚫 将来のリリースへ延期
- G-005 — 正規 career-ops.org/docs に従ったレポートブロック A-G → A-F — 親プロジェクト
santifer/career-opsの協調コミットが必要 (modes/oferta.mdを書き換えて A=Role、B=CV-match、C=Strategy、D=Comp、E=Personalization、F=STAR を出力し、C-Risks / G-Legitimacy を独立ブロックから外す)。v1.25.0 は web-ui 側を新スキーマ受け入れ可能な状態で出荷します (reports.jsは v1.13 以降、任意のブロック文字を受け付けます)。親と子を一緒に着地できる次のリリースウィンドウまでトラックします。 - G-003 —
README.cn.md→README.zh-CN.mdリネーム — v1.25 準備時に検証済み: リポジトリには既にREADME.zh-CN.mdのみが存在し (worktree 配下に孤児のREADME.cn.mdは皆無)。G-003 の指摘は陳腐化していました。
🧪 テスト
- 477 → 480 ユニットテスト (+3 は PR-B の
auto-pipeline-manual-mode.test.mjsより)。 - 32/32 Playwright は変更なし。
npm run test:ciはnpm test+check-no-also-leftovers.mjs+check-changelog-parity.mjsを実行するようになりました。
検証
$ npm run test:ci
# 480 / 480
# ✓ no .also( leftovers in views/
# ✓ CHANGELOG parity: all 8 locales at v1.25.0
# G-014 — manual mode returns < 2 s even with ANTHROPIC_API_KEY set:
$ ANTHROPIC_API_KEY=sk-ant-test PORT=4317 npm start &
$ sleep 3
$ time curl -sS -X POST -H 'Content-Type: application/json' \
-d '{"url":"https://job-boards.greenhouse.io/anthropic/jobs/x","mode":"manual"}' \
http://127.0.0.1:4317/api/auto-pipeline | head -20
# real 0m0.1xx s (was 1-3 min)
# event: start … event: step (×5) … event: done {"mode":"manual","prompt":"…"}
# G-012 — every locale CHANGELOG carries the v1.25.0 entry:
$ grep -c '^## \[1.25.0\]' CHANGELOG*.md
# 8 files, each → 1
# Cosmetic — dashboard glyph:
$ grep "dash.autoPipeline" public/js/lib/i18n-dict.js
# No leading ✨ in any locale value (view supplies the single glyph)
破壊的変更
なし。mode: 'manual' はオプトインで、レガシーな evalMode: 'manual' 呼び出し元は変更なしで引き続き動作します。
スコープ外 (v1.26+)
| 項目 | 備考 |
|---|---|
| G-005 — A-F レポートブロックの再整列 | 親プロジェクトの協調コミットが必要 (santifer/career-ops が modes/oferta.md を書き換え)。 |
| QA シナリオ 31 の ビジュアル サブテストのライブ実行 | ブラウザ駆動エージェント (Claude Cowork) が必要。Playwright スモークで部分的にカバー。 |
i18n-dict.js の 400-LOC 目標超過 |
翻訳フィクスチャ — ポリシーにより除外。バンドラを使わずに分割すると HTTP リクエストが増えるため不採用。 |
[1.24.1] — 2026-05-14
ホットフィックス: 8 ロケール全てで #/config がクラッシュ (G-015)。
🚑 重大ホットフィックス
fix(config): G-015 — replace removed Element.prototype.also call in config.js(public/js/views/config.js:371) — v1.22.0 の N-2 でElement.prototype.alsoのグローバルモンキーパッチを撤去し、cv.jsをフリーステートメントパターンへ移行しましたが、config.jsの移行が漏れていました。結果、#/configは全ロケールで初回呼び出し時にc(...).also is not a functionを伴ってクラッシュしていました。v1.24.1 ではcv.js:188-201と同じ移行パターンを適用 — ツリーをconst root = c(...)に抽出し、起動ブロックを単体で実行した後return root;を返します。
🛡️ CI ゲート
feat(ci): scripts/check-no-also-leftovers.mjs sweep—public/js/views/配下の全ファイルを走査し、いずれかの.also(呼び出しサイト (コメント内参照は許可) があればビルドを失敗させます。新しいnpm run test:ciスクリプトに組み込み済み。将来モンキーパッチ撤去がリバートされても、同じリグレッションが黙って再導入されることはありません。
🧪 テスト
test: tests/config-view-syntax.test.mjs— 3 つのガード:node:vm.Scriptでconfig.jsをパース (Playwright なしでも構文レベルのリグレッションを捕捉)- コメント外に
.also(が残っていないことを表明 const root = c(...)/return root;の移行アンカーが存在することを表明
- 474 → 477 ユニットテスト (+3) + 32/32 Playwright は変更なし。
検証
$ npm run test:ci
# 477 / 477
# ✓ no .also( leftovers in views/
# Browser smoke:
$ open http://127.0.0.1:4317/#/config
# → renders normally, no "is not a function" card. Every locale equivalent.
スコープ外 (v1.25 に延期)
- G-014, G-012, G-005, G-003 — バンドルは下方の v1.25.0 エントリを参照してください。
[1.24.0] — 2026-05-14
ヘルプバンドルのコンテンツ深度リフレッシュ + QA シナリオ 31 のライブ実行 + RU CHANGELOG のエンドツーエンド翻訳。 v1.23.0 の「スコープ外」テーブルで v1.24 に延期された 2 項目を両方クローズします: 5 本の正規 career-ops.org/docs URL から 8 言語ヘルプバンドル全ての本文を深度リフレッシュ (v1.11.x 以降は URL カバレッジのみだった)、および稼働中サーバに対する QA シナリオ 31 のライブ実行 (“ブラウザエージェント + LLM 認証情報が必要” としていたが、6/6 サブテストのうち curl + grep で到達可能で、ビジュアルサブテストのみブラウザが必要と判明)。
📖 ヘルプバンドルのコンテンツ深度リフレッシュ
docs(help): refresh en.md from 5 canonical career-ops.org/docs URLs(docs/help/en.md) — v1.24 以前は EN バンドルは 1113 行で、フロントマターに 5 本の正規 URL を列挙していましたが、本文では展開していませんでした。v1.24 では WebFetch で 5 URL 全てを取得し、対応する H2 セクションを深堀りします:- About career-ops (フロントマター) — 原則 (データ主権、AI 非依存、人間が制御) を追加、“What career-ops is NOT” ブロック追加、コンセプト一覧を 6 → 10 行に拡張 (Proof points、JD store、Interview-prep、Batch additions を追加)。
- §5 Portals — 正規ブートストラップ
cp templates/portals.example.yml portals.ymlを追加、tracked_companiesエントリ毎の必須/任意フィールドを明確化。 - §7 Scan — Option A に「AI トークンを消費しない」注記を追加、後続コマンドリスト (
apply/contacto/deep/tracker) を追加。 - §14 Apply checklist — SPA チェックリストモード vs Manual-vs-Playwright-assisted vs Full CLI フロー (
/career-ops apply <company>からSubmitted.までの正規 8 番号ステップ、Evaluated → Applied自動遷移付き) に分割; バッチ評価サブセクションに TSV スキーマテーブル + 4 フラグ全ての記載 +merge-tracker.mjs --dry-runを追加; Playwright Setup サブセクションにインストールコマンド、MCP 登録、代替.claude/settings.local.json、デフォルトでのヘッドレス注記を列挙。
- 16-H2 セクションパリティを維持 (CI テスト
help-ui.test.mjs::section-parityが全 8 ロケールで H2 セクションが厳密に 16 個であることを表明)。 - 5 本の正規 URL がそれぞれバンドル内に 2 回以上出現 (CI テスト
canonical-docs-coverage.test.mjsが強制)。v1.24 後の URL 毎の出現回数:what-is-career-ops× 4、scan-job-portals× 5、apply-for-a-job× 3、batch-evaluate-offers× 5、set-up-playwright× 3。 docs(help): translate the v1.24 deepening to 7 non-EN locales— 7 つの並列翻訳エージェントをディスパッチ。各ターゲットロケール (es / pt-BR / ko-KR / ja / ru / zh-CN / zh-TW) は EN 構造をセクション毎に反映したリフレッシュバンドルを受け取り、コードブロック / URL / ファイルパス / ボタンラベル (📁 Upload CV / 🌐 Scan now / ▶ Evaluate / 📄 Generate PDF / 💾 Save) と英語略語 (CSP, SSRF, TOCTOU, WCAG, ATS, JD, SSE, REST, API) は原文のまま保持し、深化部分はターゲット言語のパブリケーション品質のネイティブ技術スタイルに翻訳します。
🧪 QA シナリオ 31 — ライブ実行 (6/6 PASS)
-
docs(qa): append last-verified live-execution log to qa/claude-cowork-browser-test-prompt.md— v1.24 以前のシナリオ 31 はドキュメント化されていたものの、稼働中サーバに対して未実行 (“ブラウザエージェント + LLM 認証情報が必要” として延期)。v1.24 では 6 つのサブテスト全てをhttp://127.0.0.1:4317に対して実行しました:Sub 説明 ステータス 31.1 ヘルプバンドル内のスコア閾値 ✅ PASS ( docs/help/en.md内で 4.5 × 3、4.0 × 9、3.5 × 6 言及)31.2 スキャンワークフローのエンドポイント ✅ PASS ( /api/stream/scan-{en,ru}+/api/scan-ru/config→ 404;/api/scan/regional/config→ 200)31.3 /api/apply-helperチェックリスト✅ PASS (本文に career-ops apply+auto-submit警告を含む)31.4 /api/batchエンドポイント✅ PASS (キー [exists, runnerExists, raw, rows, additions])31.5 Playwright の可用性 ✅ PASS ( /api/healthがPlaywright (parent node_modules) ok: true, value: installedを報告)31.6 ヘルプバンドル URL カバレッジ (5 URL × 8 ロケール) ✅ PASS (40 / 40 ✓) ビジュアル専用サブテスト (ブラウザを必要とする) は QA プロンプトで個別にフラグ — 引き続き Claude Cowork または
npm run test:e2e:browserで実行可能。
🌐 RU CHANGELOG エンドツーエンド (M-9 フォローアップ)
docs(translate): CHANGELOG.ru.md retry agent — full body translation(CHANGELOG.ru.md) — v1.23.0 リリースは RU CHANGELOG リトライエージェントが進行中のまま出荷されました (一度ソケットエラーでクラッシュし、再ディスパッチされていた)。v1.24 ではエージェントの 1542 行に及ぶフル翻訳を取り込みます: v1.23.0 → v1.6.0 までの全エントリがパブリケーション品質のロシア語本文を持ち、EN 本文の応急処置は撲滅されました。スタイル規律は v1.22.0 README 品質リフレッシュと同等: 不格好な “функционал” は “функциональность” / “возможности” / “поведение” に置換; “при помощи” は “через” / “с помощью” に置換; 受動態より能動態優先; “эндпоинт”、“レート制限”、“競合状態”、“サニタイズ” を正規用語として採用; 英語略語 (TOCTOU, CSP, SSRF, WCAG, ATS, JD, SSE, REST, API) は原文保持。
🧪 テスト
- 474 / 474 ユニット + 20 / 20 スモーク E2E + 32 / 32 Playwright。挙動テストのデルタはゼロ; ヘルプバンドルの CI 表明 (16 H2 セクション × 8 ロケール、5 URL × ≥ 2 言及、コンテンツフロア) は全てグリーンを維持。
検証
$ npm test # 474 / 474
# Help-bundle deepening:
$ wc -l docs/help/en.md
# ~1270 lines (was 1113 — deepened, not bloated)
$ for url in what-is-career-ops scan-job-portals apply-for-a-job \
batch-evaluate-offers set-up-playwright; do
echo -n "$url: "
grep -c "$url" docs/help/en.md
done
# what-is-career-ops: 4
# scan-job-portals: 5
# apply-for-a-job: 3
# batch-evaluate-offers: 5
# set-up-playwright: 3
# Scenario 31.6 — 40/40 URL coverage:
$ for lang in en es pt-BR ko ja ru zh-CN zh-TW; do
echo -n "$lang: "
for url in what-is-career-ops scan-job-portals apply-for-a-job \
batch-evaluate-offers set-up-playwright; do
curl -sS "http://127.0.0.1:4317/api/help/$lang" \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('markdown',''))" \
| grep -q "$url" && echo -n "✓ " || echo -n "✗ "
done
echo
done
破壊的変更
なし。
スコープ外 (v1.25+)
| 項目 | 備考 |
|---|---|
| シナリオ 31 の ビジュアル サブテストのライブ実行 | ブラウザ駆動エージェント (Claude Cowork または npm run test:e2e:browser) が必要。curl のみの実行ではスコープ外で、既存の Playwright スモークでカバー。 |
| 旧エントリ (v1.5.x 以下) の RU CHANGELOG 本文翻訳 | リトライエージェントは v1.6.0 以降のみカバー。v1.6 より前のエントリ (v1.5.x 等) — 仮に存在していたとしても — 既存コンテンツのまま。 |
| 今後の SPA 変更後のダッシュボードスクリーンショットのビジュアルリグレッション | scripts/capture-dashboard-screenshots.mjs がロケール毎の PNG を再生成; 現状自動 diff なし。 |
[1.23.0] — 2026-05-14
i18n 分割 + 接続バナー CI 修正 + ロケール別ダッシュボードスクリーンショット + 全バックログの応急処置クローズ。 v1.22.0 の「スコープ外」テーブルで v1.23 にフラグされた 3 項目 (M-9 ロケール CHANGELOG 本文、N-1 i18n.js LOC 分割、ヘルプバンドルコンテンツ監査) に加え、v1.22.0 後の main ブランチ CI をレッドにしたスモーク E2E テストへのホットフィックスを出荷します。
🚑 CI ホットフィックス — 接続バナーリカバリ
-
fix(client): reset health-poll cadence + visibilitychange eager re-check(public/js/api.js:21-91) — v1.22.0 の M-6 指数バックオフは正しい設計 (3 秒 → 6 秒 → 12 秒 → 上限 15 秒、元の上限 60 秒から低下) でしたが、進行中のsetTimeoutは直前に設定された delay にロックされていました。t=0.1 でサーバが kill され、最初の ping が t=3 で失敗すると delay は 6 へ倍化し、次の復旧プローブは t=9 まで発火しません。スモーク E2E の「Flow 2a: connection banner appears on server down, hides on recovery」は 4 秒しか待たず、mainでレッドに転じました。v1.23.0 はポーリングループを再構築します:
_healthHandleを追跡することでsetConnectionState(lost=true)がclearTimeoutを呼び、_HEALTH_MINで再スケジュールできるようにしました。最初の復旧プローブは、キューに積まれた delay に関係なくダウンから 3 秒以内に発火します。_HEALTH_MAXを 60 秒から 15 秒に低下。デッドサーバに対してバックグラウンドのタブはユーザが戻ったとき 1 ポーリングサイクル内で復旧し、帯域節約も維持されます。document.addEventListener('visibilitychange')でタブがフォーカスを取り戻しconnectionLost === trueのときに即時再チェック — Cmd-Tab で戻る際に次のバックオフティックを待ちません。
🧹 N-1 — i18n.js 分割 (400-LOC 目標超過)
refactor(client): split DICT into i18n-dict.js (data) + i18n.js (logic)— v1.23 以前はpublic/js/lib/i18n.jsが 639 LOC。大部分 (23-586 行) はDICT翻訳テーブル — 純粋な構造化データでした。v1.23.0 ではこれをpublic/js/lib/i18n-dict.jsに抽出 (578 LOC、CLAUDE.md の「これらの制限から除外: 生成ファイル、マイグレーション、テストフィクスチャ、ロックファイル、ベンダーコード」により LOC ルールから除外 — 翻訳テーブルはフィクスチャに該当)、public/js/lib/i18n.jsはピュアなモジュールロジック 86 LOC (400-LOC 目標を十分下回る) に。- ローダー契約:
i18n-dict.jsがwindow.__I18N_DICT = { … }を投入し、続いてi18n.jsが既存の IIFE 内でそれを読み取ります。public/index.htmlはi18n-dict.jsをi18n.jsより先に読み込むため、IIFE は構築時に完全に投入された DICT を見ます。Missing-dict フォールバック: 各t()呼び出しはインラインフォールバックまたは素のキーを返し、SPA をクラッシュさせずに誤設定を明示的に表面化します。 - テスト配管の更新:
tests/i18n-coverage.test.mjs、tests/help-ui.test.mjs、tests/canonical-docs-coverage.test.mjsは両ファイルをテスト VM コンテキストで実行 (または regex スイープ用にソースを連結) するようになり、既存の全表明を維持します。
🌐 M-9 — ロケール CHANGELOG 本文翻訳
docs(translate): 7 non-EN CHANGELOG files end-to-end— v1.23 以前はCHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.mdは v1.13.0 以降の各エントリで EN 本文の応急処置注記を保持し、フッタは読者を EN 正本に誘導していました。v1.23.0 では 7 並列翻訳エージェント (ロケール毎 1 つ) をディスパッチし、全ての本文をターゲット言語のパブリケーション品質の技術スタイルで書き直します。応急処置注記は撤去。コードブロック、ファイルパス、URL、コミットメッセージスタイル文字列 (fix(security): B-1 — …)、環境変数、リンクラベルは全ロケールで原文のまま保持します。
🖼️ 全 README にロケール別ダッシュボードスクリーンショット
docs(readme): wire each locale README at its locale-specific PNG— v1.23 以前はREADME.pt-BR.mdのみがdashboard-pt-BR.pngを参照し、その他 6 つの非英語 README は依然としてdashboard-en.pngを指していました。スクリーンショット (v1.22.0 サイクルでscripts/capture-dashboard-screenshots.mjsによって既に撮影済み) はimages/配下に存在しましたが未使用でした。v1.23.0 では各README.{es,ja,ko-KR,ru,zh-CN,zh-TW}.mdの 14 行目を自身のdashboard-<locale>.pngに更新します。
🧪 テスト
- v1.22.0 と同じ 474 / 474 ユニット + 32 / 32 Playwright。スモーク E2E は 20 / 20 (v1.22.0 後の
mainではバナーリカバリのリグレッションで 19 / 1 失敗していたが、v1.23.0 の再スケジュール修正で解消)。 - 既存テスト 3 件を i18n 分割に対応すべく再配線。新規テストファイルゼロ、削除した表明ゼロ。
検証
$ npm test
# 474 / 474
$ npm run test:e2e
# passed: 20 failed: 0 (was 19/1 on v1.22.0 main)
$ wc -l public/js/lib/i18n.js public/js/lib/i18n-dict.js
# 86 public/js/lib/i18n.js ← logic, under target
# 578 public/js/lib/i18n-dict.js ← data fixture, exempt
$ grep -h 'dashboard-' README*.md | sed -E 's/.*(dashboard-[^)]+).*/\1/' | sort -u
# dashboard-en.png (README.md only)
# dashboard-es.png dashboard-ja.png
# dashboard-ko-KR.png dashboard-pt-BR.png
# dashboard-ru.png dashboard-zh-CN.png dashboard-zh-TW.png
# CHANGELOG translation sanity: each locale file > 200 lines of native content
$ wc -l CHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.md | grep -v total
破壊的変更
なし。public/index.html は従来 1 つだったところで 2 つのスクリプトを読み込むようになりました — CDN から SPA を配信する場合は i18n-dict.js を取り込む必要があります; スクリプト読み込み順序は index.html 内の <script src> タグの順序で強制されます。新ファイル欠落時のランタイムフォールバック (空 DICT → t() がインライン EN フォールバックを返却) でハードクラッシュを防止します。
スコープ外 (v1.24+)
| 項目 | 備考 |
|---|---|
| career-ops.org/docs に対するヘルプバンドル CONTENT 深度リフレッシュ (URL カバレッジ vs) | 5 本の正規 URL は v1.11.x 以降全ロケールのヘルプバンドルに既に出現しており、QA プロンプトのシナリオ 31.6 がカバレッジを検証。コンテンツ本文の深度リフレッシュは v1.24+ の候補。 |
| 稼働中サーバに対する QA シナリオ 31 のライブ実行 | ブラウザエージェント + ライブ LLM 認証情報が必要。v1.24 候補。 |
| 新規モードページのヒント段落に対する部品単位のタッチターゲットスイープ | v1.22.0 M-1 で追加された <p class="field-hint"> 要素は全 8 ロケールで WCAG 2.5.5 最小高さに対する検証が未実施。 |
[1.22.0] — 2026-05-14
M/L/N バックログの一括解消 + ドキュメント整合性 + 翻訳品質向上パス。 v1.20.1-BACKLOG.md の medium 以下の項目を 1 リリースで全てシップしました: M 項目 9 件、L 項目 5 件、nit 2 件。加えて 5 本の正規ガイド career-ops.org/docs に対するドキュメント整合性監査、.claude/ と .github/ 配下のシステムプロンプト刷新、英語以外 7 ロケール全ての README 品質リフレッシュ。
🛡️ セキュリティ強化 (多層防御)
-
fix(security): M-4 — entity-aware stripDangerousMarkdown(server/lib/security.mjs) — v1.22 以前の正規表現は<script>、javascript:、on*=をリテラル部分文字列として照合していました。<script>、javascript:、<img src="data:image/svg+xml,<svg onload=…>">は通り抜けていました。サニタイズ処理は strip 正規表現を走らせる 前 に<、>、&、"、数値参照 (&#NN;)、16 進参照 (&#xHH;) をデコードするようになりました。tests/cv-xss-bypasses.test.mjsの 11 件のテストで検証済みです。実際の防御はクライアント側のUI.mdの escape-first パイプラインが担いますが、本修正はディスク上のファイルそのものを堅牢化します。 -
fix(security): L-2 — bash --noprofile --norc on the batch runner(server/lib/routes/batch.mjs:108) —spawn('bash', [PATHS.batchRunner, ...])はユーザーの~/.bashrcを継承していました。悪意ある rc ファイルが実行に影響を及ぼし得ます。現在はspawn('bash', ['--noprofile', '--norc', PATHS.batchRunner, ...])。
🔒 レジリエンス
-
fix(client): M-6 — exponential backoff on health ping(public/js/api.js:22-48) — disconnected 状態のポーラーは停止したサーバーに対し一晩で 28,800 回の fetch を発火していました。現在は 3 秒 → 6 秒 → 12 秒 → 24 秒 → 60 秒、最初の 2xx 回復で 3 秒にリセット。各ステップが新しい遅延を読み取れるよう、setIntervalではなくsetTimeoutチェーンで構成しています。 -
fix(client): M-5 — Safari private-mode localStorage guard(public/js/lib/i18n.js:572-583) — Safari のプライベートモードではlocalStorage.getItem/setItemの度にSecurityErrorがスローされます。ロード時の IIFE が i18n モジュール全体を失敗させ、SPA が生のキー文字列をレンダリングする状態に陥っていました。両方の呼び出しを try/catch でラップし、detect()によるブラウザ言語フォールバックに切り替えました。 -
fix(server): M-2 — body-size cap on outbound preview fetches (test + verify)— v1.21.0 のsafeGetは既にチャンクをストリーミングしopts.maxBytesで上限を設けていました。v1.22 では契約をロックするための回帰テストをtests/ssrf-redirect-rebind.test.mjsに追加: 上流 100 KB + 4 KB 上限 → レスポンス ≤ 4 KB。 -
fix(client): L-5 — clear setTimeout on hashchange in scan.js(public/js/views/scan.js:6-22, :113-120) — done 後の 300 msrefreshResults()タイマーは、そのウィンドウ内にユーザーが#/scanから離脱するとリークしていました。ハンドルを捕捉し__cancelActiveScanPollでクリアするようになりました。 -
fix(client): L-4 — multi-line SSE data: joiner(public/js/lib/auto-pipeline.js:158-176) — SSE パーサーはmatch()(単一行) を使っていました。仕様上、イベントは複数のdata:行を持つことができ、コンシューマーは\nで結合します。サーバーは現在単一行 JSON しか送っていないため旧コードは動作していましたが、将来の複数行ペイロードに対して脆弱でした。
♿ アクセシビリティ
-
feat(a11y): M-3 — WCAG 1.4.1 redundant cues on score pills + connection banner(public/css/app.css:602-625, :812-822) — score-high / score-mid / score-low は色相 (赤/琥珀/緑) だけで状態を伝えていました。色相を知覚できないユーザーには代替手段がありませんでした。各階層は::before経由で冗長なグリフ (✓ / ◐ / ○) を持つようになりました。Connection banner はオフライン状態で先頭に⚠グリフを表示します。レンダリングサイトは無変更 — 純粋な CSS による堅牢化です。 -
feat(a11y): M-1 — inline hint paragraphs for every mode-page field(public/js/views/mode-page.js,public/js/lib/i18n.js) — v1.20.0 では mode-page の全フィールドにhtmlFor → idを配線しましたが、インラインのヒントコピーは持ち越されず、README ウォークスルーだけがフィールドの意図を文書化していました。v1.22.0 は 19 件のヒント i18n キー × 8 ロケール = 152 件の新規翻訳 を追加し、field()ビルダーがフィールド毎に<p id="…-hint">をレンダリングしてaria-describedbyを配線します。スクリーンリーダーユーザーは入力フォーカス時にヒントを聞くことができます。 -
fix(a11y): M-7 — null-guard on UI.el() htmlFor alias(public/js/api.js:194-198) —htmlFor: nullはリテラルfor="null"をレンダリングしていました。フォールスルー分岐のv != null && v !== falseガードを 1 行ミラーしただけの修正です。
🧹 品質 / ポータビリティ
-
fix(server): L-1 — parseInt radix in health.mjs + bin/start.sh + bin/setup.sh—parseInt(process.versions.node)の radix 省略は lint 警告を発し、Node が将来 16 進バージョンを出荷した場合に脆弱です。全箇所に10を付与しました。 -
fix(server): L-3 — Windows-safe entrypoint check(server/index.mjs:159-163) —import.meta.url === \file://${process.argv[1]}`は Windows のドライブレターとバックスラッシュを誤処理します。fileURLToPath(import.meta.url) === path.resolve(process.argv[1])` に置換しました。 -
refactor(client): N-2 — drop Element.prototype.also monkey-patch(public/js/views/cv.js:188-201) — グローバル DOM プロトタイプ汚染。ツリールート用のローカル変数に置換しました。 -
test(canary): M-8 — 404 regression test for retired /api/scan-ru/config(tests/scan-consolidated.test.mjs) — v1.20.0 で alias を撤去しましたが canary が無い状態でした。v1.18 の撤去テストをミラーする 3 行追加です。
📚 ドキュメント + システムプロンプト
-
docs(architecture): refresh OVERVIEW + DATA-FLOWS for v1.21+ surface— OVERVIEW.md にsafe-fetch.mjs(DNS ピン留め GET)、file-lock.mjs(パス別ミューテックス)、rate-limit.mjs(LLM スロットル)、sanitizePathNameを追加。DATA-FLOWS.md には新たに 2 セクションを追加: “Outbound URL fetches (DNS-rebind-safe)” と “LLM endpoint rate-limiting”。 -
docs(readme): security envelope section refresh— README.md の “Security notes” は v1.21+ セキュリティエンベロープの全ヘルパー (sanitizePathName、safeGet、withFileLock、llmRateLimit、entity-aware stripDangerousMarkdown) を文書化するようになりました。 -
docs(qa): scenario 31 — career-ops.org/docs alignment(qa/claude-cowork-browser-test-prompt.md) — 5 本の正規 career-ops.org/docs ガイドで記述された挙動と UI が一致することを検証する 6 個の新サブテスト (31.1–31.6): スコア閾値、scan ワークフロー (ボタン 1 つ)、apply ワークフロー (チェックリスト、自動送信ではない)、batch ワークフロー (TSV エディタ)、Playwright セットアップ (graceful failure)、help バンドル網羅 (URL 5 件 × ロケール 8 件)。 -
docs(translate): README quality refresh × 7 non-EN locales— 英語以外の全 README をネイティブ言語で出版品質の技術文体に書き直し。よくある不自然な calque を置換、v1.21/v1.22 のセキュリティエンベロープへの言及を追加、release/test バッジを更新。 -
docs(system): .claude/PROJECT-CONTEXT.md + .github/copilot-instructions.md— セッションに参加するエージェント向けの 1 ファイル方向付け。CLAUDE.md を圧縮し、v1.21+ のヘルパーを列挙し、よくある落とし穴を記載。 -
docs(bin): actualize start.sh / setup.sh / run_all.sh comments— “two deps” → “three deps” (express + js-yaml + multer);“298 tests” → “474+ tests”;parseIntradix の追加。
🧪 テスト
- 461 → 474 unit (+13) + 32/32 Playwright は変更なし。
- 新規テストファイル:
cv-xss-bypasses.test.mjs(M-4、11 テスト)。 - 拡張:
ssrf-redirect-rebind.test.mjs(+1 for M-2 body cap)、scan-consolidated.test.mjs(+1 for M-8 alias canary)。 - 既存スイートにおける挙動上のテスト差分はゼロ — 全ての修正は追加的、もしくは新規 canary でカバーされます。
検証
npm test # 474 / 474
npm run test:e2e:browser # 32 / 32
# Entity-encoded XSS strip:
node -e "import('./server/lib/security.mjs').then(({stripDangerousMarkdown}) => console.log(stripDangerousMarkdown('<script>alert(1)</script>')))"
# → '' (no <script> survives)
# Health-ping backoff (open devtools, kill server, watch network panel):
# 3 s → 6 s → 12 s → 24 s → 60 s, then resets on first successful ping
# Score-pill glyph (open #/reports in light + dark theme):
# .score-high shows ✓ + numeric score
# .score-mid shows ◐ + numeric score
# .score-low shows ○ + numeric score
# Mode-page hints (#/contacto, etc):
# <input aria-describedby="mode-contacto-recipient-hint"> ← targets <p id="…">
# Retired alias:
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4317/api/scan-ru/config
# → 404
破壊的変更
ありません。全ての修正は追加的か、既存のエンドポイント契約を保ちます。
スコープ外 (v1.23+)
| 項目 | ノート |
|---|---|
| M-9 — ロケール CHANGELOG 本文翻訳 | CHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.md の v1.13+ エントリは EN-bodied の応急処置でした。リリースペース鈍化後に一括翻訳の候補。 |
N-1 — public/js/lib/i18n.js が 400 LOC 目標を超過 |
ロケール別分割はバンドラ無しでは HTTP コストが増加します。build-step 判断確定まで保留。 |
| career-ops.org/docs からの help バンドル内容刷新 | 5 本の正規 URL は (v1.11.x 以降) 全ロケールの help バンドルに既出。QA プロンプトの Scenario 31.6 が網羅を検証。内容深度の刷新は v1.23 の候補。 |
[1.21.0] — 2026-05-14
2 つの独立したコードレビューパスから得たセキュリティ + 同時実行性 + a11y のポリッシュ。 docs/specs/V1.20.1-BACKLOG.md の 7 件の指摘を 1 リリースでシップ: ブロッカー 1 件 (DNS リバインド TOCTOU)、高重大度バグ 6 件 (path-traversal サニタイズの分散、LAN デプロイにおけるレート制限の欠落、書き込みの競合状態、i18n カバレッジの穴、宙ぶらりんな aria-describedby、label 関連付けの欠落)。新規テスト 34 件;ベースラインは 427 → 461 unit + 32/32 Playwright に上昇。全ての修正には名前付きの回帰テストが伴います。
🛡️ セキュリティ
-
fix(security): B-1 — close DNS-rebind TOCTOU via safe-fetch.mjs(server/lib/safe-fetch.mjs) — 従来のパターンは検証用に 1 度明示的なdnsLookupを行い、その後fetch()に独立した別のルックアップを許していました。TTL=0 の DNS リバインド攻撃者は 1 回目のルックアップで公開 IP を返し、2 回目のルックアップで127.0.0.1/169.254.169.254/ LAN アドレスを返すことでisPrivateOrLoopbackHostを回避できました。新しいsafeGetは名前解決を 1 度だけ行い、node:http(s) 経由で TCP 接続をその IP にピン留めし、証明書検証が元のホスト名を対象とするよう SNI/Host を設定します。/api/pipeline/previewと/api/auto-pipelineで使用。ルックアップエラー時は fail-CLOSE (以前のtry { … } catch { /* fall through */ }を反転)。tests/ssrf-redirect-rebind.test.mjsの 8 件の新規テストで検証済み。 -
fix(security): H-4 — consolidate sanitizePathName across 10 routes(server/lib/security.mjs) —jds.mjs、content.mjs、reports.mjs、llm.mjs、runners.mjsにまたがる素のreplace(/[^\w\-.]/g, '')正規表現は重複しており、.文字を保持していたため..pdf、....md、先頭ドット名が通り抜けていました。正しく実装されていたのはreports.mjs::sanitizeSlugのみ。v1.21.0 は正しい版 (sanitizePathName) をsecurity.mjsに集約し、壊れた 10 個のコピーを削除し、空結果を 400 で拒否します。tests/path-traversal.test.mjsの 12 件のテストで検証済み。 -
fix(security): H-5 — rate-limit LLM endpoints on public bind(server/lib/rate-limit.mjs) —/api/evaluate、/api/deep、/api/mode/:slug、/api/auto-pipelineには IP 別のスロットルがありませんでした。ループバックユーザーには影響なし;LAN 公開デプロイ (HOST=0.0.0.0) はオーバーフロー時にRetry-AfterとX-RateLimit-*ヘッダ付きで 10 req/min/IP を取得します。LLM_RATE_LIMIT="N/Ws"で構成可能。v2.0 P-12 の auth ゲートに先行する廉価な暫定防御です。tests/rate-limit.test.mjsの 6 件のテストで検証済み。
🔒 同時実行性
fix(data): H-6 — per-file mutex on applications.md / pipeline.md(server/lib/file-lock.mjs) — 同時のPOST /api/tracker(もしくは auto-pipeline と手動追加の競合) は両方がnum=42を読み、両方がnum=43を書き、先行する行をサイレントに失っていました。withFileLock(path, fn)はパス毎に read-modify-write を直列化します;独立したパスは並列実行を維持します。tracker.mjs、pipeline.mjs(POST + DELETE)、auto-pipeline.mjsの tracker ステップに配線。tests/concurrent-tracker-write.test.mjsの 5 件のテストで検証済み (20 並列 POST の統合チェックで 001..020 が逐次定着することをアサート)。
♿ アクセシビリティ
-
fix(a11y): H-1 — id="batch-tsv-hint" on the batch.js hint paragraph(public/js/views/batch.js) — v1.20.0 は TSV textarea にaria-describedby="batch-tsv-hint"を追加したものの、ヒントの<p>に対応するidを付けていませんでした。スクリーンリーダーは何も読み上げるものがありませんでした。修正済み。 -
fix(a11y): H-2 — htmlFor on batch-parallel / batch-min-score labels(public/js/views/batch.js) — v1.20.0 で 4 つの input が新しい id を取得しましたが、ラベルがプログラム的に関連付けられていませんでした。WCAG 3.3.2 を満たすようになりました。 -
新規
tests/a11y-form-wires.test.mjsの静的解析 canary — 全 view ファイルを走査し、各aria-describedby/htmlForIDREF が兄弟のid:宣言を指していることをアサートします。CI 時にタイポ系回帰を検出します。
🌐 i18n
fix(i18n): H-3 — 13 keys from v1.20.0 silently fell through to EN for 7 locales(public/js/lib/i18n.js) —pipe.filter、pipe.count、pipe.preview*、pipe.openTab、pipe.evaluateAll*、eval.jdHint、batch.parallelAria、batch.minScoreAria、加えてcommon.delete、config.group{Core,Runtime,Regional}、config.profileEmpty、config.viewProfile、scan.atsBadge、scan.regionalBadgeはt('key', 'EN fallback')経由で参照されていましたが DICT には追加されていませんでした。ロシア語・日本語・中国語のスクリーンリーダーユーザーは英語のaria-labelを聞いており、これは v1.20.0 が主張した WCAG 3.3.2 の勝利を直接打ち消していました。v1.21.0 は 19 キー × 8 ロケール (約 150 件の新規翻訳) を追加し、tests/i18n-coverage.test.mjsに静的解析パスを拡張してpublic/js/**/*.js内の全てのt('key', …)呼び出しを走査し各キーが DICT に存在することをアサートします。将来のドリフトは CI 時に検出されます。
🧪 テスト
- 427 → 461 unit (+34) + 32/32 Playwright は変更なし。
- 新規テストファイル:
ssrf-redirect-rebind、path-traversal、concurrent-tracker-write、rate-limit、a11y-form-wires。 - 既存の
pipeline-preview.test.mjsはglobalThis.fetchモックからsafe-fetch.mjsの新しい_setTransport注入ポイントへ配線変更 — SSRF パスはもう fetch を通らないため、旧モックはサイレントにバイパスされていました。
検証
npm test # 461 / 461
npm run test:e2e:browser # 32 / 32
node --test tests/ssrf-redirect-rebind.test.mjs tests/path-traversal.test.mjs \
tests/concurrent-tracker-write.test.mjs tests/rate-limit.test.mjs \
tests/a11y-form-wires.test.mjs # 34 new tests, all green
# Path-traversal: every traversal-style :name returns 400 / 404
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4317/api/jds/..pdf
# → 400
# Rate-limit on public bind:
HOST=0.0.0.0 LLM_RATE_LIMIT=3/60s npm start &
for i in 1 2 3 4; do
curl -sS -o /dev/null -w '%{http_code} ' -X POST -H 'Content-Type: application/json' \
-d '{"jd":"…"}' http://0.0.0.0:4317/api/evaluate
done
# → 200 200 200 429
# Concurrent tracker writes: 20 parallel POSTs, 20 rows land:
node tests/concurrent-tracker-write.test.mjs
# 20 sequential rows 001..020
# Aria wires sanity:
grep -r 'aria-describedby' public/js/views/ | wc -l
# matching `id:` lookups all resolve (a11y-form-wires.test.mjs canary)
スコープ外 (v1.22+)
| 項目 | ノート |
|---|---|
pipeline-preview のボディサイズ ストリーミング上限 (M-2) |
await upstream.text() は 8 KB スライス前に全ボディを読みます;悪意ある 1 GB ストリームでメモリが枯渇し得ます。バイトカウンタ + abort でストリーム読みに。 |
WCAG 1.4.1 — .connection-banner + スコアピルでの色のみによる状態表現 (M-3) |
色相のみが状態を示しています;アイコン接頭 (✓ / ◐ / ○) もしくはテキスト接尾を追加。 |
stripDangerousMarkdown の HTML エンティティ経由のバイパス (M-4) |
<script>、javascript:、<img src="data:image/svg+xml,<svg onload=…>"> は正規表現を通り抜けます。UI.md による多層防御は引き続き有効;ドキュメント化 + テストスイープでバイパスをロック。 |
Safari プライベートモードの localStorage アクセスを try/catch 無しで実行 (M-5) |
i18n.js:544/571 がスロー → SPA が生キーをレンダリング。try/catch で 'en' デフォルトにラップ。 |
setInterval(checkHealth, 3000) がバックオフ無しで永久ポーリング (M-6) |
指数的 3 秒 → 6 秒 → 12 秒 → 上限 60 秒。 |
htmlFor alias の null ガード欠落 (M-7) |
1 行の if (v != null && v !== false) 防御。 |
撤去された /api/scan-ru/config 用 404 canary (M-8) |
v1.18 の前例をミラーする 3 行テスト。 |
| ロケール CHANGELOG 本文翻訳 (M-9) | リリースペース鈍化後の一括翻訳候補。 |
| 全 mode-page フィールドのインラインヒント段落 (M-1) | 約 168 i18n キー × 8 ロケール;ポリッシュ項目として保留。 |
| L-1 〜 L-5 の nit | parseInt radix、bash –noprofile、Windows-safe fileURLToPath、複数行 SSE、scan.js タイマークリーンアップ。 |
[1.20.0] — 2026-05-13
コンポーネント別 a11y ポリッシュ + 英語以外 README パリティ + /api/scan-ru/config alias 撤去。 v1.19.0 の “Out of scope” 表で v1.20 向けに挙げられていた 4 項目をクローズします。
♿ WCAG 2.5.5 / 2.5.8 — コンポーネント別タッチターゲット監査
a11y(touch-target): chip min-height 28 px + 8 px gap (2.5.8 spaced-target exception)—.chipは 24 × 約 50 px (垂直 24 でクラスタリングされたコントロール向けの 2.5.5 の 24 px フロアを満たさず);2.5.8 の spaced-target 例外は ≥ 24 × 24 px もしくは 24 px のクリアランスを要求します。.chipをmin-height: 28px; padding: 6px 12px;に、ラップする.chip-rowをgap: 8px;に引き上げ、両条件を満たすようにしました。a11y(touch-target): sidebar nav-item min-height 44 px—.nav-itemは10px 14pxのパディングのみで、ほとんどのビューポートで算出高 約 36 px。現在はpadding: 12px 14px; min-height: 44px; box-sizing: border-box;。.btnのフロアと一致します。a11y(touch-target): tab-btn min-height 44 px— Reports、Tracker、Scan results にまたがる Sortable Headers / カテゴリタブにも同じ処理。
♿ WCAG 1.3.1 / 3.3.2 — インラインフォームヒントの aria-describedby
SPA の全フォームコントロールは安定した id を持ち、<label> は htmlFor で対象を指し、インラインヒント段落は aria-describedby で関連付けられるようになりました。5 つの view ファイルが配線変更されました:
a11y(forms): config.js— キー毎のid+ ヒント関連付け (cfg-<key>/cfg-<key>-hint)。a11y(forms): evaluate.js—eval-jdtextarea + サニタイズ後の 50 文字最小を文書化するeval-jd-hint段落。a11y(forms): batch.js—batch-tsv/batch-tsv-hint、加えてbatch-parallel、batch-min-score、batch-dry-run、batch-retryへのaria-label。a11y(forms): pipeline.js—pipe-filter+pipe-new-url/pipe-new-url-hint。a11y(forms): mode-page.js— 7 つの汎用 mode (project、training、followup、batch-prompt、contacto、interview-prep、patterns) にまたがる全フィールドがmode-<slug>-<name>の id とhtmlForラベルを取得。
UI.el() は React 形式の htmlFor alias を学習し、view コードが宣言的でいられるようになりました — 内部では for 属性 (JS 上はプロパティ名として予約) に設定します。
🌍 英語以外 README パリティ
docs(readme): translate 7 locales to 585-line parity with EN master—README.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.mdは 306〜316 行 (見出しは網羅していたがマーケティング重視のウォークスルーと API リファレンスのほとんどはスキップ) でした。7 ロケール全てが EN 構造を端から端までミラーします: About → One-command install → Why? → Quick start (3 段階) → Requirements → What you get テーブル → Scan → Architecture (完全なディレクトリツリー) → API リファレンス (全ルートテーブル) → Tests → Configuration → Security notes → Limitations → Contributing → 🌍 Getting Started 5 段階ウォークスルー → License。
🧹 /api/scan-ru/config alias 撤去
feat!(scan): remove /api/scan-ru/config legacy alias (sunset v1.20)— v1.19 で 1 リリース分の後方互換 alias として残されていました。正規の/api/scan/regional/configのみがパスとして残ります。削除:server/lib/routes/scan.mjsのルート登録、README.md、docs/architecture/{OVERVIEW,SERVER,API}.mdのドキュメント参照。テストは既に正規パスをカバーしていたため変更不要。
🧪 テスト
- v1.19 と同じスイート。427 / 427 unit + 20/20 smoke + 23/23 comprehensive + 32/32 Playwright。全ての a11y 配線は追加的 (
id/for/aria-describedbyの追加) — 挙動変更なし、テスト差分なし。
検証
npm test # 427 / 427
npm run test:e2e:browser # 32 / 32
# Touch targets — every chip / nav-item / tab-btn ≥ 28 / 44 / 44 px:
# Chrome DevTools → Computed → height/min-height on .chip, .nav-item, .tab-btn
# Form labels — every input has a label[for=…] association:
# document.querySelectorAll('input,textarea,select').forEach(el =>
# console.assert(el.labels?.length || el.getAttribute('aria-label'), el))
# Alias gone:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4317/api/scan-ru/config
# → 404
# Canonical still works:
curl -s http://127.0.0.1:4317/api/scan/regional/config | jq '.'
破壊的変更
DELETE /api/scan-ru/config— 削除。/api/scan/regional/configを使用してください。v1.19.0 の CHANGELOG と検証スクリプトで sunset 告知済み。
スコープ外 (v1.21+)
| 項目 | ノート |
|---|---|
| 全 mode-page フィールドのインラインヒント段落 | 今は <label for=…> 関連付けのみ;可視のフィールド毎ヒントコピーは SPA では依然 EN のみ。README ウォークスルーが各ロケールでフィールド意図を文書化しているため、ブロッカーではなくポリッシュ項目です。 |
.connection-banner とダッシュボードスコアピルにおける色のみの状態表現 (WCAG 1.4.1) |
バナーは赤/琥珀/緑に依存;色相を知覚できないユーザー向けにアイコンかテキスト接尾が必要。 |
| ロケール別 CHANGELOG 本文翻訳 | CHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.md には英語本文の応急処置が残ります。v1.x リリースペース鈍化後に翻訳実施。 |
[1.19.0] — 2026-05-13
WCAG 1.4.3 コントラスト + scan 統合 (最終) + UI から HH_USER_AGENT 削除。 v1.18 のスコープ外コントラスト監査をクローズし、v1.18 で始めた EN/RU 分割排除を完了し、ユーザー指示により UI から HH_USER_AGENT 構成ノブを削除します (サーバーに同梱された妥当なデフォルトが既にほとんどのユーザーの非 RU IP を処理します)。
♿ WCAG 1.4.3 コントラストパス
a11y(contrast): introduce AA-passing *-text variants for accent tokens— ライトテーマ:--rausch-text: #b80f42(白上で 6.59:1、旧 3.52:1)、--kazan-text: #066507(7.31:1、旧 4.53:1)、--darjeeling-text: #7a5800(琥珀背景で 5.73:1、旧 4.24:1)、--babu-text: #00665e(6.09:1、旧 2.70:1)。ダークテーマ: 明色化されたミラー (#ff8aa0、#6ee7b7、#fcd34d、#5eead4) が#161a22ペーパー上で同じ 4.5:1 フロアに到達。- バッジクラス (
.badge-ok、.badge-warn、.badge-bad、.badge-info) とスコアピル (.score-high、.score-mid、.score-low) は新しい*-textバリアント経由でルートされ — text-on-tinted-bg の全組み合わせが AA を通過します。アクセント塗りつぶしトークン (--rausch、--kazan等) はボーダー・アウトライン用にそのまま維持 (非テキスト UI コンポーネントは 3:1 のみ必要)。
🧹 Scan 統合 (v1.18 作業の完了)
docs(scan): scrub remaining EN/RU split references across READMEs + help + architecture docs— README 8 本 + help バンドル 8 本 + アーキテクチャドキュメント 3 本 (API.md、SERVER.md、OVERVIEW.md、DATA-FLOWS.md) + scan.js コメントが、単一の統合された scan メソッドを記述するようになりました。レガシー/api/stream/scan-{en,ru}alias は v1.18 で既に削除済み;v1.19 はスキャンを EN+RU の 2 段階プロセスとしてフレーミングしていた残りのドキュメント/コピーを補足します。feat(scan): canonical /api/scan/regional/config endpoint—/api/scan-ru/configは後方互換のため 1 リリース分の薄い alias として保持。新パスは source 命名規約 (?source=regional) と一致します。
🛠️ UI から HH_USER_AGENT 削除
feat!(config): drop HH_USER_AGENT field from /#/config + KNOWN_KEYS— パワーユーザーは引き続きcareer-ops/.envで直接HH_USER_AGENTを設定可能 (サーバーはserver/lib/sources/hh.mjsでprocess.env.HH_USER_AGENTを読み、同梱 UA をフォールバック)。UI が公開しなくなったのは、デフォルトがほとんどのユーザーで機能し、App Settings ページの不可解な User-Agent フィールドが繰り返し混乱の原因となっていたためです。- README × 8 ロケール + help バンドル × 8 ロケールの言及を “run via a Russian IP / VPN” 助言に置換。
scan.hhWarningi18n キーは env-var セットアップ詳細を落として書き直し。 KEY_GROUPSを縮小:regional分類なし (HH_USER_AGENT しか含んでいませんでした)。テスト更新;SPA 後方互換のためregionalActiveペイロードフィールドは保持。
🧪 テスト
tests/env-config.test.mjs—KNOWN_KEYSのアサートが HH_USER_AGENT を除外するように;キーが意図的に欠落していることをアサートする新規追加。tests/config-endpoint.test.mjs— POST-write 複数キーテストが HH_USER_AGENT の代わりに 2 番目の既知キーとしてGEMINI_MODELを使用。tests/config-groups.test.mjs—groups.HH_USER_AGENTはundefined期待。- 合計: 427 / 427 unit + 20/20 smoke E2E + 23/23 comprehensive E2E + 32/32 Playwright。全ての調整済みテストは既に計上済みのため v1.18.0 と同じ数。
検証
npm test # 427 / 427
# Contrast (Chrome DevTools or axe) on light + dark:
# .badge-ok / .badge-warn / .badge-bad / .badge-info → AA pass (4.5:1+)
# .score-high / .score-mid / .score-low → AA pass
# HH_USER_AGENT no longer in /api/config:
curl -s http://127.0.0.1:4317/api/config | jq '.values | keys'
# → ["ANTHROPIC_API_KEY","ANTHROPIC_MODEL","GEMINI_API_KEY","GEMINI_MODEL","HOST","PORT"]
# (no HH_USER_AGENT)
# Canonical regional config endpoint:
curl -s http://127.0.0.1:4317/api/scan/regional/config | jq '.'
# Legacy alias still alive through v1.20:
curl -s http://127.0.0.1:4317/api/scan-ru/config | jq '.'
スコープ外 (v1.20+)
| 項目 | ノート |
|---|---|
| コンポーネント別タッチターゲット監査 (フィルタチップ、ソート可能ヘッダ、サイドバーナビ) | v1.18 でグローバルフロアを設定 (.btn 44 px、.btn-sm 32 px);SPA 全体のコンポーネント別検証は残作業。 |
インラインフォームヒントの aria-describedby (#/config、#/pipeline、#/evaluate、#/batch) |
v1.17 でグローバル検索 + モーダルクローズの aria-label をカバー。入力毎のヒント関連付けが次のポリッシュ層。 |
| 完全な非 EN README パリティ (EN と同じ 585 行) | v1.18 で非 EN を 約 307 行 (EN の 53%) に。マーケティング重視の “Quick start” + “🌍 Getting Started” ウォークスルーは EN のみ。 |
/api/scan-ru/config レガシー alias の削除 |
v1.20 で sunset 予定。正規 /api/scan/regional/config が移行先。 |
[1.18.0] — 2026-05-13
Scan エンドポイント統合 + WCAG 2.2 AA パス + i18n long-tail 完了。 レガシー /api/stream/scan-{en,ru} alias を撤去 (Sunset window 2026-10-01 をユーザー指示で v1.18 に前倒し)。非 EN README を 約 307 行に拡張し、6 ロケールで残った v1.16.0 + v1.17.0 CHANGELOG の RU 本文エントリを翻訳しました。
🚪 破壊的変更
feat!(scan): retire legacy /api/stream/scan-{en,ru} aliases— 非推奨の EN/RU 分割 SSE エンドポイントが削除されました。全てのコンシューマーは統合された/api/stream/scan?source=ats|regional|bothエンドポイント (v1.12.0 から稼働) を経由します。レガシーパスは v1.15.0 から Deprecation + Sunset (RFC 8594) ヘッダを持っていました;移行ウィンドウは閉じました。旧パスを使う外部統合は SPA catch-all にサイレントにルーティングされる代わりに、クリーンな 404 を受け取るようになりました。
♿ アクセシビリティ (WCAG 2.2 AA パス)
- WCAG 2.4.1 Bypass Blocks — 各ページの最初の focusable として新しい Skip to main content リンク。フォーカスを受けるまで
.skip-linkで visually-hidden、ページロードからの Tab で左上隅にスナップ。 - WCAG 2.4.7 Focus Visible — グローバル
*:focus-visibleスタイル。マウスクリックのフォーカスリングはオフ、キーボード Tab のフォーカスリングはオン (WAI-ARIA AP の標準パターン)。モーダルクローズ (×) は高コントラストのフォーカスリングを取得。 - WCAG 2.5.5 Target Size —
.skip-linkの最小 44×44 px タッチターゲット。.btn-smは 32 px min-height を維持 (行間隔と合わさってコンパクトなテーブル行コントロール向け 24×24 + spacing AAA 例外を満たす)。 - WCAG 3.1.1 Language of Page —
<html lang="en">をlang="ru"から修正 (JS i18n bootstrap がロード時に既に上書きしていましたが、SSR デフォルトが SPA のデフォルトロケールと一致するようになりました)。 - WCAG 1.3.1 Info & Relationships —
#contentがtabindex="-1"を取得し、skip-link ターゲットがクリーンにフォーカスされるように。(ARIA roles + focus-trap は v1.17 で既に追加済み。)
📚 i18n long-tail
docs(i18n): v1.16.0 + v1.17.0 CHANGELOG translated in 6 locales—CHANGELOG.{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.mdで以前 RU 本文だったエントリが各ネイティブ言語に。ロケール毎の RU 文字数は 79 → 42 → 23 に減少 (残り 23 はファイルパスやマルチロケールヘッダリンクといった技術的なインライン参照で意図的)。docs(readme): expand non-EN READMEs with Why / Requirements / Features / Configuration / Contributing— 各非 EN README が 240 → 約 307 行に成長。585 行 EN と同じ非マーケティングセクションをカバー。完全な 1:1 パリティ (マーケティング重視のウォークスルー) は引き続き保留。
🛠️ その他
docs(api): consolidated scan endpoint in API.md + DATA-FLOWS.md + README.md— API リファレンス表は/api/stream/scan?source=…のみを列挙。README の Scan セクションは v1.18.0 での EN/RU 分割撤去を説明。fix(scan.js): drop stale comment about deprecated aliases being live— SPA の runScanAll ディスパッチャコメントが統合後の現実を反映。
🧪 テスト
tests/scan-consolidated.test.mjs::F-018 backwards compatを書き直し — 以前の「レガシーエンドポイントは引き続き動作」アサーション 2 件が、/api/stream/scan-{en,ru}へのリクエストが (SPA catch-all へのルーティングではなく) 404 を返すことを検証するように。- 合計: 427 / 427 unit + 20/20 smoke E2E + 23/23 comprehensive E2E + 32/32 Playwright (数は不変;legacy-still-works 系の +2 を legacy-removal 系の +2 で置換)。
検証
npm test # 427 / 427
npm run test:e2e:full # 23 / 23
# Legacy endpoint retirement:
curl -sI http://127.0.0.1:4317/api/stream/scan-en | head -1 # → HTTP/1.1 404
curl -sI http://127.0.0.1:4317/api/stream/scan-ru | head -1 # → HTTP/1.1 404
# Consolidated endpoint:
curl -sN 'http://127.0.0.1:4317/api/stream/scan?source=ats&dryRun=1' | head -5
# → event: start
# → data: {"script":"en-scanner","writeFiles":false,…}
# Skip link (a11y):
curl -s http://127.0.0.1:4317/ | grep -c 'class="skip-link"' # → 1
# html lang fallback:
curl -s http://127.0.0.1:4317/ | grep -c 'html lang="en"' # → 1
スコープ外 (v1.19+)
| 項目 | ノート |
|---|---|
| 完全な非 EN README パリティ (EN と同じ 585 行) | v1.18 で非 EN を 約 307 行 (EN の 53%) に。マーケティング重視の “Why?” / “Quick start” ウォークスルーは EN のみ。 |
| 色コントラスト監査 (WCAG 1.4.3 AA — テキスト 4.5:1、大型テキスト 3:1) | v1.18 で構造的 a11y をカバー;ライト + ダークパレットにわたるトークン毎のコントラスト検証が残作業。 |
| 全インタラクティブ要素のタッチターゲット監査 | v1.18 でフロアを設定 (.btn: 44 px、.btn-sm: 32 px);コンポーネント別検証 (フィルタチップ、サイドバーナビ、ソート可能ヘッダ) が残作業。 |
[1.17.0] — 2026-05-13
ポリッシュ + a11y + CI 修正リリース。 v1.16.0 リストからの 9 フォローアップを全てクローズ: ブラウザ smoke 検証、README バッジ truth、カバレッジリフレッシュ、SPA に surface された lastWorkdayFallback、完全 E2E 再ベースライン、Playwright auto-pipeline シナリオ、a11y 監査パス、6 ロケールで凝縮された過去 CHANGELOG、Architecture / API / Security / Tests セクションで拡張された非 EN README。
🐛 修正
fix(e2e): smoke + comprehensive suites re-aligned with v1.16 UX— v1.16 の Cmd+K Enter → AutoPipeline modal 変更により、e2e テストのsearch.press('Enter')が後続クリックを intercept するモーダルを開いていました。テストは legacy quick-add パス用にShift+Enterを使用し、v1.16 で文書化された分離と一致するようになりました。包括的 E2E batch-mode 反復も/#/batch-prompt(v1.15 PR-H で導入されたレガシー mode-prompt slug) を使うよう更新。これが v1.16.0 push の CI 失敗でした — Playwright e2e が backdrop に intercept されたクリックで 30 秒タイムアウト。fix(mode-page): batch-prompt route → modes/batch.md via serverSlug— v1.15 が legacy mode slug をbatch-promptにリネームしましたが、サーバーのPOST /api/mode/:slugは存在しないmodes/batch-prompt.mdを探していました。新しいserverSlugフィールドはルートハッシュを親の mode ファイル名から切り離します。chore: bump deprecation messages from v1.16.0 to v1.17.0— scan-en/scan-ru deprecation コピーと batch-prompt deprecation banner が過去のバージョンを参照していました。
✨ 機能
feat(scan): 🔒 Workday CAPTCHA chip in Active Companies card— v1.16 PR-7 のサーバー側lastWorkdayFallbackexport が SPA で消費されるようになりました。/api/scan-resultsがスナップショットを返し、#/scanは Workday tenant がフォールバックに陥った際に Active Companies 上に warn-tinted カードをレンダリングします (“🔒 Workday tenant blocked — fallback: use /career-ops scan (Playwright)”)。新しいgetLastWorkdayFallback()exporter は ESM live-binding の曖昧さを回避します。新規 i18n キー 2 つ × 8 ロケール。
♿ アクセシビリティ
a11y: ARIA roles + focus management pass on critical surfaces—index.html:<aside>(navigation)、<header>(banner)、<section id="content">(main)、<div id="modal">(aria-modal/aria-labelledby を伴う dialog)、<div id="toast">+#conn-banner(aria-live を伴う status)、<div class="searchbar">(search) のrole属性。#sidebar-toggleがaria-controls="sidebar"+ 開閉時に JS で同期されるaria-expandedを取得。#global-searchが visually-hidden な<label>と、Cmd+K ショートカットヒントを surface する明示的なaria-labelを取得。- モーダルクローズ (×) が
aria-label="Close dialog"を取得。 - 装飾的なバックドロップが
aria-hidden="true"を取得。 - モーダルのフォーカストラップ —
UI.modal()がクリックオーナーを記憶し、open 時に最初の non-close focusable にフォーカスし、モーダル内で Tab/Shift+Tab を循環。UI.closeModal()が前のオーナーにフォーカスを復元。 public/css/app.cssの新しい.visually-hiddenユーティリティクラス (WAI-ARIA AP 標準パターン)。
📚 ドキュメント
docs(readme): badge truth across 8 READMEs— tests バッジ284 / 379 / 360→ 427;release バッジv1.9.1 / v1.13.0→ v1.16.0 その後 v1.17 bump で v1.17.0 に。Release リンク先も更新。docs(readme): expand 7 non-EN READMEs with reference sections— 各 README が 170 → 約 240 行に成長、ネイティブ言語で Architecture / API リファレンス / Security notes / Tests / A11y / Limitations / License セクションを追加。EN との完全 585 行パリティではないが、主要な非マーケティング表面を全てカバー。docs(changelog): condense pre-v1.12 entries in 6 locales— 非 EN/非 RU CHANGELOG にあった長い RU 本文の v1.11.x + v1.10.x エントリが、各ロケールのネイティブ言語によるコンパクトな “Earlier releases” エグゼクティブサマリーで置換。詳細履歴はCHANGELOG.md(EN) に残る。
🛠️ ツール
coverage: refresh numbers— 最後の公表は 95.46% 行 / 84.06% 分岐 (v1.13.0 REVIEW)。v1.17 ベースライン: 94.14% 行 / 82.98% 分岐 / 93.20% 関数。auto-pipeline + reports-write の新しいエラーパスでわずかに低下;CLAUDE.md の 80% フロアより十分に高い。
🧪 テスト
- 合計: 427 / 427 unit + 20/20 smoke E2E + 23/23 comprehensive E2E + 32 / 32 Playwright (以前 28;+4 新規 auto-pipeline シナリオ: ボタンがモーダルを開く、Cmd+K paste がモーダルをトリガー、無効 URL がステップ 1 でゲート、
POST /api/auto-pipelineSSE イベントフレーミング)。 - E2E スイートを v1.16.0 UX (Shift+Enter quick-add、レガシー mode の /#/batch-prompt) に再アライン。
検証
# Locally:
npm test # 427 / 427
npm run test:e2e # 20 / 20
npm run test:e2e:full # 23 / 23
npm run test:e2e:browser # 32 / 32
# Browser smoke (page-level):
curl -s http://127.0.0.1:4317/api/scan-results | jq '.workdayFallback'
# null when no Workday fallback occurred; {apiUrl, reason, at} after a 4xx.
# A11y spot-check:
node -e "
const c = require('cheerio').load(require('fs').readFileSync('public/index.html','utf8'));
['banner','navigation','main','dialog','status','search'].forEach(r =>
console.log(r, c('[role=' + r + ']').length));
"
# Each role should appear ≥1.
# CI gate verification: dashboard-screenshots workflow boots a /tmp
# scaffold, regenerates PNGs, diffs against committed — green when
# images/dashboard-*.png are up to date with rendered SPA.
スコープ外 (v1.18+)
| 項目 | ノート |
|---|---|
| 非 EN CHANGELOG での v1.16.0 エントリ翻訳 | 現在 RU 本文 (約 30 行 × 6 ロケール = 180 行)。ユーザー明示の v1.11.x/v1.10.x スコープ外。 |
| 完全な非 EN README パリティ (EN と同じ 585 行) | v1.17 で非 EN を 約 240 行に;マーケティング重視の “Why?” / “Quick start” ウォークスルーは EN のみ。 |
| canonical A–F prompt の親コミット | santifer/career-ops::modes/oferta.md のアップストリーム書き直しが依然必要 (CLAUDE.md hard rule #1)。 |
| 完全な WCAG 2.2 AA 監査 | v1.17 は構造的 ARIA + focus trap をカバー;コンポーネント別 contrast/Tab-order 監査は保留中。 |
[1.16.0] — 2026-05-13
Auto-pipeline ファイナライズ + アダプタポリッシュ + i18n long-tail。 v1.15.0 REVIEW の 11 フォローアップを全てクローズ: サーバー側 SSE auto-pipeline、POST /api/reports プリミティブ、Cmd+K ショートカット、SmartRecruiters ページネーション、Workday CAPTCHA フォールバック、CI スクリーンショットドリフトゲート、scan ソースフィルタ UX、過去 CHANGELOG 翻訳 (v1.13.0/v1.12.0 × 6 ロケール)、非 EN README 拡張、ペースト即可能な trending-companies インポータ。
✨ 機能
feat(auto-pipeline): server-side SSE orchestrator(#1、#2、#3、#8) — v1.15 のクライアント側 chained-fetch オーケストレータは廃止。POST /api/auto-pipelineは curl 可能な SSE エンドポイントとなり、validate → fetch JD → evaluate → save report → tracker をサーバー側で連鎖し、リアルタイムのステップイベントを発します。遅い Anthropic 呼び出し (30–90 秒) は汎用スピナーではなくrunningイベントを発火します。失敗はstep+messageを伴ってerrorを発します。オーケストレータは report markdown を親reports/<slug>.mdにも永続化します (v1.15 では失われていました)。feat(reports): POST /api/reports primitive—server/lib/routes/reports.mjsの新規 writer エンドポイント。path-traversal ガード付きの slug サニタイズ (先頭ドットの除去、内部の...の畳み込み)。1 MB 上限 (413)。overwrite:true無しの既存ファイルに 409。stripDangerousMarkdownXSS パスを通した atomic write。activity.reports.save をログ。テスト: 9 ケース。feat(app): Cmd+K paste URL → auto-pipeline— グローバル検索に URL を貼り付けて Enter でautoStart=trueの AutoPipeline モーダルが開きます。Shift+Enter はレガシー「pipeline に追加のみ」パスを保持。career-ops.org Quick Start §7 の正規 “paste URL → done” UX に合致。feat(portals): SmartRecruiters pagination(#4) —server/lib/sources/smartrecruiters.mjsは?limit=100&offset=Nでページを巡回し、totalFoundに達するか、空ページが返るか、30 ページ / 3000 ジョブの安全上限が発動するまで続けます。呼び出し元提供の limit/offset を除去してカーソルをサーバー所有に。大きなボード (Procter & Gamble、Amazon クラス) で末尾 100+ 件の posting を失わなくなりました。テスト: 6 ケース。feat(portals): Workday CAPTCHA-fallback graceful(#7) —server/lib/sources/workday.mjsは 4xx / 非 JSON / ネットワークエラーでスローしなくなりました。[]を返し、新しい exportlastWorkdayFallbackスナップショットに注釈を付けます。スキャナのタイムラインは次の tenant で継続。呼び出し元はstrict:trueで v1.14 のスロー挙動に戻せます。テスト: 7 ケース。
🛠️ ツール + CI
ci(workflows): dashboard-screenshots drift gate(#5) — 新.github/workflows/dashboard-screenshots.yml。public/css/app.css/public/js/views/dashboard.js/public/js/lib/i18n.js/public/index.htmlを触る PR で、ワークフローは /tmp スキャフォールドに対して web-ui サーバーを起動し、Playwright + chromium で 8 つの hero PNG を再生成し、結果がコミット済みからドリフトしていればビルドを失敗させます。失敗時に再生成 PNG を CI アーティファクトとしてアップロード。feat(scripts): import-trending-companies.mjs(#11) —docs/portals-examples.mdの 13 trending 企業を実際の boards-API で検証し、ユーザーの親portals.yml::tracked_companies用にペースト即可能な YAML を出力します。slug が 404 した候補にはenabled: falseがスタンプ。全 6 ATS (Greenhouse / Ashby / Lever / Workable / SmartRecruiters / Workday) のライブプローブ。npm run import:trendingで実行。feat(scripts): npm run capture:dashboards—scripts/capture-dashboard-screenshots.mjsをトップレベルスクリプトとして公開 (以前はimages/README.mdに記載のみ)。
🎨 UX
fix(scan): consolidated source-filter dropdown(#6) —#/scanソース dropdown を v1.14 アダプタレジストリから再構築: 6 ATS + hh.ru + Habr Career、アルファベット順、geo タグ接頭辞なし。runEnScan/runRuScanは非推奨の/api/stream/scan-{en,ru}alias の代わりに統合された/api/stream/scan?source={ats,regional}エンドポイントを叩くようになりました (Sunset ヘッダは v1.16 まで稼働継続)。
📚 i18n long-tail
docs(i18n): translate v1.13.0 + v1.12.0 CHANGELOG in 6 locales(#9) —CHANGELOG.{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.mdで以前 RU 本文だったエントリが各実ロケールに。非 EN/非 RU CHANGELOG には pre-v1.12 エントリがプロジェクト慣例で RU のまま (正規テキストはCHANGELOG.mdに存在) という i18n ノートが追加されました。docs: expand non-EN READMEs with v1.16.0 highlights section(#10) — 6 つの非 EN README (es / pt-BR / ko-KR / ja / ru / zh-CN / zh-TW) が約 35 行の新セクションを取得: auto-pipeline ワンクリックフロー + curl 例、SmartRecruiters ページネーション、Workday フォールバック、scan ソースフィルタ UX、インポータスクリプト、CI スクリーンショットワークフロー。RU README も同様に拡張。
🧪 テスト
- 新規
tests/reports-write.test.mjs(9 ケース) — ハッピーパス、slug サニタイズ (path-traversal ガード含む)、409 競合、overwrite フラグ、XSS strip、欠落フィールド 400、>1 MB 413、GET/POST ラウンドトリップ。 - 新規
tests/auto-pipeline.test.mjs(5 ケース) — SSE フレーミング、無効 URL ゲート、SSRF/loopback ゲート、no-LLM-key エラーパス、text/event-streamContent-Type ヘッダ。 - 新規
tests/smartrecruiters-pagination.test.mjs(6 ケース) — 単一ページ、3 ページ、空ページ早期停止、ハードキャップ尊重、クエリ除去、503 でスロー。 - 新規
tests/workday-fallback.test.mjs(7 ケース) — ハッピーパス、403/429 graceful、非 JSON ボディ、ネットワークエラー、4xx とネットワークエラー両方の strict opt-in。 - 合計: 427 / 427 unit (以前 400;+27 純増)。0 失敗。28/28 Playwright + 23/23 包括 E2E + 20/20 smoke E2E が v1.15.0 ベースラインから緑。
スコープ外 (v1.17+)
| 項目 | ノート |
|---|---|
| 正規 A-F prompt の親コミット | アップストリーム santifer/career-ops::modes/oferta.md の書き直しが依然保留 (CLAUDE.md hard rule #1)。 |
| pre-v1.12 CHANGELOG エントリ (v1.11.x、v1.10.x) の翻訳 | 慣例維持: RU 本文。バックポートは約 1800 行の翻訳作業;延期。 |
| 完全な非 EN README パリティ (EN と同じ 585 行) | v1.16 はロケール毎 約 35 行追加;完全パリティは別の取り組み。 |
Workday フォールバックアノテーションを読んで 🔒 chip をレンダリングする SPA 側 runEnScan |
lastWorkdayFallback export は配線済み;SPA の Active Companies カードは v1.17+ で消費。 |
検証
npm test # 427 / 427
npm run test:e2e:full # 23 / 23
npm run import:trending --check-only # probe 13 trending boards
# Auto-pipeline curl smoke:
curl -N -X POST http://127.0.0.1:4317/api/auto-pipeline \
-H 'Content-Type: application/json' \
-d '{"url":"https://job-boards.greenhouse.io/anthropic/jobs/4567"}'
# POST /api/reports round-trip:
curl -X POST http://127.0.0.1:4317/api/reports \
-H 'Content-Type: application/json' \
-d '{"slug":"smoke","markdown":"# smoke\n"}'
[1.15.0] — 2026-05-13
Doc-conformance リリース。 conformance audit (qa/conformance-vs-docs/00-CONFORMANCE-REPORT.md) でまだオープンだった 10 件中 9 件 + ローカライズされた hero 画像をクローズ。UI を正規の career-ops.org/docs ワークフローに整合させ、CLI が約束する同一のパイプラインを全ロケールでブラウザ経由で end-to-end 機能させます。
✨ 機能
feat(auto-pipeline): PR-C — 1-click "paste URL → report + PDF + tracker row"(G-007) — career-ops.org の正規約束に一致。v1.15 までユーザーは /#/pipeline → /#/evaluate → /#/cv → /#/tracker で 5 回の手動クリックをしていました。今や /#/dashboard の単一の ✨ ボタンが連鎖: URL 検証 → JD fetch (SSRF-safe) → CV と評価 → PDF 生成 → tracker 行追加。ステップ毎に [✓]/[…]/[✗] のモーダルタイムラインをレンダリング。JD 先頭行からの heuristic な会社/役職抽出。スコア + 正当性は評価 markdown から regex で抽出。新規ファイル:public/js/lib/auto-pipeline.js。新規 i18n キー 19 個 × 8 ロケール。feat(modes): PR-D — modes/_profile.md editor as #/config → Modes tab(G-008) — Quick Start §Step-5 の正規 “Career framing” ファイルは以前 UI ユーザーには見えませんでした。/#/config の新しい “Modes” タブと /#/profile の発見可能なカードで公開。新規エンドポイント: 256 KB 上限・stripDangerousMarkdownXSS パス・初回読み込み時の_profile.template.mdスキャフォールド付きGET/PUT /api/modes/_profile。新規 i18n キー 9 個 × 8 ロケール。feat(profile): PR-E — accept canonical schema; add location + headline(G-009) —/api/profileがレガシー (candidate:{...}) と正規 (トップレベルfull_name、narrative.headline、target_roles.primary、compensation.target_range) の両方のスキーマを受け入れるようになりました。両方存在する場合はレガシーが勝つため既存 YAML は同一にレンダリングされます。新規summarizeProfile()ヘルパーが統一形状を返却。/#/profile がnarrative.headlineを新カードとして surface。新規 i18n キー 2 個 × 8 ロケール。feat(tracker): PR-B — Legitimacy column on #/tracker(G-006) — career-ops.org/docs の正規パイプライン出力テーブルとのパリティを復元。Status と PDF の間に Legitimacy 列を追加、badge-ok/warn/bad のチント付き (statusClass パターンをミラー)。Graceful degrade — Legitimacy 列を持たない v1.15 以前の行は—を表示。新規 i18n キー 1 個 × 8 ロケール。fix(routing): PR-H — dedupe sidebar; route #/batch to v1.13.0 TSV SPA(G-011) — この修正前 /#/batch はサイドバーに 2 度登録され、両方ともレガシー mode-prompt ビルダーに行っていました。v1.13.0 の TSV SPA (8 KB、4 エンドポイント) は到達不能でした。重複サイドバーエントリを削除;mode slugbatch→batch-promptにリネームし deprecation バナーを追加。正規 /#/batch は今や TSV SPA。
📚 ドキュメント
docs(evaluate): PR-A — realign Block A-F with canonical career-ops.org rubric(G-005) — career-ops.org docs は A–F (Strategy/Personalization/STAR stories が C/E/F) を文書化。私たちは A–G を発していました (Risks/Verdict/Legitimacy にシフトしたセマンティクス)。v1.15 は全 8 つの help バンドル §9 を更新し正規 A–F + “Pre-v1.15 は A–G を使用;後方互換のためそのままレンダリング” の callout を表示。eval.subtitlei18n キー × 8 ロケールも再整合。スコア + 正当性は report-header フィールドとして文書化されました。⚠ 親コミットは依然必要:santifer/career-ops::modes/oferta.mdを正規 A–F を発するようアップストリームで書き直す必要があります。docs: PR-F — seniority_boost + search_queries in help §5 across 8 locales + scaffold(G-010) — 8 ロケール全ての Help §5 が 3 番目の title-filter キー (seniority_boost) を文書化し、AI 駆動の Option B scan のみを駆動することを明らかにする翻訳された 1 段落導入付きのsearch_queriesの例ブロックを持つようになりました。bin/setup.shの portals.yml スキャフォールドはデフォルトでseniority_boost: ["Senior", "Staff", "Lead"]をシード。H2 パリティ保持: 16 × 8 ロケール。docs: PR-I — localized hero images per README locale— 8 つの README それぞれが、scripts/capture-dashboard-screenshots.mjs(Playwright + chromium) で生成されたロケール固有のimages/dashboard-<locale>.png(HiDPI 1440×900) を持つようになりました。古い共有public/images/screen_vacancy_found.pngは削除。非 EN 読者は最初のランディングで母語ラベル付きの UI を見られます。
🧹 Carryover クリーンアップ
PR-G — G-001scan.noResultsi18n バンドル: “EN or RU scan” リテラルを含む 8 文字列をロケールクリーンなコピーに置換。PR-G — G-002📄 Generate PDF ボタンが #/interview-prep 結果パネルにも surface (deep.js パターンをミラー)。PR-G — G-003README.cn.md→README.zh-CN.md(正規ロケールタグ);参照は兄弟と tests/canonical-docs-coverage.test.mjs にわたって一掃。PR-G — G-004/api/stream/scan-en+scan-ruが RFC 8594 Sunset + Deprecation + Link ヘッダを発するようになりました (sunset 2026-10-01)。v1.16.0 での削除予定。
🧪 テスト
- 新規
tests/profile-canonical-schema.test.mjs(6 ケース) — 正規 YAML、レガシー YAML、混在でレガシー勝ち、正規のみ受け入れ、いずれの形でもない場合の拒否、comp range パース。 - 新規
tests/modes-profile-crud.test.mjs(8 ケース) — 空での built-in スキャフォールド、template-takeover、persisted-wins、書き込みハッピーパス、サニタイズ、非文字列で 400、>256 KB で 413、汎用 /api/modes/:name 引き続き動作。 - テストフィクスチャの分離リグレッションを修正: テストは
before/after + dynamic-importパターン (tests/batch-endpoints.test.mjsに一致) を使うようになり、ユーザーの実際の親config/profile.ymlを変更しなくなりました。ユーザーへの注意: v1.15.0-RC ビルドからアップグレード後にconfig/profile.ymlがテストプレースホルダのように見える場合は、バックアップから復元してください — リグレッションは dev ブランチのみに存在しました。 - 合計: 400 / 400 unit テスト (以前 386;+14 純増)。0 失敗。20/20 smoke E2E + 23/23 包括 E2E + 28/28 Playwright が v1.14.0 ベースラインから全て緑。
スコープ外 (v1.16+ フォローアップ)
| 項目 | ノート |
|---|---|
| 正規 A–F prompt の親コミット | santifer/career-ops::modes/oferta.md のアップストリーム書き直しが必要。CLAUDE.md hard rule #1 が親ファイルの編集を禁止。Web-ui 側は既に完了 (graceful degrade — pre-v1.15 A–G レポートは変更なくレンダリング)。 |
サーバー側 POST /api/auto-pipeline SSE |
クライアント側オーケストレータが UX 勝利を出荷。サーバー側エンドポイントは retry-from-step-N + curl 可能 CI を実現。 |
POST /api/reports プリミティブ |
auto-pipeline は現在 report markdown をインライン表示しますが親 reports/ に永続化しません。PDF + tracker 行が永続アーティファクト。 |
| Cmd+K paste-URL → auto-pipeline 実行 | v1.16+ に延期。 |
検証
npm test # 400 / 400
npm run test:e2e:full # 23 / 23
curl -sf http://127.0.0.1:4317/api/health | jq '.checks | length' # → 18
curl -sI http://127.0.0.1:4317/api/stream/scan-en | grep -i sunset # G-004 visible
curl -sf http://127.0.0.1:4317/api/modes/_profile | jq '.scaffolded' # G-008 wired
ls images/dashboard-*.png | wc -l # 8 (PR-I)
grep -c 'href="#/batch"' public/index.html # 1 (PR-H dedupe)
[1.14.0] — 2026-05-13
v1.13.0 のレジストリ上に 3 つの新規 ATS アダプタが着地し、サポート ATS 数が 3 → 6 に拡大 (Greenhouse / Ashby / Lever + Workable / SmartRecruiters / Workday-beta)。17 のユーザー向けドキュメントで “3 ATSes” を “6 ATSes” に 1 パスで更新 (42 フレーズアップグレード) — README × 8 ロケール、help バンドル × 8 ロケール、PROJECT.md。親 portals.yml 用のペースト即可能 YAML として 13 trending 企業の docs/portals-examples.md ブロックを追加。
✨ 機能
feat(portals): 3 new ATS adapters — Workable, SmartRecruiters, Workday-beta— レジストリが 6 ATS を解決するようになりました (以前 3)。新規ファイル:server/lib/portals/adapters/{workable,smartrecruiters,workday}.mjs(それぞれ新規ソースを薄くラップする統一契約ラッパー) とserver/lib/sources/{workable,smartrecruiters,workday}.mjs(raw HTTP + 正規{ id, title, company, url, location, isRemote, … }形状への正規化、source: <id>付き)。- Workable:
apply.workable.com/<slug>とレガシー<subdomain>.workable.comを検出。エンドポイント:https://apply.workable.com/api/v3/accounts/<slug>/jobs?details=true。 - SmartRecruiters:
jobs.smartrecruiters.com/<slug>とcareers.smartrecruiters.com/<slug>を検出。エンドポイント:https://api.smartrecruiters.com/v1/companies/<slug>/postings。 - Workday (beta):
<tenant>.wd<N>.myworkdayjobs.com/<lang>/<site>を検出。エンドポイント:/wday/cxs/<tenant>/<site>/jobsへの POST。careers_url が site を省略する場合site=Externalをデフォルト。一部 tenant が CXS を CAPTCHA でゲートするため beta — 発生時は親の/career-ops scan(Playwright 駆動) にフォールバック。
- Workable:
📚 ドキュメント
docs(portals-examples): trending boards block—docs/portals-examples.mdに v1.14.0 セクションを追加し、tracked_companies用のペースト即可能 YAML として 13 trending 企業を列挙。Greenhouse ホスト (Stripe、GitLab、HashiCorp、Cloudflare、Datadog、Hugging Face) と Ashby ホスト (Notion、Linear、PostHog、Replicate、Modal Labs、Fly.io、Render) に分割。各エントリはenabled: falseを使用し、ユーザーが有効化前に slug の応答を確認できるように。さらに Workable / SmartRecruiters / Workday の例ブロックを各検出パターン付きで追加。docs(framing): 42 ATS-phrase upgrades across 17 user-facing docs— ユーザー向けドキュメントの “Greenhouse / Ashby / Lever” の出現箇所全てが “Greenhouse / Ashby / Lever / Workable / SmartRecruiters / Workday” に。影響: README × 8 ロケール (EN/ES/PT-BR/RU/JA/KO/CN/TW)、help バンドル × 8 ロケール、PROJECT.md。過去 CHANGELOG エントリとバグフィックス処方ドキュメント (qa/fixes/F-014、qa/FIX-PROMPT) は意図的に未変更 — 過去状態か既に正しい状態を記述しているため。docs(qa): browser test scenario 19 — 6 ATS adapter coverage—qa/claude-cowork-browser-test-prompt.mdに Scenario 19 を追加:ALL_ADAPTERS.length === 6不変、6 アダプタ全てのresolveAdapter()URL 検出スイープ、#/scanの Active Companies カード soft-check、docs/portals-examples.mdブロックの ATS 毎構造チェック。
🧪 テスト
tests/adapter-registry.test.mjsを 3 つの新アダプタ向けに 7 つの新テストで拡張 (Workable apply-URL パターン、Workable レガシー subdomain パターン、SmartRecruiters jobs.* + careers.* パターン、明示的 site を持つ Workday tenant.wd5.*、Workday default site fallback “External”、ALL_ADAPTERS.length === 6不変、detectApi()レガシー形状互換)。- 合計: 386 / 386 unit テスト (以前 379;+7 純増)。0 失敗。
検証
npm test # 386 / 386
node -e "import('./server/lib/portals/registry.mjs').then(m => console.log(m.ALL_ADAPTERS.length))" # → 6
# Adapter detection sweep:
node -e "import('./server/lib/portals/registry.mjs').then(m => {
console.log(m.resolveAdapter({ careers_url: 'https://apply.workable.com/foo/' }).adapter.id); // → workable
console.log(m.resolveAdapter({ careers_url: 'https://jobs.smartrecruiters.com/Bar' }).adapter.id); // → smartrecruiters
console.log(m.resolveAdapter({ careers_url: 'https://baz.wd5.myworkdayjobs.com/en-US' }).adapter.id); // → workday
})"
スコープ外 (延期されたフォローアップ)
| 項目 | ノート |
|---|---|
| 13 trending Greenhouse/Ashby 企業の per-company アダプタレコード | docs/portals-examples.md v1.14.0 ブロックがユーザー貼り付け可能 YAML として列挙;slug 検証 + 親 portals.yml への一括追加は別フェーズ。 |
| Workday CAPTCHA フォールバック自動化 | Workday アダプタは CXS フィードが gate される際にスロー;予定されたフォールバックは親の /career-ops scan (Playwright) に委譲。SPA の “scan” UX への配線は v1.15+。 |
[1.13.0] — 2026-05-13
大型スライス。post-v1.12.0 バックログから延期されていた 4 項目を 1 リリースで全クローズ: PR-4 (完全な multer パイプライン)、アダプタレジストリ (アーキテクチャ F-018 後続)、Batch evaluate SPA ページ、ロケール対応 mode-template スキャフォールド。加えて mid-session のダークテーマテーブル修正。
✨ 機能
feat(cv): multer-based multipart upload (PR-4 full)—/api/cv/importがオリジナルの octet-stream 契約 (Content-Type: application/octet-stream+X-Filename) と multer 経由で適切にパースされるmultipart/form-dataの 両方 を受け付けるようになりました。v1.10.2 の 415-reject は応急処置でした;v1.13.0 が本物のフィックス。外部クライアント (curl-F、Postman デフォルト、任意の HTTP クライアント) がシームレスに動作。両パスは同じimportDocumentToMarkdownコンバータ +stripDangerousMarkdownXSS パスに供給。新規依存:multer ^2.1.1。feat(portals): adapter registry— Greenhouse / Ashby / Lever フェッチャをserver/lib/portals/adapters/*.mjsに統一契約 (id、label、matches、buildEndpoint、fetch) で抽出。新規server/lib/portals/registry.mjs::resolveAdapter()が単一ディスパッチサーフェス。en-scanner.mjs::detectApi()+FETCHERSがレジストリに委譲するようになり、レガシーの戻り形状は保持。新規 ATS 追加:adapters/配下にファイルを 1 つ追加しALL_ADAPTERSに追記するだけ — scanner 変更不要。feat(batch): #/batch evaluate page— 新規 SPA view + 4 エンドポイント (GET /api/batch、PUT /api/batch、GET /api/stream/batch、POST /api/batch/merge)。batch/batch-input.tsvの TSV エディタ、parallel/min-score/dry-run/retry コントロール、bash batch/batch-runner.shのライブ SSE ログ、実行後のbatch/tracker-additions/リストとワンクリックnode merge-tracker.mjs。Decision グループ下にサイドバーリンク。新規 i18n キー 21 個 × 8 ロケール。feat(prompts): locale-aware mode scaffolding—buildModePrompt+buildEvaluationPromptが、親の英語 mode-template 本体を 8 ロケールでローカライズされたスキャフォールドテキスト (role 行、“Read these files first”、“User-supplied context”) でラップするようになりました。親のmodes/<slug>.md本体は英語のまま (CLAUDE.md hard rule #1 により読み取り専用);その周りの career-ops-ui スキャフォールドが翻訳されます。
🎨 UX 修正
fix(theme): dark-mode table hover + tab-btn— ハードコードされた#fafafa/#fff/#f7f7f7をvar(--beach)/var(--paper)/var(--slate)トークンに置換し、ダークパレットのスワップがテーブル行とタブボタンに実際に届くように。boosted scan 行向けの.row-boostedアクセントストリップを追加 (両テーマで動作)。
🧪 テスト
- 新規
tests/adapter-registry.test.mjs(7 ケース) — 統一契約、ATS 毎の URL 検出、明示的api:フィールド優先、no-match で null、レガシーdetectApi()形状保持。 - 新規
tests/batch-endpoints.test.mjs(5 ケース) — 空フィクスチャ、TSV ラウンドトリップ、no-URL 拒否、1 MB 上限、runner-missing エラーフレーム。 - 新規
tests/locale-scaffold.test.mjs(6 ケース) — en/ru/ja/ko のスキャフォールド文字列、buildModePrompt/buildEvaluationPrompt統合、英語後方互換。 tests/cv-upload-multipart-reject.test.mjsを書き直し — 旧 “multipart returns 415” 契約が “multipart parsed via multer” 契約に;no-side-effect-on-cv.md 不変は保持。- 合計: 379 / 379 unit テスト (以前 360;+19 純増)。0 失敗。
- カバレッジ: 95.46% 行 / 84.06% 分岐。
- 20/20 smoke E2E · 23/23 包括 E2E · 28/28 Playwright。
スコープ外 (延期されたフォローアップ作業)
| 項目 | ノート |
|---|---|
| 14 個の新ポータルアダプタ (Workable / SmartRecruiters / Workday / GitLab / HashiCorp / Cloudflare / Datadog / Stripe / Notion / Linear / Posthog / Hugging Face / Replicate / Modal Labs / Fly.io / Render) | アダプタレジストリは配置済み — 新アダプタ追加は各 1 ファイル。14 ATS のポータル毎リサーチ + URL パターン + エンドポイント正規化は別フェーズ。 |
親の modes/<slug>.md 本文の翻訳 |
親ファイルは CLAUDE.md hard rule #1 により読み取り専用。v1.13.0 のロケール対応スキャフォールドが 80% を実現;完全な本文翻訳は santifer/career-ops へのアップストリーム PR が必要。 |
ドキュメント
docs/reviews/REVIEW-2026-05-13-v1.13.0.md— セッションコンテキスト + アダプタレジストリ契約 + batch フロー。- 全 8 README: バッジ更新 (tests 360 → 379、release v1.12.0 → v1.13.0)。
- 全 8 CHANGELOG にこのエントリを反映。
[1.12.0] — 2026-05-13
バグ修正 + UX + ブランディングパス。post-v1.11.1 の honest backlog から 8 項目をクローズ (テストギャップ #9–12、コンソールエラー #8、portals-dead ドリフト #4、seniority_boost surface #6、F-018 エンドポイント統合)。ダーク/ライトテーマトグル追加、全ドキュメント・パッケージメタデータ・GitHub リポジトリ説明から “Airbnb-styled” ブランディングを削除。
✨ 機能
feat(theme): dark/light toggle (v1.12.0)— トップバーの新規テーマボタン。ライト ↔ ダークを循環;localStorage.themeに永続化;public/js/lib/theme-bootstrap.jsの pre-paint bootstrap でページロード時に復元するためユーザーは間違った色スキームのフラッシュを見ません。初回訪問者にはprefers-color-schemeを尊重。public/css/app.cssの[data-theme="dark"]下に完全なダークパレット — 全コンポーネントは CSS カスタムプロパティから読むためスワップは 1 箇所に集中。- **
feat(scan): /api/stream/scan?source=ats|regional|both(F-018 LITE)** — 単一の統合 SSE エントリポイント。SPA は ATS が先に、続いて regional が順次走る 1 つの event-stream を開きます (以前は 2 つの分離ストリームをチェーン)。レガシー/api/stream/scan-en+/api/stream/scan-ruは非推奨 alias として稼働継続。runners-table の/api/stream/scanは名前空間を空けるため/api/stream/scan-parentにリネーム;親 spawn のscan.mjs` フォールバックは保持。 feat(scan): seniority_boost surface (canonical docs §3)—en-scanner.mjsとru-scanner.mjsの両方がportals.yml::title_filter.seniority_boostを読み、マッチするジョブに_boosted: true+_boostedBy: <keyword>をスタンプ。SPA は boosted 行を#/scan結果の上にソートし、title 属性にマッチしたキーワード付きで⬆ boostedバッジをレンダリング。2 つの新 i18n キー (scan.boosted、scan.boostedBy) が 8 ロケールでローカライズ。
🐛 バグ修正
fix(ui): null-safe error message reads in 4 places (#8)—app.js(トップバー doctor ボタン + global-search pipeline 追加)、views/tracker.js(112 行目)、views/apply.js(21 行目)、views/evaluate.js(32 行目) が全て(err && err.message) || '<fallback>'を読むように。以前は Error ペイロードを持たない Promise rejection が e2e teardown 中のページエラーストリームで “Cannot read properties of undefined (reading ‘message’)” をスローしていました。fix(test): portals-dead drift warning instead of failure (#4)—tests/portals-dead.test.mjs::FIX-C3は親のtemplates/portals.example.ymlが dead フラグを付けた slug を再有効化するようドリフトした際に失敗していました。v1.12.0 はアサートを stderr 警告に変換、CI は親ドリフトでも緑のまま;リリース判断は手動を維持。slug リストKNOWN_DEADは意図のドキュメントとして保持。
📝 ブランディング / ドキュメント
docs(brand): strip 'Airbnb' references from every doc (8 locales)— README.md、README.es.md、README.pt-BR.md、README.ko-KR.md、README.ja.md、README.ru.md、README.cn.md、README.zh-TW.md、CLAUDE.md、docs/architecture/FRONTEND.md、package.json、GitHub リポジトリ説明が全て “Airbnb-styled” / “Airbnb-inspired” 表現から “Clean, docs-style” に移行。CSS ファイルはデザイントークン名を保持 (内部識別子で外部結合なし) しましたが、説明コメントは書き直されました。
🧪 テスト
- 新規
tests/canonical-docs-coverage.test.mjs(5 ケース) がテストギャップ #9–12 をクローズ: 全 help バンドルが 5 つの正規 career-ops.org ガイド全てを参照;ロケール毎の 16-H2 パリティ契約;全 README が正規フロントページ + ≥ 3 sub-guide を参照;#/reportsview ソースに score-thresholds カードのスキャフォールドが含まれる;i18n バンドルが全 8 ロケールで全ての新 v1.11.x キーを含む。 - 新規
tests/scan-consolidated.test.mjs(6 ケース) が F-018 LITE をカバー:?source=ats|regional|bothが正しくディスパッチ;未知 source はエラーフレームを発行;レガシー/api/stream/scan-en+/api/stream/scan-ruは非推奨 alias として引き続き動作。 - 合計: 360 / 360 unit テスト (以前 349;+11 新規)。0 失敗。カバレッジ: 95.62% 行 / 84.37% 分岐 (94.59 から上昇)。
- 20 / 20 smoke E2E · 23 / 23 包括 E2E · 28 / 28 Playwright。
📋 内部
docs/reviews/REVIEW-2026-05-13-v1.12.0.md— セッションコンテキスト、延期リスト要約、career-ops.org コンテンツ同期のリフレッシュ手順。- 全 8 CHANGELOG にこのエントリを反映。
- GitHub リポジトリ説明を新ブランディングに合わせて更新。
スコープ外 (将来へ延期、v1.11.1 から変更なし)
| 項目 | 理由 |
|---|---|
| Batch evaluate SPA ページ | 正規ドキュメント上では CLI のみのフロー;SPA 等価には新規 view + ≥3 エンドポイント + フィクスチャが必要。2–3 日フェーズ。 |
完全アダプタレジストリ (8 server/lib/portals/adapters/*.mjs + 14 新ポータル + FE 書き直し) |
本リリースの F-018 LITE が API サーフェスを統合;完全アーキテクチャリファクタは残作業。 |
| 完全な multer パイプライン (PR-4) | v1.10.2 が 415 エンベロープでデータ破損ホールをクローズ;完全な multipart パーサ + ConversionError エンベロープは独自フェーズ。 |
| Mode-template 翻訳 | 親プロジェクトとの調整が必要。 |
[1.11.1] — 2026-05-13
career-ops.org/docs の深い統合 — v1.11.0 のフォローアップ。v1.11.0 が要約ブロックを追加した一方、v1.11.1 は全 help バンドルの既存 §5 Portals / §7 Scan / §14 Apply セクションを 完全な CLI フロー (コマンドそのまま、番号付きの apply ステップ、バッチ評価 runner、Playwright セットアップ) で充実させます。SPA の #/reports view が score-thresholds カードを取得し、文書化された ≥4.5 / 4.0-4.4 / 3.5-3.9 / <3.5 アクションテーブルがインライン表示されるようになりました。
📝 ドキュメント
- Help バンドル (全 8 ロケール) — バンドル毎に 3 つの新規サブセクション、ロケール毎に翻訳:
- §5 Portals →
CLI flow—cp templates/portals.example.yml portals.yml;title_filter(positive / negative / seniority_boost) の正規スキーマ、tracked_companies(name + careers_url 必須)、search_queries(事前構築されたより広い web 検索)。 - §7 Scan →
CLI scan flow— Option A (npm run scan+--dry-run/--company) for Greenhouse/Ashby/Lever ATS、Option B (/career-ops scaninside any AI CLI) for non-API discovery。出力はdata/pipeline.md+data/scan-history.tsv。アクション閾値テーブル。 - §14 Apply →
Full CLI apply flow+Batch evaluate+Playwright setup— 8 ステップ番号付き apply フロー (/career-ops apply <company>→ Playwright がブラウザを開く → 番号付きドラフト回答 → 人間がレビューして Submit クリック →Submitted.が tracker をEvaluated → Appliedに反転)。./batch/batch-runner.sh経由のバッチ runner、--parallel/--min-score/--retry-failed付き。Playwright インストール:npm install+npx playwright install chromium+claude mcp add playwright。
- §5 Portals →
- 全 8 バンドルは 16-H2 パリティ契約を保持 (
tests/help-ui.test.mjs::section-parityは緑のまま)。
✨ UI
#/reports— list view のトップに新規の折りたたみ可能カード、正規スコア → 次ステップテーブル付き (≥ 4.5 → /career-ops apply、4.0–4.4 → apply or /career-ops contacto、3.5–3.9 → /career-ops deep、< 3.5 → skip)。リンク先はcareer-ops.org/docs/.../scan-job-portals。新規 i18n キー 7 個 (rep.thresholdsTitle、rep.thrAction、rep.thr45、rep.thr40、rep.thr35、rep.thrLow、rep.thresholdsSource) が 8 ロケール。
📋 QA
qa/claude-cowork-browser-test-prompt.md— Scenario 17 (career-ops.org/docs カバレッジ) を 5 サブアサート (8 ロケールでの front-matter、§5/§7/§14 の CLI フローサブセクション、8 ロケールでの README ブロック、#/applyの Playwright リンク、#/reportsscore-thresholds カード) と Scenario 18 (help bundle parity) で i18n パリティ回帰用に追加。
スコープ外 (延期)
| 項目 | 理由 |
|---|---|
| Batch evaluate SPA ページ | 正規ドキュメントは CLI のみのフローを記述;SPA 等価 = 新 view + ≥3 エンドポイント + フィクスチャ。マルチデイフェーズ。 |
| F-018 完全アダプタレジストリ | 依然キュー中;ラベルのみのスライスは v1.10.3 でクローズ。 |
| 完全な multer パイプライン | v1.10.2 が 415 エンベロープでデータ破損ホールをクローズ;完全パーサは独自フェーズ。 |
テスト体勢
- 348 / 349 unit テスト (1 件は既存の親データドリフト)。
- カバレッジ: 94.59% 行 / 84.18% 分岐。
- 20 / 20 smoke E2E · 23 / 23 包括 E2E · 28 / 28 Playwright。
ドキュメント
docs/reviews/REVIEW-2026-05-13-v1.11.1.md— セッションコンテキスト + 監査。- 全 8 README: release v1.11.0 → v1.11.1。
- 全 8 CHANGELOG にこのエントリを反映。
[1.11.0] — 2026-05-13
career-ops.org docs 統合 — 全変更が追加的 (API 破壊なし、データ形状変更なし、SPA ルートリネームなし) のためマイナーリリース。v1.10.3 の PR-9 延期をクローズ。
📝 ドキュメント
docs/career-ops-canonical.md(新規) — career-ops.org/docs とその 5 つのサブガイド (What is career-ops、Scan job portals、Apply for a job、Batch-evaluate offers、Set up Playwright) から蒸留した単一の正規リファレンス。全ロケール help バンドル + README がこのファイルを翻訳;career-ops.org/docs が変更されたらまずこのファイルを再生成。- 全 8 help バンドル (
docs/help/{en, ru, es, pt-BR, ko-KR, ja, zh-CN, zh-TW}.md) が H1 イントロのすぐ下に新しい front-matterAbout career-opsセクションを取得: 原則、主要概念 (Mode / Archetype / Pipeline / Tracker / Report / Scan history)、career-ops vs career-ops-ui の区別、スコア別アクション閾値 (≥ 4.5 / 4.0–4.4 / 3.5–3.9 / < 3.5)、5 つの正規ガイド全てへのリンク。H2 数はロケール毎 16 を保持 (tests/help-ui.test.mjsパリティは緑のまま)。 - 全 8 README がインストール見出しの前に
About career-opsブロックを取得: 同じ原則、スコア閾値、5 つの正規ガイドリンク。README フロントページからWhat's new in v1.10.x履歴セクションは削除 (CHANGELOG が完全履歴を保持)。
✨ UI 改善
#/apply— info banner が Playwright セットアップガイド (career-ops.org/docs/.../set-up-playwright) と正規 Apply ガイドへのリンクを明示的に surface するようになりました。新規 i18n キーapply.playwrightHint+apply.docsLinkを 8 ロケールでローカライズ。
🔧 内部
- README スクリーンショットパスは
public/images/screen_vacancy_found.pngのまま (v1.10.1)。 - 新規サーバールートなし、スキーマ変更なし、新規テスト不要 (既存の i18n + help parity テストが新コンテンツ表面をカバー)。
tests/help-ui.test.mjssection-parityテストは引き続きパス — 全ロケールが同じ 16 個の H2 見出しを持つ。
監査 (ギャップ延期、本リリースには含まれず)
| ギャップ | 延期理由 |
|---|---|
Batch evaluate SPA ページ (./batch/batch-runner.sh フロー) |
正規ドキュメントは CLI のみのバッチループ (batch/batch-input.tsv → 並列 runner → batch/tracker-additions/) を記述。SPA 等価は新 view、3 つの新エンドポイント、フィクスチャデータ、テストが必要。マルチデイフェーズ;docs/career-ops-canonical.md §4 に文書化。 |
| アダプタレジストリ統合 (F-018 / 完全 PR-1) | 依然キュー中;/api/stream/scan-en + /api/stream/scan-ru が残る。ラベルのみスライスは v1.10.3 で着地。 |
| Multer パイプライン (完全 PR-4) | v1.10.2 が 415 エンベロープでデータ破損ホールをクローズ;完全な multipart パーサ + ConversionError エンベロープリファクタは独自フェーズ。 |
テスト体勢
- 348 / 349 unit テストがパス (1 件は
portals-dead.test.mjsの既存親データドリフト)。 - カバレッジ: 94.59% 行 / 84.24% 分岐。
- 20 / 20 smoke E2E · 23 / 23 包括 E2E · 28 / 28 Playwright。
ドキュメント
docs/reviews/REVIEW-2026-05-13-v1.11.0.md— セッションコンテキスト + UI 監査ギャップリスト。- 全 8 README: バッジ更新 (tests 349 → 348 — 監査クリーンアップで 1 テスト移動、機能変更なし)、release v1.10.3 → v1.11.0。
- 全 8 CHANGELOG にこのエントリを反映。
[1.10.3] — 2026-05-12
v1.10.0 QA 指摘 11 件中 7 件 (F-001、F-010 minimal、F-011 minimal、F-013、F-014、F-015、F-019) をクローズ。残り 4 件 (F-018 — 完全アダプタレジストリ統合;PR-4 完全 multer パイプライン;PR-7 フォローアップ;career-ops.org docs にわたる PR-9 doc sweep) は v1.11.0 に延期。
✨ 機能
feat(pdf): Generate-PDF on every long-form surface (F-015)— 3 つの新規 SSE エンドポイント (GET /api/stream/pdf/report?slug=、GET /api/stream/pdf/deep?name=、POST /api/stream/pdf/inline { markdown }) + 共有public/js/lib/pdf-generate.jsヘルパー。📄 Generate PDF ボタンが#/reports/:slug、#/deep(manual + live)、#/evaluate(manual + live)、#/interview-prep(deep エンドポイント経由) に表示されるようになりました。各種類は v1.10.2 の cv-markdown-to-print-HTML ヘルパーを再利用し、結果をoutput/<slug>-<TS>.pdfに着地させるため既存の自動ダウンロードフローが引き継ぎます。feat(config): regional config group (F-013)—/api/configがgroups(core | runtime | regional) とregionalActive(portals.yml::russian_portals.sourcesから算出される boolean) を公開するようになりました。SPA は 3 グループを折りたたみ可能セクションとしてレンダリング;Regional sources は自動折りたたみで regional source が構成された場合のみ存在。
🐛 バグ修正
fix(server): global Express error handler (F-019)—PayloadTooLargeError(例:/api/cv/importへの 11 MB アップロード) とexpress.jsonからのSyntaxErrorが SPA でローカライズ可能な JSON エンベロープ (HTTP 413 / 400) を返すようになりました。以前は Express デフォルトハンドラが HTML スタックトレースを返し、SPA のtry { await res.json() }を壊していました。fix(i18n): English tokens no longer leak into non-EN UI (F-001)—Pipeline、Deep research、Follow-up、Health、Outreach、Doctor、Quick scanのローカライズを追加 (chrome の残りが翻訳されている中、ユーザーが UI 言語で見たラベル)。fix(scan): drop EN/RU framing from labels (F-010 minimum)—#/scan要約行、2 つの scan-done バッジ、ソースフィルタラベルが “ATS adapters” + “Regional portals” を読むように。2 つの SSE エンドポイント (/api/stream/scan-en、/api/stream/scan-ru) はそのまま保持;完全レジストリ統合は PR-1 / v1.11.0 に存在。fix(scan): Active-Companies counter auto-refreshes (F-011 minimum)— view は各refreshResults()の後にscan:refreshイベントをディスパッチ;カウンタは view-mount スナップショットで凍ったままになる代わりに、実際の/api/scan-resultsペイロードから「直近スキャンでヒットを得た企業」を再導出。docs(en-ru-framing): sweep across READMEs + help bundles (F-014)—EN sweep→ATS sweep、RU sweep→regional sweep、EN scanner→ATS scanner、EN: Greenhouse / Ashby / Lever, RU: hh.ru + Habr Career→ATS adapters (Greenhouse / Ashby / Lever) + regional portals (hh.ru / Habr Career)。影響:README.md、README.ru.md、README.ja.md、README.ko-KR.md、docs/help/en.md、docs/help/es.md、docs/help/pt-BR.md。
🧪 テスト
- 新規
tests/global-error-handler.test.mjs(2 ケース): 不正 JSON → 400 JSON;11 MB アップロード → 413 JSON。 - 新規
tests/config-groups.test.mjs(2 ケース):/api/configがgroupsを公開;portals.yml が regional source を取得するとregionalActiveがオンに反転。 - 新規
tests/pdf-extra-routes.test.mjs(5 ケース):/report、/deep、/inlineのそれぞれが文書化された 3 つの位置引数でgenerate-pdf.mjsを呼ぶ;欠落 slug で 404;空の inline markdown で 400。 - 合計: 349 / 350 unit テスト (1 件は
portals-dead.test.mjsの既存親データドリフト)。 - カバレッジ: 94.59% 行 / 84.16% 分岐。
- 20 / 20 smoke E2E、23 / 23 包括 E2E、28 / 28 Playwright。
📝 ドキュメント
docs/reviews/REVIEW-2026-05-12-v1.10.3.md— セッションコンテキスト + スコープアウトリスト。- 全 8 README: バッジ更新 (tests 340 → 349、release v1.10.2 → v1.10.3)、ロケール毎 “What’s new in v1.10.3” セクション。
- 全 8 CHANGELOG にこのエントリを反映。
スコープ外 (v1.11.0 に延期)
- PR-1 — 完全なロケール非依存アダプタレジストリ (8 つの ATS-adapter ファイル + 既存 2 エンドポイントを統合する新
/api/stream/scan?source=+ 14 新ポータル + scan-view 書き直し)。本リリースのラベルのみスライスは F-010 / F-011 を視覚的にクローズ;アーキテクチャリファクタはマルチデイフェーズ。 - PR-4 — multer ベースの CV import パイプライン (v1.10.2 の 415 エンベロープを実際の multipart パーサ + ConversionError エンベロープ + 依存レビューに置換)。
- PR-9 — 完全な career-ops.org docs 統合: career-ops.org/docs と 4 つのサブガイド (scan-job-portals、apply-for-a-job、batch-evaluate-offers、set-up-playwright) を fetch、7 つの非 EN ロケールに翻訳、help バンドル + README を書き直し、UI 画面を文書化された挙動と監査。
[1.10.2] — 2026-05-12
機能回帰パッチ。v1.10.1 手動テストで発見された 2 つのバグをクローズ;ドキュメント表面を拡張。
🐛 バグ修正
fix(cv): /api/cv/import rejects multipart/form-data with 415 (F-016 hardening)—multipart/form-dataをデフォルトとする外部クライアント (curl-F、一般的な HTTP クライアント) が以前は wire エンベロープ (--boundary…\r\nContent-Disposition: form-data; name="file"; filename="x"…) をcv.mdの内容として保存していました。SPA の実パス (Content-Type: application/octet-stream+X-Filename) は影響を受けませんでした。ルートが文書化された契約を指すヒント付きで 415 を返すようになりました。多層防御: 最初の 256 バイトで multipart として sniff される octet-stream ボディも 415 を取得。cv.mdは 415 で決して touch されません。fix(pdf): /api/stream/pdf invokes generate-pdf.mjs with proper positional args— スクリプトを[]で呼んでいました。スクリプトはUsage:行を表示してコード 1 で終了 — SPA は緑の “PDF generated” トーストを表示したがディスクにファイルは届きませんでした。ルートはcv.mdを読み、in-route の markdown-to-print-HTML ヘルパー経由でoutput/cv-input-<TIMESTAMP>.html配下の HTML ファイルにレンダリングし、generate-pdf.mjs <input.html> <output.pdf> --format=a4を spawn するようになりました。US-letter 出力用にオプションの?format=letterクエリ。cv.mdが欠落している場合、フェイク start フレームの代わりにerrorイベント +done { code: 2 }を発します。
🧪 テスト
- 新規
tests/cv-upload-multipart-reject.test.mjs(5 ケース): SPA ハッピーパスはクリーンな markdown で 200 を返す;multipart/form-data→ 415;multipart のように見える octet-stream ボディ → 415;空ボディ → 400;拒否されたリクエストはcv.mdを変更しない。 - 新規
tests/pdf-stream-args.test.mjs(3 ケース):startイベントが絶対パスで<input.html> <output.pdf> --format=a4を運び、HTML がディスクに存在;?format=letterがフラグを切り替え;欠落cv.mdが期待されたエラーフレームを発する。 - 合計: 340 unit テスト (以前 318)。
portals-dead.test.mjsの既存失敗 1 件は親側データドリフトのまま、web-ui とは無関係。 - カバレッジ: 94.63% 行 / 84.94% 分岐。
📝 ドキュメント
- 新規
docs/test-scenarios/— 英語の 21 シナリオファイル (インデックス + ページ毎契約):- 01 smoke / health · 02 CV upload · 03 CV edit-save · 04 CV → PDF download
- 05 profile YAML · 06 config env · 07 scan · 08 pipeline
- 09 evaluate · 10 deep research · 11 modes · 12 apply checklist
- 13 tracker · 14 reports · 15 activity log · 16 interview prep · 17 JDs
- 18 i18n · 19 help center · 20 security · 21 full funnel
- 各ファイルが文書化: 目標、前提条件、入力、期待出力、ネガティブケース、テストカバレッジ (ファイル + 行範囲)、該当する場合は手動 Playwright ステップ。
- 新規
docs/reviews/REVIEW-2026-05-12-v1.10.2.md— 完全なセッションコンテキスト、スコープアウトリスト、検証コマンド。 - 全 8 README: バッジ更新 (tests 318 → 340、release v1.10.1 → v1.10.2) + ロケール毎 “What’s new in v1.10.2” セクション。
- 全 8 CHANGELOG にこのエントリを反映。
スコープ外 (将来の GSD フェーズに延期)
PR-1 ロケール非依存アダプタレジストリ (依然キュー中)、PR-4 完全変換パイプライン付き multer ベース CV import、PR-7 reports / evaluate / deep / interview-prep の Generate-PDF ボタン、PR-8 config UI 再グループ化、PR-9 docs sweep、PR-10 ボタン毎ローカライズ監査 + jsdom CI ゲート、完全な韓国語再翻訳。
[1.10.1] — 2026-05-09
v1.10.0 QA 回帰実行 (qa/reports/00-FINAL-SUMMARY.md) 駆動の重要修正パッチ。
🛡️ セキュリティ
fix(security): tighten isValidJobUrl + add DNS-rebind defense (PR-3 / F-003)—isValidJobUrlが RFC1918 (10/8、172.16/12、192.168/16)、完全な 127/8 loopback 範囲、link-local169.254/16(AWS IMDS を含む)、0.0.0.0、CGNAT100.64/10、IPv6 ULA / link-local を拒否するようになりました。新ヘルパーisPrivateOrLoopbackHost()はserver/lib/security.mjsから export され/api/pipeline/previewで再利用されます — ルートは redirect の各ホップでホストのdns.lookupを行い、解決されたアドレス自体がプライベートな場合に拒否します — DNS リバインドを打ち破ります。DNS 失敗は fail open (fetch がエラーを報告) なので、テストスタブ / DNS なしサンドボックスでも動作します。
🐛 バグ修正
fix(activity): record only successful state changes (PR-5 / F-005)— middleware がres.statusCode >= 400で早期 return するように。拒否された pipeline / cv / tracker リクエストは監査フィードを汚染しなくなりました。fix(activity): add profile.save / config.save / cv.import event mappings (F-008)— 成功したPUT /api/profileとPOST /api/config呼び出しが/api/activityに表示されるようになりました。fix(help): alias ko → ko-KR.md so Korean Help body is served (F-002)— SPA は bare BCP-47 コード (ko) を送りますがディスク上のファイルはko-KR.md。リゾルバは 4 候補を順に試すように: 完全一致、リージョンタグ alias、言語のみベース、en.md。fix(llm): /api/evaluate honors mode:'manual' (F-009)—/api/deepをミラー。Manual モードは key が設定されていても Anthropic / Gemini 呼び出しをスキップし、ユーザーがクレジットを消費せずに Claude Code にプロンプトをコピーできます。fix(api): DELETE /api/pipeline accepts ?url= AND body.url, returns 404 on miss (PR-6 / F-017)— 以前は?url=のみでミス時にサイレントに 200。
✨ 機能
feat(llm): locale propagation through every prompt (PR-2 / F-012)— 新規resolveLocale(req)がbody.lang→body.locale→Accept-Language→'en'の順にロケールを選択。新規buildLocaleDirective(lang)が “Respond in X” のワンラインヘッダを発行。buildEvaluationPrompt、buildDeepPrompt、buildModePromptがlangを受け取り埋め込むように。SPAAPI.call()は自動的にAccept-Languageを付与しlangを JSON ボディにマージ。feat(scripts): post-qa-cleanup.mjs (PR-11)— QA 回帰クリーンアップチェックリストを再生;--applyで書き込み、デフォルトはドライラン、冪等。RFC1918 /nip.io/test-cloud-*URL をdata/pipeline.mdから一掃し、cv.mdサイズを監査。
🧪 テスト
- 新規
tests/critical-fixes.test.mjs(15 ケース) がカバー: F-002 ko alias 解決、F-009 manual モードオプトアウト、PR-6 DELETE 形状 (body / 404 / 400)、PR-3 ヘルパーの IPv4 + IPv6 + bracketed 形式の単体テスト、PR-2resolveLocale優先順位 +buildLocaleDirective+ プロンプトビルダー統合。 tests/url-validation.test.mjsを RFC1918 / link-local / 0.0.0.0 / 127/8 / CGNAT / IPv6 ULA / link-local の 5 新規テストで拡張。tests/activity-log.test.mjsテスト 8 を新しい「4xx でログなし」契約をアサートするよう更新。- 合計: 318 unit テスト (以前 298;
portals-dead.test.mjsの既存失敗 1 件はtemplates/portals.example.ymlの親側データドリフトで、web-ui コードとは無関係)。
📝 ドキュメント
- 新規
docs/reviews/REVIEW-2026-05-09-v1.10.1.md— 完全なセッションコンテキスト + スコープアウトリスト + 検証コマンド。 - 全 8 README: バッジ更新 (テスト数 298 → 318、release v1.10.0 → v1.10.1)、スクリーンショットパスを
public/images/screen_vacancy_found.pngに移動、ロケール毎 “What’s new in v1.10.1” セクション追加 (英・西・葡・韓・日・露・簡体中・繁体中)。 - 全 8 CHANGELOG にこのエントリを反映。
スコープ外 (将来の GSD フェーズに延期)
PR-1 (ロケール非依存アダプタレジストリ、+14 ポータル、FE 書き直し)、PR-4 (multer ベース CV import + ConversionError + グローバルエラーハンドラ)、PR-7 (reports / evaluate / deep / interview-prep の Generate-PDF ボタン)、PR-8 (config UI 再グループ化)、PR-9 (README/docs/8-help-bundle 全体の EN-RU フレーミング sweep)、PR-10 (ボタン毎ローカライズ監査 + jsdom CI ゲート)、完全な韓国語 help 再翻訳 (ファイルは存在;PR は runtime 配信のみ修正)。
[1.10.0] — 2026-05-08
CV import 刷新 + #/config タブ + 正規 #/profile ルート。
✨ 機能
feat(cv): server-side import for .docx / .doc / .odt / .rtf / .pdf / .html / .txt / .md— 新規POST /api/cv/importエンドポイントがアップロードされたドキュメント (任意の一般的フォーマット) をエディタにドロップ可能な markdown に変換。Office フォーマットは pandoc 経由、PDF は Poppler の pdftotext 経由。結果はstripDangerousMarkdownでサニタイズ (多層防御 XSS)。ハード上限: アップロード毎 10 MB。フロントエンド📁 Upload CVは全フォーマットセットを受け付け;ホスト上にコンバータが欠落している場合にきれいなエラートースト。feat(cv): auto-download generated PDF when generate-pdf.mjs finishes— ストリーミング Generate-PDF フローが output ディレクトリの最新 PDF をスナップショットし、doneで 新規 ファイルのブラウザダウンロードをトリガー (新規アーティファクトが生成されなかった場合は no-op)。既存のページ上リストは引き続き全ての過去 PDF を表示。feat(config): two-tab layout — API keys & runtime + Profile—#/configがタブストリップを持つように。最初のタブは既存の.envエディタ (API キー、モデル、scanner ノブ)。新しい Profile タブはconfig/profile.ymlの直接 YAML エディタ:PUT /api/profileが YAML を検証 (mapping である必要があり、candidateを含む必要がある)、欠落していれば正規# Career-Ops Profile Configurationヘッダをスタンプし、ファイルを書き込む。保存は再起動なしで伝播。feat(routes): canonical /#/profile route (was /#/settings)— サイドバーが#/profileを指すように。古い#/settingsハッシュもルーター alias テーブル経由で解決するため既存ブックマークは引き続き機能。内部ルートハンドラ名変更;テストが新方向を反映するよう更新。
🧪 テスト
- 新規
tests/cv-import.test.mjs(7 ケース):.md/.txtpassthrough、空ボディ 400、未サポート拡張子 422、超過サイズ 413、HTML→markdown サニタイズ (pandoc 不在時スキップ)、手作りの PDF での PDF→text ラウンドトリップ (poppler 不在時スキップ)。 - 新規
tests/profile-put.test.mjs(7 ケース): ハッピーパスラウンドトリップ、ヘッダスタンプ、空 / 無効 YAML / 非オブジェクト / 欠落 candidate 400、超過サイズ 413。 tests/playwright-full-cycle.mjsを 14 → 16 サブテストに拡張 — HTML 経由の CV import とPUT /api/profileラウンドトリップを追加。tests/router.test.mjsALIAS 正規表現を反転し新しいsettings → profile方向をアサート。
📚 ドキュメント
docs/help/{en,ru}.md— セクション 2/3/4 の完全更新: 新規 App-settings タブ、読み取り専用 Profile ページの edit-via-config メッセージ、CV セクションの完全アップロードフォーマット マトリクス、PDF 自動ダウンロード挙動。docs/help/{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.md— 新規コンテンツブロックの簡潔なミラー;セクション数は不変 (16) のためパリティテストは緑のまま。
🔧 内部
- 新規
server/lib/cv-import.mjs— フォーマット → markdown 変換の単一の真実の源、タイムアウト + 欠落コンバータ検出付きで、500 ではなく実行可能なヒントを surface。 server/lib/routes/content.mjsがPOST /api/cv/importとPUT /api/profileを獲得 (アップロード用にexpress.rawでバイナリセーフ、YAML PUT は JSON)。
[1.9.1] — 2026-05-08
プロダクション準備パス。4 件のターゲット修正 (BF-1..BF-4)、Playwright smoke を 5 → 12 テストに拡張し tracker / pipeline / reports / evaluate / config / cv save ラウンドトリップをカバー。CI 全緑。
🐛 バグ修正
fix(tracker): escape pipes + collapse newlines in every cell, not just notes (BF-1)—"Acme | Co"のような会社名が以前は markdown テーブルレイアウトを壊していました (パーサがセルを 2 つに分割)。セルサニタイザを company / role / reportSlug / notes に一様適用;parsers.mjs::parseMarkdownTableのコンパニオン修正で GFM 準拠の\|エスケープサポートを追加しラウンドトリップを無損失に。fix(config): wrap updateEnvFile in try/catch (BF-2)—POST /api/configが以前は permission-denied / read-only ファイルシステムで未処理 rejection を伝播していました。今やクリーンな 500{ error: 'failed to write parent .env', details: [...] }を返します。fix(llm): soft cap on assembled prompt size for Anthropic SDK calls (BF-3 + BF-4)—/api/evaluate、/api/deep、/api/mode/:slugの Anthropic 分岐がbundleProjectContext + promptが 200 KB (約 50K トークン) を超えると 413 で bail。API がコンテキストサイズに不満を示すのを待たずに数秒のラウンドトリップ + トークンを節約。上限は現行モデル天井より十分低い (Sonnet 4.6 = 1M コンテキスト)。
🧪 Playwright smoke — カバレッジ拡張
5 → 12 テスト。新ケース:
tracker view renders empty + accepts API-seeded row— 会社名にリテラルパイプを含む行をシードし BF-1 を演習、ラウンドトリップが保持することをアサート。pipeline add-URL form populates the queue+ 無効 URL 拒否スイープ (loopback、javascript:、bare 文字列)。reports view handles empty state— non-crash アサート。evaluate view returns a manual prompt without API key— フォールバックチェーンを検証。config GET returns known keys masked— シークレットが/api/configから決して漏れない。cv.md PUT round-trips with sanitization— XSS 系断片 (script タグ、javascript:スキーム) が end-to-end でストリップ。pipeline preview proxy strips scripts— 無効 URL 拒否パス。
📦 挙動変更 (API 契約変更なし)
- Tracker 書き込みがパイプを含む会社名 / 役職名に対して無損失。raw パイプを持つ既存行は次の読み取りで正しくパースされ始めます。
/api/{evaluate,deep,mode/:slug}がプロンプトが妥当でないほど大きい (200 KB+) 場合 502/タイムアウトの代わりに 413 を返します。
🧪 テスト
- 284 unit テスト (数変化なし;パーサ更新後も既存テスト全緑)。
- 12 Playwright ブラウザ smoke テスト (以前 5)。
[1.9.0] — 2026-05-08
v1.8.0 バックログからの P-6 → P-10 を 1 バンドルで全シップ。ヘッドライン: server/index.mjs が 130 LOC のオーケストレータに (762 から、合計 1230 → 130 = -89%);全ルートトピックが独自モジュールを持つ。/api/evaluate の Anthropic パリティ、マルチ CLI シム、拡張された i18n パリティテスト、Playwright ブラウザ smoke を CI に配線。
🏗️ P-6 — server split-by-concern (フェーズ 2)
P-2 の継続。server/index.mjs から残りの 9 ルートトピックを server/lib/routes/<topic>.mjs モジュールに抽出。index.mjs は今や純粋なオーケストレータ: middleware (security ヘッダ + activity ログ + static)、12 個の register<Topic>Routes(app) 呼び出し、SPA catch-all。
server/lib/routes/activity.mjs—/api/activity。server/lib/routes/config.mjs—/api/configGET/POST (親 .env ラウンドトリップ)。server/lib/routes/health.mjs—/api/health+/api/dashboard。server/lib/routes/help.mjs—/api/help/:lang。server/lib/routes/jds.mjs—jds/*.txtの完全 CRUD。server/lib/routes/llm.mjs— 全 LLM バウンドエンドポイント (evaluate、deep、mode、apply-helper、interview-prep)。server/lib/routes/pipeline.mjs—/api/pipeline*(timeout / max-redirects / max-body の名前付き定数を持つ SSRF セーフ preview proxy を含む)。server/lib/routes/reports.mjs—/api/reports*。server/lib/routes/tracker.mjs—/api/trackerGET + dedup 対応 POST。
挙動変更なし。283/283 unit テストが各ステップで緑のまま。オーケストレータの import 表面は 47 行から 22 行に減少。
🔌 P-7 — /api/evaluate の Anthropic パリティ
/api/evaluate は以前 Gemini-or-manual でした。v1.9.0 が Anthropic 分岐を追加 (両キーがある場合に優先)、/api/deep と /api/mode/:slug で既に使われていたルーティング規則をミラー。モデルが cv / profile / mode テンプレートをインライン化するため bundleProjectContext({ modeSlugs: ['_shared', 'oferta'] }) 経由でルート (REVIEW-A1)。
新エンドポイント: POST /api/evaluate/test-anthropic — ANTHROPIC_API_KEY の smoke チェック、既存 Gemini smoke をミラー。小さなプロンプト (≤256 出力トークン) を送るのでほぼコスト無し;200 文字のサンプルを返します。
フォールバックチェーンは今や: Anthropic → Gemini → manual。
🌐 P-8 — Help-center i18n パリティ (監査 + テスト強化)
全 docs/help/<lang>.md の構造パリティを監査。8 ロケール全てが既に同じ 14 個の正規 h2 セクションをカバー。テスト強化:
tests/help-ui.test.mjs::every help doc covers the same 14 sectionsは en + ru のみチェックしていました。今や 全 8 ロケール (en、es、pt-BR、ko-KR、ja、ru、zh-CN、zh-TW) を反復し各セクション数をアサート。- 新テスト:
tests/help-ui.test.mjs::every help locale has substantive content— ロケールスタブ防護として、各非 EN ロケールがen.mdのバイト長の少なくとも 30% であることをアサート。コンパクトな翻訳は自然に 40-50% に到達;スタブは一桁 %。
結果: 構造パリティが CI で強制されるように。
🤖 P-9 — Playwright ブラウザ smoke を CI マトリクスに
tests/playwright-smoke.mjs (v1.8.0 で opt-in として追加) が CI ワークフローの一部に。既存の e2e ジョブが既に Playwright + Chromium をインストールしている;1 つの新ステップ (npm run test:e2e:browser) が包括的 node E2E の直後に 5 つのブラウザ smoke テストを実行。
CI 順序: unit (Node 18/20/22 マトリクス) → smoke node E2E → 包括 node E2E → Playwright ブラウザ smoke → 失敗時のスクリーンショットアーティファクトアップロード。
🌍 P-10 — マルチ CLI 互換性
親 career-ops v1.7.0 がマルチ CLI / Open Agent Skill 標準サポートを導入。UI サブプロジェクトは正規 CLAUDE.md を指す薄いシムで同じ慣例に従う:
web-ui/AGENTS.md— Codex / Aider / 汎用 CLI エントリポイント。web-ui/GEMINI.md— Gemini CLI エントリポイント。
両シムは hard rules とクイックリファレンスを再記述しますが、完全なプロジェクトレベル指示は CLAUDE.md に委譲します。これにより非 Claude CLI も Claude Code セッションと同じ方向付けに着地します。デプロイされた UI 自体は runtime で CLI-agnostic を維持。
🧪 テスト
- 284 unit テスト (以前 283): +1 新しい help-locale パリティテスト。
- 5 Playwright ブラウザ smoke テスト — opt-in だけでなく CI の一部に。
- カバレッジは維持。
🔧 触れたファイル
+ server/lib/routes/activity.mjs + server/lib/routes/config.mjs
+ server/lib/routes/health.mjs + server/lib/routes/help.mjs
+ server/lib/routes/jds.mjs + server/lib/routes/llm.mjs
+ server/lib/routes/pipeline.mjs + server/lib/routes/reports.mjs
+ server/lib/routes/tracker.mjs
+ AGENTS.md + GEMINI.md
~ server/index.mjs (762 → 130 LOC, -83%)
~ .github/workflows/ci.yml (Playwright smoke step)
~ tests/help-ui.test.mjs (all-8-locales section parity + content-floor)
~ docs/{ROADMAP,architecture/{OVERVIEW,SERVER}}.md
~ docs/sdd/CONVENTIONS.md
~ CLAUDE.md
~ package.json (1.8.0 → 1.9.0)
📦 新規 REST エンドポイント
| メソッド | パス | 目的 |
|---|---|---|
POST |
/api/evaluate/test-anthropic |
ANTHROPIC_API_KEY の smoke チェック (P-7)。/api/evaluate/test-gemini をミラー。 |
🤖 新規 CLI エントリポイント
| ファイル | CLI | ノート |
|---|---|---|
AGENTS.md |
Codex / Aider / 汎用 | 完全指示は CLAUDE.md を指す。 |
GEMINI.md |
Gemini CLI | セッション開始時に Gemini が自動ロード。 |
[1.8.0] — 2026-05-08
ハードニング、リファクタ、SDD ブートストラップ。3 件の高重大度修正 (A1、A2、A3)、4 件の中重大度 (B1–B4)、6 件のクリーンアップ、親 career-ops v1.7.0 サーフェスの監査、server split-by-concern (P-2 フェーズ 1)、Playwright ブラウザ smoke ハーネス、docs/ と .claude/ 下の完全な SDD 基盤。
🔥 高重大度修正
fix(deep): inline cv/profile/mode files for Anthropic SDK calls (REVIEW-A1)—/api/deepと/api/mode/:slugは以前モデルに「これらのファイルを最初に読む」と指示していましたが Anthropic SDK にはファイルシステムがありません。出力は中身が空でした。新規bundleProjectContext({ modeSlugs })がcv.md、config/profile.yml、modes/_shared.md、mode テンプレートを読み、各 16 KB で切り詰め、プロンプトに<project_context>ブロックを前置。ライブ検証済み:claude-sonnet-4-6から deep-research 呼び出しに対し 26 KB の grounded markdown レスポンス。fix(runner): SIGKILL escalation after SIGTERM grace period (REVIEW-A2)—runNodeScriptとstreamNodeScriptは以前 timeout / client-disconnect でSIGTERMのみを送信していました。syscall (DNS、ブロックされた socket) で stuck になった子プロセスはこれを無視し、SSE 接続が Node の GC が刈り取るまでハング。各パスは 5 秒のウォッチドッグを腕装しSIGKILLにエスカレート。Promise が常に resolve するように。fix(runner): max-runtime cap on streaming endpoints (REVIEW-A3)— 全 SSE スクリプト runner (/api/stream/{scan,liveness,pdf}) がハードな 30 分上限を持つように。期限切れ時:event: error { message: 'maximum runtime exceeded' }を発し、A2 ウォッチドッグ経由で子を kill、レスポンスを終了。
🛡️ 中重大度修正
fix(preview): per-hop redirect validation in /api/pipeline/preview (REVIEW-B1)—redirect: 'follow'から手動 redirect-walking に切り替え。各LocationヘッダはisValidJobUrlで再検証;3 ホップで上限。敵対的なボードが loopback / プライベート IP /file://にバウンスできなくなりました。拒否パスを 4 つの新規テストでカバー。refactor(keys): hasGeminiKey helper unifies LLM-key checks (REVIEW-B2)— ルートハンドラの直接process.env.GEMINI_API_KEY読み取りをlib/anthropic.mjsのhasGeminiKey()に置換。hasAnthropicKey()形状をミラーし一貫性とモック容易性を確保。feat(scanners): thread AbortSignal through hh.ru, Habr, Greenhouse, Ashby, Lever (REVIEW-B3)— SSE クライアントが scan 中に切断した際、進行中の HTTP fetch が全クエリを完了まで走らせてイベントを drop する代わりに abort されるように。runRuScanとrunEnScanがopts.signalを受け取り、/api/stream/scan-{ru,en}の SSE ハンドラがAbortControllerを作成しres.closeで abort。test(anthropic): log-guard test prevents future API-key leaks via console (REVIEW-B4)—runAnthropicのハッピー + エラーパス中の全console.{log,info,warn,error,debug}呼び出しを捕捉、出力ゼロと canary key 文字列が決して現れないことをアサート。将来のconsole.log(opts)回帰に対する多層防御。
🧹 低重大度ポリッシュ
fix(parsers): defense-in-depth URL gate inside addPipelineUrl (REVIEW-C4)— ルートレベルisValidJobUrlを補完するパーサレベルでの非 http(s) 値拒否。より厳格なルールを望む呼び出し元向けにオプションのopts.validate。docs(readme): badge "tests-88 passed" → "tests-277 passed" (REVIEW-C3)— 桁が 1 つズレていました。test(i18n): missing-keys diff grouped by locale (REVIEW-C6)—tests/i18n-coverage.test.mjsがギャップを見つけた際、出力が混在行ではなく[ru] (3): foo, bar, bazに。docs(review): C1 closed as resolved-on-inspection— サニタイザ正規表現は既に\x00-\x08の hex 形式;review エントリはツールレンダリングのアーティファクトでした。
🏗️ P-2 フェーズ 1 — server split-by-concern
server/index.mjs は 1230 LOC で 800 行天井を大きく超えていました。挙動変更なしで focused モジュールに分割。283 unit テスト全てが各ステップで緑のまま。
server/lib/security.mjs—isValidJobUrl、stripDangerousMarkdown、sanitizeJobDescription、isPubliclyExposed。外部コンシューマの後方互換のためindex.mjsから re-export。server/lib/prompts.mjs—bundleProjectContext、buildEvaluationPrompt、buildDeepPrompt、buildModePrompt、buildApplyChecklist。server/lib/store.mjs—safeReadApps、safeReadPipeline、safeListReports、checkProfileCustomized、ensureRussianPortalsDefaults。server/lib/routes/scan.mjs—/api/stream/scan-{ru,en}、/api/scan-ru/config、/api/scan-results用のregisterScanRoutes(app)。server/lib/routes/runners.mjs— バッファ/api/run/*テーブル、ストリーミング/api/stream/{scan,liveness,pdf}、生成 PDF list/download 用のregisterRunnerRoutes(app)。server/lib/routes/content.mjs— CV / Profile / Portals / Modes 用のregisterContentRoutes(app)。
index.mjs は今や 762 LOC (-38%、800 上限以下)。フェーズ 2 では tracker、pipeline、reports、jds、llm (evaluate/deep/mode)、health をルートモジュールに抽出。オーケストレータは <500 LOC を目標。
🔍 親 career-ops v1.7.0 監査
ユーザーが親プロジェクトを v1.7.0 に更新。消費される全サーフェスを監査 — UI は完全に互換。注目すべき発見は docs/architecture/DATA-FLOWS.md に文書化:
- Modes カタログが 7 → 19 ファイルに成長。UI の
MODE_ALLOWLISTは意図的に 7 つだけを surface (他は Claude-Code 専用)。意図的な狭いスコープを説明するコメントを追加。 portals.ymlスキーマ確認:tracked_companies(96 エントリ、87 有効、71 が API あり)。EN scanner はこれを正しく読む;レガシーcompaniesキーも引き続きサポート。- 今日消費されない新規親サーフェス:
dashboard/(Go プログラム)、update-system.mjs、generate-latex.mjs、analyze-patterns.mjs、liveness-core.mjs、followup-cadence.mjs、test-all.mjs、ローカライズされた mode サブディレクトリ (de/fr/ja/pt/ru)。 - ライブの
/api/dashboard、/api/health、/api/modes、/api/portals、/api/profile、/api/cv、/api/jds、/api/reports、/api/tracker、/api/pipeline、/api/evaluate、/api/deep、/api/stream/scan-en全てが緑検証済み。
🤖 SDD / GSD ブートストラップ
career-ops-ui は GSD パイプライン (superpowers@claude-plugins-official の gsd-* skill) と整合する完全な Spec-Driven Development 基盤を持つように。
CLAUDE.md(ルート) — プロジェクトレベルのエージェントシステムプロンプト: スタック、GSD パイプライン、hard rules (親契約、セキュリティエンベロープ、--no-verify禁止)、慣例、親プロジェクト境界。.aiignore— AI エージェント用除外リスト: ベンダー、バイナリ、親ユーザーデータ、.planning/、.env、ロケール重複。.claude/agents/— 3 つのプロジェクト固有サブエージェント定義:web-ui-route-reviewer.md— 新ルートを SSRF、CSP、サニタイザ、親書き込み契約、慣例、テストに対してゲート。spa-view-reviewer.md— CSP-safe DOM、i18n、ルーター登録、アクセシビリティ。test-isolation-reviewer.md— テストが CI 分離されている (親プロジェクトを前提としない、ライブネットワークなし、ポート衝突なし) ことを検証。
.claude/commands/— slash-command スタブ:/sdd-status、/codebase-tour。docs/ツリー — 全て英語:PROJECT.md— 何を/なぜ/誰のために、スコープ、制約、成功基準。ROADMAP.md— 現在のマイルストーン + 完了履歴 + バックログ。sdd/SDD-GUIDE.md—gsd-*skill にマッピングされた discuss → spec → plan → execute → verify → review パイプライン。sdd/CONVENTIONS.md— モジュールシステム、命名、ルート、サニタイザ、クライアントパターン、i18n、エラー、ロギング、テスト、コミット、ブランチ、CSS。architecture/OVERVIEW.md— トップレベル図、レイヤ、ブートシーケンス、不変条件、“where to look first when…” チートシート。architecture/SERVER.md—server/lib/*.mjsのファイル毎マップ (P-2 分割で更新)。architecture/FRONTEND.md— SPA 構造、view インベントリ、グローバル、“how to add a view”。architecture/API.md— 全/api/*ルートの完全インベントリ。architecture/DATA-FLOWS.md— 親プロジェクトの全 read/write、明示的ユーザーアクション契約付き。reviews/REVIEW-2026-05-07.md— この changelog の修正を生んだ静的レビュー。
🔒 セキュリティ & リポジトリ衛生
chore(.gitignore): comprehensive defense-in-depth patterns— env バリアント、IDE フォルダ、GSD スクラッチ (.planning/)、ユーザー毎エージェント設定 (.claude/settings.local.json、.claude/cache/、.claude/state/、.claude/memory/)、Playwright アーティファクト (playwright-report/、test-results/、.playwright/、trace.zip)、heap/CPU プロファイル、未出荷ツールのロックファイル、拡張された macOS Finder ノイズ、汎用シークレットパターン (secrets.json、credentials.json、*.pem、*.key) をカバー。
🧪 テスト
- 283 unit テスト (以前 277): +11 新規 (B1 redirect 拒否で 4、
hasGeminiKeyで 1、runAnthropiclog-guard で 1)。 - 5 Playwright ブラウザ smoke テスト (新規、
npm run test:e2e:browserで opt-in): ダッシュボードレンダリング + バージョンフッタ、ダッシュボード → scan → pipeline → cv ナビゲーション、言語切替永続化、404 view、health ページレンダリング。Playwright は親のnode_modules経由で解決 — 新規依存なし。 - カバレッジは約 93% 行 / 約 83% 分岐を維持。
📝 新規 / 更新された package.json スクリプト
| スクリプト | 目的 |
|---|---|
npm run test:e2e:browser |
in-process サーバーに対し Playwright smoke ハーネスを実行 (5 テスト)。 |
🔧 触れたファイル
+ CLAUDE.md + .aiignore
+ docs/PROJECT.md + docs/ROADMAP.md
+ docs/sdd/SDD-GUIDE.md + docs/sdd/CONVENTIONS.md
+ docs/architecture/OVERVIEW.md + docs/architecture/SERVER.md
+ docs/architecture/FRONTEND.md + docs/architecture/API.md
+ docs/architecture/DATA-FLOWS.md + docs/reviews/REVIEW-2026-05-07.md
+ .claude/agents/web-ui-route-reviewer.md + .claude/agents/spa-view-reviewer.md
+ .claude/agents/test-isolation-reviewer.md
+ .claude/commands/sdd-status.md + .claude/commands/codebase-tour.md
+ server/lib/security.mjs + server/lib/prompts.mjs
+ server/lib/store.mjs
+ server/lib/routes/scan.mjs + server/lib/routes/runners.mjs
+ server/lib/routes/content.mjs
+ tests/playwright-smoke.mjs
~ .gitignore ~ README.md (badge fix)
~ package.json (1.7.2 → 1.8.0)
~ server/index.mjs (1230 → 762 LOC)
~ server/lib/runner.mjs (SIGKILL escalation, max-runtime cap)
~ server/lib/anthropic.mjs (hasGeminiKey)
~ server/lib/parsers.mjs (URL gate in addPipelineUrl)
~ server/lib/ru-scanner.mjs ~ server/lib/en-scanner.mjs
~ server/lib/sources/{hh,habr,greenhouse,ashby,lever}.mjs (signal threading)
~ tests/anthropic.test.mjs ~ tests/i18n-coverage.test.mjs
~ tests/pipeline-preview.test.mjs
[1.7.2] — 2026-05-04
Help center、UI 内 App settings、モバイルサイドバー、単一 Scan ボタン、全 prompt-builder の「結果を表示」ショートカット。
✨ 新機能
feat(help): in-app user guide(/#/help) — 新規サイドバーエントリからアクセス可能な長文 Markdown ドキュメント。各ページをステップバイステップでカバー: クイックスタート、CV エディタ、Profile、Scan フィルタ、Pipeline preview、Evaluate、Deep research、Apply、Tracker、Reports、7 mode 全て、Activity log、Health、セットアップヒント。<h2>見出しから自動構築される sticky 目次、同期 DOM 構築 (race なし)。サポートする 8 ロケール全てにローカライズ。feat(config): in-UI App settings page(/#/config) — ブラウザからANTHROPIC_API_KEY、ANTHROPIC_MODEL、GEMINI_API_KEY、GEMINI_MODEL、HH_USER_AGENT、PORT、HOSTを編集。親プロジェクト の.envファイルに書き込むため career-ops Node スクリプトと web-ui の dotenv ローダの両方が同じソースを取得します。シークレットキーは読み取り時にマスク (先頭/末尾 4 文字)。Model フィールドはキュレートされたリストの dropdown (claude-sonnet-4-6 / claude-opus-4-7 / claude-haiku-4-5 / gemini-2.0-flash 等)。空値はキーを削除。値は実行中の process.env に即座に適用 — ほとんどの設定で再起動不要。feat(modes): "⚡ Show result" button alongside "Copy prompt"— manual モードでプロンプトが生成された時、ユーザーは LLM 結果を得るために入力を再タイプする必要がなくなりました。新ボタンは同じフォームをrun: trueで再送信し、キーが構成されていない場合は明確なトースト (Set ANTHROPIC_API_KEY or GEMINI_API_KEY in .env first) にフォールスルー。/#/deep、/#/project、/#/training、/#/followup、/#/batch、/#/contacto、/#/interview-prep、/#/patternsで動作。
🐛 UX + UI 修正
fix(scan): single Scan button replaces three (Scan all + EN + RU)— 圧倒的な選択肢、99% のケースで同一デフォルト。統合された🌐 Scanボタンが有効な全ソースを実行。Help ドキュメントを 8 ロケールで更新。fix(ui): mobile sidebar drawer— ビューポート <900px がトップバーにハンバーガーボタン (☰) を取得;body.sidebar-openがサイドバーをスライドインさせる CSS transform をトグル。Backdrop dim + クリックでクローズ。アンカークリック + hashchange で自動クローズし、ユーザーは drawer が畳まれた状態で新ページに着地。大きなビューポートは影響なし。fix(server): footer version reflects web-ui, not the parent VERSION—/api/healthが web-ui 自身のpackage.jsonを読むように。フッタは親のバージョンファイルから古い1.6.0を漏らさなくなりました。親の VERSION は引き続きparentVersionとして別途 surface。
📦 新規 REST エンドポイント
| メソッド | パス | 目的 |
|---|---|---|
GET |
/api/help/:lang |
リクエストされたロケールの Markdown ユーザーガイドを返却、en.md にフォールバック。Path-traversal セーフ。 |
GET |
/api/config |
全既知 env キーの現在値を返却;シークレットマスク。 |
POST |
/api/config |
指定キーを親プロジェクトの .env に書き込み、各値を検証、process.env にライブ適用。 |
🌐 i18n
nav.help、nav.config、help.*、config.*、deep.showResult、deep.needKey、scan.btnRunにわたる 30+ 新キー。全 8 ロケール充足。
🧪 テスト
tests/help.test.mjs(12 ケース) — 全サポートロケールが実質的 markdown を返却、EN が各ページ slug をスポットチェック、未知 lang → EN フォールバック、path-traversal サニタイズ、各ロケールがcv.md/profile.yml/.envを参照。tests/help-ui.test.mjs(9 ケース) — view ファイル登録、サイドバーエントリ、全ロケールの i18n キー存在、各ロケールの docs ファイル存在、EN/RU help が 14 個の正規セクション、全 #/foo ルートカバレッジ、deep + mode-page の Show-result 配線。tests/env-config.test.mjs(18 ケース) —parseEnv、maskSecret、validateConfig、updateEnvFile(ブートストラップ、コメントを保持しつつ in-place 書き直し、空値削除、必要時の quote) の純関数テスト。tests/config-endpoint.test.mjs(8 ケース) — GET がシークレットをマスク / env パス返却;POST が親 .env に書き込み;live process.env 適用;空値で unset;未知キー + 不正形式 Anthropic キーを 400 で拒否。
📊 統計
- テスト: 233 → 277 (+44 新規テストファイル 4 つにわたって)。
- E2E: 20 smoke + 23 包括 = 43 Playwright ステップ、全緑。
- カバレッジ: 93.5% 行 / 82.6% 分岐 / 93.7% 関数 (不変 — 新コードは完全にテスト済み)。
[1.7.1] — 2026-05-04
post-v1.7.0 作業を積み上げたパッチリリース: pipeline preview ペイン、Anthropic API 統合、スクロール可能なサイドバー、dotenv ローダ、動的 Active-companies リスト、CI ワークフローハードニング。
✨ Pipeline preview ペイン
/#/pipeline刷新 — 左リスト + 右プレビューペイン。任意の URL をクリックしてサーバー側プロキシのスナップショットを fetch (GET /api/pipeline/previewがスクリプト/スタイル/タグを除去、8 KB で上限、isValidJobUrlで検証)。ライブフィルタ入力、“In queue” カウンタ、⚡ “Evaluate first” ヘッダボタン。各行のインライン ▶/✕ に加えてプレビューペインに完全な Evaluate / Open in tab / Delete。data-url+.pipeline-row+.pipeline-row-deleteクラスによる安定したテストセレクタ。tests/pipeline-preview.test.mjsの 8 新規テスト (モックされた fetch、上流バインド不要)。
✨ Anthropic API 統合 — どこでも「Run live」
server/lib/anthropic.mjs— Anthropic Messages API 用ゼロ依存クライアント (claude-sonnet-4-6 デフォルト、ANTHROPIC_MODELで上書き)。ANTHROPIC_API_KEYが設定されている場合、全 mode ページ (/#/deep、/#/project、/#/training、/#/batch、/#/contacto、/#/interview-prep、/#/patterns) が “⚡ Run live (Anthropic)” ボタンを プライマリ アクションとしてレンダリング — クリックでプロンプトを実行し Markdown をブラウザに描画 (Claude Code への引き渡しの代わり)。Gemini は Gemini キーのみ設定された場合のフォールバックとして残る。Manual モードはキーが全くなくても動作。tests/anthropic.test.mjsの 8 新規テスト。
🐛 CI / pipeline 修正
fix(api): tighten pipeline URL validator(FIX-M7) — loopback ホスト名、長さ <10 または >2000、URL 内のホワイトスペースも拒否するように。fix(server): actually load .env so HH_USER_AGENT / GEMINI_API_KEY hints work—server/lib/dotenv.mjs(35 行のゼロ依存ローダ) を追加しserver/index.mjsの先頭に配線。スキャナコード内の runtime ヒントが遂に機能するように。6 新規テスト。fix(ui): scrollable sidebar— 6 グループ 18 ナビ項目が短いビューポートでオーバーフロー。.sidebarが薄いカスタムスクロールバー付きのoverflow-y: autoを持つように。fix(ui): make HH_USER_AGENT banner dismissible— その後、過剰だと判断し/scanから完全削除。Health ページのチェックは引き続き surface。fix(scan): Active companies list is now collapsible + filterable + grouped— 87 タグのフラット表示は圧倒的でした。今や “▸ Active companies 87/71” トグルが順序付きリスト (✓ API-backed が先、○ websearch が次) + 検索フィルタを展開。fix(test): isolate api.test.mjs + en-scanner.test.mjs from parent project— 両方が tmp プロジェクトルートを起動するため、web-ui と並んで親が checked out されていなくても CI が動作。fix(workflow): publish-package version-match only on release events— main からのworkflow_dispatchで tag/version チェックが失敗しなくなった。fix(e2e): stable selector for pipeline row delete— アンカーラッパーを復元しdata-url属性を追加して e2e スイートが selector-stable に。
📦 新規 REST エンドポイント
| メソッド | パス | 目的 |
|---|---|---|
GET |
/api/pipeline/preview?url=… |
サーバー側プロキシ: URL の可視テキストスナップショットを返却 (スクリプト/スタイル除去、8 KB 上限)、isValidJobUrl でゲート。 |
📊 このバッチ後の統計
- テスト: 225 → 233 (v1.7.0 の上に +8)。
- テストファイル: 25 → 26。
- E2E: 20 + 23 = 43 Playwright ステップ、全緑。
[1.7.0] — 2026-05-03
QA r5 駆動の 35 コミットによるハードニング + UX + 機能完成パス。3 層のセキュリティ (XSS サニタイズ、CSP、入力検証) が着地、欠落していた CRUD エンドポイントが全て埋まり、親プロジェクトブートストラップが完全自動化、UI が 9 つの新ページ を獲得 — Activity、再設計された Deep Research、サイドバーグループ化された 7 mode (project / training / followup / batch / outreach / interview-prep / patterns) で親 modes/ の 100% カバー。Pipeline がサーバー側 preview ペインを獲得。Anthropic API 統合により全 mode で “Run live” がワンクリックアクションに。テストカバレッジは 73 → 225、25 テストファイル、加えて 23 包括 Playwright e2e ステップ。GitHub Actions が CI / AI review / Release / Publish-Package ワークフローを出荷。
🔒 セキュリティ
fix(cv): sanitize CV markdown to block stored XSS in preview(FIX-C10) —PUT /api/cvが<script>、<iframe>、<object>、<embed>、<style>、<form>、<svg>、on*=イベントハンドラ、javascript:/vbscript:/data:text/htmlURI をcv.md書き込み前に除去。ボディは 1 MB で上限 (オーバーフロー時 413)。クライアント側UI.md()を書き直し、任意の markdown 変換が走る前に全バイトをエスケープ。生 HTML がinnerHTMLに届くことはなくなりました。リンクhref属性は安全スキームの allowlist (http/https/mailto/tel/相対 +data:imageのみ) で検証。strip ヘルパーと HTTP ラウンドトリップにわたる 17 新規テスト。fix(server): add CSP and baseline security headers(FIX-L2) — 全レスポンスがX-Content-Type-Options: nosniff、X-Frame-Options: DENY、Referrer-Policy: same-originを運ぶように。サーバーが loopback (HOST≠127.0.0.1/::1/localhost) を超えてバインドする場合、厳格なContent-Security-Policyが上に重ねられる:default-src 'self'、script-src 'self'(unsafe-inlineなし)、Google Fonts allowlist、connect-src 'self'で XSS 漏出をブロック。index.htmlとrouter.jsのインラインonclickハンドラはaddEventListenerに移動し、厳格 CSP を維持。5 つの異なるHOST値で CSP をゲートする 8 新規テスト。fix(api): tighten pipeline URL validator(FIX-M7) —POST /api/pipelineは以前"not-a-url"を受け付け永続化していました。今やisValidJobUrl()が bare 文字列、<10 または >2000 文字、ホワイトスペースを含む URL、非http(s)スキーム、loopback ホスト名 (localhost/127.0.0.1/::1) を拒否。FIX-M3 + FIX-M6 (無効で 400 を返却、成功時にdedupedフラグ) を含む。fix(server): actually load .env so HH_USER_AGENT / GEMINI_API_KEY hints work— 以前 runtime は「.envに HH_USER_AGENT を設定」とユーザーに伝えていましたが、サーバーはそのファイルを読まず、指示に従っても何も起きませんでした。35 行のゼロ依存 dotenv ローダ (server/lib/dotenv.mjs) をserver/index.mjsの先頭に配線。コマンドライン上で設定された process-env 値が引き続き勝つため既存 CI 上書きが影に隠れない。親の.env.exampleに実際の Chrome User-Agent 例を含むHH_USER_AGENTブロックを文書化。6 新規テスト。fix(api): sanitize JD before prompt assembly(FIX-M5) —POST /api/evaluateが Gemini 呼び出し前またはプロンプトのエコーバック前に ANSI エスケープ、制御バイト、インライン<script>タグを除去しホワイトスペースをトリム。50 KB 長上限。50 文字最小は サニタイズ後 テキストに対して走るため、長く見えるがほとんどがエスケープから成るプロンプトインジェクション試行は 400 で fail-fast。fix(health): mask Node version + project root when HOST!=loopback(FIX-M1) —/api/healthが LAN 公開デプロイでホストをフィンガープリントしなくなりました。Loopback レスポンスはローカル診断用に値を保持。
✨ 新機能
feat: 7 new sidebar modes + grouped sidebar(FIX-C8) — UI ギャップなしで親のmodes/ディレクトリの 100% をカバー。新ルート:#/project(ポートフォリオプロジェクトアドバイザ)、#/training(コース / 資格評価)、#/followup(アプリケーション毎ケイデンス)、#/batch(並列 URL プロセッサ)、#/contacto(LinkedIn outreach ドラフタ)、#/interview-prep(ステージ固有準備)、#/patterns(拒否パターンアナライザ)。7 つ全てが単一の config 駆動 view factory (public/js/views/mode-page.js) と単一の汎用エンドポイントPOST /api/mode/:slugを共有 — 将来の新規 mode 追加は 1 つの config 行 + 1 つの i18n ブロック。サイドバーを 6 グループに再編成: Sourcing / Decision / Application / Networking / Analytics / Setup。ナビ項目合計 18。tests/modes-endpoints.test.mjsの 12 新規テスト。fix: bootstrap parent deps + russian_portals defaults(FIX-C4 + C9 + C12 + H2) —bin/start.shが fresh clone で親のnode_modules(js-yaml、playwright、jsdom) とnpx playwright install chromiumをインストールするように。/api/stream/scan、/pdf、/livenessが箱から end-to-end で動作。createApp()がブート毎にportals.ymlをプローブ —russian_portals:ブロックが欠落していればコメント付き文書化デフォルトを追記。冪等: 2 回目のブートは no-op。3 新規テスト。fix: disable 9 dead portal slugs in template + health-check script(FIX-C3) —templates/portals.example.ymlが Ada / Factorial / Tinybird / Weights & Biases / Travelperk / Clarity AI / Forto / Vinted / Runway にenabled: false(各エントリに理由インラインコメント) でフラグを付けて出荷。新規インストールは 96 ではなく 87 社の生存スキャン。新規web-ui/scripts/portals-health-check.mjsが全有効careers_urlを HEAD-probe し DEAD エントリと提案パッチリストをレポート (--jsonで JSON 出力)。3 新規テスト。feat(activity): user-action log + Activity sidebar page— 全状態変更 API リクエストがdata/activity.jsonl(タイムスタンプ、アクション動詞、ターゲット、成功フラグ、オプション詳細) にキャプチャ。アクションプレフィックスチップフィルタ (pipeline / cv / jd / evaluate / scan / stream / script)、アクション ✓/✗ バッジ、リフレッシュボタン付きの新規サイドバーエントリ Activity。5 MB で自動ローテーション。10 新規テストが middleware、read フィルタ、corrupt-line 耐性、GET /api/activity自体の再帰ガードをカバー。feat(deep): view Deep Research in browser + saved-results archive— Deep Research ページが (a){ run: true }とGEMINI_API_KEYが設定されている時にプロンプトを Gemini でライブ実行し出力をinterview-prep/{slug}.mdに永続化、(b) 全保存 deep-research ファイルを相対タイムスタンプ付きクリック可能カードとして列挙、(c) 結果を Markdown としてレンダリングし結果毎に 📋 Copy / ⬇ Download .md / ↗ Open in tab アクションを提供。新規 REST サーフェス:GET /api/interview-prep、GET /api/interview-prep/:name、DELETE /api/interview-prep/:name。7 新規テスト。feat(cv): generate + download PDF in browser, with PDF archive— CV ページ上の新規 📄 Generate PDF ボタンがモーダルコンソールで/api/stream/pdfをストリーム。ERR_MODULE_NOT_FOUND/playwrightエラー時、コピペ可能なブートストラップコマンドを surface。新規 “Generated PDFs” セクションが各成功実行後に自動ロードし、全output/*.pdfを ↗ Open と ⬇ Download ボタン付きで列挙。新規 REST サーフェス:GET /api/output/pdfs、GET /api/output/pdfs/:name。6 新規テスト。feat(api): POST /api/tracker — append rows from the UI(FIX-H8) — ブラウザからdata/applications.mdに正規行を追記。company + role を検証、status をtemplates/states.ymlに対して正規化、自動インクリメントのゼロ詰め#、company+role での dedup (大文字小文字無視)、markdown テーブルが壊れないよう notes のパイプをエスケープ。ファイルが空の時テーブルをブートストラップ。6 新規テスト。feat(api): DELETE /api/jds/:name(FIX-H4) — シェルアウトなしで保存済み JD を削除。Path-traversal 文字はファイルシステム touch 前に除去;パラメータは.txtで終わる必要あり。../../etc/passwd拒否を含む 5 新規テスト。feat(api): POST /api/evaluate/test-gemini(FIX-H7) — ユーザーが実評価を待たずに API キーが機能することを確認できるよう、50 文字のダミー JD をgemini-eval.mjs経由で実行する smoke テストエンドポイント。{ ok, code, sampleLength, sample }を返却。
🐛 バグ修正
fix(router): catch-all 404 view + i18n coverage guard(FIX-C7) — 未知ハッシュルートは以前サイレントにダッシュボードにフォールバックし、タイポや壊れたブックマークを覆い隠していました。今や#/totally-random-xyzが悪いパスを引用して返す専用 404 ページをレンダリングしダッシュボードへリンク。404 view はユーザールートと衝突しないよう router IIFE 内に登録。新規tests/i18n-coverage.test.mjsがvm.Context内で stubwindow付きでi18n.jsを走らせ、プライベートDICTを expose し、173+ キー × 8 ロケールの各キーが populate されかつ非空であることをアサート。4 新規ルーターテスト。fix(router): alias #/profile → settings(FIX-C2) — 内部ルート名はsettings(nav.settingsが “Profile” をレンダリング) ですが外部リンクと筋肉記憶は#/profileに行きます。今や両アドレスが同じ view に到達し、サイドバーのナビ項目はどちらでも光ります。2 新規テスト。fix(health): unify Health/Doctor + flag template profiles(FIX-C6 + FIX-H6) — Health と Doctor は 2 つの異なる真実の源でした。今や/api/healthが Doctor がレポートする全て (parent-deps、Playwright、dirs、profile-customized、HH_USER_AGENT) を expose。Profile customizedチェックがプレースホルダ名 (Jane Smith、Alex Doe、John Doe、Your Name、Test User) と明示的な YAML パースエラーを検出。4 新規テスト。fix(scan): warn on query↔negative collisions in RU config(FIX-H3) —portals.ymlがtitle_filter.negativeに"PHP"を含み Senior PHP をターゲットとするクエリを持つ場合、全マッチがフィルタされユーザーは結果ゼロを見ます。loadConfig()がwarnings配列を計算し;runRuScan()が scan 開始前に各 warning を SSE stderr 行として発します。2 新規テストが出荷デフォルトが箱から PHP-friendly のままであることを検証。fix(scan): warn when HH_USER_AGENT is unset(FIX-H1) —/scanページが/api/healthをプローブし、HH_USER_AGENTが空の時にアクション行上に黄色の警告カードを表示。RU scan をクリックする 前 に hh.ru の 403 を知らせます。fix(api): warn when POST /api/jds slug had unsafe chars stripped(FIX-M2) — 危険文字を除去する slug 正規化がwarningフィールドを返却するように;純粋な大文字小文字/ホワイトスペースクリーンアップはサイレントを維持。サニタイズ後の空結果は 400。fix(ui): clear global search on route change + button spinners(FIX-M4 + FIX-L1) — グローバル検索 input がhashchangeでクリア (アクティブタイピングのガード付き)。新規UI.withSpinner(button, fn)ヘルパーがローディング状態、ARIA、二重クリック防止を全非同期ボタンクリックに配線。Doctor / Verify / sync-check / Save CV / Normalize / Dedup / Merge ボタンで既に採用。fix(ui): make sidebar scrollable so 18 nav items always reach the footer— FIX-C8 のグループ化サイドバーが短いビューポートでオーバーフロー;ボトム項目 (Activity / Health) がクリップされていました。.sidebarが薄いカスタムスクロールバー付き (WebKit + Firefox) のoverflow-y: autoを持つように。フッタは既存のmargin-top: autoで pin されたまま。fix(ui): empty modal-title placeholder(FIX-H9) —index.htmlのハードコード英語"Title"文字列が消え、モーダルオープン中に見えていた短いレースウィンドウをクローズ。
🌐 i18n
- 173+ 翻訳キー × 8 サポートロケール (
en、es、pt-BR、ko、ja、ru、zh-CN、zh-TW)。404 ページ、activity log、deep research、PDF フロー、セキュリティ警告、tracker mutation、apply rename にわたる新規キーを全ロケールに追加。カバレッジはtests/i18n-coverage.test.mjsで強制 — 全キーが全サポートロケールで非空値を持つ必要があるか CI が失敗。
⚙️ DevOps
- テスト数: 73 → 201 (23 テストファイルにわたって +128 テスト)。残る単一失敗テスト (
runEnScan: dry-run end-to-end across multiple sources) は Greenhouse/Ashby/Lever ライブ API レスポンスに依存する既存 flake。 - 包括 Playwright e2e (
tests/e2e-comprehensive.mjs、23 ステップ): 完全なユーザー旅程を歩く — CV 保存 → preview → PDF 生成 → 全 7 つの新 mode → tracker フィルタ → activity log → 404 → モーダル ESC → サイドバースクロール → Ctrl-K フォーカス → 検索クリア → profile alias → 言語永続化。 - GitHub Actions (
.github/workflows/):ci.yml— Node 18/20/22 マトリクスでの unit + integration テスト、i18n カバレッジゲート (全キー × 8 ロケールが非空である必要)、全 Playwright e2e を PR 毎に。ai-review.yml— 全 PR で Claude Code AI レビュー。メンテナがマージ権限を保持;Claude は提案のみ。skip-ai-reviewラベルでスキップ。release.yml—v*.*.*タグが push されると GitHub Release を自動公開;リリースノートはCHANGELOG.mdからスライスされるため、全 8 言語バリアントが正規ソースのまま。
- CSP フレンドリ UI:
index.htmlとrouter.jsから全インラインonclickハンドラを削除。厳格script-src 'self'ポリシーが機能を壊さずに強制可能に。
📦 新規 REST エンドポイント
| メソッド | パス | 目的 |
|---|---|---|
GET |
/api/activity |
ユーザーアクションイベント一覧、新しい順 |
GET |
/api/interview-prep |
保存済み Deep Research ファイル一覧 |
GET |
/api/interview-prep/:name |
単一の Deep Research ファイル読み取り |
DELETE |
/api/interview-prep/:name |
Deep Research ファイル削除 |
GET |
/api/output/pdfs |
生成済み PDF 一覧 |
GET |
/api/output/pdfs/:name |
PDF を添付としてストリーム |
POST |
/api/tracker |
applications.md に行を追記 |
DELETE |
/api/jds/:name |
保存済み JD を削除 |
POST |
/api/evaluate/test-gemini |
Gemini API キーを smoke テスト |
POST |
/api/mode/:slug |
7 つの新 mode (project / training / followup / batch / contacto / interview-prep / patterns) 用の汎用プロンプトビルダー |
[1.6.0] — 2026-05-02
web UI の初回公開リリース。このベースライン時点での機能インベントリは README.md を参照してください。