Skip to content

Split AWF helpers into focused modules - #51154

Merged
pelikhan merged 6 commits into
mainfrom
copilot/file-diet-refactor-awf-helpers-again
Aug 7, 2026
Merged

Split AWF helpers into focused modules#51154
pelikhan merged 6 commits into
mainfrom
copilot/file-diet-refactor-awf-helpers-again

Conversation

Copilot AI commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

pkg/workflow/awf_helpers.go had grown into a monolithic AWF implementation mixing command assembly, env filtering, ARC/DinD handling, digest lookup, and version gates. This refactor separates those responsibilities while keeping the public API unchanged.

  • Module split

    • Moved command construction into awf_command_builder.go
    • Moved env exclusion and max-AI-credit helpers into awf_env.go
    • Moved ARC/DinD path, chroot, and image digest helpers into awf_arc_dind.go
    • Moved AWF version capability checks into awf_feature_flags.go
    • Trimmed awf_helpers.go to shared constants, config type, and small scaffolding
  • Coverage

    • Added focused tests for env helper edge cases introduced by the split
    • Existing AWF command/config tests continue to exercise the preserved APIs
command := BuildAWFCommand(AWFCommandConfig{...})
args := BuildAWFArgs(AWFCommandConfig{...})
excluded := ComputeAWFExcludeEnvVarNames(workflowData, coreSecrets)

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.6 AIC · ⊞ 8.4K ·
Comment /souschef to run again

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Triage Result

Category: refactor · Risk: low · Priority: low (score 22/100 — impact 10, urgency 5, quality 7)
Recommended action: defer

Notes: Early-stage WIP (0 diff yet), draft, agent still forming a plan. CI: copilot check in progress. Revisit once implementation lands and diff is non-trivial.
Batch: wip-early (grouped with other early-WIP copilot PRs).

Generated by 🔧 PR Triage Agent · auto · 31.2 AIC · ⌖ 2.44 AIC · ⊞ 7.9K ·

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor pkg/workflow/awf_helpers.go into smaller files Split AWF helpers into focused modules Aug 7, 2026
Copilot AI requested a review from pelikhan August 7, 2026 18:46
@pelikhan
pelikhan marked this pull request as ready for review August 7, 2026 18:46
Copilot AI balanced review requested due to automatic review settings August 7, 2026 18:46

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

Splits the monolithic AWF helper implementation into focused modules while preserving existing APIs.

Changes:

  • Separates command, environment, ARC/DinD, and feature-gate logic.
  • Retains shared AWF scaffolding in awf_helpers.go.
  • Adds two environment-helper edge-case tests.
Show a summary per file
File Description
pkg/workflow/awf_helpers.go Retains shared configuration and scaffolding.
pkg/workflow/awf_command_builder.go Houses AWF command and argument assembly.
pkg/workflow/awf_env.go Houses environment filtering and credit helpers.
pkg/workflow/awf_env_test.go Adds environment-helper edge-case tests.
pkg/workflow/awf_arc_dind.go Houses ARC/DinD and digest helpers.
pkg/workflow/awf_feature_flags.go Houses AWF version capability checks.

Review details

Tip

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

  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +4 to +6
// Command assembly, environment filtering, ARC/DinD handling, and feature gates
// live in awf_command_builder.go, awf_env.go, awf_arc_dind.go, and
// awf_feature_flags.go respectively.
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Design Decision Gate 🏗️. Review the logs for details.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Test Quality Sentinel. Review the logs for details.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Matt Pocock Skills Reviewer. Review the logs for details.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality 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.

@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.

Clean refactor: code moved verbatim into focused modules (awf_command_builder.go, awf_env.go, awf_arc_dind.go, awf_feature_flags.go) with no logic changes. Public API is unchanged and tests are included. LGTM.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 28.9 AIC · ⊞ 5.4K

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel 🧪 Report

Score: 100/100 ✅ Excellent

Summary

This PR refactors AWF helper modules with focused, high-quality test coverage. Two tests were added covering critical edge cases and defensive guards.

Test Coverage Analysis

Test Classification Coverage Notes
TestInjectMaxAICreditsExpressionWithoutMaxRunsLeavesJSONUnchanged Design Test High Tests edge case: missing "maxRuns" field in JSON config; verifies graceful degradation
TestApplyDefaultMaxAICreditsEnvToMapHandlesNilMap Design Test High Tests defensive guard: nil-safety; prevents panic on invalid input

Quality Signals

  • Design Tests: 2/2 (100%) — Both tests verify behavioral contracts and guard conditions
  • Edge-Case Coverage: 2/2 (100%) — Includes missing field and nil-input scenarios
  • Test-to-Production Ratio: 25 lines test / 191 lines production ≈ 0.13 (13% — well below 2:1 threshold)
  • No Violations: ✓ Proper build tags, ✓ No forbidden mock libraries
  • Implementation Tests: 0/2 (0%) — All tests target design invariants, not implementation details

Scoring Details

design_tests_pct:     100% × 40 pts = 40
edge_cases_pct:       100% × 30 pts = 30
duplicate_penalty:    0 clusters = +20 pts
inflation_penalty:    ratio 0.13 < 2.0 = +10 pts
───────────────────────────────────────
Final Score: 100/100

Conclusion

APPROVED: Test quality is excellent. Both tests target critical behavioral contracts (JSON transformation and nil-safety guards) with no redundancy. The implementation-to-test ratio is minimal and appropriate for refactoring that primarily moves code rather than changing behavior.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 14.9 AIC · ⊞ 7.7K ·
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.

✅ Test Quality Sentinel: 100/100. 0% implementation tests (threshold: 30%). Excellent test coverage targeting critical behavioral contracts: JSON transformation edge case and nil-safety guard. No redundancy.

… modules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate - ADR Required

This PR makes significant changes to core business logic (1059 new lines in pkg/workflow/) but does not have a linked Architecture Decision Record (ADR).

Draft ADR committed to branch: docs/adr/51154-split-awf-helpers-into-focused-modules.md - please review and complete it before merging.

This PR cannot merge until an ADR is linked in the PR body.

What to do next:

  1. Review the draft ADR committed to your branch - it was generated from the PR diff
  2. Complete missing sections - add context the AI could not infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding: ADR: ADR-51154: Split AWF Helpers into Focused Single-Responsibility Modules

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

Why ADRs Matter:
ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

Michael Nygard ADR Format - required sections:

  • Context: What is the problem? What forces are at play?
  • Decision: What did you decide? Why?
  • Alternatives Considered: What else could have been done?
  • Consequences: What are the trade-offs (positive and negative)?

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 71.7 AIC · ⊞ 4.4K ·
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.

Skills-Based Review 🧠

Applied /codebase-design and /tdd — requesting changes on a correctness gap and structural issues introduced alongside the split.

📋 Key Themes & Highlights

Key Themes

  1. Nil-safety regressionComputeAWFExcludeEnvVarNames accesses workflowData.ParsedTools and workflowData.Tools without guarding workflowData != nil (line 108–113 of awf_env.go). The pre-existing HasMCPServers call just above it already tolerates nil, making this inconsistency easy to miss.

  2. Four-way fmt.Sprintf duplicationBuildAWFCommand (lines 289–398) duplicates the same format string four times, differing only in whether PathSetup / configFileSetup appear. Any preamble change must be applied four times.

  3. Test split is incompleteawf_helpers_test.go (2,700 lines) was not split alongside the production code. awf_env_test.go adds only 2 tests for 191 lines of new code, and awf_command_builder.go, awf_feature_flags.go, and awf_arc_dind.go have no companion test files.

  4. BuildAWFCommand function length — at 260+ lines the function is doing command assembly, probe generation, config-file creation, and max-AI-credits injection all in one place. A builder struct would make each part independently testable.

Positive Highlights

  • ✅ Clean module boundaries: command builder, env filtering, ARC/DinD, and feature flags are now clearly separated
  • awf_feature_flags.go is a model of deep-module design: thin interface over awfVersionAtLeast, every capability check in one place
  • ✅ Public API is unchanged — callers need no updates
  • ✅ Good doc comments on all new public functions

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 68 AIC · ⊞ 7.1K
Comment /matt to run again

Comment thread pkg/workflow/awf_env.go
}

// GitHub MCP server token is always a secret when the GitHub tool is present.
if hasGitHubTool(workflowData.ParsedTools) {

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.

[/tdd] Missing nil guard on workflowData — direct field access will panic if called with nil.

workflowData.ParsedTools (line 108) and workflowData.Tools (line 113) are accessed directly without first checking workflowData != nil. Every call site in the existing tests passes a non-nil struct, so this gap is invisible in CI but reachable if a caller passes nil (as the existing HasMCPServers guard on line 103 already assumes is possible).

💡 Suggested fix
// After line 105 (HasMCPServers block), guard the next two accesses:
if workflowData != nil {
    if hasGitHubTool(workflowData.ParsedTools) {
        addUnique("GITHUB_MCP_SERVER_TOKEN")
    }
    for varName := range collectHTTPMCPHeaderSecrets(workflowData.Tools) {
        addUnique(varName)
    }
}

And add a test:

func TestComputeAWFExcludeEnvVarNamesNilWorkflowData(t *testing.T) {
    assert.NotPanics(t, func() {
        names := ComputeAWFExcludeEnvVarNames(nil, []string{"CORE_TOKEN"})
        assert.Equal(t, []string{"CORE_TOKEN"}, names)
    })
}

@copilot please address this.

Comment on lines +289 to +398
if config.PathSetup != "" && configFileSetup != "" {
command = fmt.Sprintf(`set -o pipefail
%s
%s
%s
%s
%s
%s
%s
%s
%s
%s %s %s %s %s \
-- %s 2>&1 | tee -a %s`,
writeAgentCLIStartMs,
config.PathSetup,
preCreateLog,
configFileSetup,
modelsJSONPathExport,
arcDindDockerHostProbe,
arcDindPrefixProbe,
toolCacheMountProbe,
awfShellcheckDirective,
awfCommand,
expandableArgs,
toolCacheMountRef,
arcDindDockerHostRef,
shellJoinArgs(awfArgs),
shellWrappedCommand,
shellEscapeArg(config.LogFile))
} else if config.PathSetup != "" {
// Include path setup before AWF command (runs on host before AWF)
command = fmt.Sprintf(`set -o pipefail
%s
%s
%s
%s
%s
%s
%s
%s
%s %s %s %s %s \
-- %s 2>&1 | tee -a %s`,
writeAgentCLIStartMs,
config.PathSetup,
preCreateLog,
modelsJSONPathExport,
arcDindDockerHostProbe,
arcDindPrefixProbe,
toolCacheMountProbe,
awfShellcheckDirective,
awfCommand,
expandableArgs,
toolCacheMountRef,
arcDindDockerHostRef,
shellJoinArgs(awfArgs),
shellWrappedCommand,
shellEscapeArg(config.LogFile))
} else if configFileSetup != "" {
command = fmt.Sprintf(`set -o pipefail
%s
%s
%s
%s
%s
%s
%s
%s
%s %s %s %s %s \
-- %s 2>&1 | tee -a %s`,
writeAgentCLIStartMs,
preCreateLog,
configFileSetup,
modelsJSONPathExport,
arcDindDockerHostProbe,
arcDindPrefixProbe,
toolCacheMountProbe,
awfShellcheckDirective,
awfCommand,
expandableArgs,
toolCacheMountRef,
arcDindDockerHostRef,
shellJoinArgs(awfArgs),
shellWrappedCommand,
shellEscapeArg(config.LogFile))
} else {
command = fmt.Sprintf(`set -o pipefail
%s
%s
%s
%s
%s
%s
%s
%s %s %s %s %s \
-- %s 2>&1 | tee -a %s`,
writeAgentCLIStartMs,
preCreateLog,
modelsJSONPathExport,
arcDindDockerHostProbe,
arcDindPrefixProbe,
toolCacheMountProbe,
awfShellcheckDirective,
awfCommand,
expandableArgs,
toolCacheMountRef,
arcDindDockerHostRef,
shellJoinArgs(awfArgs),
shellWrappedCommand,
shellEscapeArg(config.LogFile))
}

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.

[/codebase-design] Four near-identical fmt.Sprintf blocks in BuildAWFCommand — a maintenance hazard.

The four branches (lines 289–398) differ only in whether config.PathSetup and configFileSetup are included as lines in the format string. Any future change to the shell preamble (e.g. a new probe variable) must be applied in all four places. This is exactly the kind of duplication that causes divergence bugs.

💡 Suggested approach

Build the preamble lines as a []string slice and join with \n, conditionally inserting config.PathSetup and configFileSetup:

lines := []string{"set -o pipefail", writeAgentCLIStartMs}
if config.PathSetup != "" {
    lines = append(lines, config.PathSetup)
}
lines = append(lines, preCreateLog)
if configFileSetup != "" {
    lines = append(lines, configFileSetup)
}
lines = append(lines,
    modelsJSONPathExport,
    arcDindDockerHostProbe,
    arcDindPrefixProbe,
    toolCacheMountProbe,
    awfShellcheckDirective,
    fmt.Sprintf("%s %s %s %s %s \\\n  -- %s 2>&1 | tee -a %s",
        awfCommand, expandableArgs, toolCacheMountRef,
        arcDindDockerHostRef, shellJoinArgs(awfArgs),
        shellWrappedCommand, shellEscapeArg(config.LogFile)),
)
command = strings.Join(lines, "\n")

This reduces the four branches to zero and makes the ordering trivially auditable.

@copilot please address this.

assert.NotPanics(t, func() {
applyDefaultMaxAICreditsEnvToMap(nil, nil)
})
}

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.

[/tdd] New awf_env_test.go covers only 2 of the 191 lines in awf_env.go — the highest-risk paths are untested.

ComputeAWFExcludeEnvVarNames (84 lines, public API) has no dedicated test in this new file; injectMaxAICreditsExpression is tested only for the missing-maxRuns branch; and addCliProxyGHTokenToEnv has zero coverage here. The test split should mirror the code split.

💡 What to add to awf_env_test.go

Move or copy the relevant cases from awf_helpers_test.go (e.g. TestComputeAWFExcludeEnvVarNames, TestInjectMaxAICreditsExpression) into awf_env_test.go, and add:

  • injectMaxAICreditsExpression with a valid maxRuns JSON to verify the happy-path insertion
  • ComputeAWFExcludeEnvVarNames with workflowData == nil (see nil-guard comment on line 108)
  • addCliProxyGHTokenToEnv with both CLI-proxy-enabled and disabled firewall configs

@copilot please address this.

// providing a consistent and maintainable approach to AWF integration.
// Command assembly, environment filtering, ARC/DinD handling, and feature gates
// live in awf_command_builder.go, awf_env.go, awf_arc_dind.go, and
// awf_feature_flags.go respectively.

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.

[/codebase-design] The 2,700-line awf_helpers_test.go was not split alongside the implementation — the module boundary exists in production code but not in test code.

Tests for BuildAWFCommand / BuildAWFArgs live in awf_helpers_test.go alongside tests for ComputeAWFExcludeEnvVarNames, awfSupports*, and ARC/DinD helpers. This makes the new awf_command_builder.go, awf_feature_flags.go, and awf_arc_dind.go modules feel incomplete and harder to navigate.

💡 Suggested next step

Create companion test files:

  • awf_command_builder_test.goTestBuildAWFCommand*, TestBuildAWFArgs*, TestWrapCommandInShell*
  • awf_feature_flags_test.goTestAWFSupports* tests
  • awf_arc_dind_test.goTestBuildAWFImageTagWithDigests*, TestRewriteArcDindPath*

and move the corresponding test functions from awf_helpers_test.go. No behaviour change needed.

@copilot please address this.

// fallback 400 to align with detection budgets.
// EngineConfig.MaxAICredits is 0 when no compile-time value was set
// (neither frontmatter nor detection-engine config provided one).
// In that case, emit a runtime expression that lets the org variable

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.

[/codebase-design] BuildAWFCommand is 260+ lines and manages command-string assembly, ARC/DinD topology probes, config-file generation, max-AI-credits injection, and log-file setup all in one function — consider a builder struct.

The function has grown to the point where local variable names like arcDindPrefixProbe, arcDindDockerHostProbe, configFileSetup, expandableArgs, and modelsJSONPathExport must be read in sequence to understand their combined effect. A small awfCommandParts struct (or even a strings.Builder-based helper) would let each concern be tested in isolation and make the final assembly trivial to audit.

💡 Example shape
type awfCommandParts struct {
    preamble        []string // set -o pipefail, pathSetup, configFileSetup
    probes          []string // arcDindDockerHostProbe, toolCacheMountProbe
    invocation      string   // awfCommand + expandableArgs + ...
    shellDirectives string
}

func (p *awfCommandParts) String() string {
    return strings.Join(append(p.preamble, p.probes..., p.shellDirectives, p.invocation), "\n")
}

This is a follow-up refactor, not a blocker — raising it so the pattern is considered before the next addition.

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the open review feedback, then run the pr-finisher skill before handing back to maintainers.

Outstanding review items:

Branch refresh was requested via GitHub update-branch.
Run context: https://github.com/github/gh-aw/actions/runs/31209372433

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.6 AIC · ⊞ 8.4K ·
Comment /souschef to run again

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

Copilot AI commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please address the open review feedback, then run the pr-finisher skill before handing back to maintainers.

Outstanding review items:...

Addressed the test-split feedback. The 2,700-line awf_helpers_test.go has been split into focused modules matching the production file boundaries:

  • awf_feature_flags_test.go – all awfSupports* version-gate tests
  • awf_arc_dind_test.go – ARC/DinD path, chroot, and image digest tests
  • awf_command_builder_test.goBuildAWFArgs*, BuildAWFCommand*, and GetAWFCommandPrefix tests
  • awf_env_test.go – expanded with env helper tests (previously in helpers_test)
  • awf_helpers_test.go – trimmed to API target, Copilot, and Gemini tests

Stale inter-function doc comments that ended up in wrong files after extraction have also been fixed (removed where misplaced, restored with correct functions). All existing tests pass.

Copilot AI requested a review from gh-aw-bot August 7, 2026 19:25
@pelikhan

pelikhan commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Copilot AI commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@copilot lint go and fix https://github.com/github/gh-aw/actions/runs/31211549102/job/92976126300?pr=51154

Ran make fmt and make lint — both pass cleanly with no changes needed. The CI failure at job/92976126300 was a cache restore timeout (Failed to restore: The operation cannot be completed in timeout), not a lint or code issue. No code fixes are required.

@pelikhan
pelikhan merged commit 2c0f238 into main Aug 7, 2026
28 of 30 checks passed
@pelikhan
pelikhan deleted the copilot/file-diet-refactor-awf-helpers-again branch August 7, 2026 19:50
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.86.1

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.

[file-diet] File Diet: refactor pkg/workflow/awf_helpers.go (1148 lines)

4 participants