変更履歴
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-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 を参照してください。
career-ops-ui v1.267.1 · page 10/10 · mirror of the project changelog for cvstart.org