Skip to content

Fix trial forwarding issue_number to workflows that don't declare it - #50638

Open
pelikhan with Copilot wants to merge 4 commits into
mainfrom
copilot/fix-forward-issue-number
Open

Fix trial forwarding issue_number to workflows that don't declare it#50638
pelikhan with Copilot wants to merge 4 commits into
mainfrom
copilot/fix-forward-issue-number

Conversation

Copilot AI commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

gh aw trial appended issue_number as a workflow_dispatch field to every workflow in a trial. Workflows whose compiled schema doesn't declare that input (e.g. scheduled/global companions) rejected the dispatch with HTTP 422: Unexpected inputs provided: ["issue_number"] and were silently never run.

The trigger-derived input is now forwarded only to workflows that declare it, using the lock file already available locally at dispatch time.

Changes

  • workflowDeclaresDispatchInput(lockFilePath, inputName) (new): parses on.workflow_dispatch.inputs from the compiled .lock.yml; fails safe (false) on read/parse errors so inputs are never forwarded to workflows that may reject them.
  • triggerWorkflowRun: takes the lock file path and appends --field issue_number=… only when the input is declared; otherwise logs an informational note in verbose mode instead of failing.
  • executeTrialRun: passes the per-workflow lock file path.
  • Tests covering declared/undeclared inputs, workflow_dispatch without inputs, schedule-only triggers, and missing files.

Behavior

In a mixed-trigger trial (entity-triggered workflow + scheduled companion), the issue-triggered workflow still receives issue_number while the scheduled one dispatches cleanly without it.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issue number forwarding in workflows Fix trial forwarding issue_number to workflows that don't declare it Aug 5, 2026
Copilot AI requested a review from pelikhan August 5, 2026 17:26
@pelikhan
pelikhan marked this pull request as ready for review August 5, 2026 17:37
Copilot AI balanced review requested due to automatic review settings August 5, 2026 17:37
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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

The fix is correct and well-tested. workflowDeclaresDispatchInput correctly fails-safe to false on read/parse errors, preventing unwanted --field args from being forwarded to workflows that did not declare them.

One minor nit: the // saveTrialResult saves a trial result to a JSON file doc comment was removed and no blank line was added between the closing } of workflowDeclaresDispatchInput and func saveTrialResult. Small cosmetic issue, not blocking.> 🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 19.3 AIC · ⌖ 7.44 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

Fixes trial dispatch failures by forwarding issue_number only to workflows that declare it.

Changes:

  • Inspects compiled lock-file dispatch inputs before forwarding issue_number.
  • Passes each workflow’s lock-file path into dispatch logic.
  • Adds coverage for declared, undeclared, absent, and unreadable inputs.
Show a summary per file
File Description
pkg/cli/trial_helpers.go Conditionally forwards trigger-derived inputs.
pkg/cli/trial_issue_mode_test.go Tests dispatch-input detection cases.

Review details

Tip

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

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

@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 /diagnosing-bugs and /tdd — approving with one minor nit.

📋 Key Themes & Highlights

Key Themes

  • Root cause addressed: the fix correctly guards --field issue_number behind a YAML parse of the lock file's on.workflow_dispatch.inputs, rather than patching the caller.
  • Fail-safe default: workflowDeclaresDispatchInput returns false on read/parse error, which is exactly the safe direction.
  • One cosmetic nit: the // saveTrialResult saves... doc comment and its preceding blank line were accidentally dropped in the same hunk — see the inline comment.

Positive Highlights

  • ✅ Comprehensive test matrix: declared input, undeclared input, dispatch without inputs, schedule-only trigger, missing file — all covered.
  • ✅ Verbose-mode logging retained for both the forwarded and skipped paths.
  • triggerWorkflowRun's signature change is minimal and backward-compatible within the package.
  • ✅ YAML parsing uses the already-imported go-yaml library with an anonymous struct — no over-engineering.
> 🧠 *Reviewed using Matt Pocock's skills by [Matt Pocock Skills Reviewer](https://github.com/github/gh-aw/actions/runs/31030910994)* · sonnet46 · 29.7 AIC · ⌖ 7.95 AIC · ⊞ 7.1K > Comment /matt to run again

