GitHub Agentic Workflows

Templating

Agentic workflows support four templating and substitution mechanisms: GitHub Actions expressions in frontmatter or markdown, conditional markdown blocks, compile-time imports, and runtime imports for files or URLs.

Agentic workflows restrict expressions in markdown content so prompts cannot expose secrets or environment variables to the LLM.

Note: These restrictions apply only to markdown content. YAML frontmatter can still use secrets and environment variables for workflow configuration.

Markdown allows event properties (github.event.*), repository context (github.actor, github.owner, github.repository, github.server_url, github.workspace), run metadata (github.run_id, github.run_number, github.job, github.workflow), and pattern expressions such as needs.*, steps.*, and github.event.inputs.*.

Use steps.sanitized.outputs.text, .title, or .body in markdown prompts to access sanitized event content. text includes the full sanitized context (title + body for issues and PRs, body for comments), while title and body expose those fields individually.

Other activation outputs such as comment_id, comment_repo, and slash_command are available as needs.activation.outputs.* in downstream jobs, not in the markdown prompt itself.

All other expressions are disallowed, including secrets.*, env.*, vars.*, and complex functions like toJson() or fromJson().

Expression safety is validated during compilation. Unauthorized expressions produce errors like:

error: unauthorized expressions: [secrets.TOKEN, env.MY_VAR].
allowed: [github.repository, github.actor, github.workflow, ...]

Include or exclude prompt sections based on boolean expressions using {{#if ...}} ... {{/if}} blocks.

{{#if expression}}
Content to include if expression is truthy
{{/if}}

The compiler automatically wraps expressions with ${{ }} for GitHub Actions evaluation. For example, {{#if github.event.issue.number}} becomes {{#if ${{ github.event.issue.number }} }}.

Falsy values: false, 0, null, undefined, "" (empty string) Truthy values: Everything else

---
on:
issues:
types: [opened]
---
# Issue Analysis
Analyze issue #${{ github.event.issue.number }}.
{{#if github.event.issue.number}}
## Issue-Specific Analysis
You are analyzing issue #${{ github.event.issue.number }}.
{{/if}}
{{#if github.event.pull_request.number}}
## Pull Request Analysis
You are analyzing PR #${{ github.event.pull_request.number }}.
{{/if}}

The template system supports only basic conditionals - no nesting, else clauses, variables, loops, or complex evaluation.

Runtime imports include content from files and URLs in workflow prompts at runtime (unlike compile-time imports). File paths are restricted to the .github folder. Use {{#runtime-import filepath}} or {{#runtime-import? filepath}} for optional imports.

Use {{#runtime-import filepath}} to include file content at runtime. Use {{#runtime-import? filepath}} when the file is optional. All file paths resolve within .github, with or without the .github/ prefix:

---
on: issues
engine: copilot
---
# Code Review Agent
Follow these coding guidelines:
{{#runtime-import coding-standards.md}}
<!-- Same as: {{#runtime-import .github/coding-standards.md}} -->
Review the code changes and provide feedback.

Line range extraction:

# Bug Fix Validator
The original buggy code was (from .github/docs/auth.go):
{{#runtime-import docs/auth.go:45-52}}
Verify the fix addresses the issue.

Optional imports:

# Issue Analyzer
{{#runtime-import? shared-instructions.md}}
Analyze issue #${{ github.event.issue.number }}.

The macro syntax supports HTTP/HTTPS URLs. URLs are not restricted to .github folder and content is cached for 1 hour.

{{#runtime-import https://raw.githubusercontent.com/org/repo/main/checklist.md}}
{{#runtime-import https://example.com/standards.md:10-50}}

Runtime imports automatically strip YAML front matter and HTML/XML comments. GitHub Actions expressions (${{ ... }}) are rejected to prevent template injection or unintended variable expansion.

File paths are restricted to .github to prevent access to arbitrary repository files. Path traversal and absolute paths are rejected:

{{#runtime-import ../src/config.go}} # Error: Relative traversal outside .github
{{#runtime-import /etc/passwd}} # Error: Absolute path not allowed

Fetched URLs are cached for 1 hour per workflow run at /tmp/gh-aw/url-cache/ (keyed by SHA256 hash). The first fetch adds ~500ms–2s latency; subsequent accesses use cached content.

Runtime imports run before other substitutions:

  1. {{#runtime-import}} macros for files and URLs
  2. ${GH_AW_EXPR_*} variable interpolation
  3. {{#if}} conditional rendering

Runtime imports are limited to the .github folder for files, do not support authenticated URL fetches, use a per-run URL cache that does not persist across workflow runs, and interpret line numbers against the raw file before front matter removal.

{{#import filepath}} (without runtime-) is a deprecated body-level shorthand. It normalizes to {{#runtime-import filepath}} at runtime for backward compatibility, but emits deprecation warnings at both compile time and runtime. Use {{#runtime-import}} directly for all new workflows. See Imports for details.

ErrorMessage
File not foundRuntime import file not found: missing.txt
Invalid line rangeInvalid start line 100 for file docs/main.go (total lines: 50)
Path traversalSecurity: Path ../src/main.go must be within .github folder
GitHub Actions macrosFile template.md contains GitHub Actions macros (${{ ... }}) which are not allowed in runtime imports
URL fetch failureFailed to fetch URL https://example.com/file.txt: HTTP 404