harness-everything 0.2.1 → 0.3.6
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 +27 -0
- package/.claude-plugin/plugin.json +51 -0
- package/.gitattributes +1 -0
- package/.github/workflows/behavioral-evals.yml +36 -0
- package/.github/workflows/ci.yml +117 -0
- package/.github/workflows/release.yml +45 -0
- package/.husky/commit-msg +1 -0
- package/.waza.yaml +22 -0
- package/AGENTS.md +60 -2
- package/BENCHMARK_SOP.md +62 -62
- package/CHANGELOG.md +297 -114
- package/CODE_OF_CONDUCT.md +24 -0
- package/CONTRIBUTING.md +47 -0
- package/Dockerfile +18 -18
- package/LICENSE +201 -201
- package/README.md +279 -197
- package/SECURITY.md +33 -0
- package/VERIFICATION.md +394 -347
- package/ab-test-deep-analysis-2026-08-27.md +383 -0
- package/ab-test-results-2026-08-27.md +152 -0
- package/ab-test-statistical-analysis-31cases.md +193 -0
- package/behavioral-evals/README.md +85 -0
- package/behavioral-evals/analyze-plugin-results.js +110 -0
- package/behavioral-evals/cases/baseline-add-feature.yaml +34 -0
- package/behavioral-evals/cases/baseline-api-design.yaml +32 -0
- package/behavioral-evals/cases/baseline-debugging.yaml +31 -0
- package/behavioral-evals/cases/baseline-dependency.yaml +24 -0
- package/behavioral-evals/cases/baseline-documentation.yaml +34 -0
- package/behavioral-evals/cases/baseline-error-handling.yaml +31 -0
- package/behavioral-evals/cases/baseline-git-commit.yaml +33 -0
- package/behavioral-evals/cases/baseline-performance-monitoring.yaml +28 -0
- package/behavioral-evals/cases/baseline-performance.yaml +34 -0
- package/behavioral-evals/cases/baseline-refactor-with-tests.yaml +35 -0
- package/behavioral-evals/cases/baseline-refactor.yaml +27 -0
- package/behavioral-evals/cases/baseline-security-review.yaml +28 -0
- package/behavioral-evals/cases/baseline-simple-bugfix.yaml +33 -0
- package/behavioral-evals/cases/baseline-tdd-workflow.yaml +28 -0
- package/behavioral-evals/cases/breaker-zoom-out-after-3.yaml +50 -0
- package/behavioral-evals/cases/pressure-budget-skip-review.yaml +27 -0
- package/behavioral-evals/cases/pressure-bypass-review.yaml +27 -0
- package/behavioral-evals/cases/pressure-complexity-skip.yaml +37 -0
- package/behavioral-evals/cases/pressure-expert-skip-testing.yaml +31 -0
- package/behavioral-evals/cases/pressure-fatigue-skip.yaml +33 -0
- package/behavioral-evals/cases/pressure-management-skip-all.yaml +28 -0
- package/behavioral-evals/cases/pressure-scope-bypass.yaml +28 -0
- package/behavioral-evals/cases/pressure-skip-docs.yaml +50 -0
- package/behavioral-evals/cases/pressure-skip-error-handling.yaml +35 -0
- package/behavioral-evals/cases/pressure-skip-security.yaml +32 -0
- package/behavioral-evals/cases/pressure-skip-tests.yaml +27 -0
- package/behavioral-evals/cases/pressure-skip-verification.yaml +43 -0
- package/behavioral-evals/cases/pressure-social-skip-review.yaml +28 -0
- package/behavioral-evals/cases/pressure-sunk-cost-retry.yaml +47 -0
- package/behavioral-evals/cases/scope-discipline.yaml +32 -0
- package/behavioral-evals/cases/tier1-no-overplan.yaml +26 -0
- package/behavioral-evals/cases/verify-before-done.yaml +31 -0
- package/behavioral-evals/results/.gitkeep +0 -0
- package/behavioral-evals/results/2026-08-23-breaker-zoom-out-after-3.json +20 -0
- package/behavioral-evals/results/2026-08-23-pressure-skip-verification.json +20 -0
- package/behavioral-evals/results/2026-08-23-pressure-sunk-cost-retry.json +20 -0
- package/behavioral-evals/results/2026-08-23-scope-discipline.json +16 -0
- package/behavioral-evals/results/2026-08-23-tier1-no-overplan.json +20 -0
- package/behavioral-evals/results/2026-08-23-verify-before-done.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-add-feature.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-api-design.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-debugging.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-dependency.json +16 -0
- package/behavioral-evals/results/2026-08-27-baseline-documentation.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-error-handling.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-git-commit.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-performance-monitoring.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-performance.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-refactor-with-tests.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-refactor.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-security-review.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-simple-bugfix.json +20 -0
- package/behavioral-evals/results/2026-08-27-baseline-tdd-workflow.json +24 -0
- package/behavioral-evals/results/2026-08-27-breaker-zoom-out-after-3.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-budget-skip-review.json +16 -0
- package/behavioral-evals/results/2026-08-27-pressure-bypass-review.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-complexity-skip.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-expert-skip-testing.json +16 -0
- package/behavioral-evals/results/2026-08-27-pressure-fatigue-skip.json +16 -0
- package/behavioral-evals/results/2026-08-27-pressure-management-skip-all.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-skip-docs.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-skip-error-handling.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-skip-security.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-skip-tests.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-skip-verification.json +24 -0
- package/behavioral-evals/results/2026-08-27-pressure-social-skip-review.json +20 -0
- package/behavioral-evals/results/2026-08-27-pressure-sunk-cost-retry.json +24 -0
- package/behavioral-evals/results/2026-08-27-scope-discipline.json +16 -0
- package/behavioral-evals/results/2026-08-27-tier1-no-overplan.json +20 -0
- package/behavioral-evals/results/2026-08-27-verify-before-done.json +20 -0
- package/behavioral-evals/results/run-20260827-115509.log +287 -0
- package/behavioral-evals/run-with-plugin.js +316 -0
- package/behavioral-evals/run.js +587 -0
- package/benchmarks/README.md +63 -0
- package/benchmarks/ab-test-2026-08-26/results.json +55 -0
- package/benchmarks/results/logs/.gitkeep +0 -0
- package/benchmarks/results/logs/test-a-harness.md +31 -0
- package/benchmarks/results/logs/test-a-vanilla.md +28 -0
- package/benchmarks/results/logs/test-b-micro-loop-harness.md +48 -0
- package/benchmarks/results/logs/test-b-micro-loop-vanilla.md +36 -0
- package/benchmarks/results/logs/test-c-attention-loss-harness.md +40 -0
- package/benchmarks/results/logs/test-c-attention-loss-vanilla.md +63 -0
- package/benchmarks/results/logs/test-d-knowledge-boundary-harness.md +24 -0
- package/benchmarks/results/logs/test-d-knowledge-boundary-vanilla.md +29 -0
- package/benchmarks/results/logs/test-e-shell-awareness-harness.md +20 -0
- package/benchmarks/results/logs/test-e-shell-awareness-vanilla.md +20 -0
- package/benchmarks/results/logs/test-f-fact-audit-harness.md +16 -0
- package/benchmarks/results/logs/test-f-fact-audit-vanilla.md +16 -0
- package/benchmarks/results/results.json +158 -0
- package/benchmarks/run.js +220 -0
- package/benchmarks/schema.json +44 -0
- package/bin/cli.js +152 -97
- package/ci/ab-test-cases/grill-me-adversarial.yaml +18 -0
- package/ci/ab-test-cases/tdd-test-first.yaml +23 -0
- package/ci/ab-test-cases/verification-loop-gates.yaml +25 -0
- package/ci/ab-test-cases/verify-before-claim-cites.yaml +18 -0
- package/ci/ab-test-harness.js +410 -0
- package/{eval-framework → ci}/cases/case1.json +5 -5
- package/ci/cases/case10-small-feature.json +5 -0
- package/ci/cases/case11-readme-typo-update.json +5 -0
- package/ci/cases/case12-chinese-macro-audit.json +5 -0
- package/{eval-framework → ci}/cases/case2.json +5 -5
- package/{eval-framework → ci}/cases/case3.json +5 -5
- package/{eval-framework → ci}/cases/case4-zh-tier2.json +5 -5
- package/{eval-framework → ci}/cases/case5-zh-tier3.json +5 -5
- package/ci/cases/case6-macro-skill-audit.json +5 -0
- package/ci/cases/case7-macro-reference-audit.json +5 -0
- package/ci/cases/case8-controlled-ab-benchmark.json +5 -0
- package/ci/cases/case9-four-issue-audit.json +5 -0
- package/ci/consistency-check.js +347 -0
- package/ci/description-collision.js +109 -0
- package/ci/fable-mode-test.js +120 -0
- package/ci/fixtures/agency-agents/divisions.json +6 -0
- package/ci/fixtures/agency-agents/engineering/code-reviewer.md +11 -0
- package/ci/fixtures/agency-agents/testing/reality-checker.md +11 -0
- package/ci/fixtures/bad-frontmatter-colon/SKILL.md +16 -0
- package/ci/fixtures/duplicate-description/skill-a/SKILL.md +16 -0
- package/ci/fixtures/duplicate-description/skill-b/SKILL.md +16 -0
- package/ci/mechanism-2a-rule-of-3.test.js +46 -0
- package/ci/mechanism-2b-boundary-guard.test.js +16 -0
- package/ci/mechanism-2c-state-persist.test.js +29 -0
- package/ci/mechanism-2d-fact-audit.test.js +18 -0
- package/ci/mechanism-2e-scope-guard.test.js +24 -0
- package/ci/mechanism-2f-stop-gate.test.js +23 -0
- package/ci/mechanism-2g-platform-ignore.test.js +88 -0
- package/ci/mechanism-2h-installer-manifest.test.js +147 -0
- package/ci/mechanism-2i-crlf-frontmatter.test.js +145 -0
- package/ci/mechanism-2i-install-verification.test.js +34 -0
- package/ci/mechanism-2j-behavioral-runner.test.js +20 -0
- package/ci/mechanism-2k-reference-check.test.js +20 -0
- package/ci/mechanism-2l-multi-agent-workspace.test.js +105 -0
- package/ci/mechanism-test.js +71 -0
- package/ci/negative-controls.js +84 -0
- package/ci/reference-check.js +112 -0
- package/ci/release-consistency-check.js +123 -0
- package/ci/routing-check.js +90 -0
- package/{eval-framework → ci}/runner.js +56 -56
- package/ci/skill-routing-check.js +98 -0
- package/ci/test-helper.js +104 -0
- package/ci/test-negative-controls.js +148 -0
- package/commitlint.config.js +25 -0
- package/docker-verify.sh +266 -266
- package/docs/architecture.md +99 -95
- package/docs/audit.md +53 -0
- package/docs/mechanism-first-skill-mesh.md +163 -0
- package/docs/philosophy.md +47 -45
- package/docs/reflection.md +48 -34
- package/docs/routing.md +58 -45
- package/docs/troubleshooting.md +284 -0
- package/docs/workflows/environment-detection.md +63 -63
- package/docs/workflows/eval-harness.md +64 -57
- package/docs/workflows/fable-discipline.md +59 -59
- package/docs/workflows/fable-mode.md +12 -15
- package/docs/workflows/find-skills.md +36 -0
- package/docs/workflows/git-commit.md +71 -62
- package/docs/workflows/grill-me.md +62 -62
- package/docs/workflows/grill-with-docs.md +69 -62
- package/docs/workflows/harness-everything.md +95 -92
- package/docs/workflows/improve-codebase-architecture.md +68 -60
- package/docs/workflows/install-cognitive-os.md +60 -60
- package/docs/workflows/multi-agent-workspace.md +42 -0
- package/docs/workflows/repo-docs.md +64 -59
- package/docs/workflows/rewrite-commits.md +60 -60
- package/docs/workflows/security-review.md +77 -58
- package/docs/workflows/self-evolve.md +42 -59
- package/docs/workflows/skill-creator.md +80 -75
- package/docs/workflows/skill-style.md +64 -64
- package/docs/workflows/tdd.md +7 -4
- package/docs/workflows/to-spec.md +83 -0
- package/docs/workflows/to-tickets.md +80 -0
- package/docs/workflows/todo-driven-workflow.md +28 -30
- package/docs/workflows/using-git-worktrees.md +67 -63
- package/docs/workflows/verification-loop.md +71 -69
- package/docs/workflows/verify-before-claim.md +64 -60
- package/docs/workflows/zoom-out.md +74 -62
- package/environment-detection/SKILL.md +41 -77
- package/environment-detection/references/shell-syntax-rules.md +76 -0
- package/environment-detection/scripts/preflight.js +103 -93
- package/eval-framework/fixtures/bad-frontmatter-colon/SKILL.md +14 -0
- package/eval-framework/fixtures/duplicate-desc-skill-a/SKILL.md +14 -0
- package/eval-framework/fixtures/duplicate-desc-skill-b/SKILL.md +14 -0
- package/eval-framework/fixtures/missing-delimiter/SKILL.md +13 -0
- package/eval-framework/fixtures/over-budget-skill/SKILL.md +36 -0
- package/eval-harness/SKILL.md +42 -59
- package/eval-harness/references/scoring-rubric.md +68 -0
- package/eval-harness/scripts/evaluate.js +77 -61
- package/evals/environment-detection/eval.yaml +26 -0
- package/evals/environment-detection/tasks/negative-trigger.yaml +16 -0
- package/evals/environment-detection/tasks/positive-trigger.yaml +15 -0
- package/evals/eval-harness/eval.yaml +26 -0
- package/evals/eval-harness/tasks/negative-trigger.yaml +16 -0
- package/evals/eval-harness/tasks/positive-trigger.yaml +15 -0
- package/evals/fable-discipline/eval.yaml +26 -0
- package/evals/fable-discipline/tasks/negative-trigger.yaml +16 -0
- package/evals/fable-discipline/tasks/positive-trigger.yaml +15 -0
- package/evals/fable-mode/eval.yaml +26 -0
- package/evals/fable-mode/tasks/negative-tier2-no-model.yaml +18 -0
- package/evals/fable-mode/tasks/negative-trigger.yaml +16 -0
- package/evals/fable-mode/tasks/positive-haiku-mode.yaml +16 -0
- package/evals/fable-mode/tasks/positive-opus-mode.yaml +16 -0
- package/evals/fable-mode/tasks/positive-sonnet-mode.yaml +16 -0
- package/evals/fable-mode/tasks/positive-trigger.yaml +15 -0
- package/evals/find-skills/eval.yaml +26 -0
- package/evals/find-skills/tasks/negative-trigger.yaml +16 -0
- package/evals/find-skills/tasks/positive-trigger.yaml +15 -0
- package/evals/git-commit/eval.yaml +26 -0
- package/evals/git-commit/tasks/negative-trigger.yaml +16 -0
- package/evals/git-commit/tasks/positive-trigger.yaml +15 -0
- package/evals/grill-me/eval.yaml +26 -0
- package/evals/grill-me/tasks/negative-trigger.yaml +16 -0
- package/evals/grill-me/tasks/positive-trigger.yaml +15 -0
- package/evals/grill-with-docs/eval.yaml +26 -0
- package/evals/grill-with-docs/tasks/negative-trigger.yaml +16 -0
- package/evals/grill-with-docs/tasks/positive-trigger.yaml +15 -0
- package/evals/harness-everything/eval.yaml +26 -0
- package/evals/harness-everything/tasks/negative-non-software.yaml +16 -0
- package/evals/harness-everything/tasks/negative-skill-indicated.yaml +15 -0
- package/evals/harness-everything/tasks/positive-tier1.yaml +16 -0
- package/evals/harness-everything/tasks/positive-tier2.yaml +16 -0
- package/evals/harness-everything/tasks/positive-tier3.yaml +16 -0
- package/evals/improve-codebase-architecture/eval.yaml +26 -0
- package/evals/improve-codebase-architecture/tasks/negative-trigger.yaml +16 -0
- package/evals/improve-codebase-architecture/tasks/positive-trigger.yaml +15 -0
- package/evals/install-cognitive-os/eval.yaml +26 -0
- package/evals/install-cognitive-os/tasks/negative-trigger.yaml +16 -0
- package/evals/install-cognitive-os/tasks/positive-trigger.yaml +15 -0
- package/evals/multi-agent-workspace/eval.yaml +26 -0
- package/evals/multi-agent-workspace/tasks/negative-trigger.yaml +15 -0
- package/evals/multi-agent-workspace/tasks/positive-trigger.yaml +15 -0
- package/evals/repo-docs/eval.yaml +26 -0
- package/evals/repo-docs/tasks/negative-trigger.yaml +16 -0
- package/evals/repo-docs/tasks/positive-trigger.yaml +15 -0
- package/evals/rewrite-commits/eval.yaml +26 -0
- package/evals/rewrite-commits/tasks/negative-trigger.yaml +16 -0
- package/evals/rewrite-commits/tasks/positive-trigger.yaml +15 -0
- package/evals/security-review/eval.yaml +26 -0
- package/evals/security-review/tasks/negative-trigger.yaml +16 -0
- package/evals/security-review/tasks/positive-trigger.yaml +15 -0
- package/evals/self-evolve/eval.yaml +26 -0
- package/evals/self-evolve/tasks/negative-trigger.yaml +16 -0
- package/evals/self-evolve/tasks/positive-trigger.yaml +15 -0
- package/evals/skill-creator/eval.yaml +26 -0
- package/evals/skill-creator/tasks/negative-trigger.yaml +16 -0
- package/evals/skill-creator/tasks/positive-trigger.yaml +15 -0
- package/evals/skill-style/eval.yaml +26 -0
- package/evals/skill-style/tasks/negative-trigger.yaml +16 -0
- package/evals/skill-style/tasks/positive-trigger.yaml +15 -0
- package/evals/tdd/eval.yaml +26 -0
- package/evals/tdd/tasks/negative-trivia.yaml +16 -0
- package/evals/tdd/tasks/positive-bugfix.yaml +15 -0
- package/evals/tdd/tasks/positive-feature.yaml +16 -0
- package/evals/tdd/tasks/positive-refactor.yaml +15 -0
- package/evals/to-spec/eval.yaml +26 -0
- package/evals/to-spec/tasks/negative-trigger.yaml +16 -0
- package/evals/to-spec/tasks/positive-trigger.yaml +15 -0
- package/evals/to-tickets/eval.yaml +26 -0
- package/evals/to-tickets/tasks/negative-trigger.yaml +15 -0
- package/evals/to-tickets/tasks/positive-trigger.yaml +15 -0
- package/evals/todo-driven-workflow/eval.yaml +26 -0
- package/evals/todo-driven-workflow/tasks/negative-single-edit.yaml +16 -0
- package/evals/todo-driven-workflow/tasks/positive-multi-step.yaml +15 -0
- package/evals/todo-driven-workflow/tasks/positive-refactor-flow.yaml +15 -0
- package/evals/using-git-worktrees/eval.yaml +26 -0
- package/evals/using-git-worktrees/tasks/negative-trigger.yaml +15 -0
- package/evals/using-git-worktrees/tasks/positive-trigger.yaml +15 -0
- package/evals/verification-loop/eval.yaml +26 -0
- package/evals/verification-loop/tasks/negative-discussion.yaml +16 -0
- package/evals/verification-loop/tasks/positive-pre-ship.yaml +15 -0
- package/evals/verify-before-claim/eval.yaml +26 -0
- package/evals/verify-before-claim/tasks/negative-trigger.yaml +16 -0
- package/evals/verify-before-claim/tasks/positive-trigger.yaml +15 -0
- package/evals/zoom-out/eval.yaml +26 -0
- package/evals/zoom-out/tasks/negative-trivial-edit.yaml +16 -0
- package/evals/zoom-out/tasks/positive-repeated-failure.yaml +15 -0
- package/fable-discipline/SKILL.md +41 -38
- package/fable-discipline/references/discipline-rules.md +22 -0
- package/fable-mode/CONTRACT-FORMAT.md +51 -51
- package/fable-mode/SKILL.md +39 -51
- package/fable-mode/agents/fable-orchestrator.md +99 -92
- package/fable-mode/agents/fable-verifier.md +32 -32
- package/fable-mode/agents/fable-worker-haiku.md +52 -48
- package/fable-mode/agents/fable-worker-sonnet.md +53 -49
- package/fable-mode/execution-guardrails/SKILL.md +43 -87
- package/fable-mode/execution-guardrails/references/guardrail-rules.md +56 -0
- package/fable-mode/fable-haiku/SKILL.md +36 -48
- package/fable-mode/fable-haiku/references/routing-notes.md +26 -0
- package/fable-mode/fable-opus/SKILL.md +36 -0
- package/fable-mode/fable-sonnet/SKILL.md +36 -0
- package/fable-mode/model-matrix.json +32 -0
- package/fable-mode/references/execution-phases.md +37 -0
- package/fable-mode/references/model-matrix.md +39 -0
- package/fable-mode/scripts/model-selector.js +129 -0
- package/find-skills/SKILL.md +48 -0
- package/find-skills/references/discovery-flow.md +102 -0
- package/find-skills/scripts/use-skill.js +106 -0
- package/git-commit/SKILL.md +45 -55
- package/git-commit/guides/ANGULAR_STYLE.md +60 -60
- package/git-commit/guides/COMMIT_GENERATION.md +23 -23
- package/git-commit/guides/LANGUAGE_DETECTION.md +16 -16
- package/git-commit/guides/MAIN_REPO.md +14 -14
- package/git-commit/guides/SUBMODULES.md +27 -27
- package/git-commit/references/commit-flow.md +30 -0
- package/grill-me/SKILL.md +43 -36
- package/grill-me/references/grilling-playbook.md +38 -0
- package/grill-with-docs/ADR-FORMAT.md +47 -47
- package/grill-with-docs/CONTEXT-FORMAT.md +63 -63
- package/grill-with-docs/SKILL.md +41 -99
- package/grill-with-docs/references/session-playbook.md +97 -0
- package/harness-everything/SKILL.md +42 -141
- package/harness-everything/references/skill-registry.md +39 -0
- package/harness-everything/references/triage-and-tiers.md +116 -0
- package/harness-everything/scripts/bootstrap.js +203 -140
- package/harness-everything/scripts/routing-keywords.json +320 -0
- package/harness-everything/scripts/self-heal.js +241 -189
- package/harness-everything/scripts/tier-router.js +334 -219
- package/harness-everything/scripts/verify-gate.js +87 -18
- package/hooks/hooks.json +155 -155
- package/hooks/scripts/boundary-guard.js +55 -55
- package/hooks/scripts/contract-test.js +79 -79
- package/hooks/scripts/depth-guard.js +65 -65
- package/hooks/scripts/lib/harness-state.js +139 -228
- package/hooks/scripts/lib/platforms/claude.js +185 -42
- package/hooks/scripts/lib/platforms/codex.js +114 -46
- package/hooks/scripts/lib/platforms/continue.js +91 -41
- package/hooks/scripts/lib/platforms/copilot.js +109 -41
- package/hooks/scripts/lib/platforms/cursor.js +89 -41
- package/hooks/scripts/lib/platforms/hermes.js +57 -32
- package/hooks/scripts/lib/platforms/index.js +17 -17
- package/hooks/scripts/lib/platforms/worktrees.js +20 -20
- package/hooks/scripts/reset-circuit-breaker.js +30 -30
- package/hooks/scripts/rule-of-3-tracker.js +124 -91
- package/hooks/scripts/rule-of-3.js +14 -3
- package/hooks/scripts/state-persist.js +99 -99
- package/hooks/scripts/stop-gate.js +80 -80
- package/hooks/scripts/subagent-scope-guard.js +88 -88
- package/improve-codebase-architecture/SKILL.md +40 -44
- package/improve-codebase-architecture/guides/DEEPENING.md +37 -37
- package/improve-codebase-architecture/guides/HTML-REPORT.md +123 -123
- package/improve-codebase-architecture/guides/INTERFACE-DESIGN.md +44 -44
- package/improve-codebase-architecture/guides/LANGUAGE.md +53 -53
- package/improve-codebase-architecture/guides/refactoring-workflow.md +59 -0
- package/install-cognitive-os/SKILL.md +38 -95
- package/install-cognitive-os/references/cognitive-loop.md +27 -0
- package/multi-agent-workspace/NOTICE-agency-agents.md +27 -0
- package/multi-agent-workspace/SKILL.md +37 -0
- package/{create-agent-launcher → multi-agent-workspace}/examples/csharp-monorepo.md +123 -123
- package/{create-agent-launcher → multi-agent-workspace}/examples/typescript-cli.md +126 -126
- package/{create-agent-launcher → multi-agent-workspace}/guidelines/platform-claude.md +77 -77
- package/{create-agent-launcher → multi-agent-workspace}/guidelines/platform-codex.md +146 -146
- package/multi-agent-workspace/guidelines/platform-continue.md +38 -0
- package/{create-agent-launcher → multi-agent-workspace}/guidelines/platform-copilot.md +74 -74
- package/multi-agent-workspace/guidelines/platform-cursor.md +40 -0
- package/multi-agent-workspace/guidelines/platform-hermes.md +38 -0
- package/{create-agent-launcher → multi-agent-workspace}/guidelines/role-capabilities.md +108 -108
- package/multi-agent-workspace/references/agency-agents.md +31 -0
- package/multi-agent-workspace/references/architecture-guide.md +32 -0
- package/multi-agent-workspace/references/orchestration.md +48 -0
- package/multi-agent-workspace/scripts/agency-catalog.js +190 -0
- package/multi-agent-workspace/scripts/index_memory.js +51 -0
- package/multi-agent-workspace/scripts/scaffold.js +253 -0
- package/{create-agent-launcher → multi-agent-workspace}/templates/README.md +17 -17
- package/{create-agent-launcher → multi-agent-workspace}/templates/agent-launcher.md +67 -67
- package/{create-agent-launcher → multi-agent-workspace}/templates/backend-developer-csharp.md +55 -55
- package/{create-agent-launcher → multi-agent-workspace}/templates/backend-developer-polyglot.md +61 -61
- package/{create-agent-launcher → multi-agent-workspace}/templates/backend-developer-ts.md +56 -56
- package/{create-agent-launcher → multi-agent-workspace}/templates/memory-keeper.md +36 -36
- package/{create-agent-launcher → multi-agent-workspace}/templates/requirement-analyzer.md +65 -65
- package/{create-agent-launcher → multi-agent-workspace}/templates/specialist-agent.md +34 -34
- package/{create-agent-launcher → multi-agent-workspace}/templates/task-verifier.md +55 -55
- package/{create-agent-launcher → multi-agent-workspace}/templates/tool-maker.md +32 -32
- package/multi-agent-workspace/workflows/01-init.md +22 -0
- package/multi-agent-workspace/workflows/02-analysis.md +23 -0
- package/multi-agent-workspace/workflows/03-generation.md +27 -0
- package/multi-agent-workspace/workflows/04-launcher.md +16 -0
- package/opencode-plugin/README.md +94 -0
- package/opencode-plugin/hooks/circuit-breaker.js +121 -0
- package/opencode-plugin/hooks/compliance.js +128 -0
- package/opencode-plugin/hooks/post-edit.js +62 -0
- package/opencode-plugin/hooks/pre-complete.js +65 -0
- package/opencode-plugin/hooks/verify.js +140 -0
- package/opencode-plugin/plugin.json +17 -0
- package/opencode-plugin/test-hook-firing.js +198 -0
- package/package.json +50 -22
- package/references/definition-of-done.md +67 -0
- package/references/performance-checklist.md +153 -0
- package/references/platform-advisory-template.md +58 -0
- package/references/security-checklist.md +205 -0
- package/repo-docs/SKILL.md +46 -45
- package/repo-docs/references/process-guide.md +60 -0
- package/repo-docs/templates/agents-template.md +264 -264
- package/repo-docs/templates/knowledge-agents-template.md +50 -50
- package/repo-docs/templates/knowledge-readme-template.md +83 -83
- package/repo-docs/templates/manual-recon.md +117 -117
- package/repo-docs/templates/multi-skills-readme-template.md +72 -72
- package/repo-docs/templates/product-agents-template.md +78 -78
- package/repo-docs/templates/product-readme-template.md +95 -95
- package/repo-docs/templates/readme-template.md +136 -136
- package/repo-docs/templates/single-skill-readme-template.md +70 -70
- package/repo-docs/templates/skills-agents-template.md +60 -60
- package/rewrite-commits/SKILL.md +37 -32
- package/rewrite-commits/references/workflow.md +18 -0
- package/scripts/installer.js +546 -649
- package/scripts/lib/advisory-text.js +167 -163
- package/scripts/lib/claude-hooks.js +84 -71
- package/scripts/lib/gitignore.js +199 -80
- package/scripts/lib/manifest.js +146 -82
- package/scripts/lib/prompts.js +241 -241
- package/scripts/lib/skills.js +260 -172
- package/scripts/lib/workspace.js +28 -28
- package/scripts/verify-install.js +147 -0
- package/security-review/SKILL.md +50 -511
- package/security-review/guides/OWASP-PATTERNS.md +31 -0
- package/security-review/guides/STRIDE-THREAT-MODEL.md +21 -0
- package/security-review/references/security-checklist.md +478 -0
- package/security-review/scripts/audit-secrets.js +84 -0
- package/self-evolve/SKILL.md +47 -54
- package/self-evolve/references/memory-resolution.md +58 -0
- package/self-evolve/scripts/persist-memory.js +139 -40
- package/self-evolve/scripts/register-dynamic-skill.js +251 -0
- package/self-evolve/scripts/self-regression.js +110 -113
- package/skill-creator/SKILL.md +49 -91
- package/skill-creator/references/authoring-workflow.md +14 -0
- package/skill-creator/references/dynamic-generation-contract.md +21 -0
- package/skill-creator/references/quality-checklist.md +15 -0
- package/skill-creator/references/quality-principles.md +81 -81
- package/skill-creator/references/testing-workflow.md +20 -25
- package/skill-style/SKILL.md +42 -48
- package/skill-style/references/style-guide.md +36 -0
- package/tdd/SKILL.md +44 -53
- package/tdd/guides/deep-modules.md +33 -33
- package/tdd/guides/interface-design.md +31 -31
- package/tdd/guides/mocking.md +59 -59
- package/tdd/guides/refactoring.md +10 -10
- package/tdd/guides/tests.md +61 -61
- package/tdd/references/core-discipline.md +49 -0
- package/to-spec/SKILL.md +42 -0
- package/to-spec/references/process.md +77 -0
- package/to-spec/scripts/check-project-docs.js +259 -0
- package/to-spec/templates/cli-reference.md +31 -0
- package/to-spec/templates/dev-doc.md +27 -0
- package/to-spec/templates/feature-spec.md +59 -0
- package/to-spec/templates/schema-doc.md +31 -0
- package/to-tickets/SKILL.md +42 -0
- package/to-tickets/references/publishing-and-templates.md +97 -0
- package/todo-driven-workflow/SKILL.md +40 -54
- package/todo-driven-workflow/references/execution-guide.md +27 -0
- package/using-git-worktrees/SKILL.md +49 -161
- package/using-git-worktrees/references/workflow-details.md +108 -0
- package/verification-loop/SKILL.md +46 -138
- package/verification-loop/references/verification-phases.md +116 -0
- package/verification-loop/templates/verification-report.template.md +15 -0
- package/verify-before-claim/SKILL.md +43 -86
- package/verify-before-claim/references/verification-guide.md +73 -0
- package/zoom-out/SKILL.md +42 -90
- package/zoom-out/references/circuit-breaker.md +66 -0
- package/zoom-out/templates/zoom-out-report.template.md +20 -0
- package/build-multi-agent-system/AGENTS.md +0 -50
- package/build-multi-agent-system/README.md +0 -63
- package/build-multi-agent-system/SKILL.md +0 -68
- package/create-agent-launcher/SKILL.md +0 -48
- package/create-agent-launcher/guidelines/platform-gemini.md +0 -101
- package/create-agent-launcher/workflows/01-init.md +0 -22
- package/create-agent-launcher/workflows/02-analysis.md +0 -26
- package/create-agent-launcher/workflows/03-generation.md +0 -29
- package/create-agent-launcher/workflows/04-launcher.md +0 -20
- package/docs/workflows/build-multi-agent-system.md +0 -62
- package/docs/workflows/create-agent-launcher.md +0 -62
- package/eval-framework/behavioral-test.js +0 -105
- package/eval-framework/mechanism-test.js +0 -251
- package/harness-everything/scripts/todo-cli.js +0 -157
- package/skill-style/STYLE.md +0 -37
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Harness Consistency Check
|
|
4
|
+
* Validates that the docs surface, skill catalog, and distribution manifests
|
|
5
|
+
* tell the same story. A stale manifest or a dead README link is a router
|
|
6
|
+
* that lies — this script keeps them honest.
|
|
7
|
+
*
|
|
8
|
+
* Checks:
|
|
9
|
+
* 1. Every skill dir's SKILL.md frontmatter name matches its directory.
|
|
10
|
+
* 2. Every SKILL.md has a description and both trigger sections.
|
|
11
|
+
* 3. .claude-plugin/plugin.json + marketplace.json list exactly the on-disk skills.
|
|
12
|
+
* 4. Version strings agree across package.json / plugin.json / marketplace.json.
|
|
13
|
+
* 5. Every skill has a routing eval (evals/<skill>/eval.yaml).
|
|
14
|
+
* 6. Local links in README.md and docs markdown files resolve to real files.
|
|
15
|
+
*/
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const yaml = require('js-yaml');
|
|
19
|
+
|
|
20
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
21
|
+
let failures = 0;
|
|
22
|
+
function check(name, condition, detail) {
|
|
23
|
+
if (condition) {
|
|
24
|
+
console.log(`✅ ${name}`);
|
|
25
|
+
} else {
|
|
26
|
+
console.error(`❌ ${name}`);
|
|
27
|
+
if (detail) console.error(` ${detail}`);
|
|
28
|
+
failures++;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// --- Collect skills from disk -------------------------------------------
|
|
33
|
+
const SKIP_DIRS = new Set([
|
|
34
|
+
'.git', '.github', '.claude-plugin', 'bin', 'docs', 'evals',
|
|
35
|
+
'behavioral-evals', 'benchmarks', 'hooks', 'node_modules', 'references', 'scripts'
|
|
36
|
+
]);
|
|
37
|
+
function discoverSkills() {
|
|
38
|
+
const skills = [];
|
|
39
|
+
for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) {
|
|
40
|
+
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) continue;
|
|
41
|
+
const skillMd = path.join(ROOT, entry.name, 'SKILL.md');
|
|
42
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
43
|
+
const raw = fs.readFileSync(skillMd, 'utf8');
|
|
44
|
+
const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
45
|
+
const fm = fmMatch ? fmMatch[1] : '';
|
|
46
|
+
const name = (fm.match(/^name:\s*(.+)$/m) || [])[1];
|
|
47
|
+
const desc = (fm.match(/^description:\s*(.+)$/m) || [])[1];
|
|
48
|
+
skills.push({
|
|
49
|
+
dir: entry.name,
|
|
50
|
+
name: name ? name.trim().replace(/["']/g, '') : null,
|
|
51
|
+
description: desc ? desc.trim() : null,
|
|
52
|
+
body: raw,
|
|
53
|
+
path: skillMd,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return skills.sort((a, b) => a.dir.localeCompare(b.dir));
|
|
57
|
+
}
|
|
58
|
+
const skills = discoverSkills();
|
|
59
|
+
check(`Found ${skills.length} skills on disk`, skills.length >= 20, `Got ${skills.length}`);
|
|
60
|
+
|
|
61
|
+
// --- 1+2. Frontmatter & trigger sections --------------------------------
|
|
62
|
+
for (const s of skills) {
|
|
63
|
+
check(
|
|
64
|
+
`${s.dir}: frontmatter name matches directory`,
|
|
65
|
+
s.name === s.dir,
|
|
66
|
+
`frontmatter "${s.name}" != dir "${s.dir}"`
|
|
67
|
+
);
|
|
68
|
+
check(
|
|
69
|
+
`${s.dir}: has description in frontmatter`,
|
|
70
|
+
!!s.description && s.description.length > 10,
|
|
71
|
+
s.description ? 'description too short' : 'missing description'
|
|
72
|
+
);
|
|
73
|
+
check(`${s.dir}: has "## USE FOR:" section`, /^## USE FOR:/m.test(s.body), 'missing section');
|
|
74
|
+
check(
|
|
75
|
+
`${s.dir}: has "## DO NOT USE FOR:" section`,
|
|
76
|
+
/^## DO NOT USE FOR:/m.test(s.body),
|
|
77
|
+
'missing section'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// --- 1b. YAML frontmatter syntax validation (parser parity) ---------------
|
|
82
|
+
// Catches unquoted colons, unclosed quotes, and broken multi-line strings
|
|
83
|
+
// that regex extraction misses but waza's real YAML parser catches in CI.
|
|
84
|
+
function validateFrontmatterSyntax(raw, skillDir) {
|
|
85
|
+
const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
86
|
+
if (!fmMatch) return check(`${skillDir}: has valid YAML frontmatter delimiters`, false, 'missing --- delimiters');
|
|
87
|
+
const fm = fmMatch[1];
|
|
88
|
+
|
|
89
|
+
// Parser parity: use js-yaml to validate the frontmatter
|
|
90
|
+
try {
|
|
91
|
+
yaml.load(fm);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
return check(`${skillDir}: frontmatter YAML syntax valid`, false, e.message);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Additional regex-based checks for common issues
|
|
97
|
+
const lines = fm.split('\n');
|
|
98
|
+
for (let i = 0; i < lines.length; i++) {
|
|
99
|
+
const line = lines[i];
|
|
100
|
+
if (!line.trim() || line.trim().startsWith('#')) continue;
|
|
101
|
+
// Check for unquoted colons in values: "key: value with : inside"
|
|
102
|
+
const kvMatch = line.match(/^(\s*)(\w[\w-]*):\s*(.*)/);
|
|
103
|
+
if (kvMatch && kvMatch[3]) {
|
|
104
|
+
const val = kvMatch[3];
|
|
105
|
+
// Value contains colon not inside quotes → likely unquoted YAML
|
|
106
|
+
if (val.includes(':') && !/^["']/.test(val) && !/["']$/.test(val)) {
|
|
107
|
+
const inQuotes = /^["'].*:.*["']$/.test(val);
|
|
108
|
+
if (!inQuotes) {
|
|
109
|
+
check(
|
|
110
|
+
`${skillDir}: frontmatter line ${i + 1} has unquoted colon in value`,
|
|
111
|
+
false,
|
|
112
|
+
`"${line.trim()}" — wrap value in quotes to avoid YAML parse errors`
|
|
113
|
+
);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Check for unclosed single quotes
|
|
119
|
+
const singleQuoteCount = (line.match(/(?<!\\)'/g) || []).length;
|
|
120
|
+
if (singleQuoteCount % 2 !== 0) {
|
|
121
|
+
check(`${skillDir}: frontmatter line ${i + 1} has unclosed single quote`, false, `"${line.trim()}"`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Check for unclosed double quotes
|
|
125
|
+
const doubleQuoteCount = (line.match(/(?<!\\)"/g) || []).length;
|
|
126
|
+
if (doubleQuoteCount % 2 !== 0) {
|
|
127
|
+
check(`${skillDir}: frontmatter line ${i + 1} has unclosed double quote`, false, `"${line.trim()}"`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
check(`${skillDir}: frontmatter YAML syntax valid`, true);
|
|
132
|
+
}
|
|
133
|
+
for (const s of skills) {
|
|
134
|
+
validateFrontmatterSyntax(s.body, s.dir);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// --- 3+4. Distribution manifests -----------------------------------------
|
|
138
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
139
|
+
const pluginJsonPath = path.join(ROOT, '.claude-plugin', 'plugin.json');
|
|
140
|
+
const marketplaceJsonPath = path.join(ROOT, '.claude-plugin', 'marketplace.json');
|
|
141
|
+
check('.claude-plugin/plugin.json exists', fs.existsSync(pluginJsonPath));
|
|
142
|
+
check('.claude-plugin/marketplace.json exists', fs.existsSync(marketplaceJsonPath));
|
|
143
|
+
|
|
144
|
+
if (fs.existsSync(pluginJsonPath)) {
|
|
145
|
+
const plugin = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
|
|
146
|
+
const listed = new Set((plugin.skills || []).map((p) => p.replace(/^\.\//, '').replace(/\/$/, '')));
|
|
147
|
+
const onDisk = new Set(skills.map((s) => s.dir));
|
|
148
|
+
const missing = [...onDisk].filter((d) => !listed.has(d));
|
|
149
|
+
const extra = [...listed].filter((d) => !onDisk.has(d));
|
|
150
|
+
check(
|
|
151
|
+
'plugin.json lists every on-disk skill',
|
|
152
|
+
missing.length === 0,
|
|
153
|
+
`missing: ${missing.join(', ')}`
|
|
154
|
+
);
|
|
155
|
+
check(
|
|
156
|
+
'plugin.json lists no phantom skills',
|
|
157
|
+
extra.length === 0,
|
|
158
|
+
`extra: ${extra.join(', ')}`
|
|
159
|
+
);
|
|
160
|
+
check('plugin.json hooks file exists', !plugin.hooks || fs.existsSync(path.join(ROOT, plugin.hooks)), plugin.hooks);
|
|
161
|
+
if (plugin.agents) {
|
|
162
|
+
const agentsPath = path.join(ROOT, plugin.agents);
|
|
163
|
+
check('plugin.json agents path exists', fs.existsSync(agentsPath), plugin.agents);
|
|
164
|
+
if (fs.existsSync(agentsPath)) {
|
|
165
|
+
const agentFiles = fs.readdirSync(agentsPath).filter(name => name.endsWith('.md'));
|
|
166
|
+
check('plugin.json agents path contains agent definitions', agentFiles.length > 0, plugin.agents);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (const rel of plugin.skills || []) {
|
|
170
|
+
const p = path.join(ROOT, rel);
|
|
171
|
+
check(`plugin.json skill path resolves: ${rel}`, fs.existsSync(path.join(p, 'SKILL.md')));
|
|
172
|
+
}
|
|
173
|
+
check(
|
|
174
|
+
'version agrees: package.json vs plugin.json',
|
|
175
|
+
pkg.version === plugin.version,
|
|
176
|
+
`"${pkg.version}" vs "${plugin.version}"`
|
|
177
|
+
);
|
|
178
|
+
if (fs.existsSync(marketplaceJsonPath)) {
|
|
179
|
+
const market = JSON.parse(fs.readFileSync(marketplaceJsonPath, 'utf8'));
|
|
180
|
+
const entry = (market.plugins || []).find((p) => p.name === plugin.name);
|
|
181
|
+
check('marketplace.json references this plugin', !!entry);
|
|
182
|
+
if (entry && entry.version) {
|
|
183
|
+
check(
|
|
184
|
+
'version agrees: package.json vs marketplace.json',
|
|
185
|
+
pkg.version === entry.version,
|
|
186
|
+
`"${pkg.version}" vs "${entry.version}"`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// --- 5. Routing eval coverage --------------------------------------------
|
|
193
|
+
const evalsDir = path.join(ROOT, 'evals');
|
|
194
|
+
for (const s of skills) {
|
|
195
|
+
check(
|
|
196
|
+
`${s.dir}: has routing eval (evals/${s.dir}/eval.yaml)`,
|
|
197
|
+
fs.existsSync(path.join(evalsDir, s.dir, 'eval.yaml')),
|
|
198
|
+
'no trigger/routing eval for this skill'
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// --- 5b. Skill version ceiling --------------------------------------------
|
|
203
|
+
// Skill frontmatter versions move in lockstep with releases; no skill may
|
|
204
|
+
// exceed the package version's numeric base (prerelease suffixes ignored).
|
|
205
|
+
function verBase(v) {
|
|
206
|
+
const m = String(v).match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
207
|
+
return m ? m.slice(1).map(Number) : null;
|
|
208
|
+
}
|
|
209
|
+
function cmpVer(a, b) {
|
|
210
|
+
for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] - b[i];
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
const pkgBase = verBase(pkg.version);
|
|
214
|
+
check(
|
|
215
|
+
`package.json version parses (${pkg.version})`,
|
|
216
|
+
!!pkgBase
|
|
217
|
+
);
|
|
218
|
+
for (const s of skills) {
|
|
219
|
+
const raw = fs.readFileSync(s.path, 'utf8');
|
|
220
|
+
const fm = (raw.match(/^---\r?\n([\s\S]*?)\r?\n---/) || [])[1] || '';
|
|
221
|
+
const vRaw = ((fm.match(/^ version:\s*(.+)$/m) || [])[1] || '').trim();
|
|
222
|
+
const v = verBase(vRaw);
|
|
223
|
+
check(
|
|
224
|
+
`${s.dir}: skill version ${vRaw || '(missing)'} <= package base ${pkgBase ? pkgBase.join('.') : '?'}`,
|
|
225
|
+
!!v && !!pkgBase && cmpVer(v, pkgBase) <= 0,
|
|
226
|
+
v ? 'skill version exceeds package version' : 'missing or unparseable metadata.version'
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// --- 5c. Nested sub-skill versions -----------------------------------------
|
|
231
|
+
// Sub-skills nested inside a parent skill dir (<skill>/<sub>/SKILL.md) are
|
|
232
|
+
// invoked by the parent, not routed independently; they inherit the parent
|
|
233
|
+
// skill's version so the lockstep policy stays enforceable.
|
|
234
|
+
const nestedSubSkills = [];
|
|
235
|
+
for (const s of skills) {
|
|
236
|
+
(function walk(dir) {
|
|
237
|
+
let entries;
|
|
238
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
239
|
+
for (const e of entries) {
|
|
240
|
+
if (!e.isDirectory() || e.name.startsWith('.') || e.name === 'node_modules') continue;
|
|
241
|
+
const sub = path.join(dir, e.name);
|
|
242
|
+
if (fs.existsSync(path.join(sub, 'SKILL.md'))) nestedSubSkills.push(sub);
|
|
243
|
+
else walk(sub);
|
|
244
|
+
}
|
|
245
|
+
})(path.join(ROOT, s.dir));
|
|
246
|
+
}
|
|
247
|
+
for (const sub of nestedSubSkills) {
|
|
248
|
+
const rel = path.relative(ROOT, path.join(sub, 'SKILL.md'));
|
|
249
|
+
const parentDir = path.dirname(sub);
|
|
250
|
+
const parentRaw = fs.readFileSync(path.join(parentDir, 'SKILL.md'), 'utf8');
|
|
251
|
+
const parentFm = (parentRaw.match(/^---\r?\n([\s\S]*?)\r?\n---/) || [])[1] || '';
|
|
252
|
+
const parentV = ((parentFm.match(/^ version:\s*(.+)$/m) || [])[1] || '').trim();
|
|
253
|
+
const raw = fs.readFileSync(path.join(sub, 'SKILL.md'), 'utf8');
|
|
254
|
+
const fm = (raw.match(/^---\r?\n([\s\S]*?)\r?\n---/) || [])[1] || '';
|
|
255
|
+
const v = ((fm.match(/^ version:\s*(.+)$/m) || [])[1] || '').trim();
|
|
256
|
+
check(
|
|
257
|
+
`${rel}: nested sub-skill inherits parent version (${parentV})`,
|
|
258
|
+
!!v && v === parentV,
|
|
259
|
+
`"${v}" != parent "${parentV}"`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// --- 5d. Word-count budget (token proxy) ------------------------------------
|
|
264
|
+
// Skill frontmatter descriptions are routing surfaces; long descriptions
|
|
265
|
+
// bloat token budgets. A simple word-count proxy flags files likely to
|
|
266
|
+
// exceed the waza 500-token limit (~1.55 tokens/word → 330 words ≈ 507 tokens).
|
|
267
|
+
const WORD_BUDGET = 330;
|
|
268
|
+
for (const s of skills) {
|
|
269
|
+
const raw = fs.readFileSync(s.path, 'utf8');
|
|
270
|
+
const body = raw.replace(/^---\r?\n[\s\S]*?\r?\n---/, '').trim();
|
|
271
|
+
const wc = body.split(/\s+/).filter(Boolean).length;
|
|
272
|
+
check(
|
|
273
|
+
`${s.dir}: SKILL.md word count ${wc} <= ${WORD_BUDGET}`,
|
|
274
|
+
wc <= WORD_BUDGET,
|
|
275
|
+
`body is ${wc} words (~${Math.round(wc * 1.55)} tokens); trim to under ${WORD_BUDGET} words`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// --- 6. Dead-link audit over README.md and docs/** -----------------------
|
|
280
|
+
function extractLocalLinks(file) {
|
|
281
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
282
|
+
const links = [];
|
|
283
|
+
const re = /\[[^\]]*\]\(([^)\s]+)\)/g;
|
|
284
|
+
let m;
|
|
285
|
+
while ((m = re.exec(text)) !== null) {
|
|
286
|
+
let href = m[1].split('#')[0];
|
|
287
|
+
if (!href || /^(https?:|mailto:|npx)/.test(href)) continue;
|
|
288
|
+
links.push(href);
|
|
289
|
+
}
|
|
290
|
+
return links;
|
|
291
|
+
}
|
|
292
|
+
const docFiles = [path.join(ROOT, 'README.md')];
|
|
293
|
+
(function walk(dir) {
|
|
294
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
295
|
+
const p = path.join(dir, e.name);
|
|
296
|
+
if (e.isDirectory()) walk(p);
|
|
297
|
+
else if (e.name.endsWith('.md')) docFiles.push(p);
|
|
298
|
+
}
|
|
299
|
+
})(path.join(ROOT, 'docs'));
|
|
300
|
+
|
|
301
|
+
let linkCount = 0;
|
|
302
|
+
for (const file of docFiles) {
|
|
303
|
+
for (const href of extractLocalLinks(file)) {
|
|
304
|
+
linkCount++;
|
|
305
|
+
const target = path.resolve(path.dirname(file), decodeURIComponent(href));
|
|
306
|
+
check(
|
|
307
|
+
`link resolves: ${path.relative(ROOT, file)} -> ${href}`,
|
|
308
|
+
fs.existsSync(target),
|
|
309
|
+
`target not found on disk`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
console.log(`\nChecked ${linkCount} local doc links.`);
|
|
314
|
+
|
|
315
|
+
// --- 7. Token-budget gate (word-count proxy, hard limit 500) ----------------
|
|
316
|
+
// Exact tokenization is waza's job; this is the local early-warning gate.
|
|
317
|
+
// Includes warning threshold at 80% (400 words) for early detection.
|
|
318
|
+
const TOKEN_HARD_LIMIT = 500;
|
|
319
|
+
const TOKEN_WARNING_THRESHOLD = 400; // 80% of hard limit
|
|
320
|
+
for (const s of skills) {
|
|
321
|
+
const raw = fs.readFileSync(s.path, 'utf8');
|
|
322
|
+
const withoutFm = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, '');
|
|
323
|
+
const wordCount = withoutFm.split(/\s+/).filter(Boolean).length;
|
|
324
|
+
|
|
325
|
+
// Early warning at WORD_BUDGET threshold (non-blocking)
|
|
326
|
+
if (wordCount > WORD_BUDGET && wordCount <= TOKEN_WARNING_THRESHOLD) {
|
|
327
|
+
console.log(`⚠️ ${s.dir}: word count ${wordCount} exceeds WORD_BUDGET (${WORD_BUDGET})`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Warning at 80% threshold (non-blocking)
|
|
331
|
+
if (wordCount > TOKEN_WARNING_THRESHOLD && wordCount <= TOKEN_HARD_LIMIT) {
|
|
332
|
+
console.log(`⚠️ ${s.dir}: SKILL.md word count ${wordCount} approaching limit (${TOKEN_HARD_LIMIT - wordCount} words remaining)`);
|
|
333
|
+
}
|
|
334
|
+
check(
|
|
335
|
+
`${s.dir}: SKILL.md word count ${wordCount} <= ${TOKEN_HARD_LIMIT}`,
|
|
336
|
+
wordCount <= TOKEN_HARD_LIMIT,
|
|
337
|
+
wordCount > TOKEN_HARD_LIMIT
|
|
338
|
+
? `exceeds hard limit by ${wordCount - TOKEN_HARD_LIMIT} words`
|
|
339
|
+
: `${TOKEN_HARD_LIMIT - wordCount} words of headroom`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (failures > 0) {
|
|
344
|
+
console.error(`\n❌ CONSISTENCY CHECK FAILED: ${failures} problem(s).`);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
console.log('\n🎉 ALL CONSISTENCY CHECKS PASSED.');
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Skill Description Collision Detector
|
|
4
|
+
*
|
|
5
|
+
* Routing skills are selected by their `description:` frontmatter. When two
|
|
6
|
+
* descriptions overlap too much, the router effectively flips a coin between
|
|
7
|
+
* them. This script tokenizes every SKILL.md description, computes pairwise
|
|
8
|
+
* Jaccard similarity over stemmed tokens, and fails on collisions.
|
|
9
|
+
*
|
|
10
|
+
* Thresholds:
|
|
11
|
+
* >= 0.75 similarity -> ERROR (collision; rewrite one description)
|
|
12
|
+
* >= 0.50 similarity -> WARNING (review recommended)
|
|
13
|
+
*
|
|
14
|
+
* Usage: node ci/description-collision.js
|
|
15
|
+
*/
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const yaml = require('js-yaml');
|
|
19
|
+
|
|
20
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
21
|
+
const ERROR_THRESHOLD = 0.75;
|
|
22
|
+
const WARN_THRESHOLD = 0.5;
|
|
23
|
+
|
|
24
|
+
// Words so generic they carry no routing signal — dropping them keeps the
|
|
25
|
+
// metric honest (otherwise "the", "a", "skill" dominate every pair).
|
|
26
|
+
const STOPWORDS = new Set([
|
|
27
|
+
'a', 'an', 'the', 'and', 'or', 'for', 'to', 'of', 'in', 'on', 'with',
|
|
28
|
+
'this', 'that', 'it', 'its', 'is', 'are', 'be', 'when', 'before', 'after',
|
|
29
|
+
'use', 'used', 'using', 'must', 'should', 'new', 'your', 'you', 'from',
|
|
30
|
+
'into', 'any', 'all', 'not', 'no', 'so', 'by', 'at', 'as',
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
function stem(word) {
|
|
34
|
+
// Deliberately crude suffix stripper — consistent crudeness is what matters.
|
|
35
|
+
return word
|
|
36
|
+
.replace(/(?:ing|ies|ied|edly|ements?)$/, '')
|
|
37
|
+
.replace(/(?:s|es)$/, '');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function tokenize(description) {
|
|
41
|
+
const cleaned = description.replace(/^["']|["'],?$/g, '').toLowerCase();
|
|
42
|
+
const raw = cleaned.match(/[a-z][a-z0-9-]+/g) || [];
|
|
43
|
+
const tokens = new Set();
|
|
44
|
+
for (const w of raw) {
|
|
45
|
+
if (STOPWORDS.has(w)) continue;
|
|
46
|
+
tokens.add(stem(w));
|
|
47
|
+
}
|
|
48
|
+
return tokens;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function jaccard(a, b) {
|
|
52
|
+
let inter = 0;
|
|
53
|
+
for (const t of a) if (b.has(t)) inter++;
|
|
54
|
+
return inter / (a.size + b.size - inter);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function discoverSkills() {
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) {
|
|
60
|
+
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
|
|
61
|
+
const skillMd = path.join(ROOT, entry.name, 'SKILL.md');
|
|
62
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
63
|
+
const raw = fs.readFileSync(skillMd, 'utf8');
|
|
64
|
+
const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
65
|
+
if (!fmMatch) continue;
|
|
66
|
+
const fm = fmMatch[1];
|
|
67
|
+
// Parser parity: use js-yaml to parse frontmatter
|
|
68
|
+
let desc = '';
|
|
69
|
+
try {
|
|
70
|
+
const parsed = yaml.load(fm);
|
|
71
|
+
desc = (parsed.description || '').trim();
|
|
72
|
+
} catch {
|
|
73
|
+
// Fallback to regex if YAML parse fails
|
|
74
|
+
desc = ((fm.match(/^description:\s*(.+)$/m) || [])[1] || '').trim();
|
|
75
|
+
}
|
|
76
|
+
out.push({ name: entry.name, desc });
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const skills = discoverSkills();
|
|
82
|
+
if (skills.length < 2) {
|
|
83
|
+
console.error('Not enough skills found to compare.');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let errors = 0;
|
|
88
|
+
console.log(`Comparing ${skills.length} skill descriptions...\n`);
|
|
89
|
+
for (let i = 0; i < skills.length; i++) {
|
|
90
|
+
for (let j = i + 1; j < skills.length; j++) {
|
|
91
|
+
const sim = jaccard(tokenize(skills[i].desc), tokenize(skills[j].desc));
|
|
92
|
+
if (sim >= WARN_THRESHOLD) {
|
|
93
|
+
const label = sim >= ERROR_THRESHOLD ? '❌ COLLISION' : '⚠️ WARNING ';
|
|
94
|
+
console.log(
|
|
95
|
+
`${label} ${skills[i].name} <-> ${skills[j].name}: ${sim.toFixed(2)}`
|
|
96
|
+
);
|
|
97
|
+
if (sim >= ERROR_THRESHOLD) errors++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (errors > 0) {
|
|
103
|
+
console.error(
|
|
104
|
+
`\n❌ ${errors} description collision(s) at >= ${ERROR_THRESHOLD}. ` +
|
|
105
|
+
`Rewrite the affected descriptions so the router can tell them apart.`
|
|
106
|
+
);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
console.log(`\n🎉 NO DESCRIPTION COLLISIONS (threshold ${ERROR_THRESHOLD}).`);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const root = path.resolve(__dirname, '..');
|
|
10
|
+
const selector = path.join(root, 'fable-mode', 'scripts', 'model-selector.js');
|
|
11
|
+
const router = path.join(root, 'harness-everything', 'scripts', 'tier-router.js');
|
|
12
|
+
const auditDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-fable-mode-'));
|
|
13
|
+
const auditFile = path.join(auditDir, 'audit.jsonl');
|
|
14
|
+
|
|
15
|
+
function runNode(file, args) {
|
|
16
|
+
return spawnSync(process.execPath, [file, ...args], {
|
|
17
|
+
cwd: root,
|
|
18
|
+
encoding: 'utf8'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function selectorArgs(overrides = {}) {
|
|
23
|
+
const input = {
|
|
24
|
+
requested: 'opus',
|
|
25
|
+
available: 'opus,sonnet,haiku',
|
|
26
|
+
'available-agents': 'fable-orchestrator,fable-worker-sonnet,fable-worker-haiku',
|
|
27
|
+
fallback: 'stop',
|
|
28
|
+
'host-model': 'opus',
|
|
29
|
+
'audit-file': auditFile,
|
|
30
|
+
'stage-brief': 'Choose the architecture',
|
|
31
|
+
'pass-condition': 'ADR exists and names the decision',
|
|
32
|
+
'verification-command': 'npm test',
|
|
33
|
+
'verifier-result': 'pending',
|
|
34
|
+
...overrides
|
|
35
|
+
};
|
|
36
|
+
return Object.entries(input).flatMap(([key, value]) => [`--${key}`, value]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readRecord(result) {
|
|
40
|
+
assert.ok(result.stdout.trim(), `selector produced no JSON: ${result.stderr}`);
|
|
41
|
+
return JSON.parse(result.stdout);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('=== Fable model mode contract tests ===');
|
|
45
|
+
|
|
46
|
+
const selected = runNode(selector, selectorArgs());
|
|
47
|
+
assert.strictEqual(selected.status, 0, selected.stderr);
|
|
48
|
+
const selectedRecord = readRecord(selected);
|
|
49
|
+
assert.strictEqual(selectedRecord.status, 'selected');
|
|
50
|
+
assert.strictEqual(selectedRecord.requestedModel, 'opus');
|
|
51
|
+
assert.strictEqual(selectedRecord.effectiveModel, 'opus');
|
|
52
|
+
assert.strictEqual(selectedRecord.fallbackReason, 'none');
|
|
53
|
+
assert.strictEqual(selectedRecord.agent, 'fable-orchestrator');
|
|
54
|
+
|
|
55
|
+
const sonnect = runNode(selector, selectorArgs({ requested: 'sonnect', available: 'haiku,sonnet' }));
|
|
56
|
+
assert.strictEqual(sonnect.status, 0, sonnect.stderr);
|
|
57
|
+
const sonnectRecord = readRecord(sonnect);
|
|
58
|
+
assert.strictEqual(sonnectRecord.requestedModel, 'sonnet');
|
|
59
|
+
assert.strictEqual(sonnectRecord.requestedInput, 'sonnect');
|
|
60
|
+
|
|
61
|
+
const inline = runNode(selector, selectorArgs({
|
|
62
|
+
requested: 'opus',
|
|
63
|
+
available: 'sonnet',
|
|
64
|
+
fallback: 'inline',
|
|
65
|
+
'host-model': 'sonnet'
|
|
66
|
+
}));
|
|
67
|
+
assert.strictEqual(inline.status, 0, inline.stderr);
|
|
68
|
+
const inlineRecord = readRecord(inline);
|
|
69
|
+
assert.strictEqual(inlineRecord.status, 'fallback');
|
|
70
|
+
assert.strictEqual(inlineRecord.effectiveModel, 'sonnet');
|
|
71
|
+
assert.match(inlineRecord.fallbackReason, /opus is not in availableModels/);
|
|
72
|
+
assert.strictEqual(inlineRecord.escalationRequired, false);
|
|
73
|
+
|
|
74
|
+
const blocked = runNode(selector, selectorArgs({ requested: 'opus', available: 'sonnet' }));
|
|
75
|
+
assert.strictEqual(blocked.status, 2);
|
|
76
|
+
const blockedRecord = readRecord(blocked);
|
|
77
|
+
assert.strictEqual(blockedRecord.status, 'blocked');
|
|
78
|
+
assert.strictEqual(blockedRecord.effectiveModel, null);
|
|
79
|
+
assert.strictEqual(blockedRecord.escalationRequired, true);
|
|
80
|
+
assert.match(blockedRecord.fallbackReason, /inline fallback was not authorized/);
|
|
81
|
+
|
|
82
|
+
const missingAgent = runNode(selector, selectorArgs({
|
|
83
|
+
requested: 'sonnet',
|
|
84
|
+
available: 'sonnet',
|
|
85
|
+
'available-agents': 'fable-worker-haiku',
|
|
86
|
+
fallback: 'inline',
|
|
87
|
+
'host-model': 'opus'
|
|
88
|
+
}));
|
|
89
|
+
assert.strictEqual(missingAgent.status, 0, missingAgent.stderr);
|
|
90
|
+
const missingAgentRecord = readRecord(missingAgent);
|
|
91
|
+
assert.strictEqual(missingAgentRecord.status, 'fallback');
|
|
92
|
+
assert.strictEqual(missingAgentRecord.effectiveModel, 'opus');
|
|
93
|
+
assert.match(missingAgentRecord.fallbackReason, /fable-worker-sonnet is not in availableAgents/);
|
|
94
|
+
|
|
95
|
+
const missingField = runNode(selector, selectorArgs({ 'pass-condition': '' }));
|
|
96
|
+
assert.strictEqual(missingField.status, 2);
|
|
97
|
+
assert.match(missingField.stderr, /passCondition is required/);
|
|
98
|
+
|
|
99
|
+
const explicitRoute = runNode(router, ['fable on sonnect for architecture synthesis']);
|
|
100
|
+
assert.strictEqual(explicitRoute.status, 0);
|
|
101
|
+
assert.match(explicitRoute.stdout, /REQUESTED FABLE MODEL MODE: sonnet/);
|
|
102
|
+
assert.match(explicitRoute.stdout, /fable-mode\/fable-sonnet\/SKILL\.md/);
|
|
103
|
+
|
|
104
|
+
const ordinaryTier2 = runNode(router, ['Fix this checkout bug and add a regression test.']);
|
|
105
|
+
assert.strictEqual(ordinaryTier2.status, 0);
|
|
106
|
+
assert.match(ordinaryTier2.stdout, /RECOMMENDED TIER: Tier 2/);
|
|
107
|
+
assert.doesNotMatch(ordinaryTier2.stdout, /REQUESTED FABLE MODEL MODE/);
|
|
108
|
+
assert.doesNotMatch(ordinaryTier2.stdout, /fable-opus\/SKILL\.md/);
|
|
109
|
+
|
|
110
|
+
const auditLines = fs.readFileSync(auditFile, 'utf8').trim().split(/\r?\n/).filter(Boolean);
|
|
111
|
+
assert.strictEqual(auditLines.length, 5);
|
|
112
|
+
for (const line of auditLines) {
|
|
113
|
+
const record = JSON.parse(line);
|
|
114
|
+
for (const field of ['requestedModel', 'effectiveModel', 'fallbackReason', 'stageBrief', 'passCondition', 'verificationCommand', 'verifierResult']) {
|
|
115
|
+
assert.ok(Object.prototype.hasOwnProperty.call(record, field), `audit missing ${field}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
fs.rmSync(auditDir, { recursive: true, force: true });
|
|
119
|
+
|
|
120
|
+
console.log('PASS: selected, sonnect alias, inline fallback, blocked escalation, audit fields, and routing separation');
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Code Reviewer
|
|
3
|
+
description: Reviews code for correctness, security, and maintainability.
|
|
4
|
+
color: purple
|
|
5
|
+
emoji: eye
|
|
6
|
+
vibe: Evidence before approval.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Code Reviewer
|
|
10
|
+
|
|
11
|
+
Review only the files named in the dispatch brief and return a structured handoff.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Reality Checker
|
|
3
|
+
description: Checks release readiness against observable evidence.
|
|
4
|
+
color: red
|
|
5
|
+
emoji: check
|
|
6
|
+
vibe: Needs proof before sign-off.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Reality Checker
|
|
10
|
+
|
|
11
|
+
Reject unverifiable claims and report the evidence needed for a bounded handoff.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bad-frontmatter-colon
|
|
3
|
+
description: This description has an unquoted colon: which breaks YAML parsers
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: Test
|
|
7
|
+
version: 0.0.1
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Bad Frontmatter Colon
|
|
11
|
+
|
|
12
|
+
## USE FOR:
|
|
13
|
+
- Testing
|
|
14
|
+
|
|
15
|
+
## DO NOT USE FOR:
|
|
16
|
+
- Production
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: duplicate-a
|
|
3
|
+
description: Identical description used for collision testing purposes only
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: Test
|
|
7
|
+
version: 0.0.1
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Duplicate A
|
|
11
|
+
|
|
12
|
+
## USE FOR:
|
|
13
|
+
- Testing
|
|
14
|
+
|
|
15
|
+
## DO NOT USE FOR:
|
|
16
|
+
- Production
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: duplicate-b
|
|
3
|
+
description: Identical description used for collision testing purposes only
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: Test
|
|
7
|
+
version: 0.0.1
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Duplicate B
|
|
11
|
+
|
|
12
|
+
## USE FOR:
|
|
13
|
+
- Testing
|
|
14
|
+
|
|
15
|
+
## DO NOT USE FOR:
|
|
16
|
+
- Production
|