Claude Code is a terminal-native coding agent: it can inspect a repository, edit files, run commands, and maintain context across a task. That combination makes it more capable than code completion and more dangerous than a chat window. A production workflow therefore needs two designs at once: an effective task loop and an enforceable authority boundary.
This guide explains how to choose among the CLI, Agent SDK, direct Claude API, and GitHub Actions; how to write reviewable task contracts; and how to evaluate the result without treating the agent's own summary as proof.
Key Takeaways
- Choose the integration surface by who owns the agent loop: Claude Code, your application, or a managed runtime.
- The current self-hosted Agent SDK exposes a
query()stream in TypeScript and Python; other languages should call the documented CLI subprocess interface. CLAUDE.md, prompts, and tool descriptions shape behavior but do not enforce security.- For unattended work, isolate the process, minimize tools, protect credentials outside the agent boundary, and restrict network egress.
- A task is complete only when independent checks pass and the resulting diff survives review.
- Evaluate accepted outcomes, regressions, cost, latency, and attempted policy violations on representative repository tasks.
Choose the Right Claude Surface
The first design decision is not which prompt to write. It is which component should own planning, tools, state, and execution.
| Need | Surface | Agent loop owner | Practical boundary |
|---|---|---|---|
| Interactive repository work | Claude Code CLI | Claude Code | Local process and user-approved tools |
| Self-hosted Python or TypeScript agent | Claude Agent SDK | SDK process you operate | SDK permissions plus your runtime isolation |
| Custom tool loop in another language | Claude client SDK | Your application | Your own state machine and policy layer |
| Same Claude Code loop from Go, Java, or shell | claude -p subprocess |
Claude Code | CLI arguments, process sandbox, output schema |
| Agent inside GitHub workflows | anthropics/claude-code-action |
GitHub Action | Trigger policy, GitHub permissions, branch protection |
The distinction prevents a common implementation error: using a direct model SDK and assuming it includes Claude Code's file tools, sessions, hooks, or permission system. The reverse mistake is embedding the Agent SDK when a single model request with a small deterministic tool loop would be simpler.
Anthropic's current Agent SDK documentation defines query() as the primary TypeScript and Python interface. It returns a stream of messages from the Claude Code agent loop. Older examples based on a ClaudeAgent class and .run() method are not the current API contract and should not be copied into new projects.
Define a Task Contract Before Delegating
A coding agent works best when the request defines the desired state and the evidence required to prove it. Use four fields:
- Goal: the externally visible behavior that must become true.
- Scope: files, modules, APIs, and dependencies the agent may change.
- Constraints: compatibility, security, data, style, and rollout limits.
- Verification: commands, assertions, and review evidence required before completion.
For example:
Goal: reject expired password-reset links without changing valid-link behavior.
Scope: auth/reset handler, token verifier, and focused tests.
Constraints: no schema change; preserve current response shape; never log tokens.
Verification: targeted tests, full auth suite, lint, and a diff limited to the named modules.
This contract does more than improve the prompt. It creates a review checklist that remains valid even if the agent takes an unexpected path. Keep requirements in repository-controlled files when they are durable; keep one-off acceptance criteria in the task itself.
Run the CLI from Go with a Reviewable Boundary
The Agent SDK is officially packaged for TypeScript and Python. In Go, the documented interoperable path is to run the CLI with -p and structured output. The following standard-library program defaults to a dry run, uses a timeout, pins the working directory, requests JSON, and grants only the Read tool. Pass -execute only after claude is installed and ANTHROPIC_API_KEY is available.
package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
type resultEnvelope struct {
IsError bool `json:"is_error"`
Result string `json:"result"`
SessionID string `json:"session_id"`
}
func commandArgs(prompt string) []string {
return []string{
"--bare",
"-p",
prompt,
"--tools",
"Read",
"--allowedTools",
"Read",
"--output-format",
"json",
}
}
func run(ctx context.Context, repo, prompt string) (resultEnvelope, error) {
cmd := exec.CommandContext(ctx, "claude", commandArgs(prompt)...)
cmd.Dir = repo
output, err := cmd.CombinedOutput()
if err != nil {
return resultEnvelope{}, fmt.Errorf("claude failed: %w: %s", err, output)
}
var result resultEnvelope
if err := json.Unmarshal(output, &result); err != nil {
return resultEnvelope{}, fmt.Errorf("decode output: %w", err)
}
if result.IsError {
return resultEnvelope{}, errors.New("agent reported an error")
}
return result, nil
}
func main() {
repo := flag.String("repo", ".", "repository directory")
execute := flag.Bool("execute", false, "run Claude Code")
flag.Parse()
prompt := "Summarize the authentication boundary. Do not edit files."
if !*execute {
fmt.Println("claude", strings.Join(commandArgs(prompt), " "))
return
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
result, err := run(ctx, *repo, prompt)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("session=%s\n%s\n", result.SessionID, result.Result)
}
Why these choices matter:
--bareavoids silently loading project or user hooks, plugins, MCP servers, auto-memory, andCLAUDE.md; pass required context explicitly.--tools Readlimits the available tool set, while--allowedTools Readlets that tool run without an interactive prompt.exec.CommandContextgives the caller a cancellation boundary.- JSON output is parsed before use instead of scraped from prose.
- The working directory is explicit, so the process does not inherit an accidental repository.
The permission layer is still not a sandbox. Run high-risk or multi-tenant workloads inside an isolated process, container, gVisor sandbox, or VM according to the threat model.
Understand Sessions, Hooks, and Permissions
Three mechanisms often get conflated:
| Mechanism | What it does | What it cannot guarantee |
|---|---|---|
| Session | Preserves conversation and agent state | Correctness after context grows or changes |
| Hook | Runs deterministic code at lifecycle points | Safety if the hook itself has broad authority |
| Permission rule | Allows, blocks, or prompts for tools and commands | OS isolation or semantic understanding of every side effect |
Use sessions when continuity matters, but record the exact session ID and repository revision. A resumed session against a different commit can make earlier assumptions stale. Use hooks for mechanical policy such as formatting, secret scanning, or validating a command request. Do not use a hook's success as proof that business behavior is correct.
Permission rules should be capability-oriented:
- a review job gets read access and test commands, not repository write;
- a documentation job gets a narrow writable path;
- a migration job runs in an ephemeral environment with a disposable database;
- a deployment job proposes a release but cannot approve its own production rollout.
Treat CLAUDE.md as Context, Not Enforcement
CLAUDE.md is valuable because it makes stable repository knowledge available at the start of a session. A useful file contains:
- architecture boundaries and ownership;
- canonical build, test, lint, and generation commands;
- code conventions that are not obvious from tooling;
- files that are generated rather than hand-edited;
- the evidence required before a task may be called complete.
Keep it short enough to remain salient and verify every command it names. Avoid copying entire style guides into it when the linter or repository documentation already enforces them.
A statement such as "never read production secrets" is only an instruction. The enforceable version is to keep secrets outside the agent filesystem, expose narrowly scoped operations through a proxy, and deny direct network paths to secret stores.
Secure the Runtime, Credentials, and Network
An agent processes untrusted repository files, issue text, web pages, tool results, and generated output. Any of those can contain instructions that compete with the operator's intent. Defense therefore belongs below the prompt layer.
For higher-risk deployments:
- Mount only required code and prefer read-only mounts until an edit is needed.
- Put credentials behind a proxy outside the agent boundary instead of exposing raw secrets.
- Restrict outbound destinations and validate redirects; a hostname allowlist alone may not stop every exfiltration path.
- Drop unnecessary operating-system capabilities and impose CPU, memory, process, and time limits.
- Store outputs in a reviewable worktree, never directly in a protected branch.
- Log tool decisions and policy outcomes without logging credentials or sensitive file contents.
Anthropic's secure-deployment guidance distinguishes its permission parser from a sandbox. That distinction matters: allowing a syntactically familiar command does not prove its target path or downstream effect is safe.
Use GitHub Actions Without Creating a Privileged Prompt Endpoint
The official GitHub Action supports interactive @claude requests and automatic prompts on workflow events. The main risk is not whether the action can write code; it is who can supply input to a process holding repository authority.
A defensible workflow should:
- trigger only on the events you intend;
- rely on the action's actor checks and add repository-specific restrictions where needed;
- grant the smallest GitHub
permissionsset; - prefer workload identity federation when organizational policy requires avoiding long-lived API keys;
- keep fork-originated content away from write-capable jobs;
- require branch protection, tests, and human review after the agent commits;
- pin the action according to your dependency policy and review upgrades.
GitHub permissions and Claude tool permissions solve different problems. A read-only Claude tool policy cannot compensate for an over-privileged workflow token used by another step, and a narrow workflow token does not prevent the agent from damaging its local workspace.
Build an Evaluation That Measures Accepted Work
Feature demonstrations answer "can the agent do this once?" A production evaluation asks "how often does it produce an acceptable change under our constraints?"
Create a versioned task set with:
- representative bug fixes, refactors, tests, documentation, and dependency changes;
- hidden acceptance tests or reviewer rubrics;
- clean repository snapshots and fixed tool access;
- adversarial repository content and misleading issue text;
- expensive, ambiguous, and impossible tasks that should be escalated;
- at least one rollback or partial-failure scenario.
Track metrics with explicit denominators:
| Metric | Definition |
|---|---|
| Accepted-task rate | accepted solutions / attempted tasks |
| Regression rate | accepted-looking changes that break protected behavior / attempted tasks |
| Review burden | reviewer minutes and requested revisions per accepted task |
| Policy violation rate | unauthorized attempts / tasks exposed to the boundary |
| Cost per accepted task | total model and runner cost / accepted tasks |
| Time to accepted change | wall-clock time from task start to review acceptance |
Do not optimize only for task completion. An agent can raise apparent completion by weakening tests, expanding scope, or hiding uncertainty. The acceptance gate must be independent of the agent's own judgment.
Production Release Checklist
Before expanding Claude Code beyond low-risk local work:
- Choose the CLI, Agent SDK, client SDK, or managed runtime deliberately.
- Define goal, scope, constraints, and verification for each task class.
- Minimize tools, filesystem access, network access, and GitHub permissions.
- Keep credentials outside the agent boundary where practical.
- Decide how cancellation, timeout, retry, and partial edits are handled.
- Record session, model, policy, repository revision, and validation results.
- Run deterministic tests and inspect the diff before accepting completion.
- Require an independent reviewer for security, data, deployment, and schema changes.
- Evaluate representative and adversarial tasks before increasing autonomy.
- Re-run the evaluation when the model, CLI, SDK, permissions, or repository rules change.
Conclusion
Claude Code is most useful when you treat it as a capable software process rather than a conversational oracle. Give it a precise task contract, the smallest authority needed, an isolated runtime, and independent completion evidence. Use the Agent SDK where its Python or TypeScript agent loop fits, use the CLI subprocess for other languages, and keep direct API integrations separate. The result is not merely more automation; it is automation whose output can be reviewed, reproduced, and rejected safely.
Primary Sources
- Claude Agent SDK overview
- Claude Agent SDK TypeScript reference
- Run Claude Code programmatically
- Securely deploying AI agents
- Claude Code GitHub Actions