Comment thread pkg/cli/trial_helpers.go
_, ok := parsed.On.WorkflowDispatch.Inputs[inputName]
return ok
}
func saveTrialResult(filename string, result any, verbose bool) error {

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.

[/diagnosing-bugs] The doc comment // saveTrialResult saves a trial result to a JSON file was dropped in this diff, and there is no blank line between workflowDeclaresDispatchInput's closing brace and saveTrialResult.

💡 Suggested fix

Restore the blank line and the comment:

	return ok
}

// saveTrialResult saves a trial result to a JSON file
func saveTrialResult(filename string, result any, verbose bool) error {

@copilot please address this.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Executive Summary

Test Quality Score: 83/100 (Excellent)

4 new test functions added with comprehensive edge case coverage and no code violations. All tests are design-focused behavioral tests verifying public API contracts.


Test Coverage Overview

Metric Value Status
New Test Functions 4
Total Subtests (table-driven) 23
Total Assertions 25+
Design-Focused Tests 100% (4/4)
Error Path Coverage 75% (3/4)
Duplicate Patterns 0
Build Tag Compliance ✅ Present
Forbidden Mocks None
Hard Violations None

Test-by-Test Analysis

1. TestExtractIssueNumberFromURL (10 subtests)

Design: URL parsing invariant — extract issue numbers from GitHub URLs only, reject invalid/foreign hosts
Coverage: Valid GitHub URLs, wrong hosts (GitLab), wrong paths (pulls), missing numbers, non-numeric, empty, query parameters, fragments
Quality: ✅ Excellent — comprehensive edge cases

2. TestExtractIssueNumberFromURL_GHES (6 subtests)

Design: Multi-host invariant — correctly handle GitHub Enterprise Server configuration
Coverage: GHES URLs accepted when GH_HOST=example.ghe.com, github.com rejected in GHES mode, other hosts rejected, non-issue paths rejected
Quality: ✅ Excellent — validates host-specific rejection logic with environment isolation (t.Setenv)

3. TestTrialWorkflowSpecParsing (3 subtests)

Design: Workflow spec validation — parse specs and validate format
Coverage: Valid GitHub URL spec, simple format, invalid format (error path)
Error Cases: ✅ Yes — explicit error handling assertions
Quality: ✅ Good — covers success and error paths

4. TestWorkflowDeclaresDispatchInput (5 test cases)

Design: Workflow schema validation — verify lock file declares dispatch inputs
Coverage: Issue_number declared ✅, not declared ✅, workflow_dispatch without inputs ✅, no workflow_dispatch trigger ✅, missing file edge case ✅
Setup Pattern: Uses t.TempDir() for isolated YAML test files; reads and parses via goccy/go-yaml
Quality: ✅ Excellent — comprehensive YAML parsing and file I/O edge cases


Design Invariants Verified

  1. parseIssueSpec(url) correctly extracts issue numbers from GitHub URLs and rejects invalid URLs
  2. ✅ Issue extraction respects GH_HOST configuration (public GitHub vs. GitHub Enterprise Server)
  3. parseWorkflowSpec() validates workflow spec format and rejects malformed specs
  4. workflowDeclaresDispatchInput(lockFilePath, inputName) correctly parses YAML lock files to check input declarations

Code Quality Observations

Positive Signals:

  • ✅ All assertions have descriptive failure messages (using t.Errorf)
  • ✅ Table-driven test pattern improves maintainability
  • ✅ Environment isolation via t.Setenv() for multi-host scenarios
  • ✅ File I/O isolation via t.TempDir()
  • ✅ No forbidden mocking libraries (gomock, testify/mock)
  • ✅ Correct //go:build !integration tag on line 1

Test Inflation:

  • Ratio: 74 new test lines / 33 net production lines = 2.24:1 (threshold: 2:1)
  • Status: ⚠️ Slightly elevated but justified
  • Rationale: Tests cover 4 design invariants with comprehensive edge cases (URL fragments, query params, multi-host config, YAML parsing with file I/O)

Scoring Calculation

score = ((design_tests / total) * 40) +
        ((edge_case_tests / total) * 30) +
        (20 - duplicates * 5) +
        (0 if inflation ≤ 2:1 else 0)

     = (4/4 * 40) +
       (3/4 * 30) +
       (20 - 0) +
       0
     = 40 + 22.5 + 20 + 0
     = 82.5 ≈ 83/100 ✅

Threshold: >=80 ✅ Excellent


Conclusion

APPROVED — No violations, comprehensive design-focused test coverage with excellent edge case handling. Test inflation slightly above threshold is justified by breadth of URL parsing and YAML validation scenarios.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 18.4 AIC · ⌖ 4.53 AIC · ⊞ 7.7K ·
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: 83/100. All 4 new test functions are design-focused (100%) with comprehensive edge cases. No violations: correct build tag, no forbidden mocks, excellent coverage of URL parsing and YAML schema validation.

…lock file schema check

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

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

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

📄 Draft ADR committed: docs/adr/50638-guard-dispatch-input-forwarding-with-lock-file-schema-check.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-50638: Guard workflow_dispatch Input Forwarding with Lock File Schema Check

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., 50638-guard-dispatch-input-forwarding-with-lock-file-schema-check.md for PR #50638).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 41.9 AIC · ⌖ 37.2 AIC · ⊞ 9.6K ·
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.

Verdict: Comment — logic is sound but error handling and duplication risks need attention

Themes
  • Fail-safe error swallowing: workflowDeclaresDispatchInput returns false for read errors, parse errors, and genuine absence alike — conflates a real bug (bad path, broken lock file) with legitimate 'input not declared', silently reintroducing a variant of the original 422 bug at a different layer.
  • Duplicated path/name construction: lockFileName (short name, used for gh workflow run) and lockFilePath (full path, used for input inspection) are built independently; if one convention changes without the other, the input-forwarding decision silently diverges from what actually runs.
  • Minor: dropped doc comment before saveTrialResult.
  • Test coverage for the new helper itself is solid (declared/undeclared/no-inputs/missing-file cases all covered).> 🔎 Code quality review by PR Code Quality Reviewer · auto · 49.1 AIC · ⌖ 4.38 AIC · ⊞ 7.9K

Comment /review to run again

Comment thread pkg/cli/trial_helpers.go
// workflowDeclaresDispatchInput reports whether the compiled lock file at lockFilePath
// declares the given workflow_dispatch input. It returns false if the file cannot be
// read or parsed, so that trigger-derived inputs are not forwarded to workflows whose
// workflow_dispatch schema does not declare them (which would cause an HTTP 422).

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.

Swallowing read/parse errors as false conflates "input genuinely not declared" with "lock file missing/corrupt/wrong path" — this silently hides real bugs (e.g. a stale or mistyped lockFilePath) rather than surfacing them.

💡 Details

workflowDeclaresDispatchInput returns false uniformly whether the file genuinely lacks the input, the file doesn't exist yet (e.g. compile step failed or path mismatch), or the YAML fails to parse for an unrelated reason. All three cases currently just log via trialLog.Printf and silently disable issue_number forwarding — which is exactly the class of bug this PR set out to fix (workflow silently never receiving the intended input), just moved one level deeper.

Consider distinguishing "file not found" (arguably fine to fail-safe) from parse errors on an existing file (should probably surface a warning to the user even outside verbose mode, since it indicates a compiler/format problem).

if verbose || err != nil {
    fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not parse lock file %s: %v", lockFilePath, err)))
}

Comment thread pkg/cli/trial_helpers.go
args := []string{"workflow", "run", lockFileName, "--repo", repoSlug}

// If trigger context is provided, extract issue number and add it as input
// If trigger context is provided, extract issue number and add it as 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.

lockFileName (used to invoke gh workflow run) and the caller's lockFilePath (used to inspect declared inputs) are built via two separate, unlinked expressions — a future edit to one path convention without the other will silently break input-forwarding decisions.

Comment thread pkg/cli/trial_helpers.go
}

_, ok := parsed.On.WorkflowDispatch.Inputs[inputName]
return ok

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 saveTrialResult doc comment was dropped/merged into the new function's closing brace, losing the godoc entry for that function.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

PR Triage: #50638

Category: bug | Risk: medium | Priority Score: 58/100 (impact 25, urgency 18, quality 15)
Recommended action: batch_review
Grouped with compile/trial cluster (50640, 50639).

Generated by 🔧 PR Triage Agent · auto · 45.2 AIC · ⌖ 2.9 AIC · ⊞ 8K ·

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please follow up on the latest review feedback on this PR:

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

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot August 5, 2026 19:24
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.

trial: forwards issue_number to workflows that don't declare it, causing HTTP 422 and a silently skipped dispatch

4 participants