Skip to content

refactor: consolidate duplicated model-normalization helpers (pkg/cli ↔ pkg/modelsdev)#43340

Merged
pelikhan merged 5 commits into
mainfrom
copilot/refactor-consolidate-model-normalization-helpers
Jul 4, 2026
Merged

refactor: consolidate duplicated model-normalization helpers (pkg/cli ↔ pkg/modelsdev)#43340
pelikhan merged 5 commits into
mainfrom
copilot/refactor-consolidate-model-normalization-helpers

Conversation

Copilot AI commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

pkg/cli/model_costs.go contained byte-for-byte copies of two unexported helpers from pkg/modelsdev/catalog.go — a silent divergence hazard for provider-alias and model-ID normalization logic.

Changes

  • pkg/modelsdev/catalog.go — promote normalizeProviderNormalizeProvider and normalizeComparableModelIDNormalizeComparableModelID (exported, with doc comments); update all internal call sites
  • pkg/cli/model_costs.go — delete local normalizeCatalogProvider and normalizeComparableModelID; replace all six call sites with modelsdev.NormalizeProvider / modelsdev.NormalizeComparableModelID (no new import — file already depended on pkg/modelsdev)
  • pkg/modelsdev/catalog_test.go — add TestNormalizeProvider and TestNormalizeComparableModelID covering aliases, case-folding, trimming, and separator normalization
  • pkg/cli/model_costs_test.go — update TestNormalizeCatalogProvider to call modelsdev.NormalizeProvider
// Before — two independent copies, able to silently diverge:
// pkg/cli/model_costs.go
func normalizeCatalogProvider(provider string) string { … }
func normalizeComparableModelID(value string) string { … }

// After — single canonical implementation, consumed across packages:
// pkg/modelsdev/catalog.go
func NormalizeProvider(provider string) string { … }
func NormalizeComparableModelID(value string) string { … }

// pkg/cli/model_costs.go
normalizedProvider := modelsdev.NormalizeProvider(provider)
comparableModel   := modelsdev.NormalizeComparableModelID(normalizedModel)

Generated by 👨‍🍳 PR Sous Chef · 3.07 AIC · ⌖ 4.15 AIC · ⊞ 4.3K ·
Comment /souschef to run again

… ↔ pkg/modelsdev)

- Export NormalizeProvider and NormalizeComparableModelID from pkg/modelsdev/catalog.go
- Remove byte-for-byte duplicate normalizeCatalogProvider / normalizeComparableModelID
  from pkg/cli/model_costs.go and replace all call sites with the modelsdev versions
- Add NormalizeProvider / NormalizeComparableModelID unit tests in catalog_test.go
- Update TestNormalizeCatalogProvider in model_costs_test.go to use modelsdev.NormalizeProvider

Closes #43270

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor duplicated model-normalization helpers in pkg/cli and pkg/modelsdev refactor: consolidate duplicated model-normalization helpers (pkg/cli ↔ pkg/modelsdev) Jul 4, 2026
Copilot AI requested a review from pelikhan July 4, 2026 07:48
@pelikhan pelikhan marked this pull request as ready for review July 4, 2026 08:02
Copilot AI review requested due to automatic review settings July 4, 2026 08:02
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

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

No ADR enforcement needed: PR #43340 does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (53 additions detected, threshold is 100).

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

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

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 removes duplicated provider/model normalization helpers by exporting the canonical implementations from pkg/modelsdev and reusing them from pkg/cli, reducing the risk of silent logic divergence across packages.

Changes:

  • Export NormalizeProvider and NormalizeComparableModelID from pkg/modelsdev and update internal call sites.
  • Replace the duplicated helpers in pkg/cli/model_costs.go with calls to the exported modelsdev functions.
  • Add/adjust unit tests to cover the exported normalizers and update CLI tests accordingly.
Show a summary per file
File Description
pkg/modelsdev/catalog.go Exports the provider/model normalization helpers and updates catalog lookup/parse call sites to use them.
pkg/modelsdev/catalog_test.go Adds direct unit coverage for the newly exported normalization helpers.
pkg/cli/model_costs.go Removes local duplicate helpers and uses modelsdev.NormalizeProvider / NormalizeComparableModelID throughout.
pkg/cli/model_costs_test.go Updates the CLI normalization test to call modelsdev.NormalizeProvider (and adds the new import).

Review details

Tip

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

Comments suppressed due to low confidence (1)

pkg/modelsdev/catalog.go:223

  • NormalizeProvider currently re-computes strings.ToLower(strings.TrimSpace(provider)) in both the switch expression and the default branch. Computing it once improves readability and avoids duplicated work.
	switch strings.ToLower(strings.TrimSpace(provider)) {
	case "github", "copilot", "github_models":
		return "github-copilot"
	default:
		return strings.ToLower(strings.TrimSpace(provider))
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment on lines 44 to 46
t.Run(name, func(t *testing.T) {
got := normalizeCatalogProvider(tt.input)
got := modelsdev.NormalizeProvider(tt.input)
assert.Equal(t, tt.want, got)
Comment thread pkg/modelsdev/catalog.go
Comment on lines +227 to 231
// NormalizeComparableModelID lower-cases the value and replaces "." and "_" with "-"
// so that model IDs differing only in those separators compare equal.
func NormalizeComparableModelID(value string) string {
return strings.NewReplacer(".", "-", "_", "-").Replace(strings.ToLower(strings.TrimSpace(value)))
}
@github-actions github-actions Bot mentioned this pull request Jul 4, 2026

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

Review: refactor — consolidate duplicated model-normalization helpers

This is a clean, well-scoped refactor. The duplication hazard is real (silent divergence between pkg/cli and pkg/modelsdev), and the fix is correct.

What looks good

  • Both helpers are promoted with doc comments and thoroughly tested in catalog_test.go.
  • All six call sites in model_costs.go are updated consistently; no leftover references to the old local copies.
  • pkg/cli already imported pkg/modelsdev, so no new import cycle is introduced.
  • Test coverage added for both exported functions covers aliases, case-folding, trimming, and separator normalization.

One observation (not blocking)

pkg/workflow/compiler_model_pricing.go still contains a third copy of the same logic (normalizeProviderForPricing, ~line 81), with identical switch cases. That package does not currently import pkg/modelsdev, so this PR correctly left it alone. A follow-up to consolidate that copy (if no circular import exists) would complete the deduplication.

Verdict: No blocking issues. Approving.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 28.9 AIC · ⌖ 5.84 AIC · ⊞ 4.9K

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100 — Excellent

Analyzed 3 test(s): 3 design, 0 implementation, 0 violation(s).

📊 Metrics (3 tests)
Metric Value
Analyzed 3 (Go: 3, JS: 0)
✅ Design 3 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (67%)
Duplicate clusters 0
Inflation YES — catalog_test.go 33 added vs catalog.go 11 added (3:1)
🚨 Violations 0
Test File Classification Issues
TestNormalizeProvider pkg/modelsdev/catalog_test.go design_test / behavioral_contract none
TestNormalizeComparableModelID pkg/modelsdev/catalog_test.go design_test / behavioral_contract none
TestNormalizeCatalogProvider (modified) pkg/cli/model_costs_test.go design_test / behavioral_contract none
⚠️ Flagged Tests (1 — inflation note)

TestNormalizeProvider + TestNormalizeComparableModelID (pkg/modelsdev/catalog_test.go) — The 2 new test functions add 33 lines against 11 new production lines in catalog.go (3:1 ratio, threshold 2:1). This is flagged per policy but is expected and appropriate for this consolidation refactor: the tests cover all canonical alias mappings, case normalization, whitespace trimming, and empty-string edge cases for the newly exported helpers. The test thoroughness is the reason for the ratio, not padding.

Minor observation: assert.Equal calls omit a descriptive failure message, but the t.Run subtest names (input->want format) provide equivalent failure context — standard Go table-driven style, not flagged as a violation.

Verdict

Passed. 0% implementation tests (threshold: 30%). No guideline violations. Tests enforce the normalization contract for the two newly exported helpers with comprehensive case coverage.

Score penalty: −10 for test inflation ratio (3:1) in catalog_test.go. Inflation is contextually justified for a consolidation refactor that surfaces previously internal logic as a tested public API.

References:

Warning

Firewall blocked 1 domain

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

  • awmgmcpg

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

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 33.1 AIC · ⌖ 26.3 AIC · ⊞ 6.8K ·
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: 90/100. 0% implementation tests (threshold: 30%). No violations.

@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 — no blocking issues found; leaving three minor suggestions.

📋 Key Themes & Highlights

Positive Highlights

  • ✅ Excellent single-responsibility refactor: two byte-for-byte duplicates eliminated, single canonical implementation in the right package
  • ✅ Six call sites updated consistently with no behavioural change
  • ✅ Good doc comments added to both exported functions — they now form a proper public API
  • ✅ Comprehensive new tests in catalog_test.go covering aliases, case-folding, trimming, and separator normalisation

Minor Suggestions

  1. Stale test name (pkg/cli/model_costs_test.go line 45) — TestNormalizeCatalogProvider now tests modelsdev.NormalizeProvider; the name is misleading and the cases duplicate TestNormalizeProvider in catalog_test.go. Either remove it or replace with a wiring-level integration test for findModelPricing.

  2. Missing mixed-case alias (pkg/modelsdev/catalog_test.go line 156) — "GitHub_Models" (mixed caps) is not in the TestNormalizeProvider table. Adding it locks in the case-insensitivity invariant as a specification.

  3. Missing idempotency case (pkg/modelsdev/catalog_test.go line 178) — TestNormalizeComparableModelID has no case for already-canonical input. A {"gpt-4o", "gpt-4o"} row guards against future over-normalisation.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 60.8 AIC · ⌖ 6.86 AIC · ⊞ 6.6K
Comment /matt to run again

}
t.Run(name, func(t *testing.T) {
got := normalizeCatalogProvider(tt.input)
got := modelsdev.NormalizeProvider(tt.input)

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] TestNormalizeCatalogProvider now tests modelsdev.NormalizeProvider, making its name stale — and its cases are a subset of the new TestNormalizeProvider in catalog_test.go, so the coverage is duplicated.

💡 Suggested fix

Either remove this test entirely (canonical coverage already lives in pkg/modelsdev/catalog_test.go), or rename it and replace the cases with an integration-level test that goes through findModelPricing with a provider alias to verify the wiring end-to-end. Keeping it as-is means a failure reports TestNormalizeCatalogProvider when the function named in the message is NormalizeProvider, which is confusing.

@copilot please address this.

{"copilot", "github-copilot"},
{"github_models", "github-copilot"},
{"GITHUB_MODELS", "github-copilot"},
{"anthropic", "anthropic"},

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] TestNormalizeProvider has no case for "github_models" with mixed capitalisation (e.g. "GitHub_Models"). The switch only lower-cases before comparing, so capitalised variants work, but a test case would lock that in as a specification.

💡 Suggested additional case
{"GitHub_Models", "github-copilot"},

Adding it turns the test suite into a living specification for alias matching, and prevents a future refactor from accidentally dropping case-insensitive handling.

@copilot please address this.

}
for _, tc := range cases {
t.Run(tc.input+"->"+tc.want, func(t *testing.T) {
assert.Equal(t, tc.want, NormalizeComparableModelID(tc.input))

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] TestNormalizeComparableModelID is missing a case for a string that is already in canonical form (e.g. "gpt-4o""gpt-4o"). Without an idempotency case, a future change that over-normalises (e.g. double-stripping) would not be caught by this suite.

💡 Suggested additional case
{"gpt-4o", "gpt-4o"}, // already canonical — idempotency check

@copilot please address this.

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

Non-blocking observation

The refactor is well-scoped: the consolidation is correct, all six call sites are updated, and the new test coverage is solid.

One minor test-hygiene issue: the empty-input sub-test name (see inline comment).

🔎 Code quality review by PR Code Quality Reviewer · 151.4 AIC · ⌖ 8.85 AIC · ⊞ 5.4K
Comment /review to run again

{"", ""},
}
for _, tc := range cases {
t.Run(tc.input+"->"+tc.want, func(t *testing.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.

Unaddressable sub-test name: when tc.input is "" the sub-test name becomes "->", which cannot be targeted with -run and silently conflates with any other "->" case in test output.

💡 Suggested fix

Apply the same guard used in the CLI test (model_costs_test.go:41):

for _, tc := range cases {
    name := tc.input + "->" + tc.want
    if tc.input == "" {
        name = "<empty>->" + tc.want
    }
    t.Run(name, func(t *testing.T) {
        assert.Equal(t, tc.want, NormalizeProvider(tc.input))
    })
}

Without this, go test -run TestNormalizeProvider/-> silently matches nothing, and CI logs show "->" with no way to identify which empty case failed. The same issue exists in TestNormalizeComparableModelID (line 177).

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.
Unresolved review follow-up to address (newest first):

  • github-actions:
  • github-actions:
  • copilot-pull-request-reviewer:

Generated by 👨‍🍳 PR Sous Chef · 3.07 AIC · ⌖ 4.15 AIC · ⊞ 4.3K ·
Comment /souschef to run again

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

Copilot AI commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.
Unresolved review follow-up to address (newest first):

  • github-actions...

Both review items addressed in commit 3c3bab3:

  • TestNormalizeCatalogProvider renamed to TestNormalizeProvider in pkg/cli/model_costs_test.go
  • strings.NewReplacer hoisted to a package-level var modelIDReplacer in pkg/modelsdev/catalog.go, eliminating per-call allocations in NormalizeComparableModelID

Local validation (make fmt, make agent-report-progress) passes.

…normalizes)

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot July 4, 2026 10:38
@pelikhan pelikhan merged commit a67a29f into main Jul 4, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/refactor-consolidate-model-normalization-helpers branch July 4, 2026 10:57
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.

[refactor] Consolidate duplicated model-normalization helpers (pkg/cli vs pkg/modelsdev)

4 participants