STEP 1638 — hash-chained claim ledger v0.1-spike

2026-08-31 · 藤本さん 「既存 AI 実装不可能パーツ」 arc 第 3 段 · STEP 1632 spatial + STEP 1635 temporal に続く 「irreversible commit」 = 過去の判定を silently 書き換えられない台帳 · d8_ledger_query (STEP 1402) の 後継 lineage、 rei-aios ローカル 実装

目的

一度 commit した 主張は、 chain 破壊 なしには 消せない、 書き換えられない、 順序が 変えられない。 LLM は 自分の call を 跨いで 「過去の 判定を そのまま 保持する」 保証を 出せない (read-write な memory 上の 主張は 常に 再解釈可能)。 ledger は その 反対の 部品。

「LLM の 記憶は 全部 『読み直せる テキスト』 で、 後から 解釈が 変わる。 ハッシュで 固定され、 モデル自身が 書き換えられない 主張 台帳が ないと、 長期の 推論は 原理的に 積み上がりません」 (前 turn、 4 部品 recommendation の 3 番目 に 藤本さん が 挙げた 部品の 実装)

実装 概要

scripts/checker/ledger.ts               — SHA-256 chain runtime (init / append / verify / query)
scripts/checker/ledger-guard-binding.ts — refactor-guard を ledger に bind する (spec digest ベース)
scripts/checker/ledger-bootstrap.ts     — CLI: bootstrap / --verify / --show
data/checker-ledger/entries.jsonl       — 実 ledger (append-only JSONL、 genesis + guard bindings)
test/step1638-hash-chained-ledger-test.ts — 73/73 PASS (17 section)

Ledger 構造

each entry = {
  seq:       0, 1, 2, …          (monotonic)
  timestamp: ISO 8601 UTC
  kind:      "genesis" | "refactor-guard-bound" | "verdict" | ...
  payload:   arbitrary JSON (kind-specific)
  prevHash:  sha256 of previous entry (or 64 zeros for genesis)
  hash:      sha256(seq + timestamp + kind + canonicalJSON(payload) + prevHash)
}

Genesis (seq=0) の prevHash は 64 個の 0。 以降の entry は 直前 entry の hash を prevHash として 連鎖。 canonicalJSON は key 順序 独立 serialise で、 同じ payload は 常に 同じ bytes に なる (hash 安定性)。

4 種類の 検出

操作検出される 違反test section
過去 entry の payload を 書き換えhash-mismatch4
中間 entry を 削除prev-hash-broken + sequence-gap5
偽 entry を 挿入 (捏造した hash で)hash-mismatch6
JSONL 破損parse-error

ledger は 編集を 防ぐ tool ではない — file system は 相変わらず 書き換え可能。 ledger は 編集を 検出する tool — 誰かが 何を どこで 変えたか、 verify で 一発で 見える。

guard binding — STEP 1635 との 接続

kind: "refactor-guard-bound" の payload は:

{
  name:             string,   // spec.name (kebab-case unique)
  stepOrigin:       number,   // spec.stepOrigin (病理を fix した STEP)
  specDigest:       string,   // sha256 of structural fields
  sourceModulePath: string,   // repo-relative path of the .guard.ts file
  boundAt:          string,   // ISO 8601
}

specDigest意図的に run() / extract() / description を 除外。 driver の 変更 (mock oracle の 更新、 helper rename) は 頻繁で 正当な 進化。 一方 意味を 変える structural fields (name / stepOrigin / invariantKind / invariantExpected / sweepVariable / sweepValues / pathologyShape / correctShape) の 変更は digest に 反映される。

verifyBoundGuards の 3 種類の 判定

Status意味test section
intactlive spec が name で 見つかり、 digest が ledger と 一致11
driftedlive spec は 見つかるが structural digest が 異なる → 意図的なら re-bind で 新 entry を append13
missingledger は 名前を 覚えているが live registry に 存在しない → guard test が silently 削除された12

これが 第 3 段 の 核: refactor-guard test を 単独 file として 存続に 依拠する 代わりに、 ledger に bind すると 「誰かが test file を 削除しても ledger は 覚えている」 = 削除は verify で 即座に verdict = missing に 変わる。 時間軸の 保証が sensor 単体から ledger 経由の 抽象へ 昇格。

load-bearing 実測 — 実 ledger を bootstrap

$ npm run checker:ledger-bootstrap
> tsx scripts/checker/ledger-bootstrap.ts
ledger bootstrap: 1 new binding(s), 0 already present, path=data/checker-ledger/entries.jsonl

$ npm run checker:ledger-verify
> tsx scripts/checker/ledger-bootstrap.ts --verify
chain: ok=true entries=2
bindings: ok=true total=1 intact=1 drifted=0 missing=0
  INTACT: harvest-lag-window-compared-does-not-follow-actual-retention (bound at seq 1, digest matches live spec)

$ npm run checker:ledger-show
> tsx scripts/checker/ledger-bootstrap.ts --show
{"seq":0, "kind":"genesis", "prevHash":"000...000", "hash":"e421...0364", ...}
{"seq":1, "kind":"refactor-guard-bound", "prevHash":"e421...0364", "hash":"b797...a9d0",
 "payload":{"name":"harvest-lag-window-compared-does-not-follow-actual-retention",
            "stepOrigin":1624,
            "specDigest":"4e94...1cb3",
            "sourceModulePath":"test/refactor-guards/step1624-window-compared.guard.ts",
            "boundAt":"2026-08-31T15:23:02.105Z"}}
verdict: PASS

STEP 1624 guard が 実 ledger の seq=1 に bind され、 genesis からの chain 整合が verify OK。 以降 誰かが test/refactor-guards/step1624-window-compared.guard.ts を 削除すると、 npm run checker:ledger-verifyMISSING: harvest-lag-window-compared-... を 返し exit code 1 で CI が 落ちる。

concurrent append — lock file による 直列化

Section 9 で 20 並列 append を 検証。 lock file (data/checker-ledger/entries.jsonl.lock) 取得 → last entry read → hash 計算 → 追記 → lock 解放 の atomic 化で、 seq が 1..20 の 完全な 順列に。 multi-tab 併走の race を 予防 (STEP 1554 atomic-commit と 同 pattern)。

arc 3 段の 相補関係

STEP 1632 arbitrariness_auditSTEP 1635 refactor-guardSTEP 1638 hash-chained ledger
次元spatial (今)temporal (時間軸)irreversibility (書換禁止)
質問「置いた数が どこに あるか」「過去 病理 shape に 戻ったか」「過去の 判定が silently 消された・変えられた・並べ替えられたか」
実装静的 regex 分類器runtime invariance checkerSHA-256 chain + guard binding
triggerinline literal 追加病理 shape 復活台帳 tampering / guard 削除
依拠source file 存続test file 存続ledger file 存続 + chain 整合
runtest:step1632test:step1635test:step1638 + checker:ledger-verify

3 段全て CI wire で、 LLM が call 単位で 出せない invariant の 3 種類 (今の 状態 / 時間跨ぎ shape 保持 / 過去書換禁止) を 静的な 部品として repo に 常駐させる。 「保証は 外部からしか 来ない」 の operational form 3 部品 が これで 揃う。

Honest scope (v0.1-spike の 限界):

次段 candidate (第 4 段以降)

参照

Commit: 別 push · Mirror force-track · STEP 1554 atomic-commit dogfood