harness-everything 0.3.6 → 0.4.1
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/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +8 -7
- package/.github/workflows/behavioral-evals.yml +3 -3
- package/.github/workflows/ci.yml +49 -28
- package/.github/workflows/release.yml +52 -27
- package/.nvmrc +1 -0
- package/.releaserc.json +113 -0
- package/AGENTS.md +44 -24
- package/BENCHMARK_SOP.md +38 -18
- package/CHANGELOG.md +196 -3
- package/CONTRIBUTING.md +37 -9
- package/Dockerfile +1 -1
- package/PRIVACY.md +25 -0
- package/README.md +126 -91
- package/RELEASING.md +93 -0
- package/SUPPORT.md +27 -0
- package/TERMS.md +21 -0
- package/VERIFICATION.md +192 -284
- package/behavioral-evals/README.md +21 -0
- package/behavioral-evals/case-validator.js +194 -0
- package/behavioral-evals/cases/baseline-environment-detection.yaml +21 -0
- package/behavioral-evals/cases/baseline-eval-harness.yaml +27 -0
- package/behavioral-evals/cases/baseline-fable-discipline.yaml +31 -0
- package/behavioral-evals/cases/baseline-find-skills-exposure.yaml +49 -0
- package/behavioral-evals/cases/baseline-find-skills.yaml +21 -0
- package/behavioral-evals/cases/baseline-git-commit.yaml +2 -2
- package/behavioral-evals/cases/baseline-install-cognitive-os.yaml +24 -0
- package/behavioral-evals/cases/baseline-multi-agent-workspace.yaml +28 -0
- package/behavioral-evals/cases/baseline-rewrite-commits.yaml +34 -0
- package/behavioral-evals/cases/baseline-self-evolve.yaml +31 -0
- package/behavioral-evals/cases/baseline-skill-creator.yaml +20 -0
- package/behavioral-evals/cases/baseline-skill-style.yaml +21 -0
- package/behavioral-evals/cases/baseline-to-spec.yaml +34 -0
- package/behavioral-evals/cases/baseline-to-tickets.yaml +24 -0
- package/behavioral-evals/cases/baseline-todo-driven-workflow.yaml +32 -0
- package/behavioral-evals/cases/baseline-using-git-worktrees.yaml +15 -0
- package/behavioral-evals/cases/breaker-zoom-out-after-3.yaml +2 -2
- package/behavioral-evals/cases/pressure-complexity-skip.yaml +2 -2
- package/behavioral-evals/cases/pressure-scope-bypass.yaml +7 -2
- package/behavioral-evals/cases/pressure-sunk-cost-retry.yaml +7 -4
- package/behavioral-evals/cases/scope-discipline.yaml +2 -1
- package/behavioral-evals/cases/verify-before-done.yaml +7 -5
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/case.yaml +34 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/historical-case.yaml +34 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/historical-case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/historical-case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/historical-case.yaml +18 -0
- package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/live-run-blocker.md +16 -0
- package/behavioral-evals/evidence/2026-09-07/manifest.json +88 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/historical-case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/historical-case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/case.yaml +35 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/historical-case.yaml +35 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/historical-case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/historical-case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/historical-case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/historical-case.yaml +47 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/historical-case.yaml +47 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/historical-case.yaml +47 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/tdd-test-first/case.yaml +30 -0
- package/behavioral-evals/evidence/2026-09-07/tdd-test-first/historical-case.yaml +23 -0
- package/behavioral-evals/evidence/2026-09-07/tdd-test-first/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/triage-matrix.json +426 -0
- package/behavioral-evals/evidence/2026-09-07/triage-matrix.md +24 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/case.yaml +19 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/historical-case.yaml +18 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/historical-case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/result.sanitized.json +480 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/transcript.sanitized.jsonl +95 -0
- package/behavioral-evals/evidence-tool.js +443 -0
- package/behavioral-evals/fixtures/claude-denied.jsonl +3 -0
- package/behavioral-evals/fixtures/claude-legacy.json +1 -0
- package/behavioral-evals/fixtures/claude-oral-only.jsonl +2 -0
- package/behavioral-evals/fixtures/claude-success.jsonl +5 -0
- package/behavioral-evals/fixtures/claude-truncated.jsonl +3 -0
- package/behavioral-evals/fixtures/opencode-success.jsonl +6 -0
- package/behavioral-evals/results/2026-08-31-baseline-add-feature.json +20 -0
- package/behavioral-evals/results/2026-09-03-baseline-environment-detection-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-eval-harness-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-fable-discipline-pair.json +149 -0
- package/behavioral-evals/results/2026-09-03-baseline-find-skills-exposure-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-find-skills-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-install-cognitive-os-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-multi-agent-workspace-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-rewrite-commits-pair.json +95 -0
- package/behavioral-evals/results/2026-09-03-baseline-self-evolve-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-skill-creator-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-skill-style-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-to-spec-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-to-tickets-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-todo-driven-workflow-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-using-git-worktrees-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-summary-both.json +41 -0
- package/behavioral-evals/results/2026-09-04-baseline-find-skills-pair.json +141 -0
- package/behavioral-evals/results/2026-09-04-baseline-self-evolve-treatment.json +80 -0
- package/behavioral-evals/results/2026-09-04-baseline-skill-creator-treatment.json +80 -0
- package/behavioral-evals/results/2026-09-04-summary-both.json +41 -0
- package/behavioral-evals/run-with-plugin.js +15 -31
- package/behavioral-evals/run.js +446 -105
- package/behavioral-evals/transcript-parser.js +591 -0
- package/bin/cli.js +24 -12
- package/ci/ab-test-cases/grill-me-adversarial.yaml +14 -4
- package/ci/ab-test-cases/tdd-test-first.yaml +13 -6
- package/ci/ab-test-cases/verification-loop-gates.yaml +20 -7
- package/ci/ab-test-cases/verify-before-claim-cites.yaml +7 -6
- package/ci/ab-test-harness.js +113 -38
- package/ci/action-gate-platform-doc.test.js +31 -0
- package/ci/action-gate.test.js +260 -0
- package/ci/cases/case2.json +2 -2
- package/ci/consistency-check.js +51 -5
- package/ci/description-collision.js +15 -6
- package/ci/disclosure-check.js +225 -0
- package/ci/doc-capability-consistency.test.js +180 -0
- package/ci/fixtures/phase4/parallel-disjoint.stages.json +32 -0
- package/ci/fixtures/phase4/parallel-read-only.stages.json +32 -0
- package/ci/installer-roundtrip.test.js +159 -0
- package/ci/invariant-routing.test.js +74 -0
- package/ci/issue99-runtime.test.js +218 -0
- package/ci/mechanism-2aa-codex-action-gate-policy.test.js +117 -0
- package/ci/mechanism-2ab-openai-plugin-sync-integrity.test.js +80 -0
- package/ci/mechanism-2j-behavioral-runner.test.js +43 -1
- package/ci/mechanism-2k-reference-check.test.js +57 -2
- package/ci/mechanism-2l-multi-agent-workspace.test.js +202 -6
- package/ci/mechanism-2m-self-repo-guard.test.js +65 -0
- package/ci/mechanism-2n-opencode-plugin.test.js +461 -0
- package/ci/mechanism-2o-state-home-isolation.test.js +183 -0
- package/ci/mechanism-2p-installer-links.test.js +183 -0
- package/ci/mechanism-2q-installer-global-shared-store.test.js +132 -0
- package/ci/mechanism-2r-uninstall-flags.test.js +148 -0
- package/ci/mechanism-2s-tdd-quality.test.js +203 -0
- package/ci/mechanism-2t-disclosure.test.js +90 -0
- package/ci/mechanism-2t-evidence-triage.test.js +200 -0
- package/ci/mechanism-2t-issue-20.test.js +75 -0
- package/ci/mechanism-2t-platform-skill-targets.test.js +164 -0
- package/ci/mechanism-2t-transcript-parser.test.js +179 -0
- package/ci/mechanism-2t-workspace-identity.test.js +200 -0
- package/ci/mechanism-2u-issue-42-regressions.test.js +239 -0
- package/ci/mechanism-2u-issue-44.test.js +35 -0
- package/ci/mechanism-2v-verifier-boundaries.test.js +138 -0
- package/ci/mechanism-2w-platform-compatibility.test.js +148 -0
- package/ci/mechanism-2x-release-automation.test.js +157 -0
- package/ci/mechanism-2x-release-commit-message.test.js +66 -0
- package/ci/mechanism-2y-release-tooling-compat.test.js +47 -0
- package/ci/mechanism-2z-claude-plugin-manifest.test.js +29 -0
- package/ci/mutation-check.js +53 -0
- package/ci/mutation-loader.js +57 -0
- package/ci/negative-controls.js +76 -46
- package/ci/openai-plugin-package.test.js +182 -0
- package/ci/openai-plugin-runtime.test.js +175 -0
- package/ci/openai-plugin-submission.test.js +101 -0
- package/ci/phase4-ensemble-memory.test.js +307 -0
- package/ci/reference-check.js +73 -25
- package/ci/release-consistency-check.js +24 -1
- package/ci/repository-contract.test.js +17 -0
- package/ci/router-phase2-safety.test.js +81 -0
- package/ci/router-workflow-plan.test.js +264 -0
- package/ci/routing-check.js +6 -5
- package/ci/runner.js +6 -5
- package/ci/skill-routing-check.js +48 -11
- package/ci/test-behavioral-case-validation.js +62 -0
- package/ci/test-helper.js +10 -0
- package/ci/workflow-plan-consumers-edge.test.js +72 -0
- package/ci/workflow-plan-consumers.test.js +276 -0
- package/docs/architecture.md +137 -57
- package/docs/audit.md +22 -20
- package/docs/codex-action-gate.md +90 -0
- package/docs/issue-20-rollup.md +47 -0
- package/docs/mechanism-first-skill-mesh.md +22 -20
- package/docs/openai-plugin.md +118 -0
- package/docs/philosophy.md +28 -17
- package/docs/platform-capabilities.md +73 -0
- package/docs/platform-compatibility.json +270 -0
- package/docs/reflection.md +37 -22
- package/docs/release-evidence.md +27 -0
- package/docs/repository-contract.md +27 -0
- package/docs/routing.md +157 -44
- package/docs/security-audit.md +38 -0
- package/docs/troubleshooting.md +94 -51
- package/docs/verifier-boundaries.md +45 -0
- package/docs/workflows/fable-mode.md +77 -21
- package/docs/workflows/find-skills.md +40 -0
- package/docs/workflows/grill-with-docs.md +2 -2
- package/docs/workflows/multi-agent-workspace.md +22 -10
- package/docs/workflows/self-evolve.md +33 -0
- package/docs/workflows/tdd.md +36 -0
- package/docs/workflows/todo-driven-workflow.md +33 -0
- package/environment-detection/SKILL.md +3 -3
- package/eval-harness/SKILL.md +7 -7
- package/eval-harness/scripts/evaluate.js +12 -10
- package/evals/evaluation-reports.md +14 -0
- package/evals/harness-everything/tasks/positive-tier1.yaml +1 -1
- package/evals/harness-everything/tasks/positive-tier2.yaml +1 -1
- package/evals/harness-everything/tasks/positive-tier3.yaml +1 -1
- package/evals/install-cognitive-os/tasks/positive-trigger.yaml +1 -1
- package/fable-discipline/SKILL.md +2 -2
- package/fable-mode/CONTRACT-FORMAT.md +147 -33
- package/fable-mode/SKILL.md +3 -3
- package/fable-mode/agents/fable-orchestrator.md +95 -35
- package/fable-mode/execution-guardrails/SKILL.md +2 -2
- package/fable-mode/fable-haiku/SKILL.md +3 -3
- package/fable-mode/fable-opus/SKILL.md +3 -3
- package/fable-mode/fable-sonnet/SKILL.md +3 -3
- package/fable-mode/scripts/ensemble-review.js +371 -0
- package/fable-mode/scripts/model-selector.js +134 -3
- package/fable-mode/scripts/workflow-plan-consumer.js +337 -0
- package/find-skills/SKILL.md +5 -5
- package/find-skills/references/discovery-flow.md +1 -1
- package/find-skills/scripts/use-skill.js +7 -1
- package/git-commit/SKILL.md +4 -11
- package/grill-me/SKILL.md +5 -5
- package/grill-with-docs/SKILL.md +7 -7
- package/grill-with-docs/references/session-playbook.md +6 -4
- package/harness-everything/SKILL.md +23 -22
- package/harness-everything/references/router-workflow-plan.md +128 -0
- package/harness-everything/references/triage-and-tiers.md +173 -96
- package/harness-everything/schemas/router-task-shape.schema.json +126 -0
- package/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
- package/harness-everything/scripts/bootstrap.js +13 -4
- package/harness-everything/scripts/ensemble-policy.js +102 -0
- package/harness-everything/scripts/kernel-router.js +212 -0
- package/harness-everything/scripts/router-contract.js +612 -0
- package/harness-everything/scripts/self-heal.js +29 -20
- package/harness-everything/scripts/tier-router.js +346 -221
- package/hooks/hooks.json +60 -15
- package/hooks/scripts/action-gate-rules.json +77 -0
- package/hooks/scripts/action-gate.js +528 -0
- package/hooks/scripts/atomic-commit-check.js +5 -5
- package/hooks/scripts/codex-action-gate-post.js +46 -0
- package/hooks/scripts/codex-permission-request.js +140 -0
- package/hooks/scripts/context-compact.js +4 -3
- package/hooks/scripts/contract-test.js +118 -51
- package/hooks/scripts/lib/fable-contracts.js +121 -0
- package/hooks/scripts/lib/harness-state.js +164 -28
- package/hooks/scripts/lib/platforms/claude.js +45 -62
- package/hooks/scripts/lib/platforms/codex.js +35 -61
- package/hooks/scripts/lib/platforms/continue.js +7 -7
- package/hooks/scripts/lib/platforms/copilot.js +29 -53
- package/hooks/scripts/lib/platforms/hermes.js +71 -20
- package/hooks/scripts/openai-session-start.js +81 -0
- package/hooks/scripts/rule-of-3-tracker.js +9 -8
- package/hooks/scripts/rule-of-3.js +29 -21
- package/hooks/scripts/state-persist.js +5 -4
- package/hooks/scripts/stop-gate.js +2 -2
- package/hooks/scripts/subagent-scope-guard.js +144 -38
- package/hooks/scripts/subagent-start.js +19 -0
- package/hooks/scripts/subagent-stop.js +6 -0
- package/improve-codebase-architecture/SKILL.md +2 -4
- package/improve-codebase-architecture/references/README.md +16 -0
- package/install-cognitive-os/SKILL.md +28 -22
- package/multi-agent-workspace/SKILL.md +16 -15
- package/multi-agent-workspace/references/architecture-guide.md +13 -9
- package/multi-agent-workspace/references/orchestration.md +39 -22
- package/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
- package/multi-agent-workspace/scripts/index_memory.js +79 -26
- package/multi-agent-workspace/scripts/path-boundary.js +59 -0
- package/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
- package/multi-agent-workspace/scripts/scaffold.js +258 -103
- package/multi-agent-workspace/templates/AGENTS.md +20 -0
- package/multi-agent-workspace/workflows/03-generation.md +8 -10
- package/multi-agent-workspace/workflows/04-launcher.md +5 -5
- package/opencode-plugin/README.md +114 -71
- package/opencode-plugin/hooks/verify.js +62 -12
- package/opencode-plugin/index.mjs +547 -0
- package/opencode-plugin/plugin.json +4 -12
- package/package.json +20 -4
- package/plugins/harness-everything/.codex-plugin/plugin.json +39 -0
- package/plugins/harness-everything/hooks/hooks.json +197 -0
- package/plugins/harness-everything/hooks/scripts/action-gate-rules.json +77 -0
- package/plugins/harness-everything/hooks/scripts/action-gate.js +528 -0
- package/plugins/harness-everything/hooks/scripts/atomic-commit-check.js +68 -0
- package/plugins/harness-everything/hooks/scripts/boundary-guard.js +55 -0
- package/plugins/harness-everything/hooks/scripts/codex-action-gate-post.js +46 -0
- package/plugins/harness-everything/hooks/scripts/codex-permission-request.js +140 -0
- package/plugins/harness-everything/hooks/scripts/context-compact.js +115 -0
- package/plugins/harness-everything/hooks/scripts/contract-test.js +146 -0
- package/plugins/harness-everything/hooks/scripts/depth-guard.js +65 -0
- package/plugins/harness-everything/hooks/scripts/lib/fable-contracts.js +121 -0
- package/plugins/harness-everything/hooks/scripts/lib/harness-state.js +275 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/claude.js +168 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/codex.js +88 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/continue.js +91 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/copilot.js +85 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/cursor.js +89 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/hermes.js +108 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/index.js +17 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/worktrees.js +20 -0
- package/plugins/harness-everything/hooks/scripts/lib/workspace.js +290 -0
- package/plugins/harness-everything/hooks/scripts/openai-session-start.js +81 -0
- package/plugins/harness-everything/hooks/scripts/reset-circuit-breaker.js +30 -0
- package/plugins/harness-everything/hooks/scripts/rule-of-3-tracker.js +125 -0
- package/plugins/harness-everything/hooks/scripts/rule-of-3.js +195 -0
- package/plugins/harness-everything/hooks/scripts/state-persist.js +100 -0
- package/plugins/harness-everything/hooks/scripts/stop-gate.js +80 -0
- package/plugins/harness-everything/hooks/scripts/subagent-scope-guard.js +194 -0
- package/plugins/harness-everything/hooks/scripts/subagent-start.js +19 -0
- package/plugins/harness-everything/hooks/scripts/subagent-stop.js +6 -0
- package/plugins/harness-everything/hooks/session-start.js +81 -0
- package/plugins/harness-everything/plugin.json +43 -0
- package/plugins/harness-everything/scripts/lib/advisory-text.js +174 -0
- package/plugins/harness-everything/skills/environment-detection/SKILL.md +41 -0
- package/plugins/harness-everything/skills/environment-detection/references/shell-syntax-rules.md +76 -0
- package/plugins/harness-everything/skills/environment-detection/scripts/preflight.js +103 -0
- package/plugins/harness-everything/skills/eval-harness/SKILL.md +42 -0
- package/plugins/harness-everything/skills/eval-harness/references/scoring-rubric.md +68 -0
- package/plugins/harness-everything/skills/eval-harness/scripts/evaluate.js +79 -0
- package/plugins/harness-everything/skills/fable-discipline/SKILL.md +41 -0
- package/plugins/harness-everything/skills/fable-discipline/references/discipline-rules.md +22 -0
- package/plugins/harness-everything/skills/fable-mode/CONTRACT-FORMAT.md +165 -0
- package/plugins/harness-everything/skills/fable-mode/SKILL.md +39 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +159 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-verifier.md +32 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +52 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +53 -0
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +43 -0
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +56 -0
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +36 -0
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/references/routing-notes.md +26 -0
- package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +36 -0
- package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +36 -0
- package/plugins/harness-everything/skills/fable-mode/model-matrix.json +32 -0
- package/plugins/harness-everything/skills/fable-mode/references/execution-phases.md +37 -0
- package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +39 -0
- package/plugins/harness-everything/skills/fable-mode/scripts/ensemble-review.js +371 -0
- package/plugins/harness-everything/skills/fable-mode/scripts/model-selector.js +260 -0
- package/plugins/harness-everything/skills/fable-mode/scripts/workflow-plan-consumer.js +337 -0
- package/plugins/harness-everything/skills/find-skills/SKILL.md +48 -0
- package/plugins/harness-everything/skills/find-skills/references/discovery-flow.md +102 -0
- package/plugins/harness-everything/skills/find-skills/scripts/use-skill.js +112 -0
- package/plugins/harness-everything/skills/git-commit/SKILL.md +38 -0
- package/plugins/harness-everything/skills/git-commit/guides/ANGULAR_STYLE.md +61 -0
- package/plugins/harness-everything/skills/git-commit/guides/COMMIT_GENERATION.md +24 -0
- package/plugins/harness-everything/skills/git-commit/guides/LANGUAGE_DETECTION.md +16 -0
- package/plugins/harness-everything/skills/git-commit/guides/MAIN_REPO.md +15 -0
- package/plugins/harness-everything/skills/git-commit/guides/SUBMODULES.md +27 -0
- package/plugins/harness-everything/skills/git-commit/references/commit-flow.md +30 -0
- package/plugins/harness-everything/skills/grill-me/SKILL.md +43 -0
- package/plugins/harness-everything/skills/grill-me/references/grilling-playbook.md +38 -0
- package/plugins/harness-everything/skills/grill-with-docs/ADR-FORMAT.md +47 -0
- package/plugins/harness-everything/skills/grill-with-docs/CONTEXT-FORMAT.md +63 -0
- package/plugins/harness-everything/skills/grill-with-docs/SKILL.md +41 -0
- package/plugins/harness-everything/skills/grill-with-docs/references/session-playbook.md +99 -0
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +43 -0
- package/plugins/harness-everything/skills/harness-everything/examples/README.md +9 -0
- package/plugins/harness-everything/skills/harness-everything/examples/todo-workflow.md +22 -0
- package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +128 -0
- package/plugins/harness-everything/skills/harness-everything/references/skill-registry.md +39 -0
- package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +193 -0
- package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +126 -0
- package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/bootstrap.js +212 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/ensemble-policy.js +102 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +212 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/router-contract.js +612 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/routing-keywords.json +320 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/self-heal.js +250 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +459 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/verify-gate.js +100 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/SKILL.md +38 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/DEEPENING.md +37 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/HTML-REPORT.md +123 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/INTERFACE-DESIGN.md +44 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/LANGUAGE.md +53 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/refactoring-workflow.md +59 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/references/README.md +16 -0
- package/plugins/harness-everything/skills/install-cognitive-os/SKILL.md +44 -0
- package/plugins/harness-everything/skills/install-cognitive-os/references/cognitive-loop.md +27 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/NOTICE-agency-agents.md +27 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +38 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/examples/csharp-monorepo.md +123 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/examples/typescript-cli.md +126 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-claude.md +77 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-codex.md +146 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-continue.md +38 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-copilot.md +74 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-cursor.md +40 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-hermes.md +38 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/role-capabilities.md +108 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/references/agency-agents.md +31 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/references/architecture-guide.md +36 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/references/orchestration.md +65 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/agency-catalog.js +190 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/index_memory.js +104 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/path-boundary.js +59 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/scaffold.js +408 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/AGENTS.md +20 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/README.md +17 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/agent-launcher.md +68 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-csharp.md +56 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-polyglot.md +62 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-ts.md +57 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/memory-keeper.md +37 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/requirement-analyzer.md +66 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/specialist-agent.md +34 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/task-verifier.md +56 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/tool-maker.md +33 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/01-init.md +22 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/02-analysis.md +23 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/03-generation.md +25 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/04-launcher.md +16 -0
- package/plugins/harness-everything/skills/repo-docs/SKILL.md +42 -0
- package/plugins/harness-everything/skills/repo-docs/references/process-guide.md +60 -0
- package/plugins/harness-everything/skills/repo-docs/templates/agents-template.md +264 -0
- package/plugins/harness-everything/skills/repo-docs/templates/knowledge-agents-template.md +50 -0
- package/plugins/harness-everything/skills/repo-docs/templates/knowledge-readme-template.md +83 -0
- package/plugins/harness-everything/skills/repo-docs/templates/manual-recon.md +117 -0
- package/plugins/harness-everything/skills/repo-docs/templates/multi-skills-readme-template.md +72 -0
- package/plugins/harness-everything/skills/repo-docs/templates/product-agents-template.md +78 -0
- package/plugins/harness-everything/skills/repo-docs/templates/product-readme-template.md +95 -0
- package/plugins/harness-everything/skills/repo-docs/templates/readme-template.md +136 -0
- package/plugins/harness-everything/skills/repo-docs/templates/single-skill-readme-template.md +70 -0
- package/plugins/harness-everything/skills/repo-docs/templates/skills-agents-template.md +60 -0
- package/plugins/harness-everything/skills/rewrite-commits/SKILL.md +37 -0
- package/plugins/harness-everything/skills/rewrite-commits/references/workflow.md +18 -0
- package/plugins/harness-everything/skills/security-review/SKILL.md +47 -0
- package/plugins/harness-everything/skills/security-review/guides/OWASP-PATTERNS.md +31 -0
- package/plugins/harness-everything/skills/security-review/guides/STRIDE-THREAT-MODEL.md +21 -0
- package/plugins/harness-everything/skills/security-review/references/security-checklist.md +478 -0
- package/plugins/harness-everything/skills/security-review/scripts/audit-secrets.js +85 -0
- package/plugins/harness-everything/skills/self-evolve/SKILL.md +45 -0
- package/plugins/harness-everything/skills/self-evolve/references/memory-resolution.md +61 -0
- package/plugins/harness-everything/skills/self-evolve/scripts/persist-memory.js +272 -0
- package/plugins/harness-everything/skills/self-evolve/scripts/register-dynamic-skill.js +253 -0
- package/plugins/harness-everything/skills/self-evolve/scripts/self-regression.js +125 -0
- package/plugins/harness-everything/skills/skill-creator/SKILL.md +47 -0
- package/plugins/harness-everything/skills/skill-creator/references/authoring-workflow.md +14 -0
- package/plugins/harness-everything/skills/skill-creator/references/dynamic-generation-contract.md +21 -0
- package/plugins/harness-everything/skills/skill-creator/references/quality-checklist.md +16 -0
- package/plugins/harness-everything/skills/skill-creator/references/quality-principles.md +81 -0
- package/plugins/harness-everything/skills/skill-creator/references/testing-workflow.md +27 -0
- package/plugins/harness-everything/skills/skill-style/SKILL.md +42 -0
- package/plugins/harness-everything/skills/skill-style/references/style-guide.md +62 -0
- package/plugins/harness-everything/skills/tdd/SKILL.md +38 -0
- package/plugins/harness-everything/skills/tdd/fixtures/pass-evidence.json +100 -0
- package/plugins/harness-everything/skills/tdd/fixtures/quality-scenarios.json +20 -0
- package/plugins/harness-everything/skills/tdd/guides/deep-modules.md +33 -0
- package/plugins/harness-everything/skills/tdd/guides/interface-design.md +31 -0
- package/plugins/harness-everything/skills/tdd/guides/mocking.md +59 -0
- package/plugins/harness-everything/skills/tdd/guides/refactoring.md +10 -0
- package/plugins/harness-everything/skills/tdd/guides/tests.md +61 -0
- package/plugins/harness-everything/skills/tdd/references/core-discipline.md +51 -0
- package/plugins/harness-everything/skills/tdd/references/integration-testing.md +13 -0
- package/plugins/harness-everything/skills/tdd/references/quality-model.md +68 -0
- package/plugins/harness-everything/skills/tdd/references/unit-testing.md +15 -0
- package/plugins/harness-everything/skills/tdd/schemas/quality-evidence.schema.json +211 -0
- package/plugins/harness-everything/skills/tdd/schemas/quality-report.schema.json +110 -0
- package/plugins/harness-everything/skills/tdd/scripts/quality-gate.js +475 -0
- package/plugins/harness-everything/skills/to-spec/SKILL.md +40 -0
- package/plugins/harness-everything/skills/to-spec/references/process.md +77 -0
- package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +239 -0
- package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +111 -0
- package/plugins/harness-everything/skills/to-spec/templates/cli-reference.md +31 -0
- package/plugins/harness-everything/skills/to-spec/templates/dev-doc.md +27 -0
- package/plugins/harness-everything/skills/to-spec/templates/feature-spec.md +59 -0
- package/plugins/harness-everything/skills/to-spec/templates/schema-doc.md +31 -0
- package/plugins/harness-everything/skills/to-tickets/SKILL.md +42 -0
- package/plugins/harness-everything/skills/to-tickets/references/publishing-and-templates.md +97 -0
- package/plugins/harness-everything/skills/todo-driven-workflow/SKILL.md +42 -0
- package/plugins/harness-everything/skills/todo-driven-workflow/references/execution-guide.md +27 -0
- package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +49 -0
- package/plugins/harness-everything/skills/using-git-worktrees/references/workflow-details.md +108 -0
- package/plugins/harness-everything/skills/verification-loop/SKILL.md +46 -0
- package/plugins/harness-everything/skills/verification-loop/references/verification-phases.md +116 -0
- package/plugins/harness-everything/skills/verification-loop/templates/verification-report.template.md +15 -0
- package/plugins/harness-everything/skills/verify-before-claim/SKILL.md +43 -0
- package/plugins/harness-everything/skills/verify-before-claim/references/verification-guide.md +73 -0
- package/plugins/harness-everything/skills/zoom-out/SKILL.md +42 -0
- package/plugins/harness-everything/skills/zoom-out/references/circuit-breaker.md +66 -0
- package/plugins/harness-everything/skills/zoom-out/templates/zoom-out-report.template.md +20 -0
- package/repo-docs/SKILL.md +3 -7
- package/rewrite-commits/SKILL.md +2 -2
- package/scripts/build-openai-submission.js +158 -0
- package/scripts/installer.js +86 -11
- package/scripts/lib/advisory-text.js +25 -18
- package/scripts/lib/execution-contract.js +70 -0
- package/scripts/lib/manifest.js +34 -38
- package/scripts/lib/path-boundary.js +90 -0
- package/scripts/lib/skills.js +124 -70
- package/scripts/lib/workspace.js +269 -7
- package/scripts/repository-contract.js +213 -0
- package/scripts/sync-current-docs.js +12 -0
- package/scripts/sync-openai-plugin.js +91 -0
- package/scripts/sync-release-version.js +212 -0
- package/security-review/SKILL.md +4 -7
- package/security-review/scripts/audit-secrets.js +9 -8
- package/self-evolve/SKILL.md +9 -11
- package/self-evolve/references/memory-resolution.md +4 -1
- package/self-evolve/scripts/persist-memory.js +221 -88
- package/self-evolve/scripts/register-dynamic-skill.js +12 -10
- package/self-evolve/scripts/self-regression.js +23 -8
- package/skill-creator/SKILL.md +12 -14
- package/skill-creator/references/quality-checklist.md +1 -0
- package/skill-style/SKILL.md +4 -4
- package/skill-style/references/style-guide.md +29 -3
- package/submission/openai/README.md +67 -0
- package/submission/openai/listing.json +31 -0
- package/submission/openai/test-cases.json +61 -0
- package/tdd/SKILL.md +20 -26
- package/tdd/fixtures/pass-evidence.json +100 -0
- package/tdd/fixtures/quality-scenarios.json +20 -0
- package/tdd/references/core-discipline.md +2 -0
- package/tdd/references/integration-testing.md +13 -0
- package/tdd/references/quality-model.md +68 -0
- package/tdd/references/unit-testing.md +15 -0
- package/tdd/schemas/quality-evidence.schema.json +211 -0
- package/tdd/schemas/quality-report.schema.json +110 -0
- package/tdd/scripts/quality-gate.js +475 -0
- package/to-spec/SKILL.md +3 -5
- package/to-spec/scripts/check-project-docs.js +42 -62
- package/to-spec/scripts/project-docs-resolver.js +111 -0
- package/to-tickets/SKILL.md +4 -4
- package/todo-driven-workflow/SKILL.md +6 -4
- package/using-git-worktrees/SKILL.md +3 -3
- package/verification-loop/SKILL.md +4 -4
- package/verify-before-claim/SKILL.md +2 -2
- package/zoom-out/SKILL.md +4 -4
- package/ab-test-deep-analysis-2026-08-27.md +0 -383
- package/ab-test-results-2026-08-27.md +0 -152
- package/ab-test-statistical-analysis-31cases.md +0 -193
- package/opencode-plugin/hooks/circuit-breaker.js +0 -121
- package/opencode-plugin/hooks/compliance.js +0 -128
- package/opencode-plugin/hooks/post-edit.js +0 -62
- package/opencode-plugin/hooks/pre-complete.js +0 -65
- package/opencode-plugin/test-hook-firing.js +0 -198
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const {
|
|
7
|
+
getWorkspaceRoot,
|
|
8
|
+
getSessionId,
|
|
9
|
+
getSessionDir,
|
|
10
|
+
writeCurrentSession,
|
|
11
|
+
pruneStaleSessions
|
|
12
|
+
} = require('./lib/harness-state');
|
|
13
|
+
|
|
14
|
+
const POLICY = [
|
|
15
|
+
'Harness Cognitive OS policy is active.',
|
|
16
|
+
'Use Discover -> Think -> Try -> Summarize -> Record as a lightweight reasoning loop.',
|
|
17
|
+
'Cross-cutting invariants:',
|
|
18
|
+
'- Route before execution: establish task scope before mutating work.',
|
|
19
|
+
'- Verify before claim: require objective evidence appropriate to the change.',
|
|
20
|
+
'- Re-plan on repetition: after 3 same-signature failures, zoom out instead of repeating the same attempt.',
|
|
21
|
+
'Domain skills are advisory. Do not enforce a universal workflow order; choose the smallest useful skill/tool set.'
|
|
22
|
+
].join('\n');
|
|
23
|
+
|
|
24
|
+
function readPayload() {
|
|
25
|
+
if (process.stdin.isTTY) return null;
|
|
26
|
+
try {
|
|
27
|
+
const raw = fs.readFileSync(0, 'utf8').trim();
|
|
28
|
+
return raw ? JSON.parse(raw) : null;
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function removeIfPresent(file) {
|
|
35
|
+
try {
|
|
36
|
+
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
// Session reset is best effort; a stale breaker must never block startup.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handoffContext(sessionDir) {
|
|
43
|
+
try {
|
|
44
|
+
const handoff = JSON.parse(fs.readFileSync(path.join(sessionDir, 'handoff-state.json'), 'utf8'));
|
|
45
|
+
if (handoff.status !== 'failed') return '';
|
|
46
|
+
const detail = String(handoff.errorSummary || 'No error snippet available').trim().slice(-300);
|
|
47
|
+
return `\nPrevious session checkpoint: ${handoff.tool || 'unknown'} failed${handoff.exitCode ? ` (exit ${handoff.exitCode})` : ''}.\nReview the failure evidence before continuing.\n${detail}`;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function main() {
|
|
54
|
+
const payload = readPayload();
|
|
55
|
+
const root = getWorkspaceRoot(payload);
|
|
56
|
+
const sessionId = getSessionId(payload);
|
|
57
|
+
let additionalContext = POLICY;
|
|
58
|
+
|
|
59
|
+
if (root) {
|
|
60
|
+
try {
|
|
61
|
+
pruneStaleSessions(root);
|
|
62
|
+
writeCurrentSession(root, sessionId);
|
|
63
|
+
const sessionDir = getSessionDir(root, sessionId);
|
|
64
|
+
removeIfPresent(path.join(sessionDir, 'rule-of-3-state.json'));
|
|
65
|
+
removeIfPresent(path.join(sessionDir, 'zoom-out-report.md'));
|
|
66
|
+
removeIfPresent(path.join(sessionDir, 'subagent-scope-state.json'));
|
|
67
|
+
additionalContext += handoffContext(sessionDir);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
// Policy injection remains useful even if runtime state is unavailable.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(JSON.stringify({
|
|
74
|
+
hookSpecificOutput: {
|
|
75
|
+
hookEventName: 'SessionStart',
|
|
76
|
+
additionalContext
|
|
77
|
+
}
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
main();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Manual escape hatch for the Rule of 3 circuit breaker. Run this directly
|
|
3
|
+
// in your own terminal (not through the agent — Bash is blocked while the
|
|
4
|
+
// breaker is tripped) to clear it without waiting for a new session.
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { getWorkspaceRoot, getSessionDir, readCurrentSession } = require('./lib/harness-state');
|
|
8
|
+
|
|
9
|
+
// This is run directly by the human, not through the agent, so there's no
|
|
10
|
+
// hook payload to read a session_id from - resolve "the session that's
|
|
11
|
+
// currently blocked" via the pointer bootstrap.js wrote at its last
|
|
12
|
+
// SessionStart.
|
|
13
|
+
const root = getWorkspaceRoot();
|
|
14
|
+
const harnessDir = getSessionDir(root, readCurrentSession(root));
|
|
15
|
+
const stateFile = path.join(harnessDir, 'rule-of-3-state.json');
|
|
16
|
+
const reportFile = path.join(harnessDir, 'zoom-out-report.md');
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(stateFile)) {
|
|
19
|
+
fs.unlinkSync(stateFile);
|
|
20
|
+
console.log('Rule of 3 circuit breaker cleared.');
|
|
21
|
+
} else {
|
|
22
|
+
console.log('Rule of 3 circuit breaker is not currently tripped.');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A human reset is a full clear: the stale reflection report belongs to the
|
|
26
|
+
// loop that was just abandoned.
|
|
27
|
+
if (fs.existsSync(reportFile)) {
|
|
28
|
+
fs.unlinkSync(reportFile);
|
|
29
|
+
console.log('Stale zoom-out report removed.');
|
|
30
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const crypto = require('crypto');
|
|
5
|
+
const { getWorkspaceRoot, getSessionDir } = require('./lib/harness-state');
|
|
6
|
+
|
|
7
|
+
// PostToolUse hooks in Claude Code typically receive the tool output via stdin
|
|
8
|
+
let inputData = '';
|
|
9
|
+
|
|
10
|
+
process.stdin.on('data', chunk => {
|
|
11
|
+
inputData += chunk;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
process.stdin.on('end', () => {
|
|
15
|
+
try {
|
|
16
|
+
const payload = JSON.parse(inputData);
|
|
17
|
+
const toolResponse = payload.tool_response || {};
|
|
18
|
+
const stdout = toolResponse.stdout ?? payload.stdout ?? '';
|
|
19
|
+
const stderr = toolResponse.stderr ?? payload.stderr ?? '';
|
|
20
|
+
const rawExitCode = toolResponse.exitCode ?? toolResponse.exit_code ?? payload.exitCode;
|
|
21
|
+
const exitCode = typeof rawExitCode === 'number' ? rawExitCode : undefined;
|
|
22
|
+
|
|
23
|
+
const explicitFailure = exitCode !== undefined && exitCode !== 0;
|
|
24
|
+
const explicitSuccess = exitCode === 0;
|
|
25
|
+
const stderrSignal = typeof stderr === 'string' && stderr.trim().length > 0;
|
|
26
|
+
const looksLikeError = /\b(error|fail|failed|failure|exception|fatal|panic|traceback|denied|refused|cannot|unable)\b/i.test(stderr);
|
|
27
|
+
const isFailure = explicitFailure || (exitCode === undefined && stderrSignal && looksLikeError);
|
|
28
|
+
const errorText = (stderrSignal ? stderr : stdout) || '';
|
|
29
|
+
|
|
30
|
+
const root = getWorkspaceRoot(payload);
|
|
31
|
+
const stateFile = path.join(getSessionDir(root, payload.session_id || payload.sessionId), 'rule-of-3-state.json');
|
|
32
|
+
|
|
33
|
+
let state = { count: 0, lastHash: null, zoomOutResolved: false, zoomOutCycles: 0, lastFailureAt: 0 };
|
|
34
|
+
if (fs.existsSync(stateFile)) {
|
|
35
|
+
state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isFailure && errorText) {
|
|
39
|
+
// Enhanced failure signature detection with context-aware categorization
|
|
40
|
+
// Hash includes: error message, file path, command attempted, and failure category
|
|
41
|
+
const filePath = payload.tool_input?.file_path || payload.tool_input?.filePath || '';
|
|
42
|
+
const command = payload.tool_input?.command || '';
|
|
43
|
+
|
|
44
|
+
// Categorize failure type for context-aware thresholds
|
|
45
|
+
let category = 'unknown';
|
|
46
|
+
if (/SyntaxError|Unexpected token|Syntax Error/i.test(errorText)) {
|
|
47
|
+
category = 'syntax';
|
|
48
|
+
} else if (/ENOENT|not found|No such file|找不到|不存在/i.test(errorText)) {
|
|
49
|
+
category = 'environment';
|
|
50
|
+
} else if (/timeout|timed out|超時|逾時/i.test(errorText)) {
|
|
51
|
+
category = 'timeout';
|
|
52
|
+
} else if (/test.*fail|assertion|AssertionError|測試.*失敗|斷言.*錯誤/i.test(errorText)) {
|
|
53
|
+
category = 'test';
|
|
54
|
+
} else if (/permission|denied|EACCES|權限/i.test(errorText)) {
|
|
55
|
+
category = 'permission';
|
|
56
|
+
} else if (/dependency|module not found|Cannot find module|模組.*找不到/i.test(errorText)) {
|
|
57
|
+
category = 'dependency';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Normalize error text with context for better loop detection
|
|
61
|
+
const normalized = errorText.slice(-400)
|
|
62
|
+
.toLowerCase()
|
|
63
|
+
.replace(/0x[0-9a-f]+/g, '#')
|
|
64
|
+
.replace(/\d+/g, '#')
|
|
65
|
+
.replace(/\s+/g, ' ')
|
|
66
|
+
.trim();
|
|
67
|
+
|
|
68
|
+
// Include file path and command in hash for better context
|
|
69
|
+
const contextString = `${category}:${filePath}:${command}:${normalized.slice(-200)}`;
|
|
70
|
+
const hash = crypto.createHash('md5').update(contextString).digest('hex');
|
|
71
|
+
|
|
72
|
+
// Context-aware thresholds: different failure types have different breaking points
|
|
73
|
+
let threshold = 3; // Default: 3 strikes
|
|
74
|
+
if (category === 'syntax') {
|
|
75
|
+
threshold = 3; // Same syntax error 3 times = stop
|
|
76
|
+
} else if (category === 'environment') {
|
|
77
|
+
threshold = 4; // Environment issues may need more exploration
|
|
78
|
+
} else if (category === 'test') {
|
|
79
|
+
threshold = 3; // Same test failure 3 times = stop
|
|
80
|
+
} else if (category === 'timeout') {
|
|
81
|
+
threshold = 2; // Timeouts are expensive, fail faster
|
|
82
|
+
} else if (category === 'permission') {
|
|
83
|
+
threshold = 2; // Permission issues rarely resolve by retrying
|
|
84
|
+
} else if (category === 'dependency') {
|
|
85
|
+
threshold = 4; // Dependency issues may need different approaches
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (state.lastHash === hash) {
|
|
89
|
+
state.count += 1;
|
|
90
|
+
state.category = category; // Track failure category for smarter decisions
|
|
91
|
+
} else {
|
|
92
|
+
state.count = 1;
|
|
93
|
+
state.lastHash = hash;
|
|
94
|
+
state.category = category;
|
|
95
|
+
// New signature = new problem: the zoom-out cycle budget starts over.
|
|
96
|
+
state.zoomOutCycles = 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Store threshold for this failure type
|
|
100
|
+
state.threshold = threshold;
|
|
101
|
+
|
|
102
|
+
// Any fresh failure re-arms the breaker. Without this, a signature that
|
|
103
|
+
// failed again after a success (or after a zoom-out release) would keep
|
|
104
|
+
// zoomOutResolved=true and the breaker could never trip on it again.
|
|
105
|
+
state.zoomOutResolved = false;
|
|
106
|
+
// rule-of-3.js only honors a zoom-out report written AFTER this moment,
|
|
107
|
+
// so a stale report can't unlock a later trip.
|
|
108
|
+
state.lastFailureAt = Date.now();
|
|
109
|
+
|
|
110
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8');
|
|
111
|
+
} else if (explicitSuccess) {
|
|
112
|
+
// Only reset on a *confirmed* zero exit code, not merely "not a failure".
|
|
113
|
+
if (state.count > 0 || state.zoomOutCycles > 0) {
|
|
114
|
+
state.count = 0;
|
|
115
|
+
state.zoomOutResolved = true;
|
|
116
|
+
state.zoomOutCycles = 0;
|
|
117
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
} catch (err) {
|
|
122
|
+
// silently fail
|
|
123
|
+
}
|
|
124
|
+
process.exit(0);
|
|
125
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Rule of 3 circuit breaker (PreToolUse: Bash|PowerShell|Edit|Write|apply_patch).
|
|
3
|
+
//
|
|
4
|
+
// Tripping the breaker forces REFLECTION, not surrender. The most common
|
|
5
|
+
// agent failure mode is drilling deeper on one unverified assumption, so the
|
|
6
|
+
// breaker locks mutating tools and demands a zoom-out fact-check first
|
|
7
|
+
// (read-only tools stay available). A valid reflection report - written to
|
|
8
|
+
// the session's zoom-out-report.md (path given in the block message below) -
|
|
9
|
+
// releases the breaker again. Only a SECOND trip on the same failure
|
|
10
|
+
// signature - proof that reflection alone couldn't crack it - hard-locks the
|
|
11
|
+
// session until the Human Partner decides.
|
|
12
|
+
//
|
|
13
|
+
// State is scoped per Claude Code session_id (see lib/harness-state.js) so
|
|
14
|
+
// two sessions open on the same repo never share a trip count. The common
|
|
15
|
+
// case - nobody's breaker tripped anywhere - never has to read stdin to find
|
|
16
|
+
// that out; only once some session IS tripped do we pay to read the payload
|
|
17
|
+
// and check whether it's this one.
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { getWorkspaceRoot, getSessionDir, listSessionDirs } = require('./lib/harness-state');
|
|
21
|
+
|
|
22
|
+
const REQUIRED_SECTIONS = [
|
|
23
|
+
'## Goal',
|
|
24
|
+
'## Failed Attempts',
|
|
25
|
+
'## Verified Facts',
|
|
26
|
+
'## Diagnosis',
|
|
27
|
+
'## Decision'
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
function readState(stateFile) {
|
|
31
|
+
if (!fs.existsSync(stateFile)) return null;
|
|
32
|
+
try { return JSON.parse(fs.readFileSync(stateFile, 'utf8')); } catch (err) { return null; }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isTripped(state) {
|
|
36
|
+
if (!state || state.zoomOutResolved) return false;
|
|
37
|
+
|
|
38
|
+
// Use context-aware thresholds if available, otherwise default to 3
|
|
39
|
+
const threshold = state.threshold || 3;
|
|
40
|
+
return state.count >= threshold;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function anySessionTripped(root) {
|
|
44
|
+
return listSessionDirs(root).some(dir => isTripped(readState(path.join(dir, 'rule-of-3-state.json'))));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The one mutation allowed while tripped: writing the reflection report.
|
|
48
|
+
const ALLOWED_WRITE_TOOLS = new Set([
|
|
49
|
+
'Write', 'Edit', 'create_file', 'replace_string_in_file',
|
|
50
|
+
'insert_edit_into_file', 'write_file', 'edit_file', 'apply_patch'
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
function isReportWrite(payload, reportFile) {
|
|
54
|
+
if (!payload) return false;
|
|
55
|
+
const toolName = payload.tool_name || payload.toolName;
|
|
56
|
+
if (!ALLOWED_WRITE_TOOLS.has(toolName)) return false;
|
|
57
|
+
const input = payload.tool_input || payload.toolInput || payload.input || {};
|
|
58
|
+
const target = input.file_path || input.filePath || input.path || input.file;
|
|
59
|
+
if (target && path.resolve(target).toLowerCase() === path.resolve(reportFile).toLowerCase()) return true;
|
|
60
|
+
if (toolName !== 'apply_patch') return false;
|
|
61
|
+
|
|
62
|
+
const patch = input.patch || input.command || input.content || '';
|
|
63
|
+
const base = payload.cwd || process.cwd();
|
|
64
|
+
const files = [...String(patch).matchAll(/^\*\*\* (?:Update|Add|Delete) File:\s*(.+?)\s*$/gm)]
|
|
65
|
+
.map(match => match[1]);
|
|
66
|
+
const reportPath = path.resolve(reportFile).toLowerCase();
|
|
67
|
+
return files.length > 0 && files.every(file => path.resolve(base, file).toLowerCase() === reportPath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A report only counts if it was written AFTER the failure that tripped the
|
|
71
|
+
// breaker (a stale report from an earlier loop can't unlock a new one), has
|
|
72
|
+
// every required section, and actually committed to a RESUME/ESCALATE call.
|
|
73
|
+
function reportIsValid(state, reportFile) {
|
|
74
|
+
if (!fs.existsSync(reportFile)) return false;
|
|
75
|
+
if (fs.statSync(reportFile).mtimeMs < (state.lastFailureAt || 0)) return false;
|
|
76
|
+
const text = fs.readFileSync(reportFile, 'utf8');
|
|
77
|
+
if (!REQUIRED_SECTIONS.every(h => text.includes(h))) return false;
|
|
78
|
+
return /\b(RESUME|ESCALATE)\b/.test(text);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function blockForReflection(state, reportFile) {
|
|
82
|
+
console.error(`[CRITICAL] RULE OF 3 CIRCUIT BREAKER TRIGGERED!`);
|
|
83
|
+
console.error(`The same failure signature has now occurred 3 times.`);
|
|
84
|
+
console.error(`Error Signature: ${state.lastHash}`);
|
|
85
|
+
console.error(`\nMutating tools (Bash/PowerShell/Edit/Write/apply_patch) are locked - not because you`);
|
|
86
|
+
console.error(`must wait for a human, but because you must stop drilling and rebuild the`);
|
|
87
|
+
console.error(`full picture first. Do NOT ask the Human Partner yet.`);
|
|
88
|
+
console.error(`\nACTION REQUIRED (reflect first):`);
|
|
89
|
+
console.error(`1. CEASE FIRE. No new fix attempts, no variations of the last one.`);
|
|
90
|
+
console.error(`2. Load the 'zoom-out' skill and follow its Reflect & Fact-Check protocol.`);
|
|
91
|
+
console.error(`3. Using READ-ONLY tools (Read/Grep/Glob - still available), re-verify the`);
|
|
92
|
+
console.error(` assumption behind each failed attempt against the actual code/logs/docs.`);
|
|
93
|
+
console.error(`4. Write your findings to '${reportFile}' (the ONLY write`);
|
|
94
|
+
console.error(` allowed right now) with sections: ## Goal, ## Failed Attempts,`);
|
|
95
|
+
console.error(` ## Verified Facts, ## Diagnosis, ## Decision (ending RESUME: or ESCALATE:).`);
|
|
96
|
+
console.error(`\nA valid report releases this breaker automatically. Escalate to the Human`);
|
|
97
|
+
console.error(`Partner ONLY if your Decision concludes a genuinely human call is needed`);
|
|
98
|
+
console.error(`(requirement conflict, architecture trade-off, destructive action, missing`);
|
|
99
|
+
console.error(`access) - and then present options with a recommendation, not apologies.`);
|
|
100
|
+
// For PreToolUse, only exit code 2 actually blocks the tool call.
|
|
101
|
+
// Exit 1 is a non-blocking error and Claude Code proceeds anyway.
|
|
102
|
+
process.exit(2);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function blockForHumanDecision(state) {
|
|
106
|
+
console.error(`[CRITICAL] RULE OF 3 CIRCUIT BREAKER TRIGGERED! (repeat trip - hard lock)`);
|
|
107
|
+
console.error(`Error Signature: ${state.lastHash}`);
|
|
108
|
+
console.error(`\nYou already completed a zoom-out reflection for this same failure signature`);
|
|
109
|
+
console.error(`and the fresh diagnosis failed too. This is now past reflect-and-retry:`);
|
|
110
|
+
console.error(`STOP and hand the decision to your Human Partner.`);
|
|
111
|
+
console.error(`\nPresent a DECISION REQUEST, not a plea: the goal, the verified facts, every`);
|
|
112
|
+
console.error(`path already falsified (including the post-reflection diagnosis), 2-3`);
|
|
113
|
+
console.error(`concrete options with trade-offs, and your recommendation.`);
|
|
114
|
+
console.error(`\nThis lock persists until the Human Partner clears it by running`);
|
|
115
|
+
console.error(`"npm run harness:reset" in their own terminal, or by starting a new`);
|
|
116
|
+
console.error(`session / running /clear (which resets it automatically on SessionStart).`);
|
|
117
|
+
process.exit(2);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function main(payload) {
|
|
121
|
+
try {
|
|
122
|
+
const root = getWorkspaceRoot(payload);
|
|
123
|
+
const sessionDir = getSessionDir(root, payload && (payload.session_id || payload.sessionId));
|
|
124
|
+
const stateFile = path.join(sessionDir, 'rule-of-3-state.json');
|
|
125
|
+
const reportFile = path.join(sessionDir, 'zoom-out-report.md');
|
|
126
|
+
|
|
127
|
+
const state = readState(stateFile);
|
|
128
|
+
if (!isTripped(state)) {
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isReportWrite(payload, reportFile)) {
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (reportIsValid(state, reportFile)) {
|
|
137
|
+
state.count = 0;
|
|
138
|
+
state.zoomOutResolved = true;
|
|
139
|
+
state.zoomOutCycles = (state.zoomOutCycles || 0) + 1;
|
|
140
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8');
|
|
141
|
+
console.log(
|
|
142
|
+
`[Rule of 3] Zoom-out report accepted - breaker released. Follow your ` +
|
|
143
|
+
`report's Decision: RESUME means one fresh run at the NEW diagnosis; ` +
|
|
144
|
+
`ESCALATE means present the decision request and WAIT for the Human ` +
|
|
145
|
+
`Partner before mutating anything. If this same signature trips the ` +
|
|
146
|
+
`breaker again, it hard-locks for a human decision.`
|
|
147
|
+
);
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if ((state.zoomOutCycles || 0) >= 1) {
|
|
152
|
+
blockForHumanDecision(state);
|
|
153
|
+
}
|
|
154
|
+
blockForReflection(state, reportFile);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
// Fail open if state tracking breaks
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Read the payload before the fast path. The session registry may point at a
|
|
162
|
+
// different workspace than process.cwd() after an agent changes directory;
|
|
163
|
+
// checking the cwd first would skip an active breaker in the bound session.
|
|
164
|
+
function runForPayload(payload) {
|
|
165
|
+
let tripped = false;
|
|
166
|
+
try {
|
|
167
|
+
tripped = anySessionTripped(getWorkspaceRoot(payload));
|
|
168
|
+
} catch (err) {
|
|
169
|
+
tripped = false; // fail open
|
|
170
|
+
}
|
|
171
|
+
if (!tripped) process.exit(0);
|
|
172
|
+
main(payload);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (process.stdin.isTTY) {
|
|
176
|
+
// Manual run in a terminal - no payload is coming.
|
|
177
|
+
runForPayload(null);
|
|
178
|
+
} else {
|
|
179
|
+
let raw = '';
|
|
180
|
+
let finished = false;
|
|
181
|
+
const finish = () => {
|
|
182
|
+
if (finished) return;
|
|
183
|
+
finished = true;
|
|
184
|
+
let payload = null;
|
|
185
|
+
try { payload = JSON.parse(raw); } catch (err) { /* no payload */ }
|
|
186
|
+
runForPayload(payload);
|
|
187
|
+
};
|
|
188
|
+
// Claude Code closes stdin right after writing the payload; the timer is a
|
|
189
|
+
// safety net so an odd caller that never closes stdin can't hang the hook.
|
|
190
|
+
const timer = setTimeout(finish, 300);
|
|
191
|
+
process.stdin.setEncoding('utf8');
|
|
192
|
+
process.stdin.on('data', chunk => { raw += chunk; });
|
|
193
|
+
process.stdin.on('end', () => { clearTimeout(timer); finish(); });
|
|
194
|
+
process.stdin.on('error', () => { clearTimeout(timer); finish(); });
|
|
195
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { getWorkspaceRoot, getSessionDir } = require('./lib/harness-state');
|
|
5
|
+
|
|
6
|
+
// Commands that count as "verification ran" for the Stop gate
|
|
7
|
+
// (hooks/scripts/stop-gate.js). Recall over precision: under-blocking the
|
|
8
|
+
// gate is the correct failure direction, so a broad net is fine.
|
|
9
|
+
const VERIFY_COMMAND_RE = /\b(test|spec|jest|vitest|mocha|pytest|rspec|phpunit|tsc|eslint|lint|build|compile|verify|check)\b/i;
|
|
10
|
+
|
|
11
|
+
let inputData = '';
|
|
12
|
+
const timeout = setTimeout(() => {
|
|
13
|
+
processState(null);
|
|
14
|
+
}, 200);
|
|
15
|
+
|
|
16
|
+
process.stdin.on('data', chunk => {
|
|
17
|
+
inputData += chunk;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
process.stdin.on('end', () => {
|
|
21
|
+
clearTimeout(timeout);
|
|
22
|
+
try {
|
|
23
|
+
const payload = JSON.parse(inputData.trim());
|
|
24
|
+
processState(payload);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
processState(null);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function processState(payload) {
|
|
31
|
+
try {
|
|
32
|
+
const sessionId = payload && (payload.session_id || payload.sessionId);
|
|
33
|
+
const root = getWorkspaceRoot(payload);
|
|
34
|
+
const stateFile = path.join(getSessionDir(root, sessionId), 'handoff-state.json');
|
|
35
|
+
|
|
36
|
+
// Merge into the existing state rather than replacing it, so the
|
|
37
|
+
// failure/idle status and the Stop-gate milestones can coexist.
|
|
38
|
+
let state = { status: 'idle', timestamp: new Date().toISOString() };
|
|
39
|
+
if (fs.existsSync(stateFile)) {
|
|
40
|
+
try { state = JSON.parse(fs.readFileSync(stateFile, 'utf8')); } catch (e) { /* keep default */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (payload) {
|
|
44
|
+
// Hook hosts nest tool output under tool_response, not at the top
|
|
45
|
+
// level, and uses tool_name rather than tool. A numeric exit code
|
|
46
|
+
// also isn't guaranteed to be present at all - only trust it when
|
|
47
|
+
// it's actually a number; fall back to a stderr signal otherwise.
|
|
48
|
+
const toolResponse = payload.tool_response || {};
|
|
49
|
+
const stdout = toolResponse.stdout ?? payload.stdout ?? '';
|
|
50
|
+
const stderr = toolResponse.stderr ?? payload.stderr ?? '';
|
|
51
|
+
const rawExitCode = toolResponse.exitCode ?? toolResponse.exit_code ?? payload.exitCode;
|
|
52
|
+
const exitCode = typeof rawExitCode === 'number' ? rawExitCode : undefined;
|
|
53
|
+
const stderrSignal = typeof stderr === 'string' && stderr.trim().length > 0;
|
|
54
|
+
const isFailed = (exitCode !== undefined && exitCode !== 0) || (exitCode === undefined && stderrSignal);
|
|
55
|
+
const toolName = payload.tool_name || payload.tool || 'command';
|
|
56
|
+
|
|
57
|
+
if (isFailed) {
|
|
58
|
+
// Truncate output to avoid state bloat
|
|
59
|
+
let output = (stderrSignal ? stderr : stdout) || payload.output || '';
|
|
60
|
+
if (output.length > 500) {
|
|
61
|
+
output = '...' + output.slice(-500);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
state.status = 'failed';
|
|
65
|
+
state.timestamp = new Date().toISOString();
|
|
66
|
+
state.tool = toolName;
|
|
67
|
+
state.exitCode = exitCode;
|
|
68
|
+
state.errorSummary = output.trim();
|
|
69
|
+
} else if (exitCode === 0 && state.status === 'failed') {
|
|
70
|
+
// Clear failed state or mark as idle/resolved
|
|
71
|
+
state.lastResolved = { tool: state.tool, timestamp: state.timestamp };
|
|
72
|
+
state.status = 'idle';
|
|
73
|
+
state.timestamp = new Date().toISOString();
|
|
74
|
+
delete state.exitCode;
|
|
75
|
+
delete state.errorSummary;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Milestones for the Stop gate: when did the last mutation happen, and
|
|
79
|
+
// has any verification-ish command succeeded since.
|
|
80
|
+
if (toolName === 'Edit' || toolName === 'Write' || toolName === 'apply_patch') {
|
|
81
|
+
state.lastEditAt = Date.now();
|
|
82
|
+
} else if ((toolName === 'Bash' || toolName === 'PowerShell') && !isFailed) {
|
|
83
|
+
const command = (payload.tool_input && payload.tool_input.command) || '';
|
|
84
|
+
if (VERIFY_COMMAND_RE.test(command)) {
|
|
85
|
+
state.lastVerifyAt = Date.now();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8');
|
|
90
|
+
} else {
|
|
91
|
+
// No standard input payload, or invalid JSON. Just maintain a heartbeat timestamp
|
|
92
|
+
if (!fs.existsSync(stateFile)) {
|
|
93
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
// Fail silently in hooks
|
|
98
|
+
}
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stop Gate (Stop hook - Claude Code only)
|
|
4
|
+
* The pre-delivery gate (verification-loop) was prose-only, and prose decays
|
|
5
|
+
* as context grows. This makes the one check that matters mechanical: ending
|
|
6
|
+
* the turn with edits that were never followed by a successful
|
|
7
|
+
* verification-ish command (test/build/lint - tracked by state-persist.js)
|
|
8
|
+
* while uncommitted changes sit in the tree gets bounced back ONCE per edit
|
|
9
|
+
* batch. Objective facts only; fails open on any error.
|
|
10
|
+
*/
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const { getWorkspaceRoot, getSessionDir } = require('./lib/harness-state');
|
|
15
|
+
|
|
16
|
+
function decide(payload) {
|
|
17
|
+
try {
|
|
18
|
+
// Loop guard: if this stop already resulted from a Stop-hook block,
|
|
19
|
+
// always let it through - one bounce is the contract, never a cage.
|
|
20
|
+
if (payload && payload.stop_hook_active) {
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const root = getWorkspaceRoot(payload);
|
|
25
|
+
const sessionDir = getSessionDir(root, payload && (payload.session_id || payload.sessionId));
|
|
26
|
+
const handoffFile = path.join(sessionDir, 'handoff-state.json');
|
|
27
|
+
const gateFile = path.join(sessionDir, 'stop-gate-state.json');
|
|
28
|
+
|
|
29
|
+
if (!fs.existsSync(handoffFile)) process.exit(0);
|
|
30
|
+
const handoff = JSON.parse(fs.readFileSync(handoffFile, 'utf8'));
|
|
31
|
+
const lastEditAt = handoff.lastEditAt || 0;
|
|
32
|
+
const lastVerifyAt = handoff.lastVerifyAt || 0;
|
|
33
|
+
|
|
34
|
+
// No edits this session, or verification already ran after the last edit.
|
|
35
|
+
if (!lastEditAt || lastVerifyAt >= lastEditAt) process.exit(0);
|
|
36
|
+
|
|
37
|
+
// One bounce per edit batch: if we already blocked for this exact edit
|
|
38
|
+
// timestamp, the model either verified (which moves lastVerifyAt) or
|
|
39
|
+
// explicitly chose not to - don't nag twice for the same batch.
|
|
40
|
+
let gate = {};
|
|
41
|
+
if (fs.existsSync(gateFile)) {
|
|
42
|
+
try { gate = JSON.parse(fs.readFileSync(gateFile, 'utf8')); } catch (e) { /* ignore */ }
|
|
43
|
+
}
|
|
44
|
+
if (gate.blockedForEditAt === lastEditAt) process.exit(0);
|
|
45
|
+
|
|
46
|
+
// Only relevant when unverified work is actually sitting in the tree.
|
|
47
|
+
const dirty = execSync('git status --porcelain', {
|
|
48
|
+
cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore']
|
|
49
|
+
}).trim();
|
|
50
|
+
if (!dirty) process.exit(0);
|
|
51
|
+
|
|
52
|
+
fs.writeFileSync(gateFile, JSON.stringify({ blockedForEditAt: lastEditAt }, null, 2), 'utf8');
|
|
53
|
+
|
|
54
|
+
console.error(`[Stop Gate] Turn is ending with uncommitted edits and no successful verification command (test/build/lint) since the last edit.`);
|
|
55
|
+
console.error(`Before delivering: run the relevant check from 'verification-loop' - even a single targeted test beats none.`);
|
|
56
|
+
console.error(`If verification is genuinely not applicable (docs-only change, analysis-only turn, the Human Partner told you to stop), state that explicitly in your reply and stop again.`);
|
|
57
|
+
console.error(`This gate bounces at most once per edit batch.`);
|
|
58
|
+
process.exit(2);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
// Fail open - a broken gate must never trap the session.
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let inputData = '';
|
|
66
|
+
const timeout = setTimeout(() => {
|
|
67
|
+
decide(null);
|
|
68
|
+
}, 200);
|
|
69
|
+
|
|
70
|
+
process.stdin.on('data', chunk => { inputData += chunk; });
|
|
71
|
+
process.stdin.on('end', () => {
|
|
72
|
+
clearTimeout(timeout);
|
|
73
|
+
let payload = null;
|
|
74
|
+
try { payload = JSON.parse(inputData.trim()); } catch (err) { /* no payload */ }
|
|
75
|
+
decide(payload);
|
|
76
|
+
});
|
|
77
|
+
process.stdin.on('error', () => {
|
|
78
|
+
clearTimeout(timeout);
|
|
79
|
+
decide(null);
|
|
80
|
+
});
|