Skip to content

fix(dispatch-workflow): expose ref parameter in per-workflow tool schema when allowed-refs is configured - #49754

Merged
pelikhan merged 5 commits into
mainfrom
copilot/fix-dispatch-workflow-ref-parameter
Aug 2, 2026
Merged

fix(dispatch-workflow): expose ref parameter in per-workflow tool schema when allowed-refs is configured#49754
pelikhan merged 5 commits into
mainfrom
copilot/fix-dispatch-workflow-ref-parameter

Conversation

Copilot AI commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

generateDispatchWorkflowTool never added a ref property to the generated MCP tool schema, leaving message.ref always absent at runtime. Every dispatch silently fell through the priority chain to GITHUB_REF (the dispatching workflow's ref), making allowed-refs configuration unreachable dead config despite the backend supporting it since #49408.

Changes

  • safe_outputs_dispatch.gogenerateDispatchWorkflowTool now accepts allowedRefs []string. When non-empty, injects an optional ref string property into the tool's inputSchema.properties with a description listing the allowed glob patterns, and appends those patterns to the tool description:

    {
      "ref": {
        "type": "string",
        "description": "The git ref (branch, tag, or SHA) to dispatch the workflow on. Must match one of the configured allowed ref patterns: [silencer/* refs/heads/main]. If omitted, the dispatching workflow's ref is used."
      }
    }

    No ref property is injected when allowed-refs is absent, preserving existing behavior.

  • safe_outputs_tools_generation.go — All three generateDispatchWorkflowTool call sites updated to pass data.SafeOutputs.DispatchWorkflow.AllowedRefs.

  • safe_outputs_tools_generation_test.go — Existing tests updated for new signature; two new tests added: one verifying ref injection when allowed-refs is set, one verifying no injection when it is absent.


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 31.4 AIC · ⌖ 8.75 AIC · ⊞ 8.3K ·
Comment /souschef to run again

…ed-refs is configured

- Update generateDispatchWorkflowTool to accept allowedRefs []string
- Inject optional 'ref' property into the tool input schema when allowed-refs is non-empty
- Include allowed ref patterns in property description and tool description so the agent understands the contract
- Pass DispatchWorkflowConfig.AllowedRefs to all three generateDispatchWorkflowTool call sites in generateDynamicTools
- Add tests for the new ref parameter injection and the no-ref case

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix dispatch workflow to expose ref parameter fix(dispatch-workflow): expose ref parameter in per-workflow tool schema when allowed-refs is configured Aug 2, 2026
Copilot AI requested a review from pelikhan August 2, 2026 10:57
@pelikhan
pelikhan marked this pull request as ready for review August 2, 2026 11:15
Copilot AI review requested due to automatic review settings August 2, 2026 11:15
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

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

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

@github-actions

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

Exposes an optional dispatch target ref when allowed-refs is configured.

Changes:

  • Adds ref to generated workflow tool schemas.
  • Passes allowed refs through tool-generation call sites.
  • Adds schema-generation tests.
Show a summary per file
File Description
pkg/workflow/safe_outputs_dispatch.go Injects the ref tool parameter.
pkg/workflow/safe_outputs_tools_generation.go Passes allowed-ref configuration.
pkg/workflow/safe_outputs_tools_generation_test.go Tests conditional ref generation.

Review details

Tip

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

Suppressed comments (1)

pkg/workflow/safe_outputs_dispatch.go:124

  • workflowInputs are flattened into this same properties map, so a valid target workflow input named ref is silently overwritten here. If that input is required, its old required entry also remains, making the supposedly optional dispatch ref required; once the bridge lifts this value to message.ref, the target workflow input can no longer be supplied at all. Use a distinct tool argument (for example target_ref) that the bridge maps to message.ref, or explicitly reject/reserve colliding workflow input names.
		properties["ref"] = map[string]any{
			"type":        "string",
			"description": refDesc,
		}
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +121 to +124
properties["ref"] = map[string]any{
"type": "string",
"description": refDesc,
}

@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: fix(dispatch-workflow): expose ref parameter in per-workflow tool schema

The fix is correct and well-tested. The logic that injects the ref property only when allowedRefs is non-empty correctly preserves backward compatibility.

One non-blocking suggestion: fmt.Sprintf("%v", allowedRefs) formats a []string as [a b c] (brackets, space-separated, no quotes). For LLM-facing tool descriptions, strings.Join(allowedRefs, ", ") would produce a cleaner, more readable list. See inline comment.

Warning

Firewall blocked 1 domain

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

  • proxy.golang.org

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

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 28.5 AIC · ⌖ 7.61 AIC · ⊞ 5.4K

Comment thread pkg/workflow/safe_outputs_dispatch.go Outdated
inputSchema, _ := tool["inputSchema"].(map[string]any)
properties, _ := inputSchema["properties"].(map[string]any)

refDesc := fmt.Sprintf("The git ref (branch, tag, or SHA) to dispatch the workflow on. Must match one of the configured allowed ref patterns: %v. If omitted, the dispatching workflow's ref is used.", allowedRefs)

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.

Minor: %v formatting of []string yields [silencer/* refs/heads/main] (bracketed, space-separated, no quotes). For an LLM-facing description, a cleaner comma-separated format is preferable.

Consider using strings.Join:

refDesc := fmt.Sprintf("The git ref (branch, tag, or SHA) to dispatch the workflow on. Must match one of the configured allowed ref patterns: %s. If omitted, the dispatching workflow's ref is used.", strings.Join(allowedRefs, ", "))

Apply the same change on line 127 for the tool-level description append.

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

Skills-Based Review 🧠

Applied /tdd and /codebase-design — requesting changes on two small but concrete issues.

📋 Key Themes & Highlights

Issues

  • Brittle test assertion (safe_outputs_tools_generation_test.go L362): .([]string) type assertion on required silently no-ops if the slice is []any, so the "ref should not be required" guard is never actually exercised.
  • Go-syntax leaking into user-facing strings (safe_outputs_dispatch.go L121): fmt.Sprintf("%v", allowedRefs) renders as [silencer/* refs/heads/main] — use strings.Join(allowedRefs, ", ") for clean output.

Positive Highlights

  • ✅ Root cause is correctly identified and fixed: the missing allowedRefs parameter propagation is the exact gap that made allowed-refs unreachable.
  • ✅ All three call sites in generateDynamicTools are updated consistently.
  • ✅ Good backward-compatibility discipline: no ref property injected when allowedRefs is nil/empty.
  • ✅ Test names read as specifications and the two new tests cover both positive and negative cases cleanly.

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

// ref should not be in required (it is optional)
required, hasRequired := inputSchema["required"].([]string)
if hasRequired {
assert.NotContains(t, required, "ref", "ref should not be required")

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 .([]string) type assertion on required will silently skip the assertion if the slice is []any — meaning the "ref should not be required" check never actually runs.

💡 Suggested fix

Handle both possible slice types:

requiredRaw := inputSchema["required"]
if requiredRaw != nil {
    switch r := requiredRaw.(type) {
    case []string:
        assert.NotContains(t, r, "ref")
    case []any:
        for _, v := range r {
            assert.NotEqual(t, "ref", v)
        }
    }
}

Alternatively, introduce a small containsInRequired(schema, field) test helper since the same pattern appears in other tests.

@copilot please address this.

properties, _ := inputSchema["properties"].(map[string]any)

refDesc := fmt.Sprintf("The git ref (branch, tag, or SHA) to dispatch the workflow on. Must match one of the configured allowed ref patterns: %v. If omitted, the dispatching workflow's ref is used.", allowedRefs)
properties["ref"] = map[string]any{

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] Using %v on []string in user-facing descriptions produces Go-style [silencer/* refs/heads/main] rather than a clean comma-separated list. Agents reading the MCP tool schema will see Go syntax noise.

💡 Suggested fix

Replace both %v calls with strings.Join:

refDesc := fmt.Sprintf(
    "The git ref ... allowed ref patterns: %s. If omitted ...",
    strings.Join(allowedRefs, ", "),
)
tool["description"] = desc + fmt.Sprintf(
    " Use the 'ref' parameter to target a specific branch or tag (allowed patterns: %s).",
    strings.Join(allowedRefs, ", "),
)

This also means the test assertions (assert.Contains(t, ..., "silencer/*")) will still pass, so no test changes are needed.

@copilot please address this.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 75/100 — Acceptable

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

📊 Metrics (2 new tests)
Metric Value
Analyzed 2 (Go: 2, JS: 0)
✅ Design 2 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (100%)
Duplicate clusters 0
Inflation 2.23:1 (slightly elevated)
🚨 Violations 0
Test File Classification Issues
TestGenerateDispatchWorkflowToolWithAllowedRefs safe_outputs_tools_generation_test.go:329 design_test, behavioral_contract None
TestGenerateDispatchWorkflowToolNoRefWithoutAllowedRefs safe_outputs_tools_generation_test.go:370 design_test, behavioral_contract None
✅ Test Design Quality

Both new tests validate critical design invariants:

  1. TestGenerateDispatchWorkflowToolWithAllowedRefs — Ensures ref parameter is injected into tool schema when allowed-refs is configured, with proper type, description, and pattern documentation. Tests schema completeness across inputSchema, properties, and tool description fields. 9 assertions covering the happy path.

  2. TestGenerateDispatchWorkflowToolNoRefWithoutAllowedRefs — Validates negative case: ref property is NOT present when AllowedRefs is nil or empty (loop tests both cases). Ensures backward compatibility when the feature is not configured. 4 assertions across 2 loop iterations.

Both tests follow Go conventions: descriptive failure messages on assertions, proper use of require.True for setup validation, and test names that clearly state the condition being tested.

Verdict

PASS — 0% implementation tests (threshold: 30%), 0 violations. Both new tests are pure design tests validating schema injection behavior and backward compatibility. No mocking, proper build tags, high-value coverage of the ref parameter feature.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 20.5 AIC · ⌖ 6.94 AIC · ⊞ 8.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.

✅ Test Quality Sentinel: 75/100. 0% implementation tests (threshold: 30%), 0 violations. Both new tests are pure design tests validating schema injection behavior and backward compatibility. No mocking, proper build tags, high-value coverage of the ref parameter feature.

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

🔎 Code quality review by PR Code Quality Reviewer · auto · 61.7 AIC · ⌖ 14.1 AIC · ⊞ 7.8K
Comment /review to run again

Comment thread pkg/workflow/safe_outputs_dispatch.go Outdated
inputSchema, _ := tool["inputSchema"].(map[string]any)
properties, _ := inputSchema["properties"].(map[string]any)

refDesc := fmt.Sprintf("The git ref (branch, tag, or SHA) to dispatch the workflow on. Must match one of the configured allowed ref patterns: %v. If omitted, the dispatching workflow's ref is used.", allowedRefs)

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.

Unconditional properties["ref"] = ... silently overwrites any workflow_dispatch input literally named ref, and no test covers this collision.

💡 Details

If a target workflow defines its own workflow_dispatch input named ref (a plausible, unreserved name), this code overwrites its schema entry — including type, description, and required-ness — with the injected ref-glob description, with no warning, log, or merge logic. A caller-defined required ref input silently loses its required semantics from the caller's perspective (the property is replaced but the pre-existing required array entry, if any, stays, producing an inconsistent schema where ref is both required and described as optional/glob-validated).

Suggested fix: detect a name collision before injecting, and either skip injection with a log warning, or rename the injected property (e.g. dispatch_ref) to avoid clobbering user-defined inputs.

if len(allowedRefs) > 0 {
    inputSchema, _ := tool["inputSchema"].(map[string]any)
    properties, _ := inputSchema["properties"].(map[string]any)
    if _, collision := properties["ref"]; collision {
        safeOutputsDispatchWorkflowLog.Printf("Warning: workflow %s already defines a 'ref' input; skipping allowed-refs injection to avoid clobbering it", workflowName)
    } else {
        properties["ref"] = map[string]any{...}
    }
}

Also add a regression test with a workflow_dispatch input literally named ref plus allowed-refs configured, asserting the original input is preserved.

descriptionFormat: "Dispatch the '%s' workflow with workflow_dispatch trigger. This workflow must support workflow_dispatch and be in .github/workflows/ directory in the same repository.",
descriptionFormat: descriptionFormat,
metadataKey: "_workflow_name",
})

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 type assertions on tool["inputSchema"] and inputSchema["properties"] ignore the ok bool, so a future shape change in generateWorkflowToolDefinition would turn into a nil-map write panic here instead of a clear error.

💡 Details

properties, _ := inputSchema["properties"].(map[string]any) discards the failure case. Today generateWorkflowToolDefinition always returns a non-nil properties map (via buildInputSchema), so this is currently safe, but the coupling is implicit and undocumented — nothing enforces that invariant at this call site. If generateWorkflowToolDefinition is refactored later (e.g. to omit properties when there are no inputs, which would be a reasonable change), properties["ref"] = ... panics on a nil map write with an unhelpful stack trace.

Suggested hardening:

inputSchema, ok := tool["inputSchema"].(map[string]any)
if !ok {
    safeOutputsDispatchWorkflowLog.Printf("Warning: unexpected tool shape for %s, skipping ref injection", workflowName)
} else if properties, ok := inputSchema["properties"].(map[string]any); ok {
    properties["ref"] = map[string]any{...}
}

properties["ref"] = map[string]any{
"type": "string",
"description": refDesc,
}

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.

%v formatting of []string (e.g. [silencer/* refs/heads/main]) is duplicated verbatim in both the ref property description and the tool description, bloating the schema shown to the agent.

💡 Details

fmt.Sprintf("...%v", allowedRefs) renders as a Go slice literal with square brackets and space-separated entries, not a natural-language list (e.g. silencer/*, refs/heads/main). This is harder for an LLM (and humans) to parse cleanly, especially if a pattern itself contains a space. The same list is also repeated twice — once in refDesc and again in the appended tool["description"] — adding redundant tokens to every dispatch tool's description with no added information.

Suggested fix: format with strings.Join(allowedRefs, ", ") and drop the duplicate mention in the outer description (or make the outer sentence generic, e.g. "Use the 'ref' parameter to target a specific branch or tag; see parameter description for allowed patterns.").

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the open review feedback on this PR, then run the pr-finisher skill.

Outstanding review items observed:

  • safe_outputs_dispatch.go: avoid fmt.Sprintf("%v", allowedRefs) in the user-facing description; use a cleaner joined representation.
  • safe_outputs_tools_generation_test.go: strengthen the required assertion so the negative case really verifies ref is not required.

Branch refresh was requested for this PR. After fixing the review feedback, run the pr-finisher skill.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 31.4 AIC · ⌖ 8.75 AIC · ⊞ 8.3K ·
Comment /souschef to run again

…assertion

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

Copilot AI commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please address the open review feedback on this PR, then run the pr-finisher skill.

Outstanding review items observed:...

Fixed in commit 15d949b.

  • Replaced %v formatting of allowedRefs with a clean strings.Join(allowedRefs, ", ") representation in both dispatch tool descriptions.
  • Strengthened the negative-case test to assert required is present for a required workflow input and contains only that input (verifying ref is not required when allowed-refs is absent).

Copilot AI requested a review from gh-aw-bot August 2, 2026 12:35
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please give this PR a finishing pass and run the pr-finisher skill.

Current triage signals:

  • no failed checks are listed in compact triage,
  • a branch refresh was requested,
  • please verify the current head is ready for maintainers and summarize any remaining blocker if one still exists.

After that, run the pr-finisher skill.

Generated by 👨🍳 PR Sous Chef

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 7.13 AIC · ⌖ 5.02 AIC · ⊞ 8.3K ·
Comment /souschef to run again

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

PR Triage

  • Category:
  • Risk:
  • Priority: (score: 40/100)
    • Impact: 28/50, Urgency: 10/30, Quality: 2/20
  • Recommended action:

Automated triage — see full report issue for details.

Structured data:

{
  "action": "defer",
  "category": "bug",
  "pr_number": 49754,
  "risk": "low"
}

Generated by 🔧 PR Triage Agent · auto · 73.4 AIC · ⌖ 3.47 AIC · ⊞ 8K ·

…workflow-ref-parameter

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

Copilot AI commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please give this PR a finishing pass and run the pr-finisher skill.

Current triage signals:...

Finishing pass completed on head 30362bd (branch refreshed from main).

Current blocker: unresolved review threads are still open, and mergeable_state is currently unstable in the PR API.

Validation status from this pass:

  • local make fmt, make lint, make test-unit, make test, make recompile all passed
  • no failed jobs were returned for the triaged workflow runs via get_job_logs (failed_only)

This head is ready for maintainer follow-up once remaining review-thread state/mergeability is cleared and CI is re-triggered by a maintainer.

@pelikhan
pelikhan merged commit 89da246 into main Aug 2, 2026
29 checks passed
@pelikhan
pelikhan deleted the copilot/fix-dispatch-workflow-ref-parameter branch August 2, 2026 13:27
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.84.3

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.

dispatch-workflow: per-workflow tool schema never exposes the ref parameter added in #49408

4 participants