0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

kaji v0.18.0のlocal providerでguarded loopを検証し、baseline ABORTから再開した

0
Posted at

TL;DR

  • kaji v0.18.0のlocal providerを、外部GitHubへ変更を送らない分離環境で検証した
  • design -> review-design -> baseline -> implement -> review-code -> final-check -> closeを実行した
  • 初回はpytest-xdist不足、2回目は.kaji-artifacts/によるdirty worktreeでABORTした
  • 環境を修正し、--from baselineで再開できた
  • 最終的に既存9件と新規3件の計12テスト、ruff、format、mypyがPASSした
  • feature branchはmainへ非FFマージされ、local Issue、worktree、branchもクローズ・削除された
  • 今回確認したのは外部I/Oのない小さなPython関数1件であり、本番運用の安全性は未検証

対象読者

  • kajiのlocal providerを小さく試したい方
  • AI coding agentの設計・実装・レビューを明示的なworkflowにしたい方
  • baseline gateとstep-level resumeの実際の挙動を知りたい方

検証環境

検証日は2026年8月19日です。

項目
OS Windows 11上のWSL2 Ubuntu
kaji 0.18.0
Python 3.12.3
uv 0.9.2
tmux 3.4
Claude Code 2.1.218
provider local
model Claude Sonnet
workflow .kaji/wf/custom/dev/dev-local-lab.yaml
Issue local-lab1-1

外部starter remoteはfetch専用のupstreamとし、push URLをDISABLEDにしました。originはWSL内のlocal bare repositoryです。GitHub Issue、Pull Request、外部repositoryへのpushは行っていません。

検証課題

starter_appへ次の公開関数を追加します。

def greet(name: str) -> str:
    ...

完了条件は次のとおりです。

  • greet("海") == "こんにちは、海さん"
  • 名前の前後の空白を除去する
  • 空白除去後に空ならValueErrorを送出する
  • 正常系、空白除去、異常系をSmallテストで確認する
  • 既存のhello__version__を維持する
  • make checkを通す

workflow

検証用workflowでは、各review/fix/verify loopの最大反復回数を1に制限しました。

実行コマンドは次のとおりです。

uv run kaji run .kaji/wf/custom/dev/dev-local-lab.yaml local-lab1-1

今回使用した主なstepは次のとおりです。

step 役割
design 実装前の設計とテスト戦略を作る
review-design provenance、完了条件、テスト戦略を確認する
baseline 変更前commitのpytest結果を記録する
implement テストを先に追加して実装する
review-code diff、設計整合、独立テストを確認する
final-check make checkと前段証跡を再確認する
close mainへマージし、Issueとworktreeを片付ける

1回目のABORT:pytest -nを認識できない

初回runではdesignreview-designはPASSしましたが、baselineでABORTしました。

stderrは次の内容です。

ERROR: usage: python -m pytest [options] [file_or_dir] [...]
python -m pytest: error: unrecognized arguments: -n

verdictに記録された主な値は次のとおりです。

baseline_status=invalid
pytest_exit_code=4
failures=0

failures=0でも、pytestの終了コード4はテスト成功ではありません。テスト実行方法が不正なため、baselineはinvalidとして停止しました。

原因は、baselineが並列実行用の-nを使用する一方、dev依存にpytest-xdistがなかったことです。

pyproject.tomlへ次を追加しました。

[dependency-groups]
dev = [
    "pytest>=8.0.0",
    "pytest-xdist>=3.6.0",
]

uv lockuv syncの後、次を実行しました。

uv run pytest -n auto -q

変更前の9テストがすべてPASSしました。

2回目のABORT:.kaji-artifacts/でdirty worktreeになる

依存関係を追加し、次のコマンドでbaselineから再開しました。

uv run kaji run .kaji/wf/custom/dev/dev-local-lab.yaml local-lab1-1 --from baseline

2回目はdirty_worktreeで即時ABORTしました。

feature worktreeには次の一時成果物が作成されていました。

.kaji-artifacts/baseline/baseline.json

このディレクトリがGitの無視対象ではなかったため、baselineのclean worktree gateに反しました。

.gitignoreへ次を追加しました。

.kaji-artifacts/

