Reading additional input from stdin...
OpenAI Codex v0.163.0
--------
workdir: /Users/shimazueiichi/r2r2r-scripts
model: gpt-6.1-sol
provider: openai
approval: never
sandbox: read-only
reasoning effort: none
reasoning summaries: none
session id: 01a1da87-96a1-7d20-8a44-a8e6fabb2f24
--------
user
依頼196のレビューをお願いします。pi05/build_correction_episodes.py に 1 コマごとの state の出どころ state_source (int8: 0 実測 / 1 動いた抜けを埋めた値 / 2 動かなかった抜けを埋めた値) を足しました。差分は /tmp/req196.diff。新しいファイル pi05/test_state_source.py と pi05/diag_fill_flags.py も見てください。条件: state/actions/t の値は 1 つも変えないこと、既存データセットは書き換えないこと、印は fill_motion_gaps が埋めたコマと一致すること。正しさの不具合を P1/P2/P3 で日本語で短く挙げてください。
codex
差分と追加ファイルを確認し、既存の補間処理と照合して、値の不変性と印の一致をレビューします。
exec
/bin/zsh -lc 'cat /tmp/req196.diff' in /Users/shimazueiichi/r2r2r-scripts
 succeeded in 0ms:
diff --git a/pi05/build_correction_episodes.py b/pi05/build_correction_episodes.py
index e5bce79..ae813b3 100644
--- a/pi05/build_correction_episodes.py
+++ b/pi05/build_correction_episodes.py
@@ -16,6 +16,7 @@
   - 使うのは 結果が「成功」(y) の run だけ
   - 時間軸は 10Hz. state は実測角の線形補間 + グリッパーは指令の時刻で 0/1 (build_demo_episodes と同じ)
     実測角の抜け (送信中) は --gap-fill で埋める (linear = corr_v1 / speed20 = corr_v2. 依頼 46)
+    埋めたコマには episode.npz の state_source で印を残す (0 実測 / 1 動いた抜け / 2 動かなかった抜け. 依頼 196)
   - 学習に使うのは状態が model / correct のステップだけ. rewind / home / wait (確認の入力待ち.
     人がブロックを置き直すと手が写る) / regrip (補正の R で開いてから閉じ終わるまで. 2026-10-30 依頼 35)
     のステップと,
