Skip to content

Refactor ResolveWorkflows into focused helpers to reduce largefunc backlog#45965

Merged
pelikhan merged 10 commits into
mainfrom
copilot/lint-monster-function-length-refactoring-another-one
Jul 16, 2026
Merged

Refactor ResolveWorkflows into focused helpers to reduce largefunc backlog#45965
pelikhan merged 10 commits into
mainfrom
copilot/lint-monster-function-length-refactoring-another-one

Conversation

Copilot AI commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

This PR addresses a narrow slice of the function-length backlog by splitting ResolveWorkflows in pkg/cli/add_workflow_resolution.go into cohesive helpers while preserving behavior. It reduces one largefunc finding in pkg/cli without broad rewrites.

  • What changed

    • Extracted ResolveWorkflows orchestration into focused internal helpers for:
      • input validation
      • workflow/package spec parsing
      • current-repo guard checks
      • wildcard expansion
      • per-spec resolution
      • bootstrap profile selection
    • Kept existing public interfaces and call sites unchanged.
  • Behavior preserved

    • Repository package resolution flow, including fallback behavior for nested package-spec parsing.
    • Redirect/content resolution semantics for remote workflows.
    • Private workflow enforcement for manifest-sourced and regular workflow paths.
    • Warning aggregation behavior (including JSON-import refinement warnings and bootstrap conflict warnings).
  • Code organization improvements

    • Added small, single-purpose helpers and a local resolution result struct to make control flow explicit and auditable.
    • Isolated package/agent/action special-case handling from standard markdown workflow handling.
func ResolveWorkflows(ctx context.Context, workflows []string, verbose bool) (*ResolvedWorkflows, error) {
    if err := validateResolveWorkflowsInput(workflows); err != nil { return nil, err }
    specResolution, err := parseWorkflowSpecsForResolution(ctx, workflows)
    if err != nil { return nil, err }
    if err := validateCurrentRepositorySpecs(specResolution.ParsedSpecs); err != nil { return nil, err }

    parsedSpecs, hasWildcard, err := expandWorkflowSpecsIfNeeded(specResolution.ParsedSpecs, verbose)
    if err != nil { return nil, err }

    resolvedWorkflows, hasDispatch, warnings, err := resolveWorkflowSpecs(ctx, parsedSpecs, specResolution.Warnings, verbose)
    if err != nil { return nil, err }

    bootstrapProfile, warnings := selectBootstrapProfile(specResolution.BootstrapProfiles, warnings)
    return &ResolvedWorkflows{Workflows: resolvedWorkflows, HasWildcard: hasWildcard, HasWorkflowDispatch: hasDispatch, Warnings: warnings, BootstrapProfile: bootstrapProfile}, nil
}

Copilot AI and others added 3 commits July 16, 2026 09:27
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for working on this refactor! The PR is still marked [WIP] — once all checklist items are complete and final validations pass, this will be ready for review. Looking good so far!

Generated by ✅ Contribution Check · 116.8 AIC · ⌖ 6.96 AIC · ⊞ 6.2K ·

Copilot AI and others added 3 commits July 16, 2026 09:45
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
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] Refactor function length issues across workflow and cli packages Refactor ResolveWorkflows into focused helpers to reduce largefunc backlog Jul 16, 2026
Copilot AI requested a review from pelikhan July 16, 2026 09:58
@pelikhan
pelikhan marked this pull request as ready for review July 16, 2026 10:16
Copilot AI review requested due to automatic review settings July 16, 2026 10:16

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

This PR refactors ResolveWorkflows (in pkg/cli/add_workflow_resolution.go) by extracting its major phases into smaller, single-purpose helpers while keeping the public API and overall resolution behavior intact. The goal is to reduce the function’s size/complexity in pkg/cli without a broader rewrite.

Changes:

  • Extracted workflow input validation, spec parsing (including local/repo package handling and fallback parsing), current-repo guard validation, and wildcard expansion into dedicated helpers.
  • Extracted per-spec resolution into resolveSingleWorkflowSpec, separating package/agent/action “raw install” handling from standard markdown workflow handling (frontmatter parsing, private checks, workflow_dispatch detection, JSON-import warnings).
  • Isolated bootstrap profile selection logic into selectBootstrapProfile, preserving warning behavior on conflicts.
Show a summary per file
File Description
pkg/cli/add_workflow_resolution.go Splits ResolveWorkflows into cohesive helper functions while preserving existing workflow/package resolution behavior and warning aggregation.

Review details

Tip

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

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

No test files were added or modified in this PR. Test Quality Sentinel skipped.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

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

Reviewed the ResolveWorkflows refactor — behavior is faithfully preserved across all helpers. The extraction is clean: validation, spec parsing, repo guard, wildcard expansion, per-spec resolution, and bootstrap selection are each isolated and single-purpose. No correctness or security concerns found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 27.8 AIC · ⌖ 4.91 AIC · ⊞ 5K

Records the architectural decision to split the monolithic
ResolveWorkflows function into focused helper functions.

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

Copy link
Copy Markdown
Contributor

Design Decision Gate - ADR Required

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

Draft ADR committed: docs/adr/45965-decompose-resolveworkflows-into-helpers.md - 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 the 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 a line such as:

    ADR: ADR-45965: Decompose ResolveWorkflows into Focused Helper Functions

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 Reference

An ADR must contain these four sections to be considered complete:

  • 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)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 0042-use-postgresql.md for PR #42).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · 51.1 AIC · ⌖ 10.3 AIC · ⊞ 8.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.

Skills-Based Review 🧠

Applied /codebase-design — commenting (no blocking issues, but three design improvements worth addressing).

📋 Key Themes & Highlights

Key Themes

  • Return-value proliferation: several helpers return 4–5 positional values including a boolean sentinel (handled), making call sites harder to read and extend.
  • Unnecessary defensive copy: selectBootstrapProfile uses make+copy that Go slice semantics make redundant.
  • Dead field in struct literal: IsPrivate: isPrivate in resolveStandardWorkflow is always false at that point.

Positive Highlights

  • ✅ Top-level ResolveWorkflows reads like a clean flowchart now.
  • specResolutionResult is a good first step toward a deep intermediate type.
  • validateManifestWorkflowPrivateSetting and validateCurrentRepositorySpecs are well-named and easily unit-testable.
  • ✅ No behaviour changes — the diff is well-scoped and matches the PR description.

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 58.5 AIC · ⌖ 4.89 AIC · ⊞ 6.7K
Comment /matt to run again

Comment thread pkg/cli/add_workflow_resolution.go Outdated
Description: description,
Engine: engine,
HasWorkflowDispatch: workflowHasDispatch,
IsPrivate: isPrivate,

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] IsPrivate: isPrivate is always false in this success path — the error check on line 356 means we never reach the struct literal when isPrivate is true. The field assignment is dead code and may confuse future readers.

💡 Suggested fix

The field can be dropped from the literal entirely, or the check can be restructured so the value is meaningful:

// Option A: just omit the field — it defaults to false
return &ResolvedWorkflow{
    Spec:                resolvedSpec,
    Content:             fetched.Content,
    SourceInfo:          fetched,
    Description:         description,
    Engine:              engine,
    HasWorkflowDispatch: workflowHasDispatch,
    // IsPrivate intentionally absent: already guarded above
}, workflowHasDispatch, warning, nil

If IsPrivate on ResolvedWorkflow is used downstream for something other than this guard (e.g. display), document why it should always be false here.

@copilot please address this.

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.

Dropped IsPrivate: isPrivate from the struct literal in d199cd1. Since the error guard above returns early when isPrivate is true, the field would always be false at that point — removing it eliminates the dead assignment.

Comment thread pkg/cli/add_workflow_resolution.go Outdated
}
resolutionLog.Printf("Multiple bootstrap profiles found (%v); skipping all", ids)
resolutionWarnings = append(resolutionWarnings,
updatedWarnings := make([]string, len(resolutionWarnings), len(resolutionWarnings)+1)

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 defensive make+copy in selectBootstrapProfile is unnecessary — a simple append already allocates a new backing array when capacity is exhausted, so the original slice is never mutated. The added complexity misleads future readers into thinking the caller slice needs protection.

💡 Suggested simplification
default:
    // ...ids collection...
    resolutionLog.Printf("Multiple bootstrap profiles found (%v); skipping all", ids)
    return nil, append(resolutionWarnings,
        fmt.Sprintf("multiple bootstrap profiles found (%s); ...", strings.Join(ids, ", ")))

This is safe because resolutionWarnings is a value copy from the caller — mutating the underlying array beyond its length is the only thing to guard against, and append handles that automatically when len == cap.

@copilot please address this.

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.

Simplified the default case to a direct append in d199cd1. The make+copy was unnecessary since append already allocates a new backing array when needed, so the caller's slice is never mutated.

Comment thread pkg/cli/add_workflow_resolution.go Outdated
}
func parseSingleWorkflowSpecForResolution(ctx context.Context, workflow string) ([]*WorkflowSpec, []string, *resolvedBootstrapProfile, error) {
specs, warnings, bootstrapProfile, handled, err := resolveLocalPackageWorkflowSpec(workflow)
if err != nil || handled {

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 (handled bool, err error) sentinel pattern in resolveLocalPackageWorkflowSpec and resolveRepositoryPackageWorkflowSpec leaks implementation-level signalling into the calling function. This forces callers to reason about three values (specs, handled, err) in combination and makes the chain in parseSingleWorkflowSpecForResolution fragile to extend.

💡 Deeper module alternative

A cleaner interface would use a typed sentinel error (or named result type) so callers do not need to inspect handled:

var errNotHandled = errors.New("not handled")

func resolveLocalPackageWorkflowSpec(workflow string) (*specResolutionResult, error) {
    pkg, err := resolveLocalRepositoryPackage(workflow)
    if err != nil { return nil, err }
    if pkg == nil { return nil, errNotHandled }
    // ...build result...
    return result, nil
}
// caller
result, err := resolveLocalPackageWorkflowSpec(workflow)
if err == nil { return result.Specs, result.Warnings, result.BootstrapProfile, nil }
if !errors.Is(err, errNotHandled) { return nil, nil, nil, err }
// fall through

This keeps the decision logic in one place and avoids "boolean-plus-error" combinations that violate the principle of deep modules.

@copilot please address this.

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.

Replaced the (handled bool, err error) pattern with a package-level errNotHandled sentinel in d199cd1. Both resolveLocalPackageWorkflowSpec and resolveRepositoryPackageWorkflowSpec now return a plain error; callers use errors.Is(err, errNotHandled) to fall through, eliminating the boolean-plus-error combination.

return nil, fmt.Errorf("invalid Agentic Workflow manifest %q: workflow %q sets private: true and cannot be included because private workflows cannot be added", manifestPath, resolvedSpec.WorkflowPath)
}
}
if resolvedWorkflow, handled := resolvePackageOrActionWorkflow(spec, resolvedSpec, fetched); handled {

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] resolveSingleWorkflowSpec returns (bool, string, error) where the bool is hasDispatch — this is a behavioural result mixed into a resolution function. The dispatch flag is specific to standard markdown workflows, but the function is also the entry point for package skill/agent/action files (which always return false). Consider returning a *resolvedWorkflowResult struct so the call site in resolveWorkflowSpecs does not have to unpack positional return values.

💡 Deep module suggestion
type resolvedWorkflowResult struct {
    Workflow            *ResolvedWorkflow
    HasWorkflowDispatch bool
    Warning             string
}

func resolveSingleWorkflowSpec(ctx context.Context, spec *WorkflowSpec, verbose bool) (*resolvedWorkflowResult, error)

This makes the interface stable — adding a field (e.g. Redirected bool) does not require updating every call site.

@copilot please address this.

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.

Introduced resolvedWorkflowResult struct in d199cd1 and updated both resolveSingleWorkflowSpec and resolveStandardWorkflow to return (*resolvedWorkflowResult, error). The struct carries Workflow *ResolvedWorkflow and Warning string; HasWorkflowDispatch is read directly from result.Workflow.HasWorkflowDispatch at the call site (3df949e) to avoid duplication.

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

The refactoring is clean and behavior-preserving. One minor non-blocking improvement noted inline.

### Review summary
  • All critical paths (local package resolution, repo package resolution, wildcard expansion, private workflow guards, manifest private checks) are faithfully preserved.
  • The resolveRepositoryPackageWorkflowSpec fallthrough condition correctly inverts the original guard.
  • The 5-tuple return signature for package-parsing helpers is readable given the multiple return values in the original.
  • One non-blocking issue filed: unnecessary make/copy in selectBootstrapProfile's default case.

🔎 Code quality review by PR Code Quality Reviewer · 183.6 AIC · ⌖ 4.43 AIC · ⊞ 5.6K
Comment /review to run again

Comments that could not be inline-anchored

pkg/cli/add_workflow_resolution.go:531

Unnecessary defensive copy in selectBootstrapProfile's default case adds complexity with no benefit.

<details>
<summary>💡 Details</summary>

Lines 529–531 allocate a new slice, copy the existing warnings, then append the new one:

updatedWarnings := make([]string, len(resolutionWarnings), len(resolutionWarnings)+1)
copy(updatedWarnings, resolutionWarnings)
updatedWarnings = append(updatedWarnings, ...)

Go's append already handles capacity growth. The original code simply …

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the unresolved review feedback below, and rerun checks if needed.

Unresolved review threads:

Please resolve the review threads and update the branch as needed before re-running validation.

Generated by 👨‍🍳 PR Sous Chef · 14.6 AIC · ⌖ 4.75 AIC · ⊞ 7.5K ·
Comment /souschef to run again

Copilot AI and others added 2 commits July 16, 2026 11:37
…, remove dead IsPrivate, simplify append

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot July 16, 2026 11:40
@pelikhan
pelikhan merged commit bfd748a into main Jul 16, 2026
29 checks passed
@pelikhan
pelikhan deleted the copilot/lint-monster-function-length-refactoring-another-one branch July 16, 2026 11:53
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.11

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[lint-monster] function-length refactoring backlog (682 findings)

4 participants