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,268 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// PR #334 round 3 — heading boundary regressions in section extraction.
|
|
4
|
+
//
|
|
5
|
+
// extractSection in doc-obligations.js recognized only column-zero ATX headings. A
|
|
6
|
+
// CommonMark-valid Decision headings indented 1..3 spaces were invisible, so a real
|
|
7
|
+
// Decision edit reported quality-review and conflict-check N/A. Related boundary
|
|
8
|
+
// controls cover Setext Decision headings and tab-indented fence lookalikes.
|
|
9
|
+
//
|
|
10
|
+
// Authoritative rule: https://spec.commonmark.org/0.31.2/#atx-headings
|
|
11
|
+
// * 0-3 ASCII spaces of indentation, then 1-6 "#", then a space/tab or end of line;
|
|
12
|
+
// * 4 spaces or a leading tab is code indentation, not a heading;
|
|
13
|
+
// * a closing sequence must be preceded by a space/tab.
|
|
14
|
+
//
|
|
15
|
+
// Scope is bounded to Decision section boundaries. This is not a CommonMark parser.
|
|
16
|
+
// Fence and code boundaries are exercised as regression controls.
|
|
17
|
+
//
|
|
18
|
+
// Every case runs the full deriveDocFacts -> deriveDocObligations pipeline. Expectations
|
|
19
|
+
// come from the spec and the D6.5 normative-change rule, not from the implementation.
|
|
20
|
+
|
|
21
|
+
const assert = require('assert');
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const helper = require('./test-helper');
|
|
25
|
+
|
|
26
|
+
const modulePath = path.join(__dirname, '..', 'harness-everything', 'scripts', 'doc-obligations.js');
|
|
27
|
+
const fixtureDir = path.join(__dirname, 'fixtures', 'doc-obligations', 'derive');
|
|
28
|
+
const CONFIG = JSON.parse(fs.readFileSync(path.join(fixtureDir, 'config.json'), 'utf8'));
|
|
29
|
+
const ADR = 'docs/adr/GRPH-007.md';
|
|
30
|
+
|
|
31
|
+
let deriveDocFacts;
|
|
32
|
+
let deriveDocObligations;
|
|
33
|
+
try {
|
|
34
|
+
({ deriveDocFacts, deriveDocObligations } = require(modulePath));
|
|
35
|
+
} catch (error) {
|
|
36
|
+
helper.check('pr334-r3-headings. derive module is available', false, error.message);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log('\n[pr334-r3-headings] ATX heading indentation in Decision section extraction...');
|
|
40
|
+
|
|
41
|
+
function readFixture(caseName, side, file = 'GRPH-007.md') {
|
|
42
|
+
return fs.readFileSync(path.join(fixtureDir, caseName, side, 'docs', 'adr', file), 'utf8');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Existing typo-decision fixture: one Decision body word changes "lint" -> "lnt".
|
|
46
|
+
const TYPO_BASE = readFixture('typo-decision', 'base');
|
|
47
|
+
const TYPO_CANDIDATE = readFixture('typo-decision', 'candidate');
|
|
48
|
+
|
|
49
|
+
function adr(body) {
|
|
50
|
+
return ['---', 'id: GRPH-007', 'role: adr', 'status: accepted', '---', '# GRPH-007 Lint first', '', body, ''].join('\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function replaceOnce(text, from, to) {
|
|
54
|
+
assert.strictEqual(text.split(from).length, 2, `fixture text contains exactly one "${from}"`);
|
|
55
|
+
return text.replace(from, to);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function indentDecision(text, prefix) {
|
|
59
|
+
return replaceOnce(text, '## Decision', `${prefix}## Decision`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function applicability(base, candidate, file = ADR) {
|
|
63
|
+
const result = deriveDocFacts({
|
|
64
|
+
base: { files: { [file]: base } },
|
|
65
|
+
candidate: { files: { [file]: candidate } },
|
|
66
|
+
config: CONFIG
|
|
67
|
+
});
|
|
68
|
+
const obligations = deriveDocObligations(result.facts);
|
|
69
|
+
return new Map(obligations.map(item => [item.capability, item.applicability]));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The normative capabilities must agree with each other; doc-lint and link-check always apply to a governed edit.
|
|
73
|
+
function assertDecisionEdit(name, base, candidate, want, file = ADR) {
|
|
74
|
+
const map = applicability(base, candidate, file);
|
|
75
|
+
assert.strictEqual(map.get('doc-lint'), 'applicable', `${name}: doc-lint`);
|
|
76
|
+
assert.strictEqual(map.get('link-check'), 'applicable', `${name}: link-check`);
|
|
77
|
+
assert.strictEqual(map.get('quality-review'), want, `${name}: quality-review`);
|
|
78
|
+
assert.strictEqual(map.get('conflict-check'), want, `${name}: conflict-check`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function checkCase(name, run) {
|
|
82
|
+
try {
|
|
83
|
+
run();
|
|
84
|
+
helper.check(name, true);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
helper.check(name, false, error && error.message ? error.message : String(error));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Matrix 1: indentation of the real Decision heading. 0-3 spaces are headings; 4 spaces and a leading tab are code.
|
|
91
|
+
for (const [label, prefix] of [['0 spaces', ''], ['1 space', ' '], ['2 spaces', ' '], ['3 spaces', ' ']]) {
|
|
92
|
+
checkCase(`pr334-r3 indent matrix: Decision heading with ${label} is a heading; Decision edit is normative`, () => {
|
|
93
|
+
assertDecisionEdit(label, indentDecision(TYPO_BASE, prefix), indentDecision(TYPO_CANDIDATE, prefix), 'applicable');
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const [label, prefix] of [['4 spaces', ' '], ['a leading tab', '\t']]) {
|
|
98
|
+
checkCase(`pr334-r3 indent matrix: Decision heading with ${label} is code, not a heading; the edit is non-normative`, () => {
|
|
99
|
+
assertDecisionEdit(label, indentDecision(TYPO_BASE, prefix), indentDecision(TYPO_CANDIDATE, prefix), 'N/A');
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Matrix 2: fences. Existing fixtures keep their behavior; an indented fenced fake heading stays ignored.
|
|
104
|
+
for (const caseName of ['decision-fence-backtick', 'decision-fence-tilde']) {
|
|
105
|
+
checkCase(`pr334-r3 fence control: ${caseName} fixture still treats the real Decision as normative`, () => {
|
|
106
|
+
assertDecisionEdit(caseName, readFixture(caseName, 'base', 'GRPH-010.md'), readFixture(caseName, 'candidate', 'GRPH-010.md'), 'applicable', 'docs/adr/GRPH-010.md');
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
checkCase('pr334-r3 fence: a 3-space indented fenced "## Decision" is ignored; the real Decision edit stays applicable (not unknown)', () => {
|
|
111
|
+
const base = adr('## Context\n\nWe need lint.\n\n ```md\n ## Decision\n ```\n\n## Decision\n\nRun lint first.');
|
|
112
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
113
|
+
assertDecisionEdit('indented fence', base, candidate, 'applicable');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
checkCase('pr334-r3 fence: an indented tilde fence does not hide the following 3-space Decision heading', () => {
|
|
117
|
+
const base = adr('## Context\n\nWe need lint.\n\n ~~~md\n# Example\n ~~~\n\n ## Decision\n\nRun lint first.');
|
|
118
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
119
|
+
assertDecisionEdit('indented tilde fence', base, candidate, 'applicable');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
checkCase('pr334-r3 fence: a tab-indented fence-looking line does not hide a following Decision heading', () => {
|
|
123
|
+
const base = adr('## Context\n\nWe need lint.\n\n\t```md\n ## Decision\n\nRun lint first.');
|
|
124
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
125
|
+
assertDecisionEdit('tab-indented opener', base, candidate, 'applicable');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
checkCase('pr334-r3 fence: a tab-indented closer inside a real fence does not expose a fake Decision heading', () => {
|
|
129
|
+
const base = adr('## Context\n\nWe need lint.\n\n```md\n \t```\n## Decision\n\nFake text.\n```\n\n## Decision\n\nRun lint first.');
|
|
130
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
131
|
+
assertDecisionEdit('tab-indented closer', base, candidate, 'applicable');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
checkCase('pr334-r3 fence: a backtick in a backtick-fence info string invalidates the opener, so Decision is visible', () => {
|
|
135
|
+
const base = adr(['## Context', '', '```js `', '', '## Decision', '', 'Run lint first.'].join('\n'));
|
|
136
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
137
|
+
assertDecisionEdit('malformed backtick info', base, candidate, 'applicable');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
checkCase('pr334-r3 fence: a tilde-fence info string may contain a backtick and still hides its fake Decision', () => {
|
|
141
|
+
const base = adr(['## Context', '', '~~~js `', '## Decision', 'Fake text.', '~~~', '', '## Decision', '', 'Run lint first.'].join('\n'));
|
|
142
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
143
|
+
assertDecisionEdit('tilde info backtick', base, candidate, 'applicable');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
checkCase('pr334-r3 setext: a suspected standalone Setext Decision yields unknown, never N/A', () => {
|
|
147
|
+
const base = adr('## Context\n\nWe need lint.\n\nDecision\n--------\n\nRun lint first.');
|
|
148
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
149
|
+
assertDecisionEdit('Setext Decision', base, candidate, 'unknown');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
checkCase('pr334-r3 setext: a multiline Setext paragraph ending in "Decision" stays unknown rather than a false Decision section', () => {
|
|
153
|
+
const base = adr('## Context\n\nWe need lint.\n\nContext prose\nDecision\n--------\n\nRun lint first.');
|
|
154
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
155
|
+
assertDecisionEdit('multiline Setext title', base, candidate, 'unknown');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
checkCase('pr334-r3 setext: a Setext-looking Decision inside a fence is ignored', () => {
|
|
159
|
+
const base = adr('## Context\n\nWe need lint.\n\n```md\nDecision\n--------\nFake text.\n```\n\n## Decision\n\nRun lint first.');
|
|
160
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
161
|
+
assertDecisionEdit('Setext in fence', base, candidate, 'applicable');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
checkCase('pr334-r3 setext: an unsupported Setext boundary inside Decision yields unknown instead of guessing its paragraph scope', () => {
|
|
165
|
+
const base = adr('## Decision\n\nRun lint first.\n\nNext\n---\n\nLater.');
|
|
166
|
+
const candidate = base.replace('Next\n---', 'Next steps\n---');
|
|
167
|
+
assertDecisionEdit('unsupported Setext boundary', base, candidate, 'unknown');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
checkCase('pr334-r3 thematic break: a rule after the ATX Decision remains inside the section', () => {
|
|
171
|
+
const base = adr('## Decision\n\n---\n\nRun lint first.');
|
|
172
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
173
|
+
assertDecisionEdit('thematic break after ATX', base, candidate, 'applicable');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
checkCase('pr334-r3 thematic break: a rule after a fenced block does not turn its closing fence into a Setext heading', () => {
|
|
177
|
+
const base = adr('## Decision\n\n```js\nexample();\n```\n---\nUse A.');
|
|
178
|
+
const candidate = base.replace('Use A.', 'Use B.');
|
|
179
|
+
assertDecisionEdit('thematic break after fence', base, candidate, 'applicable');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
checkCase('pr334-r3 setext: four-space-indented Setext-looking text is code, not a Decision heading', () => {
|
|
183
|
+
const base = adr('## Context\n\nWe need lint.\n\n Decision\n --------\n\nRun lint first.');
|
|
184
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
185
|
+
assertDecisionEdit('Setext in code', base, candidate, 'N/A');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Matrix 3: section termination and inclusion.
|
|
189
|
+
checkCase('pr334-r3 termination: a 3-space subsection (### Consequences) is included in the Decision section', () => {
|
|
190
|
+
const base = adr(' ## Decision\n\nRun lint first.\n\n ### Consequences\n\nLint is cheap.');
|
|
191
|
+
const candidate = base.replace('Lint is cheap.', 'Lnt is cheap.');
|
|
192
|
+
assertDecisionEdit('subsection', base, candidate, 'applicable');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
checkCase('pr334-r3 termination: a 3-space same-level heading (## Next) ends the Decision section; an edit there is non-normative', () => {
|
|
196
|
+
const base = adr(' ## Decision\n\nRun lint first.\n\n ## Next\n\nLint later.');
|
|
197
|
+
const candidate = base.replace('Lint later.', 'Lnt later.');
|
|
198
|
+
assertDecisionEdit('same-level terminator', base, candidate, 'N/A');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
checkCase('pr334-r3 termination: a 1-space higher-level heading (# Appendix) ends the Decision section; an edit there is non-normative', () => {
|
|
202
|
+
const base = adr(' ## Decision\n\nRun lint first.\n\n # Appendix\n\nLint notes.');
|
|
203
|
+
const candidate = base.replace('Lint notes.', 'Lnt notes.');
|
|
204
|
+
assertDecisionEdit('higher-level terminator', base, candidate, 'N/A');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
checkCase('pr334-r3 termination: an empty "##" heading line ends the Decision section; an edit after it is non-normative', () => {
|
|
208
|
+
const base = adr(' ## Decision\n\nRun lint first.\n\n##\n\nLint notes.');
|
|
209
|
+
const candidate = base.replace('Lint notes.', 'Lnt notes.');
|
|
210
|
+
assertDecisionEdit('empty terminator', base, candidate, 'N/A');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
checkCase('pr334-r3 termination: a 4-space indented "## Example" is code and does not end the Decision section', () => {
|
|
214
|
+
const base = adr(' ## Decision\n\nRun lint first.\n\n ## Example\n\nLint sample.');
|
|
215
|
+
const candidate = base.replace('Lint sample.', 'Lnt sample.');
|
|
216
|
+
assertDecisionEdit('code-indented non-terminator', base, candidate, 'applicable');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Matrix 4: duplicate Decision headings are ambiguous, whatever the indentation.
|
|
220
|
+
checkCase('pr334-r3 duplicate: a column-0 Decision plus a 2-space Decision is unknown, never N/A or applicable', () => {
|
|
221
|
+
const base = adr('## Decision\n\nRun lint first.\n\n ## Decision\n\nOther.');
|
|
222
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
223
|
+
assertDecisionEdit('duplicate', base, candidate, 'unknown');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Matrix 5: closing sequences and separators, per the ATX rule. Only space/tab separators and preceding-space closings count.
|
|
227
|
+
checkCase('pr334-r3 closing: a 2-space "## Decision ##" closing sequence is the Decision heading', () => {
|
|
228
|
+
const base = adr(' ## Decision ##\n\nRun lint first.');
|
|
229
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
230
|
+
assertDecisionEdit('closing', base, candidate, 'applicable');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
checkCase('pr334-r3 closing: "## Decision#" has no preceding space, so the title is "Decision#" and Decision is absent', () => {
|
|
234
|
+
const base = adr('## Decision#\n\nRun lint first.');
|
|
235
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
236
|
+
assertDecisionEdit('glued hash', base, candidate, 'N/A');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
checkCase('pr334-r3 separator: a tab after the hashes ("##\\tDecision") is a valid separator', () => {
|
|
240
|
+
const base = adr('##\tDecision\n\nRun lint first.');
|
|
241
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
242
|
+
assertDecisionEdit('tab separator', base, candidate, 'applicable');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
checkCase('pr334-r3 separator: a no-break space after the hashes is not a CommonMark separator, so it is not a heading', () => {
|
|
246
|
+
const base = adr('## Decision\n\nRun lint first.');
|
|
247
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
248
|
+
assertDecisionEdit('nbsp separator', base, candidate, 'N/A');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
checkCase('pr334-r3 separator: "##Decision" with no separator is not a heading (control)', () => {
|
|
252
|
+
const base = adr('##Decision\n\nRun lint first.');
|
|
253
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
254
|
+
assertDecisionEdit('no separator', base, candidate, 'N/A');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
checkCase('pr334-r3 level: seven hashes is not an ATX heading (control)', () => {
|
|
258
|
+
const base = adr('####### Decision\n\nRun lint first.');
|
|
259
|
+
const candidate = base.replace('Run lint first.', 'Run lnt first.');
|
|
260
|
+
assertDecisionEdit('seven hashes', base, candidate, 'N/A');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Positive control: the column-0 typo-decision fixture is unchanged by this fix.
|
|
264
|
+
checkCase('pr334-r3 control: the existing column-0 typo-decision fixture stays normative', () => {
|
|
265
|
+
assertDecisionEdit('typo-decision', TYPO_BASE, TYPO_CANDIDATE, 'applicable');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
helper.finish();
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Test-Source Portability Lint
|
|
6
|
+
*
|
|
7
|
+
* The Windows CI matrix has failed on fixture paths that macOS/Linux never
|
|
8
|
+
* reject: a filename containing ':' and '"' (mechanism-2ay, run 39043848220)
|
|
9
|
+
* and a Windows device-namespace path (mechanism-2au, run 38760213406).
|
|
10
|
+
* This lint moves that failure class from "after push" to this local gate.
|
|
11
|
+
*
|
|
12
|
+
* Scanned: every .js file under ci/ (fixture creators, check scripts, tests).
|
|
13
|
+
*
|
|
14
|
+
* Rules, all requiring a whitespace-free single-token literal so prose,
|
|
15
|
+
* regex sources and message templates stay out of the net:
|
|
16
|
+
* WIN_DEVICE_PATH the literal begins with the \\.\ or \\?\ device
|
|
17
|
+
* namespace prefix
|
|
18
|
+
* WIN_RESERVED_NAME the literal, or a path segment of it, is CON PRN
|
|
19
|
+
* AUX NUL COM1-9 LPT1-9, with or without an extension
|
|
20
|
+
* WIN_INVALID_CHAR a path-like literal contains one of : * ? " < > |
|
|
21
|
+
* (clock-like 12:34 fragments are not counted; only
|
|
22
|
+
* verified URL/Node-specifier schemes with a value
|
|
23
|
+
* after the colon and wildcard-led glob segments are
|
|
24
|
+
* exempt, so bad:name.md and bad*name.md are flagged)
|
|
25
|
+
*
|
|
26
|
+
* The exemptions are contextual, and ci/mechanism-37-portability-lint.test.js
|
|
27
|
+
* holds the positive/negative corpus that locks them down.
|
|
28
|
+
*
|
|
29
|
+
* Known limits, kept deliberately narrow over noisy coverage: it does not
|
|
30
|
+
* flag spaced fixture names, trailing dots/spaces, bare single tokens
|
|
31
|
+
* without a path separator or extension (except reserved names), or
|
|
32
|
+
* invalid characters inside prose-like strings. Those still rely on review
|
|
33
|
+
* and the Windows CI matrix. A finding here is a tripwire, not proof of a
|
|
34
|
+
* Windows failure.
|
|
35
|
+
*
|
|
36
|
+
* Remedy: use a portable fixture name, platform-guard the code path, or mark
|
|
37
|
+
* one deliberate case with `portability-lint: allow <reason>` on its line.
|
|
38
|
+
*
|
|
39
|
+
* Usage: node ci/portability-lint.js
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
const fs = require('fs');
|
|
43
|
+
const path = require('path');
|
|
44
|
+
|
|
45
|
+
const SELF = path.resolve(__filename);
|
|
46
|
+
const SCAN_ROOT = path.resolve(__dirname);
|
|
47
|
+
const SKIP_DIRS = new Set(['fixtures', 'node_modules', '__pycache__']);
|
|
48
|
+
const EXTENSIONS = new Set(['.js', '.cjs', '.mjs']);
|
|
49
|
+
const PRAGMA = /portability-lint:\s*allow/;
|
|
50
|
+
const RESERVED_NAME = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i;
|
|
51
|
+
const INVALID_CHARS = /[:*?"<>|]/;
|
|
52
|
+
const WILDCARD_CHARS = /[*?]/g;
|
|
53
|
+
const CLOCK_FRAGMENT = /\d{1,2}:\d{2}(?::\d{2}(?:\.\d+)?)?/g;
|
|
54
|
+
const DEVICE_NAMESPACE = /^(\\\\|\\)[.?](\\|$)/;
|
|
55
|
+
// A scheme is only exempt with a real authority (`https://...`) or as a
|
|
56
|
+
// known Node/tooling/repo-data specifier (`node:assert/strict`,
|
|
57
|
+
// `pkg:npm/...`, `HEAD:refs/heads/main`, `legacy:GRAPH-1@docs/x.md`). A bare
|
|
58
|
+
// word before the colon (`bad:name.md`) is a filename, not a scheme.
|
|
59
|
+
const URL_WITH_AUTHORITY = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
60
|
+
const SPECIFIER_SCHEME = /^(node|pkg|https?|git\+https?|ssh|file|mailto|data|github|fallback-task|HEAD|legacy|human):/i;
|
|
61
|
+
const DRIVE_PREFIX = /^[A-Za-z]:/;
|
|
62
|
+
// Strings that open like regex sources rather than filenames: a char-class
|
|
63
|
+
// or inline-group opener after a leading slash, a leading inline group, or a
|
|
64
|
+
// leading backslash escape such as \s or \d.
|
|
65
|
+
const REGEX_SOURCE = /^(?:\(\?[:=!]|\/\[|\/\(\?|\\[sSdDwWbB])/;
|
|
66
|
+
// Markup tags, <placeholder> roots, desync tails, and `${...}` templates are
|
|
67
|
+
// data under test, never fixture names.
|
|
68
|
+
const DATAISH_PREFIX = /^[<>{}\[\]$]/;
|
|
69
|
+
// A wildcard-led segment (`*.md`, `**`) marks the token as a glob pattern.
|
|
70
|
+
const GLOB_SEGMENT = /^[*?]/;
|
|
71
|
+
const REGEX_PREFIX_CHARS = '([{,;:=!&|?+-*%<>~^';
|
|
72
|
+
const REGEX_KEYWORDS = /(?:^|[^\w$])(return|typeof|case|in|of|new|delete|void|instanceof)\s*$/;
|
|
73
|
+
|
|
74
|
+
function extractLiterals(source) {
|
|
75
|
+
const literals = [];
|
|
76
|
+
let line = 1;
|
|
77
|
+
let i = 0;
|
|
78
|
+
const n = source.length;
|
|
79
|
+
while (i < n) {
|
|
80
|
+
const ch = source[i];
|
|
81
|
+
if (ch === '\n') {
|
|
82
|
+
line += 1;
|
|
83
|
+
i += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (ch === '/' && source[i + 1] === '/') {
|
|
87
|
+
const next = source.indexOf('\n', i);
|
|
88
|
+
i = next === -1 ? n : next;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (ch === '/' && source[i + 1] === '*') {
|
|
92
|
+
let j = i + 2;
|
|
93
|
+
while (j < n && !(source[j] === '*' && source[j + 1] === '/')) {
|
|
94
|
+
if (source[j] === '\n') line += 1;
|
|
95
|
+
j += 1;
|
|
96
|
+
}
|
|
97
|
+
i = j + 2;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (ch === '/' && regexPosition(source, i)) {
|
|
101
|
+
// Skip regex literals so quotes and brackets inside them never desync
|
|
102
|
+
// the scanner into reading code as a string.
|
|
103
|
+
let j = i + 1;
|
|
104
|
+
let inClass = false;
|
|
105
|
+
while (j < n) {
|
|
106
|
+
const c = source[j];
|
|
107
|
+
if (c === '\n') break;
|
|
108
|
+
if (c === '\\') {
|
|
109
|
+
j += 2;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (c === '[') inClass = true;
|
|
113
|
+
else if (c === ']') inClass = false;
|
|
114
|
+
else if (c === '/' && !inClass) {
|
|
115
|
+
j += 1;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
j += 1;
|
|
119
|
+
}
|
|
120
|
+
i = j;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (ch === "'" || ch === '"' || ch === '`') {
|
|
124
|
+
const quote = ch;
|
|
125
|
+
const startLine = line;
|
|
126
|
+
let value = '';
|
|
127
|
+
let j = i + 1;
|
|
128
|
+
let closed = false;
|
|
129
|
+
while (j < n) {
|
|
130
|
+
const c = source[j];
|
|
131
|
+
if (c === '\n') break;
|
|
132
|
+
if (c === '\\') {
|
|
133
|
+
if (j + 1 >= n) break;
|
|
134
|
+
value += source[j + 1];
|
|
135
|
+
j += 2;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (c === quote) {
|
|
139
|
+
closed = true;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
value += c;
|
|
143
|
+
j += 1;
|
|
144
|
+
}
|
|
145
|
+
if (closed) {
|
|
146
|
+
literals.push({ line: startLine, value });
|
|
147
|
+
i = j + 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// Unterminated on this line: treat the quote as plain text, rescan.
|
|
151
|
+
i += 1;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
i += 1;
|
|
155
|
+
}
|
|
156
|
+
return literals;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function regexPosition(source, i) {
|
|
160
|
+
let k = i - 1;
|
|
161
|
+
while (k >= 0 && (source[k] === ' ' || source[k] === '\t')) k -= 1;
|
|
162
|
+
if (k < 0 || source[k] === '\n') return true;
|
|
163
|
+
if (REGEX_PREFIX_CHARS.includes(source[k])) return true;
|
|
164
|
+
return REGEX_KEYWORDS.test(source.slice(Math.max(0, k - 12), k + 1));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function looksPathLike(value) {
|
|
168
|
+
const s = value.trim();
|
|
169
|
+
if (!s || s.length > 300) return false;
|
|
170
|
+
if (/[\\/]/.test(s)) return true;
|
|
171
|
+
return /\.[A-Za-z0-9]{1,8}$/.test(s); // bare filename with extension
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function pathSegments(value) {
|
|
175
|
+
const body = DRIVE_PREFIX.test(value) ? value.slice(2) : value;
|
|
176
|
+
return body.split(/[\\/]+/).filter(Boolean);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function analyzeLiteral(literal) {
|
|
180
|
+
const findings = [];
|
|
181
|
+
const value = literal.value.trim();
|
|
182
|
+
if (!value) return findings;
|
|
183
|
+
if (DEVICE_NAMESPACE.test(value)) {
|
|
184
|
+
findings.push({
|
|
185
|
+
rule: 'WIN_DEVICE_PATH',
|
|
186
|
+
detail: `literal begins with the ${value.slice(0, 3)} device-namespace prefix`,
|
|
187
|
+
});
|
|
188
|
+
return findings;
|
|
189
|
+
}
|
|
190
|
+
if (DATAISH_PREFIX.test(value)) return findings;
|
|
191
|
+
if (/\s/.test(value)) return findings; // prose, templates, patch bodies
|
|
192
|
+
|
|
193
|
+
const segments = pathSegments(value);
|
|
194
|
+
const glob = segments.some((segment) => GLOB_SEGMENT.test(segment));
|
|
195
|
+
|
|
196
|
+
if (RESERVED_NAME.test(value)) {
|
|
197
|
+
findings.push({
|
|
198
|
+
rule: 'WIN_RESERVED_NAME',
|
|
199
|
+
detail: `literal "${value}" is a reserved Windows name`,
|
|
200
|
+
});
|
|
201
|
+
} else if (looksPathLike(value)) {
|
|
202
|
+
for (const segment of segments) {
|
|
203
|
+
if (RESERVED_NAME.test(segment)) {
|
|
204
|
+
findings.push({
|
|
205
|
+
rule: 'WIN_RESERVED_NAME',
|
|
206
|
+
detail: `segment "${segment}" is a reserved Windows name`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (
|
|
213
|
+
looksPathLike(value) &&
|
|
214
|
+
!URL_WITH_AUTHORITY.test(value) &&
|
|
215
|
+
!SPECIFIER_SCHEME.test(value) &&
|
|
216
|
+
!REGEX_SOURCE.test(value)
|
|
217
|
+
) {
|
|
218
|
+
// The drive-letter colon is portable; everything after it is not.
|
|
219
|
+
const withoutDrive = DRIVE_PREFIX.test(value) ? value.slice(2) : value;
|
|
220
|
+
const withoutClocks = withoutDrive.replace(CLOCK_FRAGMENT, '');
|
|
221
|
+
const scanned = glob ? withoutClocks.replace(WILDCARD_CHARS, '') : withoutClocks;
|
|
222
|
+
const invalid = scanned.match(INVALID_CHARS);
|
|
223
|
+
if (invalid) {
|
|
224
|
+
findings.push({
|
|
225
|
+
rule: 'WIN_INVALID_CHAR',
|
|
226
|
+
detail: `literal "${value}" contains '${invalid[0]}'`,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return findings;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function collectFiles(dir, out) {
|
|
234
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
235
|
+
if (entry.isDirectory()) {
|
|
236
|
+
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) continue;
|
|
237
|
+
collectFiles(path.join(dir, entry.name), out);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (!entry.isFile()) continue;
|
|
241
|
+
if (!EXTENSIONS.has(path.extname(entry.name))) continue;
|
|
242
|
+
const full = path.join(dir, entry.name);
|
|
243
|
+
if (full === SELF) continue;
|
|
244
|
+
out.push(full);
|
|
245
|
+
}
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function main() {
|
|
250
|
+
console.log('=== 🧭 Harness test-source portability lint ===');
|
|
251
|
+
|
|
252
|
+
const files = collectFiles(SCAN_ROOT, []);
|
|
253
|
+
let literalCount = 0;
|
|
254
|
+
let findings = 0;
|
|
255
|
+
|
|
256
|
+
for (const file of files) {
|
|
257
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
258
|
+
const lines = text.split('\n');
|
|
259
|
+
for (const literal of extractLiterals(text)) {
|
|
260
|
+
literalCount += 1;
|
|
261
|
+
const allowed =
|
|
262
|
+
PRAGMA.test(lines[literal.line - 1] || '') ||
|
|
263
|
+
PRAGMA.test(lines[literal.line - 2] || '');
|
|
264
|
+
if (allowed) continue;
|
|
265
|
+
for (const finding of analyzeLiteral(literal)) {
|
|
266
|
+
findings += 1;
|
|
267
|
+
const shown = literal.value.length > 80 ? `${literal.value.slice(0, 77)}...` : literal.value;
|
|
268
|
+
const rel = path.relative(path.resolve(SCAN_ROOT, '..'), file);
|
|
269
|
+
console.error(`❌ ${rel}:${literal.line} [${finding.rule}] ${finding.detail}`);
|
|
270
|
+
console.error(` literal: ${shown}`);
|
|
271
|
+
console.error(' fix: portable fixture name, platform guard, or `portability-lint: allow <reason>`');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log(`Scanned ${files.length} file(s), ${literalCount} single-line literal(s).`);
|
|
277
|
+
if (findings > 0) {
|
|
278
|
+
console.error(`❌ portability lint found ${findings} finding(s) in the Windows-hostile class.`);
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
console.log('✅ no Windows-hostile paths in scanned test sources.');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (require.main === module) {
|
|
285
|
+
main();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
module.exports = { extractLiterals, analyzeLiteral, looksPathLike, pathSegments };
|
|
@@ -11,7 +11,8 @@ Documentation is a **consumer** of the #320 workflow/obligation abstractions. No
|
|
|
11
11
|
| 0 — contract + RED fixtures | This file; fixtures under `ci/fixtures/doc-lifecycle/` |
|
|
12
12
|
| 1a — `project-docs.json` loader | `multi-agent-workspace/scripts/project-docs-resolver.js` |
|
|
13
13
|
| 2 — roles, IDs, aliases, legacy, supersession, index, D6.9 PRD revisions | `harness-everything/scripts/doc-lifecycle.js` |
|
|
14
|
-
|
|
|
14
|
+
| 3a — `doc-change` / `requirement-sync` pure derivation and evidence producer | `harness-everything/scripts/doc-obligations.js` (facts and obligations), `harness-everything/scripts/doc-evidence.js` (`checkDocObligations`). Pilot-local: not wired into workflow disposition yet. H3 and H4 have deterministic tests; H1 and H2 are deferred to 3c. Known limits: the attested-envelope fields are caller-asserted (`configDigest` is checked for presence only, not against the parsed config); `candidateDigest` covers only `docs/prd/`, so custom PRD roots and task-scope binding are not yet verified; deleted and renamed paths fail closed (#335) |
|
|
15
|
+
| 1b, 3b–3c (provider suggestions, reconciliation, H1–H2 evaluation) | Not started: blocked by #320 family metadata, D1 obligation schema and D3 evidence states. Known gap: deleted and old rename paths cannot get candidate-bound evidence, so their MUSTs fail closed |
|
|
15
16
|
| 4 — derived-view generators, `describes:` drift | Not started |
|
|
16
17
|
|
|
17
18
|
## Configuration (D5)
|
|
@@ -195,6 +196,61 @@ Anchors for cross-file diagnostics: `supersede-*`, `unresolved-ref`, `ambiguous-
|
|
|
195
196
|
|
|
196
197
|
`invalid-prd-id`, `invalid-revision`, `duplicate-revision`, `missing-parent`, `multiple-baselines`, `revision-cycle`, `revision-fork`, `stale-draft-base`, `invalid-transition`, `approval-missing`, `approval-content-mismatch`, `revision-digest-mismatch`, `immutable-revision-modified`, `immutable-revision-deleted`, `req-dropped`, `incomplete-snapshot`, `invalid-req-clause`, `replacement-alias-overlap`, `replacement-cycle`. `not-observed` is an evidence disposition, not a diagnostic and not a pass.
|
|
197
198
|
|
|
199
|
+
## D6.5 — `doc-change` applicability by diff facts (Decided; revised 2026-10-16)
|
|
200
|
+
|
|
201
|
+
Not a single class enum. The diff yields **facts**; required checks are the **union** of what each fact triggers. If a fact cannot be determined, assume it is present (`unknown` never downgrades to trivial).
|
|
202
|
+
|
|
203
|
+
| Fact (from diff) | Triggers |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| Any governed file touched | Lint (role/status/ID/alias); in-repo link check |
|
|
206
|
+
| Text/format only (no fact below) | Nothing more |
|
|
207
|
+
| New governed file | Update-over-create check; outline confirmation (MUST for PRD/ADR, SHOULD otherwise) |
|
|
208
|
+
| Declared derived source affected | Regenerate + regenerate-equals |
|
|
209
|
+
| **Normative field**: frontmatter `status`/`supersedes`, PRD REQ block/acceptance, ADR *Decision* section | Quality review (MUST); D6.4 conflict check |
|
|
210
|
+
| Governed reference/guide content | Prose > 50% reminder (SHOULD) |
|
|
211
|
+
| Code or REQ-citing test touched | `requirement-sync` evaluation (D6.6) |
|
|
212
|
+
|
|
213
|
+
Decision extraction recognizes ATX boundaries with 0–3 leading spaces and ignores fenced or indented code. A suspected Setext Decision heading, or an unsupported Setext boundary within an ATX Decision section, yields `unknown`; paragraph/container context is not inferred.
|
|
214
|
+
|
|
215
|
+
- Example: a typo in an ADR's *Context* section triggers only lint + links; the same typo in its *Decision* section is normative.
|
|
216
|
+
- Claimed scope vs diff facts mismatch → claim/evidence contradiction (#320 D3), only where the diff supports it; visible, never a lock.
|
|
217
|
+
- Commit still requires existing workflow authorization; no automatic commit.
|
|
218
|
+
|
|
219
|
+
## D6.6 — Event-driven maintenance (Decided)
|
|
220
|
+
|
|
221
|
+
**`requirement-sync` detection** (builds on D6.2: tests cite REQ IDs):
|
|
222
|
+
|
|
223
|
+
| Observation | Disposition |
|
|
224
|
+
| --- | --- |
|
|
225
|
+
| A test citing `REQ-X` is added or its assertions change | **Candidate signal**, not proof → **MUST** update/supersede REQ-X in the PRD in the same change, or record a structured `no-req-change` (reason + cited evidence, e.g. unchanged acceptance, passing test result) |
|
|
226
|
+
| An existing test's cited REQ identity set is added, removed, or retargeted, or the changed call cannot be associated with its prior identity | Applicability/scope `unknown`, never N/A; retain candidate REQ identities and the test path. If removal leaves no candidate identity, retain the prior REQ ID as a removed identity. A citation-only change is not by itself a `new-test` or `assertion-change` trigger |
|
|
227
|
+
| A legacy file-level REQ tag is added, removed, or retargeted on a modified test file | Applicability/scope `unknown`, never N/A; retain the changed identity and test path, even when stripped comments leave assertion tokens unchanged |
|
|
228
|
+
| A test title changes or reorders REQ IDs while preserving the same cited identity set, or an ordinary comment changes | No trigger → `requirement-sync` N/A |
|
|
229
|
+
| Ordinary comment-only or whitespace-only edit that preserves the cited REQ identity set | Not a trigger → `requirement-sync` N/A |
|
|
230
|
+
| REQ-citing test change whose assertion change cannot be determined | `unknown`; never a fail |
|
|
231
|
+
| Triggered MUST, **attested envelope** (all of the following), cited REQ resolved, **not** revised, **no** valid `no-req-change` waiver | **Observed fail** (`req-sync-no-revision-or-waiver`). Not a Stop hook, lock, or completion policy |
|
|
232
|
+
| Triggered MUST, `complete` inventory but envelope incomplete, mismatched, or unattested | `none` with `req-sync-envelope-unattested`. Never a fail |
|
|
233
|
+
| Attested envelope = `complete` and `waiverInventoryComplete` both `true`; `baseDigest` equal to the baseline digest; `candidateDigest` equal to the digest of the candidate PRD set; `configDigest` present; non-empty cited REQ list | Caller-attested. The derivation copies it and never infers it. Not independent proof that the caller collected the inventory completely |
|
|
234
|
+
| Unsupported or stale `no-req-change` waiver | Unverified (`unsupported-waiver`). It never satisfies the MUST. It does not by itself establish the failure, and an independently attested unmet obligation may still fail |
|
|
235
|
+
| `no-req-change` without adequate evidence | `not-observed` / unverified; never an automatic pass. An invalid waiver is diagnosed and does not satisfy the MUST |
|
|
236
|
+
| Cited REQ missing, ambiguous, or unresolved | `unresolved` with `req-unresolved`; never a pass, never an observed fail. "Ambiguous" means declared in two distinct current logical PRDs. Retained revisions of one PRD resolve to the current effective revision |
|
|
237
|
+
| Incomplete execution record (no command, no provenance, non-integer `exitCode`) | `execution-record-incomplete`; never a pass |
|
|
238
|
+
| Code changes, no REQ-citing test touched | `not-observed` (#320 D3 evidence state), not a failure; diff alone never proves behavior unchanged |
|
|
239
|
+
| Agent claims REQ-X affected, no supporting evidence | Claim/evidence contradiction; reminder, no lock |
|
|
240
|
+
|
|
241
|
+
| Event | Obligation | Strength | Enforced by |
|
|
242
|
+
| --- | --- | --- | --- |
|
|
243
|
+
| REQ touched (above) | `requirement-sync` | MUST | New rule |
|
|
244
|
+
| `describes:` fingerprint drift | Reminder | SHOULD | D5 #4 |
|
|
245
|
+
| Ticket closed | In-repo tickets: `status: done`; regenerate index. `tickets: "tracker"`: no in-repo ticket status is generated or inferred offline | MUST | D5 #1 + D6.3 lint |
|
|
246
|
+
| ADR superseded | Old ADR `status: superseded` | MUST | D6.3 lint |
|
|
247
|
+
| New defect | Goes to tracker or the declared `tickets` path | MUST | Lint reminder when a ticket is added under a `done` phase |
|
|
248
|
+
| Report finished | `final`; `archived` once its work closes | SHOULD | Reminder |
|
|
249
|
+
|
|
250
|
+
- All events are checked at the existing workflow's `verification` step; no standing maintenance loop.
|
|
251
|
+
- Checks are pure and generators deterministic, so reruns are idempotent.
|
|
252
|
+
- `requirement-sync` is the only genuinely new rule; the rest reuse D5 and D6.3 checks.
|
|
253
|
+
|
|
198
254
|
## Packaging
|
|
199
255
|
|
|
200
256
|
Source checkout, Codex plugin and Claude plugin copy whole skill trees. Callers locate `doc-lifecycle.js` and the resolver relative to their own skill directory, never the source-tree root. A to-spec-only install must use the same resolver implementation (copied byte-identically and checked), and a broken shared module is an error, never a silent fallback to older host scanning.
|
|
@@ -19,7 +19,7 @@ This page is generated from checked-in runtime and workflow sources. Do not edit
|
|
|
19
19
|
| Workflow | Node.js versions | Explicit triggers | npm quality gates |
|
|
20
20
|
| --- | --- | --- | --- |
|
|
21
21
|
| `behavioral-evals.yml` | 24 | schedule, workflow_dispatch | n/a |
|
|
22
|
-
| `ci.yml` | 22, 24 | workflow_call | `test`, `test:mutations`, `test:release:semantics`, `waza:check`, `test:consistency`, `test:references`, `test:release`, `test:collision`, `test:plugin:openai`, `test:plugin:submission`, `test:platform:compatibility` |
|
|
22
|
+
| `ci.yml` | 22, 24 | workflow_call | `test`, `test:mutations`, `test:release:semantics`, `waza:check`, `test:consistency`, `test:references`, `test:release`, `test:collision`, `test:plugin:openai`, `test:plugin:submission`, `test:platform:compatibility`, `test:portability` |
|
|
23
23
|
| `release.yml` | 24 | workflow_dispatch | n/a |
|
|
24
24
|
|
|
25
25
|
## Drift prevention
|