Allow steps.*.outputs.* in github-token expressions for same-job safe-output auth - #48101
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
steps.*.outputs.* in github-token expressions for same-job safe-output auth
There was a problem hiding this comment.
Pull request overview
Expands GitHub token expressions to support same-job step outputs.
Changes:
- Allows
steps.*.outputs.*token expressions. - Adds schema and compilation coverage.
- Updates workflow routing metadata.
Show a summary per file
| File | Description |
|---|---|
pkg/workflow/skills_frontmatter.go |
Broadens token validation and error guidance. |
pkg/workflow/skills_frontmatter_test.go |
Tests step-output skill tokens. |
pkg/workflow/github_token_validation_test.go |
Tests affected token configurations. |
pkg/parser/schemas/main_workflow_schema.json |
Expands the token schema. |
pkg/parser/schema_test.go |
Tests schema acceptance. |
.github/skills/agentic-workflows/SKILL.md |
Registers the release workflow prompt. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
pkg/parser/schemas/main_workflow_schema.json:13254
- The new branch rejects valid step/output identifiers containing hyphens, despite the advertised
steps.*.outputs.*support. This repository itself emits expressions such as${{ steps.safe-outputs-app-token.outputs.token }}(pkg/workflow/safe_outputs_env.go:230), so a common producer ID likefetch-tokenstill fails schema validation. Allow-after the first character for both the step ID and output name, and cover that form in the tests.
"pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*|steps\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$",
"description": "GitHub token expression. Accepts a secrets expression (e.g., `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`), a cross-job output expression (e.g., `${{ needs.auth.outputs.token }}`), or a same-job step output expression (e.g., `${{ steps.fetch_token.outputs.token }}`). Pattern details: secret names, job IDs, step IDs, and output names in dot notation match `[A-Za-z_][A-Za-z0-9_]*` (identifiers without hyphens).",
- Files reviewed: 6/6 changed files
- Comments generated: 2
- Review effort level: Medium
|
|
||
| var skillSpecRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@[0-9a-f]{40}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*|steps\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) |
| "pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$", | ||
| "description": "GitHub token expression. Accepts a secrets expression (e.g., `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`) or a job output expression (e.g., `${{ needs.auth.outputs.token }}`). Pattern details: secret names match `[A-Za-z_][A-Za-z0-9_]*`; job IDs and output names in dot notation match `[A-Za-z_][A-Za-z0-9_]*` (identifiers without hyphens).", | ||
| "examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", "${{ needs.auth.outputs.token }}"] | ||
| "pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*|steps\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$", |
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR #48101 does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (50 additions detected, threshold is 100). |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
|
✅ Test Quality Sentinel completed test quality analysis. |
|
✅ PR Code Quality Reviewer completed the code quality review. |
There was a problem hiding this comment.
Review: Allow steps.*.outputs.* in github-token expressions
The change is well-scoped and the implementation is internally consistent — schema regex, Go runtime regexp, tests, and error messages all updated in tandem.
One correctness issue found:
GitHub Actions step IDs allow hyphens (e.g., id: fetch-token), but the new regex segments steps\.[A-Za-z_][A-Za-z0-9_]* and outputs\.[A-Za-z_][A-Za-z0-9_]* only accept underscores. This means ${{ steps.fetch-token.outputs.my-token }} is a valid GitHub expression that will be incorrectly rejected. The fix is to use [A-Za-z_][A-Za-z0-9_-]* for step ID and output name segments (matching the existing needs pattern's same limitation, though both should ideally be fixed).
Pre-existing note (already flagged by prior comments): The skills[].github-token validation shares this same regexp. Skills run in the activation/setup job, not the agent job, so steps.* outputs may not exist in that context — but that's a separate concern already raised in existing review comments.
🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 19.3 AIC · ⌖ 5.89 AIC · ⊞ 5K
|
|
||
| var skillSpecRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@[0-9a-f]{40}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*|steps\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) |
There was a problem hiding this comment.
The new steps branch of the regex uses [A-Za-z_][A-Za-z0-9_]* for the step ID, which rejects hyphens. GitHub Actions step IDs frequently use hyphens (e.g., id: fetch-token), so ${{ steps.fetch-token.outputs.token }} would fail this validation even though it is a perfectly valid GitHub expression.
Consider using [A-Za-z_][A-Za-z0-9_-]* for the step ID segment — consistent with how GitHub Actions allows hyphens in id: fields. The output name pattern could similarly allow hyphens.
The same fix should be applied to the steps pattern in main_workflow_schema.json line 13253 for consistency.
@copilot please address this.
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /tdd and /grill-with-docs — requesting changes on two correctness issues.
📋 Key Themes & Highlights
Key Themes
-
Context mismatch on shared regexp: The single
githubTokenExpressionRegexpnow acceptssteps.*in all token-validation contexts, includingskills[].github-token. Skills run in the activation job before userpre-steps/setup-steps, so acceptingsteps.*there will silently validate tokens that can never resolve at runtime. -
Hyphenated step IDs rejected: GitHub Actions step IDs may legally contain hyphens (e.g.
fetch-token), but the new regex's character class[A-Za-z0-9_]*rejects them. This will cause confusing validation errors for a common naming convention.
Positive Highlights
- ✅ Clean, minimal change — only the regexp and matching test additions, no structural churn
- ✅ Good parity maintenance between JSON schema pattern and Go runtime regexp
- ✅ Tests cover all three usage contexts (safe-outputs, per-output, tools.github)
- ✅ Updated error message examples are helpful and actionable
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 30.1 AIC · ⌖ 4.84 AIC · ⊞ 6.7K
Comment /matt to run again
|
|
||
| var skillSpecRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@[0-9a-f]{40}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*|steps\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) |
There was a problem hiding this comment.
[/tdd] The updated regexp permits steps.* tokens for skills[].github-token, but skills run in the activation job before user pre-steps/setup-steps — so a same-job step output can never be resolved there.
This context mismatch means the new branch in the shared regexp silently accepts an expression that will always fail at runtime for skill installs.
💡 Suggested fix
Split into two regexps:
githubTokenExpressionRegexp(safe-outputs / tools) — permitssecrets.*,needs.*,steps.*skillsGithubTokenExpressionRegexp(skills[]) — permits onlysecrets.*andneeds.*
This makes each context's validator match its actual runtime capabilities.
@copilot please address this.
| "pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$", | ||
| "description": "GitHub token expression. Accepts a secrets expression (e.g., `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`) or a job output expression (e.g., `${{ needs.auth.outputs.token }}`). Pattern details: secret names match `[A-Za-z_][A-Za-z0-9_]*`; job IDs and output names in dot notation match `[A-Za-z_][A-Za-z0-9_]*` (identifiers without hyphens).", | ||
| "examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", "${{ needs.auth.outputs.token }}"] | ||
| "pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*|steps\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$", |
There was a problem hiding this comment.
[/grill-with-docs] The shared github_token $defs is referenced by both safe-outputs.github-token and skills[].github-token. Expanding it to include steps.* inadvertently permits same-job step outputs for skill installs, which run in a context where those steps don't exist.
Consider a separate $defs entry (e.g. github_token_safe_outputs) for the safe-output/tools contexts, and keep the skills entry restricted to secrets.*/needs.* only.
@copilot please address this.
|
|
||
| var skillSpecRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@[0-9a-f]{40}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*|steps\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) |
There was a problem hiding this comment.
[/tdd] Step IDs in GitHub Actions can contain hyphens (e.g. fetch-token), but the regexp character class [A-Za-z_][A-Za-z0-9_]* rejects them. A user writing ${{ steps.fetch-token.outputs.token }} will get a cryptic validation error.
💡 Fix
Change the step-ID character class to [A-Za-z_][A-Za-z0-9_-]* in both the Go regexp and the JSON schema pattern to match valid GitHub Actions step IDs.
Add a test case: ${{ steps.fetch-token.outputs.token }} should pass validation.
@copilot please address this.
There was a problem hiding this comment.
REQUEST_CHANGES — Two correctness bugs and one schema-design issue must be fixed before merge.
### Blocking issues (3)
-
Hyphenated step IDs rejected (
[A-Za-z_][A-Za-z0-9_]*rejects-).${{ steps.fetch-token.outputs.token }}— the canonical real-world form — fails validation. The schema description even concedes this but leaves it unfixed. All tests use underscored IDs, so this gap is invisible in CI. -
steps.*permitted inskills[].github-tokenvia the shared regexp. Skills run in the activation job wheresteps.*context doesn't exist; the expression silently resolves to an empty string, producing an auth failure at install time — after schema validation passes. -
Shared
$defs/github_tokenin the JSON Schema propagates thesteps.*branch to every consumer includingskills[].github-token. The definition needs to be split so only same-job consumers (safe-outputs, tools) get thesteps.*alternative.
🔎 Code quality review by PR Code Quality Reviewer · sonnet46 · 40.9 AIC · ⌖ 5.02 AIC · ⊞ 5.7K
Comment /review to run again
|
|
||
| var skillSpecRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@[0-9a-f]{40}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*|steps\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) |
There was a problem hiding this comment.
steps.* context is invalid for skills[].github-token — this regex change silently allows a pattern that will produce an empty token at runtime.
💡 Details and suggested fix
githubTokenExpressionRegexp is used to validate skills[].github-token (around line 95). Skills are installed in the activation job, which is a separate job from the agent job. The steps.* context map is only populated within the same job, so ${{ steps.fetch_token.outputs.token }} in skills[].github-token will resolve to an empty string at runtime — passing regex validation but then failing silently at install time.
The steps.* branch should only be permitted in contexts where the referencing field and the step producing the token are in the same job (e.g., safe-outputs.github-token, tools.github.github-token). Consider splitting $defs/github_token into two variants:
github_token— secrets + needs (for cross-job contexts like skills)github_token_same_job— secrets + needs + steps (for safe-outputs and tools)
This avoids giving users a false-green at schema-validation time.
|
|
||
| var skillSpecRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@[0-9a-f]{40}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) | ||
| var githubTokenExpressionRegexp = regexp.MustCompile(`^\$\{\{\s*(secrets\.[A-Za-z_][A-Za-z0-9_]*(\s*\|\|\s*secrets\.[A-Za-z_][A-Za-z0-9_]*)*|needs\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*|steps\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*)\s*\}\}$`) |
There was a problem hiding this comment.
Regex rejects hyphenated step IDs, blocking the primary use case this PR adds — ${{ steps.fetch-token.outputs.token }} will fail validation.
💡 Details and suggested fix
GitHub Actions allows hyphens in step IDs. The step-ID segment of the new pattern, [A-Za-z_][A-Za-z0-9_]*, rejects hyphens. Users who write:
- id: fetch-token
uses: ...and then reference ${{ steps.fetch-token.outputs.token }} will get a validation error despite the expression being valid in GitHub Actions. The schema description even notes this limitation ("identifiers without hyphens"), acknowledging it's a gap rather than fixing it.
Fix the character class to allow hyphens in the middle of identifiers:
// Before:
steps\.[A-Za-z_][A-Za-z0-9_]*\.outputs\.[A-Za-z_][A-Za-z0-9_]*
// After (step ID and output name can contain hyphens):
steps\.[A-Za-z_][A-Za-z0-9_-]*\.outputs\.[A-Za-z_][A-Za-z0-9_-]*Same fix is needed in the JSON Schema pattern field in main_workflow_schema.json.
Add a test case with a hyphenated step ID to prevent regression.
| "pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$", | ||
| "description": "GitHub token expression. Accepts a secrets expression (e.g., `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`) or a job output expression (e.g., `${{ needs.auth.outputs.token }}`). Pattern details: secret names match `[A-Za-z_][A-Za-z0-9_]*`; job IDs and output names in dot notation match `[A-Za-z_][A-Za-z0-9_]*` (identifiers without hyphens).", | ||
| "examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", "${{ needs.auth.outputs.token }}"] | ||
| "pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*|steps\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$", |
There was a problem hiding this comment.
Shared $defs/github_token now allows steps.* everywhere it is referenced, including contexts where steps context is unavailable — schema validation will pass for skills[].github-token: ${{ steps.foo.outputs.bar }} even though that will always resolve to an empty string at runtime.
💡 Details and suggested fix
$defs/github_token is reused by:
skills[].github-token(activation job — nosteps.*context)tools.github.github-token(agent job —steps.*available)safe-outputs.github-token(agent job —steps.*available)- Per-output
github-tokenfields (agent job —steps.*available)
Allowing steps.* in the shared definition means users who mistakenly write steps.* in skills[].github-token will get a clean schema validation pass, then a silent runtime auth failure during skill installation.
Split the definition:
"github_token": { "pattern": "...secrets...|...needs..." },
"github_token_same_job": { "pattern": "...secrets...|...needs...|...steps..." }Then use github_token_same_job only in safe-outputs and tools.github refs.
🤖 PR Triage — §30192186368
Why: Extends Next: Confirm latest review state is APPROVED, then fast-track merge.
|
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Addressed in 5026560. |
|
🎉 This pull request is included in a new release. Release: |
safe-outputs.github-tokenacceptedsecrets.*andneeds.*.outputs.*, but rejected same-jobsteps.*.outputs.*. That blocked short-lived token minting inpre-steps/setup-stepsfrom being wired directly intoProcess Safe Outputs.Schema update (
$defs.github_token)main_workflow_schema.jsonto accept:secrets.<NAME>(with||fallback chain)needs.<job>.outputs.<name>steps.<id>.outputs.<name>(new)Runtime/frontmatter validation parity
githubTokenExpressionRegexpinpkg/workflow/skills_frontmatter.goto match the same accepted forms, includingsteps.*.outputs.*.Focused coverage for accepted pattern
steps.*.outputs.*in both:safe-outputs.github-tokengithub-tokensteps.*.outputs.*in:safe-outputstools.github.github-token${{ steps.*.outputs.* }}insafe-outputs.github-tokenfor same-job token minting #47766