Skip to content

fix: nest issue-intent metadata correctly in close_issue and assign_to_agent payloads#46850

Merged
pelikhan merged 2 commits into
mainfrom
copilot/fix-issue-intent-metadata-shape
Jul 20, 2026
Merged

fix: nest issue-intent metadata correctly in close_issue and assign_to_agent payloads#46850
pelikhan merged 2 commits into
mainfrom
copilot/fix-issue-intent-metadata-shape

Conversation

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

close_issue and assign_to_agent were spreading intent metadata (rationale, confidence, suggest) as top-level request params, where the GitHub API silently ignores them. The mutation returns HTTP 200 with no error, so the error fallback never fires and no intent is persisted.

Changes

  • close_issue.cjs — wrap state as an object instead of a scalar string, nesting metadata inside it:

    // Before
    { state: "closed", ...intentMetadata }          // metadata ignored at top level
    
    // After
    { state: { value: "closed", ...intentMetadata } } // metadata nested where API reads it

    The legacy fallback path (github.rest.issues.update with scalar state) is unchanged.

  • assign_agent_helpers.cjs — use per-assignee object form (mirroring add_labels), keyed by login:

    // Before
    { assignees: ["copilot-swe-agent[bot]"], rationale: "...", confidence: "HIGH" }
    
    // After
    { assignees: [{ login: "copilot-swe-agent[bot]", rationale: "...", confidence: "HIGH" }] }
  • Tests updated to assert the corrected nested payload shapes.

…o_agent payloads

- close_issue.cjs: send `state: { value: "closed", ...intentMetadata }` instead of
  spreading intentMetadata as top-level siblings of a scalar state
- assign_agent_helpers.cjs: send `assignees: [{ login, ...intentMetadata }]` instead of
  spreading intentMetadata as top-level params with string assignees
- Update tests to assert the corrected nested payload shapes

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issue-intent handlers to use correct payload shape fix: nest issue-intent metadata correctly in close_issue and assign_to_agent payloads Jul 20, 2026
Copilot AI requested a review from pelikhan July 20, 2026 13:53
@pelikhan
pelikhan marked this pull request as ready for review July 20, 2026 13:58
Copilot AI review requested due to automatic review settings July 20, 2026 13:58

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 issue-intent metadata persistence by using the API’s required nested payload structures.

Changes:

  • Nests close metadata inside the state object.
  • Nests assignment metadata inside each assignee object.
  • Updates tests to verify both payload shapes.
Show a summary per file
File Description
actions/setup/js/close_issue.cjs Corrects close-issue payload nesting.
actions/setup/js/close_issue.test.cjs Verifies nested state metadata.
actions/setup/js/assign_agent_helpers.cjs Corrects per-assignee metadata nesting.
actions/setup/js/assign_agent_helpers.test.cjs Verifies nested assignee metadata.

Review details

Tip

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

  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jul 20, 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.

@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-structured. Both intent-path changes follow the same pattern of nesting metadata where the API actually reads it, and the fallback error paths remain intact. Tests confirm the old top-level keys are absent and the new nested shapes are asserted. LGTM.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 17.2 AIC · ⌖ 4.31 AIC · ⊞ 5K

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

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

📊 Metrics (2 tests)
Metric Value
Analyzed 2 (Go: 0, JS: 2)
✅ Design 2 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (100%)
Duplicate clusters 0
Inflation No
🚨 Violations 0
Test File Classification Issues
should include issue-intent metadata when enabled assign_agent_helpers.test.cjs:403 design_test None
should include issue-intent metadata on close when enabled close_issue.test.cjs:142 design_test None

Verdict

Passed. 0% implementation tests (threshold: 30%). Both tests validate critical API contract behavior for metadata nesting, with proper negative assertions ensuring metadata doesn't leak to unintended payload locations.

🧪 Test quality analysis by Test Quality Sentinel · 13.6 AIC · ⌖ 28.6 AIC · ⊞ 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: 100/100. 0% implementation tests (threshold: 30%). Both tests validate critical API contract behavior for metadata nesting with proper negative assertions.

@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 · 34.9 AIC · ⌖ 4.59 AIC · ⊞ 5.6K
Comment /review to run again

owner,
repo,
issue_number: issueNumber,
state: { value: "closed", ...intentMetadata },

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.

Unverified API contract: state: { value: "closed", ...intentMetadata } deviates from the GitHub REST PATCH-issues spec, which requires state to be the string "closed" — passing an object will produce a 422 and always fall back to the legacy path, so intent metadata is never persisted.

