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,112 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const verifyAll = require(path.join(__dirname, '..', 'scripts', 'verify-all.js'));
|
|
7
|
+
|
|
8
|
+
// --- launcher policy: git never through a shell, Windows shims do -------
|
|
9
|
+
|
|
10
|
+
assert.strictEqual(verifyAll.needsShell('git', 'darwin'), false, 'git stays shell-free on POSIX');
|
|
11
|
+
assert.strictEqual(verifyAll.needsShell('git', 'win32'), false, 'git stays shell-free on Windows (spaces survive)');
|
|
12
|
+
assert.strictEqual(verifyAll.needsShell('npm', 'win32'), true, 'npm may be a .cmd shim on Windows');
|
|
13
|
+
assert.strictEqual(verifyAll.needsShell('npx', 'win32'), true, 'npx may be a .cmd shim on Windows');
|
|
14
|
+
assert.strictEqual(verifyAll.needsShell('waza', 'win32'), true, 'waza may be a shim on Windows');
|
|
15
|
+
assert.strictEqual(verifyAll.needsShell('npm', 'darwin'), false, 'npm runs shell-free on POSIX');
|
|
16
|
+
|
|
17
|
+
// --- plan: every known gate is retained in every mode --------------------
|
|
18
|
+
|
|
19
|
+
const labels = (plan) => plan.map((entry) => entry.label);
|
|
20
|
+
const entryFor = (plan, label) => plan.find((entry) => entry.label === label);
|
|
21
|
+
|
|
22
|
+
const full = verifyAll.buildPlan({ fast: false, node22: false, waza: true });
|
|
23
|
+
for (const entry of verifyAll.FULL_PLAN) {
|
|
24
|
+
assert.ok(labels(full).includes(entry.label), `full plan retains ${entry.label}`);
|
|
25
|
+
assert.strictEqual(entryFor(full, entry.label).enabled, true, `${entry.label} runs by default`);
|
|
26
|
+
}
|
|
27
|
+
assert.strictEqual(full.length, verifyAll.FULL_PLAN.length + verifyAll.NODE22_PLAN.length + 2, 'full plan retains node22 + unverified + waza rows');
|
|
28
|
+
|
|
29
|
+
for (const entry of verifyAll.NODE22_PLAN) {
|
|
30
|
+
const node22Row = entryFor(full, entry.label);
|
|
31
|
+
assert.ok(node22Row, `full plan retains ${entry.label}`);
|
|
32
|
+
assert.strictEqual(node22Row.enabled, false, 'node22 lane is opt-in');
|
|
33
|
+
assert.strictEqual(node22Row.reason, 'not requested; add --node22', 'opt-out carries a reason');
|
|
34
|
+
}
|
|
35
|
+
const unverified = entryFor(full, verifyAll.NODE22_UNVERIFIED.label);
|
|
36
|
+
assert.ok(unverified, 'dependency-install row is always present');
|
|
37
|
+
assert.strictEqual(unverified.enabled, false, 'dependency-install row never runs locally');
|
|
38
|
+
assert.ok(/runtime-floor/.test(unverified.reason), 'dependency-install row names where it is covered');
|
|
39
|
+
|
|
40
|
+
const fast = verifyAll.buildPlan({ fast: true, node22: false, waza: true });
|
|
41
|
+
for (const entry of verifyAll.FULL_PLAN) {
|
|
42
|
+
const row = entryFor(fast, entry.label);
|
|
43
|
+
assert.strictEqual(row.enabled, entry.fast, `${entry.label} follows the fast subset`);
|
|
44
|
+
if (!entry.fast) {
|
|
45
|
+
assert.strictEqual(row.reason, 'not in the --fast subset', `${entry.label} explains why it is unchecked`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
assert.ok(entryFor(fast, 'waza readiness (optional)').enabled, 'waza runs by default in fast mode too');
|
|
49
|
+
|
|
50
|
+
const noWaza = verifyAll.buildPlan({ fast: false, node22: false, waza: false });
|
|
51
|
+
const wazaRow = entryFor(noWaza, 'waza readiness (optional)');
|
|
52
|
+
assert.strictEqual(wazaRow.enabled, false, '--no-waza disables the row');
|
|
53
|
+
assert.strictEqual(wazaRow.reason, 'skipped by --no-waza', '--no-waza row explains itself');
|
|
54
|
+
|
|
55
|
+
// --- report: self-describing, never overstating --------------------------
|
|
56
|
+
|
|
57
|
+
const rows = [
|
|
58
|
+
{ label: 'self-regression', status: 'PASS', seconds: 1.2 },
|
|
59
|
+
{ label: 'release consistency', status: 'FAIL', seconds: 0.4 },
|
|
60
|
+
{ label: 'references resolve', status: 'UNCHECKED', reason: 'not in the --fast subset', seconds: null },
|
|
61
|
+
];
|
|
62
|
+
const report = verifyAll.renderReport(rows, {
|
|
63
|
+
head: 'abc123',
|
|
64
|
+
node: 'v24.0.0',
|
|
65
|
+
platform: 'darwin',
|
|
66
|
+
clean: false,
|
|
67
|
+
mode: verifyAll.formatMode({ fast: true }),
|
|
68
|
+
flags: verifyAll.formatFlags({ fast: true, clean: false, node22: false, install: true, waza: true }),
|
|
69
|
+
});
|
|
70
|
+
assert.ok(/mode: fast subset/.test(report.text), 'report names the mode');
|
|
71
|
+
assert.ok(/flags: --fast/.test(report.text), 'report names the exact flags');
|
|
72
|
+
assert.ok(/HEAD `abc123`/.test(report.text), 'report names HEAD');
|
|
73
|
+
assert.ok(/node v24\.0\.0/.test(report.text), 'report names the runtime');
|
|
74
|
+
assert.ok(/darwin/.test(report.text), 'report names the OS');
|
|
75
|
+
assert.ok(report.text.includes('UNCHECKED (not in the --fast subset)'), 'unchecked rows carry their reason');
|
|
76
|
+
assert.ok(report.text.includes('| 2 | release consistency | FAIL |'), 'failed gates stay visible');
|
|
77
|
+
assert.ok(report.text.includes('Summary: 1 PASS, 1 FAIL, 1 UNCHECKED.'), 'summary counts all three statuses');
|
|
78
|
+
assert.strictEqual(report.failures, 1, 'renderReport returns the failure count for exit handling');
|
|
79
|
+
|
|
80
|
+
const defaults = verifyAll.formatFlags({ fast: false, clean: false, node22: false, install: true, waza: true });
|
|
81
|
+
assert.strictEqual(defaults, '(defaults)', 'no flags prints (defaults)');
|
|
82
|
+
assert.ok(/--clean --node22 --no-waza/.test(
|
|
83
|
+
verifyAll.formatFlags({ fast: false, clean: true, node22: true, install: true, waza: false })
|
|
84
|
+
), 'flags list every activated mode');
|
|
85
|
+
|
|
86
|
+
// --- git runner: worktree paths with spaces survive ----------------------
|
|
87
|
+
|
|
88
|
+
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'verify-all-test-'));
|
|
89
|
+
try {
|
|
90
|
+
const repo = path.join(base, 'repo');
|
|
91
|
+
fs.mkdirSync(repo);
|
|
92
|
+
assert.strictEqual(verifyAll.runGit(['init', '--quiet'], repo).status, 0, 'git init works');
|
|
93
|
+
fs.writeFileSync(path.join(repo, 'seed.txt'), 'seed');
|
|
94
|
+
assert.strictEqual(verifyAll.runGit(['add', 'seed.txt'], repo).status, 0, 'git add works');
|
|
95
|
+
const commit = verifyAll.runGit(
|
|
96
|
+
['-c', 'user.name=Harness', '-c', 'user.email=harness@example.com', 'commit', '--quiet', '-m', 'seed'],
|
|
97
|
+
repo,
|
|
98
|
+
);
|
|
99
|
+
assert.strictEqual(commit.status, 0, `git commit works: ${commit.stderr}`);
|
|
100
|
+
|
|
101
|
+
const spaced = path.join(base, 'worktree with spaces');
|
|
102
|
+
const add = verifyAll.runGit(['worktree', 'add', '--detach', spaced, 'HEAD'], repo);
|
|
103
|
+
assert.strictEqual(add.status, 0, `git worktree add survives a spaced path: ${add.stderr}`);
|
|
104
|
+
assert.ok(fs.existsSync(path.join(spaced, 'seed.txt')), 'spaced worktree has the checked-out tree');
|
|
105
|
+
|
|
106
|
+
const remove = verifyAll.runGit(['worktree', 'remove', '--force', spaced], repo);
|
|
107
|
+
assert.strictEqual(remove.status, 0, `git worktree remove survives a spaced path: ${remove.stderr}`);
|
|
108
|
+
} finally {
|
|
109
|
+
fs.rmSync(base, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('mechanism-38-verify-all: launcher, plan, report, and spaced-path contracts hold');
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const { pathToFileURL } = require('url');
|
|
5
|
+
const helper = require('./test-helper');
|
|
6
|
+
|
|
7
|
+
console.log('\n[39] opencode verification outcomes: Rule-of-3 downstream behavior with the real plugin...');
|
|
8
|
+
|
|
9
|
+
(async () => {
|
|
10
|
+
const workspace = helper.tempDir('.mechanism-test-verify-outcomes-workspace');
|
|
11
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
12
|
+
const stateHome = process.env.HARNESS_STATE_HOME;
|
|
13
|
+
|
|
14
|
+
// Mirrors the plugin's getWorkspaceKey(directory) (same derivation as the
|
|
15
|
+
// 2n test) so this harness can read the state files the real hooks write.
|
|
16
|
+
const realWorkspace = fs.realpathSync(path.resolve(workspace));
|
|
17
|
+
const slug = path.basename(realWorkspace).toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'workspace';
|
|
18
|
+
const hashInput = process.platform === 'win32' ? realWorkspace.toLowerCase() : realWorkspace;
|
|
19
|
+
const hash = crypto.createHash('sha1').update(hashInput).digest('hex').slice(0, 12);
|
|
20
|
+
const sessionsRoot = path.join(stateHome, 'workspaces', `${slug}-${hash}`, 'state', 'sessions');
|
|
21
|
+
|
|
22
|
+
const fixtureFile = path.join(workspace, 'fixture.js');
|
|
23
|
+
fs.writeFileSync(
|
|
24
|
+
path.join(workspace, 'package.json'),
|
|
25
|
+
JSON.stringify({ name: 'fixture', version: '0.0.0', scripts: { test: 'node fixture.js' } }, null, 2),
|
|
26
|
+
);
|
|
27
|
+
const setFixture = (lines) => fs.writeFileSync(fixtureFile, `${lines.join('\n')}\n`);
|
|
28
|
+
const markerFailure = (name) => [
|
|
29
|
+
"console.log('> harness-everything@0.34.0 test');",
|
|
30
|
+
"console.log('> node self-evolve/scripts/self-regression.js');",
|
|
31
|
+
`console.log('\u2716 ${name} (1.2ms)');`,
|
|
32
|
+
`console.log('AssertionError [ERR_ASSERTION]: ${name} boom');`,
|
|
33
|
+
'process.exit(1);',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const promptCalls = [];
|
|
37
|
+
const client = { session: { prompt: async (opts) => { promptCalls.push(opts); return {}; } } };
|
|
38
|
+
const { HarnessEnforcement } = await import(pathToFileURL(path.join(helper.root, 'opencode-plugin', 'index.mjs')).href);
|
|
39
|
+
const hooks = await HarnessEnforcement({ client, directory: workspace });
|
|
40
|
+
|
|
41
|
+
const stateFile = (sessionID, name) => path.join(sessionsRoot, sessionID, name);
|
|
42
|
+
const readBreaker = (sessionID) => JSON.parse(fs.readFileSync(stateFile(sessionID, 'circuit-breaker.json'), 'utf8'));
|
|
43
|
+
const readEditState = (sessionID) => JSON.parse(fs.readFileSync(stateFile(sessionID, 'edit-state.json'), 'utf8'));
|
|
44
|
+
let callCounter = 0;
|
|
45
|
+
const edit = async (sessionID) => hooks['tool.execute.after'](
|
|
46
|
+
{ tool: 'edit', sessionID, callID: `edit-${(callCounter += 1)}` },
|
|
47
|
+
{ title: '', output: '', metadata: {}, args: { filePath: path.join(workspace, 'code.js') } },
|
|
48
|
+
);
|
|
49
|
+
const idle = async (sessionID) => hooks.event({ event: { type: 'session.idle', properties: { sessionID } } });
|
|
50
|
+
const lastPrompt = () => promptCalls[promptCalls.length - 1].body.parts[0].text;
|
|
51
|
+
|
|
52
|
+
// --- three timeouts must not band together into a reflection ----------
|
|
53
|
+
const timeoutSession = 'timeouts';
|
|
54
|
+
setFixture(['setTimeout(() => {}, 3000);']);
|
|
55
|
+
process.env.HARNESS_VERIFY_TIMEOUT_MS = '150';
|
|
56
|
+
for (let i = 0; i < 3; i += 1) {
|
|
57
|
+
await edit(timeoutSession);
|
|
58
|
+
await idle(timeoutSession);
|
|
59
|
+
}
|
|
60
|
+
delete process.env.HARNESS_VERIFY_TIMEOUT_MS;
|
|
61
|
+
const timeoutPrompts = promptCalls.slice(0, 3).map((call) => call.body.parts[0].text);
|
|
62
|
+
helper.check(
|
|
63
|
+
'39. three timeouts never write a same-check failure entry',
|
|
64
|
+
!fs.existsSync(stateFile(timeoutSession, 'circuit-breaker.json')),
|
|
65
|
+
'a circuit-breaker file was written for pure timeouts',
|
|
66
|
+
);
|
|
67
|
+
helper.check(
|
|
68
|
+
'39. timeout follow-ups say the run did not complete and is not counted',
|
|
69
|
+
timeoutPrompts.length === 3 &&
|
|
70
|
+
timeoutPrompts.every((text) => /did not complete/.test(text) && /does NOT count toward the repeated-failure reflection/.test(text)) &&
|
|
71
|
+
!timeoutPrompts.some((text) => /reflection token|## Goal/.test(text)),
|
|
72
|
+
timeoutPrompts[0],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
setFixture(markerFailure('real test A'));
|
|
76
|
+
await edit(timeoutSession);
|
|
77
|
+
await idle(timeoutSession);
|
|
78
|
+
let breaker = readBreaker(timeoutSession);
|
|
79
|
+
let entries = Object.values(breaker.failures);
|
|
80
|
+
helper.check(
|
|
81
|
+
'39. a real failing check after three timeouts counts once, with no reflection',
|
|
82
|
+
entries.length === 1 && entries[0].count === 1 && breaker.reflectionPending === false,
|
|
83
|
+
JSON.stringify(breaker),
|
|
84
|
+
);
|
|
85
|
+
helper.check(
|
|
86
|
+
'39. the post-timeout failure prompt offers retries instead of reflection',
|
|
87
|
+
/2 retries before a forced reflection/.test(lastPrompt()),
|
|
88
|
+
lastPrompt(),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// --- distinct failures sharing a banner stay distinct -----------------
|
|
92
|
+
const distinctSession = 'distinct';
|
|
93
|
+
for (const name of ['alpha', 'beta', 'gamma']) {
|
|
94
|
+
setFixture(markerFailure(name));
|
|
95
|
+
await edit(distinctSession);
|
|
96
|
+
await idle(distinctSession);
|
|
97
|
+
}
|
|
98
|
+
breaker = readBreaker(distinctSession);
|
|
99
|
+
entries = Object.values(breaker.failures);
|
|
100
|
+
helper.check(
|
|
101
|
+
'39. distinct failures behind a common banner stay three separate signatures',
|
|
102
|
+
Object.keys(breaker.failures).length === 3 && entries.every((entry) => entry.count === 1) && breaker.reflectionPending === false,
|
|
103
|
+
JSON.stringify(breaker),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// --- identical stdout, different stderr assertions stay distinct ------
|
|
107
|
+
// P1 regression: the old classifier kept only stdout, so three assertion
|
|
108
|
+
// failures printed to stderr behind the same npm banner collapsed into one
|
|
109
|
+
// signature and forced a reflection for healthy retries.
|
|
110
|
+
const stderrSession = 'stderr-distinct';
|
|
111
|
+
const stderrFailure = (name) => [
|
|
112
|
+
`console.error('AssertionError [ERR_ASSERTION]: ${name} boom');`,
|
|
113
|
+
'process.exit(1);',
|
|
114
|
+
];
|
|
115
|
+
for (const name of ['alpha', 'beta', 'gamma']) {
|
|
116
|
+
setFixture(stderrFailure(name));
|
|
117
|
+
await edit(stderrSession);
|
|
118
|
+
await idle(stderrSession);
|
|
119
|
+
}
|
|
120
|
+
breaker = readBreaker(stderrSession);
|
|
121
|
+
entries = Object.values(breaker.failures);
|
|
122
|
+
helper.check(
|
|
123
|
+
'39. identical stdout with different stderr assertions stays three separate signatures',
|
|
124
|
+
Object.keys(breaker.failures).length === 3 && entries.every((entry) => entry.count === 1) && breaker.reflectionPending === false,
|
|
125
|
+
JSON.stringify(breaker),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// --- >2 KiB shared prefix, different markerless tails stay distinct ---
|
|
129
|
+
// P1 regression: the old 2 KiB leading-prefix truncation hashed the same
|
|
130
|
+
// prefix for all three runs, so different markerless failures collided.
|
|
131
|
+
const prefixSession = 'prefix-distinct';
|
|
132
|
+
const markerlessFailure = (token) => [
|
|
133
|
+
"console.log('progress '.repeat(400));",
|
|
134
|
+
`console.log('HARNESS-UNIQUE-${token}');`,
|
|
135
|
+
'process.exit(1);',
|
|
136
|
+
];
|
|
137
|
+
for (const token of ['ALPHA', 'BETA', 'GAMMA']) {
|
|
138
|
+
setFixture(markerlessFailure(token));
|
|
139
|
+
await edit(prefixSession);
|
|
140
|
+
await idle(prefixSession);
|
|
141
|
+
}
|
|
142
|
+
breaker = readBreaker(prefixSession);
|
|
143
|
+
entries = Object.values(breaker.failures);
|
|
144
|
+
helper.check(
|
|
145
|
+
'39. a shared >2 KiB markerless prefix with different tails stays three separate signatures',
|
|
146
|
+
Object.keys(breaker.failures).length === 3 &&
|
|
147
|
+
entries.every((entry) => entry.count === 1) &&
|
|
148
|
+
Object.keys(breaker.failures).every((signature) => /unrecognized-output/.test(signature)) &&
|
|
149
|
+
breaker.reflectionPending === false,
|
|
150
|
+
JSON.stringify(breaker),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// --- Jest suite output: FAIL <file> is context, not identity -----------
|
|
154
|
+
// P1 regression: the old classifier returned the first matching line, so
|
|
155
|
+
// `FAIL src/parser.test.js` made three different test failures in the same
|
|
156
|
+
// file one signature and forced a reflection.
|
|
157
|
+
const jestSession = 'jest-distinct';
|
|
158
|
+
const jestFailure = (name) => [
|
|
159
|
+
"console.log('FAIL src/parser.test.js');",
|
|
160
|
+
`console.log(' ● parser › accepts ${name}');`,
|
|
161
|
+
`console.log(' AssertionError: ${name}');`,
|
|
162
|
+
'process.exit(1);',
|
|
163
|
+
];
|
|
164
|
+
for (const name of ['alpha', 'beta', 'gamma']) {
|
|
165
|
+
setFixture(jestFailure(name));
|
|
166
|
+
await edit(jestSession);
|
|
167
|
+
await idle(jestSession);
|
|
168
|
+
}
|
|
169
|
+
breaker = readBreaker(jestSession);
|
|
170
|
+
entries = Object.values(breaker.failures);
|
|
171
|
+
helper.check(
|
|
172
|
+
'39. different Jest failures from one file stay three separate signatures',
|
|
173
|
+
Object.keys(breaker.failures).length === 3 && entries.every((entry) => entry.count === 1) && breaker.reflectionPending === false,
|
|
174
|
+
JSON.stringify(breaker),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const jestIdenticalSession = 'jest-identical';
|
|
178
|
+
setFixture(jestFailure('stable'));
|
|
179
|
+
for (let i = 0; i < 3; i += 1) {
|
|
180
|
+
await edit(jestIdenticalSession);
|
|
181
|
+
await idle(jestIdenticalSession);
|
|
182
|
+
}
|
|
183
|
+
breaker = readBreaker(jestIdenticalSession);
|
|
184
|
+
entries = Object.values(breaker.failures);
|
|
185
|
+
helper.check(
|
|
186
|
+
'39. three identical Jest failures still force the Rule-of-3 reflection',
|
|
187
|
+
Object.keys(breaker.failures).length === 1 && entries[0].count === 3 && breaker.reflectionPending === true,
|
|
188
|
+
JSON.stringify(breaker),
|
|
189
|
+
);
|
|
190
|
+
helper.check(
|
|
191
|
+
'39. the Jest-identical reflection prompt names the token',
|
|
192
|
+
/reflection token: [A-Za-z0-9]+/.test(lastPrompt()),
|
|
193
|
+
lastPrompt(),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
// --- indented Mocha tests: true identity survives volatile diagnostics ---
|
|
198
|
+
// P2 regression: the old unindented regex missed " 1) test", then the
|
|
199
|
+
// markerless digest changed on every stack-line number even for the same
|
|
200
|
+
// failing check, preventing Rule-of-3 from reaching reflection.
|
|
201
|
+
const mochaFailure = (name, frameLine) => [
|
|
202
|
+
`console.log(' 1) should parse ${name}');`,
|
|
203
|
+
"console.error(' AssertionError: expected true to equal false');",
|
|
204
|
+
`console.log(' at Context.<anonymous> (fixture.test.js:${frameLine}:7)');`,
|
|
205
|
+
'process.exit(1);',
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
const mochaDistinctSession = 'mocha-distinct';
|
|
209
|
+
for (const name of ['alpha', 'beta', 'gamma']) {
|
|
210
|
+
setFixture(mochaFailure(name, 19));
|
|
211
|
+
await edit(mochaDistinctSession);
|
|
212
|
+
await idle(mochaDistinctSession);
|
|
213
|
+
}
|
|
214
|
+
breaker = readBreaker(mochaDistinctSession);
|
|
215
|
+
entries = Object.values(breaker.failures);
|
|
216
|
+
helper.check(
|
|
217
|
+
'39. different indented Mocha checks never count as one repeated failure',
|
|
218
|
+
Object.keys(breaker.failures).length === 3 &&
|
|
219
|
+
entries.every((entry) => entry.count === 1) &&
|
|
220
|
+
breaker.reflectionPending === false,
|
|
221
|
+
JSON.stringify(breaker),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const mochaIdenticalSession = 'mocha-identical';
|
|
225
|
+
for (const frameLine of [19, 27, 35]) {
|
|
226
|
+
setFixture(mochaFailure('stable', frameLine));
|
|
227
|
+
await edit(mochaIdenticalSession);
|
|
228
|
+
await idle(mochaIdenticalSession);
|
|
229
|
+
}
|
|
230
|
+
breaker = readBreaker(mochaIdenticalSession);
|
|
231
|
+
entries = Object.values(breaker.failures);
|
|
232
|
+
helper.check(
|
|
233
|
+
'39. same indented Mocha check with changing stack output still triggers Rule-of-3',
|
|
234
|
+
Object.keys(breaker.failures).length === 1 &&
|
|
235
|
+
entries[0].count === 3 &&
|
|
236
|
+
breaker.reflectionPending === true,
|
|
237
|
+
JSON.stringify(breaker),
|
|
238
|
+
);
|
|
239
|
+
helper.check(
|
|
240
|
+
'39. Mocha Rule-of-3 reflection includes a token',
|
|
241
|
+
/reflection token: [A-Za-z0-9]+/.test(lastPrompt()),
|
|
242
|
+
lastPrompt(),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// --- Console payload noise: logged Error lines are not failure identity ---
|
|
246
|
+
// Regression guard for the indented-error pattern: a Jest Console payload
|
|
247
|
+
// (logged output, volatile run to run) must not become the marker, or the
|
|
248
|
+
// same failing check stops accumulating toward the Rule of 3.
|
|
249
|
+
const consoleNoisyFailure = [
|
|
250
|
+
"console.log('PASS src/loggy.test.js');",
|
|
251
|
+
"console.log(' ● Console');",
|
|
252
|
+
"console.log('');",
|
|
253
|
+
"console.log(' console.error');",
|
|
254
|
+
"console.log(' Error: connection refused at ' + Date.now());",
|
|
255
|
+
"console.log('');",
|
|
256
|
+
"console.log('FAIL src/parser.test.js');",
|
|
257
|
+
"console.log(' ● parser › accepts stable');",
|
|
258
|
+
'process.exit(1);',
|
|
259
|
+
];
|
|
260
|
+
const consoleNoiseSession = 'console-noise';
|
|
261
|
+
for (let i = 0; i < 3; i += 1) {
|
|
262
|
+
setFixture(consoleNoisyFailure);
|
|
263
|
+
await edit(consoleNoiseSession);
|
|
264
|
+
await idle(consoleNoiseSession);
|
|
265
|
+
}
|
|
266
|
+
breaker = readBreaker(consoleNoiseSession);
|
|
267
|
+
entries = Object.values(breaker.failures);
|
|
268
|
+
helper.check(
|
|
269
|
+
'39. volatile Console payload noise never breaks the failing check identity',
|
|
270
|
+
Object.keys(breaker.failures).length === 1 && entries[0].count === 3 && breaker.reflectionPending === true,
|
|
271
|
+
JSON.stringify(breaker),
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// --- nested Jest Console bullets are payload, not test-level markers ---
|
|
275
|
+
// The previous fix only excluded indented Error lines. A logged "●" used
|
|
276
|
+
// to terminate the Console section regardless of indentation, causing
|
|
277
|
+
// either false reflection (shared console text) or missed Rule-of-3
|
|
278
|
+
// (volatile console text) for actual Jest failures.
|
|
279
|
+
const consoleBulletFailure = (name, noise) => [
|
|
280
|
+
"console.log('PASS src/loggy.test.js');",
|
|
281
|
+
"console.log(' ● Console');",
|
|
282
|
+
"console.log('');",
|
|
283
|
+
"console.log(' console.log');",
|
|
284
|
+
`console.log(' ● ${noise}');`,
|
|
285
|
+
"console.log(' ● Console');",
|
|
286
|
+
"console.log('');",
|
|
287
|
+
"console.log('FAIL src/parser.test.js');",
|
|
288
|
+
`console.log(' ● parser › accepts ${name}');`,
|
|
289
|
+
'process.exit(1);',
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
const consoleBulletDistinctSession = 'console-bullet-distinct';
|
|
293
|
+
for (const name of ['alpha', 'beta', 'gamma']) {
|
|
294
|
+
setFixture(consoleBulletFailure(name, 'preparing runtime'));
|
|
295
|
+
await edit(consoleBulletDistinctSession);
|
|
296
|
+
await idle(consoleBulletDistinctSession);
|
|
297
|
+
}
|
|
298
|
+
breaker = readBreaker(consoleBulletDistinctSession);
|
|
299
|
+
entries = Object.values(breaker.failures);
|
|
300
|
+
helper.check(
|
|
301
|
+
'39. shared nested Console bullet with different Jest failures never causes false Rule-of-3',
|
|
302
|
+
Object.keys(breaker.failures).length === 3 &&
|
|
303
|
+
entries.every((entry) => entry.count === 1) &&
|
|
304
|
+
breaker.reflectionPending === false,
|
|
305
|
+
JSON.stringify(breaker),
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const consoleBulletIdenticalSession = 'console-bullet-identical';
|
|
309
|
+
for (const noise of ['preparing runtime', 'loading plugins', 'initialization complete']) {
|
|
310
|
+
setFixture(consoleBulletFailure('stable', noise));
|
|
311
|
+
await edit(consoleBulletIdenticalSession);
|
|
312
|
+
await idle(consoleBulletIdenticalSession);
|
|
313
|
+
}
|
|
314
|
+
breaker = readBreaker(consoleBulletIdenticalSession);
|
|
315
|
+
entries = Object.values(breaker.failures);
|
|
316
|
+
helper.check(
|
|
317
|
+
'39. differing nested Console bullets do not stop the same Jest failure from reaching Rule-of-3',
|
|
318
|
+
Object.keys(breaker.failures).length === 1 &&
|
|
319
|
+
entries[0].count === 3 &&
|
|
320
|
+
breaker.reflectionPending === true,
|
|
321
|
+
JSON.stringify(breaker),
|
|
322
|
+
);
|
|
323
|
+
helper.check(
|
|
324
|
+
'39. nested Console bullet Rule-of-3 produces a reflection token',
|
|
325
|
+
/reflection token: [A-Za-z0-9]+/.test(lastPrompt()),
|
|
326
|
+
lastPrompt(),
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
// --- genuinely identical failures keep the Rule of 3 ------------------
|
|
330
|
+
const identicalSession = 'identical';
|
|
331
|
+
setFixture(markerFailure('stable check'));
|
|
332
|
+
for (let i = 0; i < 3; i += 1) {
|
|
333
|
+
await edit(identicalSession);
|
|
334
|
+
await idle(identicalSession);
|
|
335
|
+
}
|
|
336
|
+
breaker = readBreaker(identicalSession);
|
|
337
|
+
entries = Object.values(breaker.failures);
|
|
338
|
+
helper.check(
|
|
339
|
+
'39. three genuinely identical failures still force the Rule-of-3 reflection',
|
|
340
|
+
Object.keys(breaker.failures).length === 1 && entries[0].count === 3 && breaker.reflectionPending === true,
|
|
341
|
+
JSON.stringify(breaker),
|
|
342
|
+
);
|
|
343
|
+
helper.check(
|
|
344
|
+
'39. the forced reflection names the token and required report sections',
|
|
345
|
+
/reflection token: [A-Za-z0-9]+/.test(lastPrompt()) && /## Goal/.test(lastPrompt()) && /## Decision/.test(lastPrompt()),
|
|
346
|
+
lastPrompt(),
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
// --- success after failure clears pending state -----------------------
|
|
350
|
+
const recoverSession = 'recover';
|
|
351
|
+
setFixture(markerFailure('recover check'));
|
|
352
|
+
await edit(recoverSession);
|
|
353
|
+
await idle(recoverSession);
|
|
354
|
+
const promptsBeforeSuccess = promptCalls.length;
|
|
355
|
+
setFixture(['process.exit(0);']);
|
|
356
|
+
await edit(recoverSession);
|
|
357
|
+
await idle(recoverSession);
|
|
358
|
+
const recoveredState = readEditState(recoverSession);
|
|
359
|
+
breaker = readBreaker(recoverSession);
|
|
360
|
+
entries = Object.values(breaker.failures);
|
|
361
|
+
helper.check(
|
|
362
|
+
'39. a passing run after a failure clears pending verification without erasing failure history',
|
|
363
|
+
recoveredState.verificationPending === false &&
|
|
364
|
+
recoveredState.editsSinceVerification === 0 &&
|
|
365
|
+
promptCalls.length === promptsBeforeSuccess &&
|
|
366
|
+
entries[0].count === 1,
|
|
367
|
+
JSON.stringify({ recoveredState, breaker }),
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
helper.finish();
|
|
371
|
+
})().catch((err) => {
|
|
372
|
+
console.error('39. verification outcomes test crashed:', err);
|
|
373
|
+
process.exit(1);
|
|
374
|
+
});
|