<!-- 公開用の匿名化コピー。原証拠は evidence/ 配下（非公開）。変換規則は evidence_public/CONVERSION_RECORD.md を参照。内容の実行結果は変えていない。 -->

# 記事C 最小実験 実行記録（改訂版 rev2：演習範囲の保護）

- 実行日時: 2026-09-19 07:58 JST
- 実行環境（実測）: macOS 26.6.2 / Python 3.9.6 (/usr/bin/python3) / SQLite 3.51.0
- 注意: Python 3.9 は公式サポート終了（2025-10-31）。既存環境で追加インストールなしに実行した事実の記録であり、推奨環境ではない。サポート中の版での実行は未確認。
- 実行場所: examples/sample-c/work/（演習領域、新規に作成）および自己点検用の一時領域 work_selfcheck_<時刻>/（終了時に削除）。本番・共有・外部DBへの接続なし。
- 原証拠（初版 evidence/sample-c/、rev1 evidence/rev1-2026-09-18/）は変更していない。
- 期待結果: 正常9手順はすべて終了コード0、verify は8項目一致。自己点検25件（rev1の18件＋rev2追加7件）すべて期待どおり。掃除スクリプトの単独実行は終了コード2で何もしない。GPT同梱の境界確認4件は合格・終了コード0。

### 0. reset（--confirm なし → 拒否）

```text
$ python3 drill.py reset
中止: reset は演習領域を削除する。実行するなら --confirm を付ける
終了コード: 2
```

### 1. reset --confirm（新規領域の作成）

```text
$ python3 drill.py reset --confirm
[2026-09-18T22:58:51+00:00] 演習領域を初期化した: <PROJECT_ROOT>/examples/sample-c/work
終了コード: 0
```

### 2. setup

```text
$ python3 drill.py setup
--- 初期状態（架空データ） ---
  依頼 5 件 / 履歴 7 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 0 件
  REQ-C01  承認済み  会議室プロジェクタ手配
  REQ-C02  提出済み  名刺の追加発注
  REQ-C03  差戻し   備品棚の移設
  REQ-C04  承認済み  社用車の予約
  REQ-C05  下書き   来客用駐車場の確保
[2026-09-18T22:58:51+00:00] practice.db を作成した: <PROJECT_ROOT>/examples/sample-c/work/practice.db
終了コード: 0
```

### 3. backup

```text
$ python3 drill.py backup
[2026-09-18T22:58:51+00:00] バックアップ世代 gen-001 を作成した: <PROJECT_ROOT>/examples/sample-c/work/backup/gen-001/practice_backup.db
[2026-09-18T22:58:51+00:00] 基準の控え（バックアップ自体から取得）: 依頼 5 件 / 履歴 7 件 / sha256 28e7e4040a4fb328…
終了コード: 0
```

### 4. after-backup

```text
$ python3 drill.py after-backup
--- バックアップ後の業務変更を反映した状態 ---
  依頼 6 件 / 履歴 9 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 0 件
  REQ-C01  承認済み  会議室プロジェクタ手配
  REQ-C02  承認済み  名刺の追加発注
  REQ-C03  差戻し   備品棚の移設
  REQ-C04  承認済み  社用車の予約
  REQ-C05  下書き   来客用駐車場の確保
  REQ-C06  提出済み  会議用弁当の手配
[2026-09-18T22:58:52+00:00] バックアップ後に、REQ-C06 の提出と REQ-C02 の承認が起きた（架空）
終了コード: 0
```

### 5. break

```text
$ python3 drill.py break
[2026-09-18T22:58:52+00:00] 誤った掃除スクリプト cleanup_v1_1 を実行する（架空の障害の引き金。演習領域内の practice.db だけが対象）
cleanup v1.1: 削除した履歴 6 件
--- 障害後の状態 ---
  依頼 6 件 / 履歴 3 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 3 件
  REQ-C01  承認済み  会議室プロジェクタ手配
  REQ-C02  承認済み  名刺の追加発注
  REQ-C03  差戻し   備品棚の移設
  REQ-C04  承認済み  社用車の予約
  REQ-C05  下書き   来客用駐車場の確保
  REQ-C06  提出済み  会議用弁当の手配
終了コード: 0
```

### 6. freeze

```text
$ python3 drill.py freeze
[2026-09-18T22:58:52+00:00] 壊れた状態を別名で保全した（元ファイルは変更しない）: <PROJECT_ROOT>/examples/sample-c/work/incident/practice_broken_20260918T225852Z.db
[2026-09-18T22:58:52+00:00] 保全ファイル sha256 e9054becbe22c24a…
終了コード: 0
```

### 7. rollback-code

```text
$ python3 drill.py rollback-code
[2026-09-18T22:58:52+00:00] 掃除スクリプトを既知版 cleanup_v1_0 に戻して再実行する（コードの切戻し）
cleanup v1.0: 削除処理なし（履歴 3 件はそのまま）
--- コード切戻し後の状態（データは戻っていないはず） ---
  依頼 6 件 / 履歴 3 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 3 件
  REQ-C01  承認済み  会議室プロジェクタ手配
  REQ-C02  承認済み  名刺の追加発注
  REQ-C03  差戻し   備品棚の移設
  REQ-C04  承認済み  社用車の予約
  REQ-C05  下書き   来客用駐車場の確保
  REQ-C06  提出済み  会議用弁当の手配
終了コード: 0
```

### 8. restore

```text
$ python3 drill.py restore
[2026-09-18T22:58:52+00:00] 復元元（世代 gen-001 のバックアップファイル）の指紋が控えと一致した
[2026-09-18T22:58:52+00:00] 別の場所へ復元した（元の practice.db は上書きしていない）: <PROJECT_ROOT>/examples/sample-c/work/restore/gen-001/practice_restored.db
[2026-09-18T22:58:52+00:00] 復元後のファイルの指紋 28e7e4040a4fb328…（復元元と一致）
終了コード: 0
```

### 9. verify

```text
$ python3 drill.py verify
# 復元確認レポート（自動生成）

- 生成日時: 2026-09-18T22:58:52+00:00
- 世代: gen-001　基準時点（バックアップ取得時刻）: 2026-09-18T22:58:51+00:00
- 基準の控えの出所: backup_file（バックアップファイル自体から取得）
- 復元先: restore/gen-001/practice_restored.db

## 1. 基準時点との照合（指紋・可読性・件数・内容・関係）

| 項目 | 基準 | 復元結果 | 判定 |
|---|---|---|---|
| 復元後ファイルの指紋 | 28e7e4040a4fb328… | 28e7e4040a4fb328… | 一致 |
| データベースとして読める | 読める | 読める | 一致 |
| 依頼の件数 | 5 | 5 | 一致 |
| 履歴の件数 | 7 | 7 | 一致 |
| 依頼の内容（ID・依頼者・件名・状態・承認者） | 5 行 | 不一致 0 件 | 一致 |
| 履歴の内容（依頼ID・操作・操作者・時刻・備考） | 7 行 | 不一致 0 件 | 一致 |
| 依頼のない履歴（関係の破れ） | 0 | 0 | 一致 |
| 承認履歴のない承認済み（不変条件） | 0 | 0 | 一致 |

指紋の一致・データベースとして読めること・件数の一致・内容の一致・関係の整合は、それぞれ別の確認である。
指紋が一致しても、控えが誤った時点のものなら業務要件への適合は示せない。

## 2. 基準時点より後に起きて、復元では戻らない変更（復元成功とは別の判断）

比較対象（保全した障害時のファイル）: practice_broken_20260918T225852Z.db

| 対象 | 障害時のファイルにある内容 | 復元結果 | 扱い |
|---|---|---|---|
| 依頼 REQ-C02 の状態 | 承認済み | 提出済み | 基準時点以降の操作を再実施するか判断 |
| 依頼 REQ-C06（会議用弁当の手配） | 存在（提出済み） | 存在しない | 再入力または再提出が必要 |
| 履歴 REQ-C06 提出（u-tanaka） ×1 | 存在 | 存在しない | 再操作で再生成するか、記録として扱う |

注意: 基準時点より後に作られ、障害で失われた変更は、どちらのファイルにも存在しないため、この照合では検出できない。
別系統の記録（通知、申請記録など）があれば回収できる場合がある。この演習ではそれらは用意していない。

## 3. 判定

- 基準時点との照合: すべて一致
- これは自動照合の結果であり、利用者による業務確認でも、利用再開の承認でもない。
  再開の判断は復旧確認表（templates/recovery_checklist_template.md）で、業務確認の完了後に別途行う。
[2026-09-18T22:58:52+00:00] レポートを書き出した: <PROJECT_ROOT>/examples/sample-c/work/restore/gen-001/verify_report.md
[2026-09-18T22:58:52+00:00] 終了コード 0
終了コード: 0
```

