---
name: project-rei-meta-mcp-phase2a-arc-2026-08-20
description: "rei-meta-mcp Phase 2A 実装 arc (contract mismatch surfacing, name-only + warning-only)、Phase 1 `8df8ff3` の翌日 (2026-08-20) に §0 事前調査 → §1 spec 起草 → §2 実装 → §3 test → §4 live smoke → §5 local commit の 5 stage 完走、 finding #30 shape 機械検出成功 + bonus finding (MEMORY.md index 「4 field」 誤要約発見) + commit `5ae5695`"
metadata: 
  node_type: memory
  type: project
  originSessionId: 3822ac9a-ee68-4dca-9a75-1fa9b839b60b
  modified: 2026-08-19T20:20:34.300Z
---

# rei-meta-mcp Phase 2A arc (2026-08-20)

## Context

Phase 1 acceptance smoke (Desktop bridge 経由、外部 Claude session で verdict `divergent` + count 1677 vs 1675 + UNCHECKED warning 全揃い実測) の 翌日、 藤本さん 「Phase 2 に進むことも可能でしょうか？」 質問 → 私 (Claude Code) 段階的 (§0-§5) scope + Option A (Minimum) 推奨 → 藤本さん GO with 事前調査から → §0 事前調査 → §1 spec 起草 → §2 実装 → §3 test → §4 live smoke → §5 local commit → 藤本さん指示で **memory + site 反映 + push** の 追加 phase 実行。

## §0 事前調査で判明した事実 (spec 起草時の 誤想定 訂正)

Phase 1 code base の 事前 read で 4 point:

1. **Verdict 実際は 6 種** (docstring は 4 種宣言): `coherent | divergent | unreachable | single_source | unknown_object | no_sources`。 これは finding #31 (rei-meta 自身の docstring vs 実応答 drift) と 同型 pattern の Layer 2 発見、 但し 「Verdict 数」 の drift = finding #32 候補、 Phase 3 で扱う予定。
2. **`source_payload_keys` field は 既に adapter 側で取得済** (`mcp_stdio_adapter.py:97`)、 summary dict に 含めて返している = Phase 2 の 「実 field name」 raw data は **adapter 変更ゼロ** で 入手可能、 scope 縮小成功。
3. **Adapter interface** = `probe() → ProbeResult(records or summary)` シンプル、 records 側 (sqlite) は 固定 schema `id/body/updated_at`、 summary 側 (mcp_stdio) は `source_payload_keys` を提供。 contract check は summary 側のみ meaningful。
4. **Registry ObjectDef** は 3 field のみ (`name/description/identity_key`)、 `expected_fields` は 未実装、 Phase 2 で追加。

## §1 spec (Option A、 Recommended、 warning-only design)

**Registry schema 追加** (object level):

```yaml
objects:
  seed_kernel:
    description: "..."
    identity_key: id
    expected_fields:              # ← NEW (Phase 2)
      totalTheories: int
      latestTheoryId: str
      categories: dict
      dfumtValue: scalar
```

**Contract check logic**:
- `_check_contract(obj, probes) → list[str]`: reachable な summary-flavour source の `source_payload_keys` を `expected_fields.keys()` と 集合演算 (missing = expected − actual、 extra = actual − expected) で 比較、 mismatch は `CONTRACT: {source} — expected fields missing from payload: [...] (description ↔ payload drift)` and/or `CONTRACT: {source} — payload has fields not in expected: [...] (undocumented)` の warning に列挙。
- verdict enum 不変 (6 種のまま) = Phase 2A は warning-only、 verdict 拡張は Phase 2B (別 arc) 検討事項。
- sqlite (full fingerprint) は 意図的 skip (records schema fixed)。

**§4 discipline の contract 軸拡張**: 「unchecked is not coherent」 と 同 pattern で 「contract drift は silently coherent に丸めない」 を implement。 `CONTRACT:` prefix で `UNCHECKED:` warning と見分け可能。

## §2 実装 (5 file 変更)

