[FEATURE] Permission system should be JSON-rule-based, not hardcoded — three proposals
Problem
The current permission system has three structural issues that can't be fixed incrementally:
1. Bypass-immune safetyCheck has no opt-out
bypassPermissions mode still prompts for .claude/skills/, .claude/commands/, .claude/agents/ paths. This is hardcoded — no setting, no env var, no documentation. Power users who run their own hook-based permission systems have no way to delegate this decision.
Each permission mode (plan, auto, bypassPermissions) should have a clear, documented scope. If a mode claims "bypass permissions", hidden exceptions that still prompt the user break the contract. At minimum, provide an opt-out for users who have external policy enforcement.
2. Permission rules should be structured JSON, not pattern strings
The current Bash(git:*) pattern syntax is broken in well-documented ways (#30519, #25441, #29616):
- Wildcards don't match compound commands
- "Always Allow" saves dead one-off strings
- Colon vs space syntax contradicts itself
- Carefully written rules still don't match
Proposal: Replace pattern strings with JSON Schema (Draft-07) rule documents:
{
"event": "PreToolUse",
"tool": ["Bash"],
"action": "allow",
"match": {
"properties": {
"tool_input": {
"properties": {
"command": { "pattern": "^git\\s" }
}
}
}
}
}
Benefits:
- Model can generate rules by filling a JSON template — no custom syntax to learn
- Schema validation catches malformed rules at write time
matchuses JSON Schema, which models already understand natively- Multiple rules merge by priority (deny > ask > allow), not first-match
- Rules are data — auditable, diffable, shareable
I'm currently preparing a hooks system with 50+ production rules for public release as a reference implementation.
3. Hook events should cover the full stream JSON type set
Current hook events (PreToolUse, PostToolUse, PermissionRequest, etc.) cover tool lifecycle but miss many stream JSON message types. To enable fine-grained policy:
- Token-level events (streaming content blocks)
- Model switching / routing decisions
- Context window operations (compaction, truncation)
- MCP server lifecycle (connect, disconnect, error)
- Cost/usage thresholds
The hook event set should map 1:1 to stream JSON type values. This lets external systems make policy decisions at any point in the pipeline, not just around tool calls.
Summary
| issue | current | proposed |
|-------|---------|----------|
| safetyCheck | hardcoded, no opt-out | per-mode scope contract + external policy delegation |
| rule syntax | Bash(git:*) pattern strings | JSON Schema Draft-07 documents |
| hook coverage | tool lifecycle only | full stream JSON type mapping |
The permission system should be data-driven (JSON rules the model can fill) rather than code-driven (hardcoded if/else the user can't change).
4 Comments
Update: the reference implementation is now public — mcp-claude-hooks.
55 JSON rules, Bash AST matching, zero dependencies. Ships with production rules covering all three proposals in this issue:
Proposal #2 — JSON Schema rules: Claude generates permission rules by filling a JSON template. Rules gate at any granularity: per-file, per-directory, per-session, per-tool, per-command. Matched against parsed AST, not string patterns. 100% deterministic — no prompt can guarantee this, no matter how many layers you add.
Proposal #1 — safetyCheck opt-out: Solved with one rule. A single
PermissionRequestmatch on.claude/path writes, actionallow. No patch, no fork.Proposal #3 — hook coverage: Engine handles 27 hook events across the full Claude Code lifecycle — tool use, permissions, sessions, subagents, compaction, worktrees, tasks.
The system works. But it's a workaround. The right solution is native integration: structured JSON rules that the model can read and write, enforced by the runtime.
v0.4.0 update: Agent Observer — a second Claude instance watching the session in real-time.
The hooks system now includes a background observer that runs concurrently with the main session. On every stop event, a Haiku worker reads the transcript and produces structured findings (
[ALERT],[FEEDBACK],[NOTE],[STATS]). On the next prompt, findings are injected as context.This is self-correcting AI — the observer catches bugs, flags wasted effort, and tracks decision quality that the main assistant misses. Not post-hoc review; concurrent observation.
Architecture: two-phase rule system (trigger on Stop, inject on UserPromptSubmit), per-session JSONL persistence, recursion guard preventing observer loops.
59 rules, 27 events, daemon mode, Bash AST matching, project health scoring, and now concurrent self-observation. All JSON-configured, zero dependencies.
→ https://github.com/woolkingx/mcp-claude-hooks
Closing this. I've moved on.
I spent two hours planning with Opus, then gave Codex 15 words. It finished in 90 minutes.
The data in this issue still stands — I hope it's useful to someone.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.