@@ -176,6 +177,26 @@ def fill_motion_gaps(t, at, aq, mode="speed20", gap_sec=GAP_SEC, speed=SPEED20_D
     return q, gaps
 
 
+# ---- 1 コマごとの state の出どころ (state_source. int8. 2026-11-14 依頼 196) ----
+# 関節 6 列が実測角か, fill_motion_gaps が抜けを埋めた値かの印. 爪の列は指令なのでどれでも同じ
+SRC_MEASURED = 0     # 実測角 (0.25 秒以内の前後の実測角の線形補間. ふだんは 0.1 秒おき)
+SRC_FILLED_MOVE = 1  # 動いた抜け (前後の実測角の差 > GAP_MOVE_MIN_DEG) の中を埋めた値
+SRC_FILLED_STILL = 2  # 動かなかった抜け (差 <= GAP_MOVE_MIN_DEG. 爪の送信など) の中を埋めた値
+STATE_SOURCE_CODES = {SRC_MEASURED: "measured", SRC_FILLED_MOVE: "filled_motion_gap",
+                      SRC_FILLED_STILL: "filled_still_gap"}
+
+
+def gap_fill_flags(t, at, aq, gap_sec=GAP_SEC, min_move=GAP_MOVE_MIN_DEG):
+    """時刻 t のコマごとの state_source (int8). fill_motion_gaps と同じ「抜け」(実測角の間が gap_sec より長い
+    [ta, tb]) の中 (ta < t < tb) が埋めた値. 埋め方 (linear / speed20) によらず同じ印になる."""
+    src = np.zeros(len(t), dtype=np.int8)
+    for i in np.flatnonzero(np.diff(at) > gap_sec):
+        ta, tb = at[i], at[i + 1]
+        moved = float(np.max(np.abs(aq[i + 1] - aq[i]))) > min_move
+        src[(t > ta) & (t < tb)] = SRC_FILLED_MOVE if moved else SRC_FILLED_STILL
+    return src
+
+
 def logged_sends(log):
     """log.jsonl から, 送った目標とそのときの時刻の手がかりを集める.
 
@@ -443,9 +464,12 @@ def build_one(run_dir, out_dir, args):
         return {"run": stem, "used": False,
                 "reason": f"フレーム抽出不足 written={written} steps={len(tt)} (mp4 と frame_log の不一致)"}
 
+    state_source = gap_fill_flags(t, at, aq)[sel["idx"]]
     np.savez_compressed(out_dir / "episode.npz",
                         t=(np.arange(len(tt)) / FPS).astype(np.float64),
-                        t_orig=tt - tt[0], state=sel["state"], actions=sel["actions"])
+                        t_orig=tt - tt[0], state=sel["state"], actions=sel["actions"],
+                        state_source=state_source)
+    filled = int((state_source != SRC_MEASURED).sum())
     c = sel["counts"]
     meta = {
         "run_stem": stem, "task": bde.TASK, "fps": FPS, "steps": int(len(tt)),
@@ -475,6 +499,11 @@ def build_one(run_dir, out_dir, args):
         "gap_fill": {"mode": gap_fill, "gap_sec": GAP_SEC,
                      "speed_deg_per_sec": SPEED20_DEG_PER_SEC if gap_fill == "speed20" else None,
                      "gaps": len(gaps), **gap_match},
+        # 依頼 196: episode.npz の state_source (コマごとの印) の数
+        "filled_steps": filled,
+        "filled_motion_steps": int((state_source == SRC_FILLED_MOVE).sum()),
+        "filled_ratio": round(filled / len(tt), 4),
+        "state_source_codes": {str(k): v for k, v in STATE_SOURCE_CODES.items()},
         "max_step_deg": [round(float(v), 2) for v in
                          (np.abs(np.diff(sel["state"][:, :6], axis=0)).max(axis=0) if len(sel["state"]) > 1
                           else np.zeros(6))],
@@ -506,7 +535,8 @@ def check_compat(ours, ref):
     for ep in a:
         m = json.loads((ep / "meta.json").read_text())
         z = np.load(ep / "episode.npz")
-        if sorted(z.files) != sorted(rz.files):
+        # 依頼 196: state_source (コマごとの印) はお手本にないキー. 読む側は state / actions しか使わないので比べない
+        if sorted(set(z.files) - {"state_source"}) != sorted(set(rz.files) - {"state_source"}):
             probs.append(f"{ep.name}: npz のキー {sorted(z.files)} != お手本 {sorted(rz.files)}")
         for k in ("state", "actions"):
             if z[k].dtype != rz[k].dtype or z[k].shape[1:] != rz[k].shape[1:]:
@@ -554,6 +584,11 @@ def write_readme(ds, results, args):
         + ("前後の実測角をまっすぐ結ぶ (linear)" if args.gap_fill == "linear" else
            f"抜けの頭から速度 20 の速さ ({SPEED20_DEG_PER_SEC:.0f}°/秒. いちばん大きく動く関節) で前後の実測角を結ぶ"
            "直線を進み, 着いたら止まる (speed20)"),
+        "- 1 コマごとの state の出どころを episode.npz の `state_source` (int8. state と同じ長さ) に残す: "
+        "0 = 実測角 (0.25 秒以内の前後の実測角の線形補間) / 1 = 動いた抜け (前後の実測角の差が "
+        f"{GAP_MOVE_MIN_DEG}° を超える) の中を上の埋め方で埋めた値 / 2 = 動かなかった抜け (爪の送信など) の中を埋めた値。"
+        "印は関節 6 列のもの (爪の列はいつも指令)。actions の関節は次のコマの state なので, その出どころは次のコマの印。"
+        "meta.json の filled_steps (1 と 2 のコマの数) / filled_motion_steps (1 の数) / filled_ratio (全コマに対する 1 と 2 の割合)",
         "- 戻りきれなかった巻き戻しがある run は使わない (前後の姿勢が続かない)",
     ]
     if getattr(args, "max_protrude_px", None) is not None:

exec
/bin/zsh -lc 'cat pi05/test_state_source.py pi05/diag_fill_flags.py' in /Users/shimazueiichi/r2r2r-scripts
 succeeded in 0ms:
#!/usr/bin/env python3
"""test_state_source.py — 1 コマごとの state の出どころ (state_source. 依頼 196) の確かめ.

  python3 pi05/test_state_source.py -v

前半はファイルを読まない部分だけ. 後半 (TestSameAsBefore) は KIOXIA の runs/pi05_policy を読み,
直す前のコード (git の HEAD) と今のコードで /tmp に作り, state / actions / t がバイト単位で同じことを見る
(KIOXIA がなければ飛ばす. データセットにもアームにも触らない).
"""
import importlib.util
import json
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from types import SimpleNamespace

import numpy as np

_HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(_HERE))

import build_correction_episodes as bce  # noqa: E402


def samples(still=False):
    """0〜1 秒は 0.1 秒おき → 1.0〜2.6 秒は抜け (j1 が 6° 動く. still なら動かない) → 2.6〜3.6 秒は 0.1 秒おき."""
    a = np.round(np.arange(0.0, 1.01, 0.1), 3)
    b = np.round(np.arange(2.6, 3.61, 0.1), 3)
    at = np.concatenate([a, b])
    aq = np.zeros((len(at), 6))
    if not still:
        aq[len(a):, 1] = 6.0
    return at, aq


def runs_of_nonzero(src):
    nz = np.r_[0, (np.asarray(src) != 0).astype(int)]
    return int((np.diff(nz) == 1).sum())


class TestFlags(unittest.TestCase):
    def test_motion_gap_is_1(self):
        at, aq = samples()
        t = np.arange(0.0, 3.6, 0.1)
        src = bce.gap_fill_flags(t, at, aq)
        self.assertEqual(src.dtype, np.int8)
        inside = (t > 1.0) & (t < 2.6)
        self.assertTrue(np.all(src[inside] == bce.SRC_FILLED_MOVE))
        self.assertTrue(np.all(src[~inside] == bce.SRC_MEASURED))

    def test_still_gap_is_2(self):
        at, aq = samples(still=True)
        t = np.arange(0.0, 3.6, 0.1)
        src = bce.gap_fill_flags(t, at, aq)
        self.assertEqual(set(np.unique(src)), {bce.SRC_MEASURED, bce.SRC_FILLED_STILL})

    def test_flags_mark_exactly_what_speed20_changes(self):
        """speed20 が線形補間から変えたコマは, すべて印が 0 でないコマ."""
        at, aq = samples()
        t = np.arange(0.0, 3.6, 0.1)
        q_lin, _ = bce.fill_motion_gaps(t, at, aq, mode="linear")
        q_sp, _ = bce.fill_motion_gaps(t, at, aq, mode="speed20")
        src = bce.gap_fill_flags(t, at, aq)
        changed = np.any(np.abs(q_lin - q_sp) > 1e-9, axis=1)
        self.assertTrue(changed.any())
        self.assertTrue(np.all(src[changed] != bce.SRC_MEASURED))

    def test_no_gap_no_flag(self):
        at = np.round(np.arange(0.0, 3.01, 0.1), 3)
        aq = np.cumsum(np.ones((len(at), 6)), axis=0)
        self.assertEqual(int(bce.gap_fill_flags(np.arange(0.0, 3.0, 0.1), at, aq).sum()), 0)

    def test_compat_ignores_state_source(self):
        """お手本にない state_source のキーがあっても check_compat はキーの違いとして数えない."""
        with tempfile.TemporaryDirectory() as d:
            import cv2
            for name, extra in (("ours", True), ("ref", False)):
                ep = Path(d) / name / "e0"
                (ep / "frames").mkdir(parents=True)
                cv2.imwrite(str(ep / "frames" / "000000.jpg"), np.zeros((4, 6, 3), np.uint8))
                kw = {"state_source": np.zeros(1, np.int8)} if extra else {}
                np.savez_compressed(ep / "episode.npz", t=np.zeros(1), t_orig=np.zeros(1),
                                    state=np.zeros((1, 7), np.float32), actions=np.zeros((1, 7), np.float32), **kw)
                (ep / "meta.json").write_text(json.dumps({"fps": 10, "image_size": [6, 4], "state_dim": 7,
                                                          "state_names": [], "task": "x"}))
            self.assertEqual(bce.check_compat(Path(d) / "ours", Path(d) / "ref"), [])


def _old_module():
    """直す前の build_correction_episodes.py (git の HEAD) を別の名前で読み込む."""
    src = subprocess.run(["git", "show", "HEAD:pi05/build_correction_episodes.py"], cwd=_HERE,
                         capture_output=True, text=True, check=True).stdout
    tmp = Path(tempfile.mkdtemp()) / "bce_old.py"
    tmp.write_text(src)
    spec = importlib.util.spec_from_file_location("bce_old", tmp)
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod


# 補正の多い本・巻き戻しのある本など. 依頼 196 の時点で KIOXIA にあるもの
SAMPLE_RUNS = ("pi05_20261031_175527", "pi05_20261031_180435")


@unittest.skipUnless(Path("/Volumes/KIOXIA").is_mount() and bce.RUNS_ROOT.is_dir(), "KIOXIA がない")
class TestSameAsBefore(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.old = _old_module()

    def _build(self, mod, run, d, gap_fill, trim_hold):
        args = SimpleNamespace(gap_fill=gap_fill, trim_hold=trim_hold, no_trim_mid_idle=False,
                               max_protrude_px=None, force=True)
        out = Path(d) / mod.__name__ / f"{gap_fill}_{trim_hold}" / run
        res = mod.build_one(bce.RUNS_ROOT / run, out, args)
        self.assertTrue(res.get("used"), res)
        z = np.load(out / "episode.npz")
        return {k: z[k] for k in z.files}, json.loads((out / "meta.json").read_text())

    def test_bytes_and_counts(self):
        with tempfile.TemporaryDirectory() as d:
            for run in SAMPLE_RUNS:
                if not (bce.RUNS_ROOT / run).is_dir():
                    self.skipTest(f"{run} がない")
                for gap_fill, trim_hold in (("linear", False), ("speed20", False), ("speed20", True)):
                    with self.subTest(run=run, gap_fill=gap_fill, trim_hold=trim_hold):
                        a, _ = self._build(self.old, run, d, gap_fill, trim_hold)
                        b, meta = self._build(bce, run, d, gap_fill, trim_hold)
                        self.assertNotIn("state_source", a)
                        for k in ("t", "t_orig", "state", "actions"):
                            self.assertEqual(a[k].dtype, b[k].dtype, k)
                            self.assertEqual(a[k].tobytes(), b[k].tobytes(), k)
                        src = b["state_source"]
                        self.assertEqual(src.dtype, np.int8)
                        self.assertEqual(len(src), len(b["state"]))
                        self.assertTrue(set(np.unique(src)) <= set(bce.STATE_SOURCE_CODES))
                        gf = meta["gap_fill"]
                        filled = int((src != 0).sum())
                        self.assertEqual(meta["filled_steps"], filled)
                        self.assertEqual(meta["filled_motion_steps"], int((src == 1).sum()))
                        self.assertAlmostEqual(meta["filled_ratio"], filled / len(src), places=4)
                        # 印のつながりの数は抜けの数を超えない (1 つの抜けが印を 2 つに分けることはない)
                        self.assertLessEqual(runs_of_nonzero(src), gf["gaps"])
                        self.assertLessEqual(runs_of_nonzero(src == 1), gf["motion_gaps"])
                        if gf["gaps"] == 0:
                            self.assertEqual(filled, 0)


if __name__ == "__main__":
    unittest.main()
#!/usr/bin/env python3
"""diag_fill_flags.py — 既存の補正データセットで, 実測角の抜けを埋めたコマの割合を測る (2026-11-14 依頼 196).

datasets/ の名前に corr / deep_even10 が入るデータセットの, 補正エピソード (meta.json の source が pi05_policy)
ごとに, 元の runs/pi05_policy/{run_stem}/ から build_correction_episodes.build_one を meta.json と同じ条件
(gap_fill の mode・trim_mid_idle・trim_hold) で /tmp に作り直し, state_source (コマごとの印) を数える.
作り直した state / actions / t がデータセットの episode.npz と一致しない本は数えない (理由を CSV に残す).

データセット・元の記録は読むだけ (書かない). 書くのは /tmp と diag/fill_flags_20261114/ だけ.

  python3 pi05/diag_fill_flags.py
"""
import argparse
import csv
import json
import sys
from pathlib import Path
from types import SimpleNamespace

import numpy as np

_HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(_HERE))

import build_correction_episodes as bce  # noqa: E402
import build_demo_episodes as bde  # noqa: E402

OUT_NAME = "fill_flags_20261114"
TMP_ROOT = Path("/tmp") / OUT_NAME
# 学習に使ったことがあるデータセットを先に並べる (依頼 196)
TRAINED_FIRST = ("mecharm_redblock_box_teach_v1_trim2_corr_v5", "mecharm_redblock_box_deep_even10_v1",
                 "mecharm_redblock_box_teach_v1_trim2_corr_v5_deep")


def target_datasets(ds_root):
    names = sorted(p.name for p in ds_root.iterdir()
                   if p.is_dir() and not p.name.startswith(".") and ("corr" in p.name or "deep_even10" in p.name))
    return sorted(names, key=lambda n: (n not in TRAINED_FIRST, TRAINED_FIRST.index(n) if n in TRAINED_FIRST else 0, n))


def rebuild(run_stem, meta, cache):
    """meta.json と同じ条件で /tmp に作り直す. 戻り値: (npz の中身 dict / None, 理由)."""
    gf = meta.get("gap_fill") or {}
    key = (run_stem, gf.get("mode"), bool(meta.get("trim_mid_idle")), meta.get("trim_hold") is not None)
    if key in cache:
        return cache[key]
    out = TMP_ROOT / "rebuild" / "_".join(str(k) for k in key) / run_stem
    args = SimpleNamespace(gap_fill=gf.get("mode") or "linear", trim_hold=key[3],
                           no_trim_mid_idle=not key[2], max_protrude_px=None, force=True)
    res = bce.build_one(bce.RUNS_ROOT / run_stem, out, args)
    if not res.get("used"):
        cache[key] = (None, f"作り直せない: {res.get('reason')}")
    else:
        z = np.load(out / "episode.npz")
        cache[key] = ({k: z[k] for k in z.files}, "ok")
    return cache[key]


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--dataset", action="append", help="このデータセットだけ (複数可)")
    args = ap.parse_args()
    bde.ensure_ssd()
    ds_root = bde.DATA_ROOT / "datasets"
    out_dir = bde.DATA_ROOT / "diag" / OUT_NAME
    if out_dir.exists():
        sys.exit(f"[ABORT] 出力先がもうある: {out_dir}")
    names = args.dataset or target_datasets(ds_root)
    rows, cache = [], {}
    for name in names:
        for mp in sorted((ds_root / name / "raw_episodes").glob("[!.]*/meta.json")):
            meta = json.loads(mp.read_text())
            if not str(meta.get("source", "")).startswith("pi05_policy"):
                continue
            ep = mp.parent
            stem = meta.get("run_stem", ep.name)
            z = np.load(ep / "episode.npz")
            row = {"dataset": name, "episode": ep.name, "run_stem": stem,
                   "gap_fill": (meta.get("gap_fill") or {}).get("mode"),
                   "trim_hold": meta.get("trim_hold") is not None, "steps": len(z["state"]),
                   "filled_steps": "", "filled_motion_steps": "", "filled_ratio": "",
                   "gaps": (meta.get("gap_fill") or {}).get("gaps"),
                   "motion_gaps": (meta.get("gap_fill") or {}).get("motion_gaps"), "status": ""}
            new, why = rebuild(stem, meta, cache)
            if new is None:
                row["status"] = why
            elif not all(k in new and new[k].shape == z[k].shape and new[k].tobytes() == z[k].tobytes()
                         for k in ("state", "actions", "t")):
                row["status"] = "state が一致しない (数えない)"
            else:
                src = new["state_source"]
                n_f = int((src != bce.SRC_MEASURED).sum())
                row.update(filled_steps=n_f, filled_motion_steps=int((src == bce.SRC_FILLED_MOVE).sum()),
                           filled_ratio=round(n_f / len(src), 4), status="ok")
            rows.append(row)
            print(f"{name}  {ep.name}  {row['status']}  {row['filled_steps']}/{row['steps']}")

    out_dir.mkdir(parents=True)
    with (out_dir / "episodes.csv").open("w", newline="") as f:
        w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
        w.writeheader(); w.writerows(rows)
    summ = []
    for name in names:
        rs = [r for r in rows if r["dataset"] == name]
        ok = [r for r in rs if r["status"] == "ok"]
        steps = sum(r["steps"] for r in ok); filled = sum(r["filled_steps"] for r in ok)
        top = max(ok, key=lambda r: r["filled_ratio"]) if ok else None
        summ.append({"dataset": name, "corr_episodes": len(rs), "counted": len(ok),
                     "not_counted": len(rs) - len(ok), "steps": steps, "filled_steps": filled,
                     "filled_motion_steps": sum(r["filled_motion_steps"] for r in ok),
                     "filled_ratio": round(filled / steps, 4) if steps else "",
                     "max_episode": top["episode"] if top else "",
                     "max_episode_ratio": top["filled_ratio"] if top else ""})
    with (out_dir / "summary.csv").open("w", newline="") as f:
        w = csv.DictWriter(f, fieldnames=list(summ[0].keys()))
        w.writeheader(); w.writerows(summ)
    (out_dir / "README.md").write_text(
        "# fill_flags_20261114 (依頼 196)\n\n"
        "既存の補正データセットで, 実測角の抜けを埋めたコマ (state_source が 1 または 2) の数と割合。\n"
        "`python3 pi05/diag_fill_flags.py` で作った。データセットは読むだけ。作り直しは /tmp。\n\n"
        "- state_source: 0 = 実測角 / 1 = 動いた抜けを埋めた値 / 2 = 動かなかった抜けを埋めた値\n"
        "- filled_ratio = (1 と 2 のコマ) / 全コマ。filled_motion_steps は 1 のコマの数\n"
        "- status が ok でない本 (作り直した state / actions / t がデータセットと一致しない・作り直せない) は数えない\n"
        "- episodes.csv: 本ごと / summary.csv: データセットごと (max_episode = 割合がいちばん多い本)\n")
    print(f"→ {out_dir}")
    return 0


if __name__ == "__main__":
    sys.exit(main())

exec
/bin/zsh -lc "sed -n '65,180p' pi05/build_correction_episodes.py; sed -n '310,470p' pi05/build_correction_episodes.py; rg -n 'trim_mid_idle|trim_hold|sel\\[|idx|out_dir|force' pi05/build_correction_episodes.py" in /Users/shimazueiichi/r2r2r-scripts
 succeeded in 0ms:
# 角度を読み直す間 (約 1.6 秒. 動いている間は読めずにやり直す) 返ってこないので, その間は実測角が残らない.
# 腕はこの抜けの頭で動き出し, 抜けの終わりの実測角 (着いた姿勢) で止まっている.
#   linear  : 抜けの前後の実測角をまっすぐ結ぶ (corr_v1. 1.6 秒かけてゆっくり動いたことになる)
#   speed20 : 抜けの頭から, 速度 20 の速さで前後の実測角を結ぶ直線 (関節空間. 全関節が同時に着く) を進み,
#             着いたら抜けの終わりまで止まる. 速さは trim2 の元の記録 (run_pick_place 19 本. 速度 20 で
#             0.32 秒おきに動いている間も読めていた) の, 動いている途中の先頭の関節の速さ 128 区間の中央値 29.8°/秒
GAP_FILL_MODES = ("linear", "speed20")
GAP_SEC = 0.25                  # 実測角の間がこれより長いところを「抜け」とみなす (ふだんは 0.1 秒おき)
SPEED20_DEG_PER_SEC = 30.0      # 速度 20 で動いている間の先頭の関節の速さ (上の 29.8°/秒)
GAP_MOVE_MIN_DEG = 0.5          # 抜けの前後でこれより動いていれば「動いた抜け」として数える
# ---- 着いたあとの止まりの詰め方 (--trim-hold. 2026-10-31 依頼 48. corr_v3) ----
# 推論・補正では 1 回の send_angles ごとに daemon の読み直し (約 1.6 秒) を待つので, 中継点・補正の 1 刻みの
# たびに「動いて止まる」をくり返す. trim2 の決まり (止まりの区間ごとに先頭 2 コマ) では, 区間が 2〜3 コマ
# おきに来るため止まりが約 50% 残る (corr_v2). この止まりは送信の確かめを待つだけで, 動作の意味はない.
#   model の区間       : 残さない (中継点の動きだけをつなぐ)
#   correct の区間     : 先頭 1 コマ (補正のコマを減らしすぎない. 補正は 1 刻みが 1 コマで動き終わる)
#   爪の切り替えの直前・直後の区間 : trim2 と同じ先頭 2 コマ (爪が動き終わるのを待つ意味がある) + gripper_guard
HOLD_KEEP_MODEL = 0
HOLD_KEEP_CORRECT = 1
HOLD_KEEP_GRIP = bde.MID_IDLE_KEEP_STEPS


def _rmtree(path):
    """exFAT では本体を消すと macOS が ._ (AppleDouble) も一緒に消すため, 後から ._ を消そうとして
    FileNotFoundError になる. もう無いものは飛ばして消す."""
    def _skip_missing(func, p, exc):
        exc = exc[1] if isinstance(exc, tuple) else exc   # onerror (3.11 まで) は exc_info
        if not isinstance(exc, FileNotFoundError):
            raise exc
    if sys.version_info >= (3, 12):
        shutil.rmtree(path, onexc=_skip_missing)
    else:
        shutil.rmtree(path, onerror=_skip_missing)


def load_stage_times(path):
    """states.csv の stage 行 (状態の切り替わり) と, 各行の stage 列."""
    ts, modes = [], []
    with path.open(newline="") as f:
        for row in csv.DictReader(f):
            if row.get("event_type") != "stage":
                continue
            try:
                ts.append(float(row["ts"])); modes.append(row.get("stage") or MODE_MODEL)
            except Exception:
                continue
    return np.array(ts), modes


def load_log(path):
    out = []
    if not path.exists():
        return out
    for line in path.read_text().splitlines():
        line = line.strip()
        if line:
            try:
                out.append(json.loads(line))
            except Exception:
                continue
    return out


def idle_keep_mask(state, actions):
    """build_demo_episodes.build_one の「停止区間を詰める」(IDLE_*) と同じ決まり. 戻り値: (keep, 詰めた数)."""
    n = len(state)
    motion = np.max(np.abs(actions - state), axis=1)
    keep = np.ones(n, dtype=bool)
    i, trimmed = 0, 0
    while i < n:
        if motion[i] < bde.IDLE_EPS_DEG:
            j = i
            while j < n and motion[j] < bde.IDLE_EPS_DEG:
                j += 1
            if (j - i) / FPS > bde.IDLE_MAX_SEC:
                cut_from = i + int(bde.IDLE_KEEP_SEC * FPS)
                keep[cut_from:j] = False
                trimmed += j - cut_from
            i = j
        else:
            i += 1
    guard = bde.gripper_guard_mask(state)
    trimmed -= int((~keep & guard).sum())
    keep |= guard
    return keep, trimmed


def fill_motion_gaps(t, at, aq, mode="speed20", gap_sec=GAP_SEC, speed=SPEED20_DEG_PER_SEC):
    """10Hz の時刻 t の関節角を, 実測角 (at, aq) から作る. 実測角があるところは実測角の線形補間.

    実測角の間が gap_sec より長い「抜け」[ta, tb] だけ, mode で埋め方を変える:
      linear  : qa → qb を ta〜tb でまっすぐ結ぶ (np.interp と同じ)
      speed20 : ta から qa → qb の直線を, いちばん大きく動く関節が speed (°/秒) になる速さで進み,
                着いたら tb まで qb で止まる. 着くまでの時間が抜けより長いときは抜けいっぱいを使う (linear と同じ)
    戻り値: (q (len(t), 6), 抜けの一覧 [{ta, tb, move_deg, move_sec}])
    """
    if mode not in GAP_FILL_MODES:
        raise ValueError(f"gap fill mode: {mode}")
    q = np.stack([np.interp(t, at, aq[:, k]) for k in range(6)], axis=1)
    gaps = []
    for i in np.flatnonzero(np.diff(at) > gap_sec):
        ta, tb, qa, qb = at[i], at[i + 1], aq[i], aq[i + 1]
        d = float(np.max(np.abs(qb - qa)))
        dur = min(d / speed, tb - ta) if mode == "speed20" else tb - ta
        gaps.append({"ta": float(ta), "tb": float(tb), "move_deg": d, "move_sec": float(dur)})
        if mode == "linear":
            continue
        m = (t > ta) & (t < tb)
        if not m.any():
            continue
        frac = np.ones(int(m.sum())) if dur <= 0 else np.clip((t[m] - ta) / dur, 0.0, 1.0)
        q[m] = qa + frac[:, None] * (qb - qa)
    return q, gaps


# ---- 1 コマごとの state の出どころ (state_source. int8. 2026-11-14 依頼 196) ----

def select_steps(t, q, g, mode_ts, modes, windows, trim_mid_idle=True, trim_hold=False):
    """学習に使うステップを選び, state / actions を作る (ファイルを読まない部分. テストで使う).

    戻り値: dict(idx=元の t の添字, state, actions, modes, counts)
    """
    state_all = np.concatenate([q, g[:, None]], axis=1).astype(np.float32)
    keep, m_all = step_keep_mask(t, mode_ts, modes, windows)
    idx = np.flatnonzero(keep)
    counts = {"grid_steps": int(len(t)),
              "excluded_mode": int((~np.isin(m_all, (MODE_MODEL, MODE_CORRECT))).sum()),
              "excluded_window": int((np.isin(m_all, (MODE_MODEL, MODE_CORRECT)) & ~keep).sum())}
    if len(idx) == 0:
        return {"idx": idx, "state": state_all[:0], "actions": state_all[:0], "modes": [], "counts": counts}
    state = state_all[idx]
    # 除いたあとに残ったステップでつなぐ. actions は「つないだ並びでの次の state」
    actions = next_state_actions(state)
    k1, idle_trimmed = idle_keep_mask(state, actions)
    idx, state, actions = idx[k1], state[k1], actions[k1]
    counts["idle_trimmed"] = int(idle_trimmed)
    counts["steps_before_mid_trim"] = int(len(idx))
    mid = 0
    if trim_mid_idle and len(idx):
        if trim_hold:
            mk = hold_keep_mask(state, actions, [str(m_all[k]) for k in idx])
        else:
            mk = bde.mid_idle_keep_mask(state, actions)
        mid = int((~mk).sum())
        idx, state, actions = idx[mk], state[mk], actions[mk]
    # trim2 と同じ: 関節 6 列は残ったステップで作り直す. グリッパー列は元の (次の) 指令のまま
    actions = actions.copy()
    actions[:-1, :6] = state[1:, :6]
    actions[-1, :6] = state[-1, :6]
    counts["mid_idle_trimmed"] = mid
    sel_modes = [str(m_all[k]) for k in idx]
    counts["model_steps"] = sel_modes.count(MODE_MODEL)
    counts["correct_steps"] = sel_modes.count(MODE_CORRECT)
    return {"idx": idx, "state": state, "actions": actions, "modes": sel_modes, "counts": counts}


def build_one(run_dir, out_dir, args):
    stem = run_dir.name
    trial_p = run_dir / f"{stem}.json"
    rec = run_dir / "recording"
    if not trial_p.exists():
        return {"run": stem, "used": False, "reason": f"missing:{trial_p.name}"}
    trial = json.loads(trial_p.read_text())
    if trial.get("recording_enabled") is not True:
        return {"run": stem, "used": False, "reason": "no_recording"}
    if trial.get("outcome") != "success":
        return {"run": stem, "used": False, "reason": f"outcome={trial.get('outcome')}"}
    need = [rec / "states.csv", rec / "top.mp4", rec / "top_frame_log.csv"]
    missing = [p.name for p in need if not p.exists()]
    if missing:
        return {"run": stem, "used": False, "reason": "missing:" + ",".join(missing)}
    log = load_log(run_dir / "log.jsonl")
    unfinished = [r for r in log if r.get("type") == "rewind" and not r.get("finished", True)]
    unfinished += [r for r in log if r.get("type") == "correction" and r.get("action") == "cancel"
                   and not r.get("finished", True)]
    if unfinished:
        # 戻りきれなかった巻き戻しがあると, つないだ前後で姿勢が続かない
        return {"run": stem, "used": False, "reason": f"rewind_unfinished x{len(unfinished)}"}

    at, aq, gt, gs = bde.load_states(rec / "states.csv")
    if len(at) < bde.MIN_ACTUAL_SAMPLES:
        return {"run": stem, "used": False, "reason": f"actual 実測が少ない ({len(at)})"}
    order = np.argsort(at); at, aq = at[order], aq[order]
    at, aq = bde.drop_spikes(at, aq)
    try:
        tm = json.loads((rec / "top_meta.json").read_text())
    except Exception:
        tm = {}
    if tm.get("status") != "ok":
        return {"run": stem, "used": False, "reason": f"recording_status={tm.get('status')}"}
    fidx, fts = bde.load_frame_log(rec / "top_frame_log.csv")
    if len(fts) < FPS:
        return {"run": stem, "used": False, "reason": f"top フレームが少ない ({len(fts)})"}
    t0, t1 = max(fts[0], at[0]), min(fts[-1], at[-1])
    if t1 - t0 < 3.0:
        return {"run": stem, "used": False, "reason": "記録の重なりが短い"}
    t = np.arange(t0, t1, 1.0 / FPS)
    gap_fill = getattr(args, "gap_fill", "linear")
    q, gaps = fill_motion_gaps(t, at, aq, mode=gap_fill)
    for g in gaps:
        k = int(np.searchsorted(at, g["tb"]))
        g["qb"] = aq[k]
    gap_match = match_gaps_to_sends(gaps, log)
    g = np.zeros(len(t))
    if len(gt):
        go = np.argsort(gt); gt, gs = gt[go], gs[go]
        for i, ti in enumerate(t):
            k = np.searchsorted(gt, ti, side="right") - 1
            g[i] = gs[k] if k >= 0 else 0.0

    mode_ts, modes = load_stage_times(rec / "states.csv")
    windows = excluded_windows(log)
    trim_hold = getattr(args, "trim_hold", False)
    sel = select_steps(t, q, g, mode_ts, modes, windows, trim_mid_idle=not args.no_trim_mid_idle,
                       trim_hold=trim_hold)
    if len(sel["idx"]) < FPS:
        return {"run": stem, "used": False, "reason": f"使えるステップが少ない ({len(sel['idx'])})"}
    tt = t[sel["idx"]]

    # 依頼 117: つかみの深さで採否を決める (--max-protrude-px のときだけ. 既定では測らない)
    depth = None
    max_pr = getattr(args, "max_protrude_px", None)
    if max_pr is not None:
        import grasp_depth
        drop, why, depth = grasp_depth.judge(run_dir, max_pr,
                                             grasp_depth.first_close_label_time(tt, sel["state"][:, 6]))
        if drop:
            if out_dir.exists():
                why += " (前に作ったエピソードが残っている. 消していない)"
            return {"run": stem, "used": False, "reason": why, "grasp_depth": depth}

    # codex R1-4: 画像の候補も同じ決まりで絞る (除いた区間・rewind / home の画像を, 境界の
    # 最近傍として拾わない). 候補から選ぶので, 除いた時刻の画像は入らない
    fkeep, _ = step_keep_mask(fts, mode_ts, modes, windows)
    fidx, fts = fidx[fkeep], fts[fkeep]
    if len(fts) < 2:
        return {"run": stem, "used": False, "reason": "使える画像が少ない"}
    nearest = np.clip(np.searchsorted(fts, tt), 1, len(fts) - 1)
    left = fts[nearest - 1]; right = fts[nearest]
    nearest = np.where(np.abs(tt - left) <= np.abs(right - tt), nearest - 1, nearest)
    want = {}
    for step, n in enumerate(nearest):
        want.setdefault(int(fidx[n]), []).append(step)

    if out_dir.exists():
        if not args.force:
            return {"run": stem, "used": True, "reason": "既存 (スキップ・--force で再生成)",
                    "steps": int(len(tt))}
        _rmtree(out_dir)
    (out_dir / "frames").mkdir(parents=True)
    cap = cv2.VideoCapture(str(rec / "top.mp4"))
    fi, written, max_need, size = 0, 0, max(want), None
    while fi <= max_need:
        ok, frame = cap.read()
        if not ok:
            break
        if fi in want:
            h, w = frame.shape[:2]
            s = bde.IMG_LONG / max(h, w)
            small = cv2.resize(frame, (round(w * s), round(h * s)), interpolation=cv2.INTER_AREA)
            size = small.shape[1], small.shape[0]
            for step in want[fi]:
                # codex R3 P2: 保存に失敗したコマは数えない (欠けたエピソードを採用しない)
                if cv2.imwrite(str(out_dir / "frames" / f"{step:06d}.jpg"), small,
                               [cv2.IMWRITE_JPEG_QUALITY, bde.JPEG_QUALITY]):
                    written += 1
        fi += 1
    cap.release()
    if written != len(tt):
        _rmtree(out_dir)
        return {"run": stem, "used": False,
                "reason": f"フレーム抽出不足 written={written} steps={len(tt)} (mp4 と frame_log の不一致)"}

    state_source = gap_fill_flags(t, at, aq)[sel["idx"]]
    np.savez_compressed(out_dir / "episode.npz",
                        t=(np.arange(len(tt)) / FPS).astype(np.float64),
                        t_orig=tt - tt[0], state=sel["state"], actions=sel["actions"],
39:  python3 pi05/build_correction_episodes.py --run pi05_20261030_101500 --force
311:def select_steps(t, q, g, mode_ts, modes, windows, trim_mid_idle=True, trim_hold=False):
314:    戻り値: dict(idx=元の t の添字, state, actions, modes, counts)
318:    idx = np.flatnonzero(keep)
322:    if len(idx) == 0:
323:        return {"idx": idx, "state": state_all[:0], "actions": state_all[:0], "modes": [], "counts": counts}
324:    state = state_all[idx]
328:    idx, state, actions = idx[k1], state[k1], actions[k1]
330:    counts["steps_before_mid_trim"] = int(len(idx))
332:    if trim_mid_idle and len(idx):
333:        if trim_hold:
334:            mk = hold_keep_mask(state, actions, [str(m_all[k]) for k in idx])
338:        idx, state, actions = idx[mk], state[mk], actions[mk]
344:    sel_modes = [str(m_all[k]) for k in idx]
347:    return {"idx": idx, "state": state, "actions": actions, "modes": sel_modes, "counts": counts}
350:def build_one(run_dir, out_dir, args):
384:    fidx, fts = bde.load_frame_log(rec / "top_frame_log.csv")
406:    trim_hold = getattr(args, "trim_hold", False)
407:    sel = select_steps(t, q, g, mode_ts, modes, windows, trim_mid_idle=not args.no_trim_mid_idle,
408:                       trim_hold=trim_hold)
409:    if len(sel["idx"]) < FPS:
410:        return {"run": stem, "used": False, "reason": f"使えるステップが少ない ({len(sel['idx'])})"}
411:    tt = t[sel["idx"]]
419:                                             grasp_depth.first_close_label_time(tt, sel["state"][:, 6]))
421:            if out_dir.exists():
428:    fidx, fts = fidx[fkeep], fts[fkeep]
436:        want.setdefault(int(fidx[n]), []).append(step)
438:    if out_dir.exists():
439:        if not args.force:
440:            return {"run": stem, "used": True, "reason": "既存 (スキップ・--force で再生成)",
442:        _rmtree(out_dir)
443:    (out_dir / "frames").mkdir(parents=True)
457:                if cv2.imwrite(str(out_dir / "frames" / f"{step:06d}.jpg"), small,
463:        _rmtree(out_dir)
467:    state_source = gap_fill_flags(t, at, aq)[sel["idx"]]
468:    np.savez_compressed(out_dir / "episode.npz",
470:                        t_orig=tt - tt[0], state=sel["state"], actions=sel["actions"],
473:    c = sel["counts"]
478:        "trim_mid_idle": not args.no_trim_mid_idle,
479:        "trim_hold": ({"keep_model": HOLD_KEEP_MODEL, "keep_correct": HOLD_KEEP_CORRECT,
480:                       "keep_grip": HOLD_KEEP_GRIP} if trim_hold and not args.no_trim_mid_idle else None),
508:                         (np.abs(np.diff(sel["state"][:, :6], axis=0)).max(axis=0) if len(sel["state"]) > 1
510:        "step_modes": sel["modes"],
512:    (out_dir / "meta.json").write_text(json.dumps(meta, indent=2, ensure_ascii=False))
567:        + ("" if not args.no_trim_mid_idle else " --no-trim-mid-idle")
569:        + (" --trim-hold" if getattr(args, "trim_hold", False) else "")
580:        + ("" if not getattr(args, "trim_hold", False) else
630:    ap.add_argument("--force", action="store_true", help="既存エピソードを作り直す")
644:    if args.trim_hold and args.no_trim_mid_idle:

exec
/bin/zsh -lc 'PYTHONDONTWRITEBYTECODE=1 python3 -m unittest pi05.test_state_source.TestFlags.test_motion_gap_is_1 pi05.test_state_source.TestFlags.test_still_gap_is_2 pi05.test_state_source.TestFlags.test_flags_mark_exactly_what_speed20_changes pi05.test_state_source.TestFlags.test_no_gap_no_flag -v' in /Users/shimazueiichi/r2r2r-scripts
 succeeded in 2020ms:
python3: warning: confstr() failed with code 5: couldn't get path of DARWIN_USER_TEMP_DIR; using /tmp instead
python3: error: couldn't create cache file '/tmp/xcrun_db-5TP6ATAD' (errno=Operation not permitted)
python3: warning: confstr() failed with code 5: couldn't get path of DARWIN_USER_TEMP_DIR; using /tmp instead
python3: error: couldn't create cache file '/tmp/xcrun_db-IQ6Btzl5' (errno=Operation not permitted)
test_motion_gap_is_1 (pi05.test_state_source.TestFlags) ... ok
test_still_gap_is_2 (pi05.test_state_source.TestFlags) ... ok
test_flags_mark_exactly_what_speed20_changes (pi05.test_state_source.TestFlags)
speed20 が線形補間から変えたコマは, すべて印が 0 でないコマ. ... ok
test_no_gap_no_flag (pi05.test_state_source.TestFlags) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.002s

OK

exec
/bin/zsh -lc "nl -ba pi05/diag_fill_flags.py | sed -n '109,138p'; git status --short" in /Users/shimazueiichi/r2r2r-scripts
 succeeded in 0ms:
   109	                     "not_counted": len(rs) - len(ok), "steps": steps, "filled_steps": filled,
   110	                     "filled_motion_steps": sum(r["filled_motion_steps"] for r in ok),
   111	                     "filled_ratio": round(filled / steps, 4) if steps else "",
   112	                     "max_episode": top["episode"] if top else "",
   113	                     "max_episode_ratio": top["filled_ratio"] if top else ""})
   114	    with (out_dir / "summary.csv").open("w", newline="") as f:
   115	        w = csv.DictWriter(f, fieldnames=list(summ[0].keys()))
   116	        w.writeheader(); w.writerows(summ)
   117	    (out_dir / "README.md").write_text(
   118	        "# fill_flags_20261114 (依頼 196)\n\n"
   119	        "既存の補正データセットで, 実測角の抜けを埋めたコマ (state_source が 1 または 2) の数と割合。\n"
   120	        "`python3 pi05/diag_fill_flags.py` で作った。データセットは読むだけ。作り直しは /tmp。\n\n"
   121	        "- state_source: 0 = 実測角 / 1 = 動いた抜けを埋めた値 / 2 = 動かなかった抜けを埋めた値\n"
   122	        "- filled_ratio = (1 と 2 のコマ) / 全コマ。filled_motion_steps は 1 のコマの数\n"
   123	        "- status が ok でない本 (作り直した state / actions / t がデータセットと一致しない・作り直せない) は数えない\n"
   124	        "- episodes.csv: 本ごと / summary.csv: データセットごと (max_episode = 割合がいちばん多い本)\n")
   125	    print(f"→ {out_dir}")
   126	    return 0
   127	
   128	
   129	if __name__ == "__main__":
   130	    sys.exit(main())
git: warning: confstr() failed with code 5: couldn't get path of DARWIN_USER_TEMP_DIR; using /tmp instead
git: error: couldn't create cache file '/tmp/xcrun_db-2alXXSXW' (errno=Operation not permitted)
git: warning: confstr() failed with code 5: couldn't get path of DARWIN_USER_TEMP_DIR; using /tmp instead
git: error: couldn't create cache file '/tmp/xcrun_db-xU66tj9U' (errno=Operation not permitted)
 M dynamic_tracking_limits/diag/j4_probe.py
 M dynamic_tracking_limits/drag_teaching/waypoint_streaming_replay.py
 M dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_02.json
 M dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_03.json
 M dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_04.json
 M dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_06.json
 M dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_07.json
 M dynamic_tracking_limits/place/place_modules/06f_top_white_box_detect_v7.py
 M observe/emit.py
 M observe/static/index.html
 M pi05/DEEP_DEMOS.md
 M pi05/build_correction_episodes.py
 M pi05/policy_recorder.py
 M visual_servo_pick.py
?? AGENTS.md
?? DAY181_FIX_BENCH_MAC.md
?? DAY181_FIX_BENCH_MAC_SHELL.md
?? DAY181_FIX_DAEMON_STDOUT.md
?? DAY181_TASK_BENCH_MAC.md
?? analyze_rotation_log06.py
?? archive/
?? block_positions.csv
?? calibrate_homography.py.bak_DAY168_pre_unified
?? calibrate_visual_servo.py
?? calibrate_z_axis_DAY189.py
?? calibrate_z_axis_DAY191_descend.py
?? calibration_reference.md
?? calibration_visual_servo_20260428_184513.csv
?? calibration_visual_servo_20260428_190234.csv
?? calibration_visual_servo_20260428_190453.csv
?? calibration_visual_servo_20260428_191234.csv
?? calibration_z_axis_DAY189_20260505_184248.csv
?? calibration_z_axis_DAY189_20260505_184444.csv
?? calibration_z_axis_DAY189_20260505_185644.csv
?? calibration_z_axis_DAY191_descend_pose_20260506_192217.csv
?? calibration_z_axis_DAY191_descend_pose_20260621_173245.csv
?? calibration_z_axis_DAY191_descend_pose_20260621_173839.csv
?? calibration_z_axis_DAY191_descend_pose_20260621_174508.csv
?? camera_check.py
?? camera_judge.py
?? capture_top_frames.py
?? capture_top_frames_log06.py
?? capture_top_frames_log07.py
?? capture_top_frames_log08.py
?? capture_white_box_templates.py
?? check_camera.py
?? claude_code_reports/
?? close_gripper.py
?? collect_block_positions.py
?? debug_auto/
?? debug_calib/
?? debug_camera_orientation.py
?? debug_detection.png
?? debug_detection.py
?? debug_mat_corner/
?? debug_mat_corners.py
?? debug_noise_test/
?? debug_snapshots/
?? debug_visual_servo/
?? detect_block_position.py
?? detect_blue_markers.py
?? detect_cover_corners_log07.py
?? detect_cover_edges_log07.py
?? detect_on_mat.py
?? detection_noise_test.py
?? diag_stage2_send.py
?? docs/diff_20261019_items1-8.txt
?? docs/diff_20261019_minabort.txt
?? docs/diff_20261019_r10_gripper.txt
?? docs/diff_20261019_r10fix.txt
?? docs/diff_20261019_r11fix.txt
?? docs/diff_20261019_shoulderfix.txt
?? docs/investigation_20261019_stageC_daemon_latency.md
?? docs/procedure_20261019_stageC_record_teach_demo.md
?? docs/review_request_20261018_wpsr_grip_push_hold.md
?? docs/review_request_20261019_r11_mainline_alignment.md
?? docs/review_request_20261019_record_teach_demo.md
?? docs/review_request_20261101_servo_check.md
?? docs/review_request_20261101_servo_check_r2.md
?? docs/review_result_20261018_wpsr_grip_push_hold_codex.md
?? docs/review_result_20261019_r11_codex.md
?? docs/review_result_20261019_r11b_codex.md
?? docs/review_result_20261019_r12_codex.md
?? docs/review_result_20261019_r13_codex.md
?? docs/review_result_20261019_record_teach_demo_codex.md
?? docs/review_result_20261019_record_teach_demo_codex_r10.md
?? docs/review_result_20261019_record_teach_demo_codex_r10b.md
?? docs/review_result_20261019_record_teach_demo_codex_r2.md
?? docs/review_result_20261019_record_teach_demo_codex_r3.md
?? docs/review_result_20261019_record_teach_demo_codex_r4.md
?? docs/review_result_20261019_record_teach_demo_codex_r6.md
?? docs/review_result_20261019_record_teach_demo_codex_r7.md
?? docs/review_result_20261019_record_teach_demo_codex_r8.md
?? docs/review_result_20261019_record_teach_demo_codex_r9.md
?? docs/review_result_20261101_servo_check_codex.md
?? dryrun_check.py
?? dynamic_pick_correct_20260428_121924.csv
?? dynamic_pick_correct_20260428_124241.csv
?? dynamic_pick_verify_20260428_105412.csv
?? dynamic_pick_verify_auto_20260428_111229.csv
?? dynamic_pick_verify_auto_20260428_111649.csv
?? dynamic_pick_verify_auto_20260428_113234.csv
?? dynamic_tracking_limits/VLM_REPO_INVENTORY.md
?? dynamic_tracking_limits/diag/j4_home_only.py
?? dynamic_tracking_limits/diag/j4_probe.py.bak_DAY239_pre_oneway
?? dynamic_tracking_limits/diag/logs/
?? dynamic_tracking_limits/drag_teaching/_archive/
?? dynamic_tracking_limits/drag_teaching/check_teaching_pose.py
?? dynamic_tracking_limits/drag_teaching/grasp_lift_pick_then_place.py
?? dynamic_tracking_limits/drag_teaching/grasp_lift_test.py
?? dynamic_tracking_limits/drag_teaching/record_teach_demo.py
?? dynamic_tracking_limits/drag_teaching/record_teach_demo.py.bak_pre_req121_20261108
?? dynamic_tracking_limits/drag_teaching/record_teach_demo.py.bak_pre_req152_20261111
?? dynamic_tracking_limits/drag_teaching/record_teach_demo.py.bak_pre_req156_20261112
?? dynamic_tracking_limits/drag_teaching/replay_z_postures.py
?? dynamic_tracking_limits/drag_teaching/replay_z_postures.py.bak_DAY243_pre_none_safe
?? dynamic_tracking_limits/drag_teaching/replay_z_simple.py
?? dynamic_tracking_limits/logs/j0_sweep/_INDEX.md
?? dynamic_tracking_limits/logs/j0_sweep/j0_pick_sweep20_01.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_pick_sweep20_02.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_pick_sweep20_03.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_adopted/
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_01.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_05.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_08.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_09.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_10.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_pick_11.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_01.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_02.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_03.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_04.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_05.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_06.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_07.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_08.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_09.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_10.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_11.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_12.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_13.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_14.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_15.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_16.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_17.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_18.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_19.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_20.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_21.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_22.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_approach_place_23.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_classified/
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_01.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_02.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_03.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_04.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_05.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_06.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_07.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_08.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_09.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_10.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_11.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_12.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_13.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_14.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_15.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_16.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_17.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_18.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_19.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_20.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_21.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_22.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_23.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_24.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_25.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_simple_01.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_pick_simple_02.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_01.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_02.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_03.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_04.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_05.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_06.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_07.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_08.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_09.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_10.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_11.json
?? dynamic_tracking_limits/logs/j0_sweep/j0_sweep_place_12.json
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_side_1.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_side_2.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_side_3.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_side_4.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_side_5.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_top_1.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_top_2.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_top_3.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_top_4.png
?? dynamic_tracking_limits/logs/place_capture_20260521_150159_top_5.png
?? dynamic_tracking_limits/logs/run_pick_place/
?? dynamic_tracking_limits/logs/vlm_framing_test/
?? dynamic_tracking_limits/place/diag_scripts/
?? dynamic_tracking_limits/place/place_modules/30mm
?? dynamic_tracking_limits/place/place_modules/J0_SWEEP_HOWTO.md
?? dynamic_tracking_limits/place/place_modules/_archive/
?? dynamic_tracking_limits/place/place_modules/corr_map_server.py
?? dynamic_tracking_limits/place/place_modules/diag_place_z_move.py
?? dynamic_tracking_limits/place/place_modules/handover_pose_step.py
?? dynamic_tracking_limits/place/place_modules/j0_lookup.py
?? dynamic_tracking_limits/place/place_modules/j4_isolation_test.py
?? dynamic_tracking_limits/place/place_modules/place_j0_sweep_20_scan.py
?? dynamic_tracking_limits/place/place_modules/preview_place_delta.py
?? dynamic_tracking_limits/place/place_modules/recorder.py.bak_20261015_pre_top_camera
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_20261015_pre_record_demo
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_20261016_pre_freeze_height
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY239_pre_observe_emit
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY239_pre_revert_skip6
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY239_pre_stage7_tol
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_block_place
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_blockbox_emit
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_codex_fix
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_dry_skip_ledger
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_in_box
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_ledger
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_place_skip_unknown
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_regrasp_block_sync
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_stage_emit
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_swept_tol
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_tip_measure
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY241_pre_verdict_emit
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY242_pre_aggregate
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY242_pre_caps
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY242_pre_tip_panel
?? dynamic_tracking_limits/place/place_modules/run_pick_place.py.bak_DAY243_pre_lift_back_tol
?? dynamic_tracking_limits/place/place_modules/static_corr_map/
?? dynamic_tracking_limits/place/place_modules/teach_place_poses.py
?? dynamic_tracking_limits/place/place_modules/vlm_framing_test.py
?? "dynamic_tracking_limits/place/place_modules/\347\240\264\347\266\273\351\226\276\345\200\244"
?? dynamic_tracking_limits/place/scan_input_place_lowz_DAY281.csv
?? dynamic_tracking_limits/place/templates/
?? dynamic_tracking_limits/place/templates_old_rough/
?? dynamic_tracking_limits/place/templates_skewed/
?? evaluate_matches_log07.py
?? experiments/grip_hold_capture_diag.py
?? experiments/hand_view_check.py
?? experiments/place_hole_fill_in_DAY265.py
?? experiments/place_hole_map_DAY265.py
?? experiments/place_hole_map_v3_merge_DAY265.py
?? experiments/place_seed_clusters_DAY265.py
?? fk_calibration_result_20260428_101335.csv
?? fk_calibration_result_20260428_102305.csv
?? fk_scan_calibration.csv
?? fk_scan_calibration.py
?? fk_scan_lowerleft_dense.csv
?? fk_scan_motion_based.csv.bak_DAY154
?? fk_scan_motion_based.csv.bak_DAY155
?? fk_scan_motion_based.csv.bak_DAY170_pre_overwrite_20260425_151008
?? fresh_mode_bench_mac.py
?? fresh_mode_bench_pi.py
?? fresh_mode_daemon.py
?? fresh_mode_sweep_pi.py
?? generate_grid_angles.py
?? grid_angles.csv
?? gripper_check.py
?? homography.npy.bak_20260420_181438
?? homography.npy.bak_20260424_194547
?? interpolate_motion.py
?? j0_send_test.py
?? judge_frame.jpg
?? logs/20260417_1500.md
?? logs/20260417_summary.md
?? logs/20260417_verify_y_fix.md
?? logs/20260803_0006.md
?? logs/20260807_0757.md
?? logs/20260807_0801.md
?? logs/20260807_0808.md
?? logs/20260807_0831.md
?? logs/20260807_0836.md
?? logs/20260807_0840.md
?? logs/20260807_0859.md
?? logs/20260807_0939.md
?? logs/20260807_0959.md
?? logs/20260807_1013.md
?? logs/20260807_1034.md
?? logs/20260807_1055.md
?? logs/20260807_1224.md
?? logs/20261018_1901.md
?? logs/20261019_1139.md
?? logs/20261019_1826.md
?? logs/20261019_1852.md
?? logs/20261019_1930.md
?? logs/20261021_0839.md
?? logs/20261021_0912.md
?? logs/20261029_2026.md
?? logs/20261030_1415.md
?? logs/20261030_1750.md
?? logs/20261101_1100.md
?? logs/20261109_1250.md
?? logs/20261109_1325.md
?? logs/20261109_1600.md
?? logs/20261109_1800.md
?? logs/20261109_2120.md
?? logs/20261110_1740.md
?? logs/20261110_2105.md
?? logs/20261110_2155.md
?? logs/20261110_2240.md
?? logs/20261111_1140.md
?? logs/20261112_1219.md
?? logs/20261113_2200.md
?? logs/20261114_0446.md
?? logs/20261114_0743.md
?? logs/20261114_1916.md
?? logs/20261114_1928.md
?? logs/20261114_1940.md
?? logs/20261114_2052.md
?? logs/20261114_2240.md
?? logs/20261114_2245.md
?? logs/20261114_2250.md
?? logs/20261114_2300.md
?? logs/20261114_2308.md
?? logs/20261114_2313.md
?? logs/20261114_2315.md
?? logs/20261114_2320.md
?? logs/20261114_2324.md
?? logs/20261114_2329.md
?? logs/DAY153_approach_addition.md
?? logs/DAY153_camera_index_fix.md
?? logs/DAY153_detection_debug.md
?? logs/DAY153_detection_fix.md
?? logs/DAY153_implementation_summary.md
?? logs/DAY153_pre_summary.md
?? logs/DAY153_self_check.md
?? logs/DAY154_place_dynamic.md
?? logs/DAY154_scan_setup.md
?? logs/DAY154_scan_verify.md
?? logs/DAY158_rename_summary.md
?? logs/DAY169_tipbrep_reader_creation.log
?? logs/DAY169_verify_lookup.log
?? logs/calib_frames/
?? logs/debug_frames/
?? logs/debug_side_white_mask_20260519_194345.jpg
?? logs/debug_side_white_mask_20260519_194408.jpg
?? logs/debug_side_white_mask_20260519_194441.jpg
?? logs/debug_side_white_mask_20260519_194458.jpg
?? logs/debug_side_white_mask_20260519_194518.jpg
?? logs/debug_side_white_mask_20260519_194543.jpg
?? logs/debug_side_white_mask_20260519_194606.jpg
?? logs/debug_side_white_mask_20260519_194620.jpg
?? logs/debug_side_white_mask_20260519_194624.jpg
?? logs/debug_side_white_mask_20260519_194642.jpg
?? logs/debug_white_score_DAY209_20260519_190850.jpg
?? logs/debug_white_score_DAY209_20260519_190915.jpg
?? logs/debug_white_score_DAY209_20260519_190928.jpg
?? logs/debug_white_score_DAY209_20260519_190942.jpg
?? logs/debug_white_score_DAY209_20260519_190957.jpg
?? logs/debug_white_score_DAY209_20260519_191117.jpg
?? logs/debug_white_score_DAY209_20260519_191130.jpg
?? logs/debug_white_score_DAY209_20260519_191142.jpg
?? logs/debug_white_score_DAY209_20260519_191154.jpg
?? logs/debug_white_score_DAY209_20260519_191337.jpg
?? logs/debug_white_score_DAY209_20260519_191347.jpg
?? logs/debug_white_score_DAY209_20260519_191358.jpg
?? logs/debug_white_score_DAY209_20260519_191413.jpg
?? logs/debug_white_score_DAY209_20260519_191424.jpg
?? logs/debug_white_score_DAY209_20260519_191435.jpg
?? logs/debug_white_score_DAY209_20260519_192857.jpg
?? logs/debug_white_score_DAY209_20260519_193136.jpg
?? logs/debug_white_score_DAY209_20260519_193202.jpg
?? logs/debug_white_score_DAY209_20260519_193228.jpg
?? logs/debug_white_score_DAY209_20260519_193254.jpg
?? logs/debug_white_score_DAY209_20260519_193318.jpg
?? logs/debug_white_score_DAY209_20260519_193348.jpg
?? logs/descend_log_20260602_183303.csv
?? logs/descend_log_20260602_183726.csv
?? logs/descend_log_20260602_184021.csv
?? logs/descend_log_20260602_184121.csv
?? logs/descend_log_20260602_184405.csv
?? logs/descend_log_20260602_195623.csv
?? logs/descend_log_20260602_200931.csv
?? logs/descend_log_20260602_201627.csv
?? logs/descend_log_20260603_103022.csv
?? logs/descend_log_20260603_121403.csv
?? logs/descend_log_20260603_121543.csv
?? logs/descend_log_20260603_122414.csv
?? logs/descend_log_20260603_122519.csv
?? logs/descend_log_20260603_130502.csv
?? logs/descend_log_20260603_135148.csv
?? logs/descend_log_20260603_142211.csv
?? logs/descend_log_20260603_142550.csv
?? logs/descend_log_20260603_142711.csv
?? logs/descend_log_20260603_143427.csv
?? logs/descend_log_20260603_164819.csv
?? logs/descend_log_20260603_172047.csv
?? logs/descend_log_20260603_172141.csv
?? logs/descend_log_20260603_181621.csv
?? logs/descend_log_20260603_181833.csv
?? logs/descend_log_20260603_182100.csv
?? logs/descend_log_20260603_182254.csv
?? logs/descend_log_20260603_182424.csv
?? logs/descend_log_20260603_182542.csv
?? logs/descend_log_20260603_182700.csv
?? logs/descend_log_20260603_182816.csv
?? logs/descend_log_20260603_182939.csv
?? logs/descend_log_20260603_183131.csv
?? logs/descend_log_20260604_100440.csv
?? logs/descend_log_20260604_100604.csv
?? logs/descend_log_20260604_100753.csv
?? logs/descend_log_20260604_100924.csv
?? logs/descend_log_20260604_101105.csv
?? logs/descend_log_20260604_101232.csv
?? logs/descend_log_20260604_102108.csv
?? logs/descend_log_20260604_103759.csv
?? logs/descend_log_20260604_105959.csv
?? logs/descend_log_20260606_191135.csv
?? logs/descend_log_20260606_191454.csv
?? logs/descend_log_20260606_191620.csv
?? logs/descend_log_20260606_191746.csv
?? logs/descend_log_20260606_194001.csv
?? logs/descend_log_20260606_194546.csv
?? logs/descend_log_20260606_201548.csv
?? logs/descend_log_20260606_201857.csv
?? logs/descend_log_20260606_202158.csv
?? logs/descend_log_20260606_202542.csv
?? logs/descend_log_20260606_202803.csv
?? logs/descend_log_20260606_203035.csv
?? logs/descend_log_20260606_203358.csv
?? logs/detect_place_sample_01_box_right_far.jpg
?? logs/detect_place_sample_02_box_right_far_v2.jpg
?? logs/detect_place_sample_03_box_left_far.jpg
?? logs/detect_place_sample_04_box_left_close.jpg
?? logs/detect_place_sample_05_correct_position.jpg
?? logs/joint_debug.md
?? logs/link6_debug.md
?? logs/lookup_test_20260417_1834.txt
?? logs/place_align_log_20260603_122421.csv
?? logs/place_align_log_20260603_122531.csv
?? logs/place_align_log_20260603_142602.csv
?? logs/place_align_log_20260603_143441.csv
?? logs/place_release_trials.csv
?? logs/place_test_20260519_210457.csv
?? logs/place_test_20260519_210457.txt
?? logs/place_test_20260519_211301.csv
?? logs/place_test_20260519_211301.txt
?? logs/place_test_20260519_211511.csv
?? logs/place_test_20260519_211511.txt
?? logs/place_test_20260519_211550.csv
?? logs/place_test_20260519_211550.txt
?? logs/place_test_20260519_211633.csv
?? logs/place_test_20260519_211633.txt
?? logs/place_test_20260519_211639.csv
?? logs/place_test_20260519_211639.txt
?? logs/place_test_20260519_211905.csv
?? logs/place_test_20260519_211905.txt
?? logs/place_test_20260519_212654.csv
?? logs/place_test_20260519_212654.txt
?? logs/place_test_20260519_212812.csv
?? logs/place_test_20260519_212812.txt
?? logs/place_test_20260519_213204.csv
?? logs/place_test_20260519_213204.txt
?? logs/place_test_20260519_213323.csv
?? logs/place_test_20260519_213323.txt
?? logs/place_test_20260519_213543.csv
?? logs/place_test_20260519_213543.txt
?? logs/place_test_20260519_213619.csv
?? logs/place_test_20260519_213619.txt
?? logs/place_test_20260519_213703.csv
?? logs/place_test_20260519_213703.txt
?? logs/place_test_20260519_213923.csv
?? logs/place_test_20260519_213923.txt
?? logs/place_test_20260519_214020.csv
?? logs/place_test_20260519_214020.txt
?? logs/place_test_20260519_214200.csv
?? logs/place_test_20260519_214200.txt
?? logs/place_test_20260520_101000.csv
?? logs/place_test_20260520_101000.txt
?? logs/place_test_20260520_101102.csv
?? logs/place_test_20260520_101102.txt
?? logs/place_test_20260520_101215.csv
?? logs/place_test_20260520_101215.txt
?? logs/place_test_20260520_101247.csv
?? logs/place_test_20260520_101247.txt
?? logs/place_test_20260520_103914.csv
?? logs/place_test_20260520_103914.txt
?? logs/place_test_20260520_103928.csv
?? logs/place_test_20260520_103928.txt
?? logs/place_test_20260520_104457.csv
?? logs/place_test_20260520_104457.txt
?? logs/place_test_20260520_105102.csv
?? logs/place_test_20260520_105102.txt
?? logs/place_test_20260520_105417.csv
?? logs/place_test_20260520_105417.txt
?? logs/place_test_20260520_105427.csv
?? logs/place_test_20260520_105427.txt
?? logs/place_test_20260520_105514.csv
?? logs/place_test_20260520_105514.txt
?? logs/place_test_20260520_105530.csv
?? logs/place_test_20260520_105530.txt
?? logs/place_test_20260520_105825.csv
?? logs/place_test_20260520_105825.txt
?? logs/place_test_20260520_110424.csv
?? logs/place_test_20260520_110424.txt
?? logs/place_test_20260520_110439.csv
?? logs/place_test_20260520_110439.txt
?? logs/place_test_20260520_110731.csv
?? logs/place_test_20260520_110731.txt
?? logs/place_test_20260520_111024.csv
?? logs/place_test_20260520_111024.txt
?? logs/place_test_20260520_111106.csv
?? logs/place_test_20260520_111106.txt
?? logs/place_test_20260520_111905.csv
?? logs/place_test_20260520_111905.txt
?? logs/place_test_20260520_111923.csv
?? logs/place_test_20260520_111923.txt
?? logs/place_test_20260520_112204.csv
?? logs/place_test_20260520_112204.txt
?? logs/place_test_20260520_112249.csv
?? logs/place_test_20260520_112249.txt
?? logs/place_test_20260520_112305.csv
?? logs/place_test_20260520_112305.txt
?? logs/place_test_20260520_112320.csv
?? logs/place_test_20260520_112320.txt
?? logs/place_test_20260520_112610.csv
?? logs/place_test_20260520_112610.txt
?? logs/place_test_20260520_112626.csv
?? logs/place_test_20260520_112626.txt
?? logs/place_test_20260520_112709.csv
?? logs/place_test_20260520_112709.txt
?? logs/place_test_20260520_112751.csv
?? logs/place_test_20260520_112751.txt
?? logs/place_test_20260520_112835.csv
?? logs/place_test_20260520_112835.txt
?? logs/place_test_20260520_113311.csv
?? logs/place_test_20260520_113311.txt
?? logs/place_test_20260520_113311_cleanup_home_side.png
?? logs/place_test_20260520_113311_cleanup_home_top.png
?? logs/place_test_20260520_113330.csv
?? logs/place_test_20260520_113330.txt
?? logs/place_test_20260520_113330_cleanup_home_side.png
?? logs/place_test_20260520_113330_cleanup_home_top.png
?? logs/place_test_20260520_113330_phase0_home_side.png
?? logs/place_test_20260520_113330_phase0_home_top.png
?? logs/place_test_20260520_113330_phase2_approach_side.png
?? logs/place_test_20260520_113330_phase2_approach_top.png
?? logs/place_test_20260520_113716.csv
?? logs/place_test_20260520_113716.txt
?? logs/place_test_20260520_113716_cleanup_home_side.png
?? logs/place_test_20260520_113716_cleanup_home_top.png
?? logs/place_test_20260520_113716_phase0_home_side.png
?? logs/place_test_20260520_113716_phase0_home_top.png
?? logs/place_test_20260520_113716_phase2_approach_side.png
?? logs/place_test_20260520_113716_phase2_approach_top.png
?? logs/place_test_20260520_113835.csv
?? logs/place_test_20260520_113835.txt
?? logs/place_test_20260520_113835_cleanup_home_side.png
?? logs/place_test_20260520_113835_cleanup_home_top.png
?? logs/place_test_20260520_113835_phase0_home_side.png
?? logs/place_test_20260520_113835_phase0_home_top.png
?? logs/place_test_20260520_113835_phase2_approach_side.png
?? logs/place_test_20260520_113835_phase2_approach_top.png
?? logs/place_test_20260520_114124.csv
?? logs/place_test_20260520_114124.txt
?? logs/place_test_20260520_114124_cleanup_home_side.png
?? logs/place_test_20260520_114124_cleanup_home_top.png
?? logs/place_test_20260520_114141.csv
?? logs/place_test_20260520_114141.txt
?? logs/place_test_20260520_114141_cleanup_home_side.png
?? logs/place_test_20260520_114141_cleanup_home_top.png
?? logs/place_test_20260520_114141_phase0_home_side.png
?? logs/place_test_20260520_114141_phase0_home_top.png
?? logs/place_test_20260520_114141_phase2_approach_side.png
?? logs/place_test_20260520_114141_phase2_approach_top.png
?? logs/place_test_20260520_114449.csv
?? logs/place_test_20260520_114449.txt
?? logs/place_test_20260520_114449_cleanup_home_side.png
?? logs/place_test_20260520_114449_cleanup_home_top.png
?? logs/place_test_20260520_114507.csv
?? logs/place_test_20260520_114507.txt
?? logs/place_test_20260520_114507_cleanup_home_side.png
?? logs/place_test_20260520_114507_cleanup_home_top.png
?? logs/place_test_20260520_114507_phase0_home_side.png
?? logs/place_test_20260520_114507_phase0_home_top.png
?? logs/place_test_20260520_114507_phase2_approach_side.png
?? logs/place_test_20260520_114507_phase2_approach_top.png
?? logs/place_test_20260520_114634.csv
?? logs/place_test_20260520_114634.txt
?? logs/place_test_20260520_114634_cleanup_home_side.png
?? logs/place_test_20260520_114634_cleanup_home_top.png
?? logs/place_test_20260520_114650.csv
?? logs/place_test_20260520_114650.txt
?? logs/place_test_20260520_114650_cleanup_home_side.png
?? logs/place_test_20260520_114650_cleanup_home_top.png
?? logs/place_test_20260520_114650_phase0_home_side.png
?? logs/place_test_20260520_114650_phase0_home_top.png
?? logs/place_test_20260520_114650_phase2_approach_side.png
?? logs/place_test_20260520_114650_phase2_approach_top.png
?? logs/place_test_20260520_114947.csv
?? logs/place_test_20260520_114947.txt
?? logs/place_test_20260520_114947_cleanup_home_side.png
?? logs/place_test_20260520_114947_cleanup_home_top.png
?? logs/place_test_20260520_115003.csv
?? logs/place_test_20260520_115003.txt
?? logs/place_test_20260520_115003_cleanup_home_side.png
?? logs/place_test_20260520_115003_cleanup_home_top.png
?? logs/place_test_20260520_115003_phase0_home_side.png
?? logs/place_test_20260520_115003_phase0_home_top.png
?? logs/place_test_20260520_115003_phase2_approach_side.png
?? logs/place_test_20260520_115003_phase2_approach_top.png
?? logs/place_test_20260520_115124.csv
?? logs/place_test_20260520_115124.txt
?? logs/place_test_20260520_115124_cleanup_home_side.png
?? logs/place_test_20260520_115124_cleanup_home_top.png
?? logs/place_test_20260520_115140.csv
?? logs/place_test_20260520_115140.txt
?? logs/place_test_20260520_115140_cleanup_home_side.png
?? logs/place_test_20260520_115140_cleanup_home_top.png
?? logs/place_test_20260520_115214.csv
?? logs/place_test_20260520_115214.txt
?? logs/place_test_20260520_115214_cleanup_home_side.png
?? logs/place_test_20260520_115214_cleanup_home_top.png
?? logs/place_test_20260520_115308.csv
?? logs/place_test_20260520_115308.txt
?? logs/place_test_20260520_115308_cleanup_home_side.png
?? logs/place_test_20260520_115308_cleanup_home_top.png
?? logs/place_test_20260520_115325.csv
?? logs/place_test_20260520_115325.txt
?? logs/place_test_20260520_115325_cleanup_home_side.png
?? logs/place_test_20260520_115325_cleanup_home_top.png
?? logs/place_test_20260520_115325_phase0_home_side.png
?? logs/place_test_20260520_115325_phase0_home_top.png
?? logs/place_test_20260520_115325_phase2_approach_side.png
?? logs/place_test_20260520_115325_phase2_approach_top.png
?? logs/place_test_20260520_115413.csv
?? logs/place_test_20260520_115413.txt
?? logs/place_test_20260520_115413_cleanup_home_side.png
?? logs/place_test_20260520_115413_cleanup_home_top.png
?? logs/place_test_20260520_115429.csv
?? logs/place_test_20260520_115429.txt
?? logs/place_test_20260520_115429_cleanup_home_side.png
?? logs/place_test_20260520_115429_cleanup_home_top.png
?? logs/place_test_20260520_115429_phase0_home_side.png
?? logs/place_test_20260520_115429_phase0_home_top.png
?? logs/place_test_20260520_115429_phase2_approach_side.png
?? logs/place_test_20260520_115429_phase2_approach_top.png
?? logs/place_test_20260520_115823.csv
?? logs/place_test_20260520_115823.txt
?? logs/place_test_20260520_115823_cleanup_home_side.png
?? logs/place_test_20260520_115823_cleanup_home_top.png
?? logs/place_test_20260520_115840.csv
?? logs/place_test_20260520_115840.txt
?? logs/place_test_20260520_115840_cleanup_home_side.png
?? logs/place_test_20260520_115840_cleanup_home_top.png
?? logs/place_test_20260520_115840_phase0_home_side.png
?? logs/place_test_20260520_115840_phase0_home_top.png
?? logs/place_test_20260520_115840_phase2_approach_side.png
?? logs/place_test_20260520_115840_phase2_approach_top.png
?? logs/place_test_20260520_115929.csv
?? logs/place_test_20260520_115929.txt
?? logs/place_test_20260520_115929_cleanup_home_side.png
?? logs/place_test_20260520_115929_cleanup_home_top.png
?? logs/place_test_20260520_115929_phase0_home_side.png
?? logs/place_test_20260520_115929_phase0_home_top.png
?? logs/place_test_20260520_115929_phase2_approach_side.png
?? logs/place_test_20260520_115929_phase2_approach_top.png
?? logs/place_test_20260520_120021.csv
?? logs/place_test_20260520_120021.txt
?? logs/place_test_20260520_120021_cleanup_home_side.png
?? logs/place_test_20260520_120021_cleanup_home_top.png
?? logs/place_test_20260520_120021_phase0_home_side.png
?? logs/place_test_20260520_120021_phase0_home_top.png
?? logs/place_test_20260520_120021_phase2_approach_side.png
?? logs/place_test_20260520_120021_phase2_approach_top.png
?? logs/place_test_20260520_120319.csv
?? logs/place_test_20260520_120319.txt
?? logs/place_test_20260520_120319_cleanup_home_side.png
?? logs/place_test_20260520_120319_cleanup_home_top.png
?? logs/place_test_20260520_120319_phase0_home_side.png
?? logs/place_test_20260520_120319_phase0_home_top.png
?? logs/place_test_20260520_120319_phase2_approach_side.png
?? logs/place_test_20260520_120319_phase2_approach_top.png
?? logs/place_test_20260520_120939.csv
?? logs/place_test_20260520_120939.txt
?? logs/place_test_20260520_120939_cleanup_home_side.png
?? logs/place_test_20260520_120939_cleanup_home_top.png
?? logs/place_test_20260520_120939_phase0_home_side.png
?? logs/place_test_20260520_120939_phase0_home_top.png
?? logs/place_test_20260520_120939_phase2_approach_side.png
?? logs/place_test_20260520_120939_phase2_approach_top.png
?? logs/place_test_20260520_121820.csv
?? logs/place_test_20260520_121820.txt
?? logs/place_test_20260520_121820_cleanup_home_side.png
?? logs/place_test_20260520_121820_cleanup_home_top.png
?? logs/place_test_20260520_121820_phase0_home_side.png
?? logs/place_test_20260520_121820_phase0_home_top.png
?? logs/place_test_20260520_121820_phase2_approach_side.png
?? logs/place_test_20260520_121820_phase2_approach_top.png
?? logs/place_test_20260520_121906.csv
?? logs/place_test_20260520_121906.txt
?? logs/place_test_20260520_121906_cleanup_home_side.png
?? logs/place_test_20260520_121906_cleanup_home_top.png
?? logs/place_test_20260520_121906_phase0_home_side.png
?? logs/place_test_20260520_121906_phase0_home_top.png
?? logs/place_test_20260520_121906_phase2_approach_side.png
?? logs/place_test_20260520_121906_phase2_approach_top.png
?? logs/place_test_20260520_143701.csv
?? logs/place_test_20260520_143701.txt
?? logs/place_test_20260520_143701_phase0_home_side.png
?? logs/place_test_20260520_143701_phase0_home_top.png
?? logs/place_test_20260520_143747.csv
?? logs/place_test_20260520_143747.txt
?? logs/place_trials.csv
?? logs/req186/
?? logs/shots_187/
?? logs/template_learning.log
?? logs/variation_comparison.md
?? logs/verdict_seed_ai_20261114.txt
?? logs/verdict_viewer_194_panel.png
?? logs/verdict_viewer_server.log
?? logs/xyz_track_test_20260518_193810.csv
?? logs/xyz_track_test_20260518_195725.csv
?? logs/xyz_track_test_20260518_200435.csv
?? logs/xyz_track_test_20260518_201641.csv
?? logs/xyz_track_test_20260518_202130.csv
?? logs/xyz_track_test_20260518_202831.csv
?? logs/xyz_track_test_20260518_203221.csv
?? logs/xyz_track_test_20260518_203408.csv
?? logs/xyz_track_test_20260518_204420.csv
?? logs/xyz_track_test_20260518_204557.csv
?? logs/xyz_track_test_20260518_205032.csv
?? logs/xyz_track_test_20260518_205427.csv
?? logs/xyz_track_test_20260518_205616.csv
?? logs/xyz_track_test_20260518_210647.csv
?? logs/xyz_track_test_20260518_211232.csv
?? logs/xyz_track_test_20260518_211353.csv
?? logs/xyz_track_test_20260518_211544.csv
?? logs/xyz_track_test_20260518_211703.csv
?? logs/xyz_track_test_20260518_211939.csv
?? logs/xyz_track_test_20260518_212236.csv
?? logs/xyz_track_test_20260518_212335.csv
?? logs/xyz_track_test_20260518_212447.csv
?? logs/xyz_track_test_20260518_212744.csv
?? logs/xyz_track_test_20260518_213019.csv
?? logs/xyz_track_test_20260518_213513.csv
?? logs/xyz_track_test_20260518_213656.csv
?? logs/xyz_track_test_20260518_213811.csv
?? logs/xyz_track_test_20260518_214452.csv
?? logs/xyz_track_test_20260518_214651.csv
?? logs/xyz_track_test_20260518_214851.csv
?? logs/xyz_track_test_20260518_215212.csv
?? logs/xyz_track_test_20260518_215532.csv
?? logs/xyz_track_test_20260518_221313.csv
?? logs/y_calibration_20260516_200819.csv
?? logs/y_calibration_20260516_201220.csv
?? logs/y_calibration_20260516_201452.csv
?? logs/y_calibration_20260516_201634.csv
?? logs/y_calibration_20260516_202140.csv
?? logs/y_calibration_low_20260516_203456.csv
?? logs/y_calibration_mid_20260516_204525.csv
?? mat_corner_detection_result.png
?? mat_rectified.png
?? mat_rectify.py
?? match_features_log07.py
?? measure_baseline_log08.py
?? measure_contours_log06.py
?? measure_rectified_log08.py
?? mecharm_daemon.py.QUARANTINE_send_coords_DAY287
?? mecharm_daemon.py.bak_pre_coords_20260711_213944
?? mecharm_daemon.py.pi_candidate_20261019_LOG352
?? mecharm_daemon.py.pi_candidate_v2_20261019_LOG352
?? mecharm_daemon.py.pi_deployed_20261019_LOG352
?? motion_completion_pi.py
?? move_to_home.py
?? move_to_pick.py
?? noise_test_20260502_125230.csv
?? noise_test_20260502_130537.csv
?? noise_test_20260502_130707.csv
?? noise_test_20260502_131309.csv
?? noise_test_20260502_131747.csv
?? noise_test_20260502_132515.csv
?? noise_test_20260502_132743.csv
?? observe/emit.py.bak_DAY239_pre_nonblocking
?? observe/emit.py.bak_DAY241_pre_block_place
?? observe/emit.py.bak_DAY241_pre_blockbox
?? observe/emit.py.bak_DAY241_pre_in_box
?? observe/emit.py.bak_DAY242_pre_caps
?? observe/emit.py.bak_DAY242_pre_tip_panel
?? observe/observe_server.py
?? observe/static/index.html.bak_DAY239_pre_glance_drilldown
?? observe/static/index.html.bak_DAY241_pre_arm_full_joints
?? observe/static/index.html.bak_DAY241_pre_dashboard_expand
?? observe/static/index.html.bak_DAY241_pre_gripper_coords
?? observe/static/index.html.bak_DAY241_pre_gripper_debounce
?? observe/static/index.html.bak_DAY241_pre_history
?? observe/static/index.html.bak_DAY241_pre_idle
?? observe/static/index.html.bak_DAY241_pre_inbox_latch
?? observe/static/index.html.bak_DAY241_pre_joints_glance
?? observe/static/index.html.bak_DAY241_pre_phase_latch
?? observe/static/index.html.bak_DAY241_pre_remove_arm
?? observe/static/index.html.bak_DAY241_pre_urdf_fk
?? observe/static/index.html.bak_DAY242_pre_caps
?? observe/static/index.html.bak_DAY242_pre_tip_panel
?? observe/static/index.html.bak_DAY247_pre_scrollfix
?? observe/static/index.html.bak_DAY247_pre_sidebarstretch
?? observe/static/index_pre_sidebar_20260620.html
?? observe/telemetry_probe.py
?? pi05/POWER_AB.md
?? pi05/build_deep_even10_v2.sh
?? pi05/build_demo_episodes.py.bak_pre_LOG352_20261019
?? pi05/diag_fill_flags.py
?? pi05/pos5_lift_compare.py
?? pi05/power_ab_compare.py
?? pi05/test_build_deep_even10_v2.py
?? pi05/test_pos5_lift_compare.py
?? pi05/test_power_ab.py
?? pi05/test_state_source.py
?? "pi05/verdict_viewer/\345\210\244\345\256\232\343\201\256\347\224\273\351\235\242\343\202\222\351\226\213\343\201\217.command"
?? pick_and_place_fixed.py
?? pick_cover_corners_log07.py
?? pick_sheet_corners_log08.py
?? pipeline_timeline.html
?? place_test.py
?? place_test.py.bak
?? place_test.py.bak2
?? place_test.py.bak3
?? positions_clean.json
?? positions_now.json
?? probe_idle_send.py
?? probe_wb.py
?? r2r2r_dryrun.py
?? r2r2r_loop.py.bak_DAY159_pre_2point
?? r2r2r_loop.py.bak_DAY159_pre_dynamic
?? r2r2r_loop.py.bak_DAY159_pre_phaseC
?? r2r2r_loop.py.bak_DAY166_pre_homography
?? r2r2r_loop.py.bak_DAY166_pre_template_restore
?? r2r2r_loop.py.bak_DAY166_pre_whitehsv
?? r2r2r_loop.py.bak_DAY168_pre_camera_autodetect
?? r2r2r_loop_v2.py
?? ransac_only_log07.py
?? recalibrate_homography.py.bak_DAY168_pre_index_fix
?? recalibrate_homography.py.bak_DAY168_pre_unified
?? recalibrate_homography.py.sed_bak
?? rectify_sheet_log08.py
?? rectify_sheets_log06.py
?? results/
?? run_pick_place.sh
?? sample_images/
?? show_camera.py
?? test_blue_detect.py
?? test_day170_only_day171.py
?? test_day170_speed50_day171.py
?? test_ee_offset.py
?? test_gripper_only.py
?? test_j2_range_day173.py
?? test_joint_range_day173.py
?? test_joint_range_day173_v2.py
?? test_lookup_9points.py
?? test_pick_2points_day171.py
?? test_pick_2points_day171_sync.py
?? test_pick_day175.py
?? test_pick_new_csv_day171.py
?? test_place_day175.py
?? test_place_detection_offline.py
?? test_sequence_day175.py
?? test_side_camera_tip_detection.py
?? test_side_white_mask.py
?? test_white_box_score.py
?? tools/gamepad_check.py
?? tools/pi/servo_check_pi.py
?? tools/servo_check.sh
?? tools/test_servo_check.sh
?? tools/test_servo_check_pi.py
?? verify_dynamic_pick.py
?? verify_dynamic_pick_auto.py
?? verify_dynamic_pick_auto_correct.py
?? verify_fk_calibration.py
?? verify_homography.py.bak_DAY168_pre_unified
?? verify_lowerleft_scan.py
?? verify_y_direction.py
?? vision/
?? visual_servo_20260502_084046.csv
?? visual_servo_20260502_181427.csv
?? visual_servo_20260502_183654.csv
?? visual_servo_20260502_205548.csv
?? visual_servo_20260502_211405.csv
?? visual_servo_20260503_071901.csv
?? visual_servo_20260503_073918.csv
?? visual_servo_20260503_083158.csv
?? visual_servo_20260506_082640.csv
?? visual_servo_20260506_082916.csv
?? visual_servo_20260506_194904.csv
?? visual_servo_20260514_223230.csv
?? visual_servo_20260514_224150.csv
?? visual_servo_20260514_224442.csv
?? visual_servo_20260514_225526.csv
?? visual_servo_20260514_233646.csv
?? visual_servo_20260515_212307.csv
?? visual_servo_pick.py.day180_backup
?? visual_servo_pick.py.day184_pre_debug_log
?? visual_servo_pick.py.day184_pre_descend_servo
?? visual_servo_pick.py.day184_pre_gripper_fix
?? visual_servo_pick.py.day184_pre_j1_gripper
?? visual_servo_pick.py.day184_pre_marker_fix
?? visual_servo_pick.py.day185_pre_2d_converge
?? visual_servo_pick.py.day185_pre_approach_height
?? visual_servo_pick.py.day185_pre_approach_redesign
?? visual_servo_pick.py.day185_pre_blue_fullscreen
?? visual_servo_pick.py.day185_pre_descend_fix
?? visual_servo_pick.py.day185_pre_dynamic
?? visual_servo_pick.py.day185_pre_fix_definitive
?? visual_servo_pick.py.day185_pre_j2_tune
?? visual_servo_pick.py.day185_pre_persist
?? visual_servo_pick.py.day185_pre_posture_unify
?? visual_servo_pick.py.day185_pre_revert
?? visual_servo_pick.py.day185_pre_sign_fix
?? visual_servo_pick.py.day185_pre_tight_threshold
?? visual_servo_pick.py.day185_pre_topmost
?? visual_servo_pick.py.day185_pre_visualize
?? visual_servo_pick.py.day185_pre_window_thread
?? visual_servo_pick.py.day186_pre_3joint_reach
?? visual_servo_pick.py.day186_pre_descend_j2_minus5
?? visual_servo_pick.py.day186_pre_descend_obs
?? visual_servo_pick.py.day186_pre_imwrite_off
?? visual_servo_pick.py.day186_pre_limit_disable
?? visual_servo_pick.py.day186_pre_loop_speedup
?? visual_servo_pick.py.day186_pre_nonblock
?? visual_servo_pick.py.day186_pre_normal_limit_disable
?? visual_servo_pick.py.day186_pre_post_descend_refine
?? visual_servo_pick.py.day186_pre_refine_mode
?? visual_servo_pick.py.day186_pre_send_interval
?? visual_servo_pick.py.day186_pre_sleep_removal
?? visual_servo_pick.py.day186_pre_ssh_merge
?? visual_servo_pick.py.day186_pre_ssh_persist
?? visual_servo_pick.py.day186_pre_z_tracking
?? visual_servo_pick.py.day188_pre_z_sign_fix
?? visual_servo_pick.py.day189_pre_daemon
?? visual_servo_pick_architecture_2026-05-15.json
?? xyz_track_design.md
?? xyz_track_test.py
?? y_calibration.py

codex
- **P2** — [diag_fill_flags.py:98](/Users/shimazueiichi/r2r2r-scripts/pi05/diag_fill_flags.py:98)：対象エピソードが0件だと `rows[0]` で異常終了します。先に出力ディレクトリが作られるため、再実行も「出力先がもうある」で拒否されます。空の場合も固定の列名でCSVを出力するか、ディレクトリ作成前に終了してください。

本体の変更には、指定条件に反する不具合は見つかりませんでした。印は補間と同じ区間条件を使い、選別後の `idx` に対応しています。

純粋計算の4テストは成功。実データ再生成によるバイト一致テストは未実施です。既存データセットは変更していません。
tokens used
22,414
- **P2** — [diag_fill_flags.py:98](/Users/shimazueiichi/r2r2r-scripts/pi05/diag_fill_flags.py:98)：対象エピソードが0件だと `rows[0]` で異常終了します。先に出力ディレクトリが作られるため、再実行も「出力先がもうある」で拒否されます。空の場合も固定の列名でCSVを出力するか、ディレクトリ作成前に終了してください。

本体の変更には、指定条件に反する不具合は見つかりませんでした。印は補間と同じ区間条件を使い、選別後の `idx` に対応しています。

純粋計算の4テストは成功。実データ再生成によるバイト一致テストは未実施です。既存データセットは変更していません。
