Skip to content
cccskills
FreePublished on GitHub

developer-internals

Guide changes to gh-aw internals, including workflow validation, session scoring, and GitHub output handling. Find implementation locations and design rules for configuration checks, YAML compatibility, and limits on log responses returned to agents.

OriginalShow the original description

Internal gh-aw architecture: validation, pure session graders, safe output messages, schema validation, YAML compatibility, and MCP logs guardrail.

Works withGitHub
See how to install

Good for

  • Choose where to add workflow validation
  • Review changes to session scoring
  • Investigate YAML parsing differences
  • Debug oversized MCP log responses

Files included (1)

  • SKILL.md19.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

gh-aw Internal Architecture

Use this reference when working on the gh-aw compiler internals, validation system, session graders, safe output processing, or MCP server features.

Table of Contents

Validation Architecture

The validation system ensures workflow configurations are correct, secure, and compatible with GitHub Actions before compilation.

Architecture Overview

graph LR
    WF[Workflow] --> CV[Centralized Validation]
    WF --> DV[Domain-Specific Validation]
    CV --> validation.go
    DV --> strict_mode_validation.go
    DV --> strict_mode_permissions_validation.go
    DV --> pip.go
    DV --> npm.go
    DV --> expression_safety_validation.go
    DV --> engine.go
    DV --> mcp-config.go

Centralized Validation

Location: pkg/workflow/validation.go (core compile-time checks)

Purpose: General-purpose validation that applies across the entire workflow system

Key Functions:

  • validateExpressionSizes() - Ensures GitHub Actions expression size limits
  • validateContainerImages() - Verifies Docker images exist and are accessible
  • validateRuntimePackages() - Validates runtime package dependencies
  • validateGitHubActionsSchema() - Validates against GitHub Actions YAML schema
  • validateNoDuplicateCacheIDs() - Ensures unique cache identifiers
  • validateSecretReferences() - Validates secret reference syntax
  • validateRepositoryFeatures() - Checks repository capabilities
  • validateHTTPTransportSupport() - Validates HTTP transport configuration
  • validateWorkflowRunBranches() - Validates workflow run branch configuration

When to add validation here:

  • Cross-cutting concerns that span multiple domains
  • Core workflow integrity checks
  • GitHub Actions compatibility validation
  • General schema and configuration validation
  • Repository-level feature detection

Domain-Specific Validation

Domain-specific validation is organized into separate files in pkg/workflow/:

Strict Mode Validation

Files: pkg/workflow/strict_mode_validation.go and the strict_mode_*.go validators

Enforces security and safety constraints in strict mode:

  • validateStrictPermissions() - Refuses write permissions
  • validateStrictNetwork() - Requires explicit network configuration
  • validateStrictMCPNetwork() - Requires network config on custom MCP servers
  • validateStrictBashTools() - Refuses bash wildcard tools

Python Package Validation

File: pkg/workflow/pip.go

Validates Python package availability on PyPI.

NPM Package Validation

File: pkg/workflow/npm.go

Validates NPX package availability on npm registry.

Expression Safety

File: pkg/workflow/expression_safety_validation.go

Validates GitHub Actions expression security with allowlist-based validation. The matching test coverage lives in pkg/workflow/expression_safety_test.go.

Validation Decision Tree

graph TD
    A[New Validation Requirement] --> B{Security or strict mode?}
    B -->|Yes| C[strict_mode_validation.go]
    B -->|No| D{Only applies to one domain?}
    D -->|Yes| E{Domain-specific file exists?}
    E -->|Yes| F[Add to domain file]
    E -->|No| G[Create new domain file]
    D -->|No| H{Cross-cutting concern?}
    H -->|Yes| I[validation.go]
    H -->|No| J{Validates external resources?}
    J -->|Yes| K[Domain-specific file]
    J -->|No| I

Validation Patterns

Allowlist Validation

Used for security-sensitive validation with limited set of valid options:

func validateExpressionSafety(content string) error {
    matches := expressionRegex.FindAllStringSubmatch(content, -1)
    var unauthorizedExpressions []string

    for _, match := range matches {
        expression := strings.TrimSpace(match[1])
        if !isAllowed(expression) {
            unauthorizedExpressions = append(unauthorizedExpressions, expression)
        }
    }

    if len(unauthorizedExpressions) > 0 {
        return fmt.Errorf("unauthorized expressions: %v", unauthorizedExpressions)
    }
    return nil
}

External Resource Validation

Used for validating external dependencies:

func validateDockerImage(image string, verbose bool) error {
    cmd := exec.Command("docker", "inspect", image)
    output, err := cmd.CombinedOutput()

    if err != nil {
        pullCmd := exec.Command("docker", "pull", image)
        if pullErr := pullCmd.Run(); pullErr != nil {
            return fmt.Errorf("docker image not found: %s", image)
        }
    }
    return nil
}

Schema Validation

Used for configuration file validation:

func (c *Compiler) validateGitHubActionsSchema(yamlContent string) error {
    schema := loadGitHubActionsSchema()

    var data interface{}
    if err := yaml.Unmarshal([]byte(yamlContent), &data); err != nil {
        return err
    }

    if err := schema.Validate(data); err != nil {
        return fmt.Errorf("schema validation failed: %w", err)
    }
    return nil
}

Progressive Validation

Used for applying multiple validation checks in sequence:

func (c *Compiler) validateStrictMode(frontmatter map[string]any, networkPermissions *NetworkPermissions) error {
    if !c.strictMode {
        return nil
    }

    if err := c.validateStrictPermissions(frontmatter); err != nil {
        return err
    }

    if err := c.validateStrictNetwork(networkPermissions); err != nil {
        return err
    }

    return nil
}

Session Graders

Built-ins implement computeGraders(session, graders = []) over a redacted in-memory unified-session array. Reuse createSessionLookups(session) from actions/setup/js/grader_session.cjs for statistics, typed event lookups, correlated tool calls, scoped call indexes, and trajectory projections. Keep file I/O at the collection/replay boundary, never inside graders. Ten fast built-ins are enabled by default; expensive analyses require opt-in.

Custom syntax is graders.<distinct-id>.script, a synchronous JavaScript body receiving immutable session, config, and helpers snapshots. Return a finite number, null, or an object with value. The compiler partitions custom source into a restricted downstream graders job; conclusion receives only custom IDs. Never import or execute user code in conclusion, permit its step hooks, or allow jobs.graders customization. Repository-owned graders.operational-value.run Bash also runs only in that separate job: freeze the confined .sh source at compilation, pass session JSONL as $1 / GH_AW_SESSION_FILE and config JSON as GH_AW_GRADER_CONFIG, and preserve legacy metric-array output. Compilation and grader-shell-file-write share the Bash AST write checker. Shell has bounded execution and a stripped environment but is not a filesystem/network sandbox or microVM yet. Built-in overrides remain rejected.

The separate job consumes usage/aw_session.jsonl and publishes combined results in the graders artifact without source. Preserve conclusion's published session. Audit conditionally fetches registered custom results; the logs graders artifact set includes usage, graders, and historical transports. Keep compiler, runtime, schema, replay, artifact maps, and these consumers consistent. See operational-value-designer for current authoring syntax and evidence design.

Safe Output Messages

Safe output functions handle GitHub API write operations (creating issues, discussions, comments, PRs) from AI-generated content with consistent messaging patterns.

Safe Output Message Flow

The following diagram illustrates how AI-generated content flows through the safe output system to GitHub API operations:

graph TD
    A[AI Agent Output] --> B{Staged Mode?}
    B -->|Yes| C[Generate Preview Messages]
    B -->|No| D[Process Safe Output]
    C --> E[Show 🎭 Staged Mode Preview]
    E --> F[Display in Step Summary]
    D --> G{Safe Output Type}
    G -->|create-issue| H[Create GitHub Issue]
    G -->|create-discussion| I[Create GitHub Discussion]
    G -->|add-comment| J[Add GitHub Comment]
    G -->|create-pull-request| K[Create Pull Request]
    G -->|create-pr-review-comment| L[Create PR Review Comment]
    G -->|update-issue| M[Update GitHub Issue]
    H --> N[Apply Message Patterns]
    I --> N
    J --> N
    K --> N
    L --> N
    M --> N
    N --> O[Add AI Attribution Footer]
    N --> P[Add Installation Instructions]
    N --> Q[Add Related Items Links]
    N --> R[Add Patch Preview]
    O --> S[Execute GitHub API Operation]
    P --> S
    Q --> S
    R --> S
    S --> T[Generate Success Summary]
    T --> U[Display in Step Summary]

Flow Stages:

  1. AI Agent Output - AI generates content for GitHub operations
  2. Staged Mode Check - Determines if operation is in preview mode
  3. Safe Output Processing - Routes to appropriate GitHub operation type
  4. Message Pattern Application - Applies consistent formatting (footers, instructions, links)
  5. GitHub API Execution - Performs the actual GitHub operation
  6. Success Summary - Reports results in workflow step summary

Message Categories

AI Attribution Footer

Identifies content as AI-generated and links to workflow run:

> AI generated by [WorkflowName](run_url)

With triggering context:

> AI generated by [WorkflowName](run_url) for #123

Workflow Installation Instructions

>
> To add this workflow in your repository, run `gh aw add owner/repo/path@ref`. See [usage guide](https://github.github.com/gh-aw/setup/cli/).

Staged Mode Preview

All staged mode previews use consistent format with 🎭 emoji:

## 🎭 Staged Mode: [Operation Type] Preview

The following [items] would be [action] if staged mode was disabled:

Patch Preview

Display git patches in pull request bodies with size limits:

<details><summary>Show patch (45 lines)</summary>

\`\`\`diff
diff --git a/src/auth.js b/src/auth.js
index 1234567..abcdefg 100644
--- a/src/auth.js
+++ b/src/auth.js
@@ -10,7 +10,10 @@ export async function login(username, password) {
-    throw new Error('Login failed');
+    if (response.status === 401) {
+      throw new Error('Invalid credentials');
+    }
+    throw new Error('Login error: ' + response.statusText);
\`\`\`

</details>

Limits: Max 500 lines or 2000 characters (truncated with "... (truncated)" if exceeded)

Design Principles

Consistency

  • All AI-generated content uses same blockquote footer format
  • 🎭 emoji consistently marks staged preview mode
  • URL patterns match GitHub conventions
  • Step summaries follow same heading and list structure

Clarity

  • Clear distinction between preview and actual operations
  • Explicit error messages with actionable guidance
  • Helpful fallback instructions when operations fail
  • Field labels consistently use bold text

Discoverability

  • Installation instructions included in footers when available
  • Related items automatically linked across workflow outputs
  • Step summaries provide quick access to created items
  • Collapsible sections keep large content manageable

Safety

  • Labels sanitized to prevent unintended @mentions
  • Patch sizes validated and truncated when needed
  • Staged mode allows testing without side effects
  • Graceful fallbacks when primary operations fail

Schema Validation

All three JSON schema files enforce strict validation with "additionalProperties": false at the root level, preventing typos and undefined fields from silently passing validation.

Schema Files

FilePurpose
pkg/parser/schemas/main_workflow_schema.jsonValidates agentic workflow frontmatter in .github/workflows/*.md files
pkg/parser/schemas/mcp_config_schema.jsonValidates MCP (Model Context Protocol) server configuration

How It Works

When "additionalProperties": false is set at the root level, the validator rejects any properties not explicitly defined in the schema's properties section. This catches common typos:

  • permisions instead of permissions
  • engnie instead of engine
  • toolz instead of tools
  • timeout_minute instead of timeout-minutes
  • runs_on instead of runs-on
  • safe_outputs instead of safe-outputs

Example Validation Error

$ gh aw compile workflow-with-typo.md
✗ error: Unknown properties: toolz, engnie, permisions. Valid fields are: tools, engine, permissions, ...

Validation Process

graph LR
    A[Read workflow frontmatter] --> B[Parse YAML]
    B --> C[Validate against JSON schema]
    C --> D{Valid?}
    D -->|Yes| E[Continue compilation]
    D -->|No| F[Provide detailed error]
    F --> G[Show invalid fields]
    F --> H[Show valid field names]

Schema Embedded in Binary

Schemas are embedded in the Go binary using //go:embed directives:

//go:embed schemas/main_workflow_schema.json
var mainWorkflowSchema string

This means:

  • Schema changes require running make build to take effect
  • Schemas are validated at runtime, not at build time
  • No external JSON files need to be distributed with the binary

Adding New Fields

When adding new fields to schemas:

  1. Update the schema JSON file with the new property definition
  2. Rebuild the binary with make build
  3. Add test cases to verify the new field works
  4. Update documentation if the field is user-facing

YAML Compatibility

YAML has two major versions with incompatible boolean parsing behavior that affects workflow validation.

The Core Issue

YAML 1.1 Boolean Parsing Problem

In YAML 1.1, certain plain strings are automatically converted to boolean values. The workflow trigger key on: can be misinterpreted as boolean true instead of string "on".

Example:

# Python yaml.safe_load (YAML 1.1 parser)
import yaml

content = """
on:
  issues:
    types: [opened]
"""

result = yaml.safe_load(content)
print(result)
# Output: {True: {'issues': {'types': ['opened']}}}
#          ^^^^ The key is boolean True, not string "on"!

This creates false positives when validating workflows with Python-based tools.

YAML 1.2 Correct Behavior

YAML 1.2 parsers treat on, off, yes, and no as regular strings, not booleans. Only explicit boolean literals true and false are treated as booleans.

Example:

// Go goccy/go-yaml (YAML 1.2 parser) - Used by gh-aw
var result map[string]interface{}
yaml.Unmarshal([]byte(content), &result)

fmt.Printf("%+v\n", result)
// Output: map[on:map[issues:map[types:[opened]]]]
//         ^^^ The key is string "on" ✓

How gh-aw Handles This

GitHub Agentic Workflows uses goccy/go-yaml v1.18.0, which is a YAML 1.2 compliant parser:

  • ✅ on: is correctly parsed as a string key, not a boolean
  • ✅ Workflow frontmatter validation works correctly
  • ✅ GitHub Actions YAML is compatible (GitHub Actions also uses YAML 1.2 parsing)

Compatibility Flow

graph TD
    A[Workflow File] --> B{Parser Type?}
    B -->|YAML 1.1| C[Python yaml.safe_load]
    B -->|YAML 1.2| D[gh-aw / goccy/go-yaml]
    C --> E[on: parsed as True]
    D --> F[on: parsed as string]
    E --> G[False Positive]
    F --> H[Correct Validation]

Affected Keywords

YAML 1.1 treats these as booleans (parsed as true or false):

Parsed as true: on, yes, y, Y, YES, Yes, ON, On Parsed as false: off, no, n, N, NO, No, OFF, Off

YAML 1.2 treats all of the above as strings. Only these are booleans: true, false

Recommendations

For Workflow Authors

  1. Use gh-aw's compiler for validation:

    gh aw compile workflow.md
    
  2. Don't trust Python yaml.safe_load for validation - it will give false positives for the on: trigger key.

  3. Use explicit booleans when you mean boolean values:

    enabled: true      # Explicit boolean
    disabled: false    # Explicit boolean
    
    # Avoid for boolean values:
    enabled: yes       # Might be confusing across parsers
    disabled: no       # Might be confusing across parsers
    

For Tool Developers

  1. Use YAML 1.2 parsers for gh-aw integration:

    • Go: github.com/goccy/go-yaml
    • Python: ruamel.yaml (with YAML 1.2 mode)
    • JavaScript: yaml package v2+ (YAML 1.2 by default)
    • Ruby: Psych (YAML 1.2 by default in Ruby 2.6+)
  2. Document parser version in your tool

  3. Consider adding compatibility mode to switch between YAML 1.1 and 1.2 parsing

MCP Logs Guardrail

The MCP server logs command includes an automatic guardrail to prevent overwhelming responses when fetching workflow logs.

How It Works

graph TD
    A[logs command called] --> B[Generate output]
    B --> C{Output size check}
    C -->|≤ 12000 tokens| D[Return full JSON data]
    C -->|> 12000 tokens| E[Return guardrail message]
    E --> F[Include schema description]
    E --> G[Provide suggested jq queries]

Normal Operation (Output ≤ Token Limit)

When output is within the token limit (default: 12000 tokens), the command returns full JSON data:

{
  "summary": {
    "total_runs": 5,
    "total_duration": "2h30m",
    "total_tokens": 45000,
    "total_cost": 0.23
  },
  "runs": [...],
  "tool_usage": [...]
}

Guardrail Triggered (Output > Token Limit)

When output exceeds the token limit, the command returns structured response with:

{
  "message": "⚠️  Output size (15000 tokens) exceeds the limit (12000 tokens). To reduce output size, use the 'jq' parameter with one of the suggested queries below.",
  "output_tokens": 15000,
  "output_size_limit": 12000,
  "schema": { ... },
  "suggested_queries": [
    {
      "description": "Get only the summary statistics",
      "query": ".summary",
      "example": "Use jq parameter: \".summary\""
    },
    ...
  ]
}

Configuring the Token Limit

Default limit is 12000 tokens (approximately 48KB of text). Customize using the max_tokens parameter:

{
  "name": "logs",
  "arguments": {
    "count": 100,
    "max_tokens": 20000
  }
}

Token estimation uses approximately 4 characters per token (OpenAI's rule of thumb).

Using the jq Parameter

Filter output using jq syntax:

Get only summary statistics:

{ "jq": ".summary" }

Get run IDs and basic info:

{ "jq": ".runs | map({database_id, workflow_name, status})" }

Get only failed runs:

{ "jq": ".runs | map(select(.conclusion == \"failure\"))" }

Get high token usage runs:

{ "jq": ".runs | map(select(.token_usage > 10000))" }

Implementation Details

Constants:

  • DefaultMaxMCPLogsOutputTokens: 12000 tokens (default limit)
  • CharsPerToken: 4 characters per token (estimation factor)

Files:

  • pkg/cli/mcp_logs_guardrail.go - Core guardrail implementation
  • pkg/cli/mcp_logs_guardrail_test.go - Unit tests
  • pkg/cli/mcp_logs_guardrail_integration_test.go - Integration tests
  • pkg/cli/mcp_server.go - Integration with MCP server

Benefits

  1. Prevents overwhelming responses for AI models
  2. Provides guidance with specific filters
  3. Self-documenting with schema description
  4. Preserves functionality with jq filtering
  5. Transparent messaging about why guardrail triggered

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Coordinate work across AI agents with shared rules for locating team files, recording decisions and their reasons, and requesting help. It also defines who owns revisions after a reviewer rejects an artifact.

  • Locate team files across separate working directories
  • Record decisions that affect other agents
  • Request input from another specialist
github/gh-aw5.4KUpdated Oct 19, 2026

agent-conduct

FreeSummary

Set shared development rules for an agent team: keep specific agent names out of product code, tests, and CI configuration, verify affected tests before finishing, and record lessons learned from mistakes.

  • Share development rules across multiple agents
  • Keep team-specific agent names out of code and tests
  • Check affected tests before declaring work complete
github/gh-aw5.4KUpdated Oct 19, 2026

Routes requests to design, create, update, debug, or upgrade GitHub Agentic Workflows to the appropriate instructions. It helps the agent select a workflow-specific procedure and apply any additional rules defined for the repository.

  • Designing a new workflow through questions
  • Investigating failed runs and recurring errors
  • Upgrading workflows and addressing deprecations
github/gh-aw5.4KUpdated Oct 19, 2026

Prepare architecture proposals before implementation so the team can agree on a direction. Organize evidence, technical choices, effects on existing work, risks, and the first release’s scope into a document with clear recommendations and decision owners.

  • Proposing a major architecture change
  • Replanning milestones after a foundation change
  • Comparing technical options before seeking approval
github/gh-aw5.4KUpdated Oct 19, 2026

Update the gh-aw-firewall version used by gh-aw, rebuild generated files, and review release notes and configuration schemas, which define valid settings. It guides related code and test updates while identifying follow-up work that remains.

  • Updating the firewall version used by gh-aw
  • Reviewing release notes for integration changes
  • Checking configuration schema differences
github/gh-aw5.4KUpdated Oct 19, 2026

Review git and gh operations in gh-aw workflows to check credential availability for each repository. Identify missing token handoffs, risky expansions of limited checkouts, and error handling needed where credentials are intentionally unavailable.

  • Reviewing git operations after repository checkout
  • Checking tokens for private cross-repository access
  • Reviewing sparse and shallow checkout changes
github/gh-aw5.4KUpdated Oct 19, 2026

See all skills by github

Report a problem with this skill