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 に 昇格。
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) で 見分け可能。
| 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_source で source_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 は それを 活用するだけ。
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
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)
37/37 PASS (pre-existing 31 + Phase 2A 新規 6、 regression 0、 実行時間 2.08 sec)
| # | test 名 | 検証内容 |
|---|---|---|
| 1 | test_no_expected_fields_no_contract_warning | expected_fields 未宣言 → 0 warning (backward compat) |
| 2 | test_contract_matches | expected = actual → 0 warning |
| 3 | test_contract_missing_finding30_shape | finding #30 shape (「調和度・七値分布」 不在) → CONTRACT warning |
| 4 | test_contract_extra_undocumented | payload に 余剰 field → CONTRACT warning |
| 5 | test_contract_only_applies_to_summary_sources | SQLite (full flavour) → 0 warning (contract check 対象外、意図的) |
| 6 | test_check_coherence_end_to_end_finding30 | integration、 _StubMcpAdapter monkeypatch、 status = coherent + CONTRACT warning 2 件 |
$ 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_fields に categoryCount を 書き忘れた結果、 「undocumented」 warning で 自動検出。 Phase 2A の tool が 私の memory index 記述誤りを 副次的に 発見 = SAC-4 32 例目候補、 但し tool 側 catch で blindness 完全体ではない、 Phase 2A の tool 価値の direct evidence として retain。
int/str/dict/scalar は informational)、 実 payload の 型 との 比較 は Phase 3 candidate。expected_fields は repo owner が 明示 write、 auto-derive しない (drift の正解は 人間 judgment、 tool は 意図宣言 と 実挙動 の 対比を出すだけ)。contract_mismatch verdict 格上げは Phase 2B (別 arc)。config/sources.yaml を touch しない、 藤本さん手元の live config への expected_fields 追加は 別 action (藤本さん judgment 待ち)。# 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: [...]",
...
]
contract_mismatch verdict 追加 (enum 6 → 7)、 warning-only を verdict 化5ae5695project_rei_meta_mcp_phase1_arc_2026-08-19.mdproject_rei_meta_mcp_phase2a_arc_2026-08-20.mdproject_rei_aios_get_kernel_status_description_drift_2026-08-20.mdproject_rei_meta_mcp_verdict_status_description_drift_2026-08-20.mdfeedback_windows_store_msix_virtualization_config_2026-08-20.md