Skip to content

feat(cli): import に --dry-run / --yes / --report を追加する(B6) - #39

Merged
kentalow merged 2 commits into
mainfrom
feat/b6-dry-run
Aug 30, 2026
Merged

feat(cli): import に --dry-run / --yes / --report を追加する(B6)#39
kentalow merged 2 commits into
mainfrom
feat/b6-dry-run

Conversation

@kentalow

@kentalow kentalow commented Aug 30, 2026

Copy link
Copy Markdown
Member

Summary

インポートに --dry-run / --yes / --report を追加した(バックログ B6 のコード部分。AI 変換ガイドは別 PR)。4つの import コマンド(csv / xlsx / case / rubric)すべてが対象。

フラグ 動作
--dry-run トランザクション内で実際に取り込み、commit せずロールバックする
--yes 破壊的変更をプロンプト無しで承認する(自動化向け)
--report <path> 構造化 JSON の検証レポートを書き出す

破壊的変更は「正味の損失」で数える

ドキュメント単位の再構築は更新のたびに該当タイプの関連を全削除して再作成する。削除件数で判定すると毎回発火し、運用者はプロンプトを読まなくなる。

数えるのは2つ。

  • 失われる関連: 削除され再作成されないもの((associationType, origin, destination) で比較)。並び替えは同じ組を再作成するので発火しない。親の変更は発火する(旧親との関連は実際に消える。安全側に倒した意図的な過剰検知)
  • 移動したリソース: 他ドキュメントからの付け替え。移動元の木が壊れる

判定は commit ゲート(サービス実行後・同一トランザクション内)で行う。事前推定は採らない。削除範囲はヘッダの列構成と有効行数で決まるので、推定は取り込み処理の二重実装になる。

⚠️ 自動化経路への影響

非対話環境(stdin が tty でない)で --yes が無い場合、ロールバックして exit 1 する。 CI やスクリプトが黙って先に進むことと、プロンプト待ちでハングすることの両方を防ぐためである。

compeito-aws には予告済み。Admin API とスクリプトに --yes を通す作業が要る。

C3 との関係

--reportissues には code が付く。required_field_missing(公式スキーマが required とするフィールドが元データで空)は、strict 出力で残るギャップ(conformance backlog C3)を運用で閉じるための入口である。compeito は出力側で値を捏造しないので、生成側が何を直すべきかを知る経路がこれしかない

設計から意図的に外した点

設計は warnings を property 化し、85 箇所の warnings.appendwarn() に置換するとしていた。採らなかった。 property が返すリストへの append は黙って捨てられ、warnings: list[str] を引数で引き回すパーサが多数ある。issues を併存させる形にし、外部契約(code 付き JSON)は設計どおりにした。

レビュー

Claude Opus 5 で2ラウンド。must-fix 3件はいずれも実測で再現されたもの。

  • xlsx の往復が毎回ガードに掛かっていた。生成する CSV のヘッダが関連列を全て含むため Step 7.5 が全削除し、再作成は xlsx の第2パスが行う。その前に数えていたので全件が lost になっていた。設計が避けたかった状態そのもの
  • 確認プロンプトが Rich のスピナーに消されていた。利用者には固まったスピナーしか見えない。ガードの中心が対話確認である以上、機能していないに等しかった
  • ドキュメント4箇所が事実と逆(「親の変更は掛からない」)。設計の記述から伝播していた誤解

テストの穴も埋めた。CliRunner は tty を持たないため対話分岐が1行も通っておらず、xlsx と親変更のテストも無かった(上記2件はここで漏れた)。

Test plan

  • uv run pytest(905 passed / ローカル)
  • uv run ruff check . / uv run ruff format --check .
  • 実 pty でプロンプトの可視性と exit 2 を確認
  • xlsx 往復で lost 0、CF Association の行を削れば lost 1
  • 親変更 → 発火、並び替え → 非発火
  • 対話分岐(y で commit / N で exit 2 / 拒否時レポートに cancelled
  • 変異テスト(ガード無効化 / dry-run で commit / 非対話判定無効化 / 削除件数で判定 / xlsx の再計算除去 / 作成分の未記録)
  • CI グリーン(lint / test とも pass)

🤖 Generated with Claude Code

kentalow and others added 2 commits August 30, 2026 07:46
設計は docs/dev/designs/import-dry-run-and-ai-guide.md。AI 変換ガイドは別 PR。

- **--dry-run**: トランザクション内で実際に取り込んでからロールバックする。
  サービス層は flush しかせず CLI が commit を持っているので、commit を飛ばす
  だけで成立する。事前推定は採らない(削除範囲はヘッダの列構成と有効行数で
  決まるため、推定は取り込み処理の二重実装になる)
- **確認ガード**: commit ゲートで判定する。破壊的変更は削除件数ではなく正味の
  損失で数える。(a) 削除され再作成されない関連(`(type, origin, destination)`
  で比較。並び替えや親の変更は同じ組が再作成されるので発火しない)、(b) 他
  ドキュメントからのリソース移動。ドキュメント単位の再構築は毎回全削除して
  再作成するため、削除件数で数えると毎回発火して形骸化する
- **非対話環境**では、--yes が無ければロールバックして exit 1。ハングも、
  黙って先に進むことも防ぐ
- **--report**: 実行結果を JSON で書き出す。issue に code を付け、required
  欠落(C3 の運用側)を機械可読にする。分類されなかった警告も含めるので、
  ファイルが実行結果の欠落した写しにならない

設計からの逸脱が1点ある。設計は `warnings` を property 化して全 85 箇所を
`warn()` に置換するとしていたが、`issues` を併存させる形にした。property が
返すリストへの append は黙って捨てられるため、`warnings: list[str]` を引数で
引き回すパーサ群と併せて事故になりやすい。外部契約(code 付きの構造化 JSON)
は同じで、分類は必要な箇所から段階的に増やせる。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
レビュー指摘の反映。must-fix 3件はいずれも実測で再現していた。

**M1: xlsx の往復インポートが毎回ガードに掛かっていた。** 生成する custom CSV
のヘッダは関連列を全て含むため Step 7.5 が該当タイプを全削除するが、CSV 段では
1件も再作成しない。再作成は xlsx の第2パスが行うので、import_csv の末尾で数える
と全件が lost になる。削除・作成のタプル集合をレポートに持たせ、第2パスの作成
分を足してから確定する形にした。設計が避けたかった「毎回発火して形骸化する」
状態そのものだった。

**M2: 確認プロンプトが Rich のスピナーに消されていた。** `console.status` の
内側で `click.prompt` を呼ぶと、書かれた質問が次のリフレッシュで消える。利用者
には「警告だけ出て固まったスピナー」しか見えない。ガードの中心が対話確認である
以上、機能していないに等しかった。commit ゲートをスピナーの外に出した。実端末
(pty)でプロンプトとサンプル一覧が見えることを確認した。

**M3: ドキュメント4箇所が事実と逆だった。** 「親の変更はガードに掛からない」と
書いていたが、旧親との関連は実際に消えるので掛かる(設計どおりの、安全側に
倒した過剰検知)。掛からないのは並び替えのみ。

should も反映した。

- レポートに `reportVersion` / `applied` / `cancelled` を追加。ガード拒否時も
  レポートは書かれるので、これが無いと成功した run と区別できない
- `destructive` を設計どおりの形に戻し、`itemsMoved` と `associationsMoved` を
  分けた(項目を奪われるのと関連が付け替わるのは判断材料として別物)
- `ValidationIssue` に `severity` を追加
- C3 の `required_field_missing` が新規作成時にしか付いていなかった。既存
  ドキュメントへの再インポートは CASE import の主経路なので更新側にも付けた
- `--report` の書き込み可能性を事前に確認する。`click.Path(writable=True)` は
  存在しないパスを検査しないため、大きなフレームワークを丸ごと走らせてから
  トレースバックで落ちていた

テストの穴も埋めた。CliRunner は tty を持たないため対話分岐が1行も通って
おらず、xlsx / 親変更のテストも無かった(M1 と M3 はここで漏れた)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@kentalow
kentalow merged commit 08e4881 into main Aug 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant