Sub-Agents, Hooks, and Skills: The Orchestration Layer Claude Code Won't Advertise
Agents

Sub-Agents, Hooks, and Skills: The Orchestration Layer Claude Code Won't Advertise

Sub-agents, hooks, and skills are Claude Code's secret orchestration layer. Patterns for multi-step automation without runaway token bills.

Sub-Agents: When to Fork Context vs Inline Tool Use

Understanding when to spawn a sub-agent versus using tools directly is fundamental to efficient agent design:

Use Sub-Agents When:

  • Task decomposition benefits from isolation: Complex problems that can be cleanly split into independent sub-problems
  • Different tool sets required: Sub-task needs access to tools the main agent shouldn't have (or vice versa)
  • State isolation is valuable: You don't want sub-task pollution affecting main context (e.g., experimental approaches)
  • Parallel execution possible: Sub-tasks can run simultaneously for performance gains
  • Specialized expertise needed: Sub-agent can be pre-loaded with domain-specific skills or knowledge
  • Retry semantics differ: Sub-task needs different error handling or retry policies than parent

Use Inline Tool Use When:

  • Tight coupling with main context: Sub-task needs frequent access to current state or variables
  • Low latency critical: Forking overhead would impact user experience
  • Simple, linear operations: Straightforward sequences without branching complexity
  • Resource conservation: Avoiding the overhead of additional agent instances
  • Shared state essential: Multiple steps need to read/write the same variables
  • Minimal contextual divergence: Sub-task doesn't need significantly different system prompts or tools
// Example: When to use sub-agents
// GOOD: Independent research tasks
const researchSubagent = await agent(
  `Research best practices for ${topic} in SAP security`,
  {
    agentType: "research-specialist",
    tools: ["web_search", "document_fetch"],
    // Fresh context, no pollution from main task
  }
);

// BETTER: Inline for tightly coupled operations
// Need to immediately use results in current calculation
const exchangeRate = await tool("fetch_exchange_rate", {
  from: "USD",
  to: "EUR"
});
const adjustedAmount = amount * exchangeRate;

Hooks: Pre/Post Tool Guards, Formatting, Policy Enforcement

Hooks are the invisible guardrails that make agent systems safe and predictable:

Types of Hooks and When to Use Them:

Pre-Tool Hooks (Guard Rails)

  • Input validation: Check parameters before execution
  • Permission checking: Verify user/role has rights
  • Rate limiting: Prevent abuse or excessive usage
  • Context enrichment: Add relevant information before processing
  • Policy enforcement: Block actions violating corporate guidelines
  • Transformation: Normalize inputs to expected format

Post-Tool Hooks (Quality Assurance)

  • Result validation: Check output meets expectations
  • Error handling: Convert failures to actionable feedback
  • Data transformation: Convert output to needed format
  • Logging/auditing: Record what happened for compliance
  • State updates: Modify agent's internal state based on results
  • Trigger chaining: Automatically start next step in workflow

Specialized Hook Types:

  • Formatting hooks: Automatically apply code style (Prettier, ESLint fixes)
  • Security scanning hooks: Check generated code for vulnerabilities
  • Performance analysis hooks: Flag inefficient algorithms or patterns
  • Compliance checking hooks: Ensure outputs meet regulatory requirements (SOX, GDPR, etc.)
  • Testing hooks: Automatically generate or run tests for generated code
// Example: Comprehensive hooks for SAP ABAP development
const abapHooks = {
  // Prevent dangerous operations in production-like environments
  preTool: async (toolCall) => {
    const { name, arguments: args } = toolCall;

    // Block direct production system modifications
    if (name === 'execute_abap' &&
        args.code.includes('UPDATE ') &&
        process.env.TARGET_SYSTEM === 'PROD') {
      throw new Error('Direct production updates require change management approval');
    }

    // Enforce naming conventions for new objects
    if (name === 'create_abap_object' &&
        !/^Z[A-Z][A-Z0-9_]*$/.test(args.objectName)) {
      throw new Error('Custom objects must start with "Z" followed by uppercase letter');
    }

    return toolCall; // Allow if checks
  },

  // Ensure generated code follows team standards
  postTool: async (toolResult) => {
    if (toolResult.output && typeof toolResult.output === 'string') {
      // Apply ABAP-specific formatting
      const formatted = await formatAbapCode(toolResult.output);

      // Check for hardcoded credentials (security)
      if (/PASSWORD\s*=\s*['"][^'"]+['"]/i.test(formatted)) {
        throw new Error('Hardcoded credentials detected - use secure vault instead');
      }

      // Verify proper error handling patterns
      if (!/DATA.*EXCEPTION.*CX_/i.test(formatted) &&
          /\b(CALL\s+FUNCTION|CALL\s+METHOD)/i.test(formatted)) {
        console.warn('Consider adding exception handling for API calls');
      }

      return { ...toolResult, output: formatted };
    }

    return taskResult;
  }
};

// Usage in agent configuration
const sapAgent = await agent(
  "Refactor this legacy BAPI to follow clean core principles",
  {
    tools: ["read_abap", "write_abap", "execute_abap", "run_abap_unit_test"],
    hooks: abapHooks
  }
);

Skills: Reusable Domain Packs for SAP, Cloudflare, NAS

Skills package expertise, tools, and patterns into shareable units:

What Makes a Good Skill:

  • Domain-focused: Clear boundaries (SAP ABAP, Cloudflare Workers, TrueNAS administration)
  • Tool-curated: Includes exactly the tools needed for the domain — no more, no less
  • Pre-configured: Comes with sensible defaults, templates, and examples
  • Documented: Clear usage instructions and common patterns
  • Versioned: Track improvements and breaking changes
  • Composable: Works well with other skills (UNIX philosophy applied to agents)

Example: SAP ABAP Skill Components

Core Tools

  • read_abap_source
  • write_abap_source
  • activate_transport
  • run_abap_unit_test
  • check_syntax
  • execute_function_module
  • read_table_sap

Initialized State

  • Common naming conventions (Z*/*Y*)
  • Transport layer configuration
  • Standard authorization objects
  • Preferred ABAP version flags
  • Typical system parameters (client, language)

Prompt Templates

  • "Explain this ABAP code for a junior developer"
  • "Generate unit tests for this class/method"
  • "Suggest performance improvements for this SELECT"
  • "Convert this procedural code to object-oriented"
  • "Find potential security issues in this report"

Workflow Patterns

  • Test-driven development cycle
  • Quick fix implementation process
  • Transport request creation and management
  • Performance analysis and optimization routine
  • Security review checklist

Where to Find and Share Skills:

  • Official registries: Claude Skills Marketway, Cursor Community
  • Internal repositories: Company-wide skill libraries
  • Open source: GitHub organizations sharing domain expertise
  • Vendor-provided: SAP, Cloudflare, etc. offering official skills
  • Peer sharing: Informal exchange between professional networks

Pro Tip: Create a "meta-skill" that combines your most frequently used domain skills. For example:

  • SASTechnician = SAP ABAP Skill + Security Testing Skill + Performance Optimization Skill
  • CloudDevOps = Cloudflare Workers Skill + Infrastructure as Code Skill + Monitoring & Alerting Skill
  • FullStackDeveloper = Frontend Skill + Backend Skill + Database Skill + DevOps Skill

Comparison to n8n/Ollama Orchestration on Homelab

How Claude Code's approach compares to familiar orchestration tools:

Claude Code Native Orchestration

  • Granularity: Fine-grained control at tool-call level
  • Context sharing: Rich, structured state between steps
  • Failure handling: Sophisticated retry/circuit breaker patterns
  • Debugging: Step-by-step inspection of agent reasoning
  • Language: Natural language + code
  • Latency: Higher (LLM inference time)
  • Best for: Cognitive tasks requiring judgment and adaptation

n8n/Ollama Homelab Approach

  • Granularity: Coarse-grained (node-to-node)
  • Context sharing: Limited (usually JSON payloads)
  • Failure handling: Basic retry mechanisms
  • Debugging: Visual execution tracing
  • Language: Visual workflow + code nodes
  • Latency: Lower (deterministic execution)
  • Best for: Reliable, repetitive automation workflows

The Hybrid Sweet Spot:

Most sophisticated implementations combine both approaches:

  1. Use Claude Code for the "smart" parts requiring judgment (planning, interpretation, exception handling)
  2. Use n8n/Ollama workflows for the "reliable" parts (data movement, notifications, scheduled tasks)
  3. Connect them via webhooks, message queues, or shared databases
  4. Let each system do what it does best

Token Budgets and the "If Stuck Rule" for Agent Chains

Prevent runaway costs with these practical constraints:

Per-Turn Token Budgets:

  • Set maximum tokens per agent interaction (include both prompt and completion)
  • Typical values: 2K-4K for simple tasks, 8K-16K for complex reasoning
  • Implement at the MCP/client level to catch violations early
  • Provide graceful degradation when limits approached (summarize, ask clarifying questions)
  • Log near-limit events for capacity planning

Conversation-Level Limits:

  • Maximum turns per conversation before forced reset
  • Successive failure limits (e.g., stop after 3 consecutive tool failures)
  • Time-based expiration (auto-end conversations after N hours)
  • Cost-based ceilings (stop if estimated cost exceeds threshold)
  • User-initiated continuation options for legitimate long-running tasks

The "If Stuck Rule" - Essential for Production:

A practical heuristic for determining when to escalate or abort:

  1. Define what "stuck" means for your use case (no progress, looping, errors)
  2. Set a threshold (e.g., 3 attempts without meaningful advancement)
  3. When triggered:
    • Escalate to human supervisor
    • Fallback to simpler, more deterministic approach
    • Return partial results with clear limitations
    • Abort and return error with diagnostic information
  4. // Example implementation of the "If Stuck Rule"
    class AgentWithStuckDetection {
      constructor(maxAttempts = 3) {
        this.attempts = 0;
        this.lastProgressCheck = Date.now();
        this.lastKnownState = null;
      }
    
      async executeWithStuckProtection(operation) {
        try {
          // Check if we're stuck before attempting
          if (this.isStuck()) {
            throw new Error(`Agent appears stuck after ${this.attempts} attempts`);
          }
    
          this.attempts++;
          const startState = await this.captureState();
    
          // Attempt the operation
          const result = await operation();
    
          const endState = await this.captureState();
    
          // Check if we made meaningful progress
          if (this.hasMadeProgress(startState, endState)) {
            this.resetStuckTracking();
            return result;
          }
    
          // No meaningful progress - count as stuck attempt
          throw new Error('No meaningful progress made');
        } catch (error) {
          // Handle stuck detection
          if (error.message.includes('Stuck')) {
            await this.handleStuckState();
            throw error; // Re-throw after handling
          }
          throw error; // Re-throw other errors normally
        }
      }
    
      // Implementation-specific methods would go here
      isStuck() { /* ... */ }
      captureState() { /* ... */ }
      hasMadeProgress() { /* ... */ }
      resetStuckTracking() { /* ... */ }
      handleStuckState() { /* ... */ }
    }

Security: What Sub-Agents Should Never Access

Define hard boundaries that no agent should ever cross:

Absolute Boundaries (Never Cross):

  • Production credentials: Never store or transmit plaintext production passwords, keys, or certificates
  • Unrestricted system access: No agent should have sudo/root equivalent without explicit justification
  • Customer PII: Personally identifiable information requires special handling and consent
  • Trade secrets: Clearly defined IP that requires additional protections
  • Unauthorized networks: Attempting to pivot to or scan unauthorized network segments
  • Policy violations: Actions explicitly forbidden by acceptable use policies

Conditional Boundaries (Context-Dependent):

  • Development vs production: Access permissions should differ significantly between environments
  • Data classification levels: Public, internal, confidential, restricted each need different handling
  • Time-based access: Certain operations only allowed during maintenance windows
  • Geographic restrictions: Data sovereignty requirements may limit where processing occurs
  • Dual-person approval: High-risk operations requiring two authorized individuals

Implementing Defense in Depth:

  1. Network segmentation: Agents operate in isolated VLANs or security groups
  2. Principle of least privilege: Start with no permissions, grant only what's needed
  3. Regular permission reviews: Quarterly audits of agent access rights
  4. Automated anomaly detection: ML-based profiling of normal agent behavior
  5. Immutable audit logs: Cryptographically signed, append-only records of all agent actions
  6. Break-glass procedures: Clearly documented emergency access methods
  7. Regular penetration testing: Include agent systems in your red/blue team exercises
  8. Remember: The most sophisticated agent in the world is worthless if it creates a security breach. Security isn't a feature — it's the foundation everything else builds upon.

Template: One Hooks File for ayraix Content Pipeline

Here's a concrete example of how to implement governance for a real-world content pipeline:

// .ayraix-content-hooks.js
// Governs AI-assisted content creation for ayraix.com
const crypto = require('crypto');

/**
 * Generates a deterministic content ID for audit tracking
 */
function generateContentId(title, author) {
  const hash = crypto.createHash('sha256');
  hash.update(`${title}|${author}|${new Date().toISOString().slice(0,10)}`);
  return hash.digest('hex').substring(0, 12);
}

/**
 * Pre-hook: Validate content creation requests
 */
async function preContentCreationHook(request) {
  const { type, title, author, metadata = {} } = request;

  // 1. Authentication check
  if (!await validateUserPermissions(author, 'content:create')) {
    throw new Error(`User ${author} lacks content creation permissions`);
  }

  // 2. Input validation
  if (!title || title.trim().length < 3) {
    throw new Error('Title must be at least 3 characters');
  }

  if (title.length > 100) {
    throw new Error('Title exceeds maximum length of 100 characters');
  }

  // 3. Duplicate prevention (basic)
  if (await titleExistsRecently(title, author)) {
    throw new Error('Similar title recently used by this author - consider variation');
  }

  // 4. Content type validation
  const validTypes = ['article', 'tutorial', 'case_study', 'news', 'reference'];
  if (!validTypes.includes(type)) {
    throw new Error(`Invalid content type: ${type}. Must be one of: ${validTypes.join(', ')}`);
  }

  // 5. Add tracking metadata
  return {
    ...request,
    contentId: generateContentId(title, author),
    timestamp: new Date().toISOString(),
    version: 1
  };
}

/**
 * Post-hook: Validate and process generated content
 */
async function postContentGenerationHook(result) {
  const { content, metadata, contentId } = result;

  // 1. Basic integrity check
  if (!content || content.trim().length === 0) {
    throw new Error('Generated content is empty');
  }

  // 2. Length validation (adjusted for content type)
  const minLengths = {
    article: 800,
    tutorial: 1200,
    case_study: 1000,
    news: 400,
    reference: 500
  };

  const minLength = minLengths[metadata.type] || 500;
  if (content.length < minLength) {
    throw new Error(`Content too short: ${content.length} characters (minimum ${minLength} for ${metadata.type})`);
  }

  // 3. Quality checks
  const qualityScore = await assessContentQuality(content, metadata.type);
  if (qualityScore < 0.6) {
    throw new Error(`Content quality below threshold: ${qualityScore}/1.0`);
  }

  // 4. SEO basics validation
  const seoIssues = await validateBasicSeo(content, metadata);
  if (seoIssues.length > 0) {
    console.warn(`SEO recommendations: ${seoIssues.join(', ')}`);
    // In production, might return these as warnings rather than blocking
  }

  // 5. Plagiarism check (simplified)
  const originalityScore = await checkOriginality(content);
  if (originalityScore < 0.85) {
    throw new Error(`Originality concern: ${originalityScore}/1.0 - possible excessive similarity to existing content`);
  }

  // 6. Add final metadata for publishing
  return {
    ...result,
    wordCount: content.split(/\s+/).length,
    readingTime: Math.ceil(content.split(/\s+/).length / 200), // 200 WPM
    seoScore: await calculateSeoScore(content),
    readyForReview: true,
    reviewDueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() // 1 week
  };
}

/**
 * Helper: Check if user has permission for action
 */
async function validateUserPermissions(username, permission) {
  // In reality, this would check against your auth system
  // For demo: everyone can create content
  return true;
}

/**
 * Helper: Check for recent similar titles
 */
async function titleExistsRecently(title, author) {
  // In reality, this would query your content database
  // For demo: assume no recent duplicates
  return false;
}

/**
 * Helper: Assess content quality (simplified)
 */
async function assessContentQuality(content, type) {
  // In reality, this might use ML models or rule-based checks
  // For demo: basic heuristics
  const score = {
    hasIntroduction: content.includes('# ') || content.startsWith('# ') ? 0.2 : 0,
    hasConclusion: content.toLowerCase().includes('conclusion') ||
                   content.toLowerCase().includes('summary') ? 0.2 : 0,
    properLength: content.length > 300 ? 0.2 : 0,
    hasExamples: content.includes('example') || content.includes('for instance') ? 0.2 : 0,
    readability: /* Simplified */ 0.2
  };

  return Object.values(score).reduce((sum, val) => sum + val, 0);
}

/**
 * Helper: Basic SEO validation (simplified)
 */
async function validateBasicSeo(content, metadata) {
  const issues = [];

  // Check for title in content
  if (!content.includes(metadata.title)) {
    issues.push('Title not found in body content');
  }

  // Check for sufficient headings
  const headingCount = (content.match(/^#+/gm) || []).length;
  if (headingCount < 2) {
    issues.append('Consider adding more section headings');
  }

  // Check for alt text on images (if any)
  const imgTags = content.match(/]*>/g) || [];
  const imgWithoutAlt = imgTags.filter(tag => !/alt\s*=/.test(tag));
  if (imgWithoutAlt.length > 0) {
    issues.push(`${imgWithoutAlt.length} images missing alt text`);
  }

  return issues;
}

/**
 * Helper: Calculate SEO score (simplified)
 */
async function calculateSeoScore(content) {
  // In reality, this would be more sophisticated
  let score = 0.5; // Base score

  // Bonus for good structure
  if ((content.match(/^#+/gm) || []).length >= 3) {
    score += 0.2;
  }

  // Bonus for reasonable length
  if (content.length > 800 && content.length < 3000) {
    score += 0.2;
  }

  // Penalty for keyword stuffing (simplified)
  const wordCount = content.split(/\s+/).length;
  const uniqueWords = new Set(content.toLowerCase().split(/\W+/)).size;
  const repetitionRatio = 1 - (uniqueWords / Math.max(wordCount, 1));
  if (repetitionRatio > 0.7) {
    score -= 0.2;
  }

  return Math.min(1, Math.max(0, score));
}

/**
 * Helper: Check originality (simplified placeholder)
 */
async function checkOriginality(content) {
  // In reality, this would compare against existing content database
  // For demo: assume reasonably original
  return 0.9;
}

// Export for use in agent configuration
module.exports = {
  preContentCreationHook,
  postContentGenerationHook
};

This hooks file would be referenced in your agent configuration like:

const { preContentCreationHook, postContentGenerationHook } = require('./.ayraix-content-hooks');

const contentAgent = await agent(
  "Write a technical article about MCP security best practices",
  {
    // Your content-specific tools
    tools: [
      "web_search",
      "document_fetch",
      "write_file",
      "read_file",
      "execute_command" // For running linters, etc.
    ],

    // Apply our custom hooks
    hooks: {
      preTool: preContentCreationHook,
      postTool: postContentGenerationHook
    },

    // Additional configuration
    maxTurns: 10,
    temperature: 0.7, // Balance creativity with coherence
    system: `You are an expert technical writer for ayraix.com.
             Follow the SAP-inspired writing style: practical,
             skepticism-tempered, focused on real-world implementation.`
  }
);

Building Reliable Agent Workflows: Putting It All Together

The most successful implementations combine these elements thoughtfully:

  1. Start Simple: Begin with linear workflows before adding complexity
    • Master single-agent tasks with good hooks
    • Add skills for domain expertise
    • Then experiment withsub-agent decomposition
  2. Invest in Observability: You can't improve what you can't measure
    • Log all tool invocations with inputs/outputs
    • Track token usage and costs by workflow type
    • Monitor success/failure rates and common failure points
    • Implement distributed tracing for complex workflows
  3. Test Rigorously: Treat agent workflows like any other critical software
    • Unit tests for individual tools and hooks
    • Integration tests for common workflows
    • End-to-end tests for complete processes
    • Chaos testing: simulate tool failures, timeouts, unexpected outputs
  4. Document Thoroughly: Future-you (and teammates) will thank present-you
    • Workflow purpose and business value
    • Step-by-step execution logic
    • Expected inputs, outputs, and side effects
    • Known limitations and failure modes
    • Runbooks for troubleshooting common issues
  5. Iterate Based on Feedback: Continuous improvement beats perfectionism
    • Regular retrospectives on what's working and what's not
    • Metrics-driven adjustments to workflow design
    • Stay updated on platform improvements and new capabilities
    • Be willing to abandon approaches that consistently underperform

Remember the Goal:

The purpose of agent orchestration isn't to replace human judgment — it's to eliminate toil, reduce cognitive load, and create space for higher-value work. The best implementations make humans more effective, not obsolete.