💡 Details

The standard REST API endpoint PATCH /repos/{owner}/{repo}/issues/{issue_number} documents state as a string enum ("open" | "closed"). Sending an object here will cause the server to reject the request, the catch block will swallow the error, and the legacy fallback (github.rest.issues.update) runs with no metadata — identical to the pre-fix behaviour.

If the issue-intent API uses a different shape (e.g. a GraphQL mutation or a preview endpoint), the route string and/or the field name must change accordingly. If this shape has been verified against the target API, add a comment citing the spec so reviewers can confirm it.

Expected REST shape if metadata stays at the top level:

{
  owner, repo, issue_number: issueNumber,
  state: "closed",
  state_reason: baseParams.state_reason,
  ...intentMetadata,   // rationale, confidence, etc. alongside state, not inside it
}


try {
core.info(`Assigning via issues assignees REST API with login: ${agentLogin}`);
const assignee = useIssueIntent && intentMetadata && Object.keys(intentMetadata).length > 0 ? { login: agentLogin, ...intentMetadata } : agentLogin;

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.

Broken API contract: the GitHub REST POST .../assignees endpoint expects assignees to be an array of login strings; sending an object { login, ...intentMetadata } will cause a 422 or silent failure, leaving the issue unassigned.

💡 Details

The diff parallels the description with add_labels per-item objects, but the assignees REST endpoint has no such per-element object shape in its spec. Sending [{ login: '...', rationale: '...' }] instead of `['...']' will fail.

If the issue-intent API supports a non-standard per-assignee metadata shape, that must be documented with a spec reference. Otherwise the metadata needs to be carried at a different level in the request, not inside the assignee element.

Minimal safe fix (keeps previous logic, avoids regression):

const assignParams = {
  owner: targetOwner,
  repo: targetRepo,
  issue_number: issueNumber,
  assignees: [agentLogin],
};
if (useIssueIntent && intentMetadata && Object.keys(intentMetadata).length > 0) {
  // intentMetadata goes wherever the target API actually reads it
  Object.assign(assignParams, intentMetadata);
}

@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 — the root-cause fix is correct, minor test coverage gaps noted.

📋 Key Themes & Highlights

Key Themes

  • Correct fix: metadata was silently dropped at top-level by the GitHub API; nesting it inside the state object (close path) and inside the per-assignee object (assign path) is the right structural change.
  • Test coverage gaps: the intent path is tested, but state_reason forwarding and the empty-metadata fallback path lack explicit assertions (see inline comments).

Positive Highlights

  • ✅ PR description is clear and includes before/after code snippets — easy to verify intent.
  • ✅ Both call sites (close and assign) are updated consistently.
  • ✅ Legacy fallback path (github.rest.issues.update with scalar state) is correctly left unchanged.
  • ✅ Tests are updated in the same commit as the implementation.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 33 AIC · ⌖ 4.55 AIC · ⊞ 6.7K
Comment /matt to run again

expect(requestCalls[0].params.state).toEqual({ value: "closed", rationale: "Duplicate confirmed", confidence: "MEDIUM" });
expect(requestCalls[0].params.rationale).toBeUndefined();
expect(requestCalls[0].params.confidence).toBeUndefined();
expect(requestCalls[0].params.headers).toBeUndefined();

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 intent-path test doesn't assert that state_reason is still forwarded — a future refactor could silently drop it.

💡 Add the missing assertion
expect(requestCalls[0].params.state_reason).toBe("completed");

Without this, the test wouldn't catch a regression where state_reason is accidentally omitted from the intent request body.

@copilot please address this.

assignees: [{ login: "copilot-swe-agent[bot]", rationale: "Agent owns the code path", confidence: "HIGH" }],
});
});

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 test covers the fallback where useIssueIntent is false or intentMetadata is empty — the assignee should remain a plain string in those cases.

💡 Suggested test
it("uses plain string assignee when intent metadata is absent", async () => {
  // setup: useIssueIntent=true but intentMetadata={}
  // expect: assignees: ["copilot-swe-agent[bot]"]  (not an object)
});

Without this, a regression that always emits the object form would go undetected.

@copilot please address this.

@pelikhan
pelikhan merged commit 9dc71fb into main Jul 20, 2026
53 checks passed
@pelikhan
pelikhan deleted the copilot/fix-issue-intent-metadata-shape branch July 20, 2026 14:23
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.14

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.

Issue-intent handlers use wrong payload shape (close_issue + assign_to_agent): metadata must be nested, not top-level

3 participants