Now accepting guest editorial from SAP, Oracle, Workday and ServiceNow practitioners.Pitch a guide
The Sign-Off Table

Lesson 4 of 7

Permissions, hooks, subagents and headless runs

9 min

Listen to this lesson

0:00 / 15:42

CLAUDE.md asks. Settings enforce. This lesson turns Contoso's rules into a .claude/settings.json that the repository shares, three hooks that run checks without relying on the model, a reviewer subagent, and a headless review job. The organization-wide layer (managed settings, approved MCP servers, telemetry) is in sap-aid-302, lesson 3; everything here sits underneath it.

Permission rules for the ADT tools

Claude Code evaluates rules in the order deny, then ask, then allow; the first match decides, and a deny at any scope can't be overridden by an allow elsewhere. MCP tools are matched by name: mcp__adt__* covers every tool of the adt server, and mcp__adt__abap_edit_file one tool. Rules with parentheses, such as matching an MCP tool's parameters, are skipped when a settings file loads, so plan your policy at tool level.

ListToolsContoso's reasoning
denyExemption requests, AI fixes, feed query, transport creationExemptions and requests are human decisions; AI fixes consume Joule tokens and add a second AI's edits to review; feeds can show other users' runtime errors and business data
askEdit, create, generate, quick fixes, custom actions, git pushAnything that changes an object or leaves the laptop gets a person's yes, every time
allowSearch, read, check, activate, ABAP Unit, ATC, transport display and diffReading and checking are the agent's main job; prompting for each would train people to click yes without reading

Activation is allowed because it only promotes what a person already approved as an edit; some teams put it under ask during the pilot.

This is stricter than the team-wide baseline in sap-aid-302, which asks rather than denies transport creation. Both are defensible; Contoso's core agreement team decided that the developer always creates the request and names it in the prompt.

Hooks: checks that don't depend on the model

Hooks are commands (or HTTP calls, prompts, agents or MCP tool calls) that Claude Code runs at fixed events. For ABAP work, three are worth having:

A PostToolUse hook gives fast local feedback on abapGit files; a Stop hook refuses to let Claude declare done without evidence.
The Sign-Off Table · thesignofftable.com/academy/sap-aid-203
  • Local lint on abapGit files (PostToolUse, matcher Edit|Write). A script runs abaplint, an open-source ABAP linter that works on abapGit-serialized files, pinned to one version as its README recommends (for example npx @abaplint/cli@2.120.81). If it finds errors it writes them to stderr and exits 2; for PostToolUse, Claude Code shows that stderr to Claude, which fixes the file. abaplint isn't ATC: it doesn't know your system's dictionary unless you give it dependencies, and it's no substitute for activation in CMD.
  • An evidence gate (Stop). When Claude tries to finish, a script reads the hook input's transcript_path, finds the last call to mcp__adt__abap_edit_file or abap_activate_objects, and checks that abap_run_unit_tests and abap_atc_get_result ran after it. If not, it exits 2 with "Run ABAP Unit and ATC before reporting"; for Stop, exit 2 keeps Claude working. The script checks stop_hook_active so it never loops, and Claude Code caps consecutive Stop-hook continuations at eight anyway.
  • An audit line (PreToolUse, matcher mcp__adt__.*). Appends session id, tool name and input to a log that is shipped centrally. Hook matchers are regular expressions here, so the .* is required; mcp__adt alone matches nothing. Contoso deploys this one as a managed hook (sap-aid-302).

A reviewer subagent

Subagents are Markdown files in .claude/agents/ with YAML frontmatter. Each runs in its own context window with its own tools and permission mode, which makes a good second pair of eyes: it hasn't seen the main session's reasoning, only the change.

Headless runs: review, not remote control

claude -p runs Claude Code non-interactively for scripts and CI. Contoso uses it in one place: a pipeline job on each abapGit pull request that reviews the diff. Two details matter. Without --bare, a -p run loads the repository's hooks and connects servers from .mcp.json without any trust prompt; with --bare it skips hooks, MCP servers, subagents and CLAUDE.md, so the job passes rules explicitly. And the job never gets an ADT connection: the ADT MCP server runs only inside an IDE, and a build server has no business logging on to CMD. sap-aiq-201 uses the same locked-down claude -p pattern to summarize SAP test results.

  1. Pull request openedBranch cmdk900452-overlap-check against main in repository cmc-core-agreement
  2. Diff piped ingit diff origin/main...HEAD -- src/ | claude --bare -p "Review this ABAP diff"
  3. Rules passed explicitly--append-system-prompt-file review-rules.md (a copy of the CLAUDE.md review section)
  4. Locked down--permission-mode dontAsk denies anything not pre-approved; no MCP servers
  5. Result posted--output-format json; the result field becomes a pull request comment
  6. People decideTwo approvals, one from the package owner; the comment is advice, not a gate
Contoso's headless job reviews text that is already in git; it touches no SAP system and approves nothing.
The Sign-Off Table · thesignofftable.com/academy/sap-aid-203
Key points
  • Deny exemptions, AI fixes, feeds and transport creation; ask for edits, creation and pushes; allow reads, checks, tests and diffs.
  • MCP permission rules work at tool level: mcp__adt__* or mcp__adt__<tool>; parenthesized MCP rules are skipped.
  • Hooks enforce what CLAUDE.md asks: local lint on abapGit files, a Stop-hook evidence gate, an audit line.
  • Only exit 2 blocks; timeouts don't; mcp_tool hook output on PostToolUse doesn't reach Claude.
  • Use claude -p --bare for CI review of git diffs, never to drive an SAP system unattended.

Check your understanding · not graded

Lesson check

  1. 1. A settings file contains the deny rule mcp__adt__abap_feed_query and the allow rule mcp__adt__*. Can Claude call abap_feed_query?

  2. 2. A PostToolUse hook that runs abaplint exits with code 2 and writes the findings to stderr. What happens?

  3. 3. Why does Contoso's CI review job use claude -p with --bare?

  4. 4. What is the main benefit of running the ABAP review as a subagent?