markharness

0027: 宣言的Knowledge ReconciliationによるAI authoring

ステータス

Accepted(2026-09-13決定、2026-09-13実装完了)。knowledge reconcile本体の機能、crash-recoverableな原子的保存、0028の実行、および関連文書の更新をすべて満たした。

背景

AIやスクリプトからKnowledgeを作成する現在の経路は、KnowledgeDraftknowledge validateknowledge applyへ渡した後、identity migrateを実行するという手続き的な操作を呼び出し側へ要求する。新規Requirementとそれを参照するFeatureを同時に作る場合は、Requirement UIDがまだ存在しないため、さらに事前の対話操作または一時的なdisplay ID参照が必要になる。

この経路では、呼び出し側がKnowledgeの保存順序、UID発行時期、display IDとUIDの使い分け、およびapplymigrateの順序を理解しなければならない。apply成功後にmigrateを忘れたり、その間に処理が中断したりすると、UIDなしのKnowledgeやdisplay IDを含むrequirement_uidsが正規保存領域へ露出し得る。これは0013の「UIDモードでは新規UIDなしKnowledgeを通常コマンドで導入しない」という不変条件と整合しない。

AI authoringで必要なのは保存手順の指定ではなく、「どのKnowledgeが存在してほしいか」という意図の宣言である。markharnessが、その宣言を現在のリポジトリ状態と照合し、UID発行・参照解決・検証・原子的保存を完結させる必要がある。

決定

1. knowledge reconcileをAI authoringの標準経路とする

次の非対話コマンドを導入する。

markharness knowledge reconcile <intent-file> [--check] [--json] [--dir <path>]

入力をKnowledge Intentと呼ぶ。Knowledge Intentはauthoring専用の宣言であり、.markharness/knowledge/の保存形式ではない。新規要素のUID、保存パス、identity event、および移行手順を呼び出し側へ要求しない。

knowledge reconcileは、一つのKnowledge Intentについて次を一操作として行う。

  1. 入力を解析し、対象リポジトリの現在のKnowledgeとAxisを読み取る。
  2. 文書内参照、既存UID、およびdisplay IDを解決する。
  3. ドメイン規則と現在状態に依存する規則を含め、変更全体を検証する。
  4. 新規Requirement・Feature・Behavior・ScenarioのUIDを一括で予約する。
  5. Featureのcontributes-to関連をRequirement UIDへ変換する。
  6. 作成・更新・変更なしを含むmutation planを生成する。
  7. 正規Knowledge、identity event、および必要な派生状態を単一のcrash-recoverableなトランザクションで保存する。
  8. コミット済み状態がすべての不変条件を満たすことを確認し、構造化結果を返す。

検証完了前には正規保存領域を変更しない。論理commit前の失敗は旧状態へ収束し、論理commit後の失敗はcommit済みの新状態へ冪等にroll-forwardする。UIDなし、未解決参照、Knowledgeとidentity eventの片側だけが更新された状態を通常コマンドへ公開しない。

2. Knowledge Intentでは文書内keyにより新規要素を参照する

同一Intent内の新規要素は、保存されない文書ローカルなkeyで参照する。例えば、FeatureからRequirementへの関係はcontributes_toへRequirementのkeyを記述する。既存RequirementはUIDで参照する。display IDだけによる既存Requirementの選択は行わない。

format: markharness/knowledge-intent/v1
mode: merge

requirements:
  - key: req_todo
    id: todo
    source: native
    label: TODO管理
    axis: [functional]

features:
  - key: feature_todo
    id: todo-management
    contributes_to: [req_todo]
    label: TODO管理
    axis: [functional]
    behaviors:
      - key: behavior_add
        id: add-todo
        description: TODOを追加する
        scenarios:
          - id: empty-title
            description: 空のタイトルは追加できない
            phases:
              - steps:
                  - action: タイトルを空にして追加する
                results:
                  - TODOは追加されない

keycontributes_toはauthoring上の表現であり、正規Knowledgeへ保存しない。正規Featureでは、従来どおりrequirement_uidsだけをcontributes-to関連の正本とする。新しい永続的な関連型や汎用グラフは導入しない。

3. 新規作成と既存要素の変更をUIDで明確に区別する

新規要素にはUIDを書かない。markharnessだけがUIDを発行する。

既存要素の内容変更またはdisplay ID変更には、その要素のUIDをKnowledge Intentへ明示する。内容の類似性や同じdisplay IDだけを根拠として既存UIDを推測・継承しない。この規則は0017の改訂と新規作成の区別、および0021の再登場時のidentity規則を維持する。

照合規則は次のとおりとする。

Intentの指定 現在状態 結果
UIDなし、同じkind・scope・IDが存在しない 新規 UIDを発行して作成
UIDなし、同じkind・scope・IDが存在し、正規化後の内容が完全一致する 既存 変更なし
UIDなし、同じkind・scope・IDが存在するが内容が異なる 既存 ambiguous_identityで停止し、UIDの明示を要求
UIDあり、UIDとkindが一致し、現在のscopeとIntent上のscopeも一致する 既存 内容を比較し、更新または変更なし
UIDあり、display IDだけが異なる 既存 明示的なrenameとして処理
Scenario UIDあり、Intent上で別のFeatureまたはBehaviorへ配置される 既存 明示的なreparentとしてUIDを維持して移動
UIDが存在しない、kindが異なる、またはScenario以外でscopeが矛盾する 不整合 fail-closedで停止