### 実行後の演習領域のファイル一覧と目印の内容

```text
work/.drill-work
work/backup/gen-001/manifest.json
work/backup/gen-001/practice_backup.db
work/incident/practice_broken_20260918T225852Z.db
work/practice.db
work/restore/gen-001/practice_restored.db
work/restore/gen-001/restore_record.json
work/restore/gen-001/verify_report.md
--- .drill-work ---
{"kind": "ai-system-development/sample-c drill work area", "work_realpath": "<PROJECT_ROOT>/examples/sample-c/work", "created_at": "2026-09-18T22:58:51+00:00"}
```

### 10. 掃除スクリプトの単独実行（入口廃止の確認。引数を渡しても何もしない）

```text
$ python3 cleanup_v1_1.py work/practice.db work
このスクリプトは単独では実行しない。drill.py の break から実行する。
終了コード: 2
```

### 11. 自己点検 25件（一時領域で実行）

```text
$ python3 drill_selfcheck.py
一時演習領域: <PROJECT_ROOT>/examples/sample-c/work_selfcheck_20260918T225852Z
[OK ] R1-A 正常復元の照合: 期待=すべて一致・終了コード0 実際=すべて一致・終了コード0
[OK ] R1-B 件名だけ変更: 期待=不一致あり・終了コード1 実際=不一致あり・終了コード1
[OK ] R1-B 履歴の操作者だけ変更: 期待=不一致あり・終了コード1 実際=不一致あり・終了コード1
[OK ] R1-B 履歴の時刻だけ変更: 期待=不一致あり・終了コード1 実際=不一致あり・終了コード1
[OK ] R1-B 履歴の備考だけ変更: 期待=不一致あり・終了コード1 実際=不一致あり・終了コード1
[OK ] R1-C 履歴を1件削除（件数変更）: 期待=不一致あり・終了コード1 実際=不一致あり・終了コード1
[OK ] R1-C 状態だけ変更: 期待=不一致あり・終了コード1 実際=不一致あり・終了コード1
[OK ] 復元結果を元に戻した後の再照合: 期待=すべて一致・終了コード0 実際=終了コード0
[OK ] R2-A 指紋不一致で再復元: 期待=中止（非ゼロ）・既存復元物は無傷 実際=終了コード1・復元物あり・指紋同一
[OK ] R2-A 復元先が既にある状態で再復元: 期待=拒否（非ゼロ）・既存復元物は無傷 実際=終了コード1・指紋同一
[OK ] R2-B 障害後の backup 再実行: 期待=gen-002 が増え、gen-001 と控えは不変 実際=世代=['gen-001', 'gen-002']・gen-001控え不変・gen-001指紋不変・gen-002控え=依頼6/履歴3
[OK ] R2-B 世代複数時の verify（世代省略）: 期待=拒否（非ゼロ） 実際=終了コード1
[OK ] R2-B 世代を明示した verify gen-001: 期待=すべて一致・終了コード0 実際=終了コード0
[OK ] R2-C cleanup_v1_1 に演習領域の外の DB を渡す（単独実行）: 期待=拒否（非ゼロ）・削除されない 実際=終了コード2・履歴1件
[OK ] R2-F cleanup_v1_1.run() に演習領域の外の DB を渡す: 期待=拒否（SafetyError）・内容不変 実際=拒否・指紋同一
[OK ] R2-C reset を --confirm なしで実行: 期待=拒否（非ゼロ）・領域は残る 実際=終了コード2・practice.dbあり
[OK ] R2-C 目印ファイルのない領域を reset --confirm: 期待=拒否（非ゼロ）・領域は残る 実際=終了コード2・practice.dbあり
[OK ] R2-I 空の旧形式の目印で reset --confirm: 期待=拒否（非ゼロ）・領域は残る 実際=終了コード2・practice.dbあり
[OK ] R2-I 空の旧形式の目印で通常コマンド: 期待=拒否（非ゼロ） 実際=終了コード1
[OK ] R2-I 基点の直下でない領域で setup: 期待=拒否（非ゼロ）・作成なし 実際=終了コード2
[OK ] R2-C 名前が work で始まらない領域で setup: 期待=拒否（非ゼロ） 実際=終了コード2
[OK ] R2-E 無関係なファイルのある既存フォルダで setup → reset --confirm: 期待=両方拒否・目印なし・ファイル不変 実際=setup終了コード1・目印なし・reset終了コード2・ファイル不変
[OK ] R2-G practice.db が領域外へのリンクのとき after-backup: 期待=拒否（非ゼロ）・外のDB不変 実際=終了コード1・外のDB不変
[OK ] R2-H リンク先の無い復元先リンクで restore: 期待=拒否（非ゼロ）・外に作成なし・リンク不変 実際=終了コード1・外のファイルなし・リンク残存
[OK ] R2-D v1.0 は削除処理なし: 期待=「削除処理なし」と表示 実際=削除処理なし

| 項目 | 期待結果 | 実際の結果 | 判定 |
|---|---|---|---|
| R1-A 正常復元の照合 | すべて一致・終了コード0 | すべて一致・終了コード0 | 合 |
| R1-B 件名だけ変更 | 不一致あり・終了コード1 | 不一致あり・終了コード1 | 合 |
| R1-B 履歴の操作者だけ変更 | 不一致あり・終了コード1 | 不一致あり・終了コード1 | 合 |
| R1-B 履歴の時刻だけ変更 | 不一致あり・終了コード1 | 不一致あり・終了コード1 | 合 |
| R1-B 履歴の備考だけ変更 | 不一致あり・終了コード1 | 不一致あり・終了コード1 | 合 |
| R1-C 履歴を1件削除（件数変更） | 不一致あり・終了コード1 | 不一致あり・終了コード1 | 合 |
| R1-C 状態だけ変更 | 不一致あり・終了コード1 | 不一致あり・終了コード1 | 合 |
| 復元結果を元に戻した後の再照合 | すべて一致・終了コード0 | 終了コード0 | 合 |
| R2-A 指紋不一致で再復元 | 中止（非ゼロ）・既存復元物は無傷 | 終了コード1・復元物あり・指紋同一 | 合 |
| R2-A 復元先が既にある状態で再復元 | 拒否（非ゼロ）・既存復元物は無傷 | 終了コード1・指紋同一 | 合 |
| R2-B 障害後の backup 再実行 | gen-002 が増え、gen-001 と控えは不変 | 世代=['gen-001', 'gen-002']・gen-001控え不変・gen-001指紋不変・gen-002控え=依頼6/履歴3 | 合 |
| R2-B 世代複数時の verify（世代省略） | 拒否（非ゼロ） | 終了コード1 | 合 |
| R2-B 世代を明示した verify gen-001 | すべて一致・終了コード0 | 終了コード0 | 合 |
| R2-C cleanup_v1_1 に演習領域の外の DB を渡す（単独実行） | 拒否（非ゼロ）・削除されない | 終了コード2・履歴1件 | 合 |
| R2-F cleanup_v1_1.run() に演習領域の外の DB を渡す | 拒否（SafetyError）・内容不変 | 拒否・指紋同一 | 合 |
| R2-C reset を --confirm なしで実行 | 拒否（非ゼロ）・領域は残る | 終了コード2・practice.dbあり | 合 |
| R2-C 目印ファイルのない領域を reset --confirm | 拒否（非ゼロ）・領域は残る | 終了コード2・practice.dbあり | 合 |
| R2-I 空の旧形式の目印で reset --confirm | 拒否（非ゼロ）・領域は残る | 終了コード2・practice.dbあり | 合 |
| R2-I 空の旧形式の目印で通常コマンド | 拒否（非ゼロ） | 終了コード1 | 合 |
| R2-I 基点の直下でない領域で setup | 拒否（非ゼロ）・作成なし | 終了コード2 | 合 |
| R2-C 名前が work で始まらない領域で setup | 拒否（非ゼロ） | 終了コード2 | 合 |
| R2-E 無関係なファイルのある既存フォルダで setup → reset --confirm | 両方拒否・目印なし・ファイル不変 | setup終了コード1・目印なし・reset終了コード2・ファイル不変 | 合 |
| R2-G practice.db が領域外へのリンクのとき after-backup | 拒否（非ゼロ）・外のDB不変 | 終了コード1・外のDB不変 | 合 |
| R2-H リンク先の無い復元先リンクで restore | 拒否（非ゼロ）・外に作成なし・リンク不変 | 終了コード1・外のファイルなし・リンク残存 | 合 |
| R2-D v1.0 は削除処理なし | 「削除処理なし」と表示 | 削除処理なし | 合 |

合計 25 件、期待どおり 25 件、期待と異なる 0 件
一時演習領域を削除した: <PROJECT_ROOT>/examples/sample-c/work_selfcheck_20260918T225852Z
終了コード: 0
```

### 12. GPT同梱の境界確認スクリプト（正本を一時コピーして4経路を試す）

```text
$ python3 AI_SYSTEM_DEV_PHASE1_RECHECK/review/tools/check_r2_boundaries.py --project-root .
{
  "checks": [
    {
      "id": "R2-01",
      "pass": true,
      "marker_added": false,
      "sentinel_preserved": true,
      "runs": [
        {
          "exit": 1,
          "stdout": "中止: 演習領域にしようとしたフォルダに既にファイルがある。無関係なフォルダの可能性があるため、目印を付けず何も変更しない: /private<TMP>",
          "stderr": ""
        },
        {
          "exit": 2,
          "stdout": "中止: 目印ファイル /private<TMP> が無いか、内容がこの演習領域と一致しない。演習領域と確認できないため削除しない\n",
          "stderr": ""
        }
      ]
    },
    {
      "id": "R2-02",
      "pass": true,
      "outside_db_preserved": true,
      "runs": [
        {
          "exit": 2,
          "stdout": "このスクリプトは単独では実行しない。drill.py の break から実行する。\n",
          "stderr": ""
        }
      ]
    },
    {
      "id": "R2-03",
      "pass": true,
      "outside_db_preserved": true,
      "runs": [
        {
          "exit": 0,
          "stdout": "[2026-09-18T22:58:53+00:00] 演習領域を初期化した: /private<TMP>",
          "stderr": ""
        },
        {
          "exit": 0,
          "stdout": "--- 初期状態（架空データ） ---\n  依頼 5 件 / 履歴 7 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 0 件\n  REQ-C01  承認済み  会議室プロジェクタ手配\n  REQ-C02  提出済み  名刺の追加発注\n  REQ-C03  差戻し   備品棚の移設\n  REQ-C04  承認済み  社用車の予約\n  REQ-C05  下書き   来客用駐車場の確保\n[2026-09-18T22:58:53+00:00] practice.db を作成した: /private<TMP>",
          "stderr": ""
        },
        {
          "exit": 1,
          "stdout": "中止: practice.db の実際の場所が演習領域の外にある: /private<TMP> → /private<TMP>",
          "stderr": ""
        }
      ]
    },
    {
      "id": "R2-04",
      "pass": true,
      "outside_file_absent_and_link_preserved": true,
      "runs": [
        {
          "exit": 0,
          "stdout": "[2026-09-18T22:58:53+00:00] 演習領域を初期化した: /private<TMP>",
          "stderr": ""
        },
        {
          "exit": 0,
          "stdout": "--- 初期状態（架空データ） ---\n  依頼 5 件 / 履歴 7 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 0 件\n  REQ-C01  承認済み  会議室プロジェクタ手配\n  REQ-C02  提出済み  名刺の追加発注\n  REQ-C03  差戻し   備品棚の移設\n  REQ-C04  承認済み  社用車の予約\n  REQ-C05  下書き   来客用駐車場の確保\n[2026-09-18T22:58:53+00:00] practice.db を作成した: /private<TMP>",
          "stderr": ""
        },
        {
          "exit": 0,
          "stdout": "[2026-09-18T22:58:53+00:00] バックアップ世代 gen-001 を作成した: /private<TMP> 基準の控え（バックアップ自体から取得）: 依頼 5 件 / 履歴 7 件 / sha256 28e7e4040a4fb328…\n",
          "stderr": ""
        },
        {
          "exit": 1,
          "stdout": "中止: 復元先 /private<TMP> が既にある（リンクを含む）。既存の復元物は消さない。別の世代を使うか、演習領域を reset --confirm で作り直す\n",
          "stderr": ""
        }
      ]
    }
  ],
  "all_passed": true
}
終了コード: 0
```

