STEP 1632 — arbitrariness_audit v0.1-spike

2026-08-31 · 藤本さん「既存 AI に実装不可能だが必要不可欠な部品」 conversation → 「第 1 段推奨」 approve → 実装 · rei-aios が harvest_lag arc (STEP 1600-1626) で 実測した 「置いた数」 集約 pattern を 一般化した 静的 lens

目的

sensor source code 内の 数値リテラルを 出所別に分類 する静的 lens。 harvest_lag v0.1 → v0.9 の empirical evidence ({50} が sensor 内 3 箇所 → 0 箇所) から抽出。 「保証は外部からしか来ない」 arc の 時間軸に効く部品 — 未来の author (人間 or LLM) が 恣意的な magic number を silently 増やすと 検出される。

「LLM は 『置いた数』 を 散らばせるのが 得意で、 集約するのが 本質的に 苦手。 sensor 内に arbitrary constant が 1 個でも 生き残ると、 そこが verifier の 盲点になる。 『arbitrary な数が 現れるのは ここ一箇所』 を 機械的に保証する部品は、 確率分布の 上には 載らない」 (STEP 1631 直後、 藤本さんの 「推奨をお願い」 に応答した 私の観察)

分類 (7 種)

Class検出 heuristic意味
fallback-namedconst UPPER_SNAKE = <num> の右辺命名された policy 定数 (集約済)
fallback-nullishx ?? <num> の右辺inline default (名前無しだが 出所明示)
format.toFixed(N) / .slice(A, B) / .padStart(N) / parseInt(x, N) / template ${...}表示・切出し用 (business logic ではない)
format-ratio/ 3_600_000 等の 物理単位 divisor物理定数 (ms→h 等、 load-bearing だが arbitrariness ではない)
guard-small-1 / 0 / 1 / 2 の guard・index・increment制御 boundary (arbitrariness ではない)
allowed行末 // arbitrariness-audit: allow <rationale>opt-out escape hatch (理由文 必須)
declaredそれ以外の inline literalLEAK candidate — sensor 内で 名前無く 使われる arbitrary な数

判定

PASS ⇔ declared == 0
       (全 inline literal が fallback / format / guard / allowed のいずれかに分類)
FAIL ⇔ declared > 0
       (arbitrariness leak を location 別に列挙)

実装場所

scripts/checker/arbitrariness-audit.ts   — 分類 engine + CLI (regex 単純 heuristic、 AST 不使用)
test/step1632-arbitrariness-audit-test.ts — 28/28 PASS (11 section)
package.json:
  "test:step1632": "tsx test/step1632-arbitrariness-audit-test.ts"
  "checker:arbitrariness-audit": "tsx scripts/checker/arbitrariness-audit.ts"

使用例

$ npm run checker:arbitrariness-audit -- scripts/comparator/gauges/harvest_lag_v02.ts

arbitrariness_audit v0.1-spike
scanned 1 file(s), 27 literal(s)
  fallback-named   : 4
  fallback-nullish : 1
  format           : 5
  guard-small      : 17
  format-ratio     : 0
  allowed          : 0
  declared (LEAK)  : 0
verdict: PASS

JSON 出力は --json。 CLI exit code = 0 (PASS) / 1 (FAIL) / 2 (usage error) — CI に組み込み可能。

empirical 実測 — harvest_lag arc の 素子

FilenamednullishformatguardratiodeclaredVerdict
harvest_lag.ts (v0.1、 時刻 delta) 300410 PASS
harvest_lag_v02.ts (v0.9-oracle-tri-state 現行、 集約 完了状態) 4151700 PASS

v0.9 の 4 named fallback = DEFAULT_DECLARED_RETENTION=50 + DEFAULT_FRESH_RATIO=0 + DEFAULT_AGING_RATIO=0.10 + DEFAULT_LAGGING_RATIO=0.50。 1 nullish = opts.maxLagDays ?? 3 (chat-Claude Q2 で 要件由来と 確定した 数)。 declared = 0 = 「置いた数が sensor から 消えた」 状態が 静的 lens で 実測 verify された。

Regression 検出 (positive control)

test Section 7 = 意図的な leak function tooManyLiterals(x) { return x * 42 + 7; }{declared: 2, verdict: FAIL} として 正しく 検出 (値 42 と 7 が leak として location 別に列挙)。 将来 誰かが sensor に inline literal を silently 追加した場合、 test:step1632 が fail する 抽象 — refactor guard の 基盤。

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

「既存 AI に 不可能」 の 該当性

本 tool は LLM が call 単位で 自己保証 できない 4 部品の 拡張 「時間軸に効く部品」 (item 5(a)) の 実装。 LLM は 単 call で 「置いた数」 を 分類できても、 次の call で それが 保たれる 保証は 出せない。 静的 lens として repo に 常駐する 部品だけが 時間を 跨いで 「declared == 0」 を invariant として 保持できる。

接続面: 前 turn で 藤本さん + 私が 議論した 「型付けが 本丸」 の 前段 = 「型を 書く前に 『置いた数』 を 集約させる」 部品。 harvest_lag arc は これが operational に 機能する 実測例 (chat-Claude 9-11 往復で {50} の 三箇所住処が 順次 撤廃された)。

次段候補 (第 2 段: refactor_guard_generate)

本 tool は 「今の 状態」 を 判定。 STEP 1624 で 書かれた 「actualRetention 5 値変動 + windowCompared=derived 不変 assert」 pattern を 一般化する tool が 第 2 段。 未来の author が 病理 shape (v0.2 の Math.min(oracleReturned, actualRetention)) に silently 戻すと test が 落ちる — 抽象 refactor guard 生成器。 arbitrariness_audit と 相補: 前者が spatial (今の 状態)、 後者が temporal (過去の 病理 の 再入 阻止)。

参照

Commit: 別 push で 反映 · Mirror force-track 対象 (dist-renderer/tools/) · memory: ~/.claude/projects/C--Users-user-rei-aios/memory/project_step1632_arbitrariness_audit_v01_2026-08-31.md (別 STEP で 詳細)