@lannguyensi/harness 0.52.0 → 0.54.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cli/delegate/index.d.ts +15 -2
  3. package/dist/cli/delegate/index.js +128 -4
  4. package/dist/cli/delegate/index.js.map +1 -1
  5. package/dist/cli/doctor/bypass-without-auto-approve.d.ts +38 -0
  6. package/dist/cli/doctor/bypass-without-auto-approve.js +95 -0
  7. package/dist/cli/doctor/bypass-without-auto-approve.js.map +1 -0
  8. package/dist/cli/doctor/format.js +7 -0
  9. package/dist/cli/doctor/format.js.map +1 -1
  10. package/dist/cli/doctor/index.js +17 -0
  11. package/dist/cli/doctor/index.js.map +1 -1
  12. package/dist/cli/doctor/types.d.ts +11 -0
  13. package/dist/cli/doctor/types.js.map +1 -1
  14. package/dist/cli/doctor/ug-auto-approvals.js +18 -43
  15. package/dist/cli/doctor/ug-auto-approvals.js.map +1 -1
  16. package/dist/cli/dry-run.js +32 -1
  17. package/dist/cli/dry-run.js.map +1 -1
  18. package/dist/cli/gc/index.d.ts +13 -2
  19. package/dist/cli/gc/index.js +203 -1
  20. package/dist/cli/gc/index.js.map +1 -1
  21. package/dist/cli/index.js +57 -11
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/init/composer.js +9 -0
  24. package/dist/cli/init/composer.js.map +1 -1
  25. package/dist/cli/init/profiles.d.ts +2 -2
  26. package/dist/cli/init/profiles.js +100 -8
  27. package/dist/cli/init/profiles.js.map +1 -1
  28. package/dist/cli/init/templates.d.ts +1 -1
  29. package/dist/cli/init/templates.js +174 -3
  30. package/dist/cli/init/templates.js.map +1 -1
  31. package/dist/cli/pack/hook-pre-tool-use.js +219 -115
  32. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  33. package/dist/cli/pack/index.d.ts +1 -0
  34. package/dist/cli/pack/index.js +1 -0
  35. package/dist/cli/pack/index.js.map +1 -1
  36. package/dist/cli/pack/upgrade.d.ts +32 -0
  37. package/dist/cli/pack/upgrade.js +273 -0
  38. package/dist/cli/pack/upgrade.js.map +1 -0
  39. package/dist/cli/record/index.d.ts +8 -0
  40. package/dist/cli/record/index.js +55 -7
  41. package/dist/cli/record/index.js.map +1 -1
  42. package/dist/cli/validate/checks.js +61 -9
  43. package/dist/cli/validate/checks.js.map +1 -1
  44. package/dist/io/extract.d.ts +29 -0
  45. package/dist/io/extract.js +36 -0
  46. package/dist/io/extract.js.map +1 -1
  47. package/dist/io/read-json-dir-entries.d.ts +36 -0
  48. package/dist/io/read-json-dir-entries.js +38 -0
  49. package/dist/io/read-json-dir-entries.js.map +1 -0
  50. package/dist/policies/index.d.ts +1 -1
  51. package/dist/policies/index.js +1 -1
  52. package/dist/policies/index.js.map +1 -1
  53. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.d.ts +35 -0
  54. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js +86 -0
  55. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js.map +1 -0
  56. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +51 -0
  57. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +55 -1
  58. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -1
  59. package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +3 -1
  60. package/dist/policy-packs/builtin/understanding-before-execution/index.js +18 -1
  61. package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
  62. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.d.ts +71 -0
  63. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +175 -0
  64. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +1 -0
  65. package/dist/runtime/destructive-shell-floor.d.ts +19 -0
  66. package/dist/runtime/destructive-shell-floor.js +597 -0
  67. package/dist/runtime/destructive-shell-floor.js.map +1 -0
  68. package/dist/runtime/git-context.js +7 -1
  69. package/dist/runtime/git-context.js.map +1 -1
  70. package/dist/runtime/intercept.d.ts +10 -7
  71. package/dist/runtime/intercept.js +108 -9
  72. package/dist/runtime/intercept.js.map +1 -1
  73. package/dist/runtime/read-only-bash.d.ts +21 -0
  74. package/dist/runtime/read-only-bash.js +484 -0
  75. package/dist/runtime/read-only-bash.js.map +1 -1
  76. package/dist/runtime/risk-classifier.js +68 -13
  77. package/dist/runtime/risk-classifier.js.map +1 -1
  78. package/dist/runtime/when-eval.d.ts +18 -5
  79. package/dist/runtime/when-eval.js +64 -11
  80. package/dist/runtime/when-eval.js.map +1 -1
  81. package/dist/runtime/workflow-policies.d.ts +43 -1
  82. package/dist/runtime/workflow-policies.js +158 -1
  83. package/dist/runtime/workflow-policies.js.map +1 -1
  84. package/dist/schema/extract.d.ts +4 -0
  85. package/dist/schema/extract.js +64 -1
  86. package/dist/schema/extract.js.map +1 -1
  87. package/dist/schema/index.d.ts +13 -0
  88. package/dist/schema/policies.d.ts +19 -0
  89. package/dist/schema/policies.js +17 -1
  90. package/dist/schema/policies.js.map +1 -1
  91. package/package.json +1 -1
@@ -8,15 +8,26 @@
8
8
  // defend my interpretation before I touch anything write-
9
9
  // capable" gate without dragging in the agent-tasks loop.
10
10
  //
11
- // team: solo + agent-tasks MCP server + review-before-merge policy.
12
- // Adds the merge gate that requires a ledger entry tagged
13
- // review:<pr-number> for the current session before the
14
- // pull_requests_merge MCP verb fires.
11
+ // team: solo + agent-tasks MCP server + the merge-gate policies.
12
+ // Adds the merge gates that require a recorded review for the
13
+ // current session before a PR can land: review:<pr-number>
14
+ // before pull_requests_merge, and (task 2699b476)
15
+ // review:<task-id> before task_merge or a task_finish with
16
+ // autoMerge: true.
15
17
  //
16
18
  // Both manifests are validate-clean against the v1 schema. They use the
17
19
  // same path layouts as the existing FULL template (`~/git/pandora/...`,
18
20
  // `~/.claude/...`); operators on a different layout should override via
19
21
  // `~/.harness/machines/<host>.harness.overrides.yaml` (ARCHITECTURE §8).
22
+ // D-004 (task 8f637efd, docs/decisions/2026-08-27-ug-auto-mode-approval.md,
23
+ // "Amendment: install default"): the shipped `auto_approve` block, read
24
+ // from the one canonical renderer FULL_TEMPLATE also uses, so the three
25
+ // templates cannot drift on the snippet's shape or wording. See
26
+ // auto-approve-default.ts for the rationale.
27
+ import { renderAutoApproveSnippet } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
28
+ // Both templates below nest `auto_approve:` at 6 spaces, a sibling of
29
+ // `mode:` / `approval_lifecycle:` under the pack's `config:` key.
30
+ const AUTO_APPROVE_SNIPPET = renderAutoApproveSnippet(6);
20
31
  export const SOLO_TEMPLATE = `# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)
21
32
  #
22
33
  # Bootstrapped by \`harness init --template solo\`.
@@ -112,16 +123,19 @@ policy_packs:
112
123
  - '^gh pr (merge|close)\\b'
113
124
  - '^git push origin (master|main)\\b'
114
125
  max_age: 1h
126
+ ${AUTO_APPROVE_SNIPPET}
115
127
  `;
116
128
  export const TEAM_TEMPLATE = `# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)
117
129
  #
118
130
  # Bootstrapped by \`harness init --template team\`.
119
131
  #
120
- # Solo profile + agent-tasks MCP + the review-before-merge policy. Block
132
+ # Solo profile + agent-tasks MCP + the merge-gate policies. Block
121
133
  # pull_requests_merge MCP calls unless a ledger entry tagged
122
- # review:<pr-number> exists for the current grounding session, the
123
- # standard team workflow where every PR gets a review-subagent pass
124
- # before it can land.
134
+ # review:<pr-number> exists for the current grounding session, and the
135
+ # two task-scoped merge verbs (task_merge, task_finish with
136
+ # autoMerge: true) unless a review:<task-id> entry does: the standard
137
+ # team workflow where every PR gets a review-subagent pass before it
138
+ # can land, on whichever surface it actually lands through.
125
139
  #
126
140
  # INTENTIONAL (operator decision 2026-08-08, task adf037c1): like the solo
127
141
  # profile, this template does NOT carry the full template's operator_only
@@ -217,6 +231,26 @@ hooks:
217
231
  blocking: hard
218
232
  budget_ms: 15000
219
233
 
234
+ # The two OTHER agent-tasks verbs that land a PR (task 2699b476).
235
+ # \`task_merge\` merges the PR attached to a task; \`task_finish\` with
236
+ # \`autoMerge: true\` merges as part of finishing. Each needs its own
237
+ # hook entry because a hook's \`match\` is what \`harness apply\`
238
+ # projects into settings.json's tool-name matcher. Same budget_ms
239
+ # derivation as \`require-review-evidence\` above.
240
+ - name: require-review-evidence-task-merge
241
+ event: PreToolUse
242
+ match: "mcp__agent-tasks__task_merge"
243
+ command: harness policy intercept
244
+ blocking: hard
245
+ budget_ms: 15000
246
+
247
+ - name: require-review-evidence-task-finish
248
+ event: PreToolUse
249
+ match: "mcp__agent-tasks__task_finish"
250
+ command: harness policy intercept
251
+ blocking: hard
252
+ budget_ms: 15000
253
+
220
254
  policies:
221
255
  - name: review-before-merge
222
256
  description: Block PR merges unless a ledger entry tagged review:<pr-number> exists for this session.
@@ -245,6 +279,63 @@ policies:
245
279
  run:
246
280
  - 'harness record review --pr \${PR_NUMBER} "<summary>"'
247
281
 
282
+ # Task-scoped merge surfaces (task 2699b476). Tag shape is
283
+ # \`review:\${TASK_ID}\`: both verbs derive owner/repo/PR number from the
284
+ # task, so \`taskId\` is the only identifier in the tool payload.
285
+ # \`harness record review --pr <pr> --task <id> "<summary>"\` writes the
286
+ # PR, branch, base and task tags in ONE ledger fact, so a single
287
+ # recorded review satisfies every merge gate this profile ships.
288
+ # task_finish is gated ONLY in its auto-merge mode: the plain verb
289
+ # advances the task and merges nothing, so \`input_match\` narrows the
290
+ # trigger to the merging mode alone.
291
+ - name: review-before-task-merge
292
+ description: Block agent-tasks task_merge unless a ledger entry tagged review:<task-id> exists for this session.
293
+ trigger:
294
+ event: PreToolUse
295
+ match: "mcp__agent-tasks__task_merge"
296
+ extract:
297
+ TASK_ID: "toolArgs.taskId"
298
+ requires:
299
+ ledger_tag: "review:\${TASK_ID}"
300
+ hook: require-review-evidence-task-merge
301
+ enforcement: block
302
+ producers:
303
+ - kind: mcp
304
+ verb: mcp__grounding-mcp__ledger_add
305
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review:\${TASK_ID}: <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
306
+ description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the task id.
307
+ ux:
308
+ cannot: "You cannot merge the PR for task \${TASK_ID} yet."
309
+ required:
310
+ - "a recorded review of task \${TASK_ID}"
311
+ run:
312
+ - 'harness record review --pr <pr> --task \${TASK_ID} "<summary>"'
313
+
314
+ - name: review-before-task-finish-automerge
315
+ description: 'Block agent-tasks task_finish with autoMerge: true unless a ledger entry tagged review:<task-id> exists for this session.'
316
+ trigger:
317
+ event: PreToolUse
318
+ match: "mcp__agent-tasks__task_finish"
319
+ input_match:
320
+ toolArgs.autoMerge: true
321
+ extract:
322
+ TASK_ID: "toolArgs.taskId"
323
+ requires:
324
+ ledger_tag: "review:\${TASK_ID}"
325
+ hook: require-review-evidence-task-finish
326
+ enforcement: block
327
+ producers:
328
+ - kind: mcp
329
+ verb: mcp__grounding-mcp__ledger_add
330
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review:\${TASK_ID}: <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
331
+ description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the task id. Same evidence the task_merge gate reads, so one recorded review opens both.
332
+ ux:
333
+ cannot: "You cannot finish task \${TASK_ID} with autoMerge yet."
334
+ required:
335
+ - "a recorded review of task \${TASK_ID}"
336
+ run:
337
+ - 'harness record review --pr <pr> --task \${TASK_ID} "<summary>"'
338
+
248
339
  policy_packs:
249
340
  - name: understanding-before-execution
250
341
  source: builtin
@@ -279,5 +370,6 @@ policy_packs:
279
370
  - '^gh pr (merge|close)\\b'
280
371
  - '^git push origin (master|main)\\b'
281
372
  max_age: 4h
373
+ ${AUTO_APPROVE_SNIPPET}
282
374
  `;
283
375
  //# sourceMappingURL=profiles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../src/cli/init/profiles.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,kEAAkE;AAClE,kEAAkE;AAClE,kEAAkE;AAClE,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,kEAAkE;AAClE,gDAAgD;AAChD,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AAEzE,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+F5B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsK5B,CAAC"}
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../src/cli/init/profiles.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,kEAAkE;AAClE,kEAAkE;AAClE,kEAAkE;AAClE,EAAE;AACF,mEAAmE;AACnE,wEAAwE;AACxE,qEAAqE;AACrE,4DAA4D;AAC5D,qEAAqE;AACrE,6BAA6B;AAC7B,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AAEzE,4EAA4E;AAC5E,wEAAwE;AACxE,wEAAwE;AACxE,gEAAgE;AAChE,6CAA6C;AAC7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,sEAAsE,CAAC;AAEhH,sEAAsE;AACtE,kEAAkE;AAClE,MAAM,oBAAoB,GAAG,wBAAwB,CAAC,CAAC,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+F3B,oBAAoB;CACrB,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqP3B,oBAAoB;CACrB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export declare const MINIMAL_TEMPLATE = "# ~/.harness/harness.yaml (legacy: ~/.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 ~/.harness/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 = "# ~/.harness/harness.yaml (legacy: ~/.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 # 70s budget gives the wrapped preflight (default 60s) headroom plus\n # ledger-write time. Was 30s through v0.17.4, but a healthy preflight\n # on a medium-size repo takes ~28s and the old 25s wrapper ceiling\n # blew through it. Bumped together with DEFAULT_PREFLIGHT_TIMEOUT_MS\n # (agent-tasks/7265599e).\n budget_ms: 70000\n # Floor at agent-preflight 0.2.0, the release that makes secret\n # detection git-aware and diff-scoped: a gitignored+untracked .env,\n # a .md doc, a non-git dir, or a secret in a tracked file the branch\n # never touched is a non-blocking warn, not a hard fail. Pre-0.2.0\n # installs hard-fail preflight on the normal correct state (a\n # gitignored .env holding real credentials), so this SessionStart\n # producer never writes a preflight: tag and the preflight-before-*\n # policies stay closed forever on any repo with a local .env. (0.1.1\n # had already fixed the wrapper-script \"tool not installed\" false\n # positive.) version_command points at the source-of-truth preflight\n # binary, not at the `harness session-start preflight` wrapper.\n min_version: \"0.2.0\"\n version_command: [\"preflight\", \"--version\"]\n\n # toolchain-parity (PATH-shim incident 2026-07-22 follow-up): writes THIS\n # machine's toolchain snapshot (node version, npm globals, OW-Kit\n # version, MCP server names) to `toolchain_parity.machine_state_dir`\n # and advisorily compares it against every peer machine's snapshot\n # already there, warning on drift (version mismatches, missing\n # packages, node/OW-Kit drift, MCP-name differences). Purely advisory \u2014\n # no policy consumes the `toolchain-parity:` ledger fact this writes,\n # it exists for `harness audit`/operator visibility only. If a policy\n # ever does consume it, match with the `:unparseable-peer:<n>` suffix\n # in mind: requires-matching is substring-based, so a bare\n # `toolchain-parity:ok` tag would also match `ok:unparseable-peer:2`\n # (a partial comparison). DISABLED by\n # default (no `toolchain_parity:` block above): opt in with\n # `toolchain_parity: { enabled: true }` (machine_state_dir/profile/\n # workspace_root all have sane defaults \u2014 see docs/CLI.md). No external\n # binary to floor-check (node/npm are assumed present already), so\n # unlike git-preflight this hook carries no min_version/version_command.\n - name: toolchain-parity\n event: SessionStart\n command: harness session-start toolchain-parity\n blocking: false\n # node --version + npm ls -g run in PARALLEL (bounded ~2s/~4s each, so\n # max(2s,4s)=4s worst case for that pair), plus two near-instant file\n # reads; the ledger write runs SEQUENTIALLY after those and carries its\n # own timeout floor (resolveManifestLedgerWriter's default 5s absent an\n # explicit grounding-mcp health.timeout_ms or a bound\n # SessionStartToolchainParityOptions.ledgerTimeoutMs). Worst case is\n # therefore additive, ~4s + 5s = 9s, not the sub-5s the collectors alone\n # would suggest \u2014 10s still clears it, but with only ~1s of margin, not\n # the \"comfortable\" headroom an earlier version of this comment claimed\n # (task c1b5ade5). Nowhere near git-preflight's 70s (which wraps a full\n # external test suite, a fundamentally heavier operation this hook\n # never performs).\n budget_ms: 10000\n\n # Budget note (task 7bf47554, follow-up to the ms/seconds unit fix\n # f2d2a29): every `harness policy intercept` hook below down through\n # `risk-gate` carries `budget_ms: 15000`, i.e. a Claude Code outer\n # kill-timeout of `ceil(15000/1000) = 15` seconds (generate-settings.ts's\n # `hookTimeoutSeconds`). This is deliberately UNIFORM across all eleven of\n # them, for two independent reasons:\n #\n # 1. FAIL-CLOSED MARGIN. `harness policy intercept` evaluates its FULL\n # `policies:` list against the incoming event (src/runtime/intercept.ts,\n # `intercept()`), not just the one named policy this hook happens to\n # also register. Every policy in that evaluation loop that reaches a\n # verdict has its decision written to the evidence ledger via\n # `options.ledger.record()` BEFORE `intercept()` returns and stdout is\n # flushed (intercept.ts ~L1362-1377) \u2014 this includes `operator_only`\n # policies (the three kill-switch denies below), whose VERDICT needs no\n # ledger read but whose AUDIT WRITE is still a live grounding-mcp\n # round-trip on the critical path. A `requires:`-based policy (the\n # require-*-evidence / risk-gate policies) additionally QUERIES the\n # ledger for its verdict (intercept.ts's `evaluateOnePolicy`,\n # ~L752-777) and, on a `deny-degraded` outcome, may retry the audit\n # write once more on a fresh session (`realLedgerClient`,\n # src/cli/policy/intercept.ts ~L276-306). Measured worst case with the\n # grounding-mcp health.timeout_ms=5000 default: query (~5s) + one\n # fresh-session deny-degraded retry (initialize + ledger_add, each\n # bounded by `auditRetryTimeoutMs(5000)` ~=1.25s) is ~10.8-13.75s. If\n # Claude Code's outer timeout fires FIRST, it kills the subprocess\n # before the deny JSON reaches stdout \u2014 a hook that Claude Code cannot\n # read in time is treated as ALLOW, silently turning a computed,\n # fail-closed `deny`/`deny-degraded` verdict into an unintended\n # fail-open one. 15000ms clears the measured worst case with margin,\n # for BOTH the evidence-requiring policies (their own query+retry) AND\n # the pure pattern-deny policies (bounded lower, ~1x\n # health.timeout_ms=~5s via the mandatory record() alone, but still\n # above the pre-fix 1-2s floor).\n # 2. DEDUP-SAFETY. `generate-settings.ts`'s `buildGroups` collapses\n # multiple manifest hooks that share the same `match` (settings.json's\n # tool-name matcher \u2014 `bash_match` is NOT projected there) AND the same\n # `(command, timeout)` fingerprint into ONE settings.json hook entry,\n # specifically so Claude Code spawns `harness policy intercept` ONCE\n # per matching tool call instead of once per manifest hook name (the\n # comment on `buildGroups` names this explicitly: avoiding \"redundant\n # Node bootstraps and ledger queries per tool call\"). All nine\n # `match: \"Bash\"` hooks below share one settings.json matcher group;\n # giving them a NON-uniform budget_ms would make their computed\n # `timeout` values diverge, split that one group into several entries,\n # and reintroduce exactly the redundant-invocation cost the dedup\n # exists to avoid \u2014 on top of leaving whichever entry keeps a low\n # timeout still exposed to the fail-open risk above. Keeping all\n # eleven at the identical 15000ms budget_ms preserves the existing\n # one-invocation-per-matcher-group collapse (previously they all\n # collapsed onto the shared 2s floor; now they collapse onto 15s).\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: 15000\n\n # Tool-agnostic parallel of require-review-evidence for operators on the\n # gh-cli workflow (`gh pr merge`) instead of agent-tasks MCP. Same generic\n # `harness policy intercept` entrypoint; the matching review-before-merge-bash\n # policy below picks up the trigger. A PolicyTrigger can only AND-match one\n # surface (MCP tool-name OR Bash command), so two parallel definitions are\n # the minimum-scope way to cover both PR surfaces without bumping the schema.\n - name: require-review-evidence-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 15000\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: 15000\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: 15000\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: 15000\n\n # Bash-surface parallel of require-review-subagent-evidence for operators\n # who open PRs with `gh pr create` instead of agent-tasks MCP. The matching\n # review-subagent-before-pr-create-bash policy below tags by branch\n # (`review-subagent:${BRANCH}`) because no task UUID is in `gh pr create`\n # arguments; the working branch is the closest stable handle for \"the\n # PR-in-progress\" at this point in the cycle.\n - name: require-review-subagent-evidence-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S+\\s+)*gh pr create\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 15000\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: 15000\n\n # deny-kill-switch-bash / deny-session-env-strip-bash / deny-sentinel-write-bash\n # (task cf1fde6d): `harness pause` / `harness resume` refuse to run inside an\n # agent shell in CODE, but that CLI check is a speed bump, not a boundary: a\n # Claude Code `! `-prefixed shell inherits this session's env AND its non-TTY\n # stdin, so it is indistinguishable from an agent Bash call and trips the same\n # check the agent would trip (verified live; see\n # docs/okf/pause-vs-gate-kill-switch.md). The actual enforcement point that\n # runs BEFORE the agent's Bash executes is this PreToolUse deny-policy layer.\n # These three hooks/policies below deny, from Bash: the kill-switch verbs\n # themselves, the env-stripping wrappers that would otherwise defeat\n # `refuseIfAgentShell`, and direct writes to the unsigned pause sentinel file.\n - name: deny-kill-switch-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S*\\s+)*(?:npx\\s+|\\S*/)?harness\\s+([\"\\x27]?)(?:pause|resume|gate\\s+([\"\\x27]?)(?:disable|enable)\\4)\\3(?![\\w-])'\n command: harness policy intercept\n blocking: hard\n # 15000, not a smaller pattern-only budget: see the budget note above\n # require-review-evidence. This policy's own verdict (operator_only)\n # needs no ledger read, but intercept()'s audit-record call for that\n # verdict is still a live grounding-mcp round-trip on the critical\n # path, AND this hook shares its settings.json matcher group with the\n # evidence-requiring Bash hooks above (dedup-safety).\n budget_ms: 15000\n\n - name: deny-session-env-strip-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S*\\s+)*(env\\b[^;\\n|&]*-u\\s*(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)\\b|env\\b[^;\\n|&]*--unset(?:=|\\s+)(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)\\b|unset\\s+(\\S+\\s+)*(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)\\b|(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)=(?=\\s))'\n command: harness policy intercept\n blocking: hard\n # 15000: same rationale as deny-kill-switch-bash immediately above.\n budget_ms: 15000\n\n # Known gap, deliberately not faked as coverage: this only catches the\n # obvious shell shapes (`> .harness-paused`, `tee .harness-paused`,\n # `cp ... .harness-paused`). A regex over the raw command string cannot\n # see through a heredoc, `sh -c '...'`, `bash -lc`, `eval`,\n # `python -c`, base64-decoded payloads, an fs.write call inside a script\n # file the agent creates and then executes, a symlink swap, or the\n # sentinel-write equivalents of `cp`/`>` done via `mv`, `ln`,\n # `install`, or `dd`. These are NOT covered on purpose (regex whack-a-\n # mole against every file-write-capable tool does not meaningfully close\n # this class); closing it for real needs either signing the sentinel\n # (HMAC) or a filesystem-level write guard, neither of which is in scope\n # here; both are noted as follow-ups.\n - name: deny-sentinel-write-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S*\\s+)*(tee|cp)\\b[^;\\n|&]*\\.harness-paused\\b|>{1,2}\\s*\\S*\\.harness-paused\\b'\n command: harness policy intercept\n blocking: hard\n # 15000: same rationale as deny-kill-switch-bash above.\n budget_ms: 15000\n\n # risk-gate (Phase 7 #6): the Risk Gate enforcement hook. The\n # gate-prod-destructive policies below reference it. Same generic\n # `harness policy intercept` entrypoint as every other policy hook;\n # the interceptor builds the Action Envelope, classifies risk against\n # `risk.classifiers[]`, resolves the environment against\n # `environments.resolvers[]`, and evaluates the policies' `when:`.\n - name: risk-gate\n event: PreToolUse\n match: \"Bash\"\n command: harness policy intercept\n blocking: hard\n # 15000: same rationale as the budget note above require-review-evidence.\n budget_ms: 15000\n\n # Optional: runtime-reality drift gate (NOT enabled by default).\n # Blocks destructive runtime commands (compose down/restart, systemctl,\n # kill/pkill, ./deploy-*) when the live process state has drifted from what\n # your expectations file says should be running. Left COMMENTED on purpose:\n # the hook is host-coupled and, without RUNTIME_REALITY_KEYWORD + an\n # expectations file + RUNTIME_REALITY_PROBE_CMD, degrades silently to allow,\n # a no-op that looks like protection. To arm it, uncomment the entry and\n # fill in the three env values. The expectations-file format and how to\n # install the probe are documented in docs/runtime-reality-hook.md.\n #\n # - name: runtime-reality\n # event: PreToolUse\n # command: >-\n # RUNTIME_REALITY_KEYWORD=<your-stack>\n # RUNTIME_REALITY_EXPECTATIONS_DIR=$HOME/.runtime-reality/expectations\n # RUNTIME_REALITY_PROBE_CMD=\"node $HOME/.runtime-reality/probes/runtime-reality-docker-probe.mjs\"\n # harness pack hook runtime-reality\n # blocking: hard\n # description: Block destructive runtime commands on critical process drift\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__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", 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 - 'harness record review --pr ${PR_NUMBER} \"<summary>\"'\n\n # Bash-surface parallel of review-before-merge for operators on the gh-cli\n # workflow. Two scope notes:\n # 1. Tag shape: `review:${BRANCH}` instead of `review:${PR_NUMBER}`. The\n # `gh pr merge` invocation can target the PR by number, by URL, or by\n # the current branch (default), and PR_NUMBER is not extractable from\n # `tool_input.command` with today's JSONPath-only extract DSL. BRANCH\n # is the stable identifier the producer can record at review time.\n # 2. This sits ALONGSIDE review-before-merge \u2014 not as a replacement. An\n # operator using both surfaces (e.g. agent-tasks MCP for most repos\n # + gh-cli for a quick hotfix) will have both gates active, each with\n # its own tag shape, which is semantically honest.\n - name: review-before-merge-bash\n description: Block `gh pr merge` unless a ledger entry tagged review:<branch> exists for this session.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b'\n requires:\n ledger_tag: \"review:${BRANCH}\"\n hook: require-review-evidence-bash\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"review:${BRANCH} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: Spawn a review subagent against the branch diff, capture its verdict, then persist a ledger entry tagged with the branch name. Mirror of the review-before-merge producer for the gh-cli surface.\n ux:\n cannot: \"You cannot merge the PR for branch ${BRANCH} via `gh pr merge` yet.\"\n required:\n - \"a recorded review of the PR for branch ${BRANCH}\"\n run:\n - 'harness record review --pr <pr> \"<summary>\"'\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__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", 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 - 'harness record dogfood \"<was wurde real ausprobiert>\"'\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__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", 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__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", 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 - \"an approved Understanding Report, if the Understanding Gate is still active (it blocks `harness preflight` itself)\"\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__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", 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 - 'harness record review-subagent --task ${TASK_ID} --verdict <verdict>'\n\n # Bash-surface parallel of review-subagent-before-pr-create. Tag shape is\n # `review-subagent:${BRANCH}` because TASK_ID is an agent-tasks-only\n # concept; for the gh-cli workflow the working branch is the closest stable\n # handle for \"the PR-in-progress\" at this point. Same rationale as\n # review-before-merge-bash: sits alongside the MCP variant, not as a\n # replacement.\n - name: review-subagent-before-pr-create-bash\n description: Block `gh pr create` unless a review-subagent ledger entry tagged review-subagent:<branch> exists for this session. Forces the rigorous review BEFORE the PR opens.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S+\\s+)*gh pr create\\b'\n requires:\n ledger_tag: \"review-subagent:${BRANCH}\"\n hook: require-review-subagent-evidence-bash\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"review-subagent:${BRANCH} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: After running a review subagent against the staged diff for the working branch, persist its verdict + load-bearing findings as a ledger entry tagged with the branch name. Mirror of the review-subagent-before-pr-create producer for the gh-cli surface.\n ux:\n cannot: \"You cannot open a pull request for branch ${BRANCH} via `gh pr create` yet.\"\n required:\n - \"a completed review-subagent pass on branch ${BRANCH}\"\n run:\n - 'harness record review-subagent --task <task-id> --verdict <verdict>'\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 # at_head:true lets a preflight at the current HEAD satisfy the\n # gate at any age (the standard producer writes head:<sha> into\n # the tag content). The 10m window remains the freshness ceiling\n # for the head-mismatch case (operator switched branch, preflight\n # predates HEAD shift, runtime couldn't resolve a sha).\n at_head: true\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} ready:true confidence:<n> head:<sha> to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"preflight:${BRANCH} head:<full-sha> \u2014 <summary of what is on the branch + smoke results>\", source:\"manual\"}'\n description: Direct ledger write. Include head:<full-sha> if you want the entry to count under at_head; the branch is the WIP review surface and 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 preflight for ${BRANCH} at the current HEAD (any age) OR any preflight within the last 10 minutes. Re-run `harness preflight` if you committed since the last preflight AND it has been more than 10 minutes.\"\n - \"if solution-acceptance is enabled, a ready HEAD-pinned verdict at the SAME commit too (run `solution_evaluate`). `git push` trips both gates, so commit first if the tree is dirty, then satisfy both at one HEAD.\"\n - \"an approved Understanding Report, if the Understanding Gate is still active (it blocks `harness preflight` itself)\"\n run:\n - \"harness preflight\"\n\n # Phase 7 Risk Gate \u2014 the canonical built-in worked example. These two\n # policies, with the dangerous-shell classifier and production-signals\n # resolver below, are the Risk Gate's default stance: a destructive\n # shell action whose target environment resolves to production is\n # gated before the runtime fires it. Both fire ONLY when the\n # environment resolves to production (a main / release branch, a\n # prod-looking DATABASE_URL, or a prod kube context); on an ordinary\n # feature branch the environment is unknown and neither fires. Ordered\n # deny-first so a critical action (which also matches the high\n # threshold) gets the hard-deny envelope. See docs/risk-gate.md.\n - name: gate-prod-destructive\n description: Deny critical-severity destructive shell actions against a production target.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n when:\n risk.severity_at_least: critical\n environment.name: production\n requires:\n ledger_tag: \"risk-override:${SESSION_ID}\"\n hook: risk-gate\n enforcement: block\n # Operator-in-the-loop gate: the override tag is written by the\n # operator verb (ask semantics), not by the agent. See\n # writing-custom-policies.md, tripwire 4 (the trust model).\n producers:\n - kind: ask\n command: harness approve risk --force <reason>\n description: Deliberate operator override for a critical production mutation; run from the operator shell.\n - kind: mcp\n verb: mcp__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"risk-override:${SESSION_ID} \u2014 operator-authorized <reason>\", source:\"operator\"}'\n description: Recovery path if the approve verb is unavailable; only meaningful when the OPERATOR authorizes the content.\n ux:\n cannot: \"You cannot run this critical destructive action against production.\"\n required:\n - \"a deliberate operator override: a critical production mutation has no benign reading\"\n run:\n - \"Choose a non-destructive alternative, or ask the OPERATOR to run the command themselves, outside the agent.\"\n - \"Operator override (deliberate): the OPERATOR runs `harness approve risk --force <reason>` from their own shell (`! ` prefix in Claude Code, with --i-am-the-operator to acknowledge a non-TTY invocation).\"\n - name: gate-prod-destructive-approval\n description: Require operator approval for high-severity destructive shell actions against a production target.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n when:\n risk.severity_at_least: high\n environment.name: production\n requires:\n ledger_tag: \"risk-approved:${SESSION_ID}\"\n hook: risk-gate\n enforcement: require_approval\n producers:\n - kind: ask\n command: harness approve risk\n description: Operator approves this Risk Gate decision from their own shell.\n - kind: mcp\n verb: mcp__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"risk-approved:${SESSION_ID} \u2014 operator-authorized\", source:\"operator\"}'\n description: Recovery path if the approve verb is unavailable; only meaningful when the OPERATOR authorizes the content.\n ux:\n cannot: \"You cannot run this destructive production action yet.\"\n required:\n - \"operator approval of this Risk Gate decision\"\n run:\n - \"harness approve risk\"\n\n # gate-dev-unsafe-deletion (task d03af8f6): the two policies above fire\n # ONLY when the environment resolves to production \u2014 on an ordinary\n # task branch (environment: unknown) a deletion command runs\n # unconfirmed even when its target is a stray variable or a relative\n # path pointing somewhere unintended. This policy is deliberately\n # environment-INDEPENDENT (no environment.name clause) and gates on the\n # new `action.deletion_target_unresolvable` clause instead of\n # `risk.severity_at_least`/`risk.category_in` specifically because\n # those fail-close to matched=true for ANY unclassified action \u2014\n # unscoped, that would gate every unrelated unclassified Bash call in\n # every environment. `action.deletion_target_unresolvable` only fires\n # for a recognized deletion verb (`rm -r*`/`-f*`, `find ... -delete`,\n # `git clean -f*`) whose target(s) cannot be statically proven inside\n # `risk.safe_deletion_roots` (below). See docs/risk-gate.md.\n #\n # This policy consults its OWN ledger tag\n # (`risk-approved:deletion:${SESSION_ID}`), never the tag\n # `gate-prod-destructive-approval` above consults \u2014 a routine dev-\n # context deletion approval must not also clear the production\n # approval gate. `harness approve risk --scope deletion` writes this\n # tag; the bare `harness approve risk` (no `--scope`) keeps writing\n # only the production tag. See CHANGELOG.md task d03af8f6 for the\n # measured incident this fixed.\n - name: gate-dev-unsafe-deletion\n description: Require approval for a deletion-verb command whose target cannot be statically proven safe, in every environment.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n when:\n action.deletion_target_unresolvable: true\n requires:\n ledger_tag: \"risk-approved:deletion:${SESSION_ID}\"\n hook: risk-gate\n enforcement: require_approval\n producers:\n - kind: ask\n command: harness approve risk --scope deletion\n description: Operator approves this Risk Gate decision from their own shell.\n - kind: mcp\n verb: mcp__grounding-mcp__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"risk-approved:deletion:${SESSION_ID} \u2014 operator-authorized\", source:\"operator\"}'\n description: Recovery path if the approve verb is unavailable; only meaningful when the OPERATOR authorizes the content.\n ux:\n cannot: \"You cannot run this deletion command yet \u2014 its target cannot be statically proven safe.\"\n required:\n - \"operator approval of this Risk Gate decision, OR an absolute target inside a declared risk.safe_deletion_roots entry\"\n run:\n - \"harness approve risk --scope deletion\"\n - \"Or point the command at a path under risk.safe_deletion_roots (default: /tmp, /private/tmp) instead of an unresolved variable, relative path, or traversal.\"\n\n # deny-kill-switch-bypass / deny-session-env-strip / deny-pause-sentinel-forgery\n # (task cf1fde6d): `harness pause`/`harness resume` refuse to run inside an\n # agent shell in CODE, but that CLI check (refuseIfAgentShell / refuseIfNonTTY\n # in src/cli/pause/index.ts) is a speed bump, not a boundary: a Claude Code\n # `! `-prefixed shell inherits this session's env AND its non-TTY stdin, so\n # it is indistinguishable from an agent Bash call and trips the same checks\n # (verified live; docs/okf/pause-vs-gate-kill-switch.md). This PreToolUse\n # layer is the enforcement point that actually runs BEFORE the agent's Bash\n # executes.\n #\n # UPDATE (task 2cc73f55, closes the gap the HONESTY NOTE below used to\n # document): these three now declare `operator_only: true` instead of a\n # requires.ledger_tag. Every requires:-based policy in this engine is, by\n # construction, satisfiable by the SAME agent it gates: nothing stops it\n # from calling mcp__grounding-mcp__ledger_add with content matching the\n # tag for its own session before running the denied command, which used to\n # flip deny to allow (src/runtime/intercept.ts, the satisfied-requires\n # branch). `operator_only: true` closes that specific hole: the policy\n # declares NO requires: at all, and intercept() short-circuits to an\n # unconditional deny before the requires pipeline runs \u2014 no ledger query,\n # no template substitution, so no ledger write, marker file, or flag can\n # ever flip it to allow. See docs/writing-custom-policies.md tripwire 4 and\n # the \"Operator-only unconditional deny\" section for the schema contract,\n # and tests/cli/init-full-template-kill-switch-deny.test.ts for a test that\n # forges every ledger-evidence shape available to intercept() and asserts\n # the block holds.\n #\n # RESIDUAL GAP (still open, out of scope for task 2cc73f55): bash_match is\n # still a regex over the raw command string, matched BEFORE evaluation ever\n # reaches operator_only:, so it still cannot see through a heredoc,\n # `sh -c '...'`, `eval`, a base64-decoded payload, or a script file the\n # agent writes and then executes \u2014 an agent that defeats the TRIGGER match\n # never reaches this deny at all. That is a bash_match coverage gap, not a\n # requires-satisfaction gap, and is unrelated to the fix here (see PR #341\n # for the known uncoverable shapes). `harness validate` no longer warns\n # \"declares no producers\" on these three (checkPolicySelfAttestation now\n # recognises operator_only: true as correct-by-construction).\n #\n # UPDATE (task 432db3d3, 2026-07-28, follow-up to ea8becf5): PARTIALLY\n # closed \u2014 for THIS policy only. `src/runtime/command-normalize.ts` now\n # peels wrapper prefixes (`env`, `nice`, `command`, `sudo`, `doas`,\n # `time`, `timeout`, `stdbuf`, `setsid`, `exec`, `nohup`, leading\n # VAR=value) and\n # collapses whitespace runs for the closed head-token set\n # `git`/`gh`/`npm`/`harness`, so `env harness pause`, `nice harness\n # gate disable` and the like now reach this deny via the raw-OR-normalised\n # match \u2014 measured 2026-07-28 against the real trigger regex through the\n # real hook entry point, pinned in tests/runtime/intercept-cli.test.ts.\n # The OTHER TWO kill switches are NOT covered by that closed set:\n # deny-session-env-strip keys on `env`/`unset` head tokens, and `env`\n # is STRUCTURALLY unreachable for the normaliser (its peeling loop\n # consumes `env -u <VAR>` while hunting for a gated command behind it,\n # erasing the very text this trigger keys on); deny-pause-sentinel-forgery\n # keys on `tee`/`cp`. Both remain wrapper-bypassable (`nice env -u\n # CLAUDE_CODE_SESSION_ID ...`, `nice tee <sentinel>`), measured\n # 2026-07-28 and pinned as explicit not-covered tests plus a head-token\n # drift guard (tests/runtime/bash-match-head-token-drift.test.ts) that\n # goes red when a future bash_match policy gates a new head token.\n - name: deny-kill-switch-bypass\n description: Deny harness pause, harness resume, harness gate disable, and harness gate enable from the agent's Bash tool. These are operator-only kill switches.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S*\\s+)*(?:npx\\s+|\\S*/)?harness\\s+([\"\\x27]?)(?:pause|resume|gate\\s+([\"\\x27]?)(?:disable|enable)\\4)\\3(?![\\w-])'\n operator_only: true\n hook: deny-kill-switch-bash\n enforcement: block\n ux:\n cannot: \"You cannot run `harness pause`, `harness resume`, `harness gate disable`, or `harness gate enable`.\"\n required:\n - \"the OPERATOR to run this from a terminal genuinely outside your agent session, not a `! `-prefixed Claude Code shell, which inherits this session's env and stdin and does not count\"\n run:\n - \"Stop. Explain to the operator what you were trying to do and why, and let THEM decide whether to run that command from their own terminal. This is the only way to unblock this gate: it is an unconditional deny (operator_only: true) that no in-session evidence \u2014 a ledger write, a marker file, a flag \u2014 can ever satisfy; the only remaining gap is bash_match's regex coverage of exotic shell shapes, not this evaluation (see docs/okf/pause-vs-gate-kill-switch.md).\"\n\n - name: deny-session-env-strip\n description: Deny Bash attempts to strip or unset the agent-session env vars that harness pause/resume check ($CLAUDE_CODE_SESSION_ID, $CLAUDE_SESSION_ID, $CODEX_SESSION_ID). Closes the one-liner that otherwise defeats refuseIfAgentShell.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S*\\s+)*(env\\b[^;\\n|&]*-u\\s*(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)\\b|env\\b[^;\\n|&]*--unset(?:=|\\s+)(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)\\b|unset\\s+(\\S+\\s+)*(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)\\b|(CLAUDE_CODE_SESSION_ID|CLAUDE_SESSION_ID|CODEX_SESSION_ID)=(?=\\s))'\n operator_only: true\n hook: deny-session-env-strip-bash\n enforcement: block\n ux:\n cannot: \"You cannot strip or unset your own agent-session id env vars from Bash.\"\n required:\n - \"no legitimate reason from inside this session: stripping $CLAUDE_CODE_SESSION_ID / $CLAUDE_SESSION_ID / $CODEX_SESSION_ID is recognised as an attempt to defeat the harness pause/resume agent-shell check\"\n run:\n - \"Stop. If you believe you have a legitimate reason to run a command without one of these env vars set, ask the OPERATOR to do it from their own terminal instead. This gate is an unconditional deny (operator_only: true); see docs/okf/pause-vs-gate-kill-switch.md for the honest trust model and the residual bash_match coverage gap.\"\n\n - name: deny-pause-sentinel-forgery\n description: Deny Bash writes (redirect, tee, cp) to the harness pause sentinel file (.harness-paused). The sentinel is unsigned JSON; a forged file silences every gate exactly like a real operator pause. Reads (cat, stat) are unaffected.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S*\\s+)*(tee|cp)\\b[^;\\n|&]*\\.harness-paused\\b|>{1,2}\\s*\\S*\\.harness-paused\\b'\n operator_only: true\n hook: deny-sentinel-write-bash\n enforcement: block\n ux:\n cannot: \"You cannot write to the harness pause sentinel file (.harness-paused).\"\n required:\n - \"the OPERATOR to run `harness pause`/`harness resume` themselves, from a terminal outside your agent session, never a direct write to the sentinel file\"\n run:\n - \"Stop. Do not write, redirect, tee, or copy anything to .harness-paused. Ask the OPERATOR to silence the gates themselves, from their own terminal, using the operator-only command named under Required, if the session genuinely needs it. This gate is an unconditional deny (operator_only: true); see docs/okf/pause-vs-gate-kill-switch.md for the honest trust model and the residual bash_match coverage gap.\"\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 #\n # KEEP IN SYNC (task 68b9ad9c): this text must match\n # defaultProducers() in\n # src/policy-packs/builtin/understanding-before-execution.ts \u2014\n # that function is what `harness pack reseed` and `harness doctor`'s\n # divergence warning treat as \"the shipped template\". A wording fix\n # landed here without updating defaultProducers() would make reseed\n # silently pull operators BACK to the stale wording. Pinned by\n # tests/cli/init-templates-ux-parity.test.ts.\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 #\n # KEEP IN SYNC (task 68b9ad9c): see the producers: comment above \u2014\n # same rationale applies here, against defaultUx(\"grill_me\") in the\n # same builtin module.\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, Prior Art (state what you searched for an existing solution and what you found, with an explicit adopt-or-build judgment)\"\n - \"Run `harness approve understanding` with the report attached as a quoted heredoc (harness approve understanding <<'UNDERSTANDING_REPORT' ...report... UNDERSTANDING_REPORT) so it is persisted for audit, then approve the prompt; the heredoc is the only extra shell shape the gate allows (no pipes, chaining, or other redirection)\"\n # approval_lifecycle (agent-tasks/d8ee60ca + harness/f54e0ecb,\n # v0.18.0+): expire the approval marker on task-completion\n # boundaries so a multi-task session re-prompts for an\n # Understanding Report between tasks. Without this the legacy\n # \"one approval per session\" contract lets a stale interpretation\n # drive the next task's edits.\n #\n # Full ships both boundary kinds: the agent-tasks MCP verbs for\n # operators on that workflow, plus a Bash regex list for hybrid\n # operators who also use gh-cli for PR mechanics. `max_age` is\n # the safety net. Operators who prefer the legacy per-session\n # behaviour opt out with `approval_lifecycle: { mode: session }`.\n # Operators on other task systems override the matchers.\n approval_lifecycle:\n expire_on_tool_match:\n - mcp__agent-tasks__task_finish\n - mcp__agent-tasks__task_abandon\n - mcp__agent-tasks__pull_requests_merge\n - mcp__agent-tasks__tasks_transition\n expire_on_bash_match:\n - '^gh pr (merge|close)\\b'\n - '^git push origin (master|main)\\b'\n max_age: 4h\n\n # branch-protection (agent-tasks/2fdc5bbe, default-enabled since v0.17.2):\n # blocks Write/Edit (claude-code) or apply_patch (codex) on protected\n # branches (default: master, main, develop). Complements\n # preflight-before-push, which fires at the LAST reversible step;\n # branch-protection fires at the FIRST source mutation, catching the\n # \"forgot to branch off master\" pattern earlier in the cycle.\n #\n # Two satisfying signals: a fresh `branch:non-protected:<branch>` tag\n # from the SessionStart producer (`harness session-start branch-check`),\n # or the operator-only override marker written by\n # `harness approve branch-protection --session <id>` for deliberate\n # protected-branch edits (version bumps, CI workflow patches, hotfixes).\n # A branch-protection-ack ledger tag is no longer a sufficient override\n # on its own (it is agent-writable); the marker file is the trusted signal.\n #\n # Fails closed (any load / parse / ledger error refuses). Disable by\n # setting `enabled: false` or removing this entry if your workflow\n # routinely edits master directly. Override the protected list via\n # `config.protected_branches`. Full reference:\n # docs/policy-packs/branch-protection.md.\n - name: branch-protection\n source: builtin\n enabled: true\n description: Block Write/Edit on protected branches (master, main, develop) at the first source mutation.\n config:\n # ux (agent-tasks/9806d4f8): replaces the legacy\n # \"branch-protection: refusing ...\" envelope with the\n # plain-language { cannot, required, run } shape. Engine details\n # (the BLOCK reason naming session id / freshness window) stay\n # on stderr for operator audit.\n #\n # KEEP IN SYNC (task 68b9ad9c): this text must match defaultUx() in\n # src/policy-packs/builtin/branch-protection.ts \u2014 see the identical\n # note on the understanding-before-execution pack above for why.\n # Pinned by tests/cli/init-templates-ux-parity.test.ts.\n ux:\n cannot: \"You cannot edit files on protected branch ${BRANCH} yet.\"\n required:\n - \"a checkout of a non-protected branch (current `${BRANCH}` is protected)\"\n run:\n - \"git checkout -b feat/<your-task>\"\n - \"harness session-start branch-check\"\n\n # solution-acceptance (harness cc43c7a4): Verifier-gated Done. Gates the\n # task-finishing tools (agent-tasks completion verbs + git push / gh pr\n # merge) on a ready, HEAD-pinned verdict that the grounding-mcp producer\n # (solution_evaluate) derives from a real preflight run, plus an\n # anti-forgery write-guard on the verdict marker. DISABLED by default: it\n # is a hard completion-gate and needs grounding-mcp (>= 0.3.2) under\n # tools.mcp plus the preflight binary on PATH. Flip enabled: true once the\n # producer is wired and you want completion earned, not claimed. The pack\n # emits its own instructions.md on apply; harness validate warns if you\n # enable it without the producer.\n - name: solution-acceptance\n source: builtin\n enabled: false\n description: Gate task completion on a ready, HEAD-pinned solution-acceptance verdict earned from a real preflight run.\n\n # post-merge-gate (harness T-001, post-merge-gate): catches \"kept working\n # on a branch after it was already merged\" \u2014 a PostToolUse producer\n # records a merged-tip fact on a successful `gh pr merge`; a PreToolUse\n # blocker denies curated history-mutating commands (git commit/add/push/\n # merge/rebase/cherry-pick/revert/reset/stash pop|apply, gh pr create/\n # merge) while the current branch tip still exactly matches that\n # recorded tip. The recovery vocabulary (git switch/checkout/pull/fetch,\n # git branch -d/-D, git stash list/show, any `harness ...` command)\n # always passes as its OWN command, classified before any manifest or\n # ledger access \u2014 the recovery path this gate itself recommends can never\n # be starved. Chaining one of those with a curated mutation does NOT\n # exempt the mutation (task 19356be7).\n # Fails OPEN (unlike branch-protection) when the ledger is unreachable.\n # DISABLED by default: a fresh gate, opt in once you've reviewed the\n # curated command list for your workflow. Full reference:\n # docs/policy-packs/post-merge-gate.md.\n - name: post-merge-gate\n source: builtin\n enabled: false\n description: Deny curated history-mutating git/gh commands on a branch whose current tip was already merged.\n config:\n # ux: replaces the legacy \"post-merge-gate: refusing ...\" envelope\n # with the plain-language { cannot, required, run } shape.\n #\n # KEEP IN SYNC: this text must match defaultUx() in\n # src/policy-packs/builtin/post-merge-gate.ts \u2014 see the identical\n # note on the branch-protection pack above for why. Pinned by\n # tests/cli/init-templates-ux-parity.test.ts.\n ux:\n cannot: \"You cannot run ${TOOL_NAME} on branch ${BRANCH} yet \u2014 its current tip was already merged.\"\n required:\n - \"a branch tip that is not sitting at an already-merged commit (switch off `${BRANCH}`, or move its tip with a new commit)\"\n run:\n - \"git switch ${DEFAULT_BRANCH}\"\n - \"git pull --ff-only\"\n - \"git branch -d ${BRANCH} # optional cleanup\"\n\n# Phase 7 Risk Gate vocabulary. The dangerous-shell classifier and\n# production-signals resolver feed the gate-prod-destructive policies\n# above: `harness policy intercept` builds the Action Envelope,\n# classifies the action against `risk.classifiers[]`, resolves the\n# target environment against `environments.resolvers[]`, and evaluates\n# each policy's `when:` clauses against the result. Full design and the\n# decision model: docs/risk-gate.md.\nrisk:\n # Fail posture when a policy's evidence cannot be READ (ledger timeout,\n # spawn failure, unresolved template): with `preserve_enforcement`\n # (the default) block/require_approval policies fail CLOSED\n # (`deny-degraded`) while warn policies stay non-blocking. Set\n # `fail_open` to restore the pre-0.45 availability-first behaviour\n # where EVERY degraded evaluation was a non-blocking `warn-degraded`.\n # Kept COMMENTED OUT on purpose: the schema is strict, so a manifest\n # carrying this key fails to parse on a pre-0.45 binary, and a manifest\n # load failure is ALLOW at the hook layer \u2014 on a mixed-version fleet an\n # emitted default would turn a downgrade into a silent full fail-open\n # (review 2026-08-08). See docs/okf/gate-fail-posture-matrix.md.\n # degraded_fail_posture: preserve_enforcement\n # Safe-deletion-root allowlist for gate-dev-unsafe-deletion's\n # `action.deletion_target_unresolvable` clause (task d03af8f6): an\n # absolute deletion target inside one of these roots is allowed; a\n # relative path, an unexpanded $VAR/~, or a traversal that normalizes\n # outside every root is gated. Shown explicitly even though it matches\n # the schema default (`/tmp`, `/private/tmp` \u2014 the two spellings this\n # harness's own scratchpad convention can use, macOS symlinks /tmp to\n # /private/tmp) so an operator sees the live config surface here rather\n # than having to know the schema default exists. An override REPLACES\n # this list, it does not merge with it. See docs/risk-gate.md.\n safe_deletion_roots:\n - /tmp\n - /private/tmp\n classifiers:\n - name: dangerous-shell\n tool: Bash\n patterns:\n - pattern: 'rm\\s+-rf\\s+(/|/var|/data|/mnt|~)'\n categories: [destructive, data_loss]\n severity: critical\n - pattern: 'DROP\\s+TABLE|TRUNCATE\\s+TABLE|DELETE\\s+FROM'\n categories: [destructive, data_loss]\n severity: high\n # Token-based, flag-tolerant: a flag between `kubectl` and\n # `delete` (e.g. `kubectl --context=x delete namespace payments`)\n # must not defeat the match, without matching `kubectl\n # get`/`describe` and without exponential-backtracking on a long\n # flag run. `(?:\\s+-\\S+(?:\\s+(?!delete\\b)(?!-)\\S+)?)*` consumes\n # zero or more `-`/`--` flag tokens (each optionally taking one\n # following, non-flag, non-\"delete\" value token), linear in\n # command length. See docs/risk-gate.md for the full rationale\n # and the earlier quadratic-alternation form this replaced.\n - pattern: 'kubectl(?:\\s+-\\S+(?:\\s+(?!delete\\b)(?!-)\\S+)?)*\\s+delete\\s+(namespace|deployment|statefulset|pvc)'\n categories: [destructive, infrastructure_change]\n severity: high\n # Same flag-tolerance treatment for terraform's own `-chdir=DIR`\n # global flag, which sits between the tool name and the\n # subcommand (`terraform -chdir=infra destroy`).\n - pattern: 'terraform(?:\\s+-\\S+(?:\\s+(?!destroy\\b)(?!-)\\S+)?)*\\s+destroy'\n categories: [destructive, infrastructure_change]\n severity: critical\n\nenvironments:\n resolvers:\n - name: production-signals\n environment: production\n signals:\n branch_patterns: [main, \"release/*\"]\n env_var_patterns:\n - var: DATABASE_URL\n patterns: [prod, production]\n kube_context_patterns: [\".*prod.*\"]\n kube_namespace_patterns: [prod, production]\n";
2
+ export declare const FULL_TEMPLATE: string;
3
3
  export type TemplateName = "minimal" | "full" | "solo" | "team";
4
4
  export declare function getTemplate(name: TemplateName): string;
5
5
  export declare function shippedOperatorOnlyPolicyNames(): readonly string[];
@@ -1,3 +1,13 @@
1
+ // D-004 (task 8f637efd, docs/decisions/2026-08-27-ug-auto-mode-approval.md,
2
+ // "Amendment: install default"): the shipped `auto_approve` block, read
3
+ // from the one canonical renderer so `harness init`, `harness pack
4
+ // upgrade understanding-before-execution`, and `harness doctor`'s
5
+ // missing-auto_approve finding cannot drift on the snippet's shape or
6
+ // wording. See auto-approve-default.ts for the rationale.
7
+ import { renderAutoApproveSnippet } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
8
+ // Every template below nests `auto_approve:` at 6 spaces, a sibling of
9
+ // `mode:` / `approval_lifecycle:` under the pack's `config:` key.
10
+ const AUTO_APPROVE_SNIPPET = renderAutoApproveSnippet(6);
1
11
  export const MINIMAL_TEMPLATE = `# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)
2
12
  #
3
13
  # Bootstrapped by \`harness init --template minimal\`.
@@ -203,8 +213,8 @@ hooks:
203
213
  # f2d2a29): every \`harness policy intercept\` hook below down through
204
214
  # \`risk-gate\` carries \`budget_ms: 15000\`, i.e. a Claude Code outer
205
215
  # kill-timeout of \`ceil(15000/1000) = 15\` seconds (generate-settings.ts's
206
- # \`hookTimeoutSeconds\`). This is deliberately UNIFORM across all eleven of
207
- # them, for two independent reasons:
216
+ # \`hookTimeoutSeconds\`). This is deliberately UNIFORM across all thirteen
217
+ # of them, for two independent reasons:
208
218
  #
209
219
  # 1. FAIL-CLOSED MARGIN. \`harness policy intercept\` evaluates its FULL
210
220
  # \`policies:\` list against the incoming event (src/runtime/intercept.ts,
@@ -247,7 +257,7 @@ hooks:
247
257
  # and reintroduce exactly the redundant-invocation cost the dedup
248
258
  # exists to avoid — on top of leaving whichever entry keeps a low
249
259
  # timeout still exposed to the fail-open risk above. Keeping all
250
- # eleven at the identical 15000ms budget_ms preserves the existing
260
+ # thirteen at the identical 15000ms budget_ms preserves the existing
251
261
  # one-invocation-per-matcher-group collapse (previously they all
252
262
  # collapsed onto the shared 2s floor; now they collapse onto 15s).
253
263
  - name: require-review-evidence
@@ -271,6 +281,31 @@ hooks:
271
281
  blocking: hard
272
282
  budget_ms: 15000
273
283
 
284
+ # The two OTHER agent-tasks verbs that land a PR (task 2699b476).
285
+ # \`pull_requests_merge\` is not the only merge surface the MCP server
286
+ # exposes: \`task_merge\` merges the PR attached to a task, and
287
+ # \`task_finish\` with \`autoMerge: true\` merges as part of finishing
288
+ # (both of its auto-merge modes, the soloMode work claim and the review
289
+ # claim + approve). Each needs its OWN hook entry because a hook's
290
+ # \`match\` is what \`harness apply\` projects into settings.json's
291
+ # tool-name matcher, and a matcher for \`pull_requests_merge\` never
292
+ # spawns \`harness policy intercept\` for either of these. Same
293
+ # 15000ms budget_ms as every other blocking intercept hook, for the
294
+ # same two reasons the note above gives.
295
+ - name: require-review-evidence-task-merge
296
+ event: PreToolUse
297
+ match: "mcp__agent-tasks__task_merge"
298
+ command: harness policy intercept
299
+ blocking: hard
300
+ budget_ms: 15000
301
+
302
+ - name: require-review-evidence-task-finish
303
+ event: PreToolUse
304
+ match: "mcp__agent-tasks__task_finish"
305
+ command: harness policy intercept
306
+ blocking: hard
307
+ budget_ms: 15000
308
+
274
309
  - name: require-dogfood-evidence
275
310
  event: PreToolUse
276
311
  match: "Bash"
@@ -462,6 +497,74 @@ policies:
462
497
  run:
463
498
  - 'harness record review --pr <pr> "<summary>"'
464
499
 
500
+ # The two agent-tasks verbs that ALSO land a PR (task 2699b476, closing
501
+ # the residual 99f47307 Slice 1 named twice in the CHANGELOG). Same
502
+ # shape as review-before-merge, three scope notes:
503
+ # 1. Tag shape: \`review:\${TASK_ID}\` rather than \`review:\${PR_NUMBER}\`.
504
+ # Both verbs are task-scoped: they derive owner/repo/PR number from
505
+ # the task, so \`taskId\` is the only identifier in the tool payload.
506
+ # \`harness record review --pr <pr> --task <id> "<summary>"\` writes
507
+ # the PR, branch, base AND task tags in ONE ledger fact, so a single
508
+ # recorded review satisfies all four merge gates at once.
509
+ # 2. task_finish is gated ONLY in its auto-merge mode. The verb is
510
+ # polymorphic: a plain \`task_finish\` advances the task and merges
511
+ # nothing, while \`autoMerge: true\` merges the PR (both modes do,
512
+ # the soloMode work claim and the review claim + approve). Gating
513
+ # the whole verb would block the ordinary finish call for no reason;
514
+ # \`input_match\` narrows the trigger to the merging mode alone.
515
+ # A missing \`autoMerge\` never matches, so an omitted argument
516
+ # leaves the gate out of the way rather than arming it.
517
+ # 3. These sit ALONGSIDE review-before-merge / -bash, same rationale
518
+ # as the bash variant above: an operator may use any of the four
519
+ # merge surfaces, and each carries its own tag shape.
520
+ - name: review-before-task-merge
521
+ description: Block agent-tasks task_merge unless a ledger entry tagged review:<task-id> exists for this session.
522
+ trigger:
523
+ event: PreToolUse
524
+ match: "mcp__agent-tasks__task_merge"
525
+ extract:
526
+ TASK_ID: "toolArgs.taskId"
527
+ requires:
528
+ ledger_tag: "review:\${TASK_ID}"
529
+ hook: require-review-evidence-task-merge
530
+ enforcement: block
531
+ producers:
532
+ - kind: mcp
533
+ verb: mcp__grounding-mcp__ledger_add
534
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review:\${TASK_ID}: <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
535
+ description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the task id. Mirror of the review-before-merge producer for the task-scoped merge surface.
536
+ ux:
537
+ cannot: "You cannot merge the PR for task \${TASK_ID} yet."
538
+ required:
539
+ - "a recorded review of task \${TASK_ID}"
540
+ run:
541
+ - 'harness record review --pr <pr> --task \${TASK_ID} "<summary>"'
542
+
543
+ - name: review-before-task-finish-automerge
544
+ description: 'Block agent-tasks task_finish with autoMerge: true unless a ledger entry tagged review:<task-id> exists for this session.'
545
+ trigger:
546
+ event: PreToolUse
547
+ match: "mcp__agent-tasks__task_finish"
548
+ input_match:
549
+ toolArgs.autoMerge: true
550
+ extract:
551
+ TASK_ID: "toolArgs.taskId"
552
+ requires:
553
+ ledger_tag: "review:\${TASK_ID}"
554
+ hook: require-review-evidence-task-finish
555
+ enforcement: block
556
+ producers:
557
+ - kind: mcp
558
+ verb: mcp__grounding-mcp__ledger_add
559
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review:\${TASK_ID}: <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
560
+ description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the task id. Same evidence the task_merge gate reads, so one recorded review opens both.
561
+ ux:
562
+ cannot: "You cannot finish task \${TASK_ID} with autoMerge yet."
563
+ required:
564
+ - "a recorded review of task \${TASK_ID}"
565
+ run:
566
+ - 'harness record review --pr <pr> --task \${TASK_ID} "<summary>"'
567
+
465
568
  - name: dogfood-before-release
466
569
  description: Block npm publish / git tag v* without a recent dogfood ledger entry.
467
570
  trigger:
@@ -912,6 +1015,7 @@ policy_packs:
912
1015
  - '^gh pr (merge|close)\\b'
913
1016
  - '^git push origin (master|main)\\b'
914
1017
  max_age: 4h
1018
+ ${AUTO_APPROVE_SNIPPET}
915
1019
 
916
1020
  # branch-protection (agent-tasks/2fdc5bbe, default-enabled since v0.17.2):
917
1021
  # blocks Write/Edit (claude-code) or apply_patch (codex) on protected
@@ -1069,6 +1173,73 @@ risk:
1069
1173
  - pattern: 'terraform(?:\\s+-\\S+(?:\\s+(?!destroy\\b)(?!-)\\S+)?)*\\s+destroy'
1070
1174
  categories: [destructive, infrastructure_change]
1071
1175
  severity: critical
1176
+ # Task 2929c5b7: unclassified commands no longer trivially
1177
+ # satisfy risk.severity_at_least: critical (see when-eval.ts and
1178
+ # docs/risk-gate.md's "Unclassified actions and the fail-close
1179
+ # rule") — kept in lockstep with docs/examples/full-manifest.yaml
1180
+ # by tests/cli/init-full-template-parity.test.ts.
1181
+ #
1182
+ # These patterns are the OPERATOR-EDITABLE MIRROR of the built-in
1183
+ # destructive floor (src/runtime/destructive-shell-floor.ts),
1184
+ # not the only line of defence: the floor ships in the binary and
1185
+ # already classifies these heads for an EXISTING manifest that
1186
+ # never adopts the patterns below. Edit, narrow, or raise these
1187
+ # freely: an operator pattern composes with the floor under
1188
+ # highest-severity-wins, so it can only add. The floor is
1189
+ # argv-aware where a regex cannot be (path-qualified and wrapped
1190
+ # spellings: /bin/dd, sudo dd, sh -c "dd ...", git -C <dir> push
1191
+ # -f), so a few spellings are caught by the floor alone; the
1192
+ # parity test in tests/runtime/destructive-shell-floor.test.ts
1193
+ # pins that everything caught HERE is also caught THERE, at the
1194
+ # same severity or higher.
1195
+ - pattern: '\\bdd\\s[^\\n]*\\bof='
1196
+ categories: [destructive, data_loss]
1197
+ severity: critical
1198
+ - pattern: '\\btruncate\\b[^\\n]*(\\s-[a-zA-Z]*s|--size)'
1199
+ categories: [destructive, data_loss]
1200
+ severity: critical
1201
+ - pattern: '\\bshred\\b'
1202
+ categories: [destructive, data_loss, irreversible_action]
1203
+ severity: critical
1204
+ - pattern: '\\bmkfs(\\.\\w+)?\\b'
1205
+ categories: [destructive, data_loss, infrastructure_change]
1206
+ severity: critical
1207
+ - pattern: '\\bfind\\b[^\\n]*-delete\\b'
1208
+ categories: [destructive, data_loss]
1209
+ severity: critical
1210
+ - pattern: '\\bfind\\b[^\\n]*-exec(dir)?\\s+rm\\b'
1211
+ categories: [destructive, data_loss]
1212
+ severity: critical
1213
+ - pattern: '\\bgit\\s+reset\\b[^\\n]*--hard\\b'
1214
+ categories: [destructive, data_loss]
1215
+ severity: high
1216
+ - pattern: '\\bgit\\s+push\\b[^\\n]*(--force(-with-lease)?\\b|\\s-f\\b)'
1217
+ categories: [destructive, production_mutation, deployment_change]
1218
+ severity: high
1219
+ - pattern: '\\bgit\\s+clean\\b[^\\n]*(--force\\b|\\s-[a-zA-Z]*f[a-zA-Z]*\\b)'
1220
+ categories: [destructive, data_loss]
1221
+ severity: high
1222
+ - pattern: '\\bgit\\s+checkout\\s+--\\s+\\.'
1223
+ categories: [destructive, data_loss]
1224
+ severity: high
1225
+ - pattern: '\\bgit\\s+restore\\s+\\.(\\s|$)'
1226
+ categories: [destructive, data_loss]
1227
+ severity: high
1228
+ - pattern: '\\b(chmod|chown)\\b[^\\n]*(\\s-[a-zA-Z]*R|--recursive\\b)'
1229
+ categories: [mass_update]
1230
+ severity: high
1231
+ - pattern: '\\bcurl\\b[^\\n]*(-X\\s*|--request[\\s=])(?![Gg][Ee][Tt]\\b)(?![Hh][Ee][Aa][Dd]\\b)[A-Za-z]'
1232
+ categories: [production_mutation, network_exfiltration]
1233
+ severity: high
1234
+ - pattern: '\\bcurl\\b[^\\n]*(\\s-[a-zA-Z]*[dFT]|--data\\b|--json\\b|--form(-string)?\\b|--upload-file\\b)'
1235
+ categories: [production_mutation, network_exfiltration]
1236
+ severity: high
1237
+ - pattern: '\\bcurl\\b[^\\n]*(\\s-[a-zA-Z]*[oODcK]|--output(-dir)?\\b|--remote-name\\b|--remote-header-name\\b|--dump-header\\b|--cookie-jar\\b|--config\\b|--create-dirs\\b|--etag-save\\b|--trace(-ascii)?\\b|--stderr\\b|(\\s-[a-zA-Z]*w\\b|--write-out\\b)[^\\n]*%output)'
1238
+ categories: [destructive, data_loss]
1239
+ severity: high
1240
+ - pattern: '\\bsed\\b[^\\n]*(\\s-[a-zA-Z]*i[a-zA-Z]*\\b|--in-place\\b)'
1241
+ categories: [destructive, data_loss]
1242
+ severity: high
1072
1243
 
1073
1244
  environments:
1074
1245
  resolvers:
@@ -1 +1 @@
1
- {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,wEAAwE;AACxE,mEAAmE;AACnE,kEAAkE;AAClE,sEAAsE;AACtE,0DAA0D;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,sEAAsE,CAAC;AAEhH,uEAAuE;AACvE,kEAAkE;AAClE,MAAM,oBAAoB,GAAG,wBAAwB,CAAC,CAAC,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAy9B3B,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6OrB,CAAC;AAEF,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,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;AAED;;;;;;;;;;;;;;;GAeG;AACH,IAAI,wBAAuD,CAAC;AAC5D,MAAM,UAAU,8BAA8B;IAC5C,IAAI,wBAAwB,KAAK,SAAS,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzD,wBAAwB,GAAG,QAAQ,CAAC,QAAQ;aACzC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC;aACvC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,wBAAwB,CAAC;AAClC,CAAC;AAsBD,yEAAyE;AACzE,yEAAyE;AACzE,uEAAuE;AACvE,uEAAuE;AACvE,gCAAgC;AAChC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,OAAO,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,IAAI,+BAA8E,CAAC;AACnF,MAAM,UAAU,0BAA0B;IACxC,IAAI,+BAA+B,KAAK,SAAS,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzD,MAAM,OAAO,GAA6B,EAAE,CAAC;QAC7C,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,UAAU;gBAAE,SAAS;YAC/B,MAAM,QAAQ,GAAG,wBAAwB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC3D,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzF,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YAC5C,IAAI,CAAC,SAAS;gBAAE,SAAS;YACzB,MAAM,QAAQ,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAC;YACrD,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,+BAA+B,GAAG,OAAO,CAAC;IAC5C,CAAC;IACD,OAAO,+BAA+B,CAAC;AACzC,CAAC"}