---
id: claude-md-harness-rules
app: wkappbot-workflow
description: "How to configure per-repo harness rules in CLAUDE.md: harness:safe/block/warn/done/skill syntax, tInput format, body lines, template injection. Includes the harness:skill study-lock (principle/implementation/usage): isEditOp recognition, file-path-only patterns, 4-condition block matrix, executed-read detection."
tags: [harness, claude-md, rules, safe, block, warn, done, skill, study-lock, iseditop, per-repo, config]
---

> **Refresh**: `wkappbot skill read claude-md-harness-rules --if-newer` — v1.16 (2026-05-22)

# claude-md-harness-rules

## Steps

1. WHAT: Per-repo CLAUDE.md declarative rules that the harness (wkharness-guards.ps1) enforces on every tool call. Parsed at session start from the project CLAUDE.md.
2. SYNTAX: One rule = one ## header line + optional body lines. Header: '## harness:<action> <regex>'. Body: plain text shown in message; lines starting with ! run as PowerShell and output is appended.
3. ACTIONS: safe = whitelist (skip all block/warn if tInput matches). block = Block() the tool call with message. warn = write to stderr (no block). done = PostToolUse only (completion detector).
4. tInput FORMAT: '● ToolName(cmd)' -- e.g. '● Bash(rm -rf /tmp/foo)' or '● Edit(CLAUDE.md)'. Regex matches against this string. Anchor with '● Bash\(' to avoid false positives on tool args.
5. EXAMPLES: ## harness:safe CLAUDE\.md  (allow Edit on CLAUDE.md always) 
6.  ## harness:block (?i)● Bash\(.*rm -rf  (block dangerous rm) 
7.  ## harness:warn (?i)● Bash\(.*sed -i  (warn on sed) 
8.  ## harness:done (?i)completed
9. done  (PostToolUse completion check)
10. BODY LINES: Plain text = shown as help message. Line starting with ! = PowerShell command, output inserted. E.g.: !wkappbot skill read wk-ai-tools  (shows skill content in block message)
11. TEMPLATE: If CLAUDE.md has no harness: sections, ~/.claude/harness-template.md is auto-appended. Template has 4 default rules (safe CLAUDE.md, warn wkedit--line, block bash-pwsh, done skipped).
12. SCOPE: Only the CWD project CLAUDE.md is loaded (not global ~/.claude/CLAUDE.md). Each repo controls its own rules. Global rules live in wkharness-guards.ps1 hardcoded guards.
13. BEST PRACTICES: Use safe before block to whitelist exceptions. Anchor block patterns to '● Bash\(' or '● Edit\(' to avoid matching tool arguments. Keep regex simple -- no lookahead. Test with wkharness -Test.
14. ACTION 'skill' (study-lock) -- PRINCIPLE: force the editor to actually STUDY the relevant skills BEFORE editing a matching source file, so nobody blindly edits sensitive code (placement, screensaver, caller-hwnd, etc.). IMPLEMENTATION: (i) the guard recognizes OFFICIAL EDIT commands via isEditOp = Edit/Write tool OR Bash/PS starting with wkedit/wkcodex/wkclaude or containing 'wkappbot file edit'; Read/Grep are NOT edit ops so they never block. (ii) the harness:skill PATTERN is FILE-PATH-ONLY (e.g. csharp.*MyCdpContext) -- NO Edit-open-paren prefix; the guard CODE decides the tool, the MD entry only says which files. (iii) 4-condition BLOCK matrix: A edit-op recognized; B edit-op on a source file whose path matches NO harness:skill pattern -> catch-all block (guides you to add a section); C pattern matched but the entry declares fewer than 3 distinct skill ids -> bad-config block; D pattern matched but not ALL declared skills were READ this session -> block listing the unread ones. (iv) read = an actual EXECUTED 'wkappbot skill read id' command found in the session JSONL (lines carrying a Bash/PS command field, whole-session, no recency limit) -- plain-text mentions (the rule text itself, skill Require lines, prose) do NOT count; agent/wkcodex reads DO count. Block message goes to stderr.
15. ACTION 'skill' USAGE -- declare in CLAUDE.md: a header '## harness:skill <FILE-PATH-REGEX>' followed by exactly 3+ body lines of the form 'wkappbot skill read <id>' (convention: usage / impl / task). Example: '## harness:skill csharp.*MyCdpContext' then 'wkappbot skill read standard-appbot-window' / 'wkappbot skill read standard-chrome-window' / 'wkappbot skill read wkfind-caller-hwnd-validation-3tier-pattern'. Rules: the 3 skill ids MUST exist in the catalog and be distinct (else condition C bad-config block); the pattern is matched against the normalized edited-file path, so write just the path fragment (no Edit-open-paren). To pass before editing a matching file, EXECUTE those 3 'wkappbot skill read' commands this session (text-only mention does not satisfy it). If you edit an unregistered source file, the catch-all (condition B) blocks and prints the exact '## harness:skill ext.*Filename' section to add. NOTE: this is separate from the skill-update LOCK (code_changed...lock) which is cleared by any 'wkappbot skill edit/contribute' and gates the NEXT delegation wrapper/Agent.
16. CORRECTION to the isEditOp scope (overrides the earlier list): detect ONLY explicit DIRECT edits -- the Edit and Write tools, plus appbot-edit Bash commands (wkappbot file edit / wkedit). Do NOT include wkcodex / wkclaude in isEditOp: those are delegation wrappers, not direct edits, and they are already gated by the brief-guard (3 skill-read refs in the task). Including them caused false skill-query triggers when a wkcodex task string merely MENTIONS a registered file path. So: isEditOp = (tool is Edit or Write) OR (Bash/PS command is wkappbot-file-edit or wkedit). Reads/greps/other Bash never trigger the skill query.
17. GLOBAL CLAUDE.md INTEGRATION (requirement): in addition to the CWD project CLAUDE.md (and the edited-file repo-root CLAUDE.md cross-repo pass), the harness should ALSO load and apply the GLOBAL user CLAUDE.md at HOME/.claude/CLAUDE.md so user-level harness rules apply in every repo. IMPLEMENTATION in wkharness.ps1 right after the main 'Invoke-HarnessRules cfg toolName harnessInput pre' call: add globalMd = Join-Path USERPROFILE '.claude/CLAUDE.md'; if Test-Path globalMd then Invoke-HarnessRules (Get-HarnessRules globalMd) toolName harnessInput 'pre' with skipEditCatchAll=true (true so the global pass does NOT re-fire the source-edit catch-all -- only the CWD pass owns the catch-all). Order: CWD project rules first, then edited-file repo rules, then global rules; safe/block/warn/skill all merge. This complements step 12 which previously said only CWD is loaded.
18. GLOBAL CLAUDE.md integration -- self-test safety: gate the global-load with (-not env:WKHARNESS_SELFTEST) so the 72 deterministic self-test cases (which assume only the controlled CWD fixture rules) are NOT affected by the real user-level rules; real sessions still get the global pass. The global Invoke-HarnessRules call mirrors the existing edited-file cross-repo pass (skipEditCatchAll true).
19. IMPLEMENTED SOLUTION (2026-05-27, FINAL -- supersedes the isEditOp guard-code idea above): the edit-tool / appbot-edit discrimination is done ENTIRELY in the harness:skill PATTERN, NOT in guard code. No isEditOp is needed. This is clobber-IMMUNE because it lives in the project CLAUDE.md (which is pace-exempt to edit), not in the concurrently-edited wkharness-guards.ps1. Canonical pattern prefix: (?:Edit\(|Write\(|Bash\((?:[\w./\-]+\s+)*?[\w./\-]*edit(?:\.\w+)?\b) then .*<ext>.*<FileFragment>. Coverage proven live: Edit tool (Edit\(), Write tool (Write\(), and any Bash command whose command-position token ends in 'edit' -- wkedit, wkedit.sh/.ps1/.cmd/.exe, 'wkappbot file edit', even with leading flags like --sudo or --timeout 30s (each flag/value is absorbed by the leading-token group). Reads/Greps never match (no Edit(/Write(/Bash(...edit token). wkcodex/wkclaude tasks are excluded because their 'edit' mention sits inside a quoted arg and the quote stops the unquoted-token traversal. To register a file: ## harness:skill <thatPrefix>.*csharp.*MyCdpContext + 3 wkappbot skill read lines.
20. Edit/Write TOOL bypasses harness:skill: confirmed via controlled test -- Bash appbot-edit on .ps1 BLOCKS, but Edit TOOL on same .ps1 PASSES. Cause: an early exit-0 in wkharness-guards.ps1 short-circuits Edit/Write before the harness eval (~line 1430). Fix must ensure harness:skill/catch-all runs for Edit/Write tool too.
21. Lock clears on a clean update, but a command whose text carries a wrapper-name literal gets mis-tagged by the post-hook and re-arms the lock. Keep update text free of such literals to clear the lock for delegation.
22. Lock-clear hygiene: a clearing update must carry no wrapper-tool name token anywhere in its full command line (skill id and message body included) or the post-hook mis-tags it and re-arms. Run the delegated dispatch as the very next call with no shell or version-control command in between.
23. UPDATE (commit 53feb82): the skill-lock self-rearm Catch-22 is RESOLVED. wkappbot skill edit no longer arms the lock because the post-hook now verb-anchors source-edit detection and excludes skill catalog ops (skill edit/contribute/add). The earlier clean-text-only hygiene guidance is obsolete -- a normal skill edit clears the lock and it stays clear, so delegation via wkcodex works right after.
24. harness:skill pattern prefix RULE: always use the robust prefix (?:Edit\(|Write\(|Bash\((?:[\w./\-]+\s+)*?[\w./\-]*edit(?:\.\w+)?\b).* before the target match. The old Edit\(.* prefix matches only the Edit tool, so the Write tool and Bash appbot-edit/wkedit bypass the lock. Input is normalized backslash-to-slash before matching, so target matches like .*csharp.*MyCdpContext are slash-agnostic (mixed slashes are safe). Coverage snapshot 2026-05-27: WKAppBot 8 specific + source catch-all; wkappbot-sdk Launcher placement + EyeCmdPipe + source catch-all; WkAutoQuant 17 trading sources (prefix upgraded); personal-docs wkharness.
