第 3 層のドリフト検知が、この設計の中心です。狙いは「Lint は通るが、実行の前半と後半で癖が変わった」ケースを拾うことです。たとえば早期リターン主体だった書き方が、途中からネストした if に変わる、といった変化です。
同じエージェント実行が触ったファイル群を対象に、簡単な指標を測って前半と後半で比較します。
# drift_probe.py — 実行内で書き癖がぶれた箇所を洗い出すimport re, subprocess, sysdef metrics(path: str) -> dict: src = open(path, encoding="utf-8").read() lines = src.splitlines() return { "early_return": len(re.findall(r"\breturn\b", src)), "nested_if": sum(1 for l in lines if re.match(r"\s{6,}if\b", l)), "snake_ident": len(re.findall(r"\b[a-z]+_[a-z]+\b", src)), "arrow_fn": len(re.findall(r"=>", src)), "loc": len(lines), }def normalize(m: dict) -> dict: loc = max(m["loc"], 1) return {k: round(v / loc, 4) for k, v in m.items() if k != "loc"}def changed_files() -> list[str]: out = subprocess.check_output( ["git", "diff", "--name-only", "HEAD~1", "HEAD"], text=True ) return [f for f in out.splitlines() if f.endswith((".ts", ".tsx"))]files = changed_files()half = len(files) // 2early = [normalize(metrics(f)) for f in files[:half]]late = [normalize(metrics(f)) for f in files[half:]]def avg(rows, key): return sum(r[key] for r in rows) / max(len(rows), 1)drift = {}for key in ("nested_if", "snake_ident", "arrow_fn"): a, b = avg(early, key), avg(late, key) if a and abs(b - a) / a > 0.35: # 35%以上の変化をドリフトと判定 drift[key] = (round(a, 3), round(b, 3))if drift: print("⚠️ style drift detected:", drift) sys.exit(1)print("✅ consistent within run")