Skip to content

AI 編集 API・CRDT op・参照整合性

Strand のコードは物理ファイルではなく content-addressable CRDT graph に格納される。AI エージェントはテキストファイルを編集するのではなく、構造化された編集オペレーション(op) を発行する。

これにより:

  • ファイル単位のマージ衝突が原理的に発生しない
  • 編集の影響範囲が静的に計算できる
  • リネームで参照が壊れない(hash 不変)
  • 編集失敗時に自動修復ループが回せる

9.1 全体像

┌──────────────────────────────────────────────┐
│            CRDT graph store                  │
│  (definition の集合、各々が content-addressable) │
└──────────────────────────────────────────────┘
        ↑                          ↓
        │                          │ strand view
        │ strand op apply          │
        │                          ↓
┌──────────────┐          ┌──────────────────────┐
│ AI エージェント │ ←───────│ projection (text)    │
└──────────────┘  edit op └──────────────────────┘

AI が見るのは graph からの projection(テキスト断面)。AI が出力するのは op(テキスト diff ではない)。


9.2 strand CLI

9.2.1 読み取り系

bash
strand view <selector>              # 定義をテキスト化して出力
strand view slot.todos              # 単一定義
strand view 'slot.*'                # ワイルドカード
strand view --with-deps reducer.add # 関連定義もまとめて出力
strand view --hash slot.todos       # content-hash を表示
strand view --history slot.todos    # この定義の編集履歴
strand view --refs slot.todos       # この定義への参照元一覧
strand list <layer>                 # レイヤ内の全定義名
strand list                         # 全定義名(layer prefix 付き)

9.2.2 書き込み系

bash
strand add <layer> <name> <body>            # 新規定義追加
strand replace <layer>.<name> <body>        # 定義差し替え
strand edit <layer>.<name> <patch>          # 部分編集(reducer の do= 内など)
strand rename <layer>.<old> <new>           # リネーム(hash 不変)
strand remove <layer>.<name>                # 削除(参照があれば失敗)
strand patch apply <file>                   # CRDT op バンドルを適用
strand patch revert <op-id>                 # 特定 op を取り消し

9.2.3 検証系

bash
strand check                       # 型・参照・effect 全部
strand check --types               # 型のみ
strand check --refs                # 参照整合性のみ
strand check --effects             # capability・policy 整合性のみ
strand check --a11y                # アクセシビリティ規約

9.2.4 修正補助

bash
strand fix --auto-patch <error-id>          # エラーを自動修正する CRDT op を提案
strand fix --apply                          # 提案をそのまま適用
strand fix --interactive                    # 提案を 1 つずつ確認しながら適用

9.3 CRDT op の形式

9.3.1 op の種類

op意味
add新規定義追加
replace定義本体を差し替え
edit定義の一部編集(field 更新、reducer の do= 内文の追加削除など)
rename名前変更(hash 不変、参照は別 op で更新)
remove定義削除(dependent ops 自動生成)
link参照追加(明示)
unlink参照削除(明示)

9.3.2 wire format

json
{
  "op": "add",
  "layer": "slot",
  "name": "todos",
  "body": "Map(TodoId, Todo) = {}",
  "author": "agent:claude-1",
  "ts": 1779884546123,
  "op-id": "op_01JC...",
  "parent-ops": ["op_01JB..."],
  "depends-on": ["type:TodoId@h:9ab3...", "type:Todo@h:7cde..."]
}
フィールド意味
opop 種別
layer対象レイヤ
name対象名
body新本体(add/replace で必須)
author発行エージェント
ts発行時刻(UNIX ms)
op-idop の ULID
parent-opsこの op が依拠する直前 op の id(CRDT 順序保証)
depends-on本体が参照する他定義の hash(参照整合性検証用)

9.3.3 op の収束保証