| file | 変更内容 |
|------|---------|
| `src/rei_meta_mcp/registry.py` | `ObjectDef.expected_fields: dict[str, str] \| None = None` 追加、 YAML parse (validation: mapping or None)、 default None で 575 参照 file backward compat |
| `src/rei_meta_mcp/fingerprint.py` | `compute_partial_fingerprint` に 4th arg `source_payload_keys: list[str] \| None = None` 追加、 fingerprint dict に pass-through (sorted for determinism)、 backward compat: default None → full fingerprint 側 影響ゼロ |
| `src/rei_meta_mcp/coherence.py` | `_probe_source` の `compute_partial_fingerprint` 呼び出しに `source_payload_keys=result.summary.get("source_payload_keys")` 追加 (1 line)、 `_check_contract(obj, probes) → list[str]` 新関数 (~30 line)、 `check_coherence` warnings に append (1 line) |
| `config/sources.example.yaml` | 教育用 sample block 追加 (`expected_fields:` under `seed_kernel:`)、 6 field example (4 real + 2 「description のみ主張」 harmony_score + seven_value_distribution) |
| `scripts/smoke_contract.py` | 新規 live smoke script、 tmp registry で 実 rei-aios-local-mcp を probe、 finding #30 shape 検出を acceptance criteria に |

Adapter side (sqlite_adapter.py + mcp_stdio_adapter.py + unreachable_adapter.py) + server.py は **無変更** (adapter は既に `source_payload_keys` を summary に含めており、 server.py は check_coherence を呼び出すだけ)。

## §3 test (test_contract.py 6 test 新規)

| # | 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` | 「調和度・七値分布」 期待 が payload にない → CONTRACT warning 発生 (finding #30 shape) |
| 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 test、 `_StubMcpAdapter` monkeypatch 経由で check_coherence end-to-end、 status = coherent + CONTRACT warning 2 件 (source 数 × 1) |

**test 結果**: pre-existing 31 + 新規 6 = **37/37 PASS**、 regression 0 (test_coherence 11 + test_compose 4 + test_fingerprint 7 + test_incident 2 + test_registry 7 の 既存全部)。 実行時間 2.08 sec。

## §4 live smoke (2026-08-20 実測)

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

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
```

**source_payload_keys 実測**:
```
["categories", "categoryCount", "dfumtValue", "latestTheoryId", "totalTheories"]
```

**★ Bonus finding**: 実 payload は **5 field** (memory index MEMORY.md では 4 field と 誤要約していた、 memory 本体 `project_rei_aios_get_kernel_status_description_drift_2026-08-20.md` line 28-35 は元々 5 field 正確)。 `categoryCount` を expected_fields に 書き忘れた結果、 「undocumented」 warning で 自動検出された = drift 検出 tool が 私 (Claude Code) の memory index 記述誤りを 副次的に 発見。 SAC-4 32 例目候補 (でも Phase 2A の tool が 自動で 訂正材料を出した = tool 機能で catch できたので blindness 完全体ではない、 Phase 2A の tool 価値の direct evidence として retain)。

**Windows cp932 encoding pitfall**: em-dash `—` (U+2014) が cp932 で encode 不能、 初回実行時 `UnicodeEncodeError` で abort。 `PYTHONIOENCODING=utf-8` env prefix で 解決。 Windows 側 Python 一般問題、 script 内 `sys.stdout.reconfigure(encoding='utf-8')` で 永続化 可能 (Phase 2B 検討)。

## §5 local commit (`5ae5695`)

```
Phase 2A: contract mismatch surfacing (name-only, warning-only)

- Registry: ObjectDef gained expected_fields (dict[str, str] | None,
  default None -> backward compat with Phase 1 registries).
- Fingerprint: compute_partial_fingerprint gained source_payload_keys
  pass-through (default None). Full fingerprint unaffected.