mainをfeature branchへマージし、WSL Gitでworktreeがcleanであることを確認しました。

3回目:baselineからCOMPLETE

もう一度--from baselineで再開すると、baselineは次の判定になりました。

status: PASS
baseline_status=clean
pytest_exit_code=0
failures=0

implementでは新しいテストを先に追加し、greetが存在しないためImportErrorになることを確認しました。その後、次の実装を追加しました。

src/starter_app/__init__.py
def greet(name: str) -> str:
    """Return a Japanese greeting for the given name.

    Raises:
        ValueError: If `name` is empty after stripping surrounding whitespace.
    """
    stripped = name.strip()
    if not stripped:
        raise ValueError("name must not be empty after stripping whitespace")
    return f"こんにちは、{stripped}さん"

追加したテストは3件です。

tests/test_smoke.py
@pytest.mark.small
def test_greet() -> None:
    assert greet("") == "こんにちは、海さん"


@pytest.mark.small
def test_greet_strips_surrounding_whitespace() -> None:
    assert greet("") == "こんにちは、海さん"


@pytest.mark.small
def test_greet_raises_on_blank_name() -> None:
    with pytest.raises(ValueError):
        greet("   ")

最終結果

uv run make checkの結果は次のとおりです。

ruff check: PASS
ruff format --check: PASS
mypy: PASS
workflow validation: PASS
pytest: 12 passed

review-codeでは次を独立確認しました。

  • pytest 12/12 PASS
  • baseline比較でregressions 0
  • Must Fix 0件
  • Should Fix 0件
  • 設計と実装の差異なし

最終的にfeature branchはmainへ非FFマージされ、local Issueはclosedになりました。feature worktreeとbranchも削除され、mainはlocal origin/mainへpushされました。

runごとの結果

run ID 結果 時間 停止・完了理由
260819102651 ABORT 219,041 ms pytest-xdist不足でbaseline invalid
260819103347 ABORT 1,168 ms .kaji-artifacts/によるdirty worktree
260819103421 COMPLETE 482,053 ms baseline以降の全stepがPASS

最初の実行開始から最終完了までは約15分55秒でした。providerログ上の推定コスト合計は約5.40 USDです。これは実際の請求額ではなく、Claude Sonnet、小さな課題1件、今回のworkflowに限った推定値です。

分かったこと

baseline gateは「テスト失敗0件」だけを見ていない

pytestの起動エラーを成功扱いせず、終了コード4とbaseline_status=invalidを理由に停止しました。変更前の状態を測定できていない場合に実装へ進まないことを確認できました。

step-level resumeが機能した

設計と設計レビューをやり直さず、--from baselineで停止地点から再開できました。

starterとworkflowの接続確認が必要

今回の組み合わせでは、pytest-xdist.kaji-artifacts/のignore設定を追加する必要がありました。導入時はworkflowが実行するコマンドと生成物を事前確認する必要があります。

制約と未検証事項

今回の結果は、本番環境や大規模repositoryでの安全性を証明するものではありません。

未検証事項は次のとおりです。

  • 外部I/Oを含む実装
  • GitHub Issue、Pull Request、branch protectionとの統合
  • 複数agentの同時実行と競合解決
  • timeout、rate limit、通信断からの回復
  • flaky test
  • secretsや顧客情報を含むrepository
  • 長時間実行時のコスト上限

また、make checkを仮想環境外から直接実行するとmypyが見つかりませんでした。今回の環境ではuv run make check、または仮想環境を有効化した状態で実行しています。

まとめ

kajiのlocal providerで、小さなissueを設計からマージ・クローズまで進められました。

特に確認できたのは、正常系の完走だけではありません。pytestを開始できない状態と、worktreeがdirtyな状態で理由付きABORTになり、修正後は同じbaseline stepから再開できました。

guarded loopを導入するときは、agentの性能だけでなく、次を事前に確認する必要があります。

  1. baselineが実際にテストを開始できるか
  2. workflowの生成物がGitをdirtyにしないか
  3. ABORT時のartifactが残るか
  4. 停止地点から再開できるか
  5. 最終結果を別のCIまたは人間が再確認できるか

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?