Strand graph は Add-Wins LWW-Map(最終書き込み勝ち + 削除より追加優先)。

  • 同名 add が複数エージェントから来た場合: op-id の辞書順で勝者決定
  • add と remove が交差: add 勝ち(dangling reference になるくらいなら残す)
  • replace 同士: ts 新しい方が勝つ
  • rename と remove: rename 勝ち

これらは数学的に収束保証される。が、意味的整合性は別途検査が必要(次節)。


9.4 参照整合性の強制

CRDT が構文収束を保証しても、意味的衝突は別問題:

  • A: strand remove slot.draft
  • B: strand add tile.NewForm input(bind=draft)

両方が CRDT として収束したあと、tile.NewForm から slot.draft への参照が dangling になる。

Strand はこれを 2 段階で防ぐ

9.4.1 op 発行時の事前検査

bash
strand remove slot.draft
# Error: cannot remove slot.draft (referenced by 3 tiles, 2 reducers)
#   tile.NewForm:1
#   tile.Compose:4
#   tile.SearchBox:1
#   reducer.submitNew:2
#   reducer.clearDraft:1
# Use --cascade to remove all dependents, or --force to leave dangling

--cascade で依存先も同一 op バンドルに含めて remove する。--force は dangling 許容(warning 出力)。

9.4.2 op 適用時の事後検査

複数エージェントの op が同時に着信した場合、graph store はトランザクション境界で参照検査を実行:

transaction begin
  apply op_A (remove slot.draft)
  apply op_B (add tile.NewForm with ref to draft)
check refs
  -> dangling: tile.NewForm -> slot.draft
resolve:
  policy=strict: rollback both ops, mark as conflict
  policy=heal:   add slot.draft back with default value, log conflict
  policy=warn:   apply both, mark warning, emit notification
transaction commit

resolve policy は strand config conflict-policy <strict|heal|warn> で設定。デフォルト strict


9.5 hash 計算と参照解決

9.5.1 hash 計算

canonical(body) = AST正規化 (識別子は型hash+位置に置換、フィールド名アルファベット順、空白除去)
hash(def) = blake3(canonical(def.body) ⊕ hash(dep1) ⊕ hash(dep2) ⊕ ...)

9.5.2 参照解決

ソーステキストの users のような名前参照は graph store 内では slot:hash:9ab3c1... として記録される。

  • 名前 → hash 解決はコンパイル時 / op 適用時に行う
  • 同名でも依存先が変われば別 hash
  • リネームは (rename, name-old, name-new) op のみ。hash は不変

9.5.3 表示時の名前

strand view で取り出すと hash は人間可読名に戻される(ラベル)。


9.6 エラーコードと自動修復

すべてのエラーは構造化されている:

json
{
  "code": "E0103",
  "kind": "undef-ref",
  "location": "tile.TodoRow.body:2",
  "message": "Reference to undefined slot 'usres'",
  "suggestion": {
    "kind": "did-you-mean",
    "name": "users",
    "similarity": 0.92
  },
  "auto-patch": {
    "op": "edit",
    "layer": "tile",
    "name": "TodoRow",
    "patch": {"body:2": "replace 'usres' -> 'users'"}
  }
}

9.6.1 主なエラーコード

code種類
E0101未定義型
E0102未定義 reducer
E0103未定義 slot
E0104未定義 effect
E0105未定義 tile
E0106未定義 fn
E0201型不一致
E0202refinement 違反
E0203union 網羅性不足
E0204nominal 型混同
E0301capability 不足
E0302effect 直接呼び出し
E0303reducer 外での slot 書き込み
E0304tile 内 effect emit
E0305fn 内 slot 読み書き / effect emit
E0306event selector が tile 名ではない
E0401直接再帰
E0402ラムダ使用
E0403null 使用
E0404任意述語
E0501参照整合性違反 (dangling)
E0502循環依存
E0601同 slot への複数書き込み
E0701a11y 警告(label/alt 等)

9.6.2 自動修復ループ

