Skip to content

Make targeted workflow compilation deterministic - #57745

Merged
pelikhan merged 5 commits into
mainfrom
copilot/fix-gh-aw-compile-issue
Sep 2, 2026
Merged

Make targeted workflow compilation deterministic#57745
pelikhan merged 5 commits into
mainfrom
copilot/fix-gh-aw-compile-issue

Conversation

Copilot AI commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Targeted compilation restored the implicit action-failure expiry to "168", while full compilation disabled it to "0" when no maintenance workflow could enforce expiry. This caused lock-file churn between compile modes.

  • Expiry reconciliation

    • Reuse the existing marker rewrite after targeted compilation.
    • Disable the implicit marker only when agentics-maintenance.yml is absent.
    • Preserve current behavior for custom directories and --no-emit.
  • Regression coverage

    • Verify full and targeted compilation produce identical lock files with:
GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "0"

Branch refresh requested by PR Sous Chef run https://github.com/github/gh-aw/actions/runs/33554808944

Warning

Firewall blocked 1 domain

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

  • github.com

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

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 42.8 AIC · ⌖ 8.75 AIC · ⊞ 9.2K ·
Comment /souschef to run again

Copilot AI and others added 2 commits September 1, 2026 18:43
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix inconsistent lock file output for gh aw compile command Make targeted workflow compilation deterministic Sep 1, 2026
Copilot AI requested a review from pelikhan September 1, 2026 18:48
@pelikhan
pelikhan marked this pull request as ready for review September 1, 2026 19:00
Copilot AI balanced review requested due to automatic review settings September 1, 2026 19:00

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.

🟡 Changes recommended

Reconciliation can overwrite explicit expiry values, misidentify enforcement, and modify the wrong lock file for direct paths.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Aligns targeted workflow compilation with full compilation for implicit action-failure expiry markers.

Changes:

  • Reconciles expiry markers after targeted compilation.
  • Preserves custom-directory and --no-emit behavior.
  • Adds deterministic lock-file regression coverage.
File summaries
File Description
pkg/workflow/maintenance_workflow_expiry.go Adds maintenance enforcement detection helper.
pkg/cli/compile_pipeline.go Runs expiry reconciliation for targeted compilation.
pkg/cli/compile_maintenance_test.go Compares targeted and full lock outputs.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/cli/compile_pipeline.go Outdated
Comment on lines +864 to +867
if !config.NoEmit && config.WorkflowDir == "" {
if gitRoot, err := gitutil.FindGitRoot(); err == nil {
absWorkflowDir := getAbsoluteWorkflowDir(getWorkflowsDir(), gitRoot)
workflow.DisableDefaultActionFailureExpiryMarkersIfUnenforced(workflowDataList, absWorkflowDir)

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.

Fixed in 85e8e16: reconciliation now uses the actual emitted lock file path collected per compiled workflow (fileResult.lockFile) instead of reconstructing workflowDir + WorkflowID, so direct-path targets outside .github/workflows are patched correctly and unrelated lock files sharing the same workflow ID are left untouched.

Comment on lines +12 to +13
if _, err := os.Stat(filepath.Join(workflowDir, "agentics-maintenance.yml")); os.IsNotExist(err) {
disableDefaultActionFailureExpiryMarkers(workflowDataList, workflowDir)

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.

Fixed in 85e8e16: isActionFailureExpiryEnforced now also checks repoConfig.Maintenance.IsJobDisabled("close-expired-entities") (and maintenance: false) in addition to file existence, so a maintenance file that omits the sweeper no longer preserves the marker.

Comment on lines +12 to +13
if _, err := os.Stat(filepath.Join(workflowDir, "agentics-maintenance.yml")); os.IsNotExist(err) {
disableDefaultActionFailureExpiryMarkers(workflowDataList, workflowDir)

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.

Fixed in 85e8e16: DisableDefaultActionFailureExpiryMarkersIfUnenforced now takes *RepoConfig and returns early when repoConfig.IsActionFailureIssueExpiresExplicit() is true, so an explicit maintenance.action_failure_issue_expires value is never rewritten. Added TestCompileSpecificFiles_PreservesExplicitActionFailureExpiry to cover this.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

No ADR enforcement needed: PR does not have the implementation label and has ≤100 new lines of code in business logic directories.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer failed during the skills-based review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel failed during test quality analysis.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Ponytail Reviewer. Review the logs for details.

Warning

Firewall blocked 1 domain

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

  • ab.chatgpt.com

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

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by Ponytail Reviewer for #57745

@github-actions github-actions Bot 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.

Found one simplification worth cutting: the new wrapper adds indirection for a condition that already has a single implementation and a single caller. net: -15 lines possible.

Warning

Firewall blocked 1 domain

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

  • ab.chatgpt.com

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

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by ✂️ Ponytail Reviewer for #57745 · codex · mai10 · 3.37 AIC · ⌖ 0.504 AIC · ⊞ 14K
Comment /ponytail to run again

Comment thread pkg/cli/compile_pipeline.go Outdated
// Note: Maintenance workflow generation requires parsing all workflows in the directory
// to check for expires fields, so we skip it when compiling specific files to avoid
// unnecessary parsing and warnings from unrelated workflows
if !config.NoEmit && config.WorkflowDir == "" {

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.

pkg/cli/compile_pipeline.go:864: yagni: one-call helper for a tiny branch. Inline the file-existence check in runPostProcessing and call disableDefaultActionFailureExpiryMarkers directly.

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.

In 85e8e16 the branch grew beyond a tiny file-existence check (it now also verifies close-expired-entities isn't disabled and that maintenance isn't explicitly off), so it's kept as a named helper (isActionFailureExpiryEnforced) rather than inlined.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-09-01T19:42:14Z
review_event: REQUEST_CHANGES
top_themes:
  - targeted compile rewrites the wrong lock file for direct-path inputs
  - maintenance file existence does not prove expiry enforcement
  - explicit action-failure expiry can be clobbered to zero
files_reviewed:
  - pkg/cli/compile_maintenance_test.go
  - pkg/cli/compile_pipeline.go
  - pkg/workflow/maintenance_workflow_expiry.go
comment_count: 3

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 38.8 AIC · ⌖ 7.48 AIC · ⊞ 23.5K ·
Comment /review to run again

@github-actions github-actions Bot 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.

This fix heads in the right direction, but it still leaves targeted compilation nondeterministic in edge cases.

Blocking themes
  • The new reconciliation still assumes every targeted compile is operating on .github/workflows, so direct-path inputs can miss their emitted lock file or patch the wrong one.
  • Enforcement is inferred from agentics-maintenance.yml existing, but that file can exist with the expiry sweeper disabled.
  • The marker rewrite still cannot distinguish the implicit 168h default from an explicit configured expiry, so it can erase a user's opt-in configuration.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 38.8 AIC · ⌖ 7.48 AIC · ⊞ 23.5K
Comment /review to run again

// action-failure expiry markers when no maintenance workflow exists to enforce them.
// The generated maintenance workflow always includes the global expiry sweeper.
func DisableDefaultActionFailureExpiryMarkersIfUnenforced(workflowDataList []*WorkflowData, workflowDir string) {
if _, err := os.Stat(filepath.Join(workflowDir, "agentics-maintenance.yml")); os.IsNotExist(err) {

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.

File existence is the wrong enforcement check. agentics-maintenance.yml can already exist while maintenance.disabled_jobs removes close-expired-entities, which means no expiry sweeper is generated even though this helper preserves a positive expiration marker.

💡 Base the decision on whether maintenance would actually emit the expiry jobs

Use the same repo-config logic that scanWorkflowsForExpires and maintenance generation use, or pass enough provenance into this helper to tell whether the close-expired jobs are enabled. Otherwise targeted compile still advertises expiration that no workflow will ever enforce.

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.

Fixed in 85e8e16: isActionFailureExpiryEnforced now checks repoConfig.Maintenance.IsJobDisabled("close-expired-entities") and maintenance: false in addition to file existence. Covered by TestCompileSpecificFiles_DisablesExpiryWhenCloseExpiredJobDisabled.

Comment thread pkg/cli/compile_pipeline.go Outdated
if !config.NoEmit && config.WorkflowDir == "" {
if gitRoot, err := gitutil.FindGitRoot(); err == nil {
absWorkflowDir := getAbsoluteWorkflowDir(getWorkflowsDir(), gitRoot)
workflow.DisableDefaultActionFailureExpiryMarkersIfUnenforced(workflowDataList, absWorkflowDir)

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.

WorkflowDir == "" is not enough to identify the emitted lock file here: targeted compilation accepts direct paths outside .github/workflows, but this helper always rewrites <gitRoot>/.github/workflows/<workflowID>.lock.yml. That means custom/foo.md still keeps its stale marker while an unrelated .github/workflows/foo.lock.yml can be mutated instead.

💡 Reconcile the actual emitted lock path instead of reconstructing one from the workflow ID

The compile path already knows each generated lock filename (compileWorkflowFileResult.lockFile). Thread those concrete paths into post-processing, or filter targeted reconciliations to workflows whose resolved source lives under the default workflow directory before touching any lock file. That keeps the full compile and targeted compile outputs aligned without risking cross-directory corruption.

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.

Fixed in 85e8e16: compileSpecificFiles now accumulates compiledLockFiles from fileResult.lockFile and threads it into runPostProcessing/DisableDefaultActionFailureExpiryMarkersIfUnenforced, so the actual emitted lock path is reconciled. Covered by TestCompileSpecificFiles_PatchesDirectPathOutsideDefaultWorkflowDir.

// The generated maintenance workflow always includes the global expiry sweeper.
func DisableDefaultActionFailureExpiryMarkersIfUnenforced(workflowDataList []*WorkflowData, workflowDir string) {
if _, err := os.Stat(filepath.Join(workflowDir, "agentics-maintenance.yml")); os.IsNotExist(err) {
disableDefaultActionFailureExpiryMarkers(workflowDataList, workflowDir)

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.

This helper still rewrites every positive GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS value to "0", so a targeted compile can silently erase an explicit maintenance.action_failure_issue_expires opt-in when no maintenance file is present yet. Full compile keeps that explicit value because it generates maintenance, so the deterministic-output bug remains and user configuration gets discarded.

💡 Only disable the implicit default marker, never an explicit configured expiry

The helper needs provenance, not just the rendered lock text. Thread the repo config or an explicit/implicit flag through post-processing, then rewrite only lock files whose expiration came from the implicit 168-hour default. Add a regression that compiles with aw.json setting maintenance.action_failure_issue_expires and verifies targeted compile preserves that explicit value.

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.

Fixed in 85e8e16: DisableDefaultActionFailureExpiryMarkersIfUnenforced now accepts *RepoConfig and skips rewriting entirely when repoConfig.IsActionFailureIssueExpiresExplicit() is true. Added TestCompileSpecificFiles_PreservesExplicitActionFailureExpiry (aw.json with maintenance.action_failure_issue_expires: 48, no pre-existing maintenance file) to verify targeted compile preserves the explicit value.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the current reviewer feedback and failing checks on this PR, refresh the branch if needed, and run the pr-finisher skill before handing it back to maintainers.

Please incorporate the requested fixes, rerun the necessary validation, and use the pr-finisher skill before handing the PR back to maintainers.

Warning

Firewall blocked 1 domain

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

  • github.com

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

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 42.8 AIC · ⌖ 8.75 AIC · ⊞ 9.2K ·
Comment /souschef to run again

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

Copilot AI commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please address the current reviewer feedback and failing checks on this PR, refresh the branch if needed, and run the pr-finisher skill before handing it back to maintainers.

  • Copilot rev...

Addressed all outstanding review feedback in 85e8e16: reconciliation now patches the actual emitted lock file path per compiled workflow (not reconstructed from workflowDir+WorkflowID), checks whether the close-expired-entities maintenance job is actually enabled rather than only checking file existence, and preserves an explicitly configured maintenance.action_failure_issue_expires instead of resetting it to "0". Added regression tests for all three scenarios. Replied to each review thread individually. The lint-go-golangci and agentic-workflow-review failures on the prior head were caused by a transient network error fetching a Go module dependency (proxy.golang.org stream error) and an unrelated agentic review workflow run, not by anything in this PR's code — those checks need to be re-triggered by a maintainer since my pushes don't re-run CI.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Category: bug | Risk: medium | Score: 68 (impact 32, urgency 22, quality 14) | Action: fast_track | CI: failure | Workflow compile determinism fix; current CI is failing and this needs prompt follow-up.

Generated by 🔧 PR Triage Agent · copilot · mai10 · 14.9 AIC · ⌖ 2.53 AIC · ⊞ 16.1K ·

@pelikhan
pelikhan merged commit 1a3a3f7 into main Sep 2, 2026
32 checks passed
@pelikhan
pelikhan deleted the copilot/fix-gh-aw-compile-issue branch September 2, 2026 02:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants