変更履歴

GitHub

すべての注目すべき変更をリリースごとに記録しています。同じファイルがリポジトリに収録されており、19言語すべてに翻訳されています。

[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-matter About 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.mjs section-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-local 169.254/16 (AWS IMDS を含む)、0.0.0.0、CGNAT 100.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 を受け取り埋め込むように。SPA API.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-2 resolveLocale 優先順位 + 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 / .txt passthrough、空ボディ 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.mjs ALIAS 正規表現を反転し新しい 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/config GET/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/tracker GET + 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、runAnthropic log-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/html URI を 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 内で stub window 付きで 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 を参照してください。


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