Skip to content
frontmatter
キー値
specdojo.idprj-0001:pjr-9pz7-grade-0
specdojo.typeproject
specdojo.statusready
specdojo.rulebookspecdojo:pjr-rulebook
specdojo.part_ofprj-0001:pjr-index
specdojo.item_typetodo
specdojo.item_statusdone
specdojo.priorityhigh
specdojo.ownerDEV
specdojo.registered_at2026-09-26T10:28:16Z
specdojo.completed_at2026-09-26T12:08:40Z
specdojo.block_reasonagent exited with non-zero code: runner による検証 `test-unit` が失敗しているため(`tests/tools/grade-per-document.test.ts` において 1 件の失敗が検出された)。

PJR-9PZ7 grade の打ち切りが検出されず終了コード 0 で完了扱いになる ​

1. 概要 ​

tools/grade/run-per-document.sh が対象の一部だけを処理して終了しても、終了コード 0 を返す。利用者は完了したと誤認する。実際に 5 文書を指定した実行が 2 文書で打ち切られ、終了コード 0 になった。

2. 事実 ​

2.1. 観測した挙動 ​

PJR-H5Z7 の grade を 5 文書へ指定して実行した。

text
documents=5 exhausted=0
document complete: cdfd-action.md   (migrated_stage_total=3。前回状態の引き継ぎで完了扱い)
document complete: cdfd-check.md
document start:    cdfd-overview.md
[exited with code 0]

document start の 3 件目で出力が途切れ、終了コード 0 で終わった。BPS 2 件は document start すら出ていない。

2.2. 最終行が出ていない ​

script は全対象の処理後に集計行を出力する。

sh
printf 'grade pipeline complete: selected=%s processed=%s completed_now=%s incomplete=%s exhausted=%s results=%s\n' \
  "${#selected_paths[@]}" "$processed" "$completed" "$incomplete_documents" \
  "${#exhausted_paths[@]}" "$results_file"

打ち切られた実行の出力に grade pipeline complete は含まれていない。完走した実行には含まれる。この行の有無が唯一の判別手段であり、終了コードでは区別できない。

2.3. set -euo pipefail が終了コードを奪っている ​

script 冒頭は set -euo pipefail である。ループ内でコマンドが非 0 を返すと、その時点で shell が終了する。set -e による終了は終了したコマンドの終了コードを引き継ぐが、パイプや条件式の組み合わせによっては 0 になりうる。

今回は | tail -20 を通しており、PIPESTATUS を見ずに $? を読むと tail の終了コード(0)になる。呼び出し側がパイプを使う限り、script の終了コードは伝わらない。

2.4. 前回状態の誤った引き継ぎ ​

cdfd-action は migrated_stage_total=3 として「完了」と記録されたが、graded_at は 2026-09-17 のままで実際には評価されていない。3 段構成で失敗した pipeline 状態が、単段実行で完了扱いへ移行された。

stages は設定署名に含まれるため干渉しないと想定したが、pipeline 状態ファイルは署名と独立に文書単位で保持されており、移行処理が働いた。

3. 影響 ​

影響内容
未評価を評価済みと誤認する古い graded_at の結果が残り、新しい判定と区別できない
自動実行で検出できないroutine から呼ぶ job も終了コードで判断する
打ち切りの原因が記録されないどこで止まったかは出力の末尾からしか分からない

3.1. 同じ構造の問題が別に起きている ​

prj-0001:pjr-1y9p-resume-executor-plan は「未完了の作業が完了として記録される」問題を扱った。本件は同じ性質が grade 側にある。 段や文書の処理が途中で終わっても、成功として扱われる。

