変更履歴
GitHubすべての注目すべき変更をリリースごとに記録しています。同じファイルがリポジトリに収録されており、19言語すべてに翻訳されています。
[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/sdd/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 にこのエントリを反映。
career-ops-ui v1.267.1 · page 9/10 · mirror of the project changelog for cvstart.org