ステータス:Accepted(Phase 1〜5実装済み。decisions/0009で決定した方向性の詳細設計)
関連文書:decisions/0009、decisions/0008、テスト知識管理のGit-nativeモデル_統合版.md
想定読者:markharnessの実装者(Phase 1着手時に参照する)
位置づけ:本書は、markharnessの既存ドキュメントと現在のRust実装を前提として、今後の機能追加・保守性・テスト容易性・大規模リポジトリへの適用を支えるアーキテクチャを整理したものである。Webサーバー・常駐プロセス・正準データベース・マイクロサービスは導入せず、Gitリポジトリを正準な永続化基盤、YAML/JSONを交換形式、CLIおよびCIを利用者向けInterfaceとする現行の性質を維持する。ユーザーから提供された初版提案(2026-08-18)に対し、decisions/0009で決定した2点の修正(ChangeAnalyzerのCommitRef一般化、GitRepository trait導入の先送り)を反映している。
提案の中心は、現在のGit-nativeな単一CLIという性質を維持しながら、以下の処理を一貫したパイプラインとして構成することである。
Test Knowledge
-> TestCaseの決定的生成
-> マイルストーン間のChangeEvent導出
-> 影響TestCaseの特定
-> 実行証拠との照合
-> pending / stale状態の導出
knowledge/、axes/、generated/、executions/、changes/をGit管理する。feature.ymlのidとFeatureディレクトリ全体のtree SHAで識別する。各Moduleは、呼び出し側が学ぶInterfaceを小さくし、その内部に複雑な実装を隠す。
flowchart TB
CLI["CLI / JSON出力"]
APP["Application Use Cases"]
subgraph DOMAIN["Domain Modules"]
KW["KnowledgeWorkspace"]
TC["TestcaseCompiler"]
CA["ChangeAnalyzer"]
VE["VerificationEngine"]
BF["BackfillCoordinator"]
end
subgraph INFRA["Infrastructure"]
GIT["Git Adapter (git.rs)"]
KS["KnowledgeSource"]
FS["WorkspaceStore"]
SCHEMA["SchemaValidator"]
CACHE["Derived Index / Cache"]
end
subgraph DATA["Git管理データ"]
KNOW["knowledge / axes"]
GEN["generated"]
EXEC["executions"]
CHANGE["changes"]
NOTES["git notes"]
end
CLI --> APP
APP --> KW
APP --> TC
APP --> CA
APP --> VE
APP --> BF
KW --> FS
KW --> SCHEMA
KW --> KS
TC --> KW
CA --> GIT
CA --> KS
CA --> TC
VE --> GIT
VE --> FS
BF --> CA
BF --> GIT
KS --> GIT
FS --> KNOW
FS --> GEN
FS --> EXEC
FS --> CHANGE
GIT --> NOTES
CACHE -.再構築可能.-> KNOW
依存方向は原則として、CLIからApplication、ApplicationからDomain、Domainから必要最小限のInfrastructure上のseamへ向ける。
knowledge/とaxes/を読み込み、正規化されたKnowledge Snapshotを提供するModuleである。
impl KnowledgeWorkspace {
fn load(root: &Path) -> Result<Self>;
fn validate(&self) -> ValidationReport;
fn snapshot(&self) -> &KnowledgeSnapshot;
fn reconcile(&mut self, intent: &IntentDocument) -> Result<ReconcileOutcome>;
}
内部へ隠す処理は以下とする。
forked_fromの参照検査現状はsrc/generate.rsとsrc/validate.rsがそれぞれ独自にknowledge/をfs::read_dirで走査しており、走査ロジックが重複している。KnowledgeWorkspaceの導入により、生成、検証、索引作成が同一コマンド内で同じSnapshotを共有できるようにし、この重複を解消する。
Knowledge SnapshotからTestCaseおよびトレーサビリティ索引を決定的に生成するModuleである。
fn compile(snapshot: &KnowledgeSnapshot) -> Result<GeneratedArtifacts>;
GeneratedArtifactsには以下を含める。
traceability-index.jsonの内容Compilerはファイルへ書き込まない。Application Use Caseが結果をWorkspaceStoreへ渡す。
不変条件は以下とする。
generateとverifyは必ず同じCompilerを使用する。
2つのversion間でFeatureの版を比較し、ChangeEventと影響TestCaseを導出する中核Moduleである。
版参照は、milestoneタグに固定したMilestoneRefではなく、decisions/0009決定3に基づきCommitRefで表現する。
enum CommitRef {
Milestone(MilestoneId), // タグ名。内部でgit tagをcommitへ解決する
Commit(CommitId), // 任意のcommit(PRのbase/head SHA等)
}
impl ChangeAnalyzer {
fn compute(
&self,
from: CommitRef,
to: CommitRef,
options: ChangeOptions,
) -> Result<ChangeSet>;
}
struct ChangeOptions {
cache: CachePolicy,
impact_source: ImpactSource,
}
enum ImpactSource {
HistoricalTree,
CurrentWorkingTree,
}
処理パイプラインは以下とする。
CommitRefをcommitへ解決する(Milestoneはtag解決を経由、Commitはそのまま)。to側Knowledgeから影響TestCaseを導出する。true_divergencesを調べる。changes computeとbackfill runはCommitRef::Milestoneを使って同じChangeAnalyzerを使用する。decisions/0008 Stage 2で追加するPR Verification Plan機能は、CommitRef::Commitを渡すだけで同じChangeAnalyzerを再利用でき、Interfaceの再設計を要しない。
ChangeEvent、TestCaseとの対応、および実行証拠から再検証状態を導出するModuleである。
impl VerificationEngine {
fn trace(&self, input: TraceInput) -> TraceReport;
fn pending(&self, input: VerificationInput) -> PendingReport;
}
状態は文字列ではなく型として表現する。
enum VerificationStatus {
Current,
Pending,
Stale,
Unknown,
}
VerificationEngineはファイルやGitを直接読まず、読み込み済み入力に対する純粋な判定を行う。Application層がChangeEvent、Execution、Feature versionを収集して渡す。現状のsrc/verify.rsはtrace/pending関数がfs::read_to_stringを直接呼んでおり、この分離ができていない。
Unknownは、verified_feature_tree_shasを持たない旧形式の実行記録など、判定根拠が不足する場合に使用する。
未処理のマイルストーンペアを選択し、ChangeAnalyzerを呼び出して進捗を記録するModuleである。
fn run_once(&self, policy: BackfillPolicy) -> Result<BackfillSummary>;
担当範囲は以下とする。
CommitRef::Milestone)常駐ワーカーにはせず、CIやスケジューラーから繰り返し実行できる一回実行型を維持する。
CLIサブコマンドに対応するUse Caseを置く。
application/
init_project.rs
validate_knowledge.rs
apply_knowledge.rs
generate_testcases.rs
verify_generated.rs
compute_changes.rs
record_execution.rs
verify_pending.rs
run_backfill.rs
Application層の責務は以下に限定する。
CommandOutcomeの返却終了コード、標準出力、標準エラー出力を直接扱わない。
enum CommandOutcome {
Generated(GenerateSummary),
Validation(ValidationReport),
Changes(ChangeSummary),
Verification(PendingReport),
}
CLIは以下だけを担当する。
CommandOutcomeのPresenterへの引き渡し人間向け表示とJSON出力は同じ結果型から生成する。
trait Presenter {
fn present(&self, outcome: &CommandOutcome) -> PresentedResult;
}
struct PresentedResult {
stdout: String,
stderr: String,
exit_code: i32,
}
これにより、Domain層およびApplication層からprintln!、eprintln!、std::process::exitを排除する。現状のsrc/cli.rs(2248行)はprocess::exitを32箇所、println!/eprintln!を92箇所含んでおり、この分離ができていない。
Gitはmarkharnessのドメインに不可欠であるため、汎用的なRepository<T>には抽象化しない。まず、現在src/changes.rsに分散している直接的なGitプロセス呼び出し(Command::new("git")が5箇所)をgit.rsへ集約する。
trait化は今回のスコープに含めない(decisions/0009決定4)。実装が単一(gitプロセスAdapter)である間は、以下のような関数群としてgit.rsに置く。
// git.rs — 集約後の関数群のイメージ(trait化はしない)
fn resolve_commit_ref(root: &Path, git_ref: &CommitRef) -> Result<CommitId>;
fn feature_trees(root: &Path, commit: &CommitId) -> Result<Vec<FeatureTree>>;
fn milestones(root: &Path) -> Result<Vec<Milestone>>;
fn merges_between(root: &Path, from: &CommitId, to: &CommitId) -> Result<Vec<MergeInfo>>;
fn read_note(root: &Path, key: &NoteKey) -> Result<Option<String>>;
fn write_note(root: &Path, key: &NoteKey, value: &str) -> Result<()>;
trait化(例:GitRepository trait)は、テスト用のfake実装が具体的に必要になった、または複数Adapter(他VCSサポート等)が要件化した、といった明確な必要性が生じた段階で改めて判断する。テストでは、小さな実Gitリポジトリを一時領域に作成する統合テストを引き続き優先する。
大規模リポジトリ対応として、Knowledgeの供給元を次のseamで切り替えられるようにする。ここは最初から2つの具体的なAdapterが必要なため、7.1とは異なりtrait化する。
trait KnowledgeSource {
fn list(&self, prefix: &RepoPath) -> Result<Vec<KnowledgeEntry>>;
fn read(&self, path: &RepoPath) -> Result<Vec<u8>>;
}
Adapterは次の2つを想定する。
WorkingTreeKnowledgeSourceGitTreeKnowledgeSourceこれにより、現在のworking treeと過去commitのGit treeを同じParserおよびCompilerへ渡せる。現状のhistorical_testcases_by_feature(src/changes.rs)はマイルストーンごとにgit worktree add→generate_testcases→git worktree removeを実行しており、GitTreeKnowledgeSourceの導入によってこの一時worktree作成が不要になる。
既存のfs_safetyを維持し、以下を共通化する。
generateについては、ディレクトリ全体のトランザクション性を追加する。
1. 一時ディレクトリへ全TestCaseを生成
2. traceability indexを生成
3. 全出力が成功したことを確認
4. generated/testcasesを切り替える
5. traceability indexを切り替える
途中失敗時には既存の生成物を保持する。
.markharness-cache/は正準データではなく、削除・再構築可能な派生物とする。この方針は現状のsrc/id_cache.rsで既に実装済みであり、そのキャッシュキーは以下の式と一致する。
hash(
knowledge_tree_sha
+ canonicalization_rule_version
+ id_index_schema_version
+ tool_version
)
将来的に、同じ方針で以下の索引を追加できる。
.markharness-cache/
feature-versions/ # 既存(id_cache.rs)
testcase-by-feature/ # 新規
changeevent-by-feature/ # 新規
execution-by-case/ # 新規
SQLiteを使用する場合も正準DBにはせず、再構築可能なローカル索引に限定する。
src/
main.rs
cli/
mod.rs
args.rs
presenter.rs
application/
mod.rs
commands/
domain/
knowledge/
mod.rs
model.rs
validation.rs
generation/
mod.rs
compiler.rs
artifact.rs
change/
mod.rs
analyzer.rs
model.rs # CommitRef、ChangeOptions等
verification/
mod.rs
engine.rs
model.rs
backfill/
mod.rs
coordinator.rs
infrastructure/
git/
mod.rs # 集約後のgit呼び出し(trait化しない)
knowledge_source/
mod.rs
working_tree.rs
git_tree.rs
workspace/
mod.rs
yaml.rs
atomic_write.rs
schema/
mod.rs
cache/
mod.rs
safety/
paths.rs
ファイル分割自体を目的にしない。小さな型や関数だけのファイルを過剰に作らず、ModuleのInterfaceと責務が明確になる単位で分割する。Phase4の段階で、必要に応じてこの構成へ再編する。
現在の実装は以下をすでに満たしている。
generate、changes、verify、backfill、gitなどの機能別Modulegenerateとverifyによる生成ロジックの共有backfillからcompute_changesを再利用fs_safetyによるpath traversal、symlink、junction対策id_cache.rs、7.4節)したがって、本設計は全面的な再実装ではなく、現在の長所を維持した構造整理である。
| 観点 | 現在 | 提案 |
|---|---|---|
| 全体 | 単一crate | 単一crateを維持 |
| Module配置 | 機能別のフラットな.rs |
Domain / Application / Infrastructure |
| CLI | 解析、実行、表示、終了を一括担当 | 解析とPresenter選択に限定 |
| Knowledge | 各機能が必要に応じて走査 | 正規化済みSnapshotを共有 |
| TestCase生成 | パスを受けて読込と生成を同時実行 | Snapshotを受けるCompiler |
| Change計算 | パスと複数boolを受ける関数、milestoneタグ専用 | CommitRefと設定型を受けるAnalyzer(milestone/PR共通) |
| Verification | I/Oと状態判定を同時実行 | Data Loaderと純粋なEngineを分離 |
| Git | git.rsと一部直接呼び出し |
Git呼び出しをgit.rsへ集約(trait化はしない) |
| 生成物更新 | ファイル単位で安全 | ディレクトリ全体でも原子的 |
| スケールの種類 | 改善度 | 理由 |
|---|---|---|
| 機能追加 | 大 | Use CaseとDomainの責務が分離される |
| コード量 | 大 | 変更の局所性が高くなる |
| 開発人数 | 大 | 巨大なcli.rsへの変更集中を避けられる |
| テスト数 | 大 | I/OなしでDomain判定をテストできる |
| 出力形式追加 | 中〜大 | Presenterを追加できる |
| Importer追加 | 中〜大 | KnowledgeWorkspaceのInterfaceへ接続できる |
| Knowledge件数 | 小〜中 | Snapshotを共有した場合に重複読込を削減できる |
| Git履歴・milestone数 | 小 | 中核アルゴリズムは同じ |
| 水平スケール | なし | ローカルCLIを維持するため |
本設計の主な効果は、実行速度よりも、コード量、機能数、開発人数が増えた場合の保守性である。
大規模データに対する性能改善には、アーキテクチャ整理に加えて以下を実装する。
let snapshot = workspace.load_snapshot()?;
validate(&snapshot);
compile(&snapshot);
build_traceability(&snapshot);
同一プロセス内で検証、生成、索引作成がYAMLを繰り返し読み込まないようにする。
Knowledge tree SHA
-> 変更Feature ID
-> 該当FeatureのTestCaseだけ再生成
-> 全体Manifestを更新
正確性を担保するため、全生成を正準動作として維持する。
generate 全生成
generate --incremental 増分生成
CI 定期的に全生成で検証
GitTreeKnowledgeSourceにより、一時worktreeを作らずに対象commitのblob/treeから過去Knowledgeを読み込む。historical_testcases_by_featureの置き換え対象。
以下の検索を再構築可能な索引で高速化する。
Feature ID -> ChangeEvent
Feature ID -> TestCase
case_id -> Execution milestones
case_id -> verified tree SHA
--max-pairs 10
--time-budget 5m
--from-milestone <name>
CIの実行時間を予測可能にする。並列化は、同一出力ファイルとGit notesへの競合制御を設計した後に行う。
現時点では、サーバー、共有DB、ジョブキューによる水平スケールは採用しない。
markharnessの主要な実行機会はローカル編集、PR時CI、tag push時のChange計算、定期backfillである。まず単一プロセス内で以下を実施する方がGit-nativeな性質と整合する。
CommitRef::MilestoneとCommitRef::Commitの両方でChangeAnalyzerが同一の判定ロジックを通ることfeature.ymlを変えずConditionだけ変更してもtree SHAが変わる。true_divergencesを検出できる。ChangeAnalyzerが動作する。generateを2回実行して同一バイト列になる。verifyが追加、変更、削除を区別する。decisions/0009決定8の要約。詳細な作業単位は各Phase着手時にchecklist-<task>.mdで管理する。
compute_changesのbool引数をChangeOptionsへ置換する。changes.rs内の直接Git呼び出しをgit.rsへ集約する(trait化はしない、7.1節)。tests/fixtures/stage0/changes-m1-m2.golden.ymlで固定する。この段階ではディレクトリ構成を変更しない。
CommandOutcomeを導入する。generate、changes compute、verify pendingからApplication Use Caseを抽出する。generated/へ反映する。KnowledgeSnapshotを導入する。Current/Pending/Stale/Unknownを返す純粋な状態判定を分離する。ChangeAnalyzerをCommitRefベースで確定させる(4.3節)。KnowledgeSourceとworking tree/Git treeのAdapterを導入する。GitTreeKnowledgeSourceによるblob直読で一時worktreeを置き換える。.markharness-cache/index/へ再構築可能な派生物として追加する。--max-pairsと--time-budgetを追加する。Gitリポジトリ内のローカルな知識管理という性質に対し、ネットワーク、認証、分散トランザクション、運用基盤の複雑さが過大になるため採用しない。
Gitの履歴、レビュー、branch/tag運用と二重の正準データが生じるため採用しない。SQLite等は再構築可能な索引用途に限定する。
一つしか実装がない依存まで抽象化するとInterfaceが増え、保守性を下げる。Git操作(7.1)は集約のみ先行させ、working treeとGit treeのように実際に複数Adapterが必要なseam(KnowledgeSource、7.2)だけを抽象化する。
キャッシュ破損、削除検出、正規化ルール変更による不整合を発見しにくいため採用しない。全生成を正準として残す。
ChangeAnalyzerをMilestoneRef固定で設計するdecisions/0008 Stage 2が既に決定している「PR base/headを第一級のversion rangeとして扱う」という方向と衝突し、着手時に中核Interfaceの再設計という手戻りが生じるため採用しない。CommitRefへ一般化する(4.3節)。
markharnessには、現在のRust単一CLIとGit-nativeなデータモデルを維持したモジュラーモノリスが適している。
中核となるModuleは以下の5つである。
KnowledgeWorkspaceTestcaseCompilerChangeAnalyzer(CommitRefベース、milestoneとPR base/headの両方を扱う)VerificationEngineBackfillCoordinator現在の実装は、決定的生成、Change計算の再利用、実Gitテスト、ファイル操作の安全性、内容アドレス化されたキャッシュキーなど、本設計の重要部分をすでに実現している。最優先の改善は全面的な再構築ではなく、巨大化したCLIの責務分離、型付きInterface、Git操作の集約、生成物更新の原子性である。
アーキテクチャ整理によって主に改善されるのは、機能数、コード量、開発人数に対するスケールである。データ量への性能改善は、そのInterfaceを土台としてKnowledgeSource、再構築可能な索引、Feature単位の処理、Backfillの処理量制限を段階的に導入することで実現する。ChangeAnalyzerをCommitRefベースで設計することにより、decisions/0008 Stage 2のPR Verification Plan機能への拡張も、この土台の上で後方非互換な再設計なしに行える。