4. 完了条件 ​

  • 全対象を処理せずに終了した場合、終了コードが 0 にならない。
  • 打ち切りの理由(rate limit、プロセス終了、失敗上限)が出力に残る。
  • パイプ経由で呼んでも終了コードが失われない。呼び出し側の記述を運用ガイドへ示すか、script 側で対処する。
  • grade pipeline complete の集計行が出ない状態を検出できる。
  • 前回の pipeline 状態が段構成の異なる実行へ引き継がれない。または引き継ぐ場合は graded_at の鮮度を確認する。
  • 部分的に完了した対象と未着手の対象を区別して報告する。
  • job 定義(job-grade-kata / job-grade-deliverable)が打ち切りを失敗として扱う。
  • 既存の正常な実行が失敗扱いにならない。

5. 対応の候補 ​

案内容利点懸念
1trap で異常終了を捕捉し、未処理の対象を報告して非 0 で終える原因に近いset -e との相互作用の確認要
2最後に selected と processed を比較し、不一致なら非 0実装が単純打ち切りの理由は分からない
3文書ごとの結果を results.tsv へ逐次記録し、突合で検出する既存の仕組みを使える呼び出し側の処理が要る

案 2 を基本とし、案 1 を併せる。 案 2 だけでも誤認は防げる。案 1 は原因の記録を加える。

5.1. 前回状態の引き継ぎ ​

migrated_stage_total の移行は、段構成を変えた再実行を容易にする意図と見られる。ただし評価の実体がないまま完了扱いにするのは誤りである。保存済み state の stage_total が現在の構成と異なる場合は、graded_at の鮮度を推測せず state を破棄し、現在の構成で再評価する。

6. 作業内容 ​

No作業担当状態メモ
1対応の候補から方針を決めるARCdone件数照合と EXIT / signal trap を併用
2打ち切りの検出と終了コードを実装するDEVdone現在文書と未着手文書を標準エラーへ出力
3前回状態の移行へ鮮度の確認を加えるDEVdone異なる stage_total は移行せず、state を破棄して再評価
4job 定義が打ち切りを失敗として扱うか確認するOPSdonescript の終了コードを保存してそのまま返す
5運用ガイドへ手動実行時の注意を記載するOPSdonepipefail と PIPESTATUS を記載

7. 対応結果 ​

案 2(処理数の突き合わせ)と案 1(trap)を組み合わせて実装し、develop へ統合した(e8025eaf)。

観点実装
打ち切りの検出EXIT の trap で、完走の印が立っていなければ打ち切りとして扱う
終了コード0 のまま抜けた場合は 1 に変える
理由の記録rate_limit、signal_INT、signal_TERM、completion_marker_missing、unexpected_exit
失敗上限上限に達した文書を reason=failure_limit で報告する
未着手の報告処理済みと未着手の件数、未着手の文書の一覧を stderr に出す
前回状態の引き継ぎ段構成が違う状態は完了扱いへ移さず、状態を戻して評価し直す
job 定義スクリプトの失敗を job の失敗として返す。idempotency_key の版を上げた

7.1. executor の実装に欠陥が 1 件あった ​

pipeline_state_fields に 5 つ目の項目(stage_total)を加えた一方で、record_pipeline_stage は 4 つの変数で読んでいた。bash の read は余った値を最後の変数へまとめるため、max_failures に 3\t3 が入り、失敗上限の比較が常に偽になっていた。runner の test-unit 検証がこれを検出し、実行は失敗した。読み取りに 5 つ目の変数を足して直した。

一時的にデバッグ出力を入れて値を確かめ、確認後に戻した。

7.2. 実行の経緯 ​

回結果
1PJR-BX79 と並行で実行。checkpoint 段の dubious ownership で agent 起動前に失敗(4 回目の発生)
2BX79 の修正を入れて再実行。checkpoint 段を通過し実装まで進んだが、上の欠陥で test-unit が失敗
3私が欠陥を直して統合

7.3. 検証 ​

検証結果
grade-per-document.test.ts20 件通過
test:unit1562 件すべて通過(develop へ統合後)
test:integration110 件通過、1 件 skip(PJR-TDB0)
validate:schema / lint:md通過

8. 関連ドキュメント ​