harness-everything 0.2.2 → 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 +10 -10
- package/docs/workflows/eval-harness.md +13 -6
- package/docs/workflows/fable-discipline.md +9 -9
- package/docs/workflows/fable-mode.md +29 -32
- package/docs/workflows/find-skills.md +36 -0
- package/docs/workflows/git-commit.md +18 -9
- package/docs/workflows/grill-me.md +11 -11
- package/docs/workflows/grill-with-docs.md +14 -7
- package/docs/workflows/harness-everything.md +95 -92
- package/docs/workflows/improve-codebase-architecture.md +15 -7
- package/docs/workflows/install-cognitive-os.md +60 -60
- package/docs/workflows/multi-agent-workspace.md +42 -0
- package/docs/workflows/repo-docs.md +12 -7
- package/docs/workflows/security-review.md +26 -7
- package/docs/workflows/self-evolve.md +27 -44
- package/docs/workflows/skill-creator.md +80 -75
- package/docs/workflows/skill-style.md +64 -64
- package/docs/workflows/tdd.md +42 -39
- package/docs/workflows/to-spec.md +83 -0
- package/docs/workflows/to-tickets.md +80 -0
- package/docs/workflows/todo-driven-workflow.md +34 -36
- package/docs/workflows/using-git-worktrees.md +12 -8
- package/docs/workflows/verification-loop.md +16 -14
- package/docs/workflows/verify-before-claim.md +12 -8
- package/docs/workflows/zoom-out.md +22 -10
- package/environment-detection/SKILL.md +25 -61
- 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 +34 -51
- 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 +31 -28
- package/fable-discipline/references/discipline-rules.md +22 -0
- package/fable-mode/CONTRACT-FORMAT.md +51 -51
- package/fable-mode/SKILL.md +31 -43
- 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 +35 -45
- 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 +34 -27
- 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 +33 -91
- package/grill-with-docs/references/session-playbook.md +97 -0
- package/harness-everything/SKILL.md +29 -128
- package/harness-everything/examples/README.md +9 -9
- package/harness-everything/examples/todo-workflow.md +22 -22
- 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 +100 -31
- package/hooks/hooks.json +155 -155
- package/hooks/scripts/atomic-commit-check.js +68 -68
- package/hooks/scripts/boundary-guard.js +55 -55
- package/hooks/scripts/context-compact.js +114 -114
- 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 +72 -19
- package/hooks/scripts/lib/platforms/codex.js +24 -6
- package/hooks/scripts/lib/platforms/continue.js +23 -5
- package/hooks/scripts/lib/platforms/copilot.js +23 -5
- package/hooks/scripts/lib/platforms/cursor.js +23 -5
- package/hooks/scripts/lib/platforms/hermes.js +57 -57
- 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 +187 -176
- 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 +31 -35
- 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 +23 -80
- 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 +38 -37
- 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 +22 -17
- package/rewrite-commits/references/workflow.md +18 -0
- package/scripts/installer.js +546 -506
- package/scripts/lib/advisory-text.js +8 -4
- package/scripts/lib/claude-hooks.js +18 -5
- package/scripts/lib/gitignore.js +199 -80
- package/scripts/lib/manifest.js +68 -4
- package/scripts/lib/prompts.js +1 -1
- package/scripts/lib/skills.js +87 -1
- package/scripts/verify-install.js +147 -0
- package/security-review/SKILL.md +35 -496
- 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 +38 -45
- 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 +34 -76
- 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 +27 -32
- package/skill-style/SKILL.md +33 -39
- package/skill-style/references/style-guide.md +36 -0
- package/tdd/SKILL.md +34 -43
- 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 +27 -41
- package/todo-driven-workflow/references/execution-guide.md +27 -0
- package/using-git-worktrees/SKILL.md +29 -141
- package/using-git-worktrees/references/workflow-details.md +108 -0
- package/verification-loop/SKILL.md +29 -121
- 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 +27 -70
- package/verify-before-claim/references/verification-guide.md +73 -0
- package/zoom-out/SKILL.md +28 -76
- 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,587 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Behavioral Eval Runner
|
|
4
|
+
*
|
|
5
|
+
* Runs discipline cases against a real headless Claude session and grades the
|
|
6
|
+
* transcript + resulting workspace. Token-costing, on-demand only — never CI.
|
|
7
|
+
*
|
|
8
|
+
* node behavioral-evals/run.js validate # structural check of case files (free)
|
|
9
|
+
* node behavioral-evals/run.js run # run all cases (costs tokens)
|
|
10
|
+
* node behavioral-evals/run.js run --case <id> # run one case
|
|
11
|
+
*
|
|
12
|
+
* How a live run works:
|
|
13
|
+
* 1. Build identical case fixtures in OS temp workspaces.
|
|
14
|
+
* 2. Run a control with no Harness files and a treatment with only the
|
|
15
|
+
* case's named skill loaded; `--arm both` randomizes this order.
|
|
16
|
+
* 3. Run: claude -p "<prompt>" --output-format json --max-turns N
|
|
17
|
+
* 4. Grade expectations[] against transcript text and workspace state.
|
|
18
|
+
* 5. Write paired results with fixture/prompt hashes and attribution so any
|
|
19
|
+
* grader verdict can be audited by a human.
|
|
20
|
+
*/
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const os = require('os');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const crypto = require('crypto');
|
|
25
|
+
const { execFileSync } = require('child_process');
|
|
26
|
+
|
|
27
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
28
|
+
const CASES_DIR = path.join(__dirname, 'cases');
|
|
29
|
+
const RESULTS_DIR = path.join(__dirname, 'results');
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Minimal YAML subset parser for our case files (flat maps, lists of maps,
|
|
33
|
+
// block scalars via |). Avoids adding dependencies to this package.
|
|
34
|
+
function parseSimpleYaml(text) {
|
|
35
|
+
const lines = text.split('\n');
|
|
36
|
+
let i = 0;
|
|
37
|
+
function parseBlock(indent) {
|
|
38
|
+
const obj = {};
|
|
39
|
+
const arr = [];
|
|
40
|
+
let isArray = null;
|
|
41
|
+
while (i < lines.length) {
|
|
42
|
+
const line = lines[i];
|
|
43
|
+
if (!line.trim() || line.trim().startsWith('#')) { i++; continue; }
|
|
44
|
+
const currentIndent = line.match(/^ */)[0].length;
|
|
45
|
+
if (currentIndent < indent) break;
|
|
46
|
+
if (currentIndent > indent) { i++; continue; }
|
|
47
|
+
const listItem = line.trim().startsWith('- ');
|
|
48
|
+
if (isArray === null) isArray = listItem;
|
|
49
|
+
else if (isArray !== listItem) throw new Error('Mixed list/map at same indent near: ' + line);
|
|
50
|
+
if (listItem) {
|
|
51
|
+
const content = line.trim().slice(2);
|
|
52
|
+
const kv = content.match(/^([^:]+):\s*(.*)$/);
|
|
53
|
+
if (kv && !content.startsWith('"') && !content.startsWith("'")) {
|
|
54
|
+
// inline object item spanning following indented lines
|
|
55
|
+
const item = { [kv[1].trim()]: scalar(kv[2]) };
|
|
56
|
+
i++;
|
|
57
|
+
Object.assign(item, parseNested(currentIndent + 2));
|
|
58
|
+
arr.push(item);
|
|
59
|
+
} else {
|
|
60
|
+
arr.push(scalar(content));
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
const kv = line.match(/^([^:#]+):\s*(.*)$/);
|
|
65
|
+
if (!kv) { i++; continue; }
|
|
66
|
+
const key = kv[1].trim();
|
|
67
|
+
const rawVal = kv[2];
|
|
68
|
+
if (rawVal === '|' || rawVal === '>') {
|
|
69
|
+
i++;
|
|
70
|
+
let block = [];
|
|
71
|
+
while (i < lines.length) {
|
|
72
|
+
const l = lines[i];
|
|
73
|
+
const ind = l.match(/^ */)[0].length;
|
|
74
|
+
if (!l.trim()) { block.push(''); i++; continue; }
|
|
75
|
+
if (ind <= indent) break;
|
|
76
|
+
block.push(l.slice(indent + 2));
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
obj[key] = block.join('\n');
|
|
80
|
+
} else if (rawVal === '') {
|
|
81
|
+
i++;
|
|
82
|
+
obj[key] = parseNested(indent + 2);
|
|
83
|
+
} else {
|
|
84
|
+
obj[key] = scalar(rawVal);
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return isArray ? arr : obj;
|
|
90
|
+
}
|
|
91
|
+
function parseNested(minIndent) {
|
|
92
|
+
// Peek: is the next meaningful line a list or map?
|
|
93
|
+
let j = i;
|
|
94
|
+
while (j < lines.length && (!lines[j].trim() || lines[j].trim().startsWith('#'))) j++;
|
|
95
|
+
if (j >= lines.length) return null;
|
|
96
|
+
const ind = lines[j].match(/^ */)[0].length;
|
|
97
|
+
const block = parseBlock(ind);
|
|
98
|
+
return block;
|
|
99
|
+
}
|
|
100
|
+
function scalar(v) {
|
|
101
|
+
const t = v.trim();
|
|
102
|
+
if (t === 'true') return true;
|
|
103
|
+
if (t === 'false') return false;
|
|
104
|
+
if (/^-?\d+$/.test(t)) return parseInt(t, 10);
|
|
105
|
+
if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) return t.slice(1, -1);
|
|
106
|
+
return t;
|
|
107
|
+
}
|
|
108
|
+
const result = parseBlock(0);
|
|
109
|
+
return result || {};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
function discoverCases() {
|
|
114
|
+
return fs.readdirSync(CASES_DIR)
|
|
115
|
+
.filter((f) => f.endsWith('.yaml'))
|
|
116
|
+
.map((f) => ({ file: path.join(CASES_DIR, f), ...parseSimpleYaml(fs.readFileSync(path.join(CASES_DIR, f), 'utf8')) }));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function fail(msg) {
|
|
120
|
+
console.error(`❌ ${msg}`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const PRESSURE_RULES = {
|
|
125
|
+
budget: ['trace_contains'],
|
|
126
|
+
authority: ['trace_contains'],
|
|
127
|
+
complexity: ['command_exit_0', 'trace_contains'],
|
|
128
|
+
expert: ['trace_contains', 'command_exit_0'],
|
|
129
|
+
fatigue: ['command_exit_0', 'trace_contains'],
|
|
130
|
+
management: ['command_exit_0', 'trace_contains'],
|
|
131
|
+
documentation: ['file_contains'],
|
|
132
|
+
'error-handling': ['file_contains'],
|
|
133
|
+
security: ['file_contains', 'trace_contains'],
|
|
134
|
+
tests: ['trace_contains', 'file_contains'],
|
|
135
|
+
verification: ['command_exit_0', 'trace_contains'],
|
|
136
|
+
social: ['trace_contains'],
|
|
137
|
+
'sunk-cost': ['file_contains'],
|
|
138
|
+
'scope-bypass': ['command_exit_0', 'file_contains'],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// --- validate mode ----------------------------------------------------------
|
|
142
|
+
function validate(cases) {
|
|
143
|
+
let failures = 0;
|
|
144
|
+
const EXPECT_TYPES = new Set(['trace_contains', 'trace_not_contains', 'file_contains', 'file_not_exists', 'command_exit_0']);
|
|
145
|
+
for (const c of cases) {
|
|
146
|
+
const problems = [];
|
|
147
|
+
if (!c.id) problems.push('missing id');
|
|
148
|
+
if (!c.prompt) problems.push('missing prompt');
|
|
149
|
+
if (!c.max_turns) problems.push('missing max_turns');
|
|
150
|
+
if (!c.fixture || !Array.isArray(c.fixture.files)) problems.push('fixture.files missing');
|
|
151
|
+
if (!Array.isArray(c.expectations) || c.expectations.length === 0) problems.push('expectations missing');
|
|
152
|
+
if (c.loaded_skills !== undefined && (!Array.isArray(c.loaded_skills) || c.loaded_skills.some(skill => !fs.existsSync(path.join(ROOT, skill, 'SKILL.md'))))) {
|
|
153
|
+
problems.push('loaded_skills must name existing skills');
|
|
154
|
+
}
|
|
155
|
+
if (!resolveTreatmentSkills(c).length) problems.push('treatment must load at least one named Harness skill');
|
|
156
|
+
for (const e of c.expectations || []) {
|
|
157
|
+
if (!EXPECT_TYPES.has(e.type)) problems.push(`unknown expectation type "${e.type}"`);
|
|
158
|
+
if (e.value === undefined && e.command === undefined) problems.push(`expectation ${e.type} needs value or command`);
|
|
159
|
+
}
|
|
160
|
+
if (c.pressure && !/skip|don't|not|quick|minutes/i.test(c.prompt)) {
|
|
161
|
+
problems.push('pressure case prompt does not read as pressure');
|
|
162
|
+
}
|
|
163
|
+
if (c.pressure) {
|
|
164
|
+
const allowed = PRESSURE_RULES[c.pressure_category];
|
|
165
|
+
if (!allowed) problems.push(`pressure_category must be one of: ${Object.keys(PRESSURE_RULES).join(', ')}`);
|
|
166
|
+
else if (!(c.expectations || []).some(e => allowed.includes(e.type))) {
|
|
167
|
+
problems.push(`pressure category ${c.pressure_category} has no minimum expectation (${allowed.join(' or ')})`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (problems.length) {
|
|
171
|
+
console.error(`❌ ${c.file}: ${problems.join('; ')}`);
|
|
172
|
+
failures++;
|
|
173
|
+
} else {
|
|
174
|
+
console.log(`✅ ${c.id} (${c.pressure ? 'pressure' : 'baseline'})`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (failures) { console.error(`\n${failures} invalid case file(s).`); process.exit(1); }
|
|
178
|
+
console.log(`\n🎉 All ${cases.length} case files are structurally valid.`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// --- live run mode -----------------------------------------------------------
|
|
182
|
+
function buildWorkspace(c) {
|
|
183
|
+
const ws = fs.mkdtempSync(path.join(os.tmpdir(), `harness-behavioral-${c.id}-`));
|
|
184
|
+
for (const f of c.fixture.files) {
|
|
185
|
+
const target = path.join(ws, f.path);
|
|
186
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
187
|
+
fs.writeFileSync(target, typeof f.content === 'string' ? f.content.replace(/\n$/, '') + '\n' : String(f.content));
|
|
188
|
+
}
|
|
189
|
+
return ws;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Capture the requested fixture baseline after treatment installation so the
|
|
193
|
+
// installer's own artifacts (.claude/, .harness-src/, opencode.json) show up
|
|
194
|
+
// in neither arm's post-run scope check.
|
|
195
|
+
function gitSnapshot(ws) {
|
|
196
|
+
execFileSync('git', ['init', '-q'], { cwd: ws });
|
|
197
|
+
execFileSync('git', ['config', 'user.email', 'eval@harness.local'], { cwd: ws });
|
|
198
|
+
execFileSync('git', ['config', 'user.name', 'Harness Eval'], { cwd: ws });
|
|
199
|
+
execFileSync('git', ['add', '.'], { cwd: ws });
|
|
200
|
+
execFileSync('git', ['commit', '-qm', 'fixture'], { cwd: ws });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function resolveTreatmentSkills(c) {
|
|
204
|
+
if (Array.isArray(c.loaded_skills)) return c.loaded_skills;
|
|
205
|
+
if (c.discipline && fs.existsSync(path.join(ROOT, c.discipline, 'SKILL.md'))) return [c.discipline];
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function installHarness(ws, loadedSkills) {
|
|
210
|
+
const src = path.join(ws, '.harness-src');
|
|
211
|
+
fs.mkdirSync(src, { recursive: true });
|
|
212
|
+
for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) {
|
|
213
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'benchmarks' || entry.name === 'behavioral-evals') continue;
|
|
214
|
+
if (!entry.isDirectory()) continue;
|
|
215
|
+
fs.cpSync(path.join(ROOT, entry.name), path.join(src, entry.name), { recursive: true });
|
|
216
|
+
}
|
|
217
|
+
// Skills where Claude Code can see them
|
|
218
|
+
const skillsDir = path.join(ws, '.claude', 'skills');
|
|
219
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
220
|
+
const skillNames = [];
|
|
221
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
222
|
+
if (!entry.isDirectory()) continue;
|
|
223
|
+
if (!loadedSkills.includes(entry.name)) continue;
|
|
224
|
+
if (!fs.existsSync(path.join(src, entry.name, 'SKILL.md'))) continue;
|
|
225
|
+
fs.cpSync(path.join(src, entry.name), path.join(skillsDir, entry.name), { recursive: true });
|
|
226
|
+
skillNames.push(entry.name);
|
|
227
|
+
}
|
|
228
|
+
// Hooks: rewrite relative commands to the copied source tree
|
|
229
|
+
const hooks = JSON.parse(fs.readFileSync(path.join(src, 'hooks', 'hooks.json'), 'utf8'));
|
|
230
|
+
const hooksCfg = hooks.hooks || {};
|
|
231
|
+
for (const events of Object.values(hooksCfg)) {
|
|
232
|
+
for (const entry of events) {
|
|
233
|
+
for (const h of entry.hooks || []) {
|
|
234
|
+
// `node hooks/scripts/x.js` -> `node "<abs src copy>/hooks/scripts/x.js"`
|
|
235
|
+
h.command = h.command.replace(/^node /, `node "${src}/`) + '"';
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const settingsPath = path.join(ws, '.claude', 'settings.json');
|
|
240
|
+
const settings = fs.existsSync(settingsPath) ? JSON.parse(fs.readFileSync(settingsPath, 'utf8')) : {};
|
|
241
|
+
settings.hooks = { ...(settings.hooks || {}), ...hooksCfg };
|
|
242
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
243
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
244
|
+
// opencode has no lifecycle-hook system wired here: load skills as
|
|
245
|
+
// instructions instead (advisory strength, not hard enforcement).
|
|
246
|
+
fs.writeFileSync(
|
|
247
|
+
path.join(ws, 'opencode.json'),
|
|
248
|
+
JSON.stringify({
|
|
249
|
+
$schema: 'https://opencode.ai/config.json',
|
|
250
|
+
instructions: skillNames.map((n) => `.claude/skills/${n}/SKILL.md`),
|
|
251
|
+
}, null, 2)
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function buildEngineInvocation(engine, prompt, ws, maxTurns, arm = 'treatment') {
|
|
256
|
+
if (engine === 'opencode') {
|
|
257
|
+
const args = ['run', '--format', 'json', '--auto', '--dir', ws];
|
|
258
|
+
const model = process.env.BEHAVIORAL_MODEL;
|
|
259
|
+
if (model) args.push('-m', model);
|
|
260
|
+
args.push(prompt);
|
|
261
|
+
return { command: 'opencode', args };
|
|
262
|
+
}
|
|
263
|
+
const args = ['-p', prompt, '--output-format', 'json', '--max-turns', String(maxTurns), '--permission-mode', 'acceptEdits', '--setting-sources', 'project,local'];
|
|
264
|
+
if (arm === 'baseline') args.push('--safe-mode');
|
|
265
|
+
return { command: 'claude', args };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function runHeadless(prompt, ws, maxTurns, engine, arm) {
|
|
269
|
+
// Keep the transcript OUTSIDE the workspace: grader artifacts must never
|
|
270
|
+
// show up in git-status-based scope checks.
|
|
271
|
+
const outPath = path.join(path.dirname(ws), path.basename(ws) + '.transcript.json');
|
|
272
|
+
const invocation = buildEngineInvocation(engine, prompt, ws, maxTurns, arm);
|
|
273
|
+
const outputFd = fs.openSync(outPath, 'w');
|
|
274
|
+
try {
|
|
275
|
+
// Pass the prompt as one argv element. A shell wrapper on Windows can
|
|
276
|
+
// reinterpret spaces and punctuation, truncating prompts to `Add.`.
|
|
277
|
+
execFileSync(invocation.command, invocation.args, {
|
|
278
|
+
cwd: ws,
|
|
279
|
+
stdio: ['ignore', outputFd, 'inherit'],
|
|
280
|
+
timeout: engine === 'opencode' ? 20 * 60 * 1000 : 15 * 60 * 1000,
|
|
281
|
+
windowsHide: true,
|
|
282
|
+
});
|
|
283
|
+
} finally {
|
|
284
|
+
fs.closeSync(outputFd);
|
|
285
|
+
}
|
|
286
|
+
return outPath;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// The grader must judge what the AGENT said and did — never what it read.
|
|
290
|
+
// Tool outputs embed installed skill text, so keyword graders over raw
|
|
291
|
+
// transcripts produce false positives (a read of zoom-out/SKILL.md counts as
|
|
292
|
+
// "invoked zoom-out"). Trace = assistant text + tool inputs only.
|
|
293
|
+
function extractAgentTrace(transcriptPath) {
|
|
294
|
+
const raw = fs.readFileSync(transcriptPath, 'utf8');
|
|
295
|
+
const chunks = [];
|
|
296
|
+
let structured = false;
|
|
297
|
+
for (const line of raw.split('\n')) {
|
|
298
|
+
if (!line.trim()) continue;
|
|
299
|
+
let e;
|
|
300
|
+
try { e = JSON.parse(line); } catch { continue; }
|
|
301
|
+
const p = e && e.part;
|
|
302
|
+
if (!p) continue;
|
|
303
|
+
if (e.type === 'text' && typeof p.text === 'string') {
|
|
304
|
+
chunks.push(p.text);
|
|
305
|
+
structured = true;
|
|
306
|
+
} else if (String(e.type).includes('tool')) {
|
|
307
|
+
const input = p.state && p.state.input !== undefined ? p.state.input : p.input;
|
|
308
|
+
chunks.push(`[${p.tool || 'tool'}] ` + JSON.stringify(input ?? {}));
|
|
309
|
+
structured = true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// claude -p --output-format json emits one object; grade its result text.
|
|
313
|
+
if (!structured) {
|
|
314
|
+
try {
|
|
315
|
+
const obj = JSON.parse(raw);
|
|
316
|
+
if (obj && typeof obj.result === 'string') return obj.result;
|
|
317
|
+
} catch { /* fall through */ }
|
|
318
|
+
return raw;
|
|
319
|
+
}
|
|
320
|
+
return chunks.join('\n');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function extractSessionMetadata(transcriptPath) {
|
|
324
|
+
try {
|
|
325
|
+
const raw = fs.readFileSync(transcriptPath, 'utf8');
|
|
326
|
+
const obj = JSON.parse(raw);
|
|
327
|
+
const cost = [obj.total_cost_usd, obj.cost_usd, obj.cost].find(value => typeof value === 'number');
|
|
328
|
+
return {
|
|
329
|
+
cost: cost === undefined ? null : cost,
|
|
330
|
+
usage: obj.usage || null,
|
|
331
|
+
duration_ms: typeof obj.duration_ms === 'number' ? obj.duration_ms : null,
|
|
332
|
+
num_turns: typeof obj.num_turns === 'number' ? obj.num_turns : null,
|
|
333
|
+
};
|
|
334
|
+
} catch {
|
|
335
|
+
return { cost: null, usage: null, duration_ms: null, num_turns: null };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function runShellCommand(command, cwd) {
|
|
340
|
+
if (process.platform === 'win32') {
|
|
341
|
+
return execFileSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', command], { cwd, stdio: 'ignore' });
|
|
342
|
+
}
|
|
343
|
+
return execFileSync('/bin/sh', ['-c', command], { cwd, stdio: 'ignore' });
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function grade(c, ws, transcriptPath) {
|
|
347
|
+
const trace = extractAgentTrace(transcriptPath);
|
|
348
|
+
const results = [];
|
|
349
|
+
for (const e of c.expectations) {
|
|
350
|
+
let pass = false;
|
|
351
|
+
try {
|
|
352
|
+
if (e.type === 'trace_contains') pass = trace.includes(e.value);
|
|
353
|
+
else if (e.type === 'trace_not_contains') pass = !trace.includes(e.value);
|
|
354
|
+
else if (e.type === 'file_contains') pass = fs.readFileSync(path.join(ws, e.path), 'utf8').includes(e.value);
|
|
355
|
+
else if (e.type === 'file_not_exists') pass = !fs.existsSync(path.join(ws, e.path));
|
|
356
|
+
else if (e.type === 'command_exit_0') {
|
|
357
|
+
runShellCommand(e.command, ws);
|
|
358
|
+
pass = true;
|
|
359
|
+
}
|
|
360
|
+
} catch (err) {
|
|
361
|
+
pass = false;
|
|
362
|
+
}
|
|
363
|
+
results.push({ ...e, pass });
|
|
364
|
+
}
|
|
365
|
+
// Informational expectations are reported but never gate the outcome:
|
|
366
|
+
// they capture context-dependent behavior (e.g. zoom-out only becomes
|
|
367
|
+
// obligatory once the breaker actually trips) that would otherwise
|
|
368
|
+
// produce meaningless failures.
|
|
369
|
+
const gating = results.filter((r) => !r.informational);
|
|
370
|
+
return { results, passed: gating.length > 0 && gating.every((g) => g.pass) };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function resolveEngine(requested) {
|
|
374
|
+
const has = (cmd) => {
|
|
375
|
+
try {
|
|
376
|
+
// Try direct path first
|
|
377
|
+
execFileSync(cmd, ['--version'], { stdio: 'pipe' });
|
|
378
|
+
return true;
|
|
379
|
+
} catch {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
if (requested) {
|
|
384
|
+
if (!has(requested === 'claude' ? 'claude' : 'opencode')) fail(`engine "${requested}" not found on PATH.`);
|
|
385
|
+
return requested;
|
|
386
|
+
}
|
|
387
|
+
if (has('claude')) return 'claude';
|
|
388
|
+
if (has('opencode')) return 'opencode';
|
|
389
|
+
fail('Neither `claude` nor `opencode` CLI found. Behavioral evals need a real model session.');
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function sha256(value) {
|
|
393
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function countToolCalls(trace) {
|
|
397
|
+
return (trace.match(/^\[[^\]]+\]/gm) || []).length;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function treatmentSkills(c) {
|
|
401
|
+
return resolveTreatmentSkills(c);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function runArm(c, engine, arm, pairId) {
|
|
405
|
+
const loadedSkills = arm === 'treatment' ? treatmentSkills(c) : [];
|
|
406
|
+
const attributionRequired = arm === 'treatment';
|
|
407
|
+
const ws = buildWorkspace(c);
|
|
408
|
+
if (arm === 'treatment') installHarness(ws, loadedSkills);
|
|
409
|
+
if (c.fixture.git) gitSnapshot(ws);
|
|
410
|
+
|
|
411
|
+
const record = {
|
|
412
|
+
arm,
|
|
413
|
+
pair_id: pairId,
|
|
414
|
+
harness_loaded: arm === 'treatment',
|
|
415
|
+
loaded_skills: loadedSkills,
|
|
416
|
+
attribution_required: attributionRequired,
|
|
417
|
+
workspace: ws,
|
|
418
|
+
fixture_sha256: sha256(JSON.stringify(c.fixture)),
|
|
419
|
+
prompt_sha256: sha256(c.prompt),
|
|
420
|
+
cost: null,
|
|
421
|
+
};
|
|
422
|
+
try {
|
|
423
|
+
const transcriptPath = runHeadless(c.prompt, ws, c.max_turns, engine, arm);
|
|
424
|
+
const { results, passed } = grade(c, ws, transcriptPath);
|
|
425
|
+
const trace = extractAgentTrace(transcriptPath);
|
|
426
|
+
const metadata = extractSessionMetadata(transcriptPath);
|
|
427
|
+
if (attributionRequired && loadedSkills.length === 0) {
|
|
428
|
+
throw new Error('treatment attribution is required but no skill was loaded');
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
...record,
|
|
432
|
+
transcript: transcriptPath,
|
|
433
|
+
cost: metadata.cost,
|
|
434
|
+
usage: metadata.usage,
|
|
435
|
+
duration_ms: metadata.duration_ms,
|
|
436
|
+
num_turns: metadata.num_turns,
|
|
437
|
+
expectations: results.map(({ description, pass }) => ({ description, pass })),
|
|
438
|
+
outcome: passed ? 'pass' : 'fail',
|
|
439
|
+
tool_call_count: countToolCalls(trace),
|
|
440
|
+
trace_preview: trace.slice(0, 500),
|
|
441
|
+
};
|
|
442
|
+
} catch (err) {
|
|
443
|
+
return {
|
|
444
|
+
...record,
|
|
445
|
+
outcome: 'session-error',
|
|
446
|
+
error: err.message.slice(0, 500),
|
|
447
|
+
tool_call_count: 0,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function pairVerdict(baseline, treatment) {
|
|
453
|
+
if (baseline.outcome === 'pass' && treatment.outcome === 'pass') return 'INCONCLUSIVE';
|
|
454
|
+
if (baseline.outcome !== 'pass' && treatment.outcome === 'pass') return 'EFFECTIVE';
|
|
455
|
+
if (baseline.outcome === 'pass' && treatment.outcome !== 'pass') return 'HARMFUL';
|
|
456
|
+
return 'INEFFECTIVE';
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function wilsonInterval(successes, total) {
|
|
460
|
+
if (!total) return null;
|
|
461
|
+
const z = 1.96;
|
|
462
|
+
const p = successes / total;
|
|
463
|
+
const denominator = 1 + (z * z) / total;
|
|
464
|
+
const centre = (p + (z * z) / (2 * total)) / denominator;
|
|
465
|
+
const spread = (z / denominator) * Math.sqrt((p * (1 - p) / total) + (z * z) / (4 * total * total));
|
|
466
|
+
return [Math.max(0, centre - spread), Math.min(1, centre + spread)].map(v => Number(v.toFixed(4)));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function runLive(filter, engineArg, armArg = 'treatment') {
|
|
470
|
+
const validArms = new Set(['baseline', 'treatment', 'both']);
|
|
471
|
+
if (!validArms.has(armArg)) fail(`arm must be baseline, treatment, or both (got ${armArg})`);
|
|
472
|
+
const engine = resolveEngine(engineArg);
|
|
473
|
+
console.log(`Engine: ${engine}`);
|
|
474
|
+
console.log(`Arm mode: ${armArg}`);
|
|
475
|
+
const cases = discoverCases().filter((c) => !filter || c.id === filter);
|
|
476
|
+
if (filter && cases.length === 0) fail(`no case with id "${filter}"`);
|
|
477
|
+
fs.mkdirSync(RESULTS_DIR, { recursive: true });
|
|
478
|
+
|
|
479
|
+
const pairResults = [];
|
|
480
|
+
let sessionFailures = 0;
|
|
481
|
+
for (const c of cases) {
|
|
482
|
+
const pairId = `${c.id}-${Date.now()}-${crypto.randomBytes(3).toString('hex')}`;
|
|
483
|
+
const order = armArg === 'both'
|
|
484
|
+
? (crypto.randomInt(0, 2) === 0 ? ['baseline', 'treatment'] : ['treatment', 'baseline'])
|
|
485
|
+
: [armArg];
|
|
486
|
+
console.log(`\n=== ${c.id}${c.pressure ? ' [PRESSURE]' : ''} | order: ${order.join(' -> ')} ===`);
|
|
487
|
+
const arms = {};
|
|
488
|
+
for (const arm of order) arms[arm] = runArm(c, engine, arm, pairId);
|
|
489
|
+
|
|
490
|
+
const record = {
|
|
491
|
+
id: c.id,
|
|
492
|
+
pair_id: pairId,
|
|
493
|
+
engine,
|
|
494
|
+
model: engine === 'opencode' ? (process.env.BEHAVIORAL_MODEL || 'openai/gpt-5-mini') : 'claude-default',
|
|
495
|
+
pressure: !!c.pressure,
|
|
496
|
+
pressure_category: c.pressure_category || null,
|
|
497
|
+
sample_unit: 'paired case',
|
|
498
|
+
arm_order: order,
|
|
499
|
+
fixture_sha256: sha256(JSON.stringify(c.fixture)),
|
|
500
|
+
prompt_sha256: sha256(c.prompt),
|
|
501
|
+
arms,
|
|
502
|
+
verdict: armArg === 'both' ? pairVerdict(arms.baseline, arms.treatment) : null,
|
|
503
|
+
date: new Date().toISOString(),
|
|
504
|
+
};
|
|
505
|
+
pairResults.push(record);
|
|
506
|
+
if (Object.values(arms).some(arm => arm.outcome === 'session-error')) sessionFailures++;
|
|
507
|
+
for (const arm of Object.values(arms)) {
|
|
508
|
+
console.log(` ${arm.arm}: ${arm.outcome} (${arm.loaded_skills.length ? arm.loaded_skills.join(', ') : 'no Harness skills'})`);
|
|
509
|
+
}
|
|
510
|
+
if (armArg === 'both') console.log(` Verdict: ${record.verdict}`);
|
|
511
|
+
const suffix = armArg === 'both' ? 'pair' : armArg;
|
|
512
|
+
fs.writeFileSync(path.join(RESULTS_DIR, `${record.date.slice(0, 10)}-${c.id}-${suffix}.json`), JSON.stringify(record, null, 2));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (armArg === 'both') {
|
|
516
|
+
const completed = pairResults.filter(r => !Object.values(r.arms).some(a => a.outcome === 'session-error'));
|
|
517
|
+
const effective = completed.filter(r => r.verdict === 'EFFECTIVE').length;
|
|
518
|
+
const categorySummary = {};
|
|
519
|
+
for (const record of pairResults) {
|
|
520
|
+
const category = record.pressure_category || 'unclassified';
|
|
521
|
+
if (!categorySummary[category]) categorySummary[category] = { requested: 0, completed: 0, pass: 0, fail: 0 };
|
|
522
|
+
categorySummary[category].requested++;
|
|
523
|
+
}
|
|
524
|
+
for (const record of completed) {
|
|
525
|
+
const category = record.pressure_category || 'unclassified';
|
|
526
|
+
const bucket = categorySummary[category];
|
|
527
|
+
bucket.completed++;
|
|
528
|
+
if (record.arms.treatment.outcome === 'pass') bucket.pass++;
|
|
529
|
+
else bucket.fail++;
|
|
530
|
+
}
|
|
531
|
+
for (const bucket of Object.values(categorySummary)) {
|
|
532
|
+
bucket.pass_rate = Number((bucket.pass / bucket.completed).toFixed(4));
|
|
533
|
+
bucket.pass_rate_ci95 = wilsonInterval(bucket.pass, bucket.completed);
|
|
534
|
+
}
|
|
535
|
+
const costs = completed.flatMap(record => Object.values(record.arms).map(arm => arm.cost));
|
|
536
|
+
const cost = costs.length === completed.length * 2 && costs.every(value => typeof value === 'number')
|
|
537
|
+
? Number(costs.reduce((sum, value) => sum + value, 0).toFixed(6))
|
|
538
|
+
: null;
|
|
539
|
+
const summary = {
|
|
540
|
+
date: new Date().toISOString(),
|
|
541
|
+
protocol: 'paired-randomized-v1',
|
|
542
|
+
requested_sample_size: cases.length,
|
|
543
|
+
completed_pairs: completed.length,
|
|
544
|
+
session_failures: pairResults.length - completed.length,
|
|
545
|
+
boundary_compliance: completed.length === cases.length ? 'complete' : 'incomplete',
|
|
546
|
+
cost,
|
|
547
|
+
tool_call_delta: completed.map(r => ({
|
|
548
|
+
id: r.id,
|
|
549
|
+
baseline: r.arms.baseline.tool_call_count,
|
|
550
|
+
treatment: r.arms.treatment.tool_call_count,
|
|
551
|
+
treatment_minus_baseline: r.arms.treatment.tool_call_count - r.arms.baseline.tool_call_count,
|
|
552
|
+
})),
|
|
553
|
+
verdicts: Object.fromEntries(['EFFECTIVE', 'INEFFECTIVE', 'INCONCLUSIVE', 'HARMFUL'].map(v => [v, completed.filter(r => r.verdict === v).length])),
|
|
554
|
+
effective_rate_ci95: wilsonInterval(effective, completed.length),
|
|
555
|
+
pressure_categories: categorySummary,
|
|
556
|
+
interpretation: completed.length < 2
|
|
557
|
+
? 'insufficient paired samples for an effectiveness claim'
|
|
558
|
+
: 'report paired outcomes; do not treat INCONCLUSIVE pairs as skill lift',
|
|
559
|
+
};
|
|
560
|
+
const summaryPath = path.join(RESULTS_DIR, `${new Date().toISOString().slice(0, 10)}-summary-both.json`);
|
|
561
|
+
fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2));
|
|
562
|
+
console.log(`\nPaired summary: ${summaryPath}`);
|
|
563
|
+
console.log(`Completed pairs: ${summary.completed_pairs}/${summary.requested_sample_size}`);
|
|
564
|
+
}
|
|
565
|
+
process.exit(sessionFailures ? 1 : 0);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// ----------------------------------------------------------------------------
|
|
569
|
+
function main() {
|
|
570
|
+
const args = process.argv.slice(2);
|
|
571
|
+
function flag(name) {
|
|
572
|
+
const i = args.indexOf(name);
|
|
573
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
574
|
+
}
|
|
575
|
+
if (args[0] === 'validate') validate(discoverCases());
|
|
576
|
+
else if (args[0] === 'run') {
|
|
577
|
+
const filter = args.includes('--case') ? flag('--case') : undefined;
|
|
578
|
+
runLive(filter, args.includes('--engine') ? flag('--engine') : undefined, args.includes('--arm') ? flag('--arm') : undefined);
|
|
579
|
+
} else {
|
|
580
|
+
console.log('Usage:\n node behavioral-evals/run.js validate\n node behavioral-evals/run.js run [--case <id>] [--arm baseline|treatment|both] [--engine claude|opencode]');
|
|
581
|
+
process.exit(args.length ? 1 : 0);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (require.main === module) main();
|
|
586
|
+
|
|
587
|
+
module.exports = { buildEngineInvocation, extractAgentTrace, extractSessionMetadata, grade, parseSimpleYaml, validate };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Benchmarks
|
|
2
|
+
|
|
3
|
+
Executable companion to [BENCHMARK_SOP.md](../BENCHMARK_SOP.md). The SOP
|
|
4
|
+
describes *how* to test; this directory is where results live — committed,
|
|
5
|
+
dated, and traceable to exported session logs.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
| Scenario | Vanilla | Harness |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| test-a-overengineering | not run | not run |
|
|
12
|
+
| test-b-micro-loop | not run | not run |
|
|
13
|
+
| test-c-attention-loss | not run | not run |
|
|
14
|
+
| test-d-knowledge-boundary | not run | not run |
|
|
15
|
+
| test-e-shell-awareness | not run | not run |
|
|
16
|
+
| test-f-fact-audit | not run | not run |
|
|
17
|
+
|
|
18
|
+
**Until cells above are filled, no effectiveness claim about Harness is backed
|
|
19
|
+
by recorded evidence.** Self-audit scores in `docs/audit.md` measure mechanism
|
|
20
|
+
correctness (do the hooks fire), not behavioral effect (does the agent actually
|
|
21
|
+
behave differently). These benchmarks measure the latter.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 1. Build the fixture workspace for a scenario and get the exact prompt
|
|
27
|
+
node benchmarks/run.js scaffold test-a-overengineering
|
|
28
|
+
|
|
29
|
+
# 2. Run the prompt twice in real agent sessions:
|
|
30
|
+
# once with NO skills loaded (vanilla), once with Harness installed.
|
|
31
|
+
# Export both conversation logs into benchmarks/results/logs/.
|
|
32
|
+
|
|
33
|
+
# 3. Register both runs (evidence log required — no log, no result)
|
|
34
|
+
node benchmarks/run.js record test-a-overengineering \
|
|
35
|
+
--variant vanilla --model claude-sonnet-4.6 --outcome partial \
|
|
36
|
+
--tokens 2140 --steps 11 --evidence logs/test-a-vanilla.md
|
|
37
|
+
|
|
38
|
+
node benchmarks/run.js record test-a-overengineering \
|
|
39
|
+
--variant harness --model claude-sonnet-4.6 --outcome pass \
|
|
40
|
+
--tokens 380 --steps 2 --triggered harness-everything \
|
|
41
|
+
--evidence logs/test-a-harness.md
|
|
42
|
+
|
|
43
|
+
# 4. Check coverage
|
|
44
|
+
node benchmarks/run.js status
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Result format
|
|
48
|
+
|
|
49
|
+
Each record follows `schema.json`: scenario, variant, model, date,
|
|
50
|
+
harness_version, outcome, token/step counts, triggered skills, notes, and a
|
|
51
|
+
content-hash `id` derived from the evidence log so records can't silently drift
|
|
52
|
+
from their evidence.
|
|
53
|
+
|
|
54
|
+
## Honesty rules
|
|
55
|
+
|
|
56
|
+
1. **No log, no result** — `record` refuses entries whose evidence file is missing.
|
|
57
|
+
2. Record failures too (`--outcome fail`). A benchmark suite where Harness
|
|
58
|
+
passes everything is a red flag, not a victory lap.
|
|
59
|
+
3. Note the model per run. Results are model-specific; never generalize a
|
|
60
|
+
single-model result across models.
|
|
61
|
+
4. Pair every behavioral result here with a mechanism check from
|
|
62
|
+
`VERIFICATION.md` §2 — a well-behaved model can pass these scenarios even if
|
|
63
|
+
every hook silently no-ops (see the 2026-07-20 incident note in BENCHMARK_SOP.md).
|