Skip to content

close_issue drops the agent's state_reason during validation, so duplicate is never chosen at runtime #67232

Closed

Description

@myaschmitz

The problem:

close_issue offers state_reason to the agent, but the safe-output validator strips it. Every close falls back to the first configured reason. Native duplicate marking only runs when duplicate is listed first.

Root cause:

  • computePropertyInjections in pkg/workflow/safe_outputs_tools_generation.go adds state_reason to the close_issue tool schema. This came from Allow close_issue agents to choose state-reason at runtime #47045.
  • ValidationConfig["close_issue"] in pkg/workflow/safe_outputs_validation_config.go doesn't declare state_reason.
  • Since Harden safe-output field validation #51133, validateItem in actions/setup/js/safe_output_type_validator.cjs keeps only declared fields. So state_reason is gone before close_issue.cjs runs.
  • close_issue.cjs then uses allowed_state_reason[0]. If that isn't duplicate, it logs duplicate_of is set but state_reason is completed (not DUPLICATE); native duplicate marking will be skipped.
  • TestValidationConfigCoversToolInputSchemas missed it. It checks the static safe_outputs_tools.json, not injected properties.

Impact:

  • state-reason: [duplicate, completed] closes every issue as duplicate.
  • state-reason: [completed, duplicate] never closes an issue as duplicate.
  • The agent's choice is ignored either way.

Evidence:

We tested an issue triage workflow in a test repository:

  • v0.85.4 (before Harden safe-output field validation #51133): about 25 duplicates closed as duplicate.
  • v0.89.21 with [duplicate, completed]: an issue that was already fixed closed as duplicate. The run logged Closing issue #N with state_reason=duplicate, though the agent asked for completed.
  • v0.89.21 with [completed, duplicate]: two real duplicates closed as completed. The run logged Closing issue #N with state_reason=completed.

Local repro on main (86f7afc): calling validateItem on {type: "close_issue", issue_number: 2, duplicate_of: 1, state_reason: "duplicate"} returns no state_reason. Declaring the field returns state_reason: "duplicate".

Agentic plan:

  1. In pkg/workflow/safe_outputs_validation_config.go, add this field to close_issue: "state_reason": {Type: "string", Enum: closeIssueStateReasonValues}. Keep the values lowercase. The validator returns the declared casing, and work_queue_delivery.cjs compares state_reason to the GitHub API value with strict equality.
  2. In pkg/workflow/safe_output_validation_config_test.go, add a test that calls computePropertyInjections for close_issue (omitted and list state-reason) and submit_pull_request_review (with allowed-events). Assert every injected property exists in ValidationConfig[tool].Fields.
  3. In actions/setup/js/safe_output_type_validator.test.cjs, assert a close_issue item with state_reason: "DUPLICATE" keeps state_reason: "duplicate" after validateItem.
  4. Recompile lock files and run make agent-finish.

Acceptance criteria:

  • An agent-selected state_reason reaches close_issue.cjs.
  • With state-reason: [completed, duplicate], an agent that picks duplicate with duplicate_of gets a native duplicate mark.
  • The new test fails if any injected property lacks a validation entry.

Other handlers checked:

  • close_discussion.reason: declared.
  • close_pull_request: no reason field.
  • submit_pull_request_review.event: declared.

Only close_issue.state_reason has the gap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions