Skip to content

Treat expression-backed engine.model as omitted when empty#46777

Merged
pelikhan merged 10 commits into
mainfrom
copilot/treat-empty-model-as-omitted
Jul 20, 2026
Merged

Treat expression-backed engine.model as omitted when empty#46777
pelikhan merged 10 commits into
mainfrom
copilot/treat-empty-model-as-omitted

Conversation

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

When engine.model (or top-level model) is a GitHub Actions expression like ${{ vars.HOLISTIC_REVIEW_MODEL }} and the variable is unset, the expression resolves to "" at runtime. Previously that empty string was propagated directly to the engine, making the variable mandatory rather than optional.

Changes

  • compilerenv/manager.go — new BuildModelOverrideExpressionWithUserOverride(userExprInner, primaryVar, enterpriseDefaultVar, builtinFallback) that prepends a user expression to the standard org/enterprise/built-in fallback chain
  • expression_patterns.go — new extractExpressionInner(s) helper with an explicit length guard that strips ${{ / }} delimiters safely
  • copilot_engine_execution.go, claude_engine.go, codex_engine.go — when model is a pure expression (isExpression() == true), wrap it instead of passing it through directly:
# Before — empty when vars.HOLISTIC_REVIEW_MODEL is unset
COPILOT_MODEL: ${{ vars.HOLISTIC_REVIEW_MODEL }}

# After — falls back through normal resolution chain
COPILOT_MODEL: ${{ vars.HOLISTIC_REVIEW_MODEL || vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}

Only pure expressions are affected (${{ ... }}). Literal model values (e.g. gpt-4) and partial expressions (e.g. provider/${{ vars.MODEL }}) retain existing behavior.

Four internal workflows using ${{ needs.activation.outputs.model_size }} as their model were recompiled to pick up the updated output.


run: https://github.com/github/gh-aw/actions/runs/29735453773

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · 7.59 AIC · ⌖ 5.86 AIC · ⊞ 5.4K ·
Comment /souschef to run again


Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · 7.16 AIC · ⌖ 5.51 AIC · ⊞ 5.4K ·
Comment /souschef to run again

When engine.model or model is a pure GitHub Actions expression (e.g.,
${{ vars.MY_MODEL }}), it may resolve to an empty string at runtime if
the variable is unset. Previously this empty value was propagated directly
to the engine, causing failures.

This change wraps pure expression-backed model values with the normal
fallback chain for Copilot, Claude, and Codex engines, so that an empty
resolution falls back to org/enterprise variables and the built-in default.

For Copilot:  ${{ vars.MY_MODEL || vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
For Claude:   ${{ inputs.model || vars.GH_AW_MODEL_AGENT_CLAUDE || vars.GH_AW_DEFAULT_MODEL_CLAUDE || '' }}
For Codex:    ${{ inputs.model || vars.GH_AW_MODEL_AGENT_CODEX || vars.GH_AW_DEFAULT_MODEL_CODEX || 'gpt-5.4' }}

Changes:
- pkg/workflow/compilerenv/manager.go: add BuildModelOverrideExpressionWithUserOverride
- pkg/workflow/expression_patterns.go: add extractExpressionInner helper
- pkg/workflow/copilot_engine_execution.go: wrap expressions in addCopilotModelEnv
- pkg/workflow/claude_engine.go: wrap expressions in model env assignment
- pkg/workflow/codex_engine.go: wrap expressions in model env assignment
- Updated/added tests
- Recompiled 4 internal workflows using expression-backed models

Closes #46499

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix handling of empty engine model in GitHub Actions Treat expression-backed engine.model as omitted when empty Jul 20, 2026
Copilot AI requested a review from pelikhan July 20, 2026 09:47
@pelikhan
pelikhan marked this pull request as ready for review July 20, 2026 09:48
Copilot AI review requested due to automatic review settings July 20, 2026 09:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes #46499 by adding standard model fallbacks when a pure model expression resolves empty.

Changes:

  • Adds expression extraction and fallback-chain construction.
  • Applies fallback resolution to Copilot, Claude, and Codex.
  • Updates tests and generated workflows.

Composite expressions are currently misclassified as pure expressions and rewritten unexpectedly.

Show a summary per file
File Description
pkg/workflow/model_env_vars_test.go Updates model fallback expectations.
pkg/workflow/expression_patterns.go Adds expression-content extraction.
pkg/workflow/copilot_engine_execution.go Adds Copilot expression fallbacks.
pkg/workflow/compilerenv/manager.go Builds combined fallback expressions.
pkg/workflow/compilerenv/manager_test.go Tests fallback construction.
pkg/workflow/codex_engine.go Adds Codex expression fallbacks.
pkg/workflow/claude_engine.go Adds Claude expression fallbacks.
.github/workflows/test-quality-sentinel.lock.yml Regenerates Copilot model configuration.
.github/workflows/daily-doc-healer.lock.yml Regenerates Claude model configuration.
.github/workflows/daily-caveman-optimizer.lock.yml Regenerates Claude model configuration.
.github/workflows/daily-cache-strategy-analyzer.lock.yml Regenerates Codex model configuration.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 11/11 changed files
  • Comments generated: 4
  • Review effort level: Medium

Comment on lines +595 to +597
if isExpression(workflowData.Model) {
innerExpr := extractExpressionInner(workflowData.Model)
expr := compilerenv.BuildModelOverrideExpressionWithUserOverride(innerExpr, modelEnvVar, compilerenv.DefaultModelCopilot, constants.CopilotBYOKDefaultModel)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by the later fallback-env implementation and now covered explicitly in a915121: composite values are kept verbatim in COPILOT_MODEL, and the runtime uses separate GH_AW_MODEL_FALLBACK when the resolved model is empty.

Comment thread pkg/workflow/claude_engine.go Outdated
Comment on lines +480 to +482
if isExpression(workflowData.Model) {
innerExpr := extractExpressionInner(workflowData.Model)
isDetectionJob := workflowData.SafeOutputs == nil

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a915121: Claude keeps composite values like ${{ inputs.provider }}/${{ inputs.model }} unchanged, and this pass adds the matching regression test in pkg/workflow/model_env_vars_test.go.

Comment thread pkg/workflow/codex_engine.go Outdated
Comment on lines +493 to +495
if isExpression(workflowData.Model) {
innerExpr := extractExpressionInner(workflowData.Model)
expr := compilerenv.BuildModelOverrideExpressionWithUserOverride(innerExpr, modelEnvVar, compilerenv.DefaultModelCodex, constants.CodexDefaultModel)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by the current fallback-env path and confirmed in a915121: composite interpolation stays in the original model env value, while GH_AW_MODEL_FALLBACK is only used when the runtime sees an empty resolved model.

Comment thread pkg/workflow/compilerenv/manager.go Outdated
// fallback chain. Use this when engine.model or model is a GitHub Actions expression that
// may resolve to an empty string (e.g. ${{ vars.MY_MODEL }} when the variable is unset).
// userExprInner is the raw content between "${{" and "}}" of the user expression.
// When builtinFallback is empty the expression ends with || ”.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already satisfied on the current branch and still true in a915121: the empty fallback is '', and pkg/workflow/compilerenv/manager_test.go asserts that exact string.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add a fallback env var with the expression that can be used in JavaScript if the model expression resolves to empty. That way it handles any syntax

Copilot AI and others added 2 commits July 20, 2026 10:20
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add a fallback env var with the expression that can be used in JavaScript if the model expression resolves to empty. That way it handles any syntax

Addressed in 0ce2115 (with follow-up fix in f2740c3). The runtime now keeps the configured model expression intact and adds GH_AW_MODEL_FALLBACK, which the shipped JS harness/driver paths use when the resolved model is empty.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

[pr-sous-chef] @copilot please run the pr-finisher skill, address the latest maintainer feedback, and rerun checks once the branch is ready.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · 7.59 AIC · ⌖ 5.86 AIC · ⊞ 5.4K ·
Comment /souschef to run again

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

[pr-sous-chef] @copilot please run the pr-finisher skill, address the latest maintainer feedback, and rerun checks once the branch is ready.

...

Latest maintainer feedback was already covered by 0ce2115 and f2740c3; this pass did not require additional code changes. I reran local validation (make agent-report-progress, make test, make recompile) and the branch is clean locally. GitHub CI on the current head still needs a maintainer re-trigger from GitHub before merge.

Copilot AI requested a review from gh-aw-bot July 20, 2026 10:57
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot merge main and recompile

…treat-empty-model-as-omitted

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@copilot merge main and recompile

Addressed in 9c01027. I merged the latest main into the branch, ran make recompile, make test, and make agent-report-progress, and pushed the updated branch.

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, verify the latest maintainer-requested main merge/recompile is fully reflected on this branch, address any remaining unresolved review feedback, and rerun ...

Addressed in 00216ae. The branch still reflects the main merge/recompile from 9c01027; I fixed the remaining JS typecheck failure on top, reran local validation (make build-js, make test-impacted-js BASE_REF=4972443c609f2be21ab2e4421d06ccf93e98eba7, make fmt, make lint, make test-unit, make test, make recompile, make agent-report-progress), and the branch is ready for a maintainer CI re-trigger.

@copilot fix https://github.com/github/gh-aw/actions/runs/29741214191/job/88352169803#step:4:1

Fixed in 00216ae. The failing impacted-js-tests step was the new model_fallback.cjs helper missing a logger type annotation; local make build-js and make test-impacted-js BASE_REF=4972443c609f2be21ab2e4421d06ccf93e98eba7 now pass.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟡 medium
Priority Score 65 / 100
Action fast_track

Score breakdown: impact 30 + urgency 20 + quality 15

Rationale: Empty expression for engine.model propagates """ to engine, making optional variables mandatory. Breaks workflows relying on unset vars.*`. Focused change (16 files), non-draft. CI in progress. Expedited review recommended once CI completes.

Run §29743972799

Generated by 🔧 PR Triage Agent · 47.4 AIC · ⌖ 5.32 AIC · ⊞ 5.6K ·

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@copilot fix https://github.com/github/gh-aw/actions/runs/29741214191/job/88352169803#step:4:1

Fixed on top of that run in 00216ae. The failing step was the JS typecheck for model_fallback.cjs; the current branch now passes local make build-js and make test-impacted-js BASE_REF=origin/main.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review feedback, and rerun checks once the branch is up to date.

Unresolved review threads:

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · 8.75 AIC · ⌖ 14.8 AIC · ⊞ 6.9K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address unresolved review feedback, and rerun checks once the branch is up to date.

Unresolved review threads:...

Addressed in a915121. I verified the branch is up to date and mergeable, added a Claude composite-expression regression test, and reran make fmt, go test ./pkg/workflow -run '^TestExpressionModelUsesEnvVar$', make lint, make test-unit, make test, make recompile, make agent-report-progress, and final review validation. CI on this new head is still stale until a maintainer re-triggers it.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot handle evals job

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@copilot handle evals job

Addressed in 1f3575b. Evals setup/parse now preserve expression-backed GH_AW_EVALS_MODEL, add GH_AW_MODEL_FALLBACK for empty runtime resolution, use that fallback in run_evals.cjs, and recompile the affected lock files.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review feedback on the current head, and summarize the exact next maintainer action after checks are green.

Unresolved review feedback:

Run: https://github.com/github/gh-aw/actions/runs/29765403629

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · 9.94 AIC · ⌖ 4.26 AIC · ⊞ 6.9K ·
Comment /souschef to run again

@pelikhan
pelikhan merged commit 7666650 into main Jul 20, 2026
1 check failed
@pelikhan
pelikhan deleted the copilot/treat-empty-model-as-omitted branch July 20, 2026 18:03
Copilot stopped work on behalf of gh-aw-bot due to an error July 20, 2026 18:03
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Treat an empty expression-based engine.model as omitted

4 participants