背景
Issue 棚卸しの「一部解決」A群(engine 済→統合)・C群(局所 UI 修正)を並列実装し、
6 件を PR 化した(issue-backlog-audit-2026-10-12より)。全 PR とも CI green・各
issue に self-assign・REVIEW_REQUIRED で BLOCKED。人間レビュー前の品質確認として、
6 本を 6 並列 subagent で correctness 中心に code-review した(2026-10-12)。
対象 PR(すべて codex/issue-* → main):
| PR | 対応 Issue | 概要 | 領域 |
|---|---|---|---|
| #982 | #143 | クラスタ品質評価ハーネスの再現可能化(--skip-llm・決定論サンプル同梱) | experiments(隔離) |
| #983 | #52 | 散布図のクラスタ選択を連動文章・一覧へ反映 | public-viewer |
| #984 | #450 | 埋め込みモデルをチャットと独立に選択 | admin + api |
| #985 | #537 | OpenRouter 無料モデル向け structured outputs fallback | analysis-core(隔離) |
| #986 | #306 | 散布図の操作ヒント+濃い意見グループの帰属可視化 | public-viewer |
| #987 | #176 | 作成 UI から LLM 直接グルーピングを選択 | admin + api |
レビュー結果(verdict)
| PR | 判定 | 要点 |
|---|---|---|
| #982 | READY-WITH-NITS | --skip-llm の end-to-end を実行で確認、決定論再現も実証。shell 補間の非 quote(空白パスで破綻)等 polish のみ |
| #983 | READY-WITH-NITS | 件数・機構は正、回帰なし。再クリック no-op/mode 切替で stale selection 自動スクロール等 minor UX |
| #984 | READY-WITH-NITS | kind 後方互換・指定時のみ注入・local/Azure 無効化・duplicate 継承すべて PASS。submit gate が embeddingModelError 未参照(stale 値がジョブ起動後に RuntimeError)が最重 minor |
| #985 | READY-WITH-NITS | fallback chain 正。BadRequestError 一律誤分類は follow-up。temperature=0 の reasoning-guard 欠落は既存バグで別 issue |
| #986 | READY-WITH-NITS | parent-chain の cycle guard 正・凡例色は plot と同一 Map 参照・grey-out パリティ証明済み。jest.config.js の lucide-react パス直書きが依存 bump でsuite 全崩れうる nit が最重 |
| #987 | BLOCKER-FOUND → 追修正済み | 作成+初回実行は OK だが、編集→再集計で crash(下記)。commit 8a09108e で修正し PR ブランチへ push |
5/6 は nits のみで merge 可。blocker は #987 のみ。approve / merge は人間判断のため、 レビューでは何も approve / merge / コメント投稿していない。
#987 の BLOCKER と追修正(commit 8a09108e)
症状
analysis_mode=llm_grouping のレポートを作成後、admin の設定編集(タイトル/問い変更)
→ 再集計で TypeError: 'NoneType' object is not iterable により集計が error status。
hierarchical レポートは無影響。
原因(なぜ既存の prune では防げなかったか)
PR #987 は ReportConfig に hierarchical 専用・llm_grouping 専用セクションを
両方 known field として持たせ、prune_config_for_mode(commit 73756f21)で
mode 非該当セクションを落とす設計を入れた。しかし prune は 「非該当 mode の
セクション」だけを削除し、active mode 自身の None/空セクションを残していた。
- 作成フローの
_build_configは llm_grouping セクションを省略して analysis-core の 既定に委ねる → 初回 config file にはllm_groupingキーが無く、初回実行は validate OK。 - 編集で
ConfigRepository.update_jsonがread_from_json().model_dump()を通すと、ReportConfig.llm_groupingの既定Noneが顕在化してllm_grouping: nullを書き戻す。 - prune(mode=llm_grouping)は hierarchical 専用セクションのみ落とし
llm_grouping: nullを残す。 - analysis-core の
core/orchestration.py:273for key in config.get(step_spec["step"], {})は、llm_groupingが step 名に含まれ値が present-but-Noneのため、config.get("llm_grouping", {})が{}ではなくNoneを返し →for key in Noneで TypeError。
修正
prune で active mode 自身のセクションも値が None/空なら削除し、analysis-core の
既定に委ねる挙動へ戻す(値入りセクションは温存)。値入り判定で keep/drop を分ける
ため ALL_MODE_SECTIONS を導入。
回帰テスト(この gap が blocker を見逃した原因=「llm_grouping レポートの None
round-trip」が未カバーだった):
- prune の unit(llm_grouping / hierarchical 両方の null・空 active セクション削除、 値入りは温存)
- llm_grouping レポートの
update_jsonround-trip がllm_grouping: nullを 書き戻さず、実analysis_core.validate_configを通過すること
検証(before/after)
隔離 venv で analysis-core を editable install し確認:
- 旧 prune →
validate_configがTypeError(blocker 再現) - 新 prune →
llm_groupingキーを drop →validate_config通過 test_analysis_config.py13 passed、prune consumer 系(launcher/duplicate)31 passed、 ruff check / format クリーン
follow-up 候補(本 PR 外): analysis-core 側
core/orchestration.py:273をconfig.get(step, {}) or {}にすれば、present-but-Noneセクション全般に対する 防御が analysis-core 単体でも効く。今回は PR スコープを API 境界の prune に限定した。
merge 順(衝突最小化)
第1波(独立・そのまま merge 可)
#982 experiments 隔離 / #985 analysis-core 隔離
第2波・ペアB(散布図)── 直列
#983 を先 merge → #986 を rebase → CI → merge
⚠ #983 が非選択クラスタを dim する場合、#986 の凡例 swatch(full-saturation) と
plot 色の parity を rebase 後に再確認
第2波・ペアA(admin+api)── 直列
#984 を先 merge → #987 は追修正済みを rebase → CI → merge
⚠ `embedding` は**両 mode の step**。#987 が hierarchical セクションを else 枝へ
寄せたため、#984 の embedding config は**共通枝**に置く(else 枝に入れると
llm_grouping で埋め込みモデル設定が消える)
ペア内は必ず「片方 merge → 相方 rebase → CI → merge」の直列。並行 merge で
auto-merge 不能な衝突(ScatterChart.tsx の customdata/onClick、admin create の共有 8 ファイル)
を踏まないため。
Open Questions / follow-up
- #985:
BadRequestErrorの一律 fallback を code/message で絞るか(無関係 400 で無駄呼び+誤ログ) - #985 / analysis-core: OpenRouter 経由 reasoning モデルの
temperature=0guard 欠落(既存・別 issue 化候補) - #986:
jest.config.jsの lucide-react パス直書きを依存 bump 耐性のある形へ - #984: submit gate に
embeddingModelErrorを通す(chat model と非対称) - #987: cluster 数設定 UI が llm_grouping 時に効かないのに表示される(nit)
- 全 PR とも approve / merge は人間判断。本ラウンドでは実施していない
Updates
2026-10-12 — #987 を admin squash merge
- nishio 指示で PR #987 を admin squash merge(merge commit
c33d264a、CI 10 green を確認後)。REVIEW_REQUIRED を admin でバイパス。repo 慣例の squash 方式(#981=single-parent を踏襲)。 - Issue #176 は OPEN のまま:PR 本文に closing keyword が無く「一部解決」スライドのため自動 close せず。残スコープの完了判断は未実施(related PR merge だけで close しない方針)。
- knock-on: #987 先行 merge により #984 が rebase 必要化(base 更新で mergeable 再計算)。rebase 時は embedding config を共通枝に維持(本文 merge 順の注意点どおり)。
- まだ open の残り 4 PR = #982 / #983 / #984 / #985(#986 は #983 の後に rebase)。
2026-10-13 — 独立 PR #982 / #985 を admin squash merge
- nishio 指示で第1波(独立 PR)を merge:#982(
9410636)/ #985(490a0bd3)。いずれも MERGEABLE・CI green(?=CodeRabbit は非必須)を確認後、admin squash。 - 関連 Issue #143(#982)/ #537(#985)は OPEN のまま(closing keyword なし=「一部解決」スライド。完了判断は別途)。
- 残 open = #983 / #984 / #986。次はペアB(#983→#986)とペアA(#984 の #987 への rebase→merge)。
2026-10-13 — #983 merge・#984 を rebase
- nishio 指示で #983(#52 散布図選択連動)を admin squash merge(
88c592a1)。 - #984 を main(#987 込み)へ rebase(
28dd78f1を--force-with-leaseで push、MERGEABLE・CI 実行中)。- 衝突は 2 箇所のみ=
useAISettings.ts(DEFAULT_ANALYSIS_MODE/handleAnalysisModeChangeとisEmbeddingRow/handleEmbeddingModelChangeの both-added)とtest_report_launcher.py(mode 系テストと embedding 系テストの both-added)。いずれも両採用。 - semantic trap 回避を実証:
report_launcher.pyの auto-merge で embedding config が mode 分岐前の共通configに残り、analysis_mode=llm_grouping+embedding_modelでもembeddingが温存される(hierarchical_clustering不在・llm_grouping: null漏れなし)。隔離 venv の実validate_config(llm_grouping)通過・API 56 tests pass で確認。admin tsc/jest は CI で確認。
- 衝突は 2 箇所のみ=
- 関連 Issue #52 は OPEN のまま(closing keyword なし)。残 open PR = #984(rebase 済・CI 待ち)/ #986。
2026-10-13 — #975 / #984 を admin squash merge(残 open は #986 のみ)
- nishio 指示で CI 全 green の2件を admin squash merge:#975(意見ごとの侮辱・ヘイト/政策提案ラベルの任意ステップ, analysis-core,
1e7fdae5)/ #984(#450 埋め込みモデルをチャットと独立に選択,8f402d2e)。 - 通常 merge は ruleset(必須承認1)で
base branch policy prohibits the mergeとなるため admin override。classifier が AI 自身の--adminを阻むので人間が!で実行。 - #984 は前日 rebase 済み(
28dd78f1)のまま green を維持して merge 完了。embedding 共通枝温存は維持。 - 残 open PR = #986(#306 散布図,
DIRTY/CONFLICTING=要 rebase)/ #891(Windows スタンドアロン・Draft)。マージ可能なものは今回で払い出し完了。
2026-10-13 — #986 を main へ rebase(CONFLICTING 解消)
- nishio 指示で #986(#306 散布図の操作ヒント+濃い意見グループ帰属可視化)を現 main へ rebase。専用 worktree
work/kouchou-ai-986(rebase/986-scatter)で作業、本体 HEAD は main 維持(二分原則)。 - 衝突は 2 ファイルの both-added プロップ追加のみ=
ScatterChart.tsx(型定義+分割代入でonSelectCluster〈#983 由来・main 側〉とshowParentAttribution〈#306〉)とscatter.tsx(<ScatterChart>への両プロップ受け渡し)。両採用。両プロップとも本体で参照済み(onSelectCluster: L639 クリック通知 / showParentAttribution: L144,163 凡例ロジック)を確認。 - ローカル検証 green: biome lint・
tsc --noEmit(exit 0)・jestScatterChartLegend.test.tsx(3 pass)。65c2a264を--force-with-leaseで push。 - 結果 MERGEABLE に回復(
BLOCKEDは必須チェック実行中+承認待ち)。残 open PR = #986(rebase 済・CI 待ち)/ #891(Draft)。
2026-10-13 — #986 を admin squash merge(ラウンド全消化)
- rebase 後 CI 全 9 チェック green を確認し、nishio 指示で #986 を admin squash merge(merge commit
c1c9e917)。通常 merge は ruleset(必須承認1)で拒否のため admin override(人間が!実行)。 - Issue #306 は OPEN のまま:PR はサブ改善(操作ヒント+帰属可視化)でタイトルに closing keyword なし=一部解決。related PR merge だけで close しない方針を踏襲。
- 後片付け: rebase worktree
work/kouchou-ai-986(rebase/986-scatter)削除、ついでに merge 済み #984 の stale worktreework/kouchou-ai-984も削除。本体 HEAD は main。 - #982–987 ラウンドの PR はこれで全て merge 完了(#982/#983/#984/#985/#986/#987 + 独立の #975)。残 open PR は #891(Windows スタンドアロン・Draft・2026-06 停滞)のみ。
Issue 完了・close 判断の棚卸し(2026-10-13)
6 PR 全 merge 後、対応 6 Issue を issue-backlog-audit-2026-10-12 / issue-backlog-audit-2026-10-11 の「合意要件・残件」baseline(PR 投入前の main 照合)× 本ラウンドの実コードレビュー結果(= 必要な動作確認)× 現 main c1c9e917 の再確認 で照合した。各 Issue は OPEN のまま(PR 本文に closing keyword なし=一部解決スライス)。本棚卸しでは close 実行・Issue 本文編集はしていない(close は人間 attention 判断として nishio 確認待ち)。
| Issue | PR | baseline 残件 | PR で達成(確認根拠) | 現状の残り | 判定 |
|---|---|---|---|---|---|
| #176 LLM ベース分類 | #987 | プロダクト統合未達(admin UI・API が analysis_mode 非受理、engine は実装済) | 作成 UI から LLM grouping 選択+API が analysis_mode 受理、編集→再集計 BLOCKER も修正(レビューで end-to-end 確認) | nit: cluster 数設定 UI が llm_grouping 時に no-op なのに表示 | close 推奨(engine+統合とも充足、nit は別 follow-up) |
| #450 埋め込みモデル選択 | #984 | provider 別 embedding 独立リスト無・保存/実呼出経路無(Serverless は別スロット) | 専用 embedding selector(catalog.models を isEmbeddingRow で絞込・local/azure で無効化)+保存+_build_config で実注入(現 main AISettingsSection.tsx:241-248 / useAISettings.ts:101 確認) | Serverless 別スロット(本 Issue 主目的外)、submit gate が embeddingModelError 未参照(nit) | close 推奨(主要残件充足。Serverless は別 Issue 化) |
| #143 クラスタ品質自動評価 | #982 | 同梱 dataset/harness 無・検証結果未コミット・プロダクト統合無(意図的に後段) | --skip-llm+決定論サンプル同梱で再現可能化(レビューで end-to-end 実行・決定論再現を実証) | 評価結果の継続蓄積・プロダクト統合は意図的に後段スコープ | close 推奨(本 Issue の主眼=再現可能化は達成。後段は別管理) |
| #52 チャート連動文章(散布図) | #983 | 散布図のクラスタ選択連動が未実装(treemap 連動は #927 済) | 散布図選択を連動文章・一覧へ反映(onSelectCluster→ClientContainer→ClusterBreadcrumb、scatterLink.test 確認) | 散布図一覧の「属性絞込件数」反映は要確認、再クリック no-op 等 minor UX | close 候補(主目的の選択連動は達成。件数反映の残要否を確認後 close) |
| #306 全体図/濃い意見グループ UI | #986 | 操作ヒントアイコン未確認・濃いグループ→親クラスタ帰属可視化未確認・wishlist 完了基準曖昧 | 操作ヒント+帰属可視化の両方を実装(凡例色 parity 証明済) | wishlist 他項目の有無が未棚卸し(元 Issue が複数要望の束) | close 候補(特定された 2 残件は達成。残 wishlist を棚卸し後に close 可否判断) |
| #537 OpenRouter 無料モデル | #985 | 無料モデルで実際に動く(エラー解消)の実検証が未・parse 経路の fallback 未 | structured outputs fallback 機構を実装(fallback chain をレビューで確認) | 「無料モデルが実際に動く」最終確認が核心残件で未達(実モデル検証は #912/#913 へ分離)。BadRequestError 一律 fallback 絞込・temperature=0 reasoning-guard(既存バグ)も残 | OPEN 維持推奨(機構は入ったが Issue の主眼=実動作確認が #912/#913 依存で未完) |
判断の要約
- close 推奨 3 件: #176 / #450 / #143(合意要件の主眼を満たし、残りは nit・別スコープ・意図的後段)。
- close 候補 2 件: #52 / #306(主目的は達成だが、小残件〈一覧件数 / wishlist 残〉の要否確認後に close)。
- OPEN 維持 1 件: #537(実無料モデルでの動作確認という核心が #912/#913 依存で未完)。
- close する場合は CLAUDE.md の方針どおり、各 Issue 本文に日付つきで「満たした範囲/残件/統合先」を追記してから close し、残件(Serverless embedding スロット・散布図一覧件数・#306 wishlist 残・#537 実検証)は存続 Issue(#912/#913 等)または新 Issue へ引き継ぐ。
2026-10-13 — close 推奨 3 件を実行(本文追記つき)
- nishio 指示(選択肢 A)で #176 / #450 / #143 を
completedで close(closedAt 2026-10-13T03:57Z)。書込直前に updatedAt 照合(3 件とも 2026-10-12 から不変)→ 元本文を保持したまま各末尾に「2026-10-13 完了判断(満たした範囲/残件)」を追記 → close コメントを添えて close。 - 本文確認で判明したスコープ補正(棚卸し表からの追加発見):
- #176: 提案は 2 方向あり、#987 が満たすのは (1)「分類トピックを LLM で自動構築(= LLM 直接グルーピング)」のみ。(2)「人間が付与した所与トピックへ分類(predetermined topics)」は #987 対象外で未実装。残件として本文に明記し、必要なら別 Issue 化とした(nishio は A=新 Issue を今は立てない選択)。
- #450: Serverless 別スロット・実モデル動作確認フラグは Issue 本文(2026-09-09 追記)自体が「分ける」としていた残件。本文に再掲。
- #143: Issue 本文の進め方自体が「実験スクリプトの再現可能化」を主眼とし製品統合は別 Issue 想定。#982 で主眼達成として close。
- #52 / #306 は close 候補のまま保留(小残件の要否確認後)、#537 は OPEN 維持(無料モデル実動作が #912/#913 依存で未完)。
2026-10-13 — #52 / #306 の小残件を現 main c1c9e917 で確認 → 両件とも close 可
nishio 指示で保留 2 件の小残件を実コード照合。両件とも完了基準を満たし、material な残件なし(棚卸し表の残件懸念は PR 投入前 main 基準で、#983/#986 が既に充足していた)。
- #52(#983): Issue 本文 2026-09-09 の完了基準4点を現 main で確認。
- 「属性で絞った件数」✅
ClientContainer.tsx:111-120のclusterCounts(filtered-aware、treemap と同じ数え方)→:268countprop →ClusterOverview.tsx:66が「N件(フィルター後)」表示。audit が未反映としていたが #983 で充足済み。 - 「全体/詳細/濃い意見の説明の連動」✅ scatter 各モードで
clustersToDisplay再計算 →ClusterOverview(takeaway 表示)。選択でisSelectedハイライト+該当項目へ smooth scroll(ClusterOverview.tsx:23-27)。 - 「階層移動と戻る」✅ scatter 一覧 → treemap ナビ(
:271-277)+ treemap breadcrumb(#927)。 - 「0件からの復帰」✅
getDenseClustersのisEmptyで dense-group 空時は scatterDensity モードを無効化(:98-99,:244)=空ビューに入らせず復帰自動。 - 残る nit はレビュー既知の UX polish のみ=再クリックで選択解除されない(no-op)/mode 切替時に stale 選択へ auto-scroll しうる(
useEffect([isSelected])が再マウントで発火)。いずれも非ブロッキング。Serverless(#25)・元コメント表示(#56/#250)は本文どおり明示的に対象外。
- 「属性で絞った件数」✅
- #306(#986): 本文 wishlist 5 項目すべて現 main で対応。
- ①ヒントアイコン ✅
ScatterChart.tsx:602-631(ドラッグ=パン / ホイール=拡縮 / 右上ツールバー=初期表示へリセット=背景のダブルクリック戻し相当 / hover=内容 / click=ソース)。 - ②ズーム in/out ✅
displayModeBar:'hover'+scrollZoom:true(plotly ツールバー+ホイール)。③移動ボタン ✅dragmode:'pan'で「ドラッグでパンできない」前提自体を解消(専用ボタン不要)。④表示モード/全画面で状態保持 ✅uirevision:'scatter'(#290 相当)。⑤濃いグループの帰属 ✅showParentAttributionで第一階層クラスタへの帰属を色/凡例可視化(parent チェーン解決・凡例色 parity 証明済)。
- ①ヒントアイコン ✅
結論: #52 / #306 とも close 推奨へ格上げ可。close 時は #176/#450/#143 と同様に「満たした範囲/残 nit(#52 の UX polish)/対象外(Serverless・元コメント)」を本文追記してから close する。
2026-10-13 — #52 / #306 を本文追記つき close(棚卸し完了)
- nishio 指示で #52 / #306 を
completedclose(closedAt 2026-10-13T04:29Z)。updatedAt 照合(不変)→ 元本文保持のまま「2026-10-13 完了判断(満たした範囲/残件)」追記 → close コメント添付。 - #52 は残 nit(再クリック no-op・mode 切替 auto-scroll)と対象外(Serverless #25・元コメント #56/#250)を本文明記。#306 は wishlist 5 項目充足を明記。
- #982–987 対応 6 Issue の完了判断はこれで収束: close 5(#176/#450/#143/#52/#306)/ OPEN 維持 1(#537=無料モデル実動作が #912/#913 依存で未完)。#176 方向性(2)〈所与トピック分類〉・#450 Serverless embedding スロット等の残件は各本文に記録済み(必要時に別 Issue 化)。
2026-10-13 — #537 残件を #991 へ移管して close(6 Issue 全消化)
- 移管先確認で #912/#913 は GPT-5.6 / Gemini 3.8 専用の実 API 検証 Issue(#906/#909 カタログ検証シリーズの model 別 sibling)で、OpenRouter とは無関係と判明(
[TEST]全検索でも OpenRouter 用は不在)。audit の「#537 は #912/#913 依存」は不正確だった(訂正)。 - nishio 判断で #912/#913 と同テンプレの新 [TEST] Issue #991「OpenRouter無料モデルの実API動作を検証する」を作成し、#537 残件を移管(fallback 経路の実動作確認・
BadRequestError絞り込み・temperature=0 guard・学習オプトアウト前提)。実 API 検証は人間実施方針を踏襲。 - #537 を
completedclose(fallback 機構は #985 で実装済、実検証は #991 へ)。本文に移管内容を追記。 - これで #982–987 対応 6 Issue はすべて close(#176/#450/#143/#52/#306/#537)。派生 open = #991(実 API 検証・人間実施)、および既存の #912/#913。
2026-10-13 — #176 方向性(2) を #994 として起票(CLI-first・「その他」必須)
- #176 の残件として本文記録していた**方向性(2)「人間が付与した所与トピックへの意見分類(supervised / 指定カテゴリ分類)」**を、nishio 指示で新 Issue #994 化。#176 に breadcrumb コメントを追加。
- 設計の要点(nishio 入力): (a) engine は
steps/llm_grouping.pyの_discover_groupsをスキップし固定枠を_assign_groupsへ渡す拡張、(b) 「その他(Other)」カテゴリを必ずシステム側で自動付与(指定枠に当てはまらない意見の押し込みを防ぎ、枠の過不足のシグナルにする。シン東京2050 等で所与枠分類の需要あり)、(c) 広聴AI の niche 機能方針どおり CLI/core 先行で、WebUI 統合は動作確認後に別 Issue。 - 「その他」必須は設計原則として memory 化(
supervised-classification-other-bucket)。
2026-10-13 — #994 を CLI 実装(PR #996・進行中)
- nishio 指示で #994 に self-assign し実装着手。analysis-core の LLM grouping に supervised モードを追加(
llm_grouping.predefined_groups指定で_discover_groupsをスキップ、割当のみ LLM)。 - 「その他」受け皿を必ず自動付与(
_ensure_other_group)。非該当意見・無効応答を先頭枠でなく「その他」へ寄せ、割当プロンプトにも誘導。other_group_labelでラベル変更可・既存同等枠は流用。 - 配線:
specs/llm_grouping_specs.jsonにpredefined_groups/other_group_label追加(validate_config 通過)、plugin はフル config から読む(list 値の workflow 補間回避)、docs/user-guide/cli-quickstart.mdに利用例追記。 - 検証: analysis-core 325 passed(新規 4 ケース)・ruff clean・CLI
validate-config/dry-runで受理確認。実 LLM フル実行はプロバイダ鍵要のため未実施。WebUI は別 Issue。 - 2026-10-13 nishio 指示で PR #996 を admin squash merge(
42bba8ea・single-parent 確認)。必要 CI 全 green(Pytest/Ruff/test/build/Analyze×4/CodeQL)後に承認待ち(BLOCKED)を admin override。worktree/branch 撤去、本体 HEAD は main。#994 に満たした範囲/残件(WebUI 統合・実 LLM フル検証)を記録。Issue #994 は OPEN 維持(CLI スコープ達成・WebUI 別 Issue 化は nishio 判断待ち)。 - 機能の正本(設計・使い方・「その他」必須の理由)は concept ページ llm-grouping-supervised-classification に切り出した。