harness-everything 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +48 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/config.json +9 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/config.json +20 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/expected.json +12 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r003.md +26 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/valid/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/valid/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r001.body.md +39 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-dropped.body.md +35 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-incomplete.body.md +37 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-one-replaced.body.md +39 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-overlap.body.md +39 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/docs/adr/GRPH-001.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/docs/adr/GRPH-002.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/docs/adr/GRPH-001.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/docs/adr/GRPH-002.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/docs/adr/GRPH-010.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/docs/adr/legacy/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/docs/adr/GRAPH-007.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/docs/adr/legacy/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/docs/adr/legacy/v1/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/docs/adr/legacy/v2/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-003.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-004.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-005.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-006.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-007.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/docs/adr/GRPH-020.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/docs/adr/GRPH-021.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/docs/adr/GRPH-003.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/docs/prd/graph.md +10 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/expected.json +1 -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-2aw-project-docs-config.test.js +759 -0
- package/ci/mechanism-2ax-doc-lifecycle.test.js +1061 -0
- package/ci/mechanism-2ay-prd-revisions.test.js +2891 -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-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/docs/document-lifecycle.md +262 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/doc-evidence.js +471 -0
- package/harness-everything/scripts/doc-lifecycle.js +2401 -0
- package/harness-everything/scripts/doc-obligations.js +1571 -0
- package/harness-everything/scripts/project-docs-resolver.js +764 -0
- package/multi-agent-workspace/SKILL.md +1 -1
- package/multi-agent-workspace/scripts/project-docs-resolver.js +437 -30
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -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 +2401 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-obligations.js +1571 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/project-docs-resolver.js +764 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +1 -1
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +437 -30
- package/plugins/harness-everything/skills/to-spec/SKILL.md +1 -1
- package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +168 -54
- package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +749 -96
- package/sbom.cdx.json +7 -7
- package/to-spec/SKILL.md +1 -1
- package/to-spec/scripts/check-project-docs.js +168 -54
- package/to-spec/scripts/project-docs-resolver.js +749 -96
|
@@ -17,6 +17,15 @@ const PROJECT_DOC_FIELDS = Object.freeze({
|
|
|
17
17
|
domain: ['domainLocation', 'domains', 'contextLocation', 'contexts', 'domain'],
|
|
18
18
|
architecture: ['architectureLocation', 'architectures', 'architecture']
|
|
19
19
|
});
|
|
20
|
+
const PROJECT_DOCS_CONFIG = path.join('.agents', 'harness-everything', 'project-docs.json');
|
|
21
|
+
const PROJECT_DOC_ROLES = Object.freeze(['prd', 'adr', 'ticket', 'reference', 'log', 'report']);
|
|
22
|
+
const PROJECT_DOC_CONFIG_KEYS = new Set([
|
|
23
|
+
'governed', 'domains', 'legacy', 'derived', 'tickets',
|
|
24
|
+
'docLocation', 'tracker', 'issueDefinition', 'contextMap', 'contextMapPath',
|
|
25
|
+
'decisionLocation', 'decisions', 'adrLocation', 'adr',
|
|
26
|
+
'domainLocation', 'contextLocation', 'contexts', 'domain',
|
|
27
|
+
'architectureLocation', 'architectures', 'architecture'
|
|
28
|
+
]);
|
|
20
29
|
const UNBOUND_WORKSPACE_KEY = 'unbound-workspace';
|
|
21
30
|
|
|
22
31
|
function getWorkspaceRoot(start, allowNonGit = false) {
|
|
@@ -63,20 +72,373 @@ function getRepoManifestHomes(workspaceRoot) {
|
|
|
63
72
|
return PLATFORM_HOMES.map(home => path.join(workspaceRoot, home));
|
|
64
73
|
}
|
|
65
74
|
|
|
66
|
-
function
|
|
75
|
+
function isPlainObject(value) {
|
|
76
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
77
|
+
const prototype = Object.getPrototypeOf(value);
|
|
78
|
+
return prototype === Object.prototype || prototype === null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function projectDocsConfigPath(workspaceRoot) {
|
|
82
|
+
return path.join(workspaceRoot, PROJECT_DOCS_CONFIG);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizedHostHome(activeHost) {
|
|
86
|
+
if (typeof activeHost !== 'string') return null;
|
|
87
|
+
const name = activeHost.trim().toLowerCase().replace(/^\./, '');
|
|
88
|
+
return PLATFORM_HOMES.find(home => home.slice(1) === name) || null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readHostProjectDocsConfigs(workspaceRoot) {
|
|
67
92
|
const manifests = [];
|
|
68
|
-
for (
|
|
93
|
+
for (let index = 0; index < PLATFORM_HOMES.length; index++) {
|
|
94
|
+
const homeName = PLATFORM_HOMES[index];
|
|
95
|
+
const home = path.join(workspaceRoot, homeName);
|
|
69
96
|
const file = path.join(home, 'harness-everything', 'manifest.json');
|
|
70
97
|
if (!fs.existsSync(file)) continue;
|
|
71
98
|
const data = readJson(file);
|
|
72
|
-
if (data && data.projectDocs &&
|
|
73
|
-
manifests.push({ file, projectDocs: data.projectDocs });
|
|
99
|
+
if (data && data.projectDocs && isPlainObject(data.projectDocs)) {
|
|
100
|
+
manifests.push({ file, home: homeName, projectDocs: data.projectDocs });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return manifests;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function inspectHostProjectDocs(workspaceRoot) {
|
|
107
|
+
const entries = [];
|
|
108
|
+
for (const home of PLATFORM_HOMES) {
|
|
109
|
+
const file = path.join(workspaceRoot, home, 'harness-everything', 'manifest.json');
|
|
110
|
+
if (!fs.existsSync(file)) continue;
|
|
111
|
+
const entry = { file, home, projectDocs: null, warnings: [], diagnostics: [], hasProjectDocs: false };
|
|
112
|
+
let manifest;
|
|
113
|
+
try {
|
|
114
|
+
manifest = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
115
|
+
} catch {
|
|
116
|
+
entry.diagnostics.push({
|
|
117
|
+
code: 'invalid-host-config',
|
|
118
|
+
severity: 'error',
|
|
119
|
+
path: configSource(workspaceRoot, file),
|
|
120
|
+
message: `host manifest at ${configSource(workspaceRoot, file)} is not valid JSON`
|
|
121
|
+
});
|
|
122
|
+
entries.push(entry);
|
|
123
|
+
continue;
|
|
74
124
|
}
|
|
125
|
+
if (!isPlainObject(manifest)) {
|
|
126
|
+
entry.diagnostics.push({
|
|
127
|
+
code: 'invalid-host-config',
|
|
128
|
+
severity: 'error',
|
|
129
|
+
path: configSource(workspaceRoot, file),
|
|
130
|
+
message: `host manifest at ${configSource(workspaceRoot, file)} must contain a JSON object`
|
|
131
|
+
});
|
|
132
|
+
entries.push(entry);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (!Object.prototype.hasOwnProperty.call(manifest, 'projectDocs')) {
|
|
136
|
+
entries.push(entry);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
entry.hasProjectDocs = true;
|
|
140
|
+
if (!isPlainObject(manifest.projectDocs)) {
|
|
141
|
+
entry.diagnostics.push({
|
|
142
|
+
code: 'invalid-host-config',
|
|
143
|
+
severity: 'error',
|
|
144
|
+
path: configSource(workspaceRoot, file),
|
|
145
|
+
message: `projectDocs in ${configSource(workspaceRoot, file)} must be a JSON object`
|
|
146
|
+
});
|
|
147
|
+
entries.push(entry);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const projectDocs = { ...manifest.projectDocs };
|
|
152
|
+
if (typeof projectDocs.domains === 'string') {
|
|
153
|
+
if (typeof projectDocs.domainLocation !== 'string' || !projectDocs.domainLocation.trim()) {
|
|
154
|
+
projectDocs.domainLocation = projectDocs.domains;
|
|
155
|
+
}
|
|
156
|
+
delete projectDocs.domains;
|
|
157
|
+
entry.warnings.push({
|
|
158
|
+
code: 'deprecated-domains-string',
|
|
159
|
+
severity: 'warning',
|
|
160
|
+
path: configSource(workspaceRoot, file),
|
|
161
|
+
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const validation = validateDocsConfig(projectDocs);
|
|
166
|
+
const source = configSource(workspaceRoot, file);
|
|
167
|
+
entry.warnings.push(...validation.warnings.map(item => ({ ...item, path: source })));
|
|
168
|
+
if (!validation.valid) {
|
|
169
|
+
const details = validation.diagnostics.map(item => item.message).join('; ');
|
|
170
|
+
entry.diagnostics.push({
|
|
171
|
+
code: 'invalid-host-config',
|
|
172
|
+
severity: 'error',
|
|
173
|
+
path: source,
|
|
174
|
+
message: `projectDocs in ${source} failed validation${details ? `: ${details}` : ''}`
|
|
175
|
+
});
|
|
176
|
+
entries.push(entry);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
entry.projectDocs = projectDocs;
|
|
180
|
+
entries.push(entry);
|
|
75
181
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
182
|
+
return entries;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function readProjectDocs(workspaceRoot, options = {}) {
|
|
186
|
+
const resolvedRoot = workspaceRoot || getWorkspaceRoot();
|
|
187
|
+
if (!resolvedRoot) throw new Error('cannot resolve project docs outside a git workspace; pass a workspace root explicitly');
|
|
188
|
+
const root = path.resolve(resolvedRoot);
|
|
189
|
+
const selection = resolveDocsConfig(root, options);
|
|
190
|
+
const firstSource = selection.sources && selection.sources[0];
|
|
191
|
+
const file = firstSource ? path.resolve(root, ...firstSource.split('/')) : null;
|
|
192
|
+
return {
|
|
193
|
+
file,
|
|
194
|
+
projectDocs: selection.config,
|
|
195
|
+
source: selection.status,
|
|
196
|
+
status: selection.status,
|
|
197
|
+
invalid: selection.status === 'invalid',
|
|
198
|
+
sources: selection.sources || [],
|
|
199
|
+
warnings: selection.warnings || [],
|
|
200
|
+
diagnostics: selection.diagnostics || []
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function isRepoRelativePath(value) {
|
|
205
|
+
if (typeof value !== 'string' || !value.trim()) return false;
|
|
206
|
+
const normalized = value.trim().replace(/\\/g, '/');
|
|
207
|
+
if (normalized.startsWith('/') || /^[A-Za-z]:\//.test(normalized)) return false;
|
|
208
|
+
if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return false;
|
|
209
|
+
return !normalized.split('/').includes('..');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Config globs must match the shape the matcher canonicalizes against:
|
|
213
|
+
// forward slashes, repo-relative, no "./" or "../" leading segments. A
|
|
214
|
+
// non-canonical value would validate but silently match nothing.
|
|
215
|
+
function getPatternIssue(value) {
|
|
216
|
+
if (typeof value !== 'string' || !value.trim()) return 'must be a non-empty string';
|
|
217
|
+
if (value.includes('\\')) return 'must use forward slashes (backslashes are not allowed)';
|
|
218
|
+
if (value.startsWith('/') || /^[A-Za-z]:\//.test(value)) return 'must be repo-relative (no leading "/")';
|
|
219
|
+
if (value.startsWith('./') || value === '.') return 'must be canonical (no leading "./")';
|
|
220
|
+
if (value.startsWith('../')) return 'must be canonical (no leading "../")';
|
|
221
|
+
if (value.split('/').includes('..')) return 'must not contain ".." segments';
|
|
222
|
+
if (value.split('/').includes('.')) return 'must not contain "." path segments';
|
|
223
|
+
if (value.split('/').includes('')) return 'must not contain empty path segments (no "//" or trailing "/")';
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function validateDocsConfig(config) {
|
|
228
|
+
const diagnostics = [];
|
|
229
|
+
const warnings = [];
|
|
230
|
+
function invalid(code, message) {
|
|
231
|
+
diagnostics.push({ code, severity: 'error', path: PROJECT_DOCS_CONFIG.replace(/\\/g, '/'), message });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!isPlainObject(config)) {
|
|
235
|
+
invalid('invalid-config-root', 'project-docs.json must contain a JSON object');
|
|
236
|
+
return { valid: false, diagnostics, warnings };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for (const key of Object.keys(config).sort()) {
|
|
240
|
+
if (!PROJECT_DOC_CONFIG_KEYS.has(key)) {
|
|
241
|
+
warnings.push({
|
|
242
|
+
code: 'unknown-config-key',
|
|
243
|
+
severity: 'warning',
|
|
244
|
+
path: PROJECT_DOCS_CONFIG.replace(/\\/g, '/'),
|
|
245
|
+
message: `unknown project-docs.json key: ${key}`
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (Object.prototype.hasOwnProperty.call(config, 'governed')) {
|
|
251
|
+
if (!Array.isArray(config.governed)) {
|
|
252
|
+
invalid('invalid-governed', 'governed must be an array');
|
|
253
|
+
} else {
|
|
254
|
+
config.governed.forEach((entry, index) => {
|
|
255
|
+
if (!isPlainObject(entry) || getPatternIssue(entry.path)) {
|
|
256
|
+
const issue = isPlainObject(entry) ? getPatternIssue(entry.path) : 'must be an object with a path';
|
|
257
|
+
invalid('invalid-governed-entry', `governed[${index}].path ${issue} (must be a canonical repo-relative glob like "docs/**"); got ${JSON.stringify(entry && entry.path)}`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (!PROJECT_DOC_ROLES.includes(entry.role)) {
|
|
261
|
+
invalid('invalid-governed-role', `governed[${index}].role must be one of ${PROJECT_DOC_ROLES.join(', ')}`);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (Object.prototype.hasOwnProperty.call(config, 'domains')) {
|
|
268
|
+
if (!Array.isArray(config.domains)) {
|
|
269
|
+
invalid('invalid-domains', 'domains must be an array');
|
|
270
|
+
} else {
|
|
271
|
+
const seen = new Set();
|
|
272
|
+
config.domains.forEach((domain, index) => {
|
|
273
|
+
if (typeof domain !== 'string' || !/^[A-Z][A-Z0-9]{1,9}$/.test(domain)) {
|
|
274
|
+
invalid('invalid-domain', `domains[${index}] must match ^[A-Z][A-Z0-9]{1,9}$`);
|
|
275
|
+
} else if (seen.has(domain)) {
|
|
276
|
+
invalid('duplicate-domain', `domains contains duplicate value ${domain}`);
|
|
277
|
+
}
|
|
278
|
+
seen.add(domain);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (Object.prototype.hasOwnProperty.call(config, 'legacy')) {
|
|
284
|
+
if (!Array.isArray(config.legacy)) {
|
|
285
|
+
invalid('invalid-legacy', 'legacy must be an array of repo-relative globs');
|
|
286
|
+
} else {
|
|
287
|
+
config.legacy.forEach((pattern, index) => {
|
|
288
|
+
const issue = getPatternIssue(pattern);
|
|
289
|
+
if (issue) invalid('invalid-legacy-entry', `legacy[${index}] ${issue} (must be a canonical repo-relative glob like "docs/**"); got ${JSON.stringify(pattern)}`);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (Object.prototype.hasOwnProperty.call(config, 'derived')) {
|
|
295
|
+
if (!Array.isArray(config.derived)) {
|
|
296
|
+
invalid('invalid-derived', 'derived must be an array');
|
|
297
|
+
} else {
|
|
298
|
+
config.derived.forEach((entry, index) => {
|
|
299
|
+
if (!isPlainObject(entry) || getPatternIssue(entry.path) ||
|
|
300
|
+
typeof entry.generator !== 'string' || !entry.generator.trim()) {
|
|
301
|
+
const issue = isPlainObject(entry) ? (getPatternIssue(entry.path) || null) : null;
|
|
302
|
+
if (issue) {
|
|
303
|
+
invalid('invalid-derived-entry', `derived[${index}].path ${issue} (must be a canonical repo-relative path like "docs/gen/index.md"); got ${JSON.stringify(entry && entry.path)}`);
|
|
304
|
+
} else {
|
|
305
|
+
invalid('invalid-derived-entry', `derived[${index}] must have non-empty path and generator strings`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (Object.prototype.hasOwnProperty.call(config, 'tickets') &&
|
|
313
|
+
config.tickets !== 'tracker' && !isRepoRelativePath(config.tickets)) {
|
|
314
|
+
invalid('invalid-tickets', 'tickets must be "tracker" or a repo-relative path');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return { valid: diagnostics.length === 0, diagnostics, warnings };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function canonicalize(value) {
|
|
321
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
322
|
+
if (!isPlainObject(value)) return value;
|
|
323
|
+
const normalized = {};
|
|
324
|
+
for (const key of Object.keys(value).sort()) normalized[key] = canonicalize(value[key]);
|
|
325
|
+
return normalized;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function canonicalJson(value) {
|
|
329
|
+
return JSON.stringify(canonicalize(value));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function configSource(workspaceRoot, file) {
|
|
333
|
+
return relativePath(workspaceRoot, file);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function resolveDocsConfig(workspaceRoot, options = {}) {
|
|
337
|
+
const resolvedRoot = workspaceRoot || getWorkspaceRoot();
|
|
338
|
+
if (!resolvedRoot) throw new Error('cannot resolve project docs outside a git workspace; pass a workspace root explicitly');
|
|
339
|
+
const root = path.resolve(resolvedRoot);
|
|
340
|
+
const projectFile = projectDocsConfigPath(root);
|
|
341
|
+
if (fs.existsSync(projectFile)) {
|
|
342
|
+
const source = configSource(root, projectFile);
|
|
343
|
+
let config;
|
|
344
|
+
try {
|
|
345
|
+
config = JSON.parse(fs.readFileSync(projectFile, 'utf8'));
|
|
346
|
+
} catch {
|
|
347
|
+
return {
|
|
348
|
+
status: 'invalid',
|
|
349
|
+
sources: [source],
|
|
350
|
+
config: null,
|
|
351
|
+
warnings: [],
|
|
352
|
+
diagnostics: [{ code: 'invalid-project-config-json', severity: 'error', path: source, message: 'project-docs.json must contain valid JSON' }]
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const validation = validateDocsConfig(config);
|
|
357
|
+
const diagnostics = validation.diagnostics.map(item => ({ ...item, path: source }));
|
|
358
|
+
const warnings = validation.warnings.map(item => ({ ...item, path: source }));
|
|
359
|
+
if (!validation.valid) {
|
|
360
|
+
return { status: 'invalid', sources: [source], config: null, warnings, diagnostics };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const hosts = inspectHostProjectDocs(root);
|
|
364
|
+
const hostDiagnostics = hosts.flatMap(entry => entry.diagnostics);
|
|
365
|
+
const hostWarnings = hosts.flatMap(entry => entry.warnings);
|
|
366
|
+
const validHosts = hosts.filter(entry => entry.projectDocs);
|
|
367
|
+
const differing = validHosts.filter(entry => canonicalJson(entry.projectDocs) !== canonicalJson(config));
|
|
368
|
+
if (differing.length) {
|
|
369
|
+
warnings.push({
|
|
370
|
+
code: 'project-overrides-host',
|
|
371
|
+
severity: 'warning',
|
|
372
|
+
path: source,
|
|
373
|
+
message: 'project-docs.json wholly overrides differing host projectDocs values',
|
|
374
|
+
candidates: differing.map(entry => configSource(root, entry.file))
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
return { status: 'project', sources: [source], config, warnings: [...warnings, ...hostWarnings], diagnostics: [...diagnostics, ...hostDiagnostics] };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const hosts = inspectHostProjectDocs(root);
|
|
381
|
+
const hostDiagnostics = hosts.flatMap(entry => entry.diagnostics);
|
|
382
|
+
const hostWarnings = hosts.flatMap(entry => entry.warnings);
|
|
383
|
+
const activeHome = normalizedHostHome(options.activeHost);
|
|
384
|
+
if (activeHome) {
|
|
385
|
+
const activeHost = hosts.find(entry => entry.home === activeHome);
|
|
386
|
+
const activeFile = activeHost ? activeHost.file : path.join(root, activeHome, 'harness-everything', 'manifest.json');
|
|
387
|
+
const activeSource = configSource(root, activeFile);
|
|
388
|
+
if (activeHost && activeHost.diagnostics.length) {
|
|
389
|
+
return {
|
|
390
|
+
status: 'invalid',
|
|
391
|
+
sources: [activeSource],
|
|
392
|
+
config: null,
|
|
393
|
+
warnings: [...hostWarnings],
|
|
394
|
+
diagnostics: [...hostDiagnostics]
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (!activeHost || !activeHost.hasProjectDocs) {
|
|
398
|
+
return {
|
|
399
|
+
status: 'none',
|
|
400
|
+
sources: [activeSource],
|
|
401
|
+
config: null,
|
|
402
|
+
warnings: [
|
|
403
|
+
...hostWarnings,
|
|
404
|
+
{
|
|
405
|
+
code: 'missing-project-docs-config',
|
|
406
|
+
severity: 'warning',
|
|
407
|
+
path: activeSource,
|
|
408
|
+
message: `active host has no projectDocs config; add .agents/harness-everything/project-docs.json`
|
|
409
|
+
}
|
|
410
|
+
],
|
|
411
|
+
diagnostics: [...hostDiagnostics]
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
status: 'host',
|
|
416
|
+
sources: [activeSource],
|
|
417
|
+
config: activeHost.projectDocs,
|
|
418
|
+
warnings: [...hostWarnings],
|
|
419
|
+
diagnostics: [...hostDiagnostics]
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const validHosts = hosts.filter(entry => entry.projectDocs);
|
|
424
|
+
if (!validHosts.length) return { status: 'none', sources: [], config: null, warnings: hostWarnings, diagnostics: hostDiagnostics };
|
|
425
|
+
const sources = validHosts.map(entry => configSource(root, entry.file));
|
|
426
|
+
const firstConfig = validHosts[0].projectDocs;
|
|
427
|
+
const equal = validHosts.every(entry => canonicalJson(entry.projectDocs) === canonicalJson(firstConfig));
|
|
428
|
+
if (equal) return { status: 'host', sources, config: firstConfig, warnings: hostWarnings, diagnostics: hostDiagnostics };
|
|
429
|
+
return {
|
|
430
|
+
status: 'ambiguous',
|
|
431
|
+
sources,
|
|
432
|
+
config: null,
|
|
433
|
+
warnings: hostWarnings,
|
|
434
|
+
diagnostics: [{
|
|
435
|
+
code: 'ambiguous-host-project-docs',
|
|
436
|
+
severity: 'error',
|
|
437
|
+
path: sources[0],
|
|
438
|
+
message: 'host projectDocs configurations differ and no active host selects one',
|
|
439
|
+
candidates: sources
|
|
440
|
+
}, ...hostDiagnostics]
|
|
441
|
+
};
|
|
80
442
|
}
|
|
81
443
|
|
|
82
444
|
function cleanConfiguredPath(value) {
|
|
@@ -230,8 +592,9 @@ function resolveProjectDocs(workspaceRoot, options = {}) {
|
|
|
230
592
|
const resolvedRoot = workspaceRoot || getWorkspaceRoot();
|
|
231
593
|
if (!resolvedRoot) throw new Error('cannot resolve project docs outside a git workspace; pass --workspace <path> explicitly');
|
|
232
594
|
const root = path.resolve(resolvedRoot);
|
|
233
|
-
const manifest = readProjectDocs(root);
|
|
234
|
-
const
|
|
595
|
+
const manifest = readProjectDocs(root, options);
|
|
596
|
+
const hasValidConfig = manifest.status === 'project' || manifest.status === 'host';
|
|
597
|
+
const projectDocs = hasValidConfig ? manifest.projectDocs || {} : {};
|
|
235
598
|
const contextMap = parseContextMap(root, projectDocs.contextMap || projectDocs.contextMapPath);
|
|
236
599
|
const paths = {};
|
|
237
600
|
|
|
@@ -272,7 +635,11 @@ function resolveProjectDocs(workspaceRoot, options = {}) {
|
|
|
272
635
|
logs: path.join(runtimeRoot, 'logs'),
|
|
273
636
|
roles: path.join(runtimeRoot, 'roles')
|
|
274
637
|
},
|
|
275
|
-
|
|
638
|
+
projectDocsStatus: manifest.status,
|
|
639
|
+
projectDocsSources: manifest.sources,
|
|
640
|
+
projectDocsWarnings: manifest.warnings,
|
|
641
|
+
projectDocsDiagnostics: manifest.diagnostics,
|
|
642
|
+
projectDocs: hasValidConfig && manifest.file ? { ...projectDocs, manifestPath: manifest.file } : null,
|
|
276
643
|
contextMap: contextMap ? { path: contextMap.path, links: contextMap.links } : null,
|
|
277
644
|
paths,
|
|
278
645
|
// Plural aliases make the contract readable to callers that treat these
|
|
@@ -283,14 +650,40 @@ function resolveProjectDocs(workspaceRoot, options = {}) {
|
|
|
283
650
|
};
|
|
284
651
|
}
|
|
285
652
|
|
|
286
|
-
function detectProjectDocsConventions(workspaceRoot) {
|
|
653
|
+
function detectProjectDocsConventions(workspaceRoot, options = {}) {
|
|
654
|
+
const REQUIRED_CONFIG_FIELDS = ['docLocation', 'tracker', 'issueDefinition'];
|
|
287
655
|
const resolvedRoot = workspaceRoot || getWorkspaceRoot();
|
|
288
656
|
if (!resolvedRoot) return {};
|
|
289
657
|
const root = path.resolve(resolvedRoot);
|
|
290
|
-
const
|
|
658
|
+
const selection = resolveDocsConfig(root, options);
|
|
659
|
+
if (selection.status === 'invalid' || selection.status === 'ambiguous') {
|
|
660
|
+
return {
|
|
661
|
+
status: selection.status,
|
|
662
|
+
sources: selection.sources || [],
|
|
663
|
+
warnings: selection.warnings || [],
|
|
664
|
+
diagnostics: selection.diagnostics || [],
|
|
665
|
+
missing: []
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
const configured = selection.config || {};
|
|
291
669
|
const inferred = {};
|
|
292
|
-
|
|
293
|
-
|
|
670
|
+
for (const field of REQUIRED_CONFIG_FIELDS) {
|
|
671
|
+
if (typeof configured[field] === 'string' && configured[field].trim()) inferred[field] = configured[field];
|
|
672
|
+
}
|
|
673
|
+
// Heuristic completion is a resource for UNSELECTED workspaces only: a
|
|
674
|
+
// project or selected-host configuration is authoritative for exactly the
|
|
675
|
+
// fields it declares, so missing selected fields are reported instead of
|
|
676
|
+
// being silently authorized from workspace conventions.
|
|
677
|
+
if (selection.status !== 'none') {
|
|
678
|
+
const missing = REQUIRED_CONFIG_FIELDS.filter(field => !inferred[field]);
|
|
679
|
+
return {
|
|
680
|
+
...inferred,
|
|
681
|
+
status: selection.status,
|
|
682
|
+
sources: selection.sources || [],
|
|
683
|
+
warnings: selection.warnings || [],
|
|
684
|
+
diagnostics: selection.diagnostics || [],
|
|
685
|
+
missing
|
|
686
|
+
};
|
|
294
687
|
}
|
|
295
688
|
if (!inferred.docLocation) {
|
|
296
689
|
for (const dir of ['docs/roadmaps', 'docs/reference', 'docs/specs', 'docs', 'doc']) {
|
|
@@ -300,23 +693,35 @@ function detectProjectDocsConventions(workspaceRoot) {
|
|
|
300
693
|
}
|
|
301
694
|
}
|
|
302
695
|
}
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
696
|
+
if (!inferred.tracker) {
|
|
697
|
+
if (pathIsDir(path.join(root, '.github', 'ISSUE_TEMPLATE'))) {
|
|
698
|
+
inferred.tracker = 'GitHub Issues via .github/ISSUE_TEMPLATE/';
|
|
699
|
+
} else if (pathIsDir(path.join(root, 'docs', 'roadmaps'))) {
|
|
700
|
+
inferred.tracker = 'docs/roadmaps/ local markdown tickets';
|
|
701
|
+
} else if (pathIsDir(path.join(root, 'tasks', 'tickets'))) {
|
|
702
|
+
inferred.tracker = 'tasks/tickets/ local markdown tickets';
|
|
703
|
+
} else if (pathIsDir(path.join(root, '.scratch'))) {
|
|
704
|
+
inferred.tracker = '.scratch/ local markdown workspace';
|
|
705
|
+
} else if (inferred.docLocation) {
|
|
706
|
+
inferred.tracker = `${inferred.docLocation}tickets/ local markdown tickets`;
|
|
707
|
+
}
|
|
313
708
|
}
|
|
314
|
-
if (inferred.
|
|
315
|
-
inferred.
|
|
316
|
-
|
|
317
|
-
|
|
709
|
+
if (!inferred.issueDefinition) {
|
|
710
|
+
if (inferred.tracker && inferred.tracker.includes('ISSUE_TEMPLATE')) {
|
|
711
|
+
inferred.issueDefinition = '.github/ISSUE_TEMPLATE/ template structure';
|
|
712
|
+
} else if (inferred.tracker) {
|
|
713
|
+
inferred.issueDefinition = 'Title + acceptance criteria + Status: ready-for-agent';
|
|
714
|
+
}
|
|
318
715
|
}
|
|
319
|
-
|
|
716
|
+
const missing = REQUIRED_CONFIG_FIELDS.filter(field => !inferred[field]);
|
|
717
|
+
return {
|
|
718
|
+
...inferred,
|
|
719
|
+
status: selection.status,
|
|
720
|
+
sources: selection.sources || [],
|
|
721
|
+
warnings: selection.warnings || [],
|
|
722
|
+
diagnostics: selection.diagnostics || [],
|
|
723
|
+
missing
|
|
724
|
+
};
|
|
320
725
|
}
|
|
321
726
|
|
|
322
727
|
function isIgnored(workspaceRoot, target) {
|
|
@@ -347,6 +752,8 @@ module.exports = {
|
|
|
347
752
|
getRuntimeRoot,
|
|
348
753
|
getRepoManifestHomes,
|
|
349
754
|
readProjectDocs,
|
|
755
|
+
resolveDocsConfig,
|
|
756
|
+
validateDocsConfig,
|
|
350
757
|
parseContextMap,
|
|
351
758
|
resolveProjectDocs,
|
|
352
759
|
detectProjectDocsConventions,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "harness-everything",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.35.0",
|
|
5
5
|
"description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Miya Daniel",
|