harness-everything 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -0
  4. package/ci/fixtures/doc-obligations/derive/acceptance-only/base/docs/prd/graph.md +22 -0
  5. package/ci/fixtures/doc-obligations/derive/acceptance-only/candidate/docs/prd/graph.md +22 -0
  6. package/ci/fixtures/doc-obligations/derive/alias-only/base/docs/prd/graph.md +24 -0
  7. package/ci/fixtures/doc-obligations/derive/alias-only/candidate/docs/prd/graph.md +24 -0
  8. package/ci/fixtures/doc-obligations/derive/assertion-no-revision/base/ci/mechanism-lint.test.js +2 -0
  9. package/ci/fixtures/doc-obligations/derive/assertion-no-revision/base/docs/prd/graph.md +20 -0
  10. package/ci/fixtures/doc-obligations/derive/assertion-no-revision/candidate/ci/mechanism-lint.test.js +2 -0
  11. package/ci/fixtures/doc-obligations/derive/assertion-no-revision/candidate/docs/prd/graph.md +20 -0
  12. package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/base/ci/mechanism-lint.test.js +2 -0
  13. package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/base/docs/prd/graph.md +20 -0
  14. package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/candidate/ci/mechanism-lint.test.js +2 -0
  15. package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/candidate/docs/prd/graph.md +20 -0
  16. package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/inventory.json +7 -0
  17. package/ci/fixtures/doc-obligations/derive/attribution-impossible/base/ci/mechanism-open.test.js +3 -0
  18. package/ci/fixtures/doc-obligations/derive/attribution-impossible/candidate/ci/mechanism-open.test.js.fixture +2 -0
  19. package/ci/fixtures/doc-obligations/derive/clause-context-typo/base/docs/prd/graph.md +24 -0
  20. package/ci/fixtures/doc-obligations/derive/clause-context-typo/candidate/docs/prd/graph.md +24 -0
  21. package/ci/fixtures/doc-obligations/derive/code-only/base/src/lint.js +1 -0
  22. package/ci/fixtures/doc-obligations/derive/code-only/candidate/src/lint.js +1 -0
  23. package/ci/fixtures/doc-obligations/derive/comment-only-test/base/ci/mechanism-lint.test.js +2 -0
  24. package/ci/fixtures/doc-obligations/derive/comment-only-test/candidate/ci/mechanism-lint.test.js +2 -0
  25. package/ci/fixtures/doc-obligations/derive/computed-member-assert/base/ci/mechanism-c1.test.js +2 -0
  26. package/ci/fixtures/doc-obligations/derive/computed-member-assert/candidate/ci/mechanism-c1.test.js +2 -0
  27. package/ci/fixtures/doc-obligations/derive/computed-member-variable/base/ci/mechanism-c1.test.js +2 -0
  28. package/ci/fixtures/doc-obligations/derive/computed-member-variable/candidate/ci/mechanism-c1.test.js +2 -0
  29. package/ci/fixtures/doc-obligations/derive/config.json +11 -0
  30. package/ci/fixtures/doc-obligations/derive/decision-fence-backtick/base/docs/adr/GRPH-010.md +20 -0
  31. package/ci/fixtures/doc-obligations/derive/decision-fence-backtick/candidate/docs/adr/GRPH-010.md +20 -0
  32. package/ci/fixtures/doc-obligations/derive/decision-fence-tilde/base/docs/adr/GRPH-010.md +20 -0
  33. package/ci/fixtures/doc-obligations/derive/decision-fence-tilde/candidate/docs/adr/GRPH-010.md +20 -0
  34. package/ci/fixtures/doc-obligations/derive/deep-strict-equal/base/ci/mechanism-s1.test.js +2 -0
  35. package/ci/fixtures/doc-obligations/derive/deep-strict-equal/candidate/ci/mechanism-s1.test.js +2 -0
  36. package/ci/fixtures/doc-obligations/derive/deleted-adr/base/docs/adr/GRPH-008.md +14 -0
  37. package/ci/fixtures/doc-obligations/derive/deleted-prd/base/ci/mechanism-lint.test.js +2 -0
  38. package/ci/fixtures/doc-obligations/derive/deleted-prd/base/docs/prd/graph.md +20 -0
  39. package/ci/fixtures/doc-obligations/derive/deleted-prd/candidate/ci/mechanism-lint.test.js +2 -0
  40. package/ci/fixtures/doc-obligations/derive/derived-rename/base/docs/index.md +3 -0
  41. package/ci/fixtures/doc-obligations/derive/derived-rename/base/src/model/a.js +1 -0
  42. package/ci/fixtures/doc-obligations/derive/derived-rename/candidate/docs/index.md +3 -0
  43. package/ci/fixtures/doc-obligations/derive/derived-rename/candidate/src/model/b.js +1 -0
  44. package/ci/fixtures/doc-obligations/derive/derived-rename/config.json +7 -0
  45. package/ci/fixtures/doc-obligations/derive/derived-two-outputs/base/docs/index.md +3 -0
  46. package/ci/fixtures/doc-obligations/derive/derived-two-outputs/base/docs/model-summary.md +3 -0
  47. package/ci/fixtures/doc-obligations/derive/derived-two-outputs/base/src/model.js +1 -0
  48. package/ci/fixtures/doc-obligations/derive/derived-two-outputs/candidate/docs/index.md +3 -0
  49. package/ci/fixtures/doc-obligations/derive/derived-two-outputs/candidate/docs/model-summary.md +3 -0
  50. package/ci/fixtures/doc-obligations/derive/derived-two-outputs/candidate/src/model.js +1 -0
  51. package/ci/fixtures/doc-obligations/derive/derived-two-outputs/config.json +10 -0
  52. package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/base/ci/mechanism-lint.test.js +2 -0
  53. package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/base/docs/prd/graph-one/r001.md +19 -0
  54. package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/base/docs/prd/graph-two/r001.md +19 -0
  55. package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/candidate/ci/mechanism-lint.test.js +2 -0
  56. package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/candidate/docs/prd/graph-one/r001.md +19 -0
  57. package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/candidate/docs/prd/graph-two/r001.md +19 -0
  58. package/ci/fixtures/doc-obligations/derive/docs-only/base/docs/reference/guide.md +3 -0
  59. package/ci/fixtures/doc-obligations/derive/docs-only/candidate/docs/reference/guide.md +3 -0
  60. package/ci/fixtures/doc-obligations/derive/dup-req-id/base/ci/mechanism-lint.test.js +2 -0
  61. package/ci/fixtures/doc-obligations/derive/dup-req-id/base/docs/prd/graph-a.md +20 -0
  62. package/ci/fixtures/doc-obligations/derive/dup-req-id/base/docs/prd/graph-b.md +20 -0
  63. package/ci/fixtures/doc-obligations/derive/dup-req-id/candidate/ci/mechanism-lint.test.js +2 -0
  64. package/ci/fixtures/doc-obligations/derive/dup-req-id/candidate/docs/prd/graph-a.md +20 -0
  65. package/ci/fixtures/doc-obligations/derive/dup-req-id/candidate/docs/prd/graph-b.md +20 -0
  66. package/ci/fixtures/doc-obligations/derive/expect-computed-literal/base/ci/mechanism-e.test.js +2 -0
  67. package/ci/fixtures/doc-obligations/derive/expect-computed-literal/candidate/ci/mechanism-e.test.js +2 -0
  68. package/ci/fixtures/doc-obligations/derive/expect-computed-matcher/base/ci/mechanism-e.test.js +2 -0
  69. package/ci/fixtures/doc-obligations/derive/expect-computed-matcher/candidate/ci/mechanism-e.test.js +2 -0
  70. package/ci/fixtures/doc-obligations/derive/expect-not-to-equal/base/ci/mechanism-s1.test.js +2 -0
  71. package/ci/fixtures/doc-obligations/derive/expect-not-to-equal/candidate/ci/mechanism-s1.test.js +2 -0
  72. package/ci/fixtures/doc-obligations/derive/formatting-only-test/base/ci/mechanism-lint.test.js +2 -0
  73. package/ci/fixtures/doc-obligations/derive/formatting-only-test/candidate/ci/mechanism-lint.test.js +2 -0
  74. package/ci/fixtures/doc-obligations/derive/helper-delegated-test/base/docs/prd/graph.md +18 -0
  75. package/ci/fixtures/doc-obligations/derive/helper-delegated-test/candidate/ci/mechanism-helper.test.js +1 -0
  76. package/ci/fixtures/doc-obligations/derive/helper-delegated-test/candidate/docs/prd/graph.md +18 -0
  77. package/ci/fixtures/doc-obligations/derive/historical-only-req/base/ci/mechanism-lint.test.js +2 -0
  78. package/ci/fixtures/doc-obligations/derive/historical-only-req/base/docs/prd/graph/r001.md +25 -0
  79. package/ci/fixtures/doc-obligations/derive/historical-only-req/candidate/ci/mechanism-lint.test.js +2 -0
  80. package/ci/fixtures/doc-obligations/derive/historical-only-req/candidate/docs/prd/graph/r001.md +25 -0
  81. package/ci/fixtures/doc-obligations/derive/historical-only-req/candidate/docs/prd/graph/r002.md +23 -0
  82. package/ci/fixtures/doc-obligations/derive/in-place-current/base/ci/mechanism-inplace.test.js +2 -0
  83. package/ci/fixtures/doc-obligations/derive/in-place-current/base/docs/prd/graph/r001.md +20 -0
  84. package/ci/fixtures/doc-obligations/derive/in-place-current/base/docs/prd/graph/r002.md +26 -0
  85. package/ci/fixtures/doc-obligations/derive/in-place-current/candidate/ci/mechanism-inplace.test.js +2 -0
  86. package/ci/fixtures/doc-obligations/derive/in-place-current/candidate/docs/prd/graph/r001.md +20 -0
  87. package/ci/fixtures/doc-obligations/derive/in-place-current/candidate/docs/prd/graph/r002.md +28 -0
  88. package/ci/fixtures/doc-obligations/derive/in-place-historical/base/ci/mechanism-inplace.test.js +2 -0
  89. package/ci/fixtures/doc-obligations/derive/in-place-historical/base/docs/prd/graph/r001.md +20 -0
  90. package/ci/fixtures/doc-obligations/derive/in-place-historical/base/docs/prd/graph/r002.md +26 -0
  91. package/ci/fixtures/doc-obligations/derive/in-place-historical/candidate/ci/mechanism-inplace.test.js +2 -0
  92. package/ci/fixtures/doc-obligations/derive/in-place-historical/candidate/docs/prd/graph/r001.md +22 -0
  93. package/ci/fixtures/doc-obligations/derive/in-place-historical/candidate/docs/prd/graph/r002.md +26 -0
  94. package/ci/fixtures/doc-obligations/derive/invalid-clause/base/docs/prd/graph.md +24 -0
  95. package/ci/fixtures/doc-obligations/derive/invalid-clause/candidate/docs/prd/graph.md +24 -0
  96. package/ci/fixtures/doc-obligations/derive/legacy-untagged-fallback/base/ci/mechanism-legacy.test.js +6 -0
  97. package/ci/fixtures/doc-obligations/derive/legacy-untagged-fallback/candidate/ci/mechanism-legacy.test.js +6 -0
  98. package/ci/fixtures/doc-obligations/derive/literal-whitespace-assert/base/ci/mechanism-lint.test.js +2 -0
  99. package/ci/fixtures/doc-obligations/derive/literal-whitespace-assert/candidate/ci/mechanism-lint.test.js +2 -0
  100. package/ci/fixtures/doc-obligations/derive/malformed-citation-attribution/base/ci/mechanism-bad.test.js +6 -0
  101. package/ci/fixtures/doc-obligations/derive/malformed-citation-attribution/candidate/ci/mechanism-bad.test.js.fixture +6 -0
  102. package/ci/fixtures/doc-obligations/derive/malformed-prd/base/ci/mechanism-lint.test.js +2 -0
  103. package/ci/fixtures/doc-obligations/derive/malformed-prd/base/docs/prd/graph.md +20 -0
  104. package/ci/fixtures/doc-obligations/derive/malformed-prd/candidate/ci/mechanism-lint.test.js +2 -0
  105. package/ci/fixtures/doc-obligations/derive/malformed-prd/candidate/docs/prd/graph.md +11 -0
  106. package/ci/fixtures/doc-obligations/derive/missing-approval/base/docs/prd/graph.md +20 -0
  107. package/ci/fixtures/doc-obligations/derive/missing-approval/candidate/docs/prd/graph.md +20 -0
  108. package/ci/fixtures/doc-obligations/derive/mixed-attribution/base/ci/mechanism-mixed.test.js +6 -0
  109. package/ci/fixtures/doc-obligations/derive/mixed-attribution/base/docs/prd/graph.md +24 -0
  110. package/ci/fixtures/doc-obligations/derive/mixed-attribution/candidate/ci/mechanism-mixed.test.js.fixture +5 -0
  111. package/ci/fixtures/doc-obligations/derive/mixed-attribution/candidate/docs/prd/graph.md +24 -0
  112. package/ci/fixtures/doc-obligations/derive/mixed-docs-code/base/ci/mechanism-lint.test.js +2 -0
  113. package/ci/fixtures/doc-obligations/derive/mixed-docs-code/base/docs/reference/guide.md +3 -0
  114. package/ci/fixtures/doc-obligations/derive/mixed-docs-code/base/src/lint.js +1 -0
  115. package/ci/fixtures/doc-obligations/derive/mixed-docs-code/candidate/ci/mechanism-lint.test.js +2 -0
  116. package/ci/fixtures/doc-obligations/derive/mixed-docs-code/candidate/docs/reference/guide.md +3 -0
  117. package/ci/fixtures/doc-obligations/derive/mixed-docs-code/candidate/src/lint.js +1 -0
  118. package/ci/fixtures/doc-obligations/derive/multiline-comment-only/base/ci/mechanism-multi.test.js +5 -0
  119. package/ci/fixtures/doc-obligations/derive/multiline-comment-only/candidate/ci/mechanism-multi.test.js +5 -0
  120. package/ci/fixtures/doc-obligations/derive/multiline-expect/base/ci/mechanism-multi.test.js +6 -0
  121. package/ci/fixtures/doc-obligations/derive/multiline-expect/candidate/ci/mechanism-multi.test.js +6 -0
  122. package/ci/fixtures/doc-obligations/derive/multiline-formatting-only/base/ci/mechanism-multi.test.js +5 -0
  123. package/ci/fixtures/doc-obligations/derive/multiline-formatting-only/candidate/ci/mechanism-multi.test.js +5 -0
  124. package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/base/ci/mechanism-multi.test.js +5 -0
  125. package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/base/docs/prd/graph.md +18 -0
  126. package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/candidate/ci/mechanism-multi.test.js +5 -0
  127. package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/candidate/docs/prd/graph.md +18 -0
  128. package/ci/fixtures/doc-obligations/derive/multiline-strictequal/base/ci/mechanism-multi.test.js +5 -0
  129. package/ci/fixtures/doc-obligations/derive/multiline-strictequal/candidate/ci/mechanism-multi.test.js +5 -0
  130. package/ci/fixtures/doc-obligations/derive/new-prd/candidate/docs/prd/graph-new.md +20 -0
  131. package/ci/fixtures/doc-obligations/derive/new-reference/candidate/docs/reference/new-note.md +3 -0
  132. package/ci/fixtures/doc-obligations/derive/new-req-test/candidate/ci/mechanism-new.test.js +2 -0
  133. package/ci/fixtures/doc-obligations/derive/orphan-req-test/base/ci/mechanism-lint.test.js +2 -0
  134. package/ci/fixtures/doc-obligations/derive/orphan-req-test/base/docs/prd/graph.md +20 -0
  135. package/ci/fixtures/doc-obligations/derive/orphan-req-test/candidate/ci/mechanism-lint.test.js +2 -0
  136. package/ci/fixtures/doc-obligations/derive/orphan-req-test/candidate/docs/prd/graph.md +20 -0
  137. package/ci/fixtures/doc-obligations/derive/partial-revision/base/ci/mechanism-partial.test.js +5 -0
  138. package/ci/fixtures/doc-obligations/derive/partial-revision/base/docs/prd/graph/r001.md +24 -0
  139. package/ci/fixtures/doc-obligations/derive/partial-revision/candidate/ci/mechanism-partial.test.js +5 -0
  140. package/ci/fixtures/doc-obligations/derive/partial-revision/candidate/docs/prd/graph/r001.md +24 -0
  141. package/ci/fixtures/doc-obligations/derive/partial-revision/candidate/docs/prd/graph/r002.md +26 -0
  142. package/ci/fixtures/doc-obligations/derive/regex-literal-whitespace/base/ci/mechanism-lint.test.js +2 -0
  143. package/ci/fixtures/doc-obligations/derive/regex-literal-whitespace/candidate/ci/mechanism-lint.test.js +2 -0
  144. package/ci/fixtures/doc-obligations/derive/rename/base/docs/reference/old-guide.md +3 -0
  145. package/ci/fixtures/doc-obligations/derive/rename/candidate/docs/reference/guide.md +3 -0
  146. package/ci/fixtures/doc-obligations/derive/replacement-target/base/docs/prd/graph.md +24 -0
  147. package/ci/fixtures/doc-obligations/derive/replacement-target/candidate/docs/prd/graph.md +24 -0
  148. package/ci/fixtures/doc-obligations/derive/req-context-typo/base/docs/prd/graph.md +26 -0
  149. package/ci/fixtures/doc-obligations/derive/req-context-typo/candidate/docs/prd/graph.md +26 -0
  150. package/ci/fixtures/doc-obligations/derive/req-revision/base/ci/mechanism-lint.test.js +2 -0
  151. package/ci/fixtures/doc-obligations/derive/req-revision/base/docs/prd/graph/r001.md +20 -0
  152. package/ci/fixtures/doc-obligations/derive/req-revision/candidate/ci/mechanism-lint.test.js +2 -0
  153. package/ci/fixtures/doc-obligations/derive/req-revision/candidate/docs/prd/graph/r001.md +20 -0
  154. package/ci/fixtures/doc-obligations/derive/req-revision/candidate/docs/prd/graph/r002.md +28 -0
  155. package/ci/fixtures/doc-obligations/derive/req-revision-in-place/base/docs/prd/graph/r001.md +20 -0
  156. package/ci/fixtures/doc-obligations/derive/req-revision-in-place/candidate/docs/prd/graph/r001.md +22 -0
  157. package/ci/fixtures/doc-obligations/derive/retained-revision-chain/base/ci/mechanism-lint.test.js +2 -0
  158. package/ci/fixtures/doc-obligations/derive/retained-revision-chain/base/docs/prd/graph/r001.md +20 -0
  159. package/ci/fixtures/doc-obligations/derive/retained-revision-chain/base/docs/prd/graph/r002.md +26 -0
  160. package/ci/fixtures/doc-obligations/derive/retained-revision-chain/candidate/ci/mechanism-lint.test.js +2 -0
  161. package/ci/fixtures/doc-obligations/derive/retained-revision-chain/candidate/docs/prd/graph/r001.md +20 -0
  162. package/ci/fixtures/doc-obligations/derive/retained-revision-chain/candidate/docs/prd/graph/r002.md +26 -0
  163. package/ci/fixtures/doc-obligations/derive/string-literal-tag/base/ci/mechanism-str.test.js +2 -0
  164. package/ci/fixtures/doc-obligations/derive/string-literal-tag/candidate/ci/mechanism-str.test.js +2 -0
  165. package/ci/fixtures/doc-obligations/derive/tag-inside-test-call/base/ci/mechanism-inside.test.js +5 -0
  166. package/ci/fixtures/doc-obligations/derive/tag-inside-test-call/candidate/ci/mechanism-inside.test.js +5 -0
  167. package/ci/fixtures/doc-obligations/derive/tagged-test/base/ci/mechanism-lint.test.js +2 -0
  168. package/ci/fixtures/doc-obligations/derive/tagged-test/candidate/ci/mechanism-lint.test.js +2 -0
  169. package/ci/fixtures/doc-obligations/derive/tagged-test-prd/base/ci/mechanism-lint.test.js +2 -0
  170. package/ci/fixtures/doc-obligations/derive/tagged-test-prd/base/docs/prd/graph.md +20 -0
  171. package/ci/fixtures/doc-obligations/derive/tagged-test-prd/candidate/ci/mechanism-lint.test.js +2 -0
  172. package/ci/fixtures/doc-obligations/derive/tagged-test-prd/candidate/docs/prd/graph.md +20 -0
  173. package/ci/fixtures/doc-obligations/derive/template-literal-tag/base/ci/mechanism-tpl.test.js +4 -0
  174. package/ci/fixtures/doc-obligations/derive/template-literal-tag/base/docs/prd/graph.md +18 -0
  175. package/ci/fixtures/doc-obligations/derive/template-literal-tag/candidate/ci/mechanism-tpl.test.js +4 -0
  176. package/ci/fixtures/doc-obligations/derive/template-literal-tag/candidate/docs/prd/graph.md +18 -0
  177. package/ci/fixtures/doc-obligations/derive/template-literal-whitespace/base/ci/mechanism-lint.test.js +2 -0
  178. package/ci/fixtures/doc-obligations/derive/template-literal-whitespace/candidate/ci/mechanism-lint.test.js +2 -0
  179. package/ci/fixtures/doc-obligations/derive/title-only-rename/base/ci/mechanism-title.test.js +3 -0
  180. package/ci/fixtures/doc-obligations/derive/title-only-rename/candidate/ci/mechanism-title.test.js +3 -0
  181. package/ci/fixtures/doc-obligations/derive/title-only-unrelated-computed/base/ci/mechanism-u.test.js +4 -0
  182. package/ci/fixtures/doc-obligations/derive/title-only-unrelated-computed/candidate/ci/mechanism-u.test.js +4 -0
  183. package/ci/fixtures/doc-obligations/derive/top-level-no-file-tag/base/ci/mechanism-top.test.js +4 -0
  184. package/ci/fixtures/doc-obligations/derive/top-level-no-file-tag/candidate/ci/mechanism-top.test.js +4 -0
  185. package/ci/fixtures/doc-obligations/derive/trailing-comment-test/base/ci/mechanism-lint.test.js +2 -0
  186. package/ci/fixtures/doc-obligations/derive/trailing-comment-test/base/docs/prd/graph.md +20 -0
  187. package/ci/fixtures/doc-obligations/derive/trailing-comment-test/candidate/ci/mechanism-lint.test.js +2 -0
  188. package/ci/fixtures/doc-obligations/derive/trailing-comment-test/candidate/docs/prd/graph.md +20 -0
  189. package/ci/fixtures/doc-obligations/derive/two-tests-one-file/base/ci/mechanism-two.test.js +6 -0
  190. package/ci/fixtures/doc-obligations/derive/two-tests-one-file/base/docs/prd/graph.md +24 -0
  191. package/ci/fixtures/doc-obligations/derive/two-tests-one-file/candidate/ci/mechanism-two.test.js +6 -0
  192. package/ci/fixtures/doc-obligations/derive/two-tests-one-file/candidate/docs/prd/graph.md +24 -0
  193. package/ci/fixtures/doc-obligations/derive/typo-context/base/docs/adr/GRPH-007.md +14 -0
  194. package/ci/fixtures/doc-obligations/derive/typo-context/candidate/docs/adr/GRPH-007.md +14 -0
  195. package/ci/fixtures/doc-obligations/derive/typo-decision/base/docs/adr/GRPH-007.md +14 -0
  196. package/ci/fixtures/doc-obligations/derive/typo-decision/candidate/docs/adr/GRPH-007.md +14 -0
  197. package/ci/fixtures/doc-obligations/derive/unbalanced-statement/base/ci/mechanism-multi.test.js +5 -0
  198. package/ci/fixtures/doc-obligations/derive/unbalanced-statement/candidate/ci/mechanism-multi.test.js.fixture +4 -0
  199. package/ci/fixtures/doc-obligations/derive/unknown-adr/base/docs/adr/GRPH-009.md +14 -0
  200. package/ci/fixtures/doc-obligations/derive/unknown-adr/candidate/docs/adr/GRPH-009.md +12 -0
  201. package/ci/fixtures/doc-obligations/derive/unsupported-assertion-syntax/base/ci/mechanism-s1.test.js +2 -0
  202. package/ci/fixtures/doc-obligations/derive/unsupported-assertion-syntax/candidate/ci/mechanism-s1.test.js +2 -0
  203. package/ci/fixtures/doc-obligations/derive/whitespace-only-test/base/ci/mechanism-lint.test.js +2 -0
  204. package/ci/fixtures/doc-obligations/derive/whitespace-only-test/candidate/ci/mechanism-lint.test.js +2 -0
  205. package/ci/fixtures/doc-obligations/evidence/base/docs/adr/GRPH-007.md +15 -0
  206. package/ci/fixtures/doc-obligations/evidence/base/docs/prd/PRD-GRPH-001.md +25 -0
  207. package/ci/fixtures/doc-obligations/evidence/candidate/docs/adr/GRPH-007.md +15 -0
  208. package/ci/fixtures/doc-obligations/evidence/candidate/docs/prd/PRD-GRPH-001.md +25 -0
  209. package/ci/mechanism-325-3a-cross.test.js +795 -0
  210. package/ci/mechanism-325-3a-doc-evidence.test.js +1172 -0
  211. package/ci/mechanism-325-3a-doc-obligations.test.js +742 -0
  212. package/ci/mechanism-pr334-haiku.test.js +298 -0
  213. package/ci/mechanism-pr334-luna.test.js +192 -0
  214. package/ci/mechanism-pr334-opencode.test.js +313 -0
  215. package/ci/mechanism-pr334-r2-boundary.test.js +239 -0
  216. package/ci/mechanism-pr334-r2-resolution.test.js +243 -0
  217. package/ci/mechanism-pr334-r2-scope.test.js +127 -0
  218. package/ci/mechanism-pr334-r3-boundary.test.js +356 -0
  219. package/ci/mechanism-pr334-r3-citations.test.js +205 -0
  220. package/ci/mechanism-pr334-r3-headings.test.js +268 -0
  221. package/docs/document-lifecycle.md +57 -1
  222. package/harness-everything/SKILL.md +1 -1
  223. package/harness-everything/scripts/doc-evidence.js +471 -0
  224. package/harness-everything/scripts/doc-lifecycle.js +1 -1
  225. package/harness-everything/scripts/doc-obligations.js +1571 -0
  226. package/opencode-plugin/plugin.json +1 -1
  227. package/package.json +1 -1
  228. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  229. package/plugins/harness-everything/plugin.json +1 -1
  230. package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
  231. package/plugins/harness-everything/skills/harness-everything/scripts/doc-evidence.js +471 -0
  232. package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +1 -1
  233. package/plugins/harness-everything/skills/harness-everything/scripts/doc-obligations.js +1571 -0
  234. package/sbom.cdx.json +7 -7
@@ -0,0 +1,243 @@
1
+ 'use strict';
2
+
3
+ // PR #334 round 2, finding 4307059310: the shared bound-failing-execution lane returned observed/fail
4
+ // before the requirement-sync identity/resolution validation ran. Unresolved cited REQs must never be
5
+ // observed fail; resolved, known, complete identities keep observed fail; unknown scope stays first.
6
+
7
+ const assert = require('assert');
8
+ const crypto = require('crypto');
9
+ const path = require('path');
10
+ const helper = require('./test-helper');
11
+
12
+ const modulePath = path.join(__dirname, '..', 'harness-everything', 'scripts', 'doc-evidence.js');
13
+ let checkDocObligations;
14
+ try {
15
+ ({ checkDocObligations } = require(modulePath));
16
+ } catch (error) {
17
+ helper.check('pr334-r2. evidence module is available', false, error.message);
18
+ }
19
+
20
+ console.log('\n[pr334-r2] requirement-sync resolution before the failing-execution lane (finding 4307059310)...');
21
+
22
+ function sha256(text) {
23
+ return crypto.createHash('sha256').update(text).digest('hex');
24
+ }
25
+
26
+ const OBLIGATION_ID = 'doc-change:requirement-sync';
27
+ const TEST_PATH = 'ci/mechanism-lint.test.js';
28
+ const PRD = 'docs/prd/PRD-GRPH-001.md';
29
+ const PRD_OTHER = 'docs/prd/PRD-GRPH-002.md';
30
+ const REQ_A = 'REQ-GRPH-001';
31
+ const REQ_B = 'REQ-GRPH-002';
32
+ const TEST_DIGEST = sha256('assert.equal(run(), 2);');
33
+ const PRD_DIGEST = sha256('# PRD-GRPH-001 candidate');
34
+ const BASELINE = Object.freeze({ digest: sha256('pr334-r2 baseline') });
35
+
36
+ // F2 envelope: candidateDigest = digest over sorted [path, digest] pairs of artifacts under docs/prd/.
37
+ const ATTESTED_INVENTORY = Object.freeze({
38
+ complete: true,
39
+ waiverInventoryComplete: true,
40
+ baseDigest: BASELINE.digest,
41
+ candidateDigest: sha256(JSON.stringify([[PRD, PRD_DIGEST]])),
42
+ configDigest: sha256('pr334-r2 config')
43
+ });
44
+
45
+ function requirementSync(overrides) {
46
+ return Object.freeze({
47
+ id: OBLIGATION_ID,
48
+ capability: 'requirement-sync',
49
+ strength: 'MUST',
50
+ applicability: 'applicable',
51
+ scope: { paths: [TEST_PATH, PRD] },
52
+ acceptance: 'Evaluate whether affected requirements need an update or supported no-change record.',
53
+ assertionChanged: true,
54
+ inventoryComplete: true,
55
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'resolved' }],
56
+ reqUpdates: [],
57
+ ...(overrides || {})
58
+ });
59
+ }
60
+
61
+ function requirementSyncOmitting(key, overrides) {
62
+ const obligation = { ...requirementSync(overrides) };
63
+ delete obligation[key];
64
+ return obligation;
65
+ }
66
+
67
+ function boundExecution(id, exitCode, obligationId) {
68
+ return {
69
+ id,
70
+ obligationId: obligationId || OBLIGATION_ID,
71
+ command: 'node ci/mechanism-lint.test.js',
72
+ exitCode,
73
+ paths: [TEST_PATH, PRD],
74
+ artifactDigests: { [TEST_PATH]: TEST_DIGEST, [PRD]: PRD_DIGEST },
75
+ baseDigest: BASELINE.digest,
76
+ provenance: 'execution record bound to the current candidate and baseline'
77
+ };
78
+ }
79
+
80
+ function run(obligation, executionRecords) {
81
+ return checkDocObligations({
82
+ obligations: [obligation],
83
+ artifacts: { [TEST_PATH]: { digest: TEST_DIGEST }, [PRD]: { digest: PRD_DIGEST } },
84
+ baseline: BASELINE,
85
+ executionRecords,
86
+ reviewRecords: []
87
+ });
88
+ }
89
+
90
+ function resultFor(output, obligationId) {
91
+ return output.results.find(item => item.obligationId === obligationId);
92
+ }
93
+
94
+ function hasDiagnostic(output, code) {
95
+ return output.diagnostics.some(item => item.code === code);
96
+ }
97
+
98
+ function checkCase(name, cases) {
99
+ try {
100
+ cases();
101
+ helper.check(name, true);
102
+ } catch (error) {
103
+ helper.check(name, false, error && error.message ? error.message : String(error));
104
+ }
105
+ }
106
+
107
+ // RED group 1: unresolved cited identities with a bound, complete, nonzero execution never observed fail.
108
+ const UNRESOLVED_IDENTITIES = [
109
+ { label: 'missing declaration', citedReqs: [{ id: REQ_A, file: PRD, resolution: 'missing' }] },
110
+ { label: 'ambiguous declaration', citedReqs: [{ id: REQ_A, file: PRD, resolution: 'ambiguous' }] },
111
+ { label: 'analysis-error declaration', citedReqs: [{ id: REQ_A, file: PRD, resolution: 'analysis-error' }] },
112
+ { label: 'mutated declaration', citedReqs: [{ id: REQ_A, file: PRD, resolution: 'mutated' }] },
113
+ { label: 'duplicate id across two PRDs', citedReqs: [
114
+ { id: REQ_A, file: PRD, resolution: 'resolved' },
115
+ { id: REQ_A, file: PRD_OTHER, resolution: 'resolved' }
116
+ ] },
117
+ { label: 'null PRD file', citedReqs: [{ id: REQ_A, file: null, resolution: 'resolved' }] },
118
+ { label: 'null identity id', citedReqs: [{ id: null, file: PRD, resolution: 'resolved' }] }
119
+ ];
120
+
121
+ for (const { label, citedReqs } of UNRESOLVED_IDENTITIES) {
122
+ checkCase(`pr334-r2 unresolved identity (${label}) with bound failing execution: none, unresolved, never observed fail`, () => {
123
+ const output = run(requirementSync({ citedReqs }), [boundExecution('exec-fail', 1)]);
124
+ const result = resultFor(output, OBLIGATION_ID);
125
+ assert.ok(result, 'a result is reported');
126
+ assert.strictEqual(result.outcome, 'none', 'an unresolved cited REQ is never an observed fail');
127
+ assert.strictEqual(result.observation, 'unresolved', 'the unresolved REQ leaves the obligation unresolved');
128
+ assert.deepStrictEqual(result.recordRefs, [], 'the failing record is not attributed to an unresolved REQ');
129
+ assert.ok(hasDiagnostic(output, 'req-unresolved'), 'the unresolved REQ is named in a req-unresolved diagnostic');
130
+ });
131
+ }
132
+
133
+ // RED group 2: null, empty, or absent cited identity gives no known identity, so the failing lane must not decide it.
134
+ const UNKNOWN_IDENTITIES = [
135
+ { label: 'citedReqs null', obligation: () => requirementSync({ citedReqs: null }) },
136
+ { label: 'citedReqs empty', obligation: () => requirementSync({ citedReqs: [] }) },
137
+ { label: 'citedReqs absent', obligation: () => requirementSyncOmitting('citedReqs') }
138
+ ];
139
+
140
+ for (const { label, obligation } of UNKNOWN_IDENTITIES) {
141
+ checkCase(`pr334-r2 ${label} with bound failing execution: none, unresolved, never observed fail`, () => {
142
+ const output = run(obligation(), [boundExecution('exec-fail', 1)]);
143
+ const result = resultFor(output, OBLIGATION_ID);
144
+ assert.strictEqual(result.outcome, 'none', 'an unknown cited identity is never an observed fail');
145
+ assert.strictEqual(result.observation, 'unresolved', 'an unknown cited identity leaves the obligation unresolved');
146
+ assert.ok(hasDiagnostic(output, 'req-sync-unverified'), 'the missing identity data is named in a diagnostic');
147
+ });
148
+
149
+ checkCase(`pr334-r2 ${label} with mixed pass and fail executions: none, never observed fail`, () => {
150
+ const output = run(obligation(), [boundExecution('exec-pass', 0), boundExecution('exec-fail', 1)]);
151
+ const result = resultFor(output, OBLIGATION_ID);
152
+ assert.strictEqual(result.outcome, 'none', 'contradictory records under unknown identity yield none');
153
+ assert.strictEqual(result.observation, 'unresolved', 'unknown identity keeps the obligation unresolved');
154
+ });
155
+ }
156
+
157
+ checkCase('pr334-r2 one resolved and one missing cited REQ with bound failing execution: none, never observed fail', () => {
158
+ const output = run(requirementSync({
159
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'resolved' }, { id: REQ_B, file: PRD, resolution: 'missing' }]
160
+ }), [boundExecution('exec-fail', 1)]);
161
+ const result = resultFor(output, OBLIGATION_ID);
162
+ assert.strictEqual(result.outcome, 'none', 'one unresolved cited REQ blocks the whole obligation from observed fail');
163
+ assert.strictEqual(result.observation, 'unresolved');
164
+ });
165
+
166
+ checkCase('pr334-r2 one resolved and one missing cited REQ with mixed pass and fail executions: none, never observed fail', () => {
167
+ const output = run(requirementSync({
168
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'resolved' }, { id: REQ_B, file: PRD, resolution: 'ambiguous' }]
169
+ }), [boundExecution('exec-pass', 0), boundExecution('exec-fail', 1)]);
170
+ assert.strictEqual(resultFor(output, OBLIGATION_ID).outcome, 'none', 'contradictory records under an unresolved REQ yield none');
171
+ });
172
+
173
+ checkCase('pr334-r2 unresolved identity with a complete attested envelope and bound failing execution: none, not fail', () => {
174
+ const output = run(requirementSync({
175
+ inventory: ATTESTED_INVENTORY,
176
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'ambiguous' }]
177
+ }), [boundExecution('exec-fail', 1)]);
178
+ const result = resultFor(output, OBLIGATION_ID);
179
+ assert.strictEqual(result.outcome, 'none', 'an attested envelope cannot turn an unresolved REQ into an observed fail');
180
+ assert.strictEqual(result.observation, 'unresolved');
181
+ });
182
+
183
+ checkCase('pr334-r2 unresolved identity with bound passing execution: none, never pass', () => {
184
+ const output = run(requirementSync({ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'missing' }] }),
185
+ [boundExecution('exec-pass', 0)]);
186
+ assert.strictEqual(resultFor(output, OBLIGATION_ID).outcome, 'none', 'an unresolved REQ never passes');
187
+ });
188
+
189
+ // Controls: resolved, known, complete identities keep observed fail on a bound failing execution.
190
+ checkCase('pr334-r2 control: one resolved known identity with bound failing execution: observed fail', () => {
191
+ const output = run(requirementSync({ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'resolved' }] }),
192
+ [boundExecution('exec-fail', 1)]);
193
+ const result = resultFor(output, OBLIGATION_ID);
194
+ assert.strictEqual(result.observation, 'observed');
195
+ assert.strictEqual(result.outcome, 'fail', 'a resolved known complete identity keeps observed fail');
196
+ assert.deepStrictEqual(result.recordRefs, ['exec-fail'], 'the failing record is attributed');
197
+ });
198
+
199
+ checkCase('pr334-r2 control: resolved REQ without a resolution label with bound failing execution: observed fail', () => {
200
+ const output = run(requirementSync({ citedReqs: [{ id: REQ_A, file: PRD }] }), [boundExecution('exec-fail', 1)]);
201
+ assert.strictEqual(resultFor(output, OBLIGATION_ID).outcome, 'fail', 'an absent resolution label is treated as resolved');
202
+ });
203
+
204
+ checkCase('pr334-r2 control: two resolved known identities with mixed pass and fail executions: observed fail', () => {
205
+ const output = run(requirementSync({
206
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'resolved' }, { id: REQ_B, file: PRD, resolution: 'resolved' }]
207
+ }), [boundExecution('exec-pass', 0), boundExecution('exec-fail', 1)]);
208
+ assert.strictEqual(resultFor(output, OBLIGATION_ID).outcome, 'fail', 'mixed pass and fail with resolved identities stays observed fail');
209
+ });
210
+
211
+ // Controls: unknown scope precedence is unchanged and stays ahead of resolution validation.
212
+ checkCase('pr334-r2 control: unknown scope with unresolved identity and bound failing execution: none, before resolution validation', () => {
213
+ const output = run(requirementSync({
214
+ requirementScopeUnknown: true,
215
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'missing' }]
216
+ }), [boundExecution('exec-fail', 1)]);
217
+ assert.strictEqual(resultFor(output, OBLIGATION_ID).outcome, 'none', 'an unknown scope is refused first');
218
+ assert.ok(!hasDiagnostic(output, 'req-unresolved'), 'unknown scope returns before any resolution diagnostic');
219
+ });
220
+
221
+ checkCase('pr334-r2 control: unknown scope with resolved identity and bound failing execution: none, not observed fail', () => {
222
+ const output = run(requirementSync({
223
+ scope: { paths: [TEST_PATH, PRD], requirementIds: 'unknown' },
224
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'resolved' }]
225
+ }), [boundExecution('exec-fail', 1)]);
226
+ assert.strictEqual(resultFor(output, OBLIGATION_ID).outcome, 'none', 'scope marked unknown by requirementIds is refused first');
227
+ });
228
+
229
+ checkCase('pr334-r2 control: other capability with unresolved citedReqs and bound failing execution: observed fail unchanged', () => {
230
+ const output = run({
231
+ id: 'doc-lint',
232
+ capability: 'doc-lint',
233
+ strength: 'MUST',
234
+ applicability: 'applicable',
235
+ scope: { paths: [TEST_PATH, PRD] },
236
+ citedReqs: [{ id: REQ_A, file: PRD, resolution: 'missing' }]
237
+ }, [boundExecution('exec-lint-fail', 1, 'doc-lint')]);
238
+ const result = resultFor(output, 'doc-lint');
239
+ assert.strictEqual(result.observation, 'observed');
240
+ assert.strictEqual(result.outcome, 'fail', 'a non-requirement capability keeps observed fail on a bound failing execution');
241
+ });
242
+
243
+ helper.finish();
@@ -0,0 +1,127 @@
1
+ 'use strict';
2
+
3
+ const assert = require('assert');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const helper = require('./test-helper');
7
+
8
+ const derivePath = path.join(__dirname, '..', 'harness-everything', 'scripts', 'doc-obligations.js');
9
+ const fixturePath = path.join(__dirname, 'fixtures', 'doc-obligations', 'derive', 'req-revision');
10
+ const derivePathRoot = path.join(__dirname, 'fixtures', 'doc-obligations', 'derive');
11
+ const { deriveDocFacts, deriveDocObligations } = require(derivePath);
12
+ const CONFIG = JSON.parse(fs.readFileSync(path.join(derivePathRoot, 'config.json'), 'utf8'));
13
+ const TEST_A = 'ci/mechanism-pr334-r2-a.test.js';
14
+ const TEST_B = 'ci/mechanism-pr334-r2-b.test.js';
15
+ const A_BASE = '// REQ-GRPH-011\nassert.equal(runA(), 1);\n';
16
+ const A_CHANGED = '// REQ-GRPH-011\nassert.equal(runA(), 2);\n';
17
+
18
+ function readTree(root) {
19
+ const files = {};
20
+ const walk = dir => {
21
+ if (!fs.existsSync(dir)) return;
22
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
23
+ const full = path.join(dir, entry.name);
24
+ if (entry.isDirectory()) walk(full);
25
+ else files[path.relative(root, full).split(path.sep).join('/')] = fs.readFileSync(full, 'utf8');
26
+ }
27
+ };
28
+ walk(root);
29
+ return files;
30
+ }
31
+
32
+ function deriveWithB(beforeB, afterB, afterA = A_CHANGED) {
33
+ const baseFiles = readTree(path.join(fixturePath, 'base'));
34
+ const candidateFiles = readTree(path.join(fixturePath, 'candidate'));
35
+ // The fixture's own test is replaced by the two explicit inputs below.
36
+ delete baseFiles['ci/mechanism-lint.test.js'];
37
+ delete candidateFiles['ci/mechanism-lint.test.js'];
38
+ baseFiles[TEST_A] = A_BASE;
39
+ candidateFiles[TEST_A] = afterA;
40
+ if (beforeB !== undefined) baseFiles[TEST_B] = beforeB;
41
+ if (afterB !== undefined) candidateFiles[TEST_B] = afterB;
42
+
43
+ const result = deriveDocFacts({
44
+ base: { files: baseFiles },
45
+ candidate: { files: candidateFiles },
46
+ config: CONFIG
47
+ });
48
+ const requirementSync = deriveDocObligations(result.facts)
49
+ .find(item => item.capability === 'requirement-sync');
50
+ return { facts: result.facts, requirementSync };
51
+ }
52
+
53
+ function checkCase(name, run) {
54
+ try {
55
+ run();
56
+ helper.check(name, true);
57
+ } catch (error) {
58
+ helper.check(name, false, error && error.message ? error.message : String(error));
59
+ }
60
+ }
61
+
62
+ checkCase('R2 comment-only unrelated REQ test stays out of changed A scope while legal A revision is delivered', () => {
63
+ const { facts, requirementSync } = deriveWithB(
64
+ '// REQ-GRPH-010\n// old note\nassert.equal(runB(), 1);\n',
65
+ '// REQ-GRPH-010\n// revised note\nassert.equal(runB(), 1);\n'
66
+ );
67
+
68
+ assert.strictEqual(facts.requirementSyncTrigger, 'assertion-change', 'A assertion change triggers requirement-sync');
69
+ assert.ok(facts.reqUpdates.some(item => item.id === 'REQ-GRPH-011' && item.file === 'docs/prd/graph/r002.md'),
70
+ `the legal A successor is delivered: ${JSON.stringify(facts.reqUpdates)}`);
71
+ assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-011'], 'only A contributes a cited REQ');
72
+ assert.ok(facts.reqCitingTests.includes(TEST_A), 'A is a contributing test path');
73
+ assert.ok(!facts.reqCitingTests.includes(TEST_B), 'comment-only B is not a contributing test path');
74
+ assert.ok(requirementSync.scope.paths.includes(TEST_A), 'scope retains A');
75
+ assert.ok(!requirementSync.scope.paths.includes(TEST_B), `scope excludes comment-only B: ${requirementSync.scope.paths}`);
76
+ });
77
+
78
+ checkCase('R2 title-only unrelated REQ test edit stays out of changed A scope', () => {
79
+ const { facts, requirementSync } = deriveWithB(
80
+ "test('REQ-GRPH-010 old title', () => { assert.equal(runB(), 1); });\n",
81
+ "test('REQ-GRPH-010 renamed title', () => { assert.equal(runB(), 1); });\n"
82
+ );
83
+
84
+ assert.strictEqual(facts.assertionChanged, true, 'only A has an assertion change');
85
+ assert.ok(!facts.reqCitingTests.includes(TEST_B), 'a title-only edit is not a contributing test path');
86
+ assert.ok(!requirementSync.scope.paths.includes(TEST_B), 'title-only B is excluded from requirement-sync scope');
87
+ });
88
+
89
+ checkCase('R2 formatting-only unrelated REQ test edit stays out of changed A scope', () => {
90
+ const { facts, requirementSync } = deriveWithB(
91
+ '// REQ-GRPH-010\nassert.equal(runB(), 1);\n',
92
+ '// REQ-GRPH-010\nassert.equal( runB( ), 1 );\n'
93
+ );
94
+
95
+ assert.strictEqual(facts.assertionChanged, true, 'formatting in B does not add a second assertion change');
96
+ assert.ok(!facts.reqCitingTests.includes(TEST_B), 'formatting-only B is not a contributing test path');
97
+ assert.ok(!requirementSync.scope.paths.includes(TEST_B), 'formatting-only B is excluded from requirement-sync scope');
98
+ });
99
+
100
+ checkCase('R2 genuine B assertion edit joins A in requirement-sync scope', () => {
101
+ const { facts, requirementSync } = deriveWithB(
102
+ '// REQ-GRPH-010\nassert.equal(runB(), 1);\n',
103
+ '// REQ-GRPH-010\nassert.equal(runB(), 2);\n'
104
+ );
105
+
106
+ assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-010', 'REQ-GRPH-011'],
107
+ 'both changed assertions contribute their own requirement IDs');
108
+ assert.ok(facts.reqCitingTests.includes(TEST_A) && facts.reqCitingTests.includes(TEST_B),
109
+ 'both contributing test files remain in the path inventory');
110
+ assert.ok(requirementSync.scope.paths.includes(TEST_A) && requirementSync.scope.paths.includes(TEST_B),
111
+ `both changed test paths are scoped: ${requirementSync.scope.paths}`);
112
+ });
113
+
114
+ checkCase('R2 mixed known A assertion and unknown B attribution keeps B path and unknown scope', () => {
115
+ const { facts, requirementSync } = deriveWithB(
116
+ "test('REQ-GRPH-010 existing', () => { assert.equal(runB(), 1); });\n",
117
+ "test('REQ-GRPH-010 existing', () => { assert.equal(runB(), 2);\n"
118
+ );
119
+
120
+ assert.strictEqual(facts.requirementScopeUnknown, true, 'malformed B attribution remains explicitly unknown');
121
+ assert.strictEqual(requirementSync.applicability, 'unknown', 'known A evidence cannot close the mixed unknown scope');
122
+ assert.strictEqual(requirementSync.scope.requirementIds, 'unknown', 'the scope does not claim a known subset');
123
+ assert.ok(facts.reqCitingTests.includes(TEST_B), 'the unknown B path remains visible');
124
+ assert.ok(requirementSync.scope.paths.includes(TEST_B), `unknown B path remains in scope: ${requirementSync.scope.paths}`);
125
+ });
126
+
127
+ helper.finish();