contributes_toを追加するAccepted(2026-09-15決定)。0017がFeature単位で定めたRequirementとの関連(feature.requirement_uids)を、Scenario単位でも持てるように拡張する。
StrictDocを用いる実運用の事例(利用者が実際にコマンドを実行して構築したサンプルプロジェクトの報告。本ADRはそのプロジェクトへのリンク・言及を含めない)で、次の実害が確認された。
1つのFeatureが複数のRequirementにcontributes_toする場合(例: 「タスク管理」Featureが「タスクの管理」と「タスクの永続化」という2つのRequirementに関連する)、そのFeature配下で生成される全てのTestCaseが、実際の検証内容に関わらず両方のRequirementのgenerated_from.requirement_uidsを機械的に持ってしまう。具体的には、「空白入力の抑止」を検証するTestCaseが「永続化」のRequirementとも関連付けられ、逆に「破損データからの復旧」を検証するTestCaseが「タスク管理」のRequirementとも関連付けられる、という誤った対応関係が生じた。
これはNorth Starの問い1(「どの機能に関連するテストケースか、どの要件に関連するかを確認する」)に不正確に答える結果になる。StrictDocのLow-Level Requirement(LLR)は、High-Level Requirement(HLR)よりも細かく、1つの具体的な振る舞い(=1 Scenario、ADR 0017§3の「1 Scenario = 1 TestCase」と同じ粒度)に対応する形で書かれることが一般的であり、Feature単位の関連だけではこの粒度を表現できない。
Scenarioにrequirement_uids: Vec<String>を追加するFeature.requirement_uidsと同じ型・同じ意味を持つフィールドをScenario(src/knowledge.rs)に追加する。#[serde(default)]とし、省略時は空配列になる(本コードベースの追加的フィールドの既存パターンに従う。必須制約は型ではなく個別のバリデーションルールに委ねる)。
StrictDoc側でRequirement同士がParent/Child関係(HLR/LLRの階層)を持っていても、markharnessはそれを一切保持・解釈しない。StrictDocの[REQUIREMENT]が1個あれば、それがHLRであろうとLLRであろうと関係なく、markharness側では同じ形の1個のRequirement(source: externalであればsource_keyにそのUIDを保持)になるだけである(P5「Coreは外部形式を知らない」)。Requirement同士の関係(親子含む)は0017が既に「別の未決定事項」としており、本ADRもその立場を維持する。将来必要になれば別ADRで検討する。
生成されるTestCaseのgenerated_from.requirement_uidsは次の規則で決める。
requirement_uidsが1件以上あれば、Scenario側の値だけを使う(Feature側は無視する)。requirement_uidsが空であれば、Feature側のrequirement_uidsをそのまま使う(フォールバック)。和集合(常にFeatureとScenarioを合算する)は採用しない。目的は「TestCaseごとに正確なRequirementを答える」ことであり、精度の高い情報(Scenario)がある場面で粗い情報(Feature)を混ぜる理由がないためである。Feature側の関連は、Scenario単位に分解していないFeatureのための後方互換的なフォールバックとして機能し続ける。
Scenarioが参照するRequirement UIDがFeatureのrequirement_uidsに含まれていなくても、validateはこれを検出・拒否しない。FeatureとScenarioの関連は独立した情報源として扱い、整合性は著者の責任に委ねる。この制約は実際の必要性が確認されるまで追加しない(YAGNI)。
contributes_toで統一するRequirementIntentと同様、ScenarioIntentにもcontributes_to: [<requirement key or uid>]を追加する(保存後のフィールド名はrequirement_uids)。Feature側の既存フィールドと同じ命名にすることで、利用者が覚えるルールを増やさない。
coverageのTestCase判定ロジックをFeatureメンバーシップから直接照合へ変更する現状のcoverage.rs(features_for_requirement)は、あるRequirementにcontributes_toするFeatureの集合を求め、そのFeatureに属しているというだけでTestCaseを「このRequirementをカバーしている」と扱っていた。case.generated_from.requirement_uidsは一切参照していなかった。
これを、Feature単位のgap検出(RequirementHasNoFeature)は維持する。ただしFeatureは、自身がcontributes_toしている場合に加え、contributes_toするScenarioを持つ場合もRequirementに届いているものとして数える(LLRのようにScenario単位でだけ紐づくRequirementは「Featureが無い」とは報告されず、そのScenarioを持つFeatureがfeature_idsに含まれる)。そのうえで、個々のTestCaseとRequirementの対応判定は、Featureの集合によるゲートを外し、全TestCaseを直接case.generated_from.requirement_uidsで照合する独立したロジックに置き換える。これにより、あるFeatureがcontributes_toしていないRequirementであっても、そのFeature配下のいずれかのScenarioが単独でcontributes_toしていれば正しく検出できる。
Scenario単位の上書きを1件も使わない場合(本ADR以前からの使い方全て)、この判定は現状と数学的に同値になる。generated_from.requirement_uidsはフォールバックによりFeatureのrequirement_uidsをそのまま引き継ぐため、「TestCaseがそのFeatureに属している」ことと「TestCaseのgenerated_from.requirement_uidsにそのRequirementが含まれている」ことが一致するからである。したがって本変更はcoverageの既存の振る舞いを壊すものではなく、より安全側(不正確な対応を排除する側)へ倒す実装である。impact.rsは既にcase.generated_from.requirement_uidsを直接見ているため、追加の修正は不要である。
schema_versionは変更しないプロトタイプ期であるため、markharness-v2-design.md §9.2.2の既存方針(0018・0030と同じ考え方)により、フィールド追加のみを理由にschema_versionを進めない。実際に比較・互換性ゲートを実装する必要が生じるまで値は1のまま固定する。
src/knowledge.rs(Scenario構造体・YAMLシリアライズ)、src/generate.rs(TestCase生成時の合成規則)、src/knowledge_reconcile/(Intentスキーマ・plan構築)、src/coverage.rs(TestCase判定ロジック)、schema/scenario.schema.jsonを変更する。src/impact.rs・src/alignment.rs(Spec-Reviewedトレーラー)は変更不要(前者は既にgenerated_from.requirement_uidsを直接参照、後者はRequirement/Case idを直接指定する独立した仕組みのため無関係)。Behavior単位で関連を持たせる: 実運用サンプルでは、1つのBehavior(例:「タスクの追加」)が複数のScenario(有効な入力/空白入力)を持ち、それぞれ別のRequirementに対応する例が複数確認された。Behavior単位ではこの粒度を表現できず、今回の実害が解消されない。