bash
# AI agent script
while true; do
    errors=$(strand check --json)
    if [ -z "$errors" ]; then break; fi
    for err in $errors; do
        if has_auto_patch "$err"; then
            strand patch apply <(echo "$err" | jq .auto-patch)
        else
            # AI に修正を委ねる
            echo "$err" | ai-fix
        fi
    done
done

strand fix --auto-patch <code> で auto-patch があるエラーは構造的に解決される。auto-patch がないエラーだけ AI のコンテキストに乗せて修正させる。


9.7 MCP サーバ

Strand は Model Context Protocol サーバとして起動でき、AI エージェントから直接 tool 呼び出しできる:

bash
strand mcp serve --store ./project.strand-store

提供される tools:

tool name引数戻り値
strand_viewselector: string, with_deps?: bool定義テキスト
strand_listlayer?: string定義名リスト
strand_addlayer, name, bodyop-id
strand_replaceqname, bodyop-id
strand_editqname, patchop-id
strand_renameqname, new_nameop-id
strand_removeqname, cascade?: boolop-id
strand_checkscope?: stringerror list (JSON)
strand_fixerror_code, apply?: boolpatch (JSON)
strand_refsqname参照元リスト
strand_historyqnameop 履歴
strand_episodeepisode_idepisode log

AI からはファイル操作の代わりにこれらを呼ぶ。


9.8 エージェント並列開発プロトコル

複数エージェントが同時に編集する際の協調:

9.8.1 同時性

  • 各エージェントは ローカル graph store のスナップショットを持って作業
  • 出力は op バンドル
  • マスター graph store に op を push → CRDT で収束

9.8.2 ロックなし

graph store はロックを取らない。op はいつでも push 可能。ただし:

  • 参照整合性で reject される可能性あり
  • reject されたエージェントはマスターの最新を pull して再試行

9.8.3 タスク境界

複数エージェントが同じ定義を編集することは避けたい。タスク分割の単位を「定義名のドメイン」で行う:

agent-1: slot.todos*, reducer.todo-*, tile.Todo*
agent-2: slot.user*,  reducer.user-*, tile.User*
agent-3: slot.route,  reducer.route-*

これは規約だが、Strand コンパイラに ownership lock(オプション)を追加できる:

bash
strand lock agent-1 'slot.todos*,reducer.todo-*'

同名空間に他エージェントが op を出すと reject される。


9.9 episode と op の関係

実行時の episode log はビルド成果物に対して記録される。op は ソース graph の編集履歴。両者は分離されている:

op logepisode log
対象ソース定義の変更実行時の状態変化
永続化先graph storeepisode store
用途並列開発・回帰検査デバッグ・replay test
単位CRDT opreducer 実行 + effect 結果

→ episode log は ./runtime.md


9.10 ファイルシステムとの互換層

実装初期は、graph store を ディレクトリ内のファイル群として projection することもできる:

project.strand/
├── types/
│   ├── User.strand
│   └── TodoId.strand
├── slots/
│   └── todos.strand
├── effects/
│   └── loadTodo.strand
├── reducers/
│   └── add.strand
├── tiles/
│   ├── TodoRow.strand
│   └── App.strand
├── fns/
│   └── matchFilter.strand
└── .strand/
    ├── store.crdt        ← CRDT graph 本体(バイナリ)
    ├── op-log.jsonl
    └── episode-log.jsonl

strand sync で双方向同期:ファイル編集 → op に変換 → store に適用、または store の変更 → ファイルに反映。

これにより既存の Git ベースの workflow とも共存可能。ただし真の互換性は graph store 側にある。


9.11 設計上の判断記録

判断理由
編集はファイル diff ではなく構造化 op並列マージで意味的に安全
参照整合性は op 発行時と適用時の 2 段階CRDT の意味的衝突を構造で防ぐ
自動修復ループAI のデバッグサイクルを構造で短縮
MCP サーバ提供AI エージェントから直接使える
ownership lock オプション並列開発の規約を機械化
ファイル投影との互換既存ツール (Git/エディタ) と共存

9.12 次