Skip to content

Architecture Overview

May-Kirihara edited this page Aug 26, 2026 · 23 revisions

Architecture — Overview

GaOTTT のモジュール構成と二重座標系の概要。

モジュール構成

gaottt/
├── server/
│   ├── app.py            FastAPI (REST)
│   └── mcp_server.py     MCP (LLM 向け、27 ツール)
├── core/
│   ├── engine.py         オーケストレーション
│   ├── gravity.py        軌道力学(純粋関数)
│   ├── scorer.py         スコアリング(mass, decay, emotion, certainty)
│   ├── extractor.py      F1: auto_remember
│   ├── clustering.py     F2: 類似クラスタ (union-find)
│   ├── collision.py      F2.1: 衝突合体
│   ├── prefetch.py       F6: 非同期 prefetch (LRU + Semaphore)
│   └── types.py          Pydantic モデル
├── embedding/            RURI-v3-310m
├── index/                FAISS IndexFlatIP
├── store/
│   ├── base.py           StoreBase 抽象
│   ├── sqlite_store.py   永続化(自動マイグレーション)
│   └── cache.py          In-memory + write-behind
└── graph/                共起グラフ(無向)

二重座標系

GaOTTT の中核アイディア:

[原始 embedding 空間]   ── RURI-v3 が出力、不変
       ↓
[displacement BLOB]     ── 重力で更新される変位ベクトル
       ↓
[仮想座標 = original + displacement] ── recall で実際にスコアされる位置

これにより:

  • 原始 embedding は破壊されない(再構築可能)
  • 重力変位は仮想空間でのみ作用(FAISS リビルド不要)
  • レイテンシ維持(FAISS top-K × 3 → 仮想座標で再計算)

クエリフロー

クエリ
  ↓
RURI-v3 で embedding
  ↓
prefetch cache hit? ─ Yes → 即時返却
  ↓ No
gravity wave propagation(再帰的近傍展開、mass 依存 top-k)
  ↓
N ノード到達(シミュレーション層)
  ↓
仮想座標で再スコア(gravity_sim × decay + mass_boost + wave_boost
                  + emotion_boost + certainty_boost)× saturation
  ↓
top-K 返却(プレゼンテーション層)
  ↓
全到達ノードの軌道力学更新(acceleration → velocity → displacement)

データフロー

インデックス時

  1. POST /index でドキュメント受信(または MCP remember/ingest)
  2. content SHA-256 ハッシュで重複チェック(重複は embedding 生成前にスキップ)
  3. RURI-v3 で embedding 生成(「検索文書: 」プレフィックス付き)
  4. L2 正規化後、FAISS インデックスに追加
  5. NodeState 初期化(mass=1.0, temperature=0.0, displacement=zero)
  6. SQLite にドキュメント + 状態を永続化

クエリ時

  1. POST /query または MCP recall でクエリ受信
  2. RURI-v3 でクエリ embedding 生成(「検索クエリ: 」プレフィックス付き)
  3. prefetch cache hit 確認 → hit 時は即返却
  4. 重力波伝播で広い候補取得(top-K × candidate_multiplier)
  5. 各候補の仮想座標を計算(original_emb + displacement)
  6. 仮想座標での cosine similarity × decay + mass_boost + wave_boost + emotion_boost + certainty_boost で最終スコア
  7. 負スコアを除外、top-K に絞って返却
  8. 返却後 に重力更新: 共起ペアに force 適用 → displacement 蓄積、mass/temperature/共起グラフも更新
  9. ダーティ状態は write-behind で非同期に SQLite へフラッシュ

ストレージ戦略

起動時:   SQLite → in-memory cache にロード + FAISS インデックスロード
稼働時:   キャッシュから読み取り → 変更は dirty セットに記録
定期的:   write-behind タスクが dirty 状態をバッチで SQLite にフラッシュ(5 秒間隔)
停止時:   write-behind 停止 → 全 dirty 状態フラッシュ → FAISS 保存 → 接続クローズ
異常終了: フラッシュされていない dirty 状態は消失(ドキュメント・embedding は保全)

レイヤ別役割

レイヤ 責務
server/ プロトコル変換(REST / MCP)。各 MCP ツール・REST エンドポイントは services/ を叩く薄いラッパ
services/ engine を叩いて Pydantic を返す共有ビジネスロジック層(Phase S, 2026-04-22 新設)。MCP 向け整形文字列は services/formatters.py に集約
core/ 物理シミュレーション、スコアリング、ロジック
embedding/ テキスト → ベクトル
index/ ベクトル近傍探索
store/ 永続化、in-memory cache、write-behind
graph/ 共起グラフ

設計判断の記録

GaOTTT の主要な設計選択とその根拠:

判断事項 決定内容 経緯
二重座標系 原始 embedding(不変)+ 仮想座標(重力変動) Phase 1 評価で単一空間の限界が判明
重力モデル 万有引力 F = G×m_i×m_j/d² 物理的直感に合致、パラメータが明快
変位上限 max_displacement_norm=1e6 (Phase I で 0.3 → 1e6 に引き上げ、実質 ∞) 当初は暴走防止のハードキャップだったが、Hooke 復元力 + displacement_decay + velocity cap が物理的均衡 (d ≈ (G·m/k)^(1/3) ≈ 0.8–3.0) を作るため冗長と判明 (Phase I, 2026-05-11)。boundary 張り付き → homogenization の発生源だった (P7-X)。詳細: Plans — Phase I — Free Star Movement
候補拡張 FAISS top-K × 3 → 仮想座標で再計算 FAISS リビルド不要、レイテンシ維持
graph_boost 廃止 重力変位に統合 スコア加算では順位変動が不足
並行性 Last-write-wins(ロックなし) シングルインスタンス前提(後に WAL + busy_timeout で複数 OK)
重複チェック content SHA-256 ハッシュ embedding 生成前にスキップ
モデルキャッシュ ローカルキャッシュ自動検出 HuggingFace API 通信を完全抑制
軌道力学 加速度 → 速度 → 位置の 3 段階物理 慣性による公転・彗星軌道、摩擦で減衰
アンカー引力 Hooke's law (F=-k×d) で原始位置に復元 脱出防止、銀河の暗黒物質ハローに相当
重力半径 min_sim = 1 - G×mass/(2×a_min) 質量から物理的に導出
重力波伝播 再帰的近傍展開、mass 依存 top-k 高 mass ハブは広い重力圏
fetch-by-id 経路 get_node (MCP) + GET /node/{id}/detail (REST) は store/cache の read 合成のみで、seed pool・wave・mass update を一切通らない 2026-06-12 dogfooding で「reflect に id が見えているのに recall が lexical/gravity capture で届かない」事象を観測。anti-hub (pool 内 rerank) では救えない補集合として pool 迂回の read-only 経路を追加。既存 GET /node/{id} (物理状態のみ) は不変 (Plans — Observation Apparatus Round 2 Stage A)
二層分離 シミュレーション層 + プレゼンテーション層 全到達ノードの物理更新、LLM には top-5 のみ
共起ブラックホール 共起クラスタ重心に BH 形成 銀河束縛、edge_decay で自然消滅
返却飽和 saturation = 1/(1+return_count×rate) 同じ結果の繰り返し防止、脳の馴化
温度脱出 escape = 1/(1+temp×scale) 高温ノードが BH 束縛から脱出、探索促進
TTL 短期記憶 (F4) source="hypothesis" は default 7 日で auto-expire 物理アナロジー: 仮想粒子
archive vs hard delete (F5) デフォルト soft archive、hard=True で物理削除 dormant 剪定の儀式化、物理アナロジー: ホーキング輻射
重力衝突合体 (F2.1) 質量加算 + 運動量保存 + エッジ移譲 銀河衝突合体、merged_into で履歴保持、不可逆
情動・確信度の独立軸 (F7) scorer に |emotion| boost と certainty × exp(-age) 質量と直交する軸、物理アナロジー: 角運動量 / スピン
有向リレーション (F3) 別テーブル directed_edges、typed 既存 cooccurrence の hot path を保持しつつ拡張
engine.compact() TTL expire + FAISS rebuild + 任意 auto-merge + orphan-edge 掃除 物理シミュレーションの定期メンテ
バックグラウンド prefetch (F6) LRU+TTL キャッシュ + asyncio.Semaphore 物理アナロジー: アストロサイト的事前発火、レイテンシ阻害ゼロ
prefetch キャッシュ無効化 archive/restore/forget/merge/compact が prefetch_cache.invalidate() を呼ぶ destructive op 後の stale を防ぐ
タスク状態を edge で表現 (Phase D) task_status 列を持たず、completed/abandoned エッジの存在で判定 完了の重力史が人格の年表になる
人格を多源で表現 (Phase D) source = task / commitment / intention / value / style / relationship:* 既存スキーマで実現、新テーブル不要
inherit_persona の儀式化 散文出力で過去の自分を着る 柱 X「観測者を創ること即存在」のセッション継承版
共有サービス層 (Phase S, 2026-04-22) gaottt/services/ が engine を叩き Pydantic を返す。MCP は formatter で文字列化、REST は JSON で直返却 同じロジックが二重実装にならず、REST が MCP parity に引き上がる。代替案(文字列 JSON ラップ / MCP 廃止 / 直 import)は docs/maintainers/rest-mcp-unification-plan.md §6 で却下理由記録済み
/reset は REST 専用 MCP には露出しない LLM エージェントに破壊的 reset を出さない現状判断を継承
output_mode は MCP 専用 recall MCP ツールのみ output_mode パラメータを持つ。REST は Pydantic JSON を返すためクライアント側で自由に制御可能 REST は構造化データを返しフィールド選択はクライアントの責務。MCP は文字列を返すため LLM のトークン消費を抑える整形オプションが必要。"compact" (300 chars) / "ids" (ヘッダのみ) / "full" (default, 後方互換) の 3 モード
raw_score vs virtual_score (2026-05-12 整理) QueryResultItem.raw_score フィールド名は REST 後方互換で保持。MCP formatter の出力ラベルは virtual_score= に変更 Phase I 以前は raw_score = query·node_raw(変位なし)で名前と実体が一致。Phase I Stage 2 で変位が導入され gravity_sim = query·virtual_pos になったが名前が更新されなかった。座標系: raw FAISS = 原始 embedding 不変 / virtual FAISS = normalize(raw+displacement) / seed pool = raw+virtual union / gravity_sim = query_raw · virtual_pos / pure_raw_cosines = query_raw · node_raw (Phase J Stage 3 内部用、外部非公開)
FAISS write-behind (2026-05-10) faiss_save_interval_seconds(既定 5s)周期で disk に save shutdown() でしか save しない設計だと長期常駐 MCP プロセスの新規 remember が他プロセスから永久 invisible になる歴史的バグの修正。逆方向上書き罠も発見・記載
proxy リクエスト直列化 + 自己修復 (2026-06-01) proxy が単一 upstream ClientSession への全 forwarder + ping を asyncio.Lock で 1 in-flight に直列化(proxy_serialize_requests_enabled)、session 終了系例外で _Upstream holder が session を rebuild し 1 回 retry(proxy_auto_reconnect_enabled、_ensure_backend 再利用で backend 死/idle/cold-start も自己修復)。FAISS は別途 threading.Lock で to_thread save ↔ event-loop add() のクロススレッド race を封じる(faiss_index_lock_enabled) 並列 recall が単一 streamable-http session を壊す「32600 Session terminated」の修正(Troubleshooting)。engine の broad RW-lock は 不採用 — test_engine_concurrent.py C2 が「recall mutation phase は await ゼロで cooperative scheduling 下 atomic」と決着済み、hot path 直列化は無益。FastMCP は tool 例外を isError に変換するので backend handler hardening も不要
Phase G — 重力法則の起動時適用 (2026-05-10) genesis kick (新規) + dream loop (idle 時) + Stage 0 priming (一回だけ全 active node に適用) 物理アナロジー: 軌道捕獲 + tidal capture + primordial gravity activation。新規粒子も既存粒子と同じ重力法則を最初から受ける。bootstrap curator (LLM bridge) は不採用継承
Phase G — mass boost cap genesis_mass_boost_cap=1.0 で 1 step 加算上限 dense cluster 中心では raw `
Phase H Stage 1 — mass-aware seed boost (2026-05-10) wave_seed_mass_alpha=0.1 で seed 段階 raw_cosine + α*log(1+mass) 再 rank scoring 改善は確認 (5x) だが、sparse class の embedding 距離問題は超えられず
Phase H Stage 2 — source-aware seed filtering (2026-05-10) source_filter 指定時に cache.source_by_id で seed pool から source 一致のみ抽出 23k corpus-heavy DB で初の agent class surface 達成。wave_k_with_filter=500 既定 (sparse class ~1.7%、expected 8.5 件)
Phase H Stage 3 — density-aware dynamic wave_k (2026-05-10) top-N の tail/top 比率で sparse 判定、wave_initial_k_max=50 まで拡大 query が embedding 空間の sparse 領域に着地した場合の reach を救う保険
Phase H Stage 4 — virtual FAISS (2026-05-11) 第二の FAISS index を virtual_pos = raw + displacement で構築、seed pool は raw + virtual の union priming で動いた displacement が seed step に効くようになり、本番 filter=none top1 score が 5.6x 改善
Phase I Stage 2 — implicit query-aware kick (2026-05-11) compute_acceleration に 4 番目の項 a = (α · score / m_i) · (q - pos_i) を追加 (query_kick_strength=0.01)。recall が retrieved nodes の displacement を query 方向に nudge する TTT 解釈の「retrieval = gradient step」が構造的対応の主張ではなく 実装として literal に成立。F=ma が mass damping を物理的に供給するので BH は動かず軽い node のみ反応。Hooke (項 2) が raw anchor を引き続き保持するので transient force であって anchor migration ではない (concept drift しない)。explicit kick() ツール案 (option B) は J2 bootstrap curator 批判の再現を避けるため不採用、implicit が選ばれた。詳細: Plans — Phase I — Free Star Movement §Stage 2
Phase I Stage 3 — mass-gated query attraction (2026-05-13) 第 4 項に gate = tanh(m_i / θ) を乗じる (mass_anchor_threshold=3.0)。新規 (m≈1) ノードは gate≈0.32 で 32% に減衰、mature (m≫θ) ノードはほぼ満額。θ=0 で Stage 2 へ rollback Stage 2 の副作用「単一アトラクタ pathology」(新規 m=1 ノードが初回 recall で a=α フルスケール drift → 以後全 query の top1 を独占する正のフィードバック) を物理的に矯正。「足りない保護も active な過剰駆動と同じ症状を引き起こす」 — Stage 1 が学んだ「冗長な制約は active な制約と同症状」の対称形 lesson。F=ma は破らず anchor 不変も維持、追加コード ~10 行で最小実装。詳細: Plans — Phase I — Free Star Movement §Stage 3
Phase I Stage 4 — mass-dependent Hooke (2026-05-14、opt-in default) compute_acceleration の第 2 項 (Hooke) を acc -= k · (1 + β · (1 - tanh(m_i / θ))) · displacement_i に拡張 (mass_anchor_extra_strength=0.0 default、mass_anchor_threshold は Stage 3 と共有)。β=1 で m=1 ノードの effective k が約 1.68 倍、m≥10 でほぼ legacy。β=0 で Stage 1-3 へ bit-for-bit rollback、θ=0 の場合は安全 fallback θ_eff=1.0 で divide-by-zero 回避 Stage 3 は kick を mass で gate したが、Hooke は等しく扱われ続けた非対称を解消 — 同じ θ を共有することで、kick の damping (tanh(m/θ)) と Hooke の amplification (1 - tanh(m/θ)) が双方向から同じ gate に乗る generational physics 設計同型。Stage 3 と違い観察 pathology はなく prophylactic refinement なので β=0 default、Stage 3 単独で 1-2 週間運用後に tests/perf/test_tier4_*.py で displacement 分布を見て活性化判断する手順。mass_conservation_enabled と同じく「対称性が観察を待たずに見えた refinement は記録に残し default は触らない」運用 pattern。詳細: Plans — Phase I — Free Star Movement §Stage 4
Phase J Stage 1 — persona-anchored seed boost (2026-05-13) 新規 core/persona_gravity.py で declared value/intention/commitment から fulfills/derived_from/completed を N hop traverse (default 2, decay 0.5)、propagate_gravity_wave の seed step に α_persona × proximity を追加 (persona_boost_alpha=0.5)。CacheLayer に directed_out/directed_in を Phase H Stage 2 の source_by_id と同じ pattern で mirror、startup load + relate/unrelate で同期。recall API 変更なし (Stage 1 は内部 auto-detect のみ) Phase I Stage 3 acceptance で観察された「dense mature agent cluster が sparse new agent cluster を押し退ける」現象 — Phase H Stage 2 の source_filter は同種内 (agent vs agent) で識別不可、Stage 3 の mass gate は mature 側を damping しない。declared identity から graph で繋がるノードを seed pool で優先することで「Five-Layer の人格層を retrieval geometry に literal に翻訳」。persona_boost_enabled=False で 1 行 rollback。詳細: Plans — Phase J — Persona-Anchored Retrieval
Phase K Stage 1 — stellar supernova cohort (2026-05-13) 新規 core/supernova.py で index_documents の batch を 1 超新星イベントとして読み、batch 内全員 (N≥2) に supernova_initial_weight=1.0 の相互 co-occurrence edge と centroid からの outward velocity = α × (emb-centroid) (supernova_velocity_alpha=0.03, orbital_max_velocity で clamp) を付与。Phase G genesis kick の直後に適用 (個別重力 → cohort 重力の合成)。supernova_enabled=False で rollback Phase J Stage 1 acceptance で「persona boost は pool 内 rerank のみで pool injection しない」が露呈、新規ノード群が 互いに重力を持たない散発的塵 だと dense mature cluster に競合できない。pool injection を運用回避策で済ますのではなく、記憶生成の物理そのもの を修正。Phase G genesis kick の集合版、Articulation as Carrier の複数性を物理化 (1 batch の remember は 1 つの宇宙論的イベント)。詳細: Plans — Phase K — Stellar Supernova Cohort
Phase J Stage 2 — explicit pool injection (2026-05-13) recall API に persona_context: list[str] | None + tag_filter: list[str] | None を追加 (MCP + REST parity)。cache.tags_by_id を Phase H Stage 2 source_by_id と同 pattern で持ち、tag_filter 経由で OR-substring 一致した node を seed step + final result の 両段階で force-inject。source_filter を bypass (caller の explicit ask が勝つ)。Phase K Stage 1 で「new cohort 同士の重力」を解決、Phase J Stage 2 で「embedding 距離が遠い query から既存 cohort への到達権」を解決 Phase K Stage 1 acceptance (retrospective ritual で 6216 edge + 112 velocity 追加後も 0/7) で「embedding 距離が dominant、boost は pool 入場後にしか効かない」と判明。LLM が「今の文脈 (intention/tag)」を明示的に伝える path を API として正面に位置づける。Stage 1 auto-detect は引数省略時の default として残る (backward compat)。詳細: Plans — Phase J Stage 2 §Stage 2
Phase J Stage 3 — forced 内 query-aware ordering + prefetch/explore parity (2026-05-13、Phase J 完遂) engine.py Step 4 で forced 内を raw_score 順 (query semantic) に並べる。final_score は mass/wave/emotion/certainty が dominant で「触りやすい memory」が勝つので、tag 一致の中での query 関連度 が見えない問題を解決。prefetch + explore にも persona_context + tag_filter を追加 (MCP/REST parity)。types.PrefetchRequest / ExploreRequest 拡張 Stage 2 acceptance で「top5 に tag 一致 surface ✅」と「top1 に正解 ⚠️ (1-2/7)」が分離した結果から導出。retrieval geometry の三段構造 (pool 入場 / pool 内 rerank / forced 内 ordering) を完成、各段で独立した signal が機能する設計に。詳細: Plans — Phase J Stage 3 §Stage 3
Phase M Stage 1 — Mass Conservation + mass-based BH (2026-05-13) gaottt/core/gravity.py に is_self_force_by_id(cache, a, b) (original_id または cohort_id 一致を検出) と bh_factor(mass, θ, σ) = tanh((mass-θ)/σ) clamped at θ-2σ を追加。propagate_gravity_wave に optional out_attribution 引数を追加し per-parent 力寄与を返す。engine._update_simulation の mass update で attribution を走査し、self-force 寄与をスキップ (temperature/sim_history は total force のまま — フィルタは mass update のみ)。共起 BH (compute_bh_acceleration) は呼び出し削除 + 関数定義も削除、置換として compute_mass_bh_acceleration(pos_i, neighbors, config) を compute_acceleration 第 3 項に統合 — source 分岐ゼロ、mass しきい値だけで attractor 化。cache.set_original/set_cohort + SqliteStore.get_all_originals/get_all_cohorts (JSON1、COALESCE(metadata.original_id, metadata.file_path) で既存 DB 無 migration)。ingest/loader.py の md/plaintext/csv が original_id を明示付与。engine.index_documents が supernova batch で cohort_id = uuid4().hex[:12] を生成し metadata + cache に書き込み。reset API: engine.reset_masses / engine.reset_orbital_state → REST POST /admin/reset_masses (MCP 非露出 — 破壊的、LLM 用途なし)、scripts/reset_masses.py CLI。mass_conservation_enabled / mass_bh_enabled で個別 rollback、mass_bh_theta=5.0 / mass_bh_sigma=1.5 暫定 Phase L Stage 1 acceptance で観察された「source=file/tweet が agent 知識の top1 を奪う」構造的根を追跡し、1 file = 91 chunks 平均の内輪取引による mass inflation を発見。「経験は言葉にすることで初めて重力を持つ」(Articulation as Carrier, id=9a954c62) を「言葉にした上で誰かに引かれることで mass を持つ」と精密化、if not is_self_force(...) の 1 行が哲学の literal な物理実装になる稀な設計同型。source 分岐は単一性を破るため絶対回避 — 構造的識別子 (original_id/cohort_id) だけで全 source class に普遍適用。BH 化候補が source=file に偏在する問題は「mass inflation 自体を止める」で根本解決、persona class も別格扱いしない (「使用頻度こそが重力」「埋もれる自由」)。詳細: Plans — Phase M
Phase M Stage 1 rollout — versioned wizard migrations (2026-05-13) scripts/migrate.py の framework に Migration.critical: bool + warning: str を追加し wizard mode 化。--apply の既定で critical step (M002/M003/M004) 毎に [y/N] を出す (Operations — Migration)。--yes / --skip-critical で CI 自動化。新規 4 step 追加: M002 phase-m-bh-residue-cleanup (engine.reset_orbital_state → 全 node の displacement/velocity を 0)、M003 phase-m-mass-reset (needs_apply を常に True にして全ユーザーに wizard で聞く、threshold-based auto-skip を廃止)、M004 phase-m-cosmic-bang (compute_supernova_velocities を corpus 全体に 1 回適用、cold state 点火)、M001 guard (mass_conservation_enabled=True または M002 in ledger で SKIP、Phase G 遡及 priming は Phase M roll-out 4 step に superseded)。各 step は _migrations ledger に記録され idempotent、自動 backup 付き Mass migration を運用 op として落とし込む。個別 destructive script (reset_masses.py / reset_displacements.py) はあるが、Phase M roll-out は 4 step を 正しい順序で 適用する必要があり (M002 で wipe → M003 で mass=1 → M004 で point ignition、M001 の Phase G priming は同居すると逆効果)、それを wizard で表現する。M003 を threshold で auto-skip しない判断は「Phase M Stage 1 は全ユーザー対象の one-time roll-out なので、私の DB の数値に tune した threshold で他人の意思決定を奪わない」原則 (めいさん 2026-05-13)。M004 は「migration 直後の cold cosmos で Newton 1 法則が emergent dynamics を窒息させる」観察を直接の動機 (めいさん 2026-05-13)。詳細: Plans — Phase M §Stage 1 ロールアウト
Sphere-wrap + geodesic curves in viz (2026-05-13) scripts/visualize_3d.py の default を unit sphere geometry に切り替え。PCA/UMAP の 3D 出力を sphere_wrap で L2-normalize → 半径 1 に貼る、faint な lat/lon wireframe (8 parallels × 12 meridians, alpha=0.10) を背景に置く、filament は slerp_arc (大円弧、10 点) で球面に貼り付け、velocity は tangent_geodesic (接空間 projection → |v_t| rad の大円歩行、8 点) で表現。compute_bh_centroids_3d (旧共起 BH の centroid 計算) は削除、新規 compute_mass_bh_nodes (bh_factor(mass, θ, σ) > 0 を満たすノード自身を BH として diamond + gravity well 表示)。CLI: --flat で legacy 無拘束 3D に戻す、--straight-lines で sphere 維持しつつ chord 描画 (file size ~50% 削減) 「埋め込み空間は単位球面 (RURI が unit-norm を吐く)、Phase J/M の物理は球面上で動く」が PCA-projected 平面 cloud では見えない。球面そのものが大質量 BH のように動作する (めいさんの reframe、2026-05-13) を viz が literal に embody することで設計言語と視覚言語が一致 (design-literal-correspondence 原則の延長)。Phase M で旧共起 BH を削除した時に viz だけが purple diamond を描き続けていた gap も同時に解消 — node が mass > θ-2σ で literal に BH 化される新規則と viz が連動。File size は curves で ~2x になるので 24k 全件は --sample 5000 か --straight-lines 推奨。詳細: Guides — Visualization
Phase L Stage 1 — Hybrid retrieval (BM25 union seed) (2026-05-14、完了) 新規 gaottt/index/bm25_index.py (numpy in-memory BM25) + gaottt/index/tokenizer.py (char 3-gram default、Sudachi optional extra bm25-sudachi)。gravity.py:_union_pool を raw ∪ virtual ∪ BM25 の 3-way に拡張、bm25_score_mode="rrf" (default、Cormack 2009 標準 rrf_k=60) で Reciprocal Rank Fusion、"weighted_sum" flag は legacy 2-way max-merge + BM25 normalize blend。BM25 lifecycle (startup build / index_documents / archive / restore / forget / merge / compact rebuild) を engine 全段に配線。engine.py:_rrf_forced_key で Phase J Stage 3 の forced ordering 段にも BM25 rank を反映 (acceptance 中 opencode sub-agent が gap を発見し追加修正)、retrieval geometry 三段構造の段 1 (pool 入場) と段 3 (forced ordering) の両方で BM25 が literal に効く。hybrid_bm25_enabled=False で 1 行 rollback (Phase H Stage 4 max-merge + Phase J Stage 3 cosine ordering 同等挙動)。Stage 1 は disk persistence なし (D2)、startup で SQLite content から in-memory rebuild。Bench p50 threshold を 50ms → 60ms に引き上げ、SC-002/Baseline の n=1 doc 偶然依存ロジックを Phase L-aware に修正。本番 acceptance: Surface 7/7 ✅ / Semantic 整合 strict 4/7 (Phase J Stage 3 時 0-1/7 から +3-4 改善) / top3 緩和 7/7 で機構として完成 Phase J Stage 3 完遂後の本番 acceptance で「Surface 7/7 ✅ / Semantic 整合 0-1/7 ⚠️」分離が露呈 — embedder (RURI v3 310m) の hidden ranking が dominant signal で、Phase J 三段構造 (pool 入場 / pool 内 rerank / forced 内 ordering) を どの段で動かしても解決しない構造的境界。「Eleventy Pipeline」のような surface-form 強い query は lexical metric tensor (BM25) が直接 catch すべきで、embedder layer 内で literal に拡張するのが design-literal-correspondence 原則。三段構造のような独立段では、新機構を導入したら全段への伝播を point-by-point で検証する必要があると判明 (元 Stage 1 設計は段 1 のみ手当てして段 3 を見落としていた)。Stage 2 候補: 別 embedder (e5) 追加、Stage 3 候補: query expansion or LLM rerank (Phase M に分離可能)。詳細: Plans — Phase L — Hybrid Retrieval §Stage 1 完了宣言
Phase Q — Orbital Mechanics (2026-05-30、core + 本番実測 + rework 完了 / 本番 opt-in) ノードを 自分の anchor を中心に閉軌道(楕円)で公転 させる保存系レジーム。gravity.py に _perpendicular_unit(g)(決定論的、RNG 無し)を追加し compute_gravity_kick / supernova.compute_supernova_velocities の seed で displacement(∥ gravity radial)と非共線な接線速度を付与(L = d × v ≠ 0、orbital_tangential_alpha)。update_orbital_state に velocity-Verlet 分岐(symplectic、orbital_integrator="verlet")。engine._orbital_tick を dream loop に配線し lively set(|v| > orbital_lively_v_min)だけを recall 抜きで連続積分(recall = エネルギー注入 / tick = 自由発展、age friction は tick 内で強制 0)、orbital_tick_max_nodes で cost bound。本番実測の rework で tick 近傍重力は default OFF(orbital_tick_neighbor_gravity_enabled、flag OFF 時 tick 内 gravity_G=0 → 純 self-anchor 楕円公転)。全 default OFF(orbital_tangential_alpha=0.0 で bit-for-bit rollback) 「重力に捕捉されて井戸へ落ちる」だけだった星を 公転 させたいという動機から、Bertrand の定理で Hooke アンカー F=-k·d がすでに閉軌道を生む等方調和振動子だった と判明 — 足りないのは新しい力ではなく 角運動量(接線速度)だけ。公転中心 = 自分の articulated self なので anchor migration ゼロ(衛星化・彗星脱出は公転中心が他者 = Phase M 単一規則違反の「線の外」、Phase Q は内側に留まる)。Articulation as Carrier の力学版。Stage 3 の発見: orbit mode では近傍 1/r² 近接特異点が velocity clamp で止まらない外向きドリフトを生むため max_displacement_norm の有限化が必須(__post_init__ が警告)— Phase I の 1e6 は relax regime 限定だった。本番実測 (2026-05-30) の発見: _orbital_tick が散在 lively set を相互近傍として扱う実装は、RURI の狭 cosine 帯で近傍重力が coherent に加算し anchor を ~1000倍 上回り(net |a|~10-640 vs 0.005)displacement を clamp に張り付け self-limiting を殺す。純 self-anchor(G=0)は bounded + self-limiting で健全 → tick 近傍重力を default OFF 化(measurement-first 運用が本番 mutate 前に捕捉)。rosette precession は要・tamed 近傍重力で future work。詳細: Plans — Phase Q — Orbital Mechanics
Phase Q2 — Gravitational Scale Governor (2026-05-30 設計+実装、2026-05-31 本番 LIVE + code default ON 昇格) compute_acceleration で attractive 近傍力(近傍重力 + mass-BH)を per-node で g_i = min(1, α · k_eff(m_i) · max(‖d_i‖, d_floor) / ‖acc_neigh‖) で cap する密度適応オートゲイン(gravity_neighbor_governor_enabled / _alpha=0.2 / _disp_floor=0.1)。方向保存、anchor / query 引力 / Λ は cap せず、source 分岐ゼロ。M006 velocity cooldown(velocity=0、displacement 保全)とセットで rollout。2026-05-31 に code default を OFF→ON へ昇格(gravity_neighbor_governor_enabled=False で bit-exact pre-Q2 legacy)。Phase I query-kick の integration test は governor を明示 OFF にして isolate(governor 自体は tests/unit/test_phase_q2_governor.py で担保)。 Phase Q rollout finding(§8)の「tick 近傍重力が暴走」の根を、めいさんの診断「重力の重みが宇宙のスケールに合っていない」を literal に受けて解いた — 密な RURI 宇宙では近傍重力が coherent に加算(coherence ~0.8-1.0、net∝N)し anchor の 10⁴-10⁵倍・重い裾(p9010⁵)で単一大域定数では飼えない → per-node anchor 基準 governor。段階4 の核心発見: governor は単一クエリ ranking 中立(ranking は mutation 前に確定、governed force は post-ranking の _update_simulation でのみ効く)だが、過大近傍重力に踏み潰されていた query 引力(Phase I Stage 2 の literal な TTT 勾配項)を un-mask する(drift OFF 0.018 → ON 0.832、top-5 安定)— 劣化でなく意図された学習機構の回復、GaOTTT thesis(Gravity as Optimizer, TTT)に literal 整合。「新 field は default OFF」規約からの意図的 promotion: 段階4 acceptance + 本番 live healthy(velocity baseline 飽和ゼロ、max 0.012 ≪ clamp 0.05)を承け owner 判断で default ON。velocity の向きは「現在の gravity の影」(pass-6 cos≈0.87)で再生成可能なので M006 cooldown は安全。残り = 1-2 週観測 + α tuning。詳細: Plans — Phase Q2
逆方向 cache 上書きの罠 (2026-05-10 発見) bulk 書き換え (Stage 0 priming 等) は他 MCP server プロセスを kill してから実施 古い cache を持つプロセスが flush し続ける限り新しい書き込みを上書きする。CLAUDE.md と Architecture-Concurrency.md に記載
Phase T — 意味的観測と重力場の分離 (Semantic Requalification) (2026-08-25) retrieval 品質を「意味的観測が資格を判定し、重力場が資格の中で順位を育てる」層分離に再編。Stage 2 (default ON): semantic decay を秒 rate delta (exp(-δ·age)、10 分で factor≈0.0025) から half-life + floor 契約 (floor + (1−floor)·0.5^(age/halflife)、7 日 / 0.35) へ — 経年記憶の semantic 項が数値上消滅し mass/wave が順位を完全支配する欠陥の修正 (semantic_halflife_enabled=False で bit-for-bit rollback + deprecation warning)。Stage 3 (default OFF・env opt-in — Phase U 2026-08-26 に default ON 昇格): direct 候補の relevance qualification (3 軸 OR: raw_cos / 正規化 virtual_cos_norm / BM25 lexical strength の absolute+relative 二重条件) で qualified-first 順序 + fallback を breakdown qualified=false + reason 行 gravity pick (below relevance floor) で明示。Stage 4 (default OFF — Phase U 2026-08-26 に default ON 昇格): TTT 更新のうち query-conditioned 項 (mass growth ×confidence / query kick / cooccurrence) のみ qualified learn set に限定 — bad gradient の自己強化を遮断。maintenance 系 (last_access / evaporation / sim_history+temperature / orbital N-body) は all-reached のまま、dream loop の synthetic recall は免除。Stage 5 (default ON): ambient gate を BM25 veto から OR gate へ (BM25 reject でも passive recall が走り virtual_score OR raw_cos で accept) + gate_diagnostics (staged counts + gate 入力 + 離散 empty_reason) で空返しの段階診断。sentinel (関連する記憶なし) は一字不変、診断行は expose_breakdown=True 時のみ。Stage 6 (default OFF — Phase U 2026-08-26 に default ON 昇格): explore の engine 層 MMR presentation (pool 拡大 / explore_min_semantic floor / cohort penalty、diversity=0.0 は完全 legacy)。knob 一覧: Operations — Tuning §Semantic Requalification 2026-08-25 の SQLite/FAISS 不整合障害 (silent semantic search failure) 復旧後も「semantic 項の消滅」「低 relevance・高 mass ノードが direct top-K を占有し active recall で自己強化」「ambient BM25 veto で関連ノードが存在しても無言の空返し (baseline で 19/19 on-topic golden query が veto、gate score 5.8-8.8 vs 閾値 32.0)」が残り、いずれも 重力場が意味的適格性の判定まで兼ねていた 構造に起因すると特定 (WP-1 baseline、scripts/score_baseline.py)。設計原則: qualification は score 値のみで判断 (source 分岐ゼロ、Phase M 単一規則不変)、seed/wave 到達性には触れない (qualification は final selection のみを改善)、breakdown/formatter 変更は追加のみ。default ON/OFF は golden corpus baseline + tier3/6/7 + dogfooding の較正で確定 (Stage 2/5 = 再現済み欠陥の修正なので ON、Stage 3/4/6 = 効果観測後に昇格)。REST/MCP parity (breakdown 拡張 field / gate_diagnostics 両方)。詳細: Plans — Phase T
injected_ids の initial_k キャップ問題修正 (2026-05-12) gravity.propagate_gravity_wave の wave 終了後に injected_ids 欠落 node を reached[nid]=1.0 で強制追加 Phase J Stage 2 の tag_filter / persona_context で注入した node が initial_k 上限を超えると reached dict に入らず scoring から除外される問題。FAISS にベクトルが存在していても surface しない非直感的挙動だった。詳細: Operations — Troubleshooting §tag_filter
_rebuild_faiss_index の再埋め込み対応 (2026-05-12) engine._rebuild_faiss_index が FAISS 欠落 node を store.get_document() → 再埋め込み → FAISS 追加するパスを追加 compact(rebuild_faiss=True) が「FAISS に既存のベクトルを再配置するだけ」で write-behind フラッシュ前に終了したプロセスの node を復旧できなかった。修正後は SQLite 全 active node を確実に FAISS に収録する。scripts/verify_faiss_recovery.py で診断可。詳細: Operations — Troubleshooting §FAISS
7 階層テストスイート (2026-05-14) tests/perf/ に Tier 1/3/4/5/6/7 を 38 tests 整備、各 Tier は対応する事件群 (FAISS 空、WAL 暴走、RRF scale 不整合、hub chunk 独占) が起こったときに鳴る。real RURI v3 310m を session-shared singleton で使い、~15 秒で全件、engine.query 全段で production-grade な数値 (recall p50 ~35ms、ingest ~1200 docs/sec) を測る。仮説 → 実装 → 検証 ループの 検証 step で手動実行、CI 自動化は意図的にしない (deliberate な measurement、PR 通過で薄められる類の signal じゃない)。scripts/perf_baseline.py + perf_diff.py で before/after diff、scripts/diag_recall.py で per-query 3 layer snapshot 当初 (2026-05-14) StubEmbedder + CI workflow で実装したが、めいさんの「実際と違う環境でテストするのは何故か、性能評価は仮説 → 実装 → 検証の検証 step」指摘を受けて real RURI 化 + CI 削除 + budget を CLAUDE.md p50<50ms target ベースに再校正。設計案 memory id=55579286、Stage 1 完了 id=c356abe5、Stage 2/3 完了 id=3eb313b9、real RURI 化 + reframe id=(this commit に紐づけ)、commitment id=faf61f5f。REST/uvicorn stack 込みの e2e は scripts/run_benchmark_isolated.sh を併用 (engine 直叩き vs server stack 込みの関係)。詳細: Operations — Performance Testing
Phase O Stage 1 — Score breakdown observability (2026-05-14、default ON) gaottt/core/types.py に新規 ScoreBreakdown Pydantic model (11 field + expected_sum プロパティ) を追加、QueryResultItem.score_breakdown / MemoryItem.score_breakdown を optional で attach。engine._query_internal の scoring loop で per-node に (raw_cosine, virtual_cosine, decay_factor, wave_score, mass_boost, emotion_term, certainty_term, saturation, persona_proximity, bm25_contributed, forced_inclusion) を捕捉、expected_sum = (virtual_cosine · decay_factor + wave + mass + emo + cert) × sat が rel_tol=1e-4 で final_score を再現。MCP formatter は既存 [i] id=... 行の 下に新規 1 行 ("breakdown: cos=... vcos=...·decay=... +wave=... +mass=... ×sat=... persona_prox=... [bm25,forced]") を挿入、既存 substring assertion を壊さない。REST POST /recall は Pydantic auto-serialize で items[].score_breakdown に dict を返す。expose_score_breakdown=False で None 返却 (legacy 互換) LLM caller を TTT loop の participant に昇格 させる構造的措置。Phase I Stage 4 本番 acceptance (Sonnet) で観察された「final_score=0.92 が semantic-unrelated content を伴う」(score deception) と「persona invisibility」は engine bug ではなく caller-awareness の欠如 — breakdown.raw_cosine が見えれば「mass で勝った semantic 弱者」を if breakdown.raw_cosine < 0.3: skip 的に literal に避けられる。設計 draft からの修正点: persona/bm25 は _seed_boost 経由で wave_score に baked-in されており独立 additive 項に分離できないため、informational field として persona_proximity (float) と bm25_contributed (bool) で露出。forced_inclusion を bool で追加し tag_filter / persona_context 由来かを明示。設計同型: Phase I Stage 2 で実装した「retrieval = gradient step」の caller-side closure — gradient (forward の中身) を見せる Stage 1 と、parameter update (backward の中身、Stage 2 で実装予定) を見せる Stage 2 が補完して TTT loop が caller を含めて閉じる。詳細: Plans — Phase O §Stage 1
Phase O Stage 2 — Training delta trailer (2026-05-14、default ON) gaottt/core/types.py に新規 TrainingDelta Pydantic model (8 field) を追加、RecallResponse.training_delta / ExploreResponse.training_delta を optional で attach。engine.query / _query_internal に optional out_training_delta: dict | None = None を追加 — caller 側 (services 層) が dict を作って engine に渡し、engine は _update_simulation 呼び出しの前後で displacement_norm + mass を snapshot して差分を populate。topk_only=True (default) で result_ids のみ、False で all_reached_ids 全件をカバー。persona_hop_reached は persona_proximities[nid] > 0.0 を満たす reached node の count、cache_hit=True は prefetch cache 経由で simulation を suppress したことを明示。MCP formatter は recall/explore body 末尾に新規 ## 訓練差分 セクションを append (cache hit / top-3 movers by absolute Δ / coverage label の 3 case 対応)。REST は Pydantic auto-serialize で training_delta に dict 返却。training_delta_enabled=False で None 返却 (legacy 互換) Stage 1 で「forward の中身 (gradient)」を露出した対称形として、backward pass の中身 (parameter update) を露出する。これで「retrieval = gradient step」の設計同型 (Phase I Stage 2 で literal 成立) が caller を含めた閉ループ になる — LLM が「自分が今この memory を 3 回 recall して mass +0.15 押し上げた」と自覚でき、deliberate な Hebbian rehearsal が可能になる。設計 draft からの修正点: cache_hit bool field を新規追加 (prefetch ヒット時の「update suppress」と「触れる node 無し」の意味分離は必須)、topk_only を Pydantic model にも露出 (response から caller が直接読める)、engine.query は return type を維持して optional out-param で delta を返す方式 (既存 caller 影響ゼロ)。コスト構造: snapshot O(top_k) × 2 (前後)、payload top_k=5 で ~250 byte 増、topk_only=False は debug 専用 (reached が数百件規模になる)。詳細: Plans — Phase O §Stage 2
Phase O Stage 3 — Query routing (recall + reflect auto-merge) (2026-05-14、default ON) 新規 gaottt/services/query_routing.py (compiled regex pattern → aspect name の classifier、JP + EN 両対応、word-boundary 込みで 15 pattern)、新規 RoutingHint Pydantic model 4 field を RecallResponse / ExploreResponse に attach。services/memory.recall / explore は per-call auto_route flag + config.auto_route_enabled を見て、surface form が aspect ("commitments" / "values" / "intentions" / "tasks_doing" / "tasks_completed" / "tasks_abandoned" / "tasks_todo" / "relationships") に match したら 対応する reflect aspect を並走実行 し、formatted summary を routing_hint.reflect_summary に詰める。reflect dispatch は services/reflection.dispatch_aspect() に集約し MCP server _reflect_dispatch も同じ table を共有 (drift 防止)。MCP formatter は recall/explore body 末尾に新規 ## 関連 reflect サマリ (auto-routed) セクションを append (item 0 件でも routing summary は surface する — Phase O Stage 3 の存在意義そのもの)。REST は Pydantic auto-serialize。legacy /query endpoint は auto_route=False 固定で fast path のまま LLM caller の 認知負荷を engine 側に押し戻す 構造的措置。Sonnet 本番 acceptance で観察された「現在 active な commitment は?」→ recall で startup-diagnostic 提案 (正解は reflect(aspect='commitments')) のような surface-form / tool-choice mismatch を、caller が毎回 judgement する代わりに classifier が裏で reflect を並走実行することで吸収。判定は query 形式 (caller の surface form) が基準で、physics rule (mass update / Hooke / kick) は一切触らない — Phase M の「source 分岐ゼロの単一規則」を侵さない (caller intent layer の routing であって、source class を gate にする branching ではない)。設計 draft からの修正点: 当初 query 形式パターンは 6 程度の想定だったが、JP / EN 両対応 + word boundary 込みで 15 patterns に拡大 ("abandoned" が "done" を substring 包含する罠などを word boundary で防止)。KNOWN_ASPECTS を tuple で公開、テストが routing 結果と aspect 名の drift 検証 に使う。RoutingHint.pattern_matched と .auto_routed を独立 bool に分離 — 「router が off だった」「pattern に一致しなかった」を caller が区別可能。reflect の per-aspect dispatch table を services/reflection.dispatch_aspect に集約し、MCP server _reflect_dispatch と recall auto-router で 同じ呼び出しテーブルを共有 (将来 aspect 追加時の drift 防止)。コスト構造: classifier 自体 < 10 µs (15 compiled regex)、match 時の reflect は対象 aspect の cost (1k node で数 ms、22k node で 100ms 以下)、payload ~ summary 1 件分。詳細: Plans — Phase O §Stage 3
Phase O Stage 4 — Recall list mode (2026-05-14、opt-in default detail) RecallRequest.mode: str = "detail" 追加 ("list" で各結果の content を config.list_mode_excerpt_chars (既定 80) 字に切り詰め、改行を空白に置換)。truncation は services/memory._to_memory_item に excerpt_chars kwarg として注入 (service 層で実行 → MCP/REST 両方で同じ payload が wire に乗る)。MCP recall tool と REST /recall body に mode arg、gaottt/config.py に list_mode_excerpt_chars: int = 80 追加。mode='list' は既存 output_mode (MCP-only formatter truncate) とは 独立: mode は service 層 (wire truncate)、output_mode は MCP 表示制御 — 同時指定可能、効果は compose recall(top_k=20) の典型 payload (agent memo 平均 240字 × 20 ≈ 5 KB) を mode='list' で ~1.8 KB に圧縮、「id 列を scan → 興味あれば deep dive」の 2-step pattern を支える。Phase I Stage 4 本番 acceptance (Sonnet) で観察された「top_k 大きく取りたいが context 圧迫する」を service 層で解決 — MCP-only output_mode="compact" は他 transport (REST) に効かなかった盲点を埋める。設計 draft からの修正点: 当初 plan では QueryResultItem.content=None + excerpt 別 field の想定だったが、Pydantic schema 互換性を守るため content 自体を truncate する方式に変更 (新 field 追加なし、既存 caller への影響ゼロ)。output_mode との コンセプト分離: 「service 層 = 何を返すか (wire 上の payload)」「formatter 層 = どう見せるか (MCP 文字列)」 — この層分離は Phase S の services/server 分割と整合
Ambient Recall Enrichment — 構造化スロット注入 + 重力レンズ (2026-05-21) 新サービス services/memory.ambient_recall() + 新 MCP ツール ambient_recall + REST POST /ambient_recall(パリティ)。1 回の passive recall から多スロット <gaottt-ambient-recall> ブロックを組む: ① 直接ヒット(final_score 上位)/ ② 重力レンズ枠(virtual_cosine − raw_cosine gap 最大)/ ③ メタ注釈(source·certainty·age)/ ④ 理由の連鎖(derived_from/supersedes 1-hop の because)/ ⑤ 矛盾フラグ(contradicts ペア)/ ⑥ 人格行(active な declared value/intention)。relevance gate(virtual_score の max < ambient_min_score で空応答)と整形はサービス側、フックは「ブロックが返ったら emit」だけの薄いラッパに。スロット単位 config フラグ(ambient_*)で個別 rollback。新型 AmbientMemory/AmbientTension/AmbientPersona/AmbientRecall{Request,Response}、formatter format_ambient。 Ambient Recall の v1 が「フラットな top-k」を注入していた限界(live acceptance で「関連はするが核心でない記憶」が surface)を、注入を 構造 にして解消。設計の核は重力レンズ ② — Phase I/J の displacement が embedding 的に遠い記憶を query 近傍へ曲げた、その「場が学習した類推」を virtual − raw gap で拾う。普通の RAG はクエリ類似度でしか引けないが、GaOTTT は重力場が co-recall 連想を学習しているので「テキストには無いが構造的に近い」記憶を surface できる — ambient recall を serendipity engine にする枠。重力レンズ(質量場が光の経路を曲げ視線外の天体を見せる)の物理アナロジーで命名。relevance gate は語単位(Sudachi)BM25 の「強一致」gate — 当初 virtual_score(dense cosine)設計だったが本番 32k コーパス校正 (2026-05-21) を 4 ラウンド回し確定。dense cosine も char-3gram BM25 も on/off-topic を分離できず(前者は温度ノイズ埋没、後者はクエリ長依存で長い off-topic が on-topic を上回る)、語単位(Sudachi)BM25 だけが「卵焼き」を単一の rare 語として扱い分離できた(off-topic ≤29 / 強い on-topic ≥34、閾値 32)。核心の発見: この corpus はユーザーの生活+仕事まるごとで真の off-topic は存在しない — gate が分けるのは「on/off-topic」でなく「強一致 vs 弱一致」、高精度・低再現。実装は Phase L の char-3gram bm25_index と独立した専用 word-level ambient_gate_index。virtual_score は recall ランキング の正しい信号であり続けるが二値 gate は別問題、が学び(feedback-gate-on-virtual-score の精緻化)。ambient_gate_use_bm25=False / bm25-sudachi extra 未導入で virtual_score gate にフォールバック。詳細: Plans — Ambient Recall Enrichment
Ambient Recall — passive (read-only) recall + Claude Code フック (2026-05-21) recall に passive: bool = False を追加 (RecallRequest / services.memory.recall / engine.query / engine._query_internal / MCP recall ツール / REST /recall を貫通、新引数は全て optional)。passive=True で _query_internal 末尾の _update_simulation() + _update_cooccurrence() を if not passive: でスキップ — 検索・wave・scoring はそのまま走り結果も同一、副作用 (mass 更新 / query attraction displacement / co-occurrence edge / last_access 更新) だけ抑止。query() は passive 時 prefetch cache を読むが書かない (use_cache and not passive) — passive 結果が後続 active recall に cache hit させ simulation update を握り潰すのを防ぐ。フック scripts/hooks/ambient_recall.py は Claude Code UserPromptSubmit で発火、mcp SDK で proxy backend (7878) に streamable-http 接続し recall(passive=True) を呼び、relevance gate (top-1 virtual_score ≥ 閾値) を通った結果を <gaottt-ambient-recall> ブロックで文脈注入。 観察行為が観察対象を変える (P7-Z) を機構で閉じる。 ambient recall は毎ターン・全プロンプトで発火するので、通常 recall を使うとノイズクエリ (「ls して」等) が毎ターン重力場を学習させてしまう — acceptance test を別プロセスに隔離してまで守ってきた observer effect を Claude Code 本体が破る。passive recall = 「摂動なしの観察」で、ambient な大量クエリが無制御の TTT シグナルになるのを防ぐ。設計判断: passive は source 分岐ではない — Phase M の「source 分岐ゼロの単一規則」は physics rule に対する制約、passive は physics rule を一切変えず どの caller がどの目的で recall するか で副作用の有無を切り替える operation-level な flag (cache hit が常に副作用ゼロなのと同じ既存セマンティクスの一般化)。v1 は完全 read-only — down-weighted な分数勾配 (attraction_scale) は将来拡張。relevance gate が final_score でなく virtual_score を見るのは、decay 非依存の意味的類似度こそ「この記憶はプロンプトの話題か」を答えるから。詳細: Guides — Ambient Recall
Ambient Recall — opencode chat.message プラグイン (2026-05-21) Claude Code UserPromptSubmit フックと同等の受動的文脈注入を opencode に追加。scripts/hooks/opencode-ambient-recall.ts(@opencode-ai/plugin の chat.message フック)が新ユーザーメッセージを受け、テキストパートを連結し、Claude Code 版の Python フック scripts/hooks/ambient_recall.py をそのまま Bun.spawn で子プロセス起動して <gaottt-ambient-recall> ブロックを得、メッセージ末尾のテキストパートに追記。~/.config/opencode/plugin/ にコピーして使う。fail-safe(chat.message 全体 try/catch、例外でメッセージを一切ブロック/摂動しない)、GAOTTT_AMBIENT_DEBUG で診断ログ。二重注入防止は注入ブロック固有のヘッダ行で判定(タグ文字列の単純 includes はプロンプトが偶然タグに言及すると誤発火する)。 設計判断: opencode プラグインに MCP 呼び出しを再実装しない — relevance gate・スロット組み立て・GAOTTT_AMBIENT_* env・fail-safe を二重管理にすると parity が崩れる。プラグインは「テキスト抽出 → Python フック spawn → ブロック追記」だけの薄い shim にし、ロジックの単一の真実の源を Python フックに置く(ambient_recall プロトコルが変われば両フロントエンドが同時追従)。MCP/REST parity 鉄則の、フロントエンド層への自然な延長。spawn コスト(~0.5s steady)は Claude Code フックと同じで許容。詳細: Guides — Ambient Recall
Multi-Source Query — クエリを質量分布として扱う (2026-05-21) engine._query_internal がプロンプトを節分割(新 pure module core/segmentation.py の segment_query、正規表現・句読点ベース)し、各節を embedder.encode_queries で 1 回 batch embed。propagate_gravity_wave に segment_vectors 引数を追加し、新 _multi_source_pool が per-segment の _union_pool を _rrf_fusion で融合 — seed pool は N 個の点質量の重ね合わせになる。wave は 1 回。pooled query_vec(centroid)は scoring と compute_acceleration の query-attraction anchor に引き続き使用。multi_source_enabled / multi_source_ambient_enabled の 2 フラグ(後者は毎ターンの ambient パス perf 隔離)、両 default ON(2026-05-21 実 RURI perf 検証後 — 複合クエリ recall ~2× / p95 ~40ms、Tier 6 ゲート 120ms に余裕)。TrainingDelta.intent_centers で分割数を観測露出。 pooling は GaOTTT のパイプラインで唯一の非物理ステップだった。 wave も compute_acceleration も力を加算的に重ね合わせるのに、クエリ埋め込みだけがプロンプトを重心の 1 点質量に潰していた — 重力は質量を平均せず場を superpose する。2026-05-21 opencode ambient 本番観察で「複合プロンプトが語彙的に重い側に引っ張られる」症状として顕在化。設計判断: superposition は seed pool 段で行う — 力場段(compute_acceleration)での superposition は query-attraction 項を N 重化し displacement runaway リスク + physics 関数への分岐混入、N 本独立 wave は ambient の ~0.5s 予算超過。seed pool 段なら安価(N×FAISS search のみ)・物理忠実・rollback 単一フラグ。physics rule を一切変えない(centroid が anchor のまま)ので Phase レター非消費 — Phase N 候補繰り下げの P/Q/R 予約とも衝突しない。複数節の場が交差する doc は複数 pool に現れ RRF 和が高くなる = 重力レンズの交点が無償で得られる。分割は Sudachi(optional extra)に hard-depend させず正規表現で。詳細: Plans — Query as Mass Distribution
Ambient Recall Refinement Stage 1-5 + follow-up (b) (2026-05-25) 5 stage の read-side 質向上 (Phase A 由来) を一気通貫実装。Stage 1: services/memory._pick_persona() を query-conditioned 再ランクに改修 (mass × cos(query, persona_vec)、Phase J の persona-anchored geometry を slot 選択に降ろす)、ambient_persona_pool_size=10 / ambient_persona_min_relevance=0.65 (2026-07-02 に 0.5→0.65 へ昇格) の 2 knob 追加。Stage 2: ambient_recall(exclude_tags=...) API + GAOTTT_AMBIENT_EXCLUDE_TAGS=smoke-test,test hook env で test artifact が ambient surface を奪うのを防ぐ (tag_filter の正の filter と substring セマンティクス完全一致)。Stage 3: expose_breakdown=true で direct/lensing/persona 各 slot に ScoreBreakdown を attach (Phase O Stage 1 の流用)、formatter で [raw= virt= wave= mass= bm25] suffix。Stage 4: hook-only で GAOTTT_AMBIENT_HISTORY_TURNS=2 を読んで transcript_path から前 N user prompts を連結し ambient query を強化 (multi-turn context)。Stage 5: tests/perf/test_tier3_ambient_quality.py + golden corpus (12 seed × 6 query × 3 axis) — Tier 6/7 と同じ「仮説→実装→検証 の 検証 step」原則、CI 自動化なし。follow-up (b): production 23k DB の acceptance で発見した Heavy Persona Dominance (harakiriworks intention mass=2.82 が query 横断で persona slot を独占) に対し ambient_persona_mass_weight knob を追加。score = (mass ** w) × cos、w=0.0 で relevance_dominant (mass^0=1)、w≈0.3 で log-scale 近似、w=1.0 で Stage 1 と bit-identical (累乗 skip)。2026-07-02 に w=0.3 + min_relevance=0.65 を同時 default 昇格 (env opt-in の w=0.3 単独では dense JA embeddings の cos≈0.52 が旧 0.5 floor を slip したため、両 knob の同時 default 化が必要だった)。 構造を作った後の質を測ること自体を機構化する。 Enrichment v1 は 6 スロット構造を作って「フラット top-k → 構造化ブロック」の機構的飛躍を完了させたが、「各スロットを何で埋めるか」「その選択を debug できるか」「何が壊れたかを計測できるか」は別問題として残っていた。Refinement の 5 stage は 選択 (Stage 1: query relevance / Stage 2: exclude artifact) + 観察可能性 (Stage 3: breakdown / Stage 4: turn context) + 計測可能性 (Stage 5: golden corpus tier) を 1 plan に束ねた quality push。設計判断: follow-up (b) は 3 案 (mass_weight / relevance_dominant / log-scale) のうち mass_weight 単一 knob を選び、他 2 案を degenerate / 近似ケースとして subsumes — w=0.0 で mass^0=1 となり cos のみ ranking (= relevance_dominant)、w≈0.3 で mass^0.3 ≈ log-scale 近似 (Phase H Stage 1 wave_seed_mass_alpha と同形)。3 つの flag を立てるより 1 つの連続 knob で全 spectrum をカバーし、既定値 1.0 でこれまでの挙動を bit-identical に保存 (累乗 skip 分岐) する方が、後方互換と表現力の両立として clean。critical exponent の予測式 w* = log(cos_ratio) / log(mass_ratio) を docstring 内に literal で書いてあるので、本番 tuning 時に「目当ての cos_ratio に対してどこまで w を下げればいいか」を計算で出せる。Plan open question 2 の対称形 (薄いユーザーで永久空 vs heavy 1 つで永久固定) が production data で literal 化したことが knob の起点 — 設計の予期しなかった失敗形を data が教えてくれた、measurement-first 原則 (Stage 5) の最初の配当。詳細: Plans — Ambient Recall Refinement
Lateral Association Stage 7 — Anti-Hub + Dormant Percentile (2026-05-26) 2 つの follow-up を 1 commit に。Stage 7.1: services/memory._apply_cluster_anti_hub() で ambient_recall.direct と recall の top-K 構成に greedy MMR penalty λ × shared_cluster_count。cluster_key = cohort_id OR original_id (新ヘルパー _cluster_key_for(cache))。両方とも None (pre-Phase-M 旧 memo) は penalty 対象外。direct_hit_anti_hub_lambda: float = 0.0 (default OFF、推奨 0.4)。Stage 7.2: _dormant_surface() で dormant_mass_threshold の絶対値を dormant_mass_percentile: float | None = None で分布相対 P パーセンタイル化。新スクリプト scripts/diag_dormant.py で本番 DB の active mass 分布診断 (read-only)。GLM acceptance: 米国会社四季報 (638-chunk 本) クエリで book chunks in top-5 = 1/5 ✅、dormant = 0→25 件 surface ✅、singleton high-mass dominance は scope 外で残存 (期待通り)。 設計の core: cluster_key の routing は Phase M 単一規則を侵さない。当初 Stage 7.1 を cohort_id 一本で設計したのは Phase M 整合 + tag prefix の脆さ回避だったが、本番 dogfooding で cohort_id 保有率 0% (1件ずつの remember() が batch=1 で supernova 閾値未満) と判明し、original_id (89% 保有、最大 638-chunk 本 cluster) への fallback を追加。設計判断: fallback は branching ではなく routing — anti-hub は ranking layer (physics rule layer ではない)、cohort_id is None ? fallback to original_id : use cohort_id は「どの構造識別子で cluster を見るか」という routing であって physics rule の source-class branching ではない。両方とも Phase M が用意した構造識別子で、source/tag に依存しない。dogfooding-as-design-test の literal な配当: 「理論的に clean な単一 identifier も production の indexing pattern と合わなければ literal に効かない」を internal test が捕まえられなかった盲点として記録 (stage-7-learning memo)。Stage 7.1 limitation: 個別 singleton high-mass node が query 横断で top に張り付く現象 (ffe48a30 mass=1.92 等) は cluster 問題ではなく、Phase N Mass Evaporation 領域。Stage 7.1 が解くのは「同じ cluster が複数席を占める」、Phase N が解くのは「同じ individual が複数 query で 1 席を占める」。詳細: Plans — Lateral Association Stage 7
Save Candidates Hook — Stop → UserPromptSubmit bridge (2026-05-27) Ambient Recall の write-side 対称。新 service services/memory.save_candidates() (auto_remember 既存ヘルパー + persona slot 再利用)、新 MCP tool save_candidates + REST POST /save_candidates (parity)、新 formatter format_save_candidates() (<gaottt-save-candidates> block、format_ambient 同型の fail-silent sentinel (保存候補なし) 規約)。Stop hook script scripts/hooks/save_candidates.py が turn 終了時に MCP を叩いて block を per-session state file (~/.gaottt/save_candidates/<sid>.txt) に書き、companion save_candidates_inject.py (UserPromptSubmit) が次 turn 開始時に読み + 消し + stdout で次 prompt に注入。Claude Code Stop hook stdout は次 prompt に auto-inject されないので state file が durable bridge。env GAOTTT_SAVE_CANDIDATES_* 一式 (enabled / url / timeout / max / turns / state_dir / include_persona)。テスト 11 unit + 4 integration + 2 REST parity + 2 MCP round-trip + Tier 1 contract 26→27 + smoke 両方 green。 観察層 (lens) は自動化、physics 層 (mass の入口) は能動的判断のまま — Articulation as Carrier (memory id 9a954c62) と Phase M 単一規則の前提を崩さない設計。option A 採用 (option B 不採用): 候補を 次 prompt 先頭に注入 することで (1) ambient_recall と shim pattern 統一、(2) 「次の文脈で見たときにまだ価値があるか」の二段 filter になり、(3) Observation vs Physics boundary (memory id 701e7822) と整合。設計判断: 2-script bridge — Claude Code Stop hook stdout が次 prompt に auto-inject されないため、state file 経由の Stop → UserPromptSubmit bridge を選択 (option A の literal な実装)。state file は per-session で、UserPromptSubmit-inject 側は read → delete → emit の順 (partial-write 時の二重 inject 防止)。v1 scope は Claude Code のみ — opencode plugin (TS、opencode-ambient-recall.ts ひな型あり) / codex (hook 仕様待ち) は v2/v3 別 PR。(2026-05-31 追記: opencode v2 + Codex CLI 完了。Codex は Claude Code とほぼ同一の hook イベント体系なので別実装を書かず、同じ Python script を --codex フラグで再利用 — 出力のみ JSON エンベロープ (hookSpecificOutput.additionalContext) 化、transcript reader は Codex rollout JSONL (event_msg) も解釈。Codex は command を shlex 分解のみで $HOME 非展開なので sh -c '$HOME/GaOTTT/…' で起動し machine 非依存に。Plans-Lens-Hygiene が予測した「codex で 3 重実装」を回避。登録は repo 同梱 .codex/hooks.json、strict JSON ゆえ _comment 不可。) 詳細: Plans — Save Candidates Hook
Save Candidates Hook v2 — opencode chat.message plugin (2026-05-27) Claude Code 用 2-script bridge (Stop → state file → UserPromptSubmit-inject) を、opencode で chat.message plugin 1 本に圧縮。scripts/hooks/opencode-save-candidates.ts が SDK の client.session.messages で前ターン (user N-1, assistant N-1) を fetch → [role] text に整形 → Python save_candidates.py を新環境変数 GAOTTT_SAVE_CANDIDATES_EMIT=stdout で Bun.spawn → block を current user message text に append。Python 側は同コミットで _EMIT_MODE を追加し state (default、Claude Code) / stdout (opencode) の 2 モードを runtime 切り替え可能に。テスト 3 unit (default=state / stdout writes to stdout not state / state writes to state not stdout)、計 733 passed。 設計判断: Stop event を待たず chat.message で次ターン開始時に計算 — opencode の plugin hook で turn-end 相当の確定 API が無く、Claude Code の 2-hook bridge を「次ターン chat.message lookback」に再投影することで UX を同一に保ちつつ、opencode 固有 (state file 不要) の simplification を活かす。設計判断: Python script を spawn する設計をキープ (TS で MCP client を再実装しない) — opencode-ambient-recall.ts と同じ single-source-of-truth 原則。MCP call / 認証 / fail-safe は Python 側 1 箇所のみ。設計判断: emit mode は opt-in 環境変数 (state がデフォルト) — Claude Code 側の動作を一切変えない、opencode-save-candidates.ts だけが stdout を要求する rollback ゼロコスト構造。詳細: Plans — Save Candidates Hook §v2 完了報告
Phase P — Pressure Terms (Λ + Langevin) (2026-05-26 起草、Stage 1+2 実装完了 2026-05-26〜27、両 default OFF) 「gravity への対抗 pressure」第 3 法則。P-α (Cosmological Λ): gaottt/core/gravity.py:compute_acceleration に 5 番目の項を追加、wave neighbor scope の全 pair に a_Λ(i) += +H · (pos_i - pos_j) を加算 (Hubble flow 類比、long-range repulsion)。cosmological_lambda_enabled: bool = False / cosmological_lambda_h: float = 0.001 (gravity_G の 1/10)。Λ は gravity と filter 共有 — propagate_gravity_wave が渡す neighbor set を再 filter しない (Plan §3.1)。Self-force filter (Phase M is_self_force) は Λ 非適用 (Λ は mass を生まない、distance の関数のみ)。P-β (Langevin Temperature): position 更新時に √(2·T·dt)·ξ (ξ~N(0,I)) を加算 (SGLD = Welling-Teh 2011 の literal な実装)。langevin_temperature_enabled: bool = False / langevin_temperature_t0: float = 0.001 (σ ≈ 0.045 per step、Hooke 平衡 (G·m/k)^(1/3) ≈ 0.8 の ~5%)。legacy state.temperature (read-time noise / measurement side) と 別経路 — write-time noise (dynamics side)、両者は orthogonal で coexist。新スクリプト scripts/diag_pressure.py で read-only な dry-run projection (Λ / Langevin の mass / displacement 分布予測) を提供、本番 opt-in 前の事前確認に使う。両 default OFF で merge、env で個別に opt-in (GAOTTT_COSMOLOGICAL_LAMBDA_ENABLED=1 / GAOTTT_LANGEVIN_TEMPERATURE_ENABLED=1)。 Phase M (mass 増の単一規則、入力側) + Phase N (mass 減の単一規則、時間側) では届かない領域を、mass 値そのものに頼らず geometry 側で解く。Lateral Association Stage 7.1 anti-hub の scope 外として残った individual-node high-mass dominance (一握りの singleton agent/intention/commitment memo が cluster 関係なく top-K を占有する production 観察、例 ffe48a30 mass=1.92) は、ranking layer の MMR penalty では届かない — cluster ではなく individual が問題なので。compute_acceleration / position update step で 構造的に押し返す ことで、Phase N β (時間で mass を減らす) と相補的に「いまの mass のままでも geometry で散らす」第 3 軸を提供。設計判断: source 分岐ゼロを守る — Λ も Langevin も physics rule に新 source-class 分岐を導入しない (Λ は distance のみ、Langevin は random kick のみ)、Phase M 単一規則と整合。当初検討された対抗案 (Eddington limit = lower LR per mass) はユーザー判断で 不採用 — Eddington は「mass を見て LR を下げる」mass-dependent な分岐に近付くため。P-α と P-β は数学的に直交: Λ は deterministic な長距離斥力 (時間平均的に cluster 構造が緩む)、Langevin は stochastic な per-step ゆらぎ (個別 node が確率的に hop)、両方有効でも干渉せず — Λ は acceleration loop、Langevin は post-acceleration position update に作用。Stage 順は smaller blast radius 優先: Stage 1 = Langevin (随所に position update だけ拡張)、Stage 2 = Λ (compute_acceleration loop 拡張、Λ は filter-shared なので gravity と同じ scope 制約を継承する設計上の発見が Stage 2 で確定)。本番 opt-in 順は Phase N β Stage 1.5 完了 + 1-2 週観測後 (project_phase_p_pressure_terms)。詳細: Plans — Phase P
Phase O Stage 5 — Explore dormant surface (2026-05-14、opt-in default serendipity) ExploreRequest.mode: str = "serendipity" 追加 ("dormant" で wave / FAISS を完全に bypass し、自己発信 source class (agent / value / intention / commitment / note / reference) のうち last_access < now - dormant_age_threshold_seconds AND mass ≤ dormant_mass_threshold AND source ∈ dormant_source_classes を満たす node から random.sample(k=top_k) を返す)。services/memory._dormant_surface() を新設、engine.cache.get_all_nodes() を走査 + engine.store.get_document() で metadata 取得、3 条件 filter + random.sample。dormant 専用 formatter (format_explore(result, mode='dormant')) は wave-free を明示 — final_score を出さず「💭 Dormant memories surfaced (N):」 prefix。query は request shape の互換性維持のため必須 (any non-empty str) だが service は ignore。training_delta / routing_hint は None 返却 (wave 走らず、aspect 意図も検出しない) 「埋もれる自由」の対をなす「思い出される自由」 を機構として追加。Phase I Stage 4 本番 + Sonnet finding で観察された「自己発信 agent memory が dense cluster (tweet / file) に raw cosine で押し負け永久に低 mass のまま埋もれる」を counter-importance sampling で解決 — 「mass が育たないから surface しない / surface しないから mass が育たない」の悪循環を 意図的な外部介入 で切る。設計判断: source 列挙は Phase M 違反ではない — Phase M の「source 分岐ゼロの単一規則」は physics rule (mass update / Hooke / kick) に対する制約、Stage 5 の dormant_source_classes は physics 一切触らず、「自己が能動的に書いた memo」という structural class identifier に対する filter (query intent) として使うだけ。Phase D の inherit_persona が value/intention/commitment を構造的に列挙するのと同じ思想 — branching ではなく structural definition。dormant_mass_threshold=2.0 は Phase I Stage 3 mass-gated kick の θ=3.0 と整合 (tanh(2/3) ≈ 0.58、mature gate の 58% で「raw cosine 弱者として未到達」)。dormant explore は metric 違反ではなく、別 operation — wave 走らせるのと dormant surface するのは異なる operation なので、同じ explore tool に mode で同居していても rule の混入ではない。詳細: Plans — Phase O §Stage 5
Multiverse supervisor は MCP/REST parity 対象外(管理面)(MV3, 2026-07-02) universe supervisor / embedding service / control plane の API は engine の能力ではなく 管理面。MCP/REST parity 鉄則(engine 能力の MCP/REST 同時提供)はこれらに適用されず、/reset(破壊的操作を LLM に露出しない)と同じ例外クラス。supervisor は admin API(宇宙作成 / 一覧 / 削除 / /route)+ backend token middleware を単独で持ち、services/*.py の service 層を経由しない。MV5 の backup hook(create/delete 成功経路での litestream 設定再生成)も新 API ではなく既存 admin API の side-effect なので、この parity 例外をそのまま継承する managed 宇宙は owner lease (MV2) で二重 engine を構造的に拒否するため、REST app (build_engine() で独自 engine を立てる) が 開けない。よって v1 で宇宙に露出する経路は MCP のみ。テナント管理者の操作が必要な場合は supervisor admin API を拡張する(MV4 で reverse-proxy + key 検証込みで検討)。本計画は implementation-plan 横断ルール 4「engine の能力を追加しない(MCP 新ツール 0、REST 新エンドポイント 0 が原則)」と整合。詳細: Plans — Multiverse Scale-Out §Stage 2、Operations — Multiverse Setup、MV5 hook は Operations — Backup & DR
Control plane API も MCP/REST parity 対象外(管理面の延長)(MV4, 2026-07-03) MV3 の parity 例外を control plane に延長。control plane API(/admin/tenants / /admin/hosts / /hosts/{hid}/usage 等)は engine の能力ではなく管理面なので、/reset・supervisor admin API と同じ例外クラス。supervisor の GET /admin/status(permanent auth failure state を expose)も管理面。engine 側に新 API を足す場合は従来どおり MCP + REST 同時追加 control plane は gaottt/ パッケージの外(独立パッケージ control/)で、asyncpg は gaottt/ に import されない(J9)。supervisor 側の gaottt/multiverse/control_client.py は httpx のみ使い、asyncpg を import しない。詳細: Operations — Control Plane
Control plane は aggregator / audit / billing 収集点(運用 deprovisioning 権威なし、local 一次・J5)(MV4, 2026-07-03) control plane はテナント・宇宙の台帳・監査・usage telemetry 収集点だが、運用 deprovisioning の権威ではない。宇宙状態の一次は local manifest / registry。control 側 DELETE /admin/universes/{uid} は台帳 row の論理削除 (status='deleted') のみで、supervisor は次回 sync で矛盾を検知して WARNING(即時削除しない)。この 2 つの権限モデルを混ぜない 誤削除防止 + degraded mode 整合性維持。control が落ちてもホストは local で自走し、復旧後に残留 spool を再送する。operator が conflict を手動解決する。詳細: Operations — Control Plane「権限モデル」節、multiverse-mv4-execution-plan.md 設計判断 J5
MV4 J1=A — usage は route_resolution telemetry(operation count ではない)、billing-grade は MV4.1 (2026-07-03) implementation-plan §MV4 の SoT は usage counter を「recall / remember / ingest 回数」と指定していたが、engine.py への usage counter 機構追加は high-risk(engine 横断ルール 1「physics 層に触らない」と衝突)なので、PM 承認済み (A) の SoT deviation として v1 は supervisor の /route 解決回数を activity telemetry に置き換えた。usage_events.event_type='route_resolution'(operation count ではないことを命名で明示、proxy reconnect で過小カウントしうる)。billing-grade の正確な operation count は MV4.1(backend MCP notification 機構、別 PR)で導入 横断ルール 1(physics 層に触らない)+ engine.py の usage counter 追加は high-risk。J1 deviation を docs + implementation-plan 完了マークに明記し、将来の混乱を防ぐ。audit_log.action は過去形 (universe_createdd 等)、usage_events.event_type は原形 (universe_create 等) と意図的に使い分け。詳細: Operations — Control Plane「usage telemetry の意味」節
Multiverse importer は MCP/REST parity 対象外(管理面の延長)(2026-07-04) scripts/import_universe.py + gaottt/multiverse/importer.py は standalone data_dir を multiverse layout の 1 宇宙へ変換する管理 CLI ツール。supervisor / control plane と同じく engine の能力ではなく管理面なので、/reset・supervisor admin API と同じ例外クラス(MCP 新ツール 0、REST 新エンドポイント 0)。importer は services/*.py の service 層を経由せず、MultiverseRegistry を直接触る。MV3 の「supervisor 起動中・停止中 両方で動く」要件を満たすため、admin API 拡張(POST /admin/universes/import)ではなく CLI script 単体で完結する設計 managed 宇宙は owner lease (MV2) で二重 engine を構造的に拒否するため、REST app が宇宙を開けない点は MV3 と同じ。importer は破壊的管理操作(copy/move + registry INSERT)を伴うため、LLM に露出しない(/reset と同じ判断)。CLI 経由のみで、MCP/REST には非露出。詳細: Operations — Multiverse Import Universe
Supervisor による embedder lazy spawn(MV3 follow-on、2026-07-06) multiverse supervisor が embedding service (port 7879) を初回 embed 需要(create_universe / /route)で lazy spawn し、全 backend idle で SIGTERM で落とす。supervisor_spawn_embedder=True(default 有効)。3 状態の state machine (unowned / owned_idle / owned_terminating) + asyncio.Lock (in-process) + <multiverse_root>/.embedder.spawn.lock の fcntl.flock (cross-process) で race safety を担保。新 knob 4 つ(supervisor_spawn_embedder / embedder_spawn_idle_timeout_seconds=300 / embedder_spawn_readiness_timeout_seconds=90 / embedder_idle_watchdog_poll_seconds=30)。正常な本番 systemd embedder が先に立ってる場合の挙動は完全不変(/healthz で検知して unowned に倒れ、所有権を主張しない)。PermissionError / SIGKILL 後 alive は state を unowned に消さず owned_terminating を保持、ERROR log で手動 recovery を促す(安全側、auto-respawn しない)。/route handler は EmbedderValidationError を 503 に mapping(create_universe は従来通り 400) 開発機(systemd 無し)で multiverse を 1 プロセスで運用できるようにする UX 改善。SPOF 設計判断「in-process fallback は model load が重いので自動では行わない」(旧)を「supervisor による lazy spawn は開発機向け、正常な本番 systemd embedder が先に立ってる場合は完全不変」に精密化(Plans — Multiverse Scale-Out §SPOF)。systemd 1 本運用か supervisor lazy spawn かの分岐ゼロ — /healthz で誰が先に立ったかを判定する構造で、supervisor_spawn_embedder=False で従来挙動(即 EmbedderValidationError)に 1 行 rollback。PermissionError / SIGKILL 後 alive を owned_terminating に留める設計は既存 _stop_backend の _BackendAliveConflict パターンの踏襲(安全側)。詳細: Operations — Tuning §MV3 follow-on、Operations — Server Setup「supervisor 経由の lazy spawn」節
Phase U WP-6b — staged readiness protocol + GET /admin/readiness は FastMCP HTTP app 専用 (2026-08-26) HTTP backend transport (Starlette app lifespan) の起動と同時に 単一の engine startup task を eager 開始(lazy 初回 tool-call 生成を廃止)、全 MCP handler は共有 task を readiness_wait_timeout_seconds=30s で bounded wait し timeout 時は retryable な構造化 error(task は継続)。engine の生存期間は transport に紐づき、per-session lifespan では tear down しない(warm reconnect で再構築が走らない)。状態は GET /admin/readiness(Bearer GAOTTT_BACKEND_TOKEN)が単調遷移 (STARTING → SEMANTIC_READY → HYBRID_READY / FAILED) で露出。supervisor /route は endpoint を poll(route_readiness_timeout_seconds=35s、STARTING 超過は readiness:"starting" 付き応答、FAILED は 503、404 は legacy fallback)。rollback readiness_protocol_enabled=False で legacy lazy path /admin/readiness は REST app (server/app.py) には置かない — /reset と同じ文書化された parity 例外。backend lifecycle の確認は engine 能力(LLM が MCP/REST 経由で使う操作)ではなく 運用面(supervisor / monitor が消費する)で、MCP/REST parity 鉄則の適用対象外(MV3 の supervisor admin API と同じ例外クラス)。route は FastMCP の custom_route seam (mcp.custom_route) で streamable-http / SSE 両 app に注入 — supervisor が spawn する backend は常に MCP HTTP backend なので経路として十分。lazy 初回生成の廃止: R5 (cold start 129s) の観測で「最初の tool call が engine 構築を引き受ける」構造が client timeout の原因だった — 起動と初期化を分離し、client は STARTING を観測して待つ。diagnostics Tier B の bm25 size check は background build 中 tier_b_bm25_size_pending (INFO) に格下げ
Phase U WP-6c/6d — BM25 background build + snapshot 永続化 (2026-08-26) WP-6c: startup の BM25 build (hybrid + ambient gate の 2 index) を 新規 index object への snapshot build + build 窓内 mutation の journal replay + engine-level lock 下での atomic swap で background task 化(in-place 変更なし、CPU-bound な tokenization は thread 実行)。build 窓内は hybrid seed pool が raw/virtual 縮退運転し ambient gate は semantic fallback。engine.bm25_build_state ∈ idle/building/ready/failed(failed でも検索は raw/virtual で稼働継続 — readiness は HYBRID_READY + bm25:"failed" 印)。WP-6d: build 済み index を data_dir/bm25.snapshot に永続化(checksum-verified pickle、tmp write → fsync → atomic rename、保存は build 完了時と graceful shutdown 時のみ)。fingerprint = content digest (sorted (id, sha256(content)) の sha256) + tokenizer identity + k1/b + format version + universe id。一致なら build skip、不一致は rebuild。rollback は各 flag で同期 build / 非永続化に復帰 WP-6a 計装 (decision gate) の実測: production-scale copy (42,060 nodes) で bm25_build 147.3s = startup_total 152.9s の 96% — cache load 4.4s は軽微(startup-timings)。cold start の主要因が BM25 build 単独と確定したので、(1) startup 同期処理から外す(SEMANTIC_READY ≈6s、production rollout 実測 7.3s — R5 acceptance「30 秒以内」を大幅達成)、(2) 次回起動の build 自体を snapshot で消す、の 2 段構え。fingerprint に content digest を使う判断: ID / timestamp 系 (count + max(updated_at) 等) は content 変更 (同一 ID の本文更新) を取りこぼすため使わない —「常に正しい index を保証する」方向の fallback (fail-open ではない)。journal replay + atomic swap は FAISS 逆方向上書きと同種の race を構造的に排除する同じ作法
Phase U WP-4b — raw-top rescue (direct_rescue_raw_rank=3) (2026-08-26) Phase T Stage 3 の qualified-first 並べ替えの拡張 — qualified な natural item のうち scored pool 内 raw cosine rank ≤ 3 の item を qualified group の先頭 tier に lift(tier 内は raw cosine 降順、sort key = (rescued, qualified, final_score desc))。presentation 専用(score / physics / breakdown には書き込まない)、forced/injected 経路 (Phase J) には不適用、MMR は rescue 適用後の pool から選択。0 で無効、direct_qualification_enabled=False なら rescue も無効 WP-4 の target trace (scripts/diag_target_trace.py) で既知障害 node de1b528f が raw rank 1 (cos 0.8967) / hybrid BM25 rank 1 / ambient word-BM25 rank 1 / qualification True でありながら virtual cosine 低迷 (genesis/priming kick で displacement が raw content 方向から逸脱) により final_score 最下位帯 → top-K 外、と完全に特定できたことへの scoped fix。観測 (raw cosine・lexical) が場の drift (displacement 発散) に veto されるのは「場が順位を育てる」契約の範囲外 — rescue は観測の土台を field の drift から守る最小の防腐剤 (post-recovery handoff の北極星「semantic relevance を観測の土台に置く」と整合)。breakdown/reason 行は書き換えないので observability は不変
Phase U WP-2 — supervisor exact-name tuning env allowlist (2026-08-26) multiverse supervisor の _build_spawn_env は gaottt/multiverse/tuning_env.py の閉じた完全名 allowlist RUNTIME_TUNING_ENV_ALLOWLIST (28 変数、Phase T/U tuning knob のみ) に一致する GAOTTT_* のみ backend に伝播。exact-name 一致のみ(prefix / wildcard 無し、未来の knob は自動 deny、allowlist ↔ 実 config field 対応の staleness guard 付き、GAOTTT_AMBIENT_GATE_MODE は enum 閉集合 ("or","composite") 検査付き)。GAOTTT_CONFIG は伝播しない(任意 field を含め得るため tuning-only allowlist と矛盾)。値検証は fail-fast — bool 認識 token 外 / unparseable 数値 / NaN・Inf は backend spawn を拒否し /route が 500 で観測可能。identity 系 4 key (GAOTTT_DATA_DIR / GAOTTT_EMBEDDER_ENDPOINT / GAOTTT_OWNER_LEASE_ENABLED / GAOTTT_BACKEND_TOKEN) は allowlist 外・明示上書きが常に勝つ。恒久 rollback は supervisor を起動する service 定義 (systemd 等) に env を置く運用 R1 の根因は「supervisor が env-inheritance trap 対策で GAOTTT_* を全 strip する」設計と「Phase T knob の rollback 経路が env」の衝突。全 GAOTTT_* の無条件伝播ではなく閉じた allowlist を選んだ理由: spawn env 経路で任意の config field (data_dir 等) が差し替わると identity 系の明示上書きと競合し silent な構成破壊になり得る — tuning knob (rollback が目的の集合) だけを正確名で開く。値検証を spawn 時点で行う理由: backend 側 _resolve_overrides は unparseable 値を log-and-drop、bool は黙って False coerce するため、spawn を通すと「設定したのに効かない」静かな事故になる。MV5 review #7 の精神(LITESTREAM/BACKUP 系が spawn env に存在しないことの assertion)は維持。allowlist 拡張は review 経由のみ

エントリポイントの読み方

主要な処理フローの入り口を順に追えば、コード全体が読み解ける:

  1. サーバー起動: server/app.py の lifespan() → 全コンポーネント初期化
  2. MCP 起動: server/mcp_server.py の get_engine() → 遅延初期化
  3. クエリ処理: engine.query() → gravity.propagate_gravity_wave() → 仮想座標スコアリング
  4. 軌道力学: engine._update_simulation() → gravity.update_orbital_state()(3 段階物理)
  5. MCP remember: mcp_server.remember() → services.memory.remember() → services.memory.save_memory() → engine.index_documents() → formatters.format_remember()
  6. MCP commit (Phase D): mcp_server.commit() → services.phase_d.commit() → save_memory(source="task") → engine.relate(fulfills, parent) → formatters.format_commit()

詳細セクション

コード参照

GaOTTT Wiki

(formerly GER-RAG)

🏠 Home

🚀 Getting Started

🌱 Tutorial / はじめての方へ

📚 Guides

🛠 MCP Reference

🌐 REST

🏗 Architecture

⚙ Operations

🗺 Plans

🔬 Research

💭 Reflections

Clone this wiki locally