6.3 KiB
6.3 KiB
Review: Run 入力の事前 validate を Pod 内部に閉じる
前提・要件の確認
- 要件1「
Pod::runの冒頭、commit_entry(LogEntry::UserInput { .. })よりも前にvalidate_workflow_invocationsを呼び、エラー時は session log に何も commit せずPodErrorで early return」: 満たされている。crates/pod/src/pod.rs:1138-1145でself.validate_workflow_invocations(&input)?;がprepare_for_runよりも前、すなわちensure_session_head/ pre-run compact /commit_entry(UserInput)のいずれよりも手前にある。?でWorkflowResolveErrorがPodError::WorkflowResolveに lift され early return される。 - 要件2「
controller_loopのMethod::Runハンドリングから事前 validate 呼び出しを削除」: 満たされている。crates/pod/src/controller.rs:600-630のMethod::Runブランチに validate 呼び出しは存在しない。 - 要件3「
PodErrorをworker_error_codeでErrorCode::InvalidRequestにマップ」: 満たされている。crates/pod/src/controller.rs:944にPodError::WorkflowResolve(_) => ErrorCode::InvalidRequest分岐が追加され、ticket が示した「既存バリアントの再利用」方針通りWorkflowResolveErrorを新規ラップせずに既存PodError::WorkflowResolve(#[from] WorkflowResolveError)(pod.rs:3177-3178) を流用している。 - 要件4「
Pod::validate_workflow_invocationsの可視性はpubのまま」: 満たされている (pod.rs:1407)。docstring は controller 経路ではなくPod::run/Pod::interrupt_and_run入口で呼ぶ旨に更新済 (pod.rs:1402-1406)。
完了条件の確認
- 「
controller.rsのMethod::Runブランチにpod.validate_workflow_invocationsの呼び出しが残っていない」: OK (grepで残存呼び出しなし、Pod::run/interrupt_and_run のみ参照)。 - 「不正な workflow slug を含む
Method::Runを投げると、UserInput が session log に commit されないままEvent::Error { code: InvalidRequest, .. }が flow する」: OK。Pod::run入口のvalidate_workflow_invocations?がprepare_for_run/commit_entry(UserInput)よりも手前で抜けるため session log には何も書かれない。drive_turnのErr(e)ブランチがworker_error_code経由でInvalidRequestを載せたEvent::Errorを broadcast する。 - 「既存テスト通過」:
cargo test -p pod --lib173/173 通過 (再現確認済)。workflow::tests::{resolves_requires_before_workflow_body, user_invocable_false_errors, missing_required_knowledge_errors}はresolve_workflow_invocationの関数単体テストで本 refactor の影響を受けない。
アーキテクチャ・スコープ
- 責務漏れの是正方向は妥当: controller が Pod 内部の
workflow_registryを覗いて条件分岐していたのを Pod の入口に集約。read-only 関数を本来の所有者側で呼ぶだけの最小変更。 - 範囲外項目 (resolve ロジック / 判定基準 / Resume / RunForNotification) には踏み込んでいない。
run_for_notification/resumeは user 入力 segments を受け取らないので validate 経路に乗らない点もコードと整合。 interrupt_and_run側にも validate を入れた判断は妥当。チケットには明示されていないが、ticket 背景にある同じ責務漏れパターン (pod-interrupt-prep-internalize) と整合させるため必要。validate 抜きでinterrupt_and_runを呼ぶと orphan tool_result closure / interrupt system note が worker history に書き込まれた後でPod::run入口の validate に弾かれ、half-applied 状態が残る。interrupt_and_run.rs:31-37のコメントがpod-interrupt-prep-internalize統合時に自然に重複解消する旨を明示しており、将来の畳み込みに対する負債もない。worker_error_codeの新規WorkflowResolve分岐は副作用として post-commit な resolve エラー (KnowledgeNotFound/KnowledgeRead/KnowledgeFrontmatter) もInvalidRequestにマップする。これらは以前_ => Internalに落ちていたため挙動変化だが、意味論的にはより適切方向であり、validate / resolve でバリアントを分けるのは本チケットの範囲を超える。許容範囲。
指摘事項
Non-blocking / Follow-up
Event::UserMessage早期 broadcast 起因の UX 差分: 旧実装では validate 失敗時、Event::UserMessageもEvent::Status { Running }も飛ばなかった。新実装では bad slug の場合でも UserMessage → Status(Running) → Error → Status(Idle) が並ぶ。TUI (crates/tui/src/app.rs:478-485) はUserMessage受信でturn_index += 1し TurnHeader + UserMessage block を push するため、エラーが返るだけのリクエストでも turn 番号が前進し、ターンヘッダと user line がエラーブロックの上に残る。チケットの完了条件 (session log への UserInput commit 抑止) には抵触しないが、bad slug → ghost turn header が残る表示挙動は旧実装と異なる。フォローアップで controller 側のMethod::Runハンドリングを「validate を controller でも呼ばずに Pod 側に丸投げするのは維持しつつ、UserMessage broadcast は drive_turn の中で投げる」「Pod::run 内で resolve 成功直後に Event::UserMessage 相当を内側から飛ばす」等の動線整理を検討する余地がある。本チケットの責務漏れ修正としては完了範囲外と判断。worker_error_codeのWorkflowResolve分岐コメント: validate-stage 失敗 (InvalidSlug / NotFound / NotUserInvocable) と resolve-stage 失敗 (KnowledgeNotFound / KnowledgeRead / KnowledgeFrontmatter) が同じInvalidRequestに丸まる旨を一行コメントで明示しておくと将来の差分判断が楽になる。Nit に近い。
Nits
- なし。
判断
Approve with follow-up — チケットの前提・要件・完了条件は全て満たされ、責務漏れの修正方向も妥当。Event::UserMessage 早期 broadcast による turn 表示差分は許容範囲のトレードオフ (実装者ノート通り) で、本チケットの完了範囲外として別途整理する余地のみ残る。