Scenarioの内容変更およびUID付きの明示的なreparentではScenario UIDを維持し、effective contentに応じてCase revisionを再計算する。reparentは、既存Scenario UIDを現在とは異なるFeatureまたはBehavior配下へIntent上で配置した場合に限り成立する。UIDを省略した同名Scenarioから移動を推測しない。Scenarioの分割・統合・コピーで新しく生じるScenarioには新しいUIDを発行し、内容一致から分割・統合を推測しない。

4. 初期版は削除を行わないmergeだけを提供する

初期版のmodemergeのみとする。Intentに記載した要素だけを作成または更新し、記載されていない既存要素は変更しない。省略を削除またはRetireの意思と解釈しない。

望ましい状態との差分から未記載要素をRetireするexactモードは、対象scope、確認・権限、派生するScenario/TestCaseへの影響を別途決定するまで実装しない。--delete--allow-retireも本ADRの初期実装範囲外とする。

Axisは登録済みのものだけを参照できる。Knowledge IntentからのAxis作成や削除は、KnowledgeとAxisの所有範囲を混在させるため初期版では行わない。未知のAxisはunknown_axisで停止する。

5. UID付き既存要素にはpatch semanticsを適用する

UIDを指定した既存要素は、Intentに記載したフィールドだけを変更する。省略したscalar、value collectionおよび子Knowledge要素は現在値を維持する。明示したscalarは置換する。必須値をnullまたは空値で消去する入力は、そのフィールドのドメイン規則に従って拒否する。

Collectionは次の二種類に分ける。

Featureのcontributes_toを省略した場合は現在のrequirement_uidsを維持し、指定した場合は文書ローカルkeyまたはRequirement UIDを解決した集合で全置換する。これにより、関連の追加と削除を別コマンドなしで表現する。

既存要素の子Knowledge要素をIntentへ記載したことは、その子だけを作成またはpatchする意味であり、同じ親の未記載の子を削除しない。proceduresphasesは独立したUIDを持つKnowledge要素ではなく、所有元のBehaviorまたはScenarioの値であるため、この規則ではなくValue collectionの全置換規則に従う。

External Requirementの固定参照を現在の.sdoc blobへ進める場合は、UID付きRequirementへsource_revision: currentを明示する。Reconciliation Moduleはsource_locatorが指す現在のblob OIDを解決して正規Knowledgeへ保存し、native Requirement、存在しないlocator、または解決不能なGit状態ではfail-closedで停止する。currentはIntentだけの命令値であり、正規Knowledgeへ保存しない。

6. --checkと通常実行は同じmutation planを使う

--checkは解析・照合・検証・mutation plan生成までを通常実行と同じ実装で行い、書込みだけを行わない。差分がなければ成功し、変更が必要なら機械判定可能な専用終了コードと計画を返す。

通常実行はコミット直前に現在状態を再確認し、--check後または計画中に入力状態が変化していればstale planとして停止する。--checkの結果を、そのまま後続書込みの許可証として扱わない。

同じリポジトリ状態と同じIntentからは、UIDの具体値を除いて同じmutation planを生成する。新規UIDは計画上の一時トークンで表し、コミットする実行内で割り当てる。同じUIDなしIntentを成功後に再実行した場合、kind・scope・display IDおよび正規化後の内容が完全一致する要素はunchangedとなる。内容差分を伴う更新やrenameには、最初の成功結果または最新の機械可読snapshotから得たUIDをIntentへ明示する。

7. 結果と診断を安定した機械可読形式で返す

--jsonの成功結果は、少なくともcreatedupdatedunchanged、各要素のkind・UID・display ID、および変更パスを返す。失敗結果は安定したcode、Intent内の位置、message、可能ならremediationを返す。Rustの型名や内部エラー文字列を外部契約にしない。

初期版で必要な診断には、少なくとも次を含める。

8. identity migrateをauthoring手順にしない

knowledge reconcile成功時点で、作成したすべての正規KnowledgeはUIDを持ち、すべてのUID参照が解決済みでなければならない。後続のidentity migrateを成功条件に含めない。

identity migrateは既存・手動導入データの移行または明示的な修復操作として残す。AI向け文書ではknowledge reconcileを標準経路とし、複数コマンドの手続き的フローを案内しない。

knowledge reconcileと責務が重複する既存のauthoringコマンドは、0028に従い、本ADRのReconciliation Moduleと移行先Interfaceが完成した後に廃止する。旧経路との後方互換は提供しない。

不変条件

影響範囲

ADR 0028開始ゲート

0028の削除へ進む前に、次の機能完成ゲートをすべて満たす。本節はADR 0027全体の受け入れ条件ではなく、ADR 0028を開始するための中間ゲートである。

受け入れ条件

本ADRは、次をすべて満たしたときに実装完了とする。

検討したが採用しない選択肢