Skip to content

Deduplicate safe-output parser wrapper boilerplate with parseConfigScaffoldWithPostProcess - #52158

Merged
pelikhan merged 5 commits into
mainfrom
copilot/duplicate-code-fix
Aug 12, 2026
Merged

Deduplicate safe-output parser wrapper boilerplate with parseConfigScaffoldWithPostProcess#52158
pelikhan merged 5 commits into
mainfrom
copilot/duplicate-code-fix

Conversation

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Many safe-output handlers already used the shared parseConfigScaffold helper, but each caller repeated the same outer wrapper: a nil-check, default-value assignment, and post-parse debug logging. This pattern was duplicated across 13+ non-test files in pkg/workflow.

Changes

  • New helper: Added parseConfigScaffoldWithPostProcess[T any] in pkg/workflow/config_helpers.go. It wraps parseConfigScaffold and invokes an optional postProcess(config *T) callback only when parsing succeeds, replacing the repeated if config == nil { return nil } ... return config wrapper.
  • Refactored callers to use the new helper, moving default-value logic and logging into the postProcess closure: add_comment.go, add_labels.go, add_reviewer.go, assign_milestone.go, assign_to_agent.go, assign_to_user.go, close_entity_helpers.go, mark_pull_request_as_ready_for_review.go, remove_labels.go, replace_label.go, set_issue_field.go, set_issue_type.go, unassign_from_user.go.
  • Left create_entity_helpers.go untouched — it already has its own generic scaffold (parseCreateEntityConfig) with pre/post hooks tailored to the create-* handler family.

No behavioral changes; existing default values, log messages, and error handling are preserved exactly.

Before:

config := parseConfigScaffold(outputMap, "assign-milestone", assignMilestoneLog, func(err error) *AssignMilestoneConfig {
	assignMilestoneLog.Printf("Failed to unmarshal config: %v", err)
	return &AssignMilestoneConfig{}
})
if config != nil {
	assignMilestoneLog.Printf("Parsed milestone config: target=%s, allowed_count=%d", config.Target, len(config.Allowed))
}
return config

After:

return parseConfigScaffoldWithPostProcess(outputMap, "assign-milestone", assignMilestoneLog,
	func(err error) *AssignMilestoneConfig {
		assignMilestoneLog.Printf("Failed to unmarshal config: %v", err)
		return &AssignMilestoneConfig{}
	},
	func(config *AssignMilestoneConfig) {
		assignMilestoneLog.Printf("Parsed milestone config: target=%s, allowed_count=%d", config.Target, len(config.Allowed))
	})

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 15.2 AIC · ⌖ 5.24 AIC · ⊞ 8.5K ·
Comment /souschef to run again

…arser wrappers

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor safe-output parser wrappers across workflow handlers Deduplicate safe-output parser wrapper boilerplate with parseConfigScaffoldWithPostProcess Aug 11, 2026
Copilot AI requested a review from pelikhan August 11, 2026 22:22
@pelikhan
pelikhan marked this pull request as ready for review August 11, 2026 22:38
Copilot AI balanced review requested due to automatic review settings August 11, 2026 22:38
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the 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.

Warning

Firewall blocked 1 domain

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

  • api.individual.githubcopilot.com

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

network:
  allowed:
    - defaults
    - "api.individual.githubcopilot.com"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

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

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Aug 11, 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.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Reviewed PR #52158 for over-engineering. This is a pure deduplication refactor: parseConfigScaffoldWithPostProcess has 13 real call sites, each replacing a previously duplicated 5-8 line nil-check/default/log wrapper. No speculative abstraction, no unused flexibility, no reinvented stdlib. Lean already. Ship.

Generated by Ponytail Reviewer for #52158

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

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

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

@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, well-scoped refactor. The new parseConfigScaffoldWithPostProcess helper correctly eliminates repeated nil-check wrapper boilerplate across all safe-output parsers. The nil-guard on postProcess and the unchanged behavior for onError-returning-nil are both handled correctly. No issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 20.4 AIC · ⌖ 6.14 AIC · ⊞ 5.4K

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

Centralizes safe-output parser post-processing to reduce repeated nil checks, defaulting, and logging across workflow handlers.

Changes:

  • Adds a generic post-processing parser scaffold.
  • Migrates 13 safe-output configuration parsers.
  • Preserves handler-specific defaults and compatibility logic.
Show a summary per file
File Description
pkg/workflow/config_helpers.go Adds the shared post-processing helper.
pkg/workflow/add_comment.go Migrates comment configuration parsing.
pkg/workflow/add_labels.go Migrates add-label parsing.
pkg/workflow/add_reviewer.go Moves defaults and legacy-field handling into the callback.
pkg/workflow/assign_milestone.go Migrates milestone parsing and logging.
pkg/workflow/assign_to_agent.go Moves default maximum handling into the callback.
pkg/workflow/assign_to_user.go Moves default maximum handling into the callback.
pkg/workflow/close_entity_helpers.go Moves close-entity defaults and compatibility mapping.
pkg/workflow/mark_pull_request_as_ready_for_review.go Moves target and filter extraction into the callback.
pkg/workflow/remove_labels.go Migrates remove-label parsing.
pkg/workflow/replace_label.go Migrates replace-label parsing.
pkg/workflow/set_issue_field.go Migrates issue-field parsing.
pkg/workflow/set_issue_type.go Migrates issue-type parsing.
pkg/workflow/unassign_from_user.go Moves default maximum handling into the callback.

Review details

Tip

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

  • Files reviewed: 14/14 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread pkg/workflow/config_helpers.go Outdated
Comment on lines +284 to +285
// parseConfigScaffoldWithPostProcess wraps parseConfigScaffold and additionally invokes the
// optional postProcess callback when parsing succeeds (i.e. the returned config is non-nil).
// assignMilestoneLog.Printf("Parsed milestone config: target=%s, allowed_count=%d",
// config.Target, len(config.Allowed))
// })
func parseConfigScaffoldWithPostProcess[T any](

@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 /tdd and /codebase-design — two minor issues found, no blocking concerns.

📋 Key Themes & Highlights

Issues

  • Missing test: parseConfigScaffoldWithPostProcess is the new central abstraction but has no unit test. Given that parseConfigScaffold itself likely lacks a test, this is a good opportunity to add one.
  • Style inconsistency: Seven callers use config := ...; return config instead of the simpler return ... pattern used by the other seven callers.

Positive Highlights

  • ✅ Clean, minimal implementation (12 lines) that does exactly one thing
  • ✅ Nil-safe postProcess guard is correct and well-documented
  • ✅ PR description is clear, with before/after examples
  • create_entity_helpers.go correctly left untouched — good judgment on scope
  • ✅ No behavioral changes; purely mechanical deduplication of 186 lines

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 39.7 AIC · ⌖ 6.8 AIC · ⊞ 7K
Comment /matt to run again

key string,
debugLog *logger.Logger,
onError func(err error) *T,
postProcess func(config *T),

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] No unit test covers parseConfigScaffoldWithPostProcess — the new generic helper is the central abstraction of this PR, but config_helpers_test.go does not exist.

💡 Suggested test skeleton
func TestParseConfigScaffoldWithPostProcess_CallsPostProcess(t *testing.T) {
    called := false
    got := parseConfigScaffoldWithPostProcess(map[string]any{"key": map[string]any{}}, "key", someLog,
        func(err error) *SomeConfig { return &SomeConfig{} },
        func(cfg *SomeConfig) { called = true },
    )
    if got == nil || !called {
        t.Fatalf("expected non-nil config and postProcess called")
    }
}

func TestParseConfigScaffoldWithPostProcess_NilPostProcessIsNoop(t *testing.T) {
    got := parseConfigScaffoldWithPostProcess(map[string]any{"key": map[string]any{}}, "key", someLog,
        func(err error) *SomeConfig { return &SomeConfig{} },
        nil,
    )
    if got == nil {
        t.Fatal("expected non-nil config")
    }
}

Edge cases worth covering: nil postProcess, parse failure via onError, missing key in outputMap.

@copilot please address this.

}
})

return config

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] Minor style inconsistency: some callers assign the result to a local config variable then immediately return config, while others (e.g. add_labels.go, assign_milestone.go) use return parseConfigScaffoldWithPostProcess(...) directly. The assign-then-return form adds a line of noise with no benefit.

💡 Preferred pattern
// Before (this file and add_reviewer.go, assign_to_agent.go, assign_to_user.go, etc.)
config := parseConfigScaffoldWithPostProcess(...)
return config

// After — consistent with add_labels.go, assign_milestone.go
return parseConfigScaffoldWithPostProcess(...)

Affected files: add_comment.go, add_reviewer.go, assign_to_agent.go, assign_to_user.go, close_entity_helpers.go, mark_pull_request_as_ready_for_review.go, unassign_from_user.go.

@copilot please address this.

@github-actions github-actions Bot mentioned this pull request Aug 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

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

📄 Draft ADR committed: docs/adr/52158-deduplicate-safe-output-parser-wrappers-with-post-process-helper.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-52158: Deduplicate Safe-Output Parser Wrappers with a Post-Process Helper

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

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

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., 52158-deduplicate-safe-output-parser-wrappers-with-post-process-helper.md for PR #52158).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 60.9 AIC · ⌖ 12.3 AIC · ⊞ 8.7K ·
Comment /review to run again

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot This PR needs a finishing pass before maintainers look again. Please refresh the branch context, address outstanding feedback, and run the pr-finisher skill.

Open items:

  • The ADR gate says a draft ADR was committed and still needs review/completion plus a PR-body link. Please finish that workflow and ensure the PR body references the ADR.
  • Re-check any remaining automated review feedback after the ADR update and summarize the resulting validation state in your reply.

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

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 15.2 AIC · ⌖ 5.24 AIC · ⊞ 8.5K ·
Comment /souschef to run again

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

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot This PR needs a finishing pass before maintainers look again. Please refresh the branch context, address outstanding feedback, and run the pr-finisher skill.

Open items:...

Finishing pass done in edc89ef:

  • ADR: ADR-52158 is completed — status moved from Draft to Proposed — pending maintainer acceptance on merge, deciders filled in, the callback contract documented, and the agent "review and finalize" trailer removed. The PR body now links to docs/adr/52158-deduplicate-safe-output-parser-wrappers-with-post-process-helper.md.
  • Remaining review feedback: both outstanding review comments on config_helpers.go are addressed — the doc comment now states that postProcess runs for every non-nil result (including non-nil onError fallbacks) and is skipped when the result is nil, and direct unit tests for the helper were added covering valid config, non-nil error fallback, nil error fallback, absent key, and a nil callback.
  • Validation: make fmt and make agent-report-progress (lint + impacted unit tests, workflow lock files in sync) pass locally. CI cannot be re-triggered from this agent, so a maintainer push/re-run is needed for a fresh green CI signal.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Triage: PR #52158

Generated by 🔧 PR Triage Agent · auto · 44.5 AIC · ⌖ 2.66 AIC · ⊞ 7.8K ·

@pelikhan
pelikhan merged commit ad8c16d into main Aug 12, 2026
29 checks passed
@pelikhan
pelikhan deleted the copilot/duplicate-code-fix branch August 12, 2026 01:11
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.

[duplicate-code] Duplicate Code: Safe-output parser wrappers repeated across workflow handlers

4 participants