Skip to content

cross-review: fix結果の resolved_threads が int だと state.py merge-fix が TypeError で落ちる(スキーマ不整合 + 受理が非寛容) #25

Description

@takemi-ohama

概要

/ndf:cross-review の修正フェーズで、fix サブエージェントが書き出す戻り値ファイル
$TMP_DIR/fix-pr<PR>-result.jsonresolved_threadsint(件数) で書くと、
scripts/state.py merge-fixlen() を int に適用して TypeError でクラッシュし、
ループが止まります。頻繁に再発します。

  • plugin: ndf v4.12.1
  • 該当: skills/cross-review/scripts/state.pycmd_merge_fix(v4.12.1 では L704)

再現時のエラー

Traceback (most recent call last):
File ".../skills/cross-review/scripts/state.py", line 895, in <module>
main()
File ".../skills/cross-review/scripts/state.py", line 891, in main
args.func(args)
File ".../skills/cross-review/scripts/state.py", line 704, in cmd_merge_fix
"resolved_threads": len(fix.get("resolved_threads", []) or []),
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: object of type 'int' has no len()

fix-pr<PR>-result.json が例えば "resolved_threads": 3 のとき発生。

根本原因(ドキュメントの自己矛盾)

resolved_threads の表現がドキュメント間で食い違っており、エージェントが int を書きやすい:

箇所表現実態
docs/02-fix-and-rotation.md 戻り値スキーマ (L133付近)list[{"thread_id":...,"path":...}]コードが期待する形
docs/01-state-and-review.md state.json 例 (L55)"resolved_threads": 4intstate.json 側の保存形(len() 後)
docs/01-state-and-review.md 説明 (L75)「resolveReviewThread で resolve した件数int を連想させる

state.pyfix結果=list を受け取り len() して state.json には int(件数) を保存する設計
だが、ドキュメントの「件数」表現により fix エージェントが fix結果側にも int を書いてしまう。

提案する改修(2点:両側で堅牢化)

1. fix結果JSONを list で返すよう強制(プロンプト/ドキュメント)

  • docs/02 の戻り値スキーマで resolved_threads必ず list であることを明記し、
    「件数(int)ではない」と注意書きを追加。
  • docs/01 L75 の「件数」という説明を、fix結果側は list・state.json 側は
    その len()(int) である、と区別して記述。
  • fix サブエージェント起動プロンプト(docs/02 Step 5 / 本体 skill の Step 5 説明、
    および最終スイープ Step 7.5)でも list 形式を明示。

2. state.py 側を int でも list でも受理できるようにする(後方互換)

cmd_merge_fix の該当行を、int / list / None いずれも受理する正規化に変更:

def_count(v):
\"\"\"int(件数) でも list でも None でも件数(int)に正規化する。\"\"\"ifisinstance(v, int):
returnvifisinstance(v, (list, tuple)):
returnlen(v)
return0# ..."resolved_threads": _count(fix.get("resolved_threads")),
"deferred": _count(fix.get("deferred")),
"rejected": _count(fix.get("rejected")),

deferred / rejected も同種の len() 適用箇所であり、同じく int 混入で落ちうるため
まとめて _count() 経由にすると再発防止になる(deferred は別途 for d in fix["deferred"]
で list 走査する箇所があるため、そちらは isinstance(..., list) ガードを追加)。

期待される効果

  • fix エージェントが int / list どちらを書いてもループが止まらない。
  • ドキュメントの一貫性が取れ、そもそも int を書きにくくなる。

補足

回避策として、発生時に fix-pr<PR>-result.jsonresolved_threads
["t1","t2","t3"] のような list に手で書き換えて merge-fix を再実行すると復旧できる
(今回はこれで継続できた)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions