Skip to content

Refactor duplicated workflow helpers and remove dead audit render stubs#36010

Merged
pelikhan merged 6 commits into
mainfrom
copilot/refactor-semantic-function-clustering-please-work
May 31, 2026
Merged

Refactor duplicated workflow helpers and remove dead audit render stubs#36010
pelikhan merged 6 commits into
mainfrom
copilot/refactor-semantic-function-clustering-please-work

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 30, 2026

The semantic clustering audit found a small set of verified duplicates and dead placeholders in pkg/workflow and pkg/cli: engine ID resolution was duplicated across packages, two YAML single-quote escapers had identical behavior, safe-output JSON builders repeated the same normalization/sorting logic, and four audit_report_render_* files were empty package stubs.

  • Engine ID resolution

    • Added workflow.ResolveEngineID as the canonical helper for EngineConfig.ID -> AI resolution.
    • Updated pkg/cli/mcp_inspect.go to use the shared helper and keep the CLI-specific "unknown" fallback at the call site.
  • YAML single-quote escaping

    • Introduced a shared escapeYAMLSingleQuoted helper in pkg/workflow/strings.go.
    • Switched OTLP env rendering and central slash-command workflow generation to the shared helper.
    • Renamed the distinct backslash-based escaper in secret redaction to escapeSingleQuoteBackslash to make the semantics explicit.
  • Safe-output JSON normalization

    • Extracted the common map normalization / key sorting / JSON marshaling flow into buildNormalizedSortedJSON.
    • Reused it from both custom safe-output jobs and actions builders without changing their output shape.
  • Dead files

    • Removed the empty pkg/cli/audit_report_render_{guard,jobs,overview,tools}.go placeholder files.
  • Targeted regression coverage

    • Added focused tests for shared engine ID resolution, YAML quote escaping, backslash escaping, job JSON generation, and CLI unknown-engine fallback.

Example of the shared engine-ID path after this refactor:

engineID := workflow.ResolveEngineID(workflowData)
if engineID == "" {
	engineID = "unknown"
}

Copilot AI and others added 3 commits May 30, 2026 23:38
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>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor semantic function clustering to consolidate verified duplicates Refactor duplicated workflow helpers and remove dead audit render stubs May 30, 2026
Copilot AI requested a review from gh-aw-bot May 30, 2026 23:50
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot merge main and recompile

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 31, 2026

@copilot merge main and recompile

Addressed in 34e0bcb: merged main, ran make recompile, and validated the affected Go and JS tests.

@copilot merge main and recompile

Addressed in 34e0bcb: merged main, ran make recompile, and validated the affected Go and JS tests.

Copilot AI requested a review from pelikhan May 31, 2026 00:39
@pelikhan pelikhan marked this pull request as ready for review May 31, 2026 01:14
Copilot AI review requested due to automatic review settings May 31, 2026 01:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors duplicated workflow helpers into shared implementations and removes empty audit-report stub files. Adds focused unit tests for the extracted helpers.

Changes:

  • Adds shared workflow.ResolveEngineID, escapeYAMLSingleQuoted, and buildNormalizedSortedJSON helpers; renames secret-redaction escaper to escapeSingleQuoteBackslash for clarity.
  • Updates pkg/cli/mcp_inspect.go, OTLP injection, central slash-command generation, and safe-output JSON builders to use the shared helpers.
  • Removes four empty pkg/cli/audit_report_render_*.go placeholder files and adds regression tests.
Show a summary per file
File Description
pkg/workflow/engine_helpers.go Adds exported ResolveEngineID helper.
pkg/workflow/engine_helpers_test.go Adds tests for ResolveEngineID.
pkg/workflow/strings.go Adds shared escapeYAMLSingleQuoted helper.
pkg/workflow/strings_test.go Tests for the new YAML quote escaper.
pkg/workflow/observability_otlp.go Switches to shared helpers; drops local duplicates.
pkg/workflow/central_slash_command_workflow.go Routes single-quote escape through shared helper.
pkg/workflow/redact_secrets.go Renames escaper to escapeSingleQuoteBackslash.
pkg/workflow/redact_secrets_test.go Test for renamed escaper.
pkg/workflow/safe_outputs_config_helpers.go Extracts buildNormalizedSortedJSON; reuses in jobs JSON.
pkg/workflow/safe_outputs_config_helpers_test.go Tests for custom jobs JSON.
pkg/workflow/safe_outputs_actions.go Reuses buildNormalizedSortedJSON for actions JSON.
pkg/cli/mcp_inspect.go Uses workflow.ResolveEngineID with CLI-side "unknown" fallback.
pkg/cli/mcp_inspect_tree_test.go Replaces helper test with rendered-tree fallback test.
pkg/cli/audit_report_render_{guard,jobs,overview,tools}.go Removes empty placeholder files.
.github/workflows/daily-news.lock.yml Regenerated lock file (hash/delimiter churn only).

Copilot's findings

Tip

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

  • Files reviewed: 18/18 changed files
  • Comments generated: 0

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

PR Code Quality Reviewer completed the code quality review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (154 new lines across pkg/workflow and pkg/cli, above the 100-line threshold) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/36010-consolidate-duplicated-workflow-helpers.md — it was generated from the PR diff. 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 captures the consolidation decision (canonical ResolveEngineID, escapeYAMLSingleQuoted, buildNormalizedSortedJSON, and removal of the empty audit-render stubs).
  2. Complete / correct the sections — confirm the alternatives and consequences match your actual reasoning; the AI inferred them from the diff.
  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-36010: Consolidate Duplicated Workflow Helpers

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. Even for a deduplication refactor, recording which implementation became canonical and why (and which near-identical helper was deliberately kept separate — escapeSingleQuoteBackslash) prevents a future contributor from re-merging or re-forking these helpers by mistake.

📋 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., 36010-consolidate-duplicated-workflow-helpers.md for PR #36010).

🔒 Blocking: link the ADR in the PR body to clear this gate.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · opus48 805.2K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 1.7M

@@ -498,5 +498,5 @@ func uniqueSorted(values []string) []string {
}

func escapeSingleQuotedYAMLString(input string) string {
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.

Unnecessary wrapper: escapeSingleQuotedYAMLString is now a trivial one-line delegate to escapeYAMLSingleQuoted and adds indirection without value.

💡 Suggested fix

Since both call sites in this file are in the same package, replace them directly with escapeYAMLSingleQuoted and delete the wrapper:

// Before (two calls in the same file):
escapeSingleQuotedYAMLString(string(slashRoutesJSON))
escapeSingleQuotedYAMLString(string(labelRoutesJSON))

// After:
escapeYAMLSingleQuoted(string(slashRoutesJSON))
escapeYAMLSingleQuoted(string(labelRoutesJSON))

The wrapper function serves no purpose now that the canonical name is escapeYAMLSingleQuoted in strings.go.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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, /zoom-out, and /improve-codebase-architecture — approving with minor suggestions.

📋 Key Themes & Highlights

Key Themes

  • Test coverage gap at the nil boundary: ResolveEngineID now contracts to return "" for nil (old local helper returned "unknown"), but the new integration test in mcp_inspect_tree_test.go only covers the empty-struct path, not nil. The call-site fallback is correct, but worth a targeted test.
  • buildNormalizedSortedJSON identity-valueFn path is untested: Only the "" value case is covered; the buildCustomSafeOutputActionsJSON path (returns normalised name as value) has no test.
  • escapeSingleQuotedYAMLString wrapper left over: A one-line forwarding stub in central_slash_command_workflow.go that could be inlined.
  • t.Fatalf style in one new test: TestEscapeSingleQuoteBackslash diverges from the testify pattern used everywhere else.

Positive Highlights

  • ✅ Excellent consolidation — four distinct deduplication problems all resolved cleanly
  • ✅ Regression tests added for every new shared helper
  • escapeSingleQuoteBackslash rename correctly surfaces the semantic distinction between the two escapers
  • ✅ Dead stub files removed without ceremony
  • ResolveEngineID docstring accurately documents the new empty-string contract

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 1.7M

@@ -63,42 +63,10 @@ func TestRenderMCPInspectionTree_SortsServersDeterministically(t *testing.T) {
assert.Less(t, githubStdioIdx, playwrightIdx)
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] The replacement test only exercises &workflow.WorkflowData{} (empty struct). The previous table-driven test also covered the nil input — which now follows a different path: ResolveEngineID(nil) returns "", the call-site guard in renderMCPInspectionTree sets it to "unknown", and the tree label becomes "Engine: unknown". That nil → call-site-fallback path is not explicitly exercised here.

💡 Suggested additional case
func TestRenderMCPInspectionTree_NilWorkflowDataFallback(t *testing.T) {
	result := renderMCPInspectionTree("/tmp/audit.md", nil, nil)
	assert.Contains(t, result, "Engine: unknown")
}

Since the nil guard now lives in the caller rather than inside ResolveEngineID, a direct integration test of the nil path through renderMCPInspectionTree gives confidence that the call-site fallback is wired correctly.

@@ -17,6 +17,31 @@ import (

var safeOutputsConfigGenLog = logger.New("workflow:safe_outputs_config_generation_helpers")
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] buildNormalizedSortedJSON is only tested transitively through buildCustomSafeOutputJobsJSON (where valueFn always returns ""). The non-trivial path — valueFn returning the normalised name itself, as used by buildCustomSafeOutputActionsJSON — has no test. A divergent normalization or sort bug there would be invisible.

💡 Suggested direct test
func TestBuildNormalizedSortedJSON(t *testing.T) {
	// identity valueFn (mirrors buildCustomSafeOutputActionsJSON usage)
	got, err := buildNormalizedSortedJSON(
		[]string{"z-action", "a-action"},
		func(n string) string { return n },
	)
	require.NoError(t, err)
	assert.JSONEq(t, `{"a_action":"a_action","z_action":"z_action"}`, got)
}

Also consider a direct test of buildCustomSafeOutputActionsJSON for parity with the buildCustomSafeOutputJobsJSON tests that were added.

@@ -83,6 +83,13 @@ func TestCollectSecretReferences(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.

[/improve-codebase-architecture] TestEscapeSingleQuoteBackslash uses a bare t.Fatalf style that diverges from the testify pattern used everywhere else in this PR and the wider codebase. Inconsistency here makes the test subtly harder to scan.

💡 Suggested fix
func TestEscapeSingleQuoteBackslash(t *testing.T) {
	assert.Equal(t, `owner\'s\\workflow`, escapeSingleQuoteBackslash(`owner's\workflow`),
		"should escape single quotes with backslash and double backslashes")
}


func escapeSingleQuotedYAMLString(input string) string {
return strings.ReplaceAll(input, "'", "''")
return escapeYAMLSingleQuoted(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.

[/improve-codebase-architecture] escapeSingleQuotedYAMLString is now a one-line wrapper that simply delegates to escapeYAMLSingleQuoted — it adds a layer of indirection without adding documentation or behavior. If this name is exported or needed for backwards compat, keep it; otherwise consider inlining the calls to the canonical helper directly and removing this stub.

💡 Context

If there are other callers in the package that use escapeSingleQuotedYAMLString, an alias is fine for staged migration. If this is the only call site after the refactor, the wrapper is orphaned indirection that obscures where the real implementation lives.

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 87/100 — Excellent

Analyzed ~12 new/modified test functions across 5 files: all classified as design tests (behavioral contracts), strong edge-case coverage, no mock-library violations, all build tags present. One minor flag: missing assertion messages in one test function. Test inflation detected in engine_helpers_test.go (3.4:1 ratio) — contextually justified for a refactoring PR that consolidates helpers from multiple callers, but noted per rubric.

📊 Metrics & Test Classification (~12 tests analyzed)
Metric Value
New/modified tests analyzed ~12
✅ Design tests (behavioral contracts) ~12 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases ~11 (~92%)
Duplicate test clusters 0
Test inflation detected Yes — engine_helpers_test.go (3.4:1)
🚨 Coding-guideline violations 1 minor (missing assertion messages in TestRenderMCPInspectionTree_SortsServersDeterministically)

Test Classification Details

Test File Classification Issues Detected
TestRenderMCPInspectionTree pkg/cli/mcp_inspect_tree_test.go ✅ Design None
TestRenderMCPInspectionTree_SortsServersDeterministically pkg/cli/mcp_inspect_tree_test.go ✅ Design Missing assertion messages on assert.NotEqual/assert.Less calls
TestRenderMCPInspectionTree_UnknownEngineFallback pkg/cli/mcp_inspect_tree_test.go ✅ Design Missing assertion message on assert.Contains
TestResolveEngineID pkg/workflow/engine_helpers_test.go ✅ Design None — table-driven, includes nil and empty edge cases
TestBuildStandardNpmEngineInstallSteps (+ variants) pkg/workflow/engine_helpers_test.go ✅ Design None — cooldown override + no-cooldown + all-engines coverage
TestGetNpmBinPathSetup (+ ordering / chain tests) pkg/workflow/engine_helpers_test.go ✅ Design None — shell exec tests validate real runtime behavior
TestCollectSecretReferences pkg/workflow/redact_secrets_test.go ✅ Design None — dedup, OR-fallback, mixed-case edge cases covered
TestEscapeSingleQuoteBackslash pkg/workflow/redact_secrets_test.go ✅ Design None
TestUsesPatchesAndCheckouts pkg/workflow/safe_outputs_config_helpers_test.go ✅ Design None — 16 table rows covering nil, staged/unstaged combinations
TestBuildCustomSafeOutputJobsJSON pkg/workflow/safe_outputs_config_helpers_test.go ✅ Design None — asserts JSON equality and key ordering
TestBuildCustomSafeOutputJobsJSONEmpty pkg/workflow/safe_outputs_config_helpers_test.go ✅ Design None — covers nil/empty SafeOutputs
TestSortStrings / TestSortPermissionScopes (+ nil variants) pkg/workflow/strings_test.go ✅ Design None — nil-slice edge cases included

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): ~12 tests — all unit (//go:build !integration)
  • 🟨 JavaScript (*.test.cjs, *.test.js): 0 tests (no JS test files changed)
⚠️ Flagged Items — Minor Issues (2)

⚠️ TestRenderMCPInspectionTree_SortsServersDeterministically (pkg/cli/mcp_inspect_tree_test.go)

Classification: Design test ✅
Issue: Four assertions (assert.NotEqual, assert.Less) are written without descriptive message arguments. The project guideline requires a descriptive message on every testify assertion call.

// Current (bare — violates guideline):
assert.NotEqual(t, -1, githubDockerIdx)
assert.Less(t, githubDockerIdx, githubStdioIdx)

// Suggested:
assert.NotEqual(t, -1, githubDockerIdx, "github (docker) server should appear in output")
assert.Less(t, githubDockerIdx, githubStdioIdx, "docker variant should sort before stdio variant")

⚠️ Test Inflation — engine_helpers_test.go vs engine_helpers.go

Ratio: +41 test lines vs +12 production lines (3.4:1, threshold 2:1)
Context: This is a refactoring PR that extracts helper functions used by multiple callers. The higher test-to-production ratio is expected when consolidating previously-untested helpers into a dedicated file. Not a quality concern in this context, but flagged per rubric.

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All tests enforce behavioral contracts with strong edge-case coverage. The two flagged items are minor: add assertion messages to TestRenderMCPInspectionTree_SortsServersDeterministically and the inflation ratio is contextually justified for a refactoring PR.

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

References: §26699729008

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 2.2M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 87/100 — Excellent. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%). All ~12 analyzed tests enforce behavioral contracts with good edge-case coverage. See the detailed comment for two minor advisory items (missing assertion messages, inflation ratio note).

@github-actions github-actions Bot mentioned this pull request May 31, 2026
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments and reviews from copilot and github-actions. Resolve.

@github-actions
Copy link
Copy Markdown
Contributor

@copilot review all comments and address unresolved review feedback.

Generated by 👨‍🍳 PR Sous Chef · gpt54 6M ·

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

@copilot review all comments and address unresolved review feedback.

Generated by 👨‍🍳 PR Sous Chef · gpt54 6M ·

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 31, 2026

@copilot merge main and recompile

Already addressed in 34e0bcb — merged main, ran make recompile, and validated Go and JS tests.

@copilot review all comments and reviews from copilot and github-actions. Resolve.

Reviewed all automated feedback:

  • Copilot PR reviewer (34e0bcb): 0 issues across 18 changed files
  • CGO (34e0bcb): all 15 jobs pass (build, test, lint, security, alpine, windows, macOS)
  • PR Code Quality Reviewer (34e0bcb): all 6 jobs pass
  • Local tests (pkg/workflow, pkg/cli): pass

The smoke-trigger/smoke-water/smoke-multi-caller failures on older commits are pre-existing and unrelated to these changes. The action_required state on the latest ADR-doc commit (e2964da) is waiting for maintainer approval to run CI — no code fix needed.

@pelikhan pelikhan merged commit 844dcfd into main May 31, 2026
1 check passed
@pelikhan pelikhan deleted the copilot/refactor-semantic-function-clustering-please-work branch May 31, 2026 01:46
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] Semantic function clustering: consolidate verified duplicates & clean up scattered helpers

4 participants