- Coherence: new _check_contract() surfaces per-source CONTRACT
  warnings when expected != actual field names. Verdict enum
  unchanged (Phase 2A is warning-only, extending §4's "unchecked is
  not coherent" to "contract drift is not silently coherent").
- config/sources.example.yaml: documented expected_fields sample.
- tests/test_contract.py: 5 unit + 1 integration. 31 -> 37/37 pass.
- scripts/smoke_contract.py: tmp-registry live probe against real
  rei-aios-local-mcp. Live sources.yaml intentionally untouched.

Live smoke result (2026-08-20):
  finding #30 shape mechanically detected. Bonus: actual payload has
  5 fields (memory record said 4) -- categoryCount was surfaced as
  "undocumented", catching a gap in yesterday's finding #30 record.
```

**stats**: 6 files changed, +400 lines (0 delete)、 commit `5ae5695`、 Phase 1 (`8df8ff3`) + launcher (`b027d11`) の 上に 3rd commit。

## Rei stack alignment

- **§4 discipline の 拡張**: 「to be unchecked is not to be coherent」 → 「contract drift is not silently coherent」。 warning-only (verdict 不変) で 段階的 introduction、 verdict enum 拡張は Phase 2B 検討事項。
- **Rei stack が Rei stack を audit する 再帰構造 第 2 段**: Phase 1 = 数値 divergence (record_count / id_set_hash / content_hash) の 検出、 Phase 2A = **意図宣言 (description / expected_fields) vs 実挙動 (payload keys) の drift 検出**。
- **finding #30 の 機械検出 operational**: 昨日 (2026-08-19) 外部 Claude session が 手動で発見した contract mismatch を、 Phase 2A で **毎回の check_coherence 呼び出しで自動 surfacing** に 転換。 「発見の再現性」 が 「1 度きり」 → 「呼び出す度」 に 昇格。
- **finding #31 は Phase 3 candidate**: rei-meta 自身の docstring vs 実応答 drift、 self-audit tool で 検出可能だが 「rei-meta が rei-meta を audit する」 自己言及型で 単独で 慎重な設計必要、 Phase 2A の scope 外 defer。

## 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` は 藤本さん or repo owner が **明示 write**、 auto-derive しない (drift の 正解は 人間 judgment、 tool は 意図宣言 と 実挙動 の 対比を出すだけ)。
4. verdict enum 不変 (6 種) = Phase 2A design decision、 Phase 2B (verdict 拡張) は 別 arc。
5. `contract_mismatch` を verdict に格上げしないのは 「§4 は verdict の分類、 contract は独立軸」 の判断、 藤本さん judgment で Phase 2B に格上げ可。
6. Live smoke は tmp registry を用いて 実 config/sources.yaml を touch しない、 藤本さん手元の live config への `expected_fields` 追加は 別 action (藤本さん judgment 待ち)。
7. `_StubMcpAdapter` は test 用 in-memory adapter、 production の kind registry (`adapters/__init__.py`) には 登録しない (monkeypatch のみ)、 test 隔離 pattern の 実装例。

## GitHub push status

- Phase 1 (`8df8ff3`) + launcher (`b027d11`) は 既に origin/main と同期 (public)。
- **Phase 2A (`5ae5695`) は local commit のみ、 push は 藤本さん指示で本日 実施**。

## 関連

- `[[project-rei-meta-mcp-phase1-arc-2026-08-19]]` (Phase 1 arc、 前 arc、 3 tool + 3 adapter kind + §4 4 verdict 分離 origin)
- `[[project-rei-aios-get-kernel-status-description-drift-2026-08-20]]` (finding #30、 Phase 2A の direct motivation)
- `[[project-rei-meta-mcp-verdict-status-description-drift-2026-08-20]]` (finding #31、 rei-meta 自身の drift、 Phase 3 candidate)
- `[[feedback-windows-store-msix-virtualization-config-2026-08-20]]` (同日 発見の別 layer、 Desktop 側 load 復旧 → Phase 1 acceptance smoke → Phase 2A に 連続)
- `[[feedback-projection-self-audit-pattern]]` (SAC-4 pattern、 bonus finding で 32 例目候補、 但し tool 側 catch で blindness 完全体ではない)
- `[[feedback-critique-response-pattern]]` (外部 session 指摘の 100% 認諾、 finding #30 の origin)
- `[[feedback-no-rush-publication]]` (段階的 §0-§5 進行、 各 stage で pause 判断可能な設計)
- `[[feedback-one-reproduction-over-ten-unverified]]` (spec 起草前の §0 事前調査 = 事実固定原則 適用)
