@lannguyensi/harness 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +63 -1
- package/dist/cli/doctor/format.js +24 -0
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +7 -0
- package/dist/cli/doctor/index.js +10 -0
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/rogue-ledger.d.ts +25 -0
- package/dist/cli/doctor/rogue-ledger.js +106 -0
- package/dist/cli/doctor/rogue-ledger.js.map +1 -0
- package/dist/cli/doctor/types.d.ts +10 -1
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/index.js +210 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +42 -0
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +42 -0
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.d.ts +30 -0
- package/dist/cli/pack/hook-branch-protection.js +279 -0
- package/dist/cli/pack/hook-branch-protection.js.map +1 -0
- package/dist/cli/pack/hook-codex-pre-tool-use.d.ts +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +27 -2
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +52 -10
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.d.ts +13 -0
- package/dist/cli/pack/understanding-report-schema-hint.js +54 -0
- package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -0
- package/dist/cli/session-start/branch-check.d.ts +44 -0
- package/dist/cli/session-start/branch-check.js +165 -0
- package/dist/cli/session-start/branch-check.js.map +1 -0
- package/dist/cli/uninstall/index.d.ts +68 -0
- package/dist/cli/uninstall/index.js +586 -0
- package/dist/cli/uninstall/index.js.map +1 -0
- package/dist/cli/uninstall/snapshot.d.ts +40 -0
- package/dist/cli/uninstall/snapshot.js +34 -0
- package/dist/cli/uninstall/snapshot.js.map +1 -0
- package/dist/policy-packs/builtin/branch-protection-runtime.d.ts +47 -0
- package/dist/policy-packs/builtin/branch-protection-runtime.js +92 -0
- package/dist/policy-packs/builtin/branch-protection-runtime.js.map +1 -0
- package/dist/policy-packs/builtin/branch-protection.d.ts +9 -0
- package/dist/policy-packs/builtin/branch-protection.js +146 -0
- package/dist/policy-packs/builtin/branch-protection.js.map +1 -0
- package/dist/policy-packs/registry.d.ts +1 -1
- package/dist/policy-packs/registry.js +10 -3
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/runtime/agent-facing.d.ts +9 -0
- package/dist/runtime/agent-facing.js +47 -0
- package/dist/runtime/agent-facing.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/intercept.js +20 -2
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/schema/index.d.ts +63 -0
- package/dist/schema/policies.d.ts +90 -0
- package/dist/schema/policies.js +29 -0
- package/dist/schema/policies.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const MINIMAL_TEMPLATE = "# ~/.claude/harness.yaml\n#\n# Bootstrapped by `harness init --template minimal`.\n#\n# This is the empty-but-valid manifest. Run `harness validate` to confirm it\n# parses, then add entries under the five top-level keys:\n#\n# grounding: evidence-ledger + claim-gate config (see docs/ARCHITECTURE.md \u00A72)\n# tools: mcp / cli / skills / builtin inventory (\u00A73)\n# memory: directories, retention, scopes (\u00A74)\n# hooks: event-bound shell commands (\u00A75)\n# policies: named rules that bind hooks to triggers (\u00A76)\n#\n# Phase 2 verbs to add entries safely: `harness add mcp <name> ...`,\n# `harness add cli`, `harness add hook`, `harness add skill`.\n# Per-machine overrides live at ~/.claude/machines/<discriminator>.harness.overrides.yaml\n# (ARCHITECTURE.md \u00A78) for paths that vary per host.\n#\n# Docs: https://github.com/LanNguyenSi/harness\n\nversion: 1\n";
|
|
2
|
-
export declare const FULL_TEMPLATE = "# ~/.claude/harness.yaml\n#\n# Bootstrapped by `harness init --template full`. The reference manifest:\n# every example policy from docs/examples/full-manifest.yaml wired through\n# the generic `harness policy intercept` engine, so no external shell\n# scripts under ~/.claude/hooks/ are required.\n#\n# Canonical source for the policy + policy_packs sections is\n# docs/examples/full-manifest.yaml. A parity vitest\n# (tests/cli/init-full-template-parity.test.ts) fails the build if the\n# two diverge on policy names or load-bearing fields.\n#\n# What you still need on PATH (the wizard offers to `npm i -g` these on\n# init): agent-tasks-mcp-bridge, grounding-mcp, memory-router-*,\n# understanding-gate-claude-*.\n\nversion: 1\n\ngrounding:\n session:\n auto_start: true\n id_format: \"gs-{repo}-{rand:8}\"\n evidence_ledger:\n path: ~/.evidence-ledger/ledger.db\n retention_days: 90\n policies_source: ~/.claude/harness.d/policies/claim-gate.yaml\n\ntools:\n mcp:\n # codebase-oracle (the Pandora RAG MCP server) is intentionally NOT\n # in the Full default. It is published as\n # `@lannguyensi/codebase-oracle` and works fine standalone, but it\n # is an opinionated workflow add-on (multi-repo semantic search)\n # rather than infrastructure harness itself assumes. Operators who\n # want it wire it explicitly:\n # npm i -g @lannguyensi/codebase-oracle\n # harness add mcp codebase-oracle --command codebase-oracle,mcp\n # Set ORACLE_SCAN_ROOT (absolute path; tilde is not expanded by the\n # MCP env block) and OPENAI_API_KEY (or switch providers via\n # ORACLE_LLM_PROVIDER) before the first call.\n - name: agent-tasks\n # Zero-setup entry: `@agent-tasks/mcp-bridge` exposes the\n # `agent-tasks-mcp-bridge` binary on PATH. The bridge owns token\n # storage and defaults to the hosted backend; override with\n # `AGENT_TASKS_BASE_URL` / `AGENT_TASKS_TOKEN` for self-hosted.\n # `min_version` floor: 0.6.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-tasks/240, release-cut PR 241).\n # Bump the floor whenever a fix you depend on lands; loose floors\n # are fine, the point is the drift signal not pinning a specific cut.\n command: [agent-tasks-mcp-bridge]\n min_version: \"0.6.0\"\n health:\n verb: projects_list\n timeout_ms: 5000\n enabled: true\n - name: grounding-mcp\n # Published bin from `@lannguyensi/grounding-mcp`. No env is set:\n # the bundled default resolves to `~/.evidence-ledger/ledger.db`\n # via os.homedir() at startup. Passing a literal tilde in env\n # bypasses shell expansion and creates rogue cwd-relative DB files\n # (see agent-tasks/42d224a6 incident). `min_version` floor: 0.2.0\n # added the `--version` short-circuit the doctor probe needs (PR\n # agent-grounding/76, release-cut PR 77).\n command: [grounding-mcp]\n min_version: \"0.2.0\"\n health:\n verb: ledger_status\n timeout_ms: 5000\n enabled: true\n\n cli:\n - name: gh\n binary: gh\n required: true\n\n skills:\n enabled:\n - simplify\n - init\n - review\n - security-review\n source_dirs:\n - ~/.claude/skills\n\n builtin:\n known: [Read, Edit, Write, Bash, Agent, Skill, TaskCreate, Glob, Grep]\n\nmemory:\n directories:\n - path: ~/.claude/projects/{project}/memory\n scope: project\n router:\n # Published bin from `@lannguyensi/memory-router`.\n # `min_version` floor: 0.3.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-memory/40, release-cut PR 41).\n command: [memory-router-user-prompt-submit]\n min_version: \"0.3.0\"\n enabled: true\n retention:\n staleness_days: 180\n broken_refs: warn\n scopes:\n default: project\n allowed: [project, user]\n\n# All PreToolUse hooks share the generic `harness policy intercept` CLI\n# entrypoint. The engine reads the tool event on stdin, evaluates whichever\n# policy below has a matching trigger (`match` + optional `bash_match`),\n# and emits Claude Code's deny envelope when the required ledger tag is\n# absent. No external shell scripts are required.\n#\n# The `git-preflight` SessionStart hook is the producer side of the\n# `preflight-before-*` policies: `harness session-start preflight` runs\n# agent-preflight against the session cwd and, on a ready:true result,\n# records `preflight:${REPO}` to the evidence ledger. It needs the\n# `preflight` binary on PATH (`npm i -g @lannguyensi/agent-preflight`); when\n# that is absent the hook logs to stderr and exits 0, so the session is\n# never broken \u2014 the preflight gates just stay closed until a tag is\n# produced some other way.\nhooks:\n - name: git-preflight\n event: SessionStart\n command: harness session-start preflight\n blocking: false\n budget_ms: 30000\n\n - name: require-review-evidence\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-dogfood-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-preflight-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 1000\n\n - name: require-review-subagent-evidence\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_create\"\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-preflight-push-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 1000\n\npolicies:\n - name: review-before-merge\n description: Block PR merges unless a ledger entry tagged review:<pr-number> exists for this session.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n extract:\n PR_NUMBER: \"toolArgs.prNumber\"\n requires:\n ledger_tag: \"review:${PR_NUMBER}\"\n hook: require-review-evidence\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the PR number. The content should be self-contained enough for an auditor to read without re-opening the chat.\n\n - name: dogfood-before-release\n description: Block npm publish / git tag v* without a recent dogfood ledger entry.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'\n requires:\n ledger_tag: \"dogfood:${SESSION_ID}\"\n within: 24h\n hook: require-dogfood-evidence\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"dogfood:${SESSION_ID} \u2014 <end-to-end smoke summary against the live system>\", source:\"manual smoke test\"}'\n description: Before tagging or publishing, run the release path end-to-end against the live system (not just unit tests) and persist the result as a session-tagged ledger entry. Document what you exercised (install, CLI happy path, MCP handshake, etc.) so a future auditor can tell whether the smoke covered the change.\n\n - name: two-reviewers-required\n description: At least two distinct reviewer ledger entries must exist for the PR.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n extract:\n PR_NUMBER: \"toolArgs.prNumber\"\n requires:\n ledger_tag: \"review:${PR_NUMBER}\"\n count:\n min: 2\n hook: require-review-evidence\n enforcement: warn\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review (reviewer 2)\"}'\n description: Same shape as review-before-merge but TWO DISTINCT reviewer entries must exist before the gate is satisfied (count.min 2). Distinguish reviewers by source so the count is honest. Warn-level enforcement, so the agent CAN merge with one reviewer but should consider spawning a second for load-bearing changes.\n\n - name: preflight-before-investigation\n description: Block investigative git reads (status/log/diff/branch) when agent-preflight has not run recently with ready:true for the current repo.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'\n requires:\n ledger_tag: \"preflight:${REPO}\"\n within: 1h\n hook: require-preflight-evidence\n enforcement: block\n producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${REPO} to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"preflight:${REPO}\", source:\"manual\"}'\n description: Direct ledger write. Use when the Bash hook is locked down (e.g. understanding-gate active) or when the standard producer is unavailable.\n\n - name: review-subagent-before-pr-create\n description: Block agent-tasks PR creation unless a review-subagent ledger entry tagged for this task already exists. Forces the rigorous review BEFORE the PR opens, not after.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_create\"\n extract:\n TASK_ID: \"toolArgs.taskId\"\n requires:\n ledger_tag: \"review-subagent:${TASK_ID}\"\n hook: require-review-subagent-evidence\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review-subagent:${TASK_ID} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: After running a review subagent against the staged diff, persist its verdict + load-bearing findings as a ledger entry tagged with the task UUID. The content should be self-contained enough to audit later without re-reading the chat.\n\n - name: preflight-before-push\n description: Block git push unless a fresh preflight ledger entry exists for the current branch. Catches the stale-checkout class of incident at the last reversible step.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'\n requires:\n ledger_tag: \"preflight:${BRANCH}\"\n within: 10m\n hook: require-preflight-push-evidence\n enforcement: block\n producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${BRANCH} to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"preflight:${BRANCH} \u2014 <summary of what is on the branch + smoke results>\", source:\"manual\"}'\n description: Direct ledger write. The branch is the WIP review surface; the content should summarise what is staged + the smoke evidence so a reviewer can audit later without re-reading the chat.\n\n# Full inherits the Solo/Team understanding-gate stack: the Stop hook\n# persists each Understanding Report and the PreToolUse pre-tool-use\n# blocker refuses Edit/Write/Bash until the report is approved. Drop\n# this block if you want the reference policies above without the\n# baseline gate.\npolicy_packs:\n - name: understanding-before-execution\n source: builtin\n enabled: true\n description: Force agents to expose their task interpretation and wait for explicit human approval before any write-capable tool fires.\n config:\n mode: grill_me\n # Producers (agent-tasks/25bced52): rendered into the gate's deny\n # envelope by the same engine as policy producers. Constraint at\n # this layer: at-least-one `ask`. Post-v0.14.0 the gate signal\n # is a filesystem marker and the mcp ledger_add path no longer\n # satisfies the gate; the canonical unblock surface is the\n # operator-approval prompt.\n producers:\n - kind: ask\n command: harness approve understanding\n description: \"Bare command, no pipes or chaining. The hook recognises it via isEscapeCommand and emits permissionDecision:ask; the operator's go on that prompt IS the gate approval. Golden path.\"\n - kind: bash\n command: harness approve understanding\n description: Same command from any un-hooked terminal (operator only, not reachable from inside the gated session). Writes the canonical marker at harness.generated/.approvals/${SESSION_ID}.\n";
|
|
2
|
+
export declare const FULL_TEMPLATE = "# ~/.claude/harness.yaml\n#\n# Bootstrapped by `harness init --template full`. The reference manifest:\n# every example policy from docs/examples/full-manifest.yaml wired through\n# the generic `harness policy intercept` engine, so no external shell\n# scripts under ~/.claude/hooks/ are required.\n#\n# Canonical source for the policy + policy_packs sections is\n# docs/examples/full-manifest.yaml. A parity vitest\n# (tests/cli/init-full-template-parity.test.ts) fails the build if the\n# two diverge on policy names or load-bearing fields.\n#\n# What you still need on PATH (the wizard offers to `npm i -g` these on\n# init): agent-tasks-mcp-bridge, grounding-mcp, memory-router-*,\n# understanding-gate-claude-*.\n\nversion: 1\n\ngrounding:\n session:\n auto_start: true\n id_format: \"gs-{repo}-{rand:8}\"\n evidence_ledger:\n path: ~/.evidence-ledger/ledger.db\n retention_days: 90\n policies_source: ~/.claude/harness.d/policies/claim-gate.yaml\n\ntools:\n mcp:\n # codebase-oracle (the Pandora RAG MCP server) is intentionally NOT\n # in the Full default. It is published as\n # `@lannguyensi/codebase-oracle` and works fine standalone, but it\n # is an opinionated workflow add-on (multi-repo semantic search)\n # rather than infrastructure harness itself assumes. Operators who\n # want it wire it explicitly:\n # npm i -g @lannguyensi/codebase-oracle\n # harness add mcp codebase-oracle --command codebase-oracle,mcp\n # Set ORACLE_SCAN_ROOT (absolute path; tilde is not expanded by the\n # MCP env block) and OPENAI_API_KEY (or switch providers via\n # ORACLE_LLM_PROVIDER) before the first call.\n - name: agent-tasks\n # Zero-setup entry: `@agent-tasks/mcp-bridge` exposes the\n # `agent-tasks-mcp-bridge` binary on PATH. The bridge owns token\n # storage and defaults to the hosted backend; override with\n # `AGENT_TASKS_BASE_URL` / `AGENT_TASKS_TOKEN` for self-hosted.\n # `min_version` floor: 0.6.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-tasks/240, release-cut PR 241).\n # Bump the floor whenever a fix you depend on lands; loose floors\n # are fine, the point is the drift signal not pinning a specific cut.\n command: [agent-tasks-mcp-bridge]\n min_version: \"0.6.0\"\n health:\n verb: projects_list\n timeout_ms: 5000\n enabled: true\n - name: grounding-mcp\n # Published bin from `@lannguyensi/grounding-mcp`. No env is set:\n # the bundled default resolves to `~/.evidence-ledger/ledger.db`\n # via os.homedir() at startup. Passing a literal tilde in env\n # bypasses shell expansion and creates rogue cwd-relative DB files\n # (see agent-tasks/42d224a6 incident). `min_version` floor: 0.2.0\n # added the `--version` short-circuit the doctor probe needs (PR\n # agent-grounding/76, release-cut PR 77).\n command: [grounding-mcp]\n min_version: \"0.2.0\"\n health:\n verb: ledger_status\n timeout_ms: 5000\n enabled: true\n\n cli:\n - name: gh\n binary: gh\n required: true\n\n skills:\n enabled:\n - simplify\n - init\n - review\n - security-review\n source_dirs:\n - ~/.claude/skills\n\n builtin:\n known: [Read, Edit, Write, Bash, Agent, Skill, TaskCreate, Glob, Grep]\n\nmemory:\n directories:\n - path: ~/.claude/projects/{project}/memory\n scope: project\n router:\n # Published bin from `@lannguyensi/memory-router`.\n # `min_version` floor: 0.3.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-memory/40, release-cut PR 41).\n command: [memory-router-user-prompt-submit]\n min_version: \"0.3.0\"\n enabled: true\n retention:\n staleness_days: 180\n broken_refs: warn\n scopes:\n default: project\n allowed: [project, user]\n\n# All PreToolUse hooks share the generic `harness policy intercept` CLI\n# entrypoint. The engine reads the tool event on stdin, evaluates whichever\n# policy below has a matching trigger (`match` + optional `bash_match`),\n# and emits Claude Code's deny envelope when the required ledger tag is\n# absent. No external shell scripts are required.\n#\n# The `git-preflight` SessionStart hook is the producer side of the\n# `preflight-before-*` policies: `harness session-start preflight` runs\n# agent-preflight against the session cwd and, on a ready:true result,\n# records `preflight:${REPO}` to the evidence ledger. It needs the\n# `preflight` binary on PATH (`npm i -g @lannguyensi/agent-preflight`); when\n# that is absent the hook logs to stderr and exits 0, so the session is\n# never broken \u2014 the preflight gates just stay closed until a tag is\n# produced some other way.\nhooks:\n - name: git-preflight\n event: SessionStart\n command: harness session-start preflight\n blocking: false\n budget_ms: 30000\n\n - name: require-review-evidence\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-dogfood-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-preflight-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 1000\n\n - name: require-review-subagent-evidence\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_create\"\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-preflight-push-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 1000\n\npolicies:\n - name: review-before-merge\n description: Block PR merges unless a ledger entry tagged review:<pr-number> exists for this session.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n extract:\n PR_NUMBER: \"toolArgs.prNumber\"\n requires:\n ledger_tag: \"review:${PR_NUMBER}\"\n hook: require-review-evidence\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the PR number. The content should be self-contained enough for an auditor to read without re-opening the chat.\n ux:\n cannot: \"You cannot merge PR #${PR_NUMBER} yet.\"\n required:\n - \"a recorded review of PR #${PR_NUMBER}\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\" }'\n\n - name: dogfood-before-release\n description: Block npm publish / git tag v* without a recent dogfood ledger entry.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'\n requires:\n ledger_tag: \"dogfood:${SESSION_ID}\"\n within: 24h\n hook: require-dogfood-evidence\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"dogfood:${SESSION_ID} \u2014 <end-to-end smoke summary against the live system>\", source:\"manual smoke test\"}'\n description: Before tagging or publishing, run the release path end-to-end against the live system (not just unit tests) and persist the result as a session-tagged ledger entry. Document what you exercised (install, CLI happy path, MCP handshake, etc.) so a future auditor can tell whether the smoke covered the change.\n ux:\n cannot: \"You cannot publish a release yet.\"\n required:\n - \"an end-to-end dogfood run in this session\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"dogfood:${SESSION_ID} \u2014 <end-to-end smoke summary>\" }'\n\n - name: two-reviewers-required\n description: At least two distinct reviewer ledger entries must exist for the PR.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n extract:\n PR_NUMBER: \"toolArgs.prNumber\"\n requires:\n ledger_tag: \"review:${PR_NUMBER}\"\n count:\n min: 2\n hook: require-review-evidence\n enforcement: warn\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review (reviewer 2)\"}'\n description: Same shape as review-before-merge but TWO DISTINCT reviewer entries must exist before the gate is satisfied (count.min 2). Distinguish reviewers by source so the count is honest. Warn-level enforcement, so the agent CAN merge with one reviewer but should consider spawning a second for load-bearing changes.\n\n - name: preflight-before-investigation\n description: Block investigative git reads (status/log/diff/branch) when agent-preflight has not run recently with ready:true for the current repo.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'\n requires:\n ledger_tag: \"preflight:${REPO}\"\n within: 1h\n hook: require-preflight-evidence\n enforcement: block\n producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${REPO} to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"preflight:${REPO}\", source:\"manual\"}'\n description: Direct ledger write. Use when the Bash hook is locked down (e.g. understanding-gate active) or when the standard producer is unavailable.\n ux:\n cannot: \"You cannot investigate this repository yet.\"\n required:\n - \"verified repository preflight\"\n run:\n - \"harness preflight\"\n\n - name: review-subagent-before-pr-create\n description: Block agent-tasks PR creation unless a review-subagent ledger entry tagged for this task already exists. Forces the rigorous review BEFORE the PR opens, not after.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_create\"\n extract:\n TASK_ID: \"toolArgs.taskId\"\n requires:\n ledger_tag: \"review-subagent:${TASK_ID}\"\n hook: require-review-subagent-evidence\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review-subagent:${TASK_ID} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: After running a review subagent against the staged diff, persist its verdict + load-bearing findings as a ledger entry tagged with the task UUID. The content should be self-contained enough to audit later without re-reading the chat.\n ux:\n cannot: \"You cannot open a pull request for task ${TASK_ID} yet.\"\n required:\n - \"a completed review-subagent pass on this task\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"review-subagent:${TASK_ID} \u2014 <verdict + key findings + nits>\" }'\n\n - name: preflight-before-push\n description: Block git push unless a fresh preflight ledger entry exists for the current branch. Catches the stale-checkout class of incident at the last reversible step.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'\n requires:\n ledger_tag: \"preflight:${BRANCH}\"\n within: 10m\n hook: require-preflight-push-evidence\n enforcement: block\n producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${BRANCH} to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"preflight:${BRANCH} \u2014 <summary of what is on the branch + smoke results>\", source:\"manual\"}'\n description: Direct ledger write. The branch is the WIP review surface; the content should summarise what is staged + the smoke evidence so a reviewer can audit later without re-reading the chat.\n ux:\n cannot: \"You cannot push branch ${BRANCH} yet.\"\n required:\n - \"a fresh preflight for ${BRANCH} (within the last 10 minutes)\"\n run:\n - \"harness preflight\"\n\n# Full inherits the Solo/Team understanding-gate stack: the Stop hook\n# persists each Understanding Report and the PreToolUse pre-tool-use\n# blocker refuses Edit/Write/Bash until the report is approved. Drop\n# this block if you want the reference policies above without the\n# baseline gate.\npolicy_packs:\n - name: understanding-before-execution\n source: builtin\n enabled: true\n description: Force agents to expose their task interpretation and wait for explicit human approval before any write-capable tool fires.\n config:\n mode: grill_me\n # Producers (agent-tasks/25bced52): rendered into the gate's deny\n # envelope by the same engine as policy producers. Constraint at\n # this layer: at-least-one `ask`. Post-v0.14.0 the gate signal\n # is a filesystem marker and the mcp ledger_add path no longer\n # satisfies the gate; the canonical unblock surface is the\n # operator-approval prompt.\n producers:\n - kind: ask\n command: harness approve understanding\n description: \"Bare command, no pipes or chaining. The hook recognises it via isEscapeCommand and emits permissionDecision:ask; the operator's go on that prompt IS the gate approval. Golden path.\"\n - kind: bash\n command: harness approve understanding\n description: Same command from any un-hooked terminal (operator only, not reachable from inside the gated session). Writes the canonical marker at harness.generated/.approvals/${SESSION_ID}.\n # ux (agent-tasks/e48e3b45): replaces the legacy engine-vocabulary\n # deny envelope with the plain-language { cannot, required, run }\n # shape. Engine details (the BLOCK reason naming session id /\n # marker / report state) still land in stderr for operator audit;\n # the agent only sees this.\n ux:\n cannot: \"You cannot use write-capable tools yet.\"\n required:\n - \"an approved Understanding Report for this session\"\n run:\n - \"Write an Understanding Report covering: Current Understanding, Intended Outcome, Derived Todos, Acceptance Criteria, Assumptions, Open Questions, Out Of Scope, Risks, Verification Plan\"\n - \"Run `harness approve understanding` and approve the prompt\"\n";
|
|
3
3
|
export type TemplateName = "minimal" | "full" | "solo" | "team";
|
|
4
4
|
export declare function getTemplate(name: TemplateName): string;
|
|
@@ -201,6 +201,12 @@ policies:
|
|
|
201
201
|
verb: mcp__agent-grounding__ledger_add
|
|
202
202
|
example: '{type:"fact", content:"review:\${PR_NUMBER} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
|
|
203
203
|
description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the PR number. The content should be self-contained enough for an auditor to read without re-opening the chat.
|
|
204
|
+
ux:
|
|
205
|
+
cannot: "You cannot merge PR #\${PR_NUMBER} yet."
|
|
206
|
+
required:
|
|
207
|
+
- "a recorded review of PR #\${PR_NUMBER}"
|
|
208
|
+
run:
|
|
209
|
+
- 'mcp__agent-grounding__ledger_add { type: "fact", content: "review:\${PR_NUMBER} — <verdict + key findings + nits>" }'
|
|
204
210
|
|
|
205
211
|
- name: dogfood-before-release
|
|
206
212
|
description: Block npm publish / git tag v* without a recent dogfood ledger entry.
|
|
@@ -218,6 +224,12 @@ policies:
|
|
|
218
224
|
verb: mcp__agent-grounding__ledger_add
|
|
219
225
|
example: '{type:"fact", content:"dogfood:\${SESSION_ID} — <end-to-end smoke summary against the live system>", source:"manual smoke test"}'
|
|
220
226
|
description: Before tagging or publishing, run the release path end-to-end against the live system (not just unit tests) and persist the result as a session-tagged ledger entry. Document what you exercised (install, CLI happy path, MCP handshake, etc.) so a future auditor can tell whether the smoke covered the change.
|
|
227
|
+
ux:
|
|
228
|
+
cannot: "You cannot publish a release yet."
|
|
229
|
+
required:
|
|
230
|
+
- "an end-to-end dogfood run in this session"
|
|
231
|
+
run:
|
|
232
|
+
- 'mcp__agent-grounding__ledger_add { type: "fact", content: "dogfood:\${SESSION_ID} — <end-to-end smoke summary>" }'
|
|
221
233
|
|
|
222
234
|
- name: two-reviewers-required
|
|
223
235
|
description: At least two distinct reviewer ledger entries must exist for the PR.
|
|
@@ -257,6 +269,12 @@ policies:
|
|
|
257
269
|
verb: mcp__agent-grounding__ledger_add
|
|
258
270
|
example: '{type:"fact", content:"preflight:\${REPO}", source:"manual"}'
|
|
259
271
|
description: Direct ledger write. Use when the Bash hook is locked down (e.g. understanding-gate active) or when the standard producer is unavailable.
|
|
272
|
+
ux:
|
|
273
|
+
cannot: "You cannot investigate this repository yet."
|
|
274
|
+
required:
|
|
275
|
+
- "verified repository preflight"
|
|
276
|
+
run:
|
|
277
|
+
- "harness preflight"
|
|
260
278
|
|
|
261
279
|
- name: review-subagent-before-pr-create
|
|
262
280
|
description: Block agent-tasks PR creation unless a review-subagent ledger entry tagged for this task already exists. Forces the rigorous review BEFORE the PR opens, not after.
|
|
@@ -274,6 +292,12 @@ policies:
|
|
|
274
292
|
verb: mcp__agent-grounding__ledger_add
|
|
275
293
|
example: '{type:"fact", content:"review-subagent:\${TASK_ID} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
|
|
276
294
|
description: After running a review subagent against the staged diff, persist its verdict + load-bearing findings as a ledger entry tagged with the task UUID. The content should be self-contained enough to audit later without re-reading the chat.
|
|
295
|
+
ux:
|
|
296
|
+
cannot: "You cannot open a pull request for task \${TASK_ID} yet."
|
|
297
|
+
required:
|
|
298
|
+
- "a completed review-subagent pass on this task"
|
|
299
|
+
run:
|
|
300
|
+
- 'mcp__agent-grounding__ledger_add { type: "fact", content: "review-subagent:\${TASK_ID} — <verdict + key findings + nits>" }'
|
|
277
301
|
|
|
278
302
|
- name: preflight-before-push
|
|
279
303
|
description: Block git push unless a fresh preflight ledger entry exists for the current branch. Catches the stale-checkout class of incident at the last reversible step.
|
|
@@ -294,6 +318,12 @@ policies:
|
|
|
294
318
|
verb: mcp__agent-grounding__ledger_add
|
|
295
319
|
example: '{type:"fact", content:"preflight:\${BRANCH} — <summary of what is on the branch + smoke results>", source:"manual"}'
|
|
296
320
|
description: Direct ledger write. The branch is the WIP review surface; the content should summarise what is staged + the smoke evidence so a reviewer can audit later without re-reading the chat.
|
|
321
|
+
ux:
|
|
322
|
+
cannot: "You cannot push branch \${BRANCH} yet."
|
|
323
|
+
required:
|
|
324
|
+
- "a fresh preflight for \${BRANCH} (within the last 10 minutes)"
|
|
325
|
+
run:
|
|
326
|
+
- "harness preflight"
|
|
297
327
|
|
|
298
328
|
# Full inherits the Solo/Team understanding-gate stack: the Stop hook
|
|
299
329
|
# persists each Understanding Report and the PreToolUse pre-tool-use
|
|
@@ -320,6 +350,18 @@ policy_packs:
|
|
|
320
350
|
- kind: bash
|
|
321
351
|
command: harness approve understanding
|
|
322
352
|
description: Same command from any un-hooked terminal (operator only, not reachable from inside the gated session). Writes the canonical marker at harness.generated/.approvals/\${SESSION_ID}.
|
|
353
|
+
# ux (agent-tasks/e48e3b45): replaces the legacy engine-vocabulary
|
|
354
|
+
# deny envelope with the plain-language { cannot, required, run }
|
|
355
|
+
# shape. Engine details (the BLOCK reason naming session id /
|
|
356
|
+
# marker / report state) still land in stderr for operator audit;
|
|
357
|
+
# the agent only sees this.
|
|
358
|
+
ux:
|
|
359
|
+
cannot: "You cannot use write-capable tools yet."
|
|
360
|
+
required:
|
|
361
|
+
- "an approved Understanding Report for this session"
|
|
362
|
+
run:
|
|
363
|
+
- "Write an Understanding Report covering: Current Understanding, Intended Outcome, Derived Todos, Acceptance Criteria, Assumptions, Open Questions, Out Of Scope, Risks, Verification Plan"
|
|
364
|
+
- "Run \`harness approve understanding\` and approve the prompt"
|
|
323
365
|
`;
|
|
324
366
|
import { SOLO_TEMPLATE, TEAM_TEMPLATE } from "./profiles.js";
|
|
325
367
|
export function getTemplate(name) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsV5B,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAI7D,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,aAAa,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC;QACvB,KAAK,SAAS;YACZ,OAAO,gBAAgB,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type LedgerEntry } from "../../policies/index.js";
|
|
2
|
+
import type { Manifest } from "../../schema/index.js";
|
|
3
|
+
import { type LoaderOptions } from "../loader.js";
|
|
4
|
+
export interface PackHookBranchProtectionOptions extends LoaderOptions {
|
|
5
|
+
/** Defaults to process.stdin. */
|
|
6
|
+
stdin?: NodeJS.ReadableStream;
|
|
7
|
+
/** Defaults to process.stdout. */
|
|
8
|
+
stdout?: NodeJS.WritableStream;
|
|
9
|
+
/** Defaults to process.stderr. */
|
|
10
|
+
stderr?: NodeJS.WritableStream;
|
|
11
|
+
/** Override "now" for deterministic freshness-window tests. */
|
|
12
|
+
now?: Date;
|
|
13
|
+
/** Override the cwd resolution (test injection). */
|
|
14
|
+
cwd?: string;
|
|
15
|
+
/** Per-call ledger timeout in ms. */
|
|
16
|
+
ledgerTimeoutMs?: number;
|
|
17
|
+
/** Inject a manifest (test). */
|
|
18
|
+
manifest?: Manifest;
|
|
19
|
+
/** Inject a fake ledger query (test). */
|
|
20
|
+
ledgerQuery?: (sessionId: string) => Promise<LedgerEntry[] | {
|
|
21
|
+
degraded: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export interface PackHookBranchProtectionResult {
|
|
25
|
+
exitCode: number;
|
|
26
|
+
blocked: boolean;
|
|
27
|
+
/** Diagnostic line emitted to stderr (always, even on allow). */
|
|
28
|
+
diagnostic: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function runPackHookBranchProtectionCli(opts?: PackHookBranchProtectionOptions): Promise<PackHookBranchProtectionResult>;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// `harness pack hook branch-protection` — PreToolUse blocker for the
|
|
2
|
+
// `branch-protection` policy pack.
|
|
3
|
+
//
|
|
4
|
+
// Receives Claude Code's PreToolUse event JSON on stdin and emits a
|
|
5
|
+
// `{ decision: "block" }` envelope when the agent is about to mutate
|
|
6
|
+
// source on a protected branch without a satisfying ledger tag.
|
|
7
|
+
//
|
|
8
|
+
// Two paths satisfy the gate:
|
|
9
|
+
//
|
|
10
|
+
// 1. **Producer path** — a `branch:non-protected` tag exists in the
|
|
11
|
+
// ledger from within the last 5 minutes (set by
|
|
12
|
+
// `harness session-start branch-check` when the session opened on
|
|
13
|
+
// a non-protected branch).
|
|
14
|
+
//
|
|
15
|
+
// 2. **Override path** — a `branch-protection-ack:` tag exists in the
|
|
16
|
+
// ledger (any age). The operator writes this from outside the
|
|
17
|
+
// gated shell via `mcp__agent-grounding__ledger_add` to bless a
|
|
18
|
+
// deliberate protected-branch edit (version bumps, CI workflow
|
|
19
|
+
// patches, hotfixes). The `:<reason>` suffix is free-form so the
|
|
20
|
+
// audit log can read WHY the override fired.
|
|
21
|
+
//
|
|
22
|
+
// Failure mode: any error in load / parse / ledger query resolves to
|
|
23
|
+
// BLOCK. This is the inverse of understanding-before-execution's
|
|
24
|
+
// fail-open contract: branch-protection's whole job is to prevent
|
|
25
|
+
// edit-on-master incidents, so a bug in the blocker that silently
|
|
26
|
+
// allowed Writes through would defeat the purpose. The block envelope
|
|
27
|
+
// always names a recovery path so the operator is never wedged.
|
|
28
|
+
import { queryLedgerByTag, } from "../../policies/index.js";
|
|
29
|
+
import { ACK_TAG_PREFIX, DEFAULT_PROTECTED_BRANCHES, NON_PROTECTED_TAG_PREFIX, PACK_NAME, PRODUCER_FRESHNESS_MS, resolveProtectedBranches, } from "../../policy-packs/builtin/branch-protection-runtime.js";
|
|
30
|
+
import { resolveGitContext } from "../../runtime/git-context.js";
|
|
31
|
+
import { POLICY_DECISION_TYPE } from "../../runtime/ledger-record.js";
|
|
32
|
+
import { loadManifest } from "../loader.js";
|
|
33
|
+
async function readStdin(stream) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
let data = "";
|
|
36
|
+
stream.setEncoding("utf8");
|
|
37
|
+
stream.on("data", (chunk) => {
|
|
38
|
+
data += chunk;
|
|
39
|
+
});
|
|
40
|
+
stream.on("end", () => resolve(data));
|
|
41
|
+
stream.on("error", (err) => reject(err));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function findGroundingMcp(manifest) {
|
|
45
|
+
return manifest.tools.mcp.find((m) => m.name === "grounding-mcp") ?? null;
|
|
46
|
+
}
|
|
47
|
+
function evaluateEntries(entries, now) {
|
|
48
|
+
const cutoff = now.getTime() - PRODUCER_FRESHNESS_MS;
|
|
49
|
+
let hasFreshProducer = false;
|
|
50
|
+
let hasAck = false;
|
|
51
|
+
let freshProducerContent = null;
|
|
52
|
+
let ackContent = null;
|
|
53
|
+
for (const e of entries) {
|
|
54
|
+
// Skip policy_decision audit rows: their serialized payload
|
|
55
|
+
// incidentally contains the tag they're about (e.g. a denied
|
|
56
|
+
// decision the engine recorded for THIS pack would carry the
|
|
57
|
+
// literal "branch:non-protected" or "branch-protection-ack" in
|
|
58
|
+
// its JSON, falsely satisfying the gate). Two-tier filter
|
|
59
|
+
// mirrors `src/policies/requires.ts:75-83`: by-type for current
|
|
60
|
+
// ledger rows, by-content-prefix as a backstop for legacy rows
|
|
61
|
+
// a pre-Phase-5-#4 ledger may still carry.
|
|
62
|
+
if (e.type === POLICY_DECISION_TYPE)
|
|
63
|
+
continue;
|
|
64
|
+
if (e.content.startsWith(`${POLICY_DECISION_TYPE}:`))
|
|
65
|
+
continue;
|
|
66
|
+
if (e.content.includes(ACK_TAG_PREFIX)) {
|
|
67
|
+
hasAck = true;
|
|
68
|
+
if (ackContent === null)
|
|
69
|
+
ackContent = e.content;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (!e.content.includes(NON_PROTECTED_TAG_PREFIX))
|
|
73
|
+
continue;
|
|
74
|
+
const ts = e.createdAt instanceof Date ? e.createdAt : new Date(e.createdAt);
|
|
75
|
+
if (Number.isNaN(ts.getTime()))
|
|
76
|
+
continue;
|
|
77
|
+
if (ts.getTime() >= cutoff) {
|
|
78
|
+
hasFreshProducer = true;
|
|
79
|
+
if (freshProducerContent === null)
|
|
80
|
+
freshProducerContent = e.content;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
hasFreshProducer,
|
|
85
|
+
hasAck,
|
|
86
|
+
freshProducerContent,
|
|
87
|
+
ackContent,
|
|
88
|
+
totalEntries: entries.length,
|
|
89
|
+
degraded: null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async function probeLedger(manifest, sessionId, opts) {
|
|
93
|
+
if (opts.ledgerQuery) {
|
|
94
|
+
const r = await opts.ledgerQuery(sessionId);
|
|
95
|
+
if ("degraded" in r) {
|
|
96
|
+
return {
|
|
97
|
+
hasFreshProducer: false,
|
|
98
|
+
hasAck: false,
|
|
99
|
+
freshProducerContent: null,
|
|
100
|
+
ackContent: null,
|
|
101
|
+
totalEntries: 0,
|
|
102
|
+
degraded: r.degraded,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return evaluateEntries(r, opts.now ?? new Date());
|
|
106
|
+
}
|
|
107
|
+
if (!manifest) {
|
|
108
|
+
return {
|
|
109
|
+
hasFreshProducer: false,
|
|
110
|
+
hasAck: false,
|
|
111
|
+
freshProducerContent: null,
|
|
112
|
+
ackContent: null,
|
|
113
|
+
totalEntries: 0,
|
|
114
|
+
degraded: "manifest unavailable",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const server = findGroundingMcp(manifest);
|
|
118
|
+
if (!server) {
|
|
119
|
+
return {
|
|
120
|
+
hasFreshProducer: false,
|
|
121
|
+
hasAck: false,
|
|
122
|
+
freshProducerContent: null,
|
|
123
|
+
ackContent: null,
|
|
124
|
+
totalEntries: 0,
|
|
125
|
+
degraded: "grounding-mcp not declared in manifest",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const command = Array.isArray(server.command)
|
|
129
|
+
? server.command
|
|
130
|
+
: server.command.trim().split(/\s+/);
|
|
131
|
+
const env = server.env ?? undefined;
|
|
132
|
+
const timeoutMs = opts.ledgerTimeoutMs ?? server.health?.timeout_ms ?? 5_000;
|
|
133
|
+
const result = await queryLedgerByTag({
|
|
134
|
+
mcpCommand: command,
|
|
135
|
+
...(env && { mcpEnv: env }),
|
|
136
|
+
sessionId,
|
|
137
|
+
timeoutMs,
|
|
138
|
+
});
|
|
139
|
+
if (result.kind === "degraded") {
|
|
140
|
+
return {
|
|
141
|
+
hasFreshProducer: false,
|
|
142
|
+
hasAck: false,
|
|
143
|
+
freshProducerContent: null,
|
|
144
|
+
ackContent: null,
|
|
145
|
+
totalEntries: 0,
|
|
146
|
+
degraded: result.reason,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return evaluateEntries(result.entries, opts.now ?? new Date());
|
|
150
|
+
}
|
|
151
|
+
function blockJson(toolName, branch, detail, protectedList) {
|
|
152
|
+
const minutes = Math.round(PRODUCER_FRESHNESS_MS / 60000);
|
|
153
|
+
const reasonText = `branch-protection: refusing ${toolName} on protected branch "${branch}". ` +
|
|
154
|
+
`${detail}\n` +
|
|
155
|
+
`To proceed, cut a feature branch and re-run the producer:\n` +
|
|
156
|
+
` git checkout -b <feature-slug>\n` +
|
|
157
|
+
` harness session-start branch-check\n` +
|
|
158
|
+
`Once the gate sees a fresh ${NON_PROTECTED_TAG_PREFIX} tag (within ${minutes}m), this tool call will succeed.\n` +
|
|
159
|
+
`\n` +
|
|
160
|
+
`Override (use sparingly): write \`${ACK_TAG_PREFIX}:<reason>\` to the ledger via mcp__agent-grounding__ledger_add. ` +
|
|
161
|
+
`The override survives the session and bypasses this gate.\n` +
|
|
162
|
+
`\n` +
|
|
163
|
+
`Protected branches: ${protectedList.join(", ")}.`;
|
|
164
|
+
return JSON.stringify({
|
|
165
|
+
decision: "block",
|
|
166
|
+
reason: reasonText,
|
|
167
|
+
hookSpecificOutput: {
|
|
168
|
+
hookEventName: "PreToolUse",
|
|
169
|
+
permissionDecision: "deny",
|
|
170
|
+
permissionDecisionReason: reasonText,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
export async function runPackHookBranchProtectionCli(opts = {}) {
|
|
175
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
176
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
177
|
+
const stderr = opts.stderr ?? process.stderr;
|
|
178
|
+
const note = (msg) => {
|
|
179
|
+
stderr.write(`harness pack hook branch-protection: ${msg}\n`);
|
|
180
|
+
};
|
|
181
|
+
// Defensive stdin parse. Empty / malformed input resolves to BLOCK
|
|
182
|
+
// (the inverse of understanding-before-execution's allow-on-malformed
|
|
183
|
+
// default): we'd rather block a Write we couldn't classify than let
|
|
184
|
+
// it through silently.
|
|
185
|
+
const raw = await readStdin(stdin);
|
|
186
|
+
let event = {};
|
|
187
|
+
try {
|
|
188
|
+
event = JSON.parse(raw.trim() || "{}");
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
/* event stays {} — handled by the sessionId check below */
|
|
192
|
+
}
|
|
193
|
+
const sessionId = (typeof event.session_id === "string" ? event.session_id : undefined) ??
|
|
194
|
+
process.env.CLAUDE_SESSION_ID ??
|
|
195
|
+
"";
|
|
196
|
+
const toolName = typeof event.tool_name === "string" ? event.tool_name : "(unknown)";
|
|
197
|
+
const cwd = typeof opts.cwd === "string" && opts.cwd.length > 0
|
|
198
|
+
? opts.cwd
|
|
199
|
+
: typeof event.cwd === "string" && event.cwd.length > 0
|
|
200
|
+
? event.cwd
|
|
201
|
+
: process.cwd();
|
|
202
|
+
// Load manifest to resolve the protected-branches list AND the
|
|
203
|
+
// grounding-mcp wiring. A manifest load failure forces BLOCK with a
|
|
204
|
+
// clear hint — we can't know if the gate should fire if we can't
|
|
205
|
+
// read its config.
|
|
206
|
+
let manifest = null;
|
|
207
|
+
if (opts.manifest) {
|
|
208
|
+
manifest = opts.manifest;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
try {
|
|
212
|
+
manifest = loadManifest(opts).manifest;
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
const reason = `manifest load failed (${err.message}); refusing on failsafe`;
|
|
216
|
+
const diagnostic = `BLOCK — ${reason}`;
|
|
217
|
+
note(diagnostic);
|
|
218
|
+
stdout.write(`${blockJson(toolName, "(unresolvable)", reason, DEFAULT_PROTECTED_BRANCHES)}\n`);
|
|
219
|
+
return { exitCode: 0, blocked: true, diagnostic };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const pack = manifest.policy_packs.find((p) => p.name === PACK_NAME);
|
|
223
|
+
if (!pack) {
|
|
224
|
+
const diagnostic = `pack "${PACK_NAME}" not declared in manifest, allowing`;
|
|
225
|
+
note(diagnostic);
|
|
226
|
+
return { exitCode: 0, blocked: false, diagnostic };
|
|
227
|
+
}
|
|
228
|
+
if (!pack.enabled) {
|
|
229
|
+
const diagnostic = `pack "${PACK_NAME}" is enabled:false, allowing`;
|
|
230
|
+
note(diagnostic);
|
|
231
|
+
return { exitCode: 0, blocked: false, diagnostic };
|
|
232
|
+
}
|
|
233
|
+
const { branches: protectedList } = resolveProtectedBranches(pack);
|
|
234
|
+
const { branch } = resolveGitContext(cwd);
|
|
235
|
+
// Outside a git work tree (or detached HEAD) we can't tell what the
|
|
236
|
+
// edit would land on. We choose to allow here — the alternative is
|
|
237
|
+
// blocking every Write in non-git workspaces, which would be hostile
|
|
238
|
+
// to standalone-script workflows. A detached HEAD on an in-repo cwd
|
|
239
|
+
// also lands here; arguably should block, but git-detached-HEAD
|
|
240
|
+
// edits don't auto-push to a protected ref so the downstream
|
|
241
|
+
// `preflight-before-push` gate still catches the actual hazard.
|
|
242
|
+
if (branch === "") {
|
|
243
|
+
const diagnostic = `cwd is not on a named branch (detached HEAD or outside a git work tree); allowing`;
|
|
244
|
+
note(diagnostic);
|
|
245
|
+
return { exitCode: 0, blocked: false, diagnostic };
|
|
246
|
+
}
|
|
247
|
+
if (!protectedList.includes(branch)) {
|
|
248
|
+
const diagnostic = `branch "${branch}" is not in the protected list (${protectedList.join(", ")}); allowing`;
|
|
249
|
+
note(diagnostic);
|
|
250
|
+
return { exitCode: 0, blocked: false, diagnostic };
|
|
251
|
+
}
|
|
252
|
+
// On a protected branch: probe the ledger for either gate path.
|
|
253
|
+
if (sessionId === "") {
|
|
254
|
+
const reason = `no session_id resolvable from stdin or $CLAUDE_SESSION_ID; cannot consult ledger`;
|
|
255
|
+
const diagnostic = `BLOCK — ${reason}`;
|
|
256
|
+
note(diagnostic);
|
|
257
|
+
stdout.write(`${blockJson(toolName, branch, reason, protectedList)}\n`);
|
|
258
|
+
return { exitCode: 0, blocked: true, diagnostic };
|
|
259
|
+
}
|
|
260
|
+
const check = await probeLedger(manifest, sessionId, opts);
|
|
261
|
+
if (check.hasAck) {
|
|
262
|
+
const diagnostic = `ACK override active (${check.ackContent ?? ACK_TAG_PREFIX}); allowing`;
|
|
263
|
+
note(diagnostic);
|
|
264
|
+
return { exitCode: 0, blocked: false, diagnostic };
|
|
265
|
+
}
|
|
266
|
+
if (check.hasFreshProducer) {
|
|
267
|
+
const diagnostic = `fresh producer tag (${check.freshProducerContent ?? NON_PROTECTED_TAG_PREFIX}); allowing`;
|
|
268
|
+
note(diagnostic);
|
|
269
|
+
return { exitCode: 0, blocked: false, diagnostic };
|
|
270
|
+
}
|
|
271
|
+
const why = check.degraded !== null
|
|
272
|
+
? `ledger degraded (${check.degraded}); refusing on failsafe`
|
|
273
|
+
: `no fresh ${NON_PROTECTED_TAG_PREFIX} tag (${check.totalEntries} entries scanned) and no ${ACK_TAG_PREFIX} override`;
|
|
274
|
+
const diagnostic = `BLOCK — ${why}`;
|
|
275
|
+
note(diagnostic);
|
|
276
|
+
stdout.write(`${blockJson(toolName, branch, why, protectedList)}\n`);
|
|
277
|
+
return { exitCode: 0, blocked: true, diagnostic };
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=hook-branch-protection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-branch-protection.js","sourceRoot":"","sources":["../../../src/cli/pack/hook-branch-protection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type LedgerEntry } from "../../policies/index.js";
|
|
2
2
|
import { type ApprovalCheckResult } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type Manifest } from "../../schema/index.js";
|
|
4
4
|
import { type LoaderOptions } from "../loader.js";
|
|
5
5
|
export interface PackHookCodexPreToolUseOptions extends LoaderOptions {
|
|
6
6
|
/** Pack name to evaluate. Defaults to understanding-before-execution. */
|
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
import { queryLedgerByTag } from "../../policies/index.js";
|
|
23
23
|
import { checkApprovalMarker, checkPersistedReport, defaultReportsDir, matchLedgerEntries, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
24
24
|
import { resolveGeneratedDir } from "../../runtime/pending-approval.js";
|
|
25
|
+
import { renderAgentFacing } from "../../runtime/agent-facing.js";
|
|
26
|
+
import { PolicyUxSchema } from "../../schema/index.js";
|
|
25
27
|
import { loadManifest } from "../loader.js";
|
|
28
|
+
import { renderReportSchemaHint } from "./understanding-report-schema-hint.js";
|
|
26
29
|
const PACK_NAME = "understanding-before-execution";
|
|
27
30
|
const EXIT_BLOCK = 2;
|
|
28
31
|
async function readStdin(stream) {
|
|
@@ -36,6 +39,18 @@ async function readStdin(stream) {
|
|
|
36
39
|
stream.on("error", (err) => reject(err));
|
|
37
40
|
});
|
|
38
41
|
}
|
|
42
|
+
function parseConfigUx(raw, stderr) {
|
|
43
|
+
if (raw === undefined)
|
|
44
|
+
return undefined;
|
|
45
|
+
const result = PolicyUxSchema.safeParse(raw);
|
|
46
|
+
if (!result.success) {
|
|
47
|
+
stderr.write(`harness pack hook codex: config.ux ignored (${result.error.issues
|
|
48
|
+
.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
49
|
+
.join("; ")})\n`);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return result.data;
|
|
53
|
+
}
|
|
39
54
|
function pickString(...candidates) {
|
|
40
55
|
for (const c of candidates) {
|
|
41
56
|
if (typeof c === "string" && c.length > 0)
|
|
@@ -162,8 +177,18 @@ export async function runPackHookCodexPreToolUseCli(opts = {}) {
|
|
|
162
177
|
const reason = generatedDir !== undefined
|
|
163
178
|
? `no approval marker for session ${sessionId}; ${report.detail}; ${ledger.detail}`
|
|
164
179
|
: `generatedDir not resolvable (test/injection path); ${report.detail}; ${ledger.detail}`;
|
|
165
|
-
|
|
166
|
-
|
|
180
|
+
// When the pack config declares `ux:`, the agent-facing block becomes
|
|
181
|
+
// the plain-language shape and the legacy schemaHint text is
|
|
182
|
+
// suppressed. The engine-vocabulary `reason` still lands in stderr
|
|
183
|
+
// (operator audit surface, not agent surface) so a flapping gate
|
|
184
|
+
// remains diagnosable.
|
|
185
|
+
const configUx = parseConfigUx(declared.config["ux"], stderr);
|
|
186
|
+
const agentFacing = configUx
|
|
187
|
+
? renderAgentFacing(configUx, { SESSION_ID: sessionId, TOOL_NAME: toolName })
|
|
188
|
+
: `Run \`harness approve understanding\` once you have produced and confirmed an Understanding Report.\n${renderReportSchemaHint()}`;
|
|
189
|
+
const diagnostic = configUx
|
|
190
|
+
? `harness pack hook codex: BLOCK: ${reason}.\n${agentFacing}`
|
|
191
|
+
: `harness pack hook codex: BLOCK: ${reason}. Tool: ${toolName}. ${agentFacing}`;
|
|
167
192
|
stderr.write(`${diagnostic}\n`);
|
|
168
193
|
return {
|
|
169
194
|
exitCode: EXIT_BLOCK,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-codex-pre-tool-use.js","sourceRoot":"","sources":["../../../src/cli/pack/hook-codex-pre-tool-use.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,qEAAqE;AACrE,wEAAwE;AACxE,4BAA4B;AAC5B,EAAE;AACF,+DAA+D;AAC/D,wEAAwE;AACxE,+DAA+D;AAC/D,0CAA0C;AAC1C,mEAAmE;AACnE,sCAAsC;AACtC,qEAAqE;AACrE,oEAAoE;AACpE,sEAAsE;AACtE,0DAA0D;AAC1D,EAAE;AACF,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,qEAAqE;AAErE,OAAO,EAAE,gBAAgB,EAAoB,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GAEnB,MAAM,sEAAsE,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-codex-pre-tool-use.js","sourceRoot":"","sources":["../../../src/cli/pack/hook-codex-pre-tool-use.ts"],"names":[],"mappings":"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"}
|