harness-everything 0.34.0 → 0.36.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/ci.yml +2 -0
- package/AGENTS.md +3 -0
- package/CHANGELOG.md +42 -0
- package/ci/fixtures/doc-obligations/derive/acceptance-only/base/docs/prd/graph.md +22 -0
- package/ci/fixtures/doc-obligations/derive/acceptance-only/candidate/docs/prd/graph.md +22 -0
- package/ci/fixtures/doc-obligations/derive/alias-only/base/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/alias-only/candidate/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision/candidate/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/candidate/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/assertion-no-revision-attested/inventory.json +7 -0
- package/ci/fixtures/doc-obligations/derive/attribution-impossible/base/ci/mechanism-open.test.js +3 -0
- package/ci/fixtures/doc-obligations/derive/attribution-impossible/candidate/ci/mechanism-open.test.js.fixture +2 -0
- package/ci/fixtures/doc-obligations/derive/clause-context-typo/base/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/clause-context-typo/candidate/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/code-only/base/src/lint.js +1 -0
- package/ci/fixtures/doc-obligations/derive/code-only/candidate/src/lint.js +1 -0
- package/ci/fixtures/doc-obligations/derive/comment-only-test/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/comment-only-test/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/computed-member-assert/base/ci/mechanism-c1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/computed-member-assert/candidate/ci/mechanism-c1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/computed-member-variable/base/ci/mechanism-c1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/computed-member-variable/candidate/ci/mechanism-c1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/config.json +11 -0
- package/ci/fixtures/doc-obligations/derive/decision-fence-backtick/base/docs/adr/GRPH-010.md +20 -0
- package/ci/fixtures/doc-obligations/derive/decision-fence-backtick/candidate/docs/adr/GRPH-010.md +20 -0
- package/ci/fixtures/doc-obligations/derive/decision-fence-tilde/base/docs/adr/GRPH-010.md +20 -0
- package/ci/fixtures/doc-obligations/derive/decision-fence-tilde/candidate/docs/adr/GRPH-010.md +20 -0
- package/ci/fixtures/doc-obligations/derive/deep-strict-equal/base/ci/mechanism-s1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/deep-strict-equal/candidate/ci/mechanism-s1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/deleted-adr/base/docs/adr/GRPH-008.md +14 -0
- package/ci/fixtures/doc-obligations/derive/deleted-prd/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/deleted-prd/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/deleted-prd/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/derived-rename/base/docs/index.md +3 -0
- package/ci/fixtures/doc-obligations/derive/derived-rename/base/src/model/a.js +1 -0
- package/ci/fixtures/doc-obligations/derive/derived-rename/candidate/docs/index.md +3 -0
- package/ci/fixtures/doc-obligations/derive/derived-rename/candidate/src/model/b.js +1 -0
- package/ci/fixtures/doc-obligations/derive/derived-rename/config.json +7 -0
- package/ci/fixtures/doc-obligations/derive/derived-two-outputs/base/docs/index.md +3 -0
- package/ci/fixtures/doc-obligations/derive/derived-two-outputs/base/docs/model-summary.md +3 -0
- package/ci/fixtures/doc-obligations/derive/derived-two-outputs/base/src/model.js +1 -0
- package/ci/fixtures/doc-obligations/derive/derived-two-outputs/candidate/docs/index.md +3 -0
- package/ci/fixtures/doc-obligations/derive/derived-two-outputs/candidate/docs/model-summary.md +3 -0
- package/ci/fixtures/doc-obligations/derive/derived-two-outputs/candidate/src/model.js +1 -0
- package/ci/fixtures/doc-obligations/derive/derived-two-outputs/config.json +10 -0
- package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/base/docs/prd/graph-one/r001.md +19 -0
- package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/base/docs/prd/graph-two/r001.md +19 -0
- package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/candidate/docs/prd/graph-one/r001.md +19 -0
- package/ci/fixtures/doc-obligations/derive/distinct-logical-prds/candidate/docs/prd/graph-two/r001.md +19 -0
- package/ci/fixtures/doc-obligations/derive/docs-only/base/docs/reference/guide.md +3 -0
- package/ci/fixtures/doc-obligations/derive/docs-only/candidate/docs/reference/guide.md +3 -0
- package/ci/fixtures/doc-obligations/derive/dup-req-id/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/dup-req-id/base/docs/prd/graph-a.md +20 -0
- package/ci/fixtures/doc-obligations/derive/dup-req-id/base/docs/prd/graph-b.md +20 -0
- package/ci/fixtures/doc-obligations/derive/dup-req-id/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/dup-req-id/candidate/docs/prd/graph-a.md +20 -0
- package/ci/fixtures/doc-obligations/derive/dup-req-id/candidate/docs/prd/graph-b.md +20 -0
- package/ci/fixtures/doc-obligations/derive/expect-computed-literal/base/ci/mechanism-e.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/expect-computed-literal/candidate/ci/mechanism-e.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/expect-computed-matcher/base/ci/mechanism-e.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/expect-computed-matcher/candidate/ci/mechanism-e.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/expect-not-to-equal/base/ci/mechanism-s1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/expect-not-to-equal/candidate/ci/mechanism-s1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/formatting-only-test/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/formatting-only-test/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/helper-delegated-test/base/docs/prd/graph.md +18 -0
- package/ci/fixtures/doc-obligations/derive/helper-delegated-test/candidate/ci/mechanism-helper.test.js +1 -0
- package/ci/fixtures/doc-obligations/derive/helper-delegated-test/candidate/docs/prd/graph.md +18 -0
- package/ci/fixtures/doc-obligations/derive/historical-only-req/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/historical-only-req/base/docs/prd/graph/r001.md +25 -0
- package/ci/fixtures/doc-obligations/derive/historical-only-req/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/historical-only-req/candidate/docs/prd/graph/r001.md +25 -0
- package/ci/fixtures/doc-obligations/derive/historical-only-req/candidate/docs/prd/graph/r002.md +23 -0
- package/ci/fixtures/doc-obligations/derive/in-place-current/base/ci/mechanism-inplace.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/in-place-current/base/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/in-place-current/base/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-obligations/derive/in-place-current/candidate/ci/mechanism-inplace.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/in-place-current/candidate/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/in-place-current/candidate/docs/prd/graph/r002.md +28 -0
- package/ci/fixtures/doc-obligations/derive/in-place-historical/base/ci/mechanism-inplace.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/in-place-historical/base/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/in-place-historical/base/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-obligations/derive/in-place-historical/candidate/ci/mechanism-inplace.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/in-place-historical/candidate/docs/prd/graph/r001.md +22 -0
- package/ci/fixtures/doc-obligations/derive/in-place-historical/candidate/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-obligations/derive/invalid-clause/base/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/invalid-clause/candidate/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/legacy-untagged-fallback/base/ci/mechanism-legacy.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/legacy-untagged-fallback/candidate/ci/mechanism-legacy.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/literal-whitespace-assert/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/literal-whitespace-assert/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/malformed-citation-attribution/base/ci/mechanism-bad.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/malformed-citation-attribution/candidate/ci/mechanism-bad.test.js.fixture +6 -0
- package/ci/fixtures/doc-obligations/derive/malformed-prd/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/malformed-prd/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/malformed-prd/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/malformed-prd/candidate/docs/prd/graph.md +11 -0
- package/ci/fixtures/doc-obligations/derive/missing-approval/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/missing-approval/candidate/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/mixed-attribution/base/ci/mechanism-mixed.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/mixed-attribution/base/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/mixed-attribution/candidate/ci/mechanism-mixed.test.js.fixture +5 -0
- package/ci/fixtures/doc-obligations/derive/mixed-attribution/candidate/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/mixed-docs-code/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/mixed-docs-code/base/docs/reference/guide.md +3 -0
- package/ci/fixtures/doc-obligations/derive/mixed-docs-code/base/src/lint.js +1 -0
- package/ci/fixtures/doc-obligations/derive/mixed-docs-code/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/mixed-docs-code/candidate/docs/reference/guide.md +3 -0
- package/ci/fixtures/doc-obligations/derive/mixed-docs-code/candidate/src/lint.js +1 -0
- package/ci/fixtures/doc-obligations/derive/multiline-comment-only/base/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/multiline-comment-only/candidate/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/multiline-expect/base/ci/mechanism-multi.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/multiline-expect/candidate/ci/mechanism-multi.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/multiline-formatting-only/base/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/multiline-formatting-only/candidate/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/base/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/base/docs/prd/graph.md +18 -0
- package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/candidate/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/multiline-numeric-prd/candidate/docs/prd/graph.md +18 -0
- package/ci/fixtures/doc-obligations/derive/multiline-strictequal/base/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/multiline-strictequal/candidate/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/new-prd/candidate/docs/prd/graph-new.md +20 -0
- package/ci/fixtures/doc-obligations/derive/new-reference/candidate/docs/reference/new-note.md +3 -0
- package/ci/fixtures/doc-obligations/derive/new-req-test/candidate/ci/mechanism-new.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/orphan-req-test/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/orphan-req-test/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/orphan-req-test/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/orphan-req-test/candidate/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/partial-revision/base/ci/mechanism-partial.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/partial-revision/base/docs/prd/graph/r001.md +24 -0
- package/ci/fixtures/doc-obligations/derive/partial-revision/candidate/ci/mechanism-partial.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/partial-revision/candidate/docs/prd/graph/r001.md +24 -0
- package/ci/fixtures/doc-obligations/derive/partial-revision/candidate/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-obligations/derive/regex-literal-whitespace/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/regex-literal-whitespace/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/rename/base/docs/reference/old-guide.md +3 -0
- package/ci/fixtures/doc-obligations/derive/rename/candidate/docs/reference/guide.md +3 -0
- package/ci/fixtures/doc-obligations/derive/replacement-target/base/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/replacement-target/candidate/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/req-context-typo/base/docs/prd/graph.md +26 -0
- package/ci/fixtures/doc-obligations/derive/req-context-typo/candidate/docs/prd/graph.md +26 -0
- package/ci/fixtures/doc-obligations/derive/req-revision/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/req-revision/base/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/req-revision/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/req-revision/candidate/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/req-revision/candidate/docs/prd/graph/r002.md +28 -0
- package/ci/fixtures/doc-obligations/derive/req-revision-in-place/base/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/req-revision-in-place/candidate/docs/prd/graph/r001.md +22 -0
- package/ci/fixtures/doc-obligations/derive/retained-revision-chain/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/retained-revision-chain/base/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/retained-revision-chain/base/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-obligations/derive/retained-revision-chain/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/retained-revision-chain/candidate/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-obligations/derive/retained-revision-chain/candidate/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-obligations/derive/string-literal-tag/base/ci/mechanism-str.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/string-literal-tag/candidate/ci/mechanism-str.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/tag-inside-test-call/base/ci/mechanism-inside.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/tag-inside-test-call/candidate/ci/mechanism-inside.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/tagged-test/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/tagged-test/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/tagged-test-prd/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/tagged-test-prd/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/tagged-test-prd/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/tagged-test-prd/candidate/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/template-literal-tag/base/ci/mechanism-tpl.test.js +4 -0
- package/ci/fixtures/doc-obligations/derive/template-literal-tag/base/docs/prd/graph.md +18 -0
- package/ci/fixtures/doc-obligations/derive/template-literal-tag/candidate/ci/mechanism-tpl.test.js +4 -0
- package/ci/fixtures/doc-obligations/derive/template-literal-tag/candidate/docs/prd/graph.md +18 -0
- package/ci/fixtures/doc-obligations/derive/template-literal-whitespace/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/template-literal-whitespace/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/title-only-rename/base/ci/mechanism-title.test.js +3 -0
- package/ci/fixtures/doc-obligations/derive/title-only-rename/candidate/ci/mechanism-title.test.js +3 -0
- package/ci/fixtures/doc-obligations/derive/title-only-unrelated-computed/base/ci/mechanism-u.test.js +4 -0
- package/ci/fixtures/doc-obligations/derive/title-only-unrelated-computed/candidate/ci/mechanism-u.test.js +4 -0
- package/ci/fixtures/doc-obligations/derive/top-level-no-file-tag/base/ci/mechanism-top.test.js +4 -0
- package/ci/fixtures/doc-obligations/derive/top-level-no-file-tag/candidate/ci/mechanism-top.test.js +4 -0
- package/ci/fixtures/doc-obligations/derive/trailing-comment-test/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/trailing-comment-test/base/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/trailing-comment-test/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/trailing-comment-test/candidate/docs/prd/graph.md +20 -0
- package/ci/fixtures/doc-obligations/derive/two-tests-one-file/base/ci/mechanism-two.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/two-tests-one-file/base/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/two-tests-one-file/candidate/ci/mechanism-two.test.js +6 -0
- package/ci/fixtures/doc-obligations/derive/two-tests-one-file/candidate/docs/prd/graph.md +24 -0
- package/ci/fixtures/doc-obligations/derive/typo-context/base/docs/adr/GRPH-007.md +14 -0
- package/ci/fixtures/doc-obligations/derive/typo-context/candidate/docs/adr/GRPH-007.md +14 -0
- package/ci/fixtures/doc-obligations/derive/typo-decision/base/docs/adr/GRPH-007.md +14 -0
- package/ci/fixtures/doc-obligations/derive/typo-decision/candidate/docs/adr/GRPH-007.md +14 -0
- package/ci/fixtures/doc-obligations/derive/unbalanced-statement/base/ci/mechanism-multi.test.js +5 -0
- package/ci/fixtures/doc-obligations/derive/unbalanced-statement/candidate/ci/mechanism-multi.test.js.fixture +4 -0
- package/ci/fixtures/doc-obligations/derive/unknown-adr/base/docs/adr/GRPH-009.md +14 -0
- package/ci/fixtures/doc-obligations/derive/unknown-adr/candidate/docs/adr/GRPH-009.md +12 -0
- package/ci/fixtures/doc-obligations/derive/unsupported-assertion-syntax/base/ci/mechanism-s1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/unsupported-assertion-syntax/candidate/ci/mechanism-s1.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/whitespace-only-test/base/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/derive/whitespace-only-test/candidate/ci/mechanism-lint.test.js +2 -0
- package/ci/fixtures/doc-obligations/evidence/base/docs/adr/GRPH-007.md +15 -0
- package/ci/fixtures/doc-obligations/evidence/base/docs/prd/PRD-GRPH-001.md +25 -0
- package/ci/fixtures/doc-obligations/evidence/candidate/docs/adr/GRPH-007.md +15 -0
- package/ci/fixtures/doc-obligations/evidence/candidate/docs/prd/PRD-GRPH-001.md +25 -0
- package/ci/mechanism-325-3a-cross.test.js +795 -0
- package/ci/mechanism-325-3a-doc-evidence.test.js +1172 -0
- package/ci/mechanism-325-3a-doc-obligations.test.js +742 -0
- package/ci/mechanism-36-verify-timeout.test.js +534 -0
- package/ci/mechanism-37-portability-lint.test.js +71 -0
- package/ci/mechanism-38-verify-all.test.js +112 -0
- package/ci/mechanism-39-opencode-verify-outcomes.test.js +374 -0
- package/ci/mechanism-pr334-haiku.test.js +298 -0
- package/ci/mechanism-pr334-luna.test.js +192 -0
- package/ci/mechanism-pr334-opencode.test.js +313 -0
- package/ci/mechanism-pr334-r2-boundary.test.js +239 -0
- package/ci/mechanism-pr334-r2-resolution.test.js +243 -0
- package/ci/mechanism-pr334-r2-scope.test.js +127 -0
- package/ci/mechanism-pr334-r3-boundary.test.js +356 -0
- package/ci/mechanism-pr334-r3-citations.test.js +205 -0
- package/ci/mechanism-pr334-r3-headings.test.js +268 -0
- package/ci/portability-lint.js +288 -0
- package/docs/document-lifecycle.md +57 -1
- package/docs/repository-contract.md +1 -1
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/doc-evidence.js +471 -0
- package/harness-everything/scripts/doc-lifecycle.js +1 -1
- package/harness-everything/scripts/doc-obligations.js +1571 -0
- package/opencode-plugin/README.md +33 -2
- package/opencode-plugin/hooks/verify.js +87 -11
- package/opencode-plugin/index.mjs +216 -25
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +3 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-evidence.js +471 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-obligations.js +1571 -0
- package/sbom.cdx.json +7 -7
- package/scripts/verify-all.js +314 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// PR #334 finding 4305942767: a no-REQ-change waiver for one cited REQ must not waive an unrelated
|
|
4
|
+
// cited REQ. These controls exercise the per-REQ requirementIds coverage in doc-evidence.js directly,
|
|
5
|
+
// with inline inputs, and do not touch the shared phase 3a suites.
|
|
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 { checkDocObligations } = require(path.join(__dirname, '..', 'harness-everything', 'scripts', 'doc-evidence.js'));
|
|
13
|
+
|
|
14
|
+
console.log('\n[pr334-haiku] per-REQ waiver coverage (finding 4305942767)...');
|
|
15
|
+
|
|
16
|
+
const OBLIGATION_ID = 'doc-change:requirement-sync';
|
|
17
|
+
const PRD = 'docs/prd/PRD-PR334-001.md';
|
|
18
|
+
const TEST = 'ci/pr334-waiver-fixture.test.js';
|
|
19
|
+
const REQ_A = 'REQ-PR334-001';
|
|
20
|
+
const REQ_B = 'REQ-PR334-002';
|
|
21
|
+
|
|
22
|
+
function sha256(text) {
|
|
23
|
+
return crypto.createHash('sha256').update(text).digest('hex');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const BASE_DIGEST = sha256('baseline tree before this change');
|
|
27
|
+
const TEST_DIGEST = sha256('candidate test assertions');
|
|
28
|
+
const STALE_TEST_DIGEST = sha256('earlier test assertions');
|
|
29
|
+
const PRD_DIGEST = sha256('candidate PRD with REQ-PR334-001 revised and REQ-PR334-002 unchanged');
|
|
30
|
+
// Mirrors prdSetDigest in doc-evidence.js: sha256 over sorted [path, digest] pairs under docs/prd/.
|
|
31
|
+
const PRD_SET_DIGEST = sha256(JSON.stringify([[PRD, PRD_DIGEST]]));
|
|
32
|
+
|
|
33
|
+
const ARTIFACTS = Object.freeze({ [TEST]: { digest: TEST_DIGEST }, [PRD]: { digest: PRD_DIGEST } });
|
|
34
|
+
const BASELINE = Object.freeze({ digest: BASE_DIGEST });
|
|
35
|
+
|
|
36
|
+
function requirementSync(overrides) {
|
|
37
|
+
return {
|
|
38
|
+
id: OBLIGATION_ID,
|
|
39
|
+
capability: 'requirement-sync',
|
|
40
|
+
strength: 'MUST',
|
|
41
|
+
applicability: 'applicable',
|
|
42
|
+
scope: { paths: [TEST, PRD] },
|
|
43
|
+
acceptance: 'Evaluate whether affected requirements need an update or supported no-change record.',
|
|
44
|
+
assertionChanged: true,
|
|
45
|
+
requirementSyncTrigger: 'assertion-change',
|
|
46
|
+
inventoryComplete: true,
|
|
47
|
+
inventory: {
|
|
48
|
+
complete: true,
|
|
49
|
+
waiverInventoryComplete: true,
|
|
50
|
+
baseDigest: BASE_DIGEST,
|
|
51
|
+
candidateDigest: PRD_SET_DIGEST,
|
|
52
|
+
configDigest: sha256('shared derive config')
|
|
53
|
+
},
|
|
54
|
+
citedReqs: [
|
|
55
|
+
{ id: REQ_A, file: PRD, resolution: 'resolved' },
|
|
56
|
+
{ id: REQ_B, file: PRD, resolution: 'resolved' }
|
|
57
|
+
],
|
|
58
|
+
reqUpdates: [],
|
|
59
|
+
...(overrides || {})
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function execution(id, overrides) {
|
|
64
|
+
return {
|
|
65
|
+
id: id || 'exec-req',
|
|
66
|
+
obligationId: OBLIGATION_ID,
|
|
67
|
+
command: 'node ci/pr334-waiver-fixture.test.js',
|
|
68
|
+
exitCode: 0,
|
|
69
|
+
paths: [TEST, PRD],
|
|
70
|
+
artifactDigests: { [TEST]: TEST_DIGEST, [PRD]: PRD_DIGEST },
|
|
71
|
+
baseDigest: BASE_DIGEST,
|
|
72
|
+
provenance: 'execution record bound to the current candidate and baseline',
|
|
73
|
+
...(overrides || {})
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function waiver(id, requirementIds, overrides) {
|
|
78
|
+
return {
|
|
79
|
+
id,
|
|
80
|
+
obligationId: OBLIGATION_ID,
|
|
81
|
+
kind: 'waiver',
|
|
82
|
+
verdict: 'approve',
|
|
83
|
+
summary: 'no-req-change',
|
|
84
|
+
reason: 'no-req-change: acceptance criteria unchanged, cited passing test',
|
|
85
|
+
requirementIds,
|
|
86
|
+
citedRecordIds: ['exec-req'],
|
|
87
|
+
paths: [TEST, PRD],
|
|
88
|
+
artifactDigests: { [TEST]: TEST_DIGEST, [PRD]: PRD_DIGEST },
|
|
89
|
+
baseDigest: BASE_DIGEST,
|
|
90
|
+
provenance: 'waiver record bound to the current candidate and baseline',
|
|
91
|
+
...(overrides || {})
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function run(obligation, { reviewRecords = [], executionRecords = [execution('exec-req')] } = {}) {
|
|
96
|
+
return checkDocObligations({
|
|
97
|
+
obligations: [obligation],
|
|
98
|
+
artifacts: ARTIFACTS,
|
|
99
|
+
baseline: BASELINE,
|
|
100
|
+
executionRecords,
|
|
101
|
+
reviewRecords
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function resultOf(output) {
|
|
106
|
+
return output.results.find(item => item.obligationId === OBLIGATION_ID);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function unmetDiagnostic(output) {
|
|
110
|
+
return output.diagnostics.find(item => item.code === 'req-sync-no-revision-or-waiver');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function unsupportedWaiver(output, waiverId) {
|
|
114
|
+
return output.diagnostics.find(item => item.code === 'unsupported-waiver' && JSON.stringify(item).includes(waiverId));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const REVISED_A = [{ id: REQ_A, file: PRD }];
|
|
118
|
+
const REVISED_B = [{ id: REQ_B, file: PRD }];
|
|
119
|
+
const REVISED_BOTH = [{ id: REQ_A, file: PRD }, { id: REQ_B, file: PRD }];
|
|
120
|
+
|
|
121
|
+
function checkCase(name, body) {
|
|
122
|
+
try {
|
|
123
|
+
body();
|
|
124
|
+
helper.check(name, true);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
helper.check(name, false, error && error.message ? error.message : String(error));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Negative controls: a waiver never covers a REQ it does not list, and malformed or stale coverage never passes.
|
|
131
|
+
|
|
132
|
+
checkCase('pr334-haiku A-only waiver with A revised and B unrevised: fail naming REQ-PR334-002 only', () => {
|
|
133
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
134
|
+
reviewRecords: [waiver('waiver-a', [REQ_A])]
|
|
135
|
+
});
|
|
136
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'the A-only waiver cannot cover unrelated B');
|
|
137
|
+
const diag = unmetDiagnostic(output);
|
|
138
|
+
assert.ok(diag && JSON.stringify(diag).includes(REQ_B), 'B is named as unmet');
|
|
139
|
+
assert.ok(diag && !JSON.stringify(diag).includes(REQ_A), 'the revised A is not named');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
checkCase('pr334-haiku A-only waiver with neither REQ revised: fail naming REQ-PR334-002 only', () => {
|
|
143
|
+
const output = run(requirementSync({ reqUpdates: [] }), {
|
|
144
|
+
reviewRecords: [waiver('waiver-a', [REQ_A])]
|
|
145
|
+
});
|
|
146
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'A is covered by its waiver, B is not');
|
|
147
|
+
const diag = unmetDiagnostic(output);
|
|
148
|
+
assert.ok(diag && JSON.stringify(diag).includes(REQ_B), 'B is named as unmet');
|
|
149
|
+
assert.ok(diag && !JSON.stringify(diag).includes(REQ_A), 'the waived A is not named');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
checkCase('pr334-haiku B-only waiver with A unrevised: fail naming REQ-PR334-001 only (mirror of A-only)', () => {
|
|
153
|
+
const output = run(requirementSync({ reqUpdates: [] }), {
|
|
154
|
+
reviewRecords: [waiver('waiver-b', [REQ_B])]
|
|
155
|
+
});
|
|
156
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'the B-only waiver cannot cover unrelated A');
|
|
157
|
+
const diag = unmetDiagnostic(output);
|
|
158
|
+
assert.ok(diag && JSON.stringify(diag).includes(REQ_A), 'A is named as unmet');
|
|
159
|
+
assert.ok(diag && !JSON.stringify(diag).includes(REQ_B), 'the waived B is not named');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
checkCase('pr334-haiku B-only waiver and legal A revision but no execution evidence: not pass, B unsupported and named', () => {
|
|
163
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
164
|
+
reviewRecords: [waiver('waiver-b', [REQ_B])],
|
|
165
|
+
executionRecords: []
|
|
166
|
+
});
|
|
167
|
+
assert.notStrictEqual(resultOf(output).outcome, 'pass', 'a waiver and a revision cannot pass without observed execution');
|
|
168
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'the waiver cites no observed record, so B is an attested absence');
|
|
169
|
+
assert.ok(unsupportedWaiver(output, 'waiver-b'), 'the waiver citing no observed record is diagnosed by id');
|
|
170
|
+
assert.ok(unmetDiagnostic(output) && JSON.stringify(unmetDiagnostic(output)).includes(REQ_B), 'B is named as unmet');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
checkCase('pr334-haiku B-only waiver with stale baseDigest: not a valid contributor, fail naming REQ-PR334-002', () => {
|
|
174
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
175
|
+
reviewRecords: [waiver('waiver-b-stale', [REQ_B], { baseDigest: sha256('older baseline') })]
|
|
176
|
+
});
|
|
177
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'a stale-baseline waiver covers nothing');
|
|
178
|
+
assert.ok(unsupportedWaiver(output, 'waiver-b-stale'), 'the stale waiver is diagnosed by id');
|
|
179
|
+
assert.ok(unmetDiagnostic(output) && JSON.stringify(unmetDiagnostic(output)).includes(REQ_B), 'B stays unmet');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
checkCase('pr334-haiku B-only waiver bound to a stale test digest: not a valid contributor, fail naming REQ-PR334-002', () => {
|
|
183
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
184
|
+
reviewRecords: [waiver('waiver-b-digest', [REQ_B], {
|
|
185
|
+
artifactDigests: { [TEST]: STALE_TEST_DIGEST, [PRD]: PRD_DIGEST }
|
|
186
|
+
})]
|
|
187
|
+
});
|
|
188
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'a waiver with a stale artifact digest covers nothing');
|
|
189
|
+
assert.ok(unsupportedWaiver(output, 'waiver-b-digest'), 'the stale-digest waiver is diagnosed by id');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
checkCase('pr334-haiku B-only waiver citing a stale execution record: not supported, fail naming REQ-PR334-002', () => {
|
|
193
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
194
|
+
reviewRecords: [waiver('waiver-b-exec', [REQ_B])],
|
|
195
|
+
executionRecords: [execution('exec-req', { artifactDigests: { [TEST]: STALE_TEST_DIGEST, [PRD]: PRD_DIGEST } })]
|
|
196
|
+
});
|
|
197
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'a waiver citing stale execution does not cover B');
|
|
198
|
+
assert.ok(unsupportedWaiver(output, 'waiver-b-exec'), 'the waiver citing stale execution is diagnosed by id');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
checkCase('pr334-haiku empty requirementIds on an otherwise valid waiver: covers nothing, fail naming unrevised B', () => {
|
|
202
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
203
|
+
reviewRecords: [waiver('waiver-empty-ids', [])]
|
|
204
|
+
});
|
|
205
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'an empty requirementIds list covers no REQ');
|
|
206
|
+
assert.ok(unsupportedWaiver(output, 'waiver-empty-ids'), 'the waiver is diagnosed by id');
|
|
207
|
+
assert.ok(unmetDiagnostic(output) && JSON.stringify(unmetDiagnostic(output)).includes(REQ_B), 'B stays unmet');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
checkCase('pr334-haiku requirementIds given as a bare string: not coerced into coverage, fail naming REQ-PR334-002', () => {
|
|
211
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
212
|
+
reviewRecords: [waiver('waiver-string-ids', REQ_B)]
|
|
213
|
+
});
|
|
214
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'a string is not a requirementIds list');
|
|
215
|
+
assert.ok(unsupportedWaiver(output, 'waiver-string-ids'), 'the malformed waiver is diagnosed by id');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
checkCase('pr334-haiku malformed requirementIds members (null, number, object, empty string): covers nothing, fail naming REQ-PR334-002', () => {
|
|
219
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
220
|
+
reviewRecords: [waiver('waiver-junk-ids', [null, 7, { id: REQ_B }, ''])]
|
|
221
|
+
});
|
|
222
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'non-string members never match a cited REQ id');
|
|
223
|
+
assert.ok(unmetDiagnostic(output) && JSON.stringify(unmetDiagnostic(output)).includes(REQ_B), 'B stays unmet');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
checkCase('pr334-haiku lookalike requirementIds (superstring and prefix of REQ-PR334-002) do not cover B: fail naming REQ-PR334-002', () => {
|
|
227
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
228
|
+
reviewRecords: [waiver('waiver-lookalike', ['REQ-PR334-0020', 'REQ-PR334-00'])]
|
|
229
|
+
});
|
|
230
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'ids are matched exactly, not by substring or prefix');
|
|
231
|
+
assert.ok(unmetDiagnostic(output) && JSON.stringify(unmetDiagnostic(output)).includes(REQ_B), 'B stays unmet');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
checkCase('pr334-haiku B-only waiver with an empty citedRecordIds list: waiver cites no evidence, fail naming REQ-PR334-002', () => {
|
|
235
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
236
|
+
reviewRecords: [waiver('waiver-no-cites', [REQ_B], { citedRecordIds: [] })]
|
|
237
|
+
});
|
|
238
|
+
assert.strictEqual(resultOf(output).outcome, 'fail', 'a waiver with no cited execution covers nothing');
|
|
239
|
+
assert.ok(unsupportedWaiver(output, 'waiver-no-cites'), 'the waiver is diagnosed by id');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
checkCase('pr334-haiku B-only waiver with absent reqUpdates: unknown update data, none, not pass', () => {
|
|
243
|
+
const output = run(requirementSync({ reqUpdates: undefined }), {
|
|
244
|
+
reviewRecords: [waiver('waiver-b-unknown', [REQ_B])]
|
|
245
|
+
});
|
|
246
|
+
assert.notStrictEqual(resultOf(output).outcome, 'pass', 'unknown update data never passes');
|
|
247
|
+
assert.strictEqual(resultOf(output).outcome, 'none', 'absent update data gives no absence fail either');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Positive controls: each cited REQ is covered by its own revision or its own listing waiver.
|
|
251
|
+
|
|
252
|
+
checkCase('pr334-haiku B-only waiver with legal A revision and matching evidence: pass, waiver cited', () => {
|
|
253
|
+
const output = run(requirementSync({ reqUpdates: REVISED_A }), {
|
|
254
|
+
reviewRecords: [waiver('waiver-b', [REQ_B])]
|
|
255
|
+
});
|
|
256
|
+
const result = resultOf(output);
|
|
257
|
+
assert.strictEqual(result.outcome, 'pass', 'the waiver lists the one unrevised REQ and A is revised');
|
|
258
|
+
assert.ok(result.recordRefs.includes('waiver-b'), 'the pass cites the waiver that covered B');
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
checkCase('pr334-haiku A-only waiver with legal B revision and matching evidence: pass (symmetric control)', () => {
|
|
262
|
+
const output = run(requirementSync({ reqUpdates: REVISED_B }), {
|
|
263
|
+
reviewRecords: [waiver('waiver-a', [REQ_A])]
|
|
264
|
+
});
|
|
265
|
+
assert.strictEqual(resultOf(output).outcome, 'pass', 'the waiver lists the one unrevised REQ and B is revised');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
checkCase('pr334-haiku both cited REQs revised, no waiver, matching evidence: pass', () => {
|
|
269
|
+
const output = run(requirementSync({ reqUpdates: REVISED_BOTH }));
|
|
270
|
+
assert.strictEqual(resultOf(output).outcome, 'pass', 'revisions cover every cited REQ without any waiver');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
checkCase('pr334-haiku separate single-id waivers for A and B, no revisions, matching evidence: pass', () => {
|
|
274
|
+
const output = run(requirementSync({ reqUpdates: [] }), {
|
|
275
|
+
reviewRecords: [waiver('waiver-a', [REQ_A]), waiver('waiver-b', [REQ_B])]
|
|
276
|
+
});
|
|
277
|
+
const result = resultOf(output);
|
|
278
|
+
assert.strictEqual(result.outcome, 'pass', 'each cited REQ is covered by a waiver listing it');
|
|
279
|
+
assert.ok(result.recordRefs.includes('waiver-a') && result.recordRefs.includes('waiver-b'),
|
|
280
|
+
'both covering waivers are cited');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
checkCase('pr334-haiku one multi-id waiver for A and B, no revisions, matching evidence: pass', () => {
|
|
284
|
+
const output = run(requirementSync({ reqUpdates: [] }), {
|
|
285
|
+
reviewRecords: [waiver('waiver-both', [REQ_A, REQ_B])]
|
|
286
|
+
});
|
|
287
|
+
assert.strictEqual(resultOf(output).outcome, 'pass', 'a waiver that lists both cited REQs covers both');
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
checkCase('pr334-haiku both REQs revised while a stale waiver is present: pass, stale waiver diagnosed and ignored', () => {
|
|
291
|
+
const output = run(requirementSync({ reqUpdates: REVISED_BOTH }), {
|
|
292
|
+
reviewRecords: [waiver('waiver-stale', [REQ_B], { baseDigest: sha256('older baseline') })]
|
|
293
|
+
});
|
|
294
|
+
assert.strictEqual(resultOf(output).outcome, 'pass', 'revisions do not depend on the stale waiver');
|
|
295
|
+
assert.ok(unsupportedWaiver(output, 'waiver-stale'), 'the stale waiver is still diagnosed');
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
helper.finish();
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const helper = require('./test-helper');
|
|
6
|
+
|
|
7
|
+
const { deriveDocFacts, deriveDocObligations } = require(path.join(
|
|
8
|
+
__dirname,
|
|
9
|
+
'..',
|
|
10
|
+
'harness-everything',
|
|
11
|
+
'scripts',
|
|
12
|
+
'doc-obligations.js'
|
|
13
|
+
));
|
|
14
|
+
|
|
15
|
+
const TEST_PATH = 'ci/pr334-identity.test.js';
|
|
16
|
+
|
|
17
|
+
function deriveSnapshots(baseFiles, candidateFiles) {
|
|
18
|
+
const result = deriveDocFacts({
|
|
19
|
+
base: { files: baseFiles },
|
|
20
|
+
candidate: { files: candidateFiles },
|
|
21
|
+
config: {}
|
|
22
|
+
});
|
|
23
|
+
const requirementSync = deriveDocObligations(result.facts)
|
|
24
|
+
.find(item => item.capability === 'requirement-sync');
|
|
25
|
+
return { facts: result.facts, requirementSync };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function derive(beforeText, afterText) {
|
|
29
|
+
return deriveSnapshots({ [TEST_PATH]: beforeText }, { [TEST_PATH]: afterText });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function checkCase(name, run) {
|
|
33
|
+
try {
|
|
34
|
+
run();
|
|
35
|
+
helper.check(name, true);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
helper.check(name, false, error && error.message ? error.message : String(error));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
checkCase('PR334 title-only REQ test rename leaves assertion range unchanged and requirement-sync N/A', () => {
|
|
42
|
+
const { facts, requirementSync } = derive(
|
|
43
|
+
"test('REQ-GRPH-001 old title', () => { assert.equal(run(), 1); });\n",
|
|
44
|
+
"test('REQ-GRPH-001 renamed title', () => { assert.equal(run(), 1); });\n"
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
assert.strictEqual(facts.assertionChanged, false, 'changed title tokens are outside the assertion call');
|
|
48
|
+
assert.strictEqual(facts.requirementSyncTrigger, null, 'a title rename is not a new test or assertion edit');
|
|
49
|
+
assert.deepStrictEqual(facts.citedReqs, [], 'a title-only edit contributes no changed assertion citation');
|
|
50
|
+
assert.strictEqual(requirementSync.applicability, 'N/A', 'the title-only rename is not a sync candidate');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
checkCase('PR334 changing a REQ ID in an existing test title keeps the new citation and marks identity unknown', () => {
|
|
54
|
+
const { facts, requirementSync } = derive(
|
|
55
|
+
"test('REQ-GRPH-001 unchanged title', () => { verify(); });\n",
|
|
56
|
+
"test('REQ-GRPH-002 unchanged title', () => { verify(); });\n"
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
assert.strictEqual(facts.requirementSyncTrigger, null, 'the test body and inventory are unchanged');
|
|
60
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-002'], 'the candidate citation is retained');
|
|
61
|
+
assert.strictEqual(requirementSync.requirementScopeUnknown, true, 'body sameness does not settle changed citation identity');
|
|
62
|
+
assert.strictEqual(requirementSync.applicability, 'unknown', 'changed citation identity is never treated as N/A');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
checkCase('PR334 adding a REQ ID only to an existing test title keeps the new citation and marks identity unknown', () => {
|
|
66
|
+
const { facts, requirementSync } = derive(
|
|
67
|
+
"test('unchanged title', () => { verify(); });\n",
|
|
68
|
+
"test('REQ-GRPH-001 unchanged title', () => { verify(); });\n"
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
assert.strictEqual(facts.requirementSyncTrigger, null, 'a title-only citation is not a new test');
|
|
72
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-001'], 'the candidate citation is retained');
|
|
73
|
+
assert.strictEqual(requirementSync.requirementScopeUnknown, true, 'body sameness does not settle changed citation identity');
|
|
74
|
+
assert.strictEqual(requirementSync.applicability, 'unknown', 'changed citation identity is never treated as N/A');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
checkCase('PR334 title rename plus a different REQ test assertion edit stays assertion-change and scopes only the edited assertion', () => {
|
|
78
|
+
const { facts, requirementSync } = derive(
|
|
79
|
+
[
|
|
80
|
+
"test('REQ-GRPH-001 stable assertion', () => { assert.equal(runA(), 1); });",
|
|
81
|
+
"test('REQ-GRPH-002 edited assertion', () => { assert.equal(runB(), 2); });"
|
|
82
|
+
].join('\n'),
|
|
83
|
+
[
|
|
84
|
+
"test('REQ-GRPH-001 renamed assertion title', () => { assert.equal(runA(), 1); });",
|
|
85
|
+
"test('REQ-GRPH-002 edited assertion', () => { assert.equal(runB(), 3); });"
|
|
86
|
+
].join('\n')
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
assert.strictEqual(facts.assertionChanged, true, 'the changed numeric argument is inside REQ-GRPH-002 assertion');
|
|
90
|
+
assert.strictEqual(facts.requirementSyncTrigger, 'assertion-change', 'an existing test body edit is not a new test');
|
|
91
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-002'],
|
|
92
|
+
'the title-only REQ-GRPH-001 rename does not enter the changed assertion scope');
|
|
93
|
+
assert.strictEqual(requirementSync.applicability, 'applicable', 'the changed assertion triggers requirement-sync');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
checkCase('PR334 title and assertion edits in one existing REQ test do not become a new-test signal', () => {
|
|
97
|
+
const { facts, requirementSync } = derive(
|
|
98
|
+
"test('REQ-GRPH-001 old title', () => { assert.equal(run(), 1); });\n",
|
|
99
|
+
"test('REQ-GRPH-001 renamed title', () => { assert.equal(run(), 2); });\n"
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
assert.strictEqual(facts.assertionChanged, true, 'the assertion argument changed');
|
|
103
|
+
assert.strictEqual(facts.requirementSyncTrigger, 'assertion-change', 'an existing requirement call remains the same test');
|
|
104
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-001'],
|
|
105
|
+
'the changed assertion retains its own requirement citation');
|
|
106
|
+
assert.strictEqual(requirementSync.requirementScopeUnknown, true, 'simultaneous title and body edits make test identity ambiguous');
|
|
107
|
+
assert.strictEqual(requirementSync.applicability, 'unknown', 'ambiguous identity cannot be treated as a fully known scope');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
checkCase('PR334 a same-REQ call replacement with no stable identity is unknown, never silently N/A', () => {
|
|
111
|
+
const { facts, requirementSync } = derive(
|
|
112
|
+
"test('REQ-GRPH-001 old check', () => { verifyOldBehavior(); });\n",
|
|
113
|
+
"test('REQ-GRPH-001 replacement check', () => { verifyNewBehavior(); });\n"
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
assert.strictEqual(facts.requirementSyncTrigger, null, 'the snapshots cannot prove whether this is a new test or a wholesale edit');
|
|
117
|
+
assert.strictEqual(requirementSync.requirementScopeUnknown, true, 'ambiguous test identity remains explicit');
|
|
118
|
+
assert.strictEqual(requirementSync.applicability, 'unknown', 'ambiguous identity is never downgraded to N/A');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
checkCase('PR334 added REQ-citing test beside an unchanged one remains a new-test control', () => {
|
|
122
|
+
const { facts, requirementSync } = derive(
|
|
123
|
+
"test('REQ-GRPH-001 existing', () => { verifyA(); });\n",
|
|
124
|
+
[
|
|
125
|
+
"test('REQ-GRPH-001 existing', () => { verifyA(); });",
|
|
126
|
+
"test('REQ-GRPH-002 added', verifyB);"
|
|
127
|
+
].join('\n')
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
assert.strictEqual(facts.assertionChanged, false, 'the helper-delegated addition has no assertion-body edit');
|
|
131
|
+
assert.strictEqual(facts.requirementSyncTrigger, 'new-test', 'the added REQ-citing test is a new-test signal');
|
|
132
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-002'],
|
|
133
|
+
'the new test contributes its own cited requirement');
|
|
134
|
+
assert.strictEqual(requirementSync.applicability, 'applicable', 'the new test triggers requirement-sync');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
checkCase('PR334 reordering existing same-REQ test calls does not become a new-test identity', () => {
|
|
138
|
+
const { facts, requirementSync } = derive(
|
|
139
|
+
[
|
|
140
|
+
"test('REQ-GRPH-001 first', () => { verifyFirst(); });",
|
|
141
|
+
"test('REQ-GRPH-001 second', () => { verifySecond(); });"
|
|
142
|
+
].join('\n'),
|
|
143
|
+
[
|
|
144
|
+
"test('REQ-GRPH-001 second', () => { verifySecond(); });",
|
|
145
|
+
"test('REQ-GRPH-001 first', () => { verifyFirst(); });"
|
|
146
|
+
].join('\n')
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
assert.notStrictEqual(facts.requirementSyncTrigger, 'new-test', 'the same REQ call inventory remains');
|
|
150
|
+
assert.strictEqual(requirementSync.requirementScopeUnknown, false, 'both existing calls retain stable identities');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
checkCase('PR334 a same-REQ helper test inserted before an identical existing test still contributes its citation', () => {
|
|
154
|
+
const { facts, requirementSync } = derive(
|
|
155
|
+
"test('REQ-GRPH-001 existing', () => { verifyA(); });\n",
|
|
156
|
+
[
|
|
157
|
+
"test('REQ-GRPH-001 added first', verifyA);",
|
|
158
|
+
"test('REQ-GRPH-001 existing', () => { verifyA(); });"
|
|
159
|
+
].join('\n')
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
assert.strictEqual(facts.requirementSyncTrigger, 'new-test', 'the per-REQ test count increased');
|
|
163
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-001'],
|
|
164
|
+
'candidate-only changed tokens retain the added test citation');
|
|
165
|
+
assert.strictEqual(requirementSync.applicability, 'applicable', 'the added test triggers requirement-sync');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
checkCase('PR334 adding a file-level tag to an unchanged unwrapped assertion keeps the candidate identity under unknown scope', () => {
|
|
169
|
+
const { facts, requirementSync } = derive(
|
|
170
|
+
'assert.equal(run(), 1);\n',
|
|
171
|
+
'// REQ-GRPH-001\nassert.equal(run(), 1);\n'
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
assert.strictEqual(facts.assertionChanged, false, 'the assertion tokens are unchanged');
|
|
175
|
+
assert.strictEqual(facts.requirementSyncTrigger, null, 'adding a citation comment is not a new test');
|
|
176
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-001'], 'the changed tag identity remains visible');
|
|
177
|
+
assert.strictEqual(facts.requirementScopeUnknown, true, 'the legacy file-level association changed');
|
|
178
|
+
assert.strictEqual(requirementSync.applicability, 'unknown', 'a file-level tag delta is never silently N/A');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
checkCase('PR334 a newly added file-level-tagged unwrapped assertion keeps the legacy new-test and citation signal', () => {
|
|
182
|
+
const { facts, requirementSync } = deriveSnapshots({}, {
|
|
183
|
+
'ci/pr334-added.test.js': '// REQ-GRPH-001\nassert.equal(run(), 1);\n'
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
assert.strictEqual(facts.requirementSyncTrigger, 'new-test', 'the newly added tagged test file remains a candidate');
|
|
187
|
+
assert.deepStrictEqual(facts.citedReqs.map(item => item.id), ['REQ-GRPH-001'],
|
|
188
|
+
'the new file-level tag identifies the added assertion');
|
|
189
|
+
assert.strictEqual(requirementSync.applicability, 'applicable', 'the added tagged test triggers requirement-sync');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
helper.finish();
|