自動修正を安全に統合する方法――許可リスト関所の実測と落とし穴

消耗品を描いたアイキャッチ画像

委任した自動修正は動いているのに、最後の取り込みだけが人の一覧に積もる。この詰まりに対して、同じ保管庫・統合待ち・許可した仕事名という三条件を満たす便だけを通す、小さな関所を置きました。

固定した5件を通すと、候補は2件だけです。さらに同じ候補でも、作業場所がcleanなら統合へ、編集中の変更が残っていれば退避せず次の巡回へ戻します。

自動修正を取り込む範囲を広げたい運用者は、何を候補にし、何を触らず見送るべきかを、この最小構成から判断できます。委任先の自己申告だけで終えず状態と成果物を確かめる考え方は、先に公開したAIエージェントの自己申告を検証する記録ともつながっています。

目次

候補を広く拾うほど、統合の判断は人へ戻ってきます

自動修正を委任しても、統合対象の確認を一件ずつ人が行えば、最後に同じ判断が滞留します。ここで必要だったのは「自動統合できる」と言い切ることではなく、任せられる対象を名前で狭く定義することでした。

選び方今回の扱い運用上の意味
仕事名が似ているものを拾う採用しない許可範囲が曖昧になる
同じ保管庫・統合待ち・許可した先頭文言採用する候補を名指しで限定できる
候補外の便を別状態へ動かす行わない関所が余計な状態変更をしない

候補探索は読み取り専用で行い、実際の状態遷移や履歴操作は既存の統合機構へ渡します。候補外を掃除する機械ではありません。狭く選び、選ばなかったものには触れない。そこが今回の設計判断です。

5件の入力から、条件を満たす2件だけが残りました

関所の条件と、固定fixtureでの選別結果を図1にまとめます。

入力判定に使う値結果
101lab-core / awaiting_integration / # Reader Eye repair候補
102lab-core / awaiting_integration / # Theme material supplement候補
103lab-core / awaiting_integration / # Unnamed repair許可外の仕事名で除外
104other-repo / awaiting_integration / # Reader Eye repair別保管庫で除外
105lab-core / completed / # Output Audit repair完了済みで除外
図1: 構成図: 固定fixture 5件のうち、三条件を満たす候補は101・102の2件

図1で重要なのは、103が「repair」という似た話に見えても通らないことです。また104は仕事名が許可対象でも保管庫が異なり、105は許可対象でも統合待ちではありません。三条件のどれか一つでも欠ければ、候補にしません。

許可リストは先頭一致のまま扱います

仕事名は部分一致ではなく、許可リストにある文言から始まるかで判定します。大文字小文字の正規化や曖昧な部分一致を足すと、名指しで許可した範囲が静かに広がります。今回の関所では、その便利さを採りませんでした。

観測した事実:固定fixture 5件から101・102の2件を選び、3件を除外しました。
解釈:保管庫・状態・先頭文言を分けて確認すると、候補の境界を読み取れます。
未確定:許可する仕事の種類を増やした時の適切な境界は、まだ定めていません。

dirtyな作業場所では、片付けずに繰り越します

候補に選ばれたことと、今すぐ統合してよいことは別です。編集中の変更が残る作業場所で自動退避して進めると、その変更を見えにくくします。この関所はそこで止まり、deferとして次の定期実行へ送ります。

注意:dirtyを検出した時に自動退避を加えると、編集中の変更を隠したまま統合を進める可能性があります。この最小実物は退避を行わず、次回へ繰り越す設計です。

標準ライブラリだけで関所の境界を再現します

図解: 候補選択と統合可否(5件入力 / 候補101 / clean / dirty / 統合可否)
図解: 候補選択と統合可否(AI生成)

Python 3.10以降と標準ライブラリのdataclassesだけで動き、ネットワーク、追加パッケージ、実運用のデータベースは不要です。次の内容をallowlisted_gate.pyとして保存して実行します。

#!/usr/bin/env python3
"""Offline miniature of an allowlisted integration gate."""

from __future__ import annotations

from dataclasses import dataclass


ALLOWED_GOAL_PREFIXES = (
    "# Reader Eye repair",
    "# Output Audit repair",
    "# Theme material supplement",
)


@dataclass(frozen=True, order=True)
class Task:
    task_id: int
    repo: str
    state: str
    goal: str


def candidates(tasks: list[Task]) -> list[Task]:
    """Select only named repair classes that are ready for integration."""
    return sorted(
        (
            task
            for task in tasks
            if task.repo == "lab-core"
            and task.state == "awaiting_integration"
            and task.goal.startswith(ALLOWED_GOAL_PREFIXES)
        )
    )


def decide(task: Task, *, checkout_clean: bool) -> str:
    """Defer a selected task when local edits make integration unsafe."""
    if not checkout_clean:
        return f"task={task.task_id} action=defer retry=next-tick reason=dirty-checkout"
    return f"task={task.task_id} action=integrate"


def main() -> None:
    fixture = [
        Task(101, "lab-core", "awaiting_integration", "# Reader Eye repair\nbody"),
        Task(102, "lab-core", "awaiting_integration", "# Theme material supplement\nbody"),
        Task(103, "lab-core", "awaiting_integration", "# Unnamed repair\nbody"),
        Task(104, "other-repo", "awaiting_integration", "# Reader Eye repair\nbody"),
        Task(105, "lab-core", "completed", "# Output Audit repair\nbody"),
    ]
    selected = candidates(fixture)
    ids = ",".join(str(task.task_id) for task in selected)
    print(f"selected={len(selected)} ids={ids} excluded={len(fixture) - len(selected)}")
    print("clean  " + decide(selected[0], checkout_clean=True))
    print("dirty  " + decide(selected[0], checkout_clean=False))


if __name__ == "__main__":
    main()

実行は次の一回で足ります。

$ python3 allowlisted_gate.py

2026年8月24日、Linux x86_64/Python 3.14.4のoffline task worktreeで得た出力は次のとおりでした。

selected=2 ids=101,102 excluded=3
clean  task=101 action=integrate
dirty  task=101 action=defer retry=next-tick reason=dirty-checkout

同じ101番に対し、cleanではintegrate、dirtyではdeferretry=next-tickが出ています。変化しない側、つまりdirty時に統合を続けないことが、この道具で確認したい動作です。

確認時点主要要素確認した値
2026年8月24日Python3.14.4
同日実行環境Linux x86_64、offline task worktree
同日fixture5件入力、候補2件、除外3件

観測した事実:候補101はcleanで統合、dirtyで次回へ繰り越しになりました。
解釈:候補選択と統合可否を分ければ、作業場所の変更を隠さずに自動処理を止められます。
未確定:dirty状態が解消されるまでの待ち時間や、繰り越し回数の上限はこの最小実物では扱いません。

実行記録では、成功・繰り越し・衝突が分かれました

既存の実行記録には、候補1件が成功1件で終わった回があります。一方で候補3件が編集中の変更のため全件skipになった回もあり、候補2件では成功1件と衝突1件に分かれました。成功だけを並べた説明ではありません。

衝突をここで自動的に解消することはしません。関所が担うのは、許可された候補を選び、危険な作業場所では進めないことまでです。衝突の中身を人が比較しやすくする部品は、別の問題として残っています。

広げる前に、見送る動作が残っているかを確認します

今は三種類の自動修正便がこの関所を通り、作業場所が空いている時だけ本線へ進みます。空いていない時に無理に片付けず、次の巡回へ戻すところまでが現物です。

次に作りたいのは許可対象をむやみに増やすことではなく、衝突した便を小さな比較カードにして、どこが重なったかをすぐ眺められる部品です。

まずは自分の統合候補を、保管庫・状態・名前の三条件で一度書き出してください。編集中の変更がある時に「何もしない」出口を残せないなら、自動統合の範囲はまだ広げないほうが安全です。

この検証を回している環境

この検証は、自宅の常設ラボ(使い捨てVM/LXCを回す母艦+GPU+VLAN分離ネットワーク)で動かしています。使っている機材と選定理由、全体構成は1本にまとめています。

ラボ構成のまとめを見る →
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次