ステータス:Draft
本書は、markharness CLIが外部へ公開する読み取り用データモデル(リードモデル)を定義する。主な利用者は、将来別リポジトリで実装するmarkharness-viewなどの表示ツールである。
本設計では、表示画面の構造やUIの都合をmarkharnessへ持ち込まない。markharnessは、Git・Knowledge・関連付け・判定結果から、再現可能な読み取りモデルを生成する。impact・coverage(および新設するtraceability)は、各コマンド専用モジュールが構造体を組み立て、cli.rsからserde_jsonで直接JSONへシリアライズする(§8)。現状これらのコマンドに人間可読出力(Human Presenter相当)は実装されていない。追加する場合も同じ構造体から生成する。
Knowledge / Git / Binding
↓
Domainの判定
↓
CLIリードモデル(各コマンド専用モジュールの構造体)
↓
JSON(serde_json直接シリアライズ)
↓
CLI / markharness-view
markharness v2は、Change ImpactとRelease CoverageをCLI/JSONで提供し、ビューやダッシュボードは別ツールに委ねる方針である。したがって、CLIのJSON出力は単なるログではなく、外部ツールが読むための明示的な出力契約として設計する。
一方、リードモデルはDomainモデルやUIモデルと同一ではない。
本書で扱うのは、中央のCLIリードモデルである。
Domain型を直接シリアライズしない。各コマンド専用モジュール(impact・coverage・traceability)がリードモデルの構造体を生成し、それをserde_jsonでシリアライズする。人間可読出力を追加する場合も、Domain型ではなく同じリードモデルの構造体から生成する。
これにより、Domainの内部構造を変更しても、外部出力契約への影響を局所化できる。
リードモデルに画面タブ、選択状態、展開状態、ソート状態、ページング状態などを含めない。これらはmarkharness-viewの責務である。
すべてのKnowledgeを一つの巨大なKnowledgeReadModelへ詰め込まない。利用者の問いに対応する、複数の小さなリードモデルを定義する。
初期対象は次の3つとする。
TraceabilityReadModelChangeImpactReadModelReleaseCoverageReadModel初期実装では、リードモデルを.markharness/へ新しい正本ファイルとして保存しない。コマンド実行時にKnowledge・Git・Binding等から生成し、標準出力へJSONとして出力する。
キャッシュや永続的な派生物が必要になった場合は、別の設計判断として扱う。
ExecutionBindingがあることを、テストが実行済み・合格済みであることとして出力しない。ReleaseScopeも、実行計画や実行結果として表現しない。
record_kind/schema_versionは新設するエンベロープ形式ではない。impact(src/impact.rsのChangeImpact)・coverage(src/coverage.rsのReleaseCoverage)が、CommandOutcome/Presenter(src/presentation.rs)を一切経由せず、cli.rsからserde_jsonで直接出力している既存の形式({"schema_version": 1, "record_kind": "<tag>", ...フィールド})をそのまま踏襲する。record_kindはmarkharness-v2-design.md§9.1・ADR 0025が既に確立している永続レコード(execution_binding・release_scope等)の命名規約と同じ名前であり、impact/coverageのCLI出力も既にこの規約に従っている(ADR 0032参照)。
本書が新設するのは、この既存パターンに従うtraceabilityだけである。canonical_imported/generated/changes_computed(markharness changes compute)はCommandOutcome/Presenter系統の別の出力形式(outcomeフィールド)を使う、無関係な書込み系コマンドであり、本書のリードモデルの対象外とする。
{
"record_kind": "change_impact",
"schema_version": 1
}
record_kindレコードの種類を識別する。リードモデル3種の値は次のとおりとする(change_impact・release_coverageはimpact・coverageが既に出力している値)。
traceability
change_impact
release_coverage
impact --worktreeはchange_impact_worktreeを出力する。フィールド構成はchange_impactと同一で、head_commitは固定文字列"worktree"、rejected_trailersは常に空になる(ADR 0046)。
schema_version初期値は1とする。これは過去形式を読み分けるための互換機構ではなく、異なるレコード種類や将来の別モデルとの取り違えを防ぐための識別情報である。
過去形式の読み取りが必要になった場合は、既存モデルへ分岐を追加するのではなく、別のレコード種類または別のReaderとして設計する。
Requirement・Feature・Behavior・Scenario・TestCaseの関係を、外部ツールが閲覧できる形で提供する。
作業ツリーを読む(ADR 0043)。impact・coverageと異なり、traceabilityには2点比較やリリース監査の要件がなく、generate・verifyと同じく作業ツリーを直接読むことに支障がないためである。コミット済みの特定時点を読みたい場合はcoverage --atを使う。
src/traceability.rs)struct TraceabilityReadModel {
schema_version: u32, // "record_kind": "traceability" と共に出力
record_kind: &'static str,
requirements: Vec<RequirementNode>,
features: Vec<FeatureNode>,
behaviors: Vec<BehaviorNode>,
scenarios: Vec<ScenarioNode>,
test_cases: Vec<TestCaseNode>,
relations: Vec<TraceabilityRelation>,
}
各Nodeには、少なくとも表示IDとUIDを含める(UIDはidentity migrate未実行の要素ではNoneになりうる)。テストケースには、さらにCase revisionと生成パスを含める。
traceabilityの目的は「関係を外部ツールが閲覧できる形で提供する」ことであり(§5.1)、識別子(*_id/*_uid)だけでは閲覧できない。そのため、各Nodeには人間可読なlabelも含める。Feature・Behavior・ScenarioのlabelはKnowledge上で必須のためNoneにならない(String)。Requirementのlabelはnativeの場合のみ存在する(ADR 0023によりexternalはmarkharnessが内容を所有しないため、source_locator/source_keyとは非対称に、externalでは常にNoneのまま追加しない)。
struct RequirementNode {
requirement_id: String,
requirement_uid: Option<String>,
source: &'static str, // "native" | "external"
// source: "native"の場合のみ値を持つ。externalはmarkharnessが内容を
// 所有しないため(ADR 0023)、代表テキストを持たせない。
label: Option<String>,
// source: "external" の場合のみ値を持つ(ADR 0023)。StrictDoc等の実データを
// 参照する手段。source: "native" では常にNone。
source_locator: Option<String>, // 参照する.sdocファイルのリポジトリ内パス
source_key: Option<String>, // StrictDocのMID(ADR 0030)
// このRequirementに紐づくTestCaseのcase_uid(昇順)。規則は`coverage`と同じ
// `generate::testcases_for_requirement`(ADR 0042)。
case_uids: Vec<String>,
}
struct FeatureNode {
feature_id: String,
feature_uid: Option<String>,
label: String,
}
struct BehaviorNode {
behavior_id: String,
// `identity migrate`未実行のBehaviorではNone(他のuidフィールドと同じ)。
behavior_uid: Option<String>,
feature_id: String,
// 親Featureがuidを持たない(未migrate)場合はNone。`*_id`は親の中でしか
// 一意でないため、親をuidでも指す。
feature_uid: Option<String>,
label: String,
}
struct ScenarioNode {
scenario_id: String,
scenario_uid: Option<String>,
behavior_id: String,
behavior_uid: Option<String>, // 親Behaviorが未migrateならNone
label: String,
}
struct TestCaseNode {
case_id: String, // 表示ID。generate.rs等の既存コードと同じ用語(TestCase.case_id)に揃える
case_uid: Option<CaseUid>,
case_revision: CaseRevision, // ハッシュ値の文字列型。u64ではない
relative_path: String,
scenario_id: String,
scenario_uid: Option<String>, // 親Scenarioが未migrateならNone
}
関係は、各Nodeに相手側の配列を重複して持たせず、明示的なRelationとして表す。
enum RelationKind {
ContributesTo, // Feature または Scenario から Requirement へ
GeneratedFrom, // TestCase から Scenario へ
}
struct TraceabilityRelation {
from_uid: String,
to_uid: String,
kind: RelationKind,
}
UIDを持たない要素(未migrate)は、relationsのいずれの側にも現れない。UIDのない値同士を関連付けても、外部の読み取り側が再実行間で同一性を確認できないためである。
例:
{
"from_uid": "feature-uid-1",
"to_uid": "requirement-uid-1",
"kind": "contributes_to"
}
TraceabilityReadModelには、Bindingの有無やCoverageの判定結果を含めない。それらはReleaseCoverageReadModelの責務とする。
requirements・features・behaviorsはKnowledgeに存在する全件を対象とする(生成されたTestCaseの有無を問わない。coverageのAC21と同じ理由で、対応するTestCaseがまだ無いFeatureも、Scenarioをまだ持たないBehaviorも可視化する)。scenarios・test_casesは生成される全TestCaseから導出する。空のPhaseを持つScenarioはgenerateが生成時に拒否するため、実在するScenarioは必ず1件のTestCaseに対応し、この導出に抜け漏れは生じない。knowledge reconcileはScenarioを持たないBehaviorを作れるため、behaviorsをTestCaseから導出すると、外部ツールがその後にScenarioを追加するために必要なbehavior_uidを引けなくなる。
TraceabilityReadModelは読み取り用であり、これを直接Knowledgeファイルへ書き戻すための形式にはしない。既存要素を修正する場合は、読み取り結果から最小限のKnowledge Intentを生成し、knowledge reconcileへ渡す。
既存要素はuidで選択する。
format: markharness/knowledge-intent/v1
mode: merge
features:
- uid: 01J8Z...
id: todo-create
label: Create a TODO
contributes_to: [01J8A..., 01J8B...]
このIntentの意味は次のとおりである。
uidを指定した要素は既存要素の更新対象になる。uidを持たない新規要素は、Intent内のローカルなkeyで相互参照する。axis・contributes_to・proceduresなどのcollectionは、指定した場合に限り全置換する。idを変更した場合は、UIDを維持したrenameになる。mode: mergeでは、Intentに含まれない既存要素を削除しない。したがって、GUIが編集用Intentを生成する際は、少なくとも次を含める。
全KnowledgeをIntentへ無条件に展開する必要はない。未変更のフィールドを省略することで、Intentを小さく保ち、意図しない更新を避ける。
GUIからの適用は次の経路とする。
TraceabilityReadModel
↓
編集対象UID付きの最小Intent
↓
knowledge reconcile --check
↓
利用者の確認
↓
knowledge reconcile
--check後にKnowledgeやidentity stateが変化していた場合、通常実行はstale planとして停止する。GUIは最新のリードモデルを再取得し、編集内容を再確認してからIntentを再生成する。GUIやviewがKnowledgeファイルを直接書き換えてはならない。
traceability show(選んだ1要素の内容)traceabilityの各Nodeは識別子とlabelだけを持ち、内容(軸・説明文・手順)を持たない。GUIの詳細ペインのように内容が要るときは、木から選んだ要素のuidでtraceability showを呼び、その1件の内容だけを読む(ADR 0040)。
markharness traceability show --uid <UID>
traceabilityと同じく、作業ツリーを読む。
出力は、schema_version・record_kind(traceability_detail)・kind・uidに、要素の種類(kind)ごとの項目が加わる1件のJSONである。
kind |
項目 |
|---|---|
requirement |
requirement_id、axis、description |
feature |
feature_id、axis、description |
behavior |
behavior_id、axis、description、procedures |
scenario |
scenario_id、description、phases、implementation_note |
test_case |
case_id、case_revision、phases |
nullにせずキーごと省略する。source: externalのRequirementはdescriptionを持たない(外部文書が内容を持つ。ADR 0023)。phasesは、Knowledgeに書かれたまま返す。各stepsの要素はactionまたはuseを持ち、useは展開しない。phasesは、useを展開した、実際に実行される手順を返す。stepsは文字列の配列である。TestCaseはcase_uidで引き、作業ツリーの現在の版を返す。uidが、その時点のどの要素にも一致しない場合は、終了コード2で終了し、標準出力には何も出さない。GUIは木を再取得する。source_revision・related_issuesとFeatureのforked_fromは、表示に使わないので含めない。編集用Intentを作るとき(§5.4)の現在値も、この出力から取る。
impactは実装済み)impactコマンドが算出した、変更されたRequirement・関連するFeature・TestCase、および対応確認状態を提供する。
impact::ChangeImpact)ChangeImpactReadModelは新規設計ではなく、src/impact.rsのChangeImpactを指す。既にrecord_kind/schema_version付きで実装・出力されている。
struct ChangeImpact {
schema_version: u32, // "record_kind": "change_impact" と共に既に出力
record_kind: &'static str,
rule_version: u32,
base_commit: String,
head_commit: String,
requirements: Vec<RequirementImpact>, // 各RequirementのFeature・TestCase対応を含む
stale_pins: Vec<StalePin>,
rejected_trailers: Vec<RejectedTrailerReport>,
}
struct RequirementImpact {
requirement_id: String,
requirement_uid: Option<String>,
source: &'static str, // "native" | "external"
spec_changed: bool, // Requirement側が変更されたか。Feature変更のみから到達した場合はfalse
feature_ids: Vec<String>,
cases: Vec<CaseAlignment>,
}
struct CaseAlignment {
case_id: String,
case_uid: Option<String>,
case_changed: bool,
status: AlignmentStatus, // Confirmed | FollowedUp | Unconfirmed
}
Spec-Reviewed trailerによる対応確認状態(AlignmentStatus)は、次の区別を維持する(ADR 0019)。
confirmed その組に対する有効なSpec-Reviewedがある
followed_up 区間内で両側が変更されたが、確認の記録は無い
unconfirmed 仕様側または関連Featureが変更され、追随した形跡も確認の記録も無い
impactimpact --base <ref> --head <ref>は、件数だけの簡易結果ではなく、ChangeImpact全体(各RequirementのFeature・TestCase対応、対応確認状態)を既に返している。markharness-viewはこの出力をそのまま読み取り対象にできる。追加の実装は不要である。
coverageは実装済み)指定されたRequirement集合とGit refにおける、Feature・TestCase・検証手段・coverage gap・ReleaseScopeの選定状態を提供する。
coverage::ReleaseCoverage)ReleaseCoverageReadModelは新規設計ではなく、src/coverage.rsのReleaseCoverageを指す。既にrecord_kind/schema_version付きで実装・出力されている。
struct ReleaseCoverage {
schema_version: u32, // "record_kind": "release_coverage" と共に既に出力
record_kind: &'static str,
rule_version: u32,
at_commit: String,
requirements: Vec<RequirementCoverage>,
gaps: Vec<CoverageGap>,
release: Option<ReleaseView>, // releaseを指定しなかった場合は省略(フィールド自体が出力されない)
}
struct RequirementCoverage {
requirement_id: String,
requirement_uid: Option<String>,
source: &'static str, // "native" | "external"
feature_ids: Vec<String>,
cases: Vec<CaseCoverage>,
}
struct CaseCoverage {
case_id: String,
case_uid: Option<String>,
feature_id: String,
binding_mode: Option<String>, // 未宣言なら省略
binding_reference: Option<String>, // 未宣言なら省略
reference_status: Option<ReferenceStatus>, // binding_referenceがある場合のみ。exists | missing | not_checked
selected: Option<bool>, // releaseを指定した場合のみ意味を持つ
}
struct CoverageGap {
kind: GapKind, // "requirement_has_no_feature" | "feature_has_no_case"
requirement_id: String,
feature_id: Option<String>,
}
struct ReleaseView {
release_id: String,
selected_case_uids: Vec<String>,
unselected_case_uids: Vec<String>,
absent_case_uids: Vec<String>,
}
binding_mode/binding_referenceは、ExecutionBinding(検証手段の宣言)をそのまま表す。これらの値を「実行済み」「合格済み」と表示してはならない。合否・実行日時・実行者・実行環境は別ツールの責務である(ADR 0025 §1)。「選定された(selected)」と「実行された」も混同しない(ADR 0024 §5)。
reference_statusは、binding_referenceが--atのコミットのtreeに実在するかを示す(exists・missing・not_checked)。作業ツリーは見ない。URLはnot_checkedで、到達性は確認しない。existsは参照先がある事実だけで、実行や合格を意味しない。判定規則と、誤判定されうるパターンはADR 0041に従う。
coveragecoverage --requirements <ids-or-all> [--release <id>] --at <ref>は、上記ReleaseCoverage全体を既に返している。markharness-viewはこの出力をそのまま読み取り対象にできる。追加の実装は不要である。
既存のCommandOutcome(src/presentation.rs)は、CanonicalImported・Generated・ChangesComputedの3variantを持つ型で、canonical_import・generate・changes computeという書込み系コマンドの処理結果を表す。Presenter(HumanPresenter・JsonPresenter)がこれをシリアライズする。
リードモデルはこのCommandOutcomeを拡張しない。impact(ChangeImpact)・coverage(ReleaseCoverage)は、そもそもCommandOutcome/Presenterを経由せず、各コマンド専用のモジュールが自分の構造体をcli.rsから直接serde_jsonでシリアライズしている。新設するtraceability(TraceabilityReadModel)も、このimpact/coverageと同じ経路に合わせる(ADR 0032)。
なおsrc/traceability.rsという名前は、generateが組み立てる生成アーティファクトTraceabilityIndex(.markharness/generated/traceability-index.json。verifyが再生成の決定性確認に使う)が既に使っている。両者は生成物か問い合わせ結果かという点を含め責務が異なるため、既存ファイルをsrc/traceability_index.rsへリネームし、空いたsrc/traceability.rsを新設のtraceabilityコマンド用に使う(ADR 0032 決定2)。
CommandOutcome(書込み系。CanonicalImported / Generated / ChangesComputed)
→ Presenter(Human / Json)
読み取り系コマンドの専用モジュール(CommandOutcomeとは無関係)
impact::ChangeImpact → 実装済み。cli.rsからserde_jsonで直接出力
coverage::ReleaseCoverage → 実装済み。cli.rsからserde_jsonで直接出力
traceability::TraceabilityReadModel → 新設。同じ経路に合わせる
traceability_index::TraceabilityIndex → 既存(旧traceability.rsからリネーム)。generateの生成アーティファクト。読み取り系コマンドではない
初期の対応は次のとおりとする。
markharness traceability
→ TraceabilityReadModel(作業ツリー。ADR 0043)
markharness traceability show --uid <UID>
→ TraceabilityDetailReadModel(選んだ1要素の内容。ADR 0040)
markharness impact --base <ref> --head <ref>
→ ChangeImpactReadModel
markharness coverage --requirements <ids-or-all> [--release <id>] --at <ref>
→ ReleaseCoverageReadModel
traceabilityは新設する。既存のgenerateは生成物を書き込むコマンドであり、生成処理の成功結果とKnowledgeの閲覧結果を同じコマンドへ混ぜない。traceabilityは読み取り専用とし、作業ツリーのKnowledgeと生成済みTestCaseを読む(ADR 0043)。impact・coverageは2点比較・リリース監査という性質上--at(または--base/--head)でコミットを指定する。
impactとcoverageは、ChangeImpactReadModel・ReleaseCoverageReadModel全体をJSONで返す。出力の形式を選ぶオプション(--format)は設けない(ADR 0043)。人間可読出力が必要になった場合は、その時点で、同じ構造体から生成する。
コマンド名とJSONのrecord_kindは一致させるが、JSON契約の識別はコマンド名ではなくrecord_kindとschema_versionで行う。
初期リードモデルでは、次を新しい正本ファイルとして保存しない。
.markharness/配下のKnowledge全体の複製既存のKnowledge、Binding、ReleaseScope、Git履歴を入力として、必要な時点で決定的に生成する。
リードモデルは、次の観点でテストする。
record_kindとschema_versionが必ず出力される。ExecutionBindingを実行結果と誤認させる表現がない。source_locator・source_keyにより、StrictDoc等の実データへ実際に辿り着けること(区別が付くだけでは不十分)。JSON fixtureをリードモデル単位で用意し、viewリポジトリが参照できる出力例としても利用する。
fixtureはtests/fixtures/read-models/<record_kind>/v1/に置く。markharness側のfixtureを各リードモデルの正規例とし、CLI統合テストで実際の出力と一致することを検証する。
次のモデルは、viewで具体的な必要性が確認されてから追加する。
これらを将来性だけを理由に初期モデルへ含めない。二つ目の実在する読み取り形式や、具体的な利用上の不足が現れた時点で、新しいリードモデルまたはReaderを設計する。
なお各Nodeのlabel(§5.2)はこの限りではない。traceability自身の目的である「閲覧できる形での提供」に直接必要な、識別子の付随情報であり、TestCaseの本文のような独立したcontentではないため、初期モデルに含める。
なお、TestCaseの詳細表示専用モデルは、GUIの詳細ペインという具体的な必要が確認されたため、traceability show(§5.5)として追加済みである(ADR 0040)。
traceabilityは独立コマンドにするgenerateへの統合は採用しない。generateはKnowledgeからTestCaseと生成物を作成する書込みコマンドであり、traceabilityはKnowledgeと生成済みTestCaseを読む読み取りコマンドである。両者を分けることで、読み取りだけを行いたいviewが生成処理やファイル書込みを発生させずに済む。
RequirementNode・FeatureNode・BehaviorNode・ScenarioNode・TestCaseNodeを分ける。全種類を共通のNode { kind, uid, fields }へ正規化する方式は採用しない。
理由は、型ごとの必須項目・外部Requirementの制約・TestCaseのCase revisionなどの意味をJSON契約から失わせないためである。関係の表現だけは共通のTraceabilityRelationへ正規化する。
外部契約を実装とfixtureだけで管理しない。既存のschema/ディレクトリに、次のJSON Schemaを置く。
schema/traceability-read-model.schema.json
schema/change-impact-read-model.schema.json
schema/release-coverage-read-model.schema.json
JSON Schemaは構造と型を検証する。UIDの存在、Git refの意味、Bindingを実行結果と解釈してはならないことなど、意味上の不変条件はRustのDomain/Applicationテストで検証する。
markharnessとviewは別リポジトリのため、初期段階で共有ディレクトリ、submodule、実行時の相互参照は設けない。
markharnessは、リードモデルのJSON Schemaと代表fixtureを公開契約として管理する。CLI統合テストtests/read_model_contract.rsが、実際のCLI出力をSchemaで検証し、fixtureと照合する。viewは対応するrecord_kindとschema_versionのfixtureを自分のリポジトリへ取り込み、契約テストに使用する。fixture更新は、Schema変更または意図的な出力契約変更として明示的に行う。
この分離により、viewのビルドがmarkharnessの作業ツリーやローカルパスへ依存しない。一方、markharness側ではfixtureをCLI統合テストへ使うため、実際のCLI出力と外部契約の乖離を検知できる。
CLIリードモデル設計に関係して、最終的に提供するコマンドは次のとおりとする。
| コマンド | 区分 | 書込み | 主な用途 | 出力リードモデル |
|---|---|---|---|---|
markharness traceability |
新規追加 | なし | Requirement・Feature・Behavior・Scenario・TestCaseの関係を読む(作業ツリー) | TraceabilityReadModel |
markharness impact --base <ref> --head <ref> |
既存(実装済み) | なし | 変更影響、影響を受けるTestCase、対応確認状態を読む | ChangeImpactReadModel |
markharness coverage --requirements <ids-or-all> [--release <id>] --at <ref> |
既存(実装済み) | なし | Release Coverage、検証手段、coverage gapを読む | ReleaseCoverageReadModel |
markharness knowledge reconcile <intent-file> [--check] |
既存コマンド | あり(--check時はなし) |
UID付きIntentを検証し、Knowledgeを作成・更新・renameする | 反映結果。リードモデルの入力を更新する |
markharness-viewが利用するのは、次の読み取りコマンドである。
markharness traceability # 編集中のプレビュー: 作業ツリーを読む(ADR 0043)
markharness traceability show --uid <UID> # 木で選んだ1要素の内容(詳細ペイン用)
markharness impact --base main --head HEAD
markharness coverage --requirements all --at HEAD
これらは標準出力へ1つのJSONリードモデルを出力する。viewはKnowledgeや.markharness/を直接読み取らず、これらの出力を入力とする。traceabilityは、利用者が編集した直後(コミット前)の状態をそのまま反映できる点が、impact・coverageと異なる(ADR 0033)。impactも--worktreeでコミット前の変更を暫定結果として読める(ADR 0046)。
GUI(ADR 0038)は、これに加えて検証手段を読むためにmarkharness binding list --jsonを使う。binding listの出力は§3.3の初期3種のリードモデルではなく、outcomeとschema_versionを最上位に持ち各要素がrecord_kindを持つ形式である。したがって、GUIが読むことは0038の範囲で決め、リードモデルへ加えるかどうかは別に決める。
GUIやviewがテスト知識を修正する場合は、knowledge reconcileだけを通す。
リードモデルを取得
↓
UID付きの最小Intentを生成・編集
↓
markharness knowledge reconcile <intent-file> --check
↓
markharness knowledge reconcile <intent-file>
traceability・impact・coverageがKnowledgeファイルを直接変更することはない。また、viewがKnowledgeファイルを直接変更することもない。
次のコマンドは、初期リードモデル設計には追加しない。
markharness view:viewは別リポジトリの別ツールとして提供する。ビューアの実装を本体に持たないという意味であり、別ツールのGUIを起動するだけのmarkharness guiは、ADR 0038により追加するmarkharness serve:ローカルWebサーバーを本体へ組み込まないmarkharness search:具体的な検索要件が確認されるまで作らないmarkharness edit:編集経路を増やさず、Intentとknowledge reconcileへ統一する