rei-meta-mcp Phase 2A — contract mismatch surfacing
(name-only, warning-only)

Phase 2A 37/37 test PASS live smoke PASS Phase 2B / 3 open 2026-08-20 · commit 5ae5695

目的

rei-meta-mcp Phase 1 (2026-08-19、 3 tool + 3 adapter kind + §4 「to be unchecked is not to be coherent」 4 verdict 分離) の 翌日、 意図宣言 (tool description / registry の expected_fields) と 実挙動 (payload keys) の drift を機械検出する layer を追加。

Direct target: finding #30 (rei-aios get_kernel_status の description 「理論数・調和度・七値分布を含む」 vs 実 payload 「調和度・七値分布」 両方不在) を、 昨日 外部 Claude session が 手動で発見した状態から、 毎回の check_coherence 呼び出しで自動 surfacing に 昇格。

§4 discipline の contract 軸拡張

Phase 1 の §4: 「to be unchecked is not to be coherent」 = 到達不能な source を silently coherent に 丸めない。

Phase 2A の 拡張: 「contract drift は silently coherent に丸めない」 = 意図宣言 と 実挙動 の 不一致を warning に列挙、 verdict は 変えない (段階的 introduction、 verdict 拡張は Phase 2B 検討事項)。

Warning prefix: UNCHECKED: (Phase 1) と CONTRACT: (Phase 2A) で 見分け可能。

実装内容 (5 file 変更)

file変更
src/rei_meta_mcp/registry.py ObjectDef.expected_fields: dict[str, str] | None = None 追加、 YAML parse (mapping or None validation)、 default None で backward compat
src/rei_meta_mcp/fingerprint.py compute_partial_fingerprint に 4th arg source_payload_keys: list[str] | None 追加、 fingerprint dict に pass-through (sorted for determinism)、 default None → full fingerprint 側 影響ゼロ
src/rei_meta_mcp/coherence.py _check_contract(obj, probes) → list[str] 新関数 (~30 line)、 check_coherence warnings に append、 _probe_sourcesource_payload_keys pass
config/sources.example.yaml 教育用 sample block (expected_fields under seed_kernel、 6 field 例)
scripts/smoke_contract.py 新規 live smoke script、 tmp registry で 実 rei-aios-local-mcp を probe、 finding #30 shape 検出を acceptance criteria に

Adapter side (sqlite / mcp_stdio / unreachable_placeholder) + server.py は 無変更。 adapter は 既に source_payload_keys を summary に含めており (Phase 1 設計、 mcp_stdio_adapter.py:97)、 Phase 2A は それを 活用するだけ。

Contract check logic

def _check_contract(obj, probes) -> list[str]:
    if not obj.expected_fields:
        return []          # backward compat: no expectation → no check
    warnings = []
    expected = set(obj.expected_fields.keys())
    for probe in probes:
        if not probe["reachable"]:
            continue
        actual_keys = probe["fingerprint"].get("source_payload_keys")
        if actual_keys is None:
            continue       # full-flavour (sqlite) intentionally skipped
        actual = set(actual_keys)
        missing = expected - actual
        extra   = actual - expected
        if missing:
            warnings.append(
                f"CONTRACT: {probe['name']} — expected fields missing "
                f"from payload: {sorted(missing)} (description ↔ payload drift)"
            )
        if extra:
            warnings.append(
                f"CONTRACT: {probe['name']} — payload has fields not in "
                f"expected: {sorted(extra)} (undocumented)"
            )
    return warnings

Registry schema (Phase 2 追加)

version: 1

objects:
  seed_kernel:
    description: "SEED_KERNEL 理論群"
    identity_key: id
    expected_fields:            # ← NEW (Phase 2)
      totalTheories: int
      latestTheoryId: str
      categories: dict
      dfumtValue: scalar
      # 藤本さん が 明示 write。 auto-derive しない。
      # 「description 主張 vs 実装」 の 正解基準
      # (drift の どちらが 正か は 別 arc の judgment)

Test 結果

37/37 PASS (pre-existing 31 + Phase 2A 新規 6、 regression 0、 実行時間 2.08 sec)

#test 名検証内容
1test_no_expected_fields_no_contract_warningexpected_fields 未宣言 → 0 warning (backward compat)
2test_contract_matchesexpected = actual → 0 warning
3test_contract_missing_finding30_shapefinding #30 shape (「調和度・七値分布」 不在) → CONTRACT warning
4test_contract_extra_undocumentedpayload に 余剰 field → CONTRACT warning
5test_contract_only_applies_to_summary_sourcesSQLite (full flavour) → 0 warning (contract check 対象外、意図的)
6test_check_coherence_end_to_end_finding30integration、 _StubMcpAdapter monkeypatch、 status = coherent + CONTRACT warning 2 件

Live smoke 実測 (2026-08-20)

$ PYTHONIOENCODING=utf-8 python scripts/smoke_contract.py

source_payload_keys: [
  "categories", "categoryCount", "dfumtValue",
  "latestTheoryId", "totalTheories"
]

VERDICT: single_source
CONTRACT warnings: 2
  ! CONTRACT: rei-aios-local-mcp — expected fields missing from payload:
     ['harmony_score', 'seven_value_distribution']
     (description ↔ payload drift)
  ! CONTRACT: rei-aios-local-mcp — payload has fields not in expected:
     ['categoryCount'] (undocumented)

PHASE 2 SMOKE PASS — finding #30 shape mechanically detected

★ Bonus finding: 実 payload は 5 field (memory index MEMORY.md では 「4 field」 と誤要約、 memory 本体は元々正確)。 私 (Claude Code) が spec 起草時に expected_fieldscategoryCount を 書き忘れた結果、 「undocumented」 warning で 自動検出。 Phase 2A の tool が 私の memory index 記述誤りを 副次的に 発見 = SAC-4 32 例目候補、 但し tool 側 catch で blindness 完全体ではない、 Phase 2A の tool 価値の direct evidence として retain。

Rei stack alignment

Honest scope

  1. Phase 2A は name check only (型 hint int/str/dict/scalar は informational)、 実 payload の 型 との 比較 は Phase 3 candidate。
  2. Contract check は summary-flavour source のみ (mcp_stdio)、 full-flavour (sqlite) は 意図的 skip (records schema fixed)。
  3. expected_fields は repo owner が 明示 write、 auto-derive しない (drift の正解は 人間 judgment、 tool は 意図宣言 と 実挙動 の 対比を出すだけ)。
  4. verdict enum 不変 (6 種) = Phase 2A design decision、 contract_mismatch verdict 格上げは Phase 2B (別 arc)。
  5. Live smoke は tmp registry を用いて 実 config/sources.yaml を touch しない、 藤本さん手元の live config への expected_fields 追加は 別 action (藤本さん judgment 待ち)。
  6. finding #30 の 実修正 (option a payload 追加 / b description 訂正 / c defer note) は Rei-AIOS 側の 別 arc、 本 Phase 2A は 「検出 tool 側」 の 実装のみ。

使用例

# 1. registry (config/sources.yaml) に expected_fields を追加
version: 1
objects:
  seed_kernel:
    description: "SEED_KERNEL"
    identity_key: id
    expected_fields:
      totalTheories: int
      latestTheoryId: str
      # ... 追加

# 2. Claude Desktop 経由で呼び出し
> meta_check_coherence(object_name="seed_kernel")

# 3. output に CONTRACT: warning が 自動 surfacing
warnings: [
  "CONTRACT: rei-aios-local-mcp — expected fields missing from payload: [...]",
  ...
]

Open items

関連