Skip to content

feat: support explicit custom key for close-older matching#21076

Merged
pelikhan merged 4 commits intomainfrom
copilot/add-custom-key-for-close-older
Mar 15, 2026
Merged

feat: support explicit custom key for close-older matching#21076
pelikhan merged 4 commits intomainfrom
copilot/add-custom-key-for-close-older

Conversation

Copy link
Contributor

Copilot AI commented Mar 15, 2026

close-older-issues/close-older-discussions deduplication relies on gh-aw-workflow-id / gh-aw-workflow-call-id markers, which can collide when multiple caller workflows share a reusable workflow. This adds an optional close-older-key to give users a stable, explicit deduplication key that bypasses marker-identity inference entirely.

New config

safe-outputs:
  create-issue:
    close-older-issues: true
    close-older-key: "my-stable-key"   # optional

  create-discussion:
    close-older-discussions: true
    close-older-key: "my-stable-key"   # optional

Behavior

  • When close-older-key is set: the value is normalized to identifier style (lowercase, alphanumeric/dash/underscore only), then <!-- gh-aw-close-key: <normalized-value> --> is embedded in the body and used as the primary GitHub search term and exact-match filter — workflow-id markers are ignored for matching
  • When not set: existing behavior unchanged (backward compatible)
  • Invalid keys (empty or normalizing to empty, e.g. all-whitespace or all-special-characters) cause an explicit error instead of silently falling back to workflow-id matching

Changes

  • generate_footer.cjs — new normalizeCloseOlderKey() helper (identifier-style normalization), generateCloseKeyMarker() / getCloseKeyMarkerContent() helpers
  • close_older_issues.cjs / close_older_discussions.cjs — accept optional closeOlderKey; when set, substitute the close-key marker as the search term and exact-match predicate
  • create_issue.cjs — reads config.close_older_key, normalizes it, embeds marker in body, forwards to closeOlderIssues(); throws on empty-after-normalization
  • create_discussion.cjs — same; additionally wires in the previously-missing closeOlderDiscussions() call after successful direct discussion creation (existing gap)
  • Go structs (create_issue.go, create_discussion.go) — add CloseOlderKey string field
  • compiler_safe_outputs_config.go — emit close_older_key into JS handler config for both issue and discussion builders
  • main_workflow_schema.json — add close-older-key string property with pattern: "\\S" (rejects whitespace-only values) to both issue and discussion schemas
  • Tests — 16 new tests: 7 covering key-based search query construction, exact-match filtering, empty-workflowId fallback, and key-over-callerWorkflowId precedence; 9 covering normalizeCloseOlderKey normalization behavior
  • smoke-copilot.md — added close-older-key: "smoke-copilot" to both create-issue and create-discussion as a smoke test of the new feature

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits March 15, 2026 05:25
…ion (#close-older-key)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI linked an issue Mar 15, 2026 that may be closed by this pull request
@pelikhan pelikhan marked this pull request as ready for review March 15, 2026 13:40
Copilot AI review requested due to automatic review settings March 15, 2026 13:40
@pelikhan
Copy link
Contributor

@copilot set close-older-key on one of the smoke-* agentic workflow

Copy link
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

Adds an explicit close-older-key configuration option to make close-older deduplication deterministic across reusable workflow callers, and wires discussion close-older behavior after successful discussion creation.

Changes:

  • Introduce close-older-key across schema, Go config structs, and safe-outputs compiler output.
  • Embed and search on a new <!-- gh-aw-close-key: <value> --> marker to drive close-older matching (issues + discussions).
  • Add/expand JS close-older logic and tests, including key precedence and empty-workflowId behavior.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/workflow/create_issue.go Adds close-older-key to issue config struct for YAML parsing.
pkg/workflow/create_discussion.go Adds close-older-key to discussion config struct for YAML parsing.
pkg/workflow/compiler_safe_outputs_config.go Emits close_older_key into JS handler configs for issues/discussions.
pkg/parser/schemas/main_workflow_schema.json Extends workflow schema with close-older-key for issues/discussions.
actions/setup/js/generate_footer.cjs Adds helper functions to generate/search the close-key marker.
actions/setup/js/create_issue.cjs Parses close_older_key, embeds marker, forwards key to close-older logic.
actions/setup/js/create_discussion.cjs Parses close_older_key, embeds marker, forwards to fallback issue creation, and calls close-older after creating a discussion.
actions/setup/js/close_older_issues.cjs Uses close-key marker as primary search + exact-match predicate when provided.
actions/setup/js/close_older_discussions.cjs Same close-key primary matching behavior for discussions.
actions/setup/js/close_older_issues.test.cjs Adds tests for close-key search query construction and precedence.
actions/setup/js/close_older_discussions.test.cjs Adds tests for close-key search query construction and empty-workflowId behavior.
Comments suppressed due to low confidence (1)

pkg/parser/schemas/main_workflow_schema.json:4252

  • The schema only enforces minLength: 1 for close-older-key, but the runtime code trims the value and treats all-whitespace as unset. Consider adding a pattern that requires at least one non-whitespace character (and optionally forbids newlines / -->) to prevent configurations that will silently disable key-based matching.
                "close-older-key": {
                  "type": "string",
                  "description": "Optional explicit deduplication key for close-older matching. When set, a `<!-- gh-aw-close-key: <value> -->` marker is embedded in the issue body and used as the primary key for searching and filtering older issues instead of the workflow-id markers. This gives deterministic isolation across caller workflows and is stable across workflow renames.",
                  "minLength": 1
                },

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

You can also share your feedback on Copilot code review. Take the survey.

"close-older-key": {
"type": "string",
"description": "Optional explicit deduplication key for close-older matching. When set, a `<!-- gh-aw-close-key: <value> -->` marker is embedded in the discussion body and used as the primary key for searching and filtering older discussions instead of the workflow-id markers. This gives deterministic isolation across caller workflows and is stable across workflow renames.",
"minLength": 1
const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config);
const groupEnabled = parseBoolTemplatable(config.group, false);
const closeOlderIssuesEnabled = parseBoolTemplatable(config.close_older_issues, false);
const closeOlderKey = config.close_older_key ? String(config.close_older_key).trim() : "";
const fallbackToIssue = config.fallback_to_issue !== false; // Default to true
const closeOlderDiscussions = parseBoolTemplatable(config.close_older_discussions, false);
const closeOlderDiscussionsEnabled = parseBoolTemplatable(config.close_older_discussions, false);
const closeOlderKey = config.close_older_key ? String(config.close_older_key).trim() : "";
Comment on lines +134 to +147
function generateCloseKeyMarker(closeKey) {
return `<!-- gh-aw-close-key: ${closeKey} -->`;
}

/**
* Gets the close-key marker content (without XML comment wrapper) for searching.
* This is used when searching for issues/discussions by close-older-key.
*
* @param {string} closeKey - User-supplied close-older deduplication key
* @returns {string} Close-key marker content for search queries
*/
function getCloseKeyMarkerContent(closeKey) {
return `gh-aw-close-key: ${closeKey}`;
}
@pelikhan
Copy link
Contributor

@copilot apply comments. normalize older key before writing down (convert to identifier style string), fail for empty

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

Copilot AI commented Mar 15, 2026

@copilot set close-older-key on one of the smoke-* agentic workflow

Done in d8cf827. Added close-older-key: "smoke-copilot" to both create-issue and create-discussion in smoke-copilot.md. The compiled lock file now includes "close_older_key":"smoke-copilot" in the handler config for both output types.

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

Copilot AI commented Mar 15, 2026

@copilot apply comments. normalize older key before writing down (convert to identifier style string), fail for empty

Done in 1eb0cab. Added normalizeCloseOlderKey() to generate_footer.cjs which converts the key to identifier style (lowercase, alphanumeric/dash/underscore only, collapses consecutive dashes, strips leading/trailing dashes). Both create_issue.cjs and create_discussion.cjs now use this normalization and throw an error if the original key was provided but normalizes to empty. The schema also gains "pattern": "\\S" to reject whitespace-only keys at validation time. 9 new tests cover the normalization behavior.

@pelikhan pelikhan merged commit 12a6436 into main Mar 15, 2026
58 checks passed
@pelikhan pelikhan deleted the copilot/add-custom-key-for-close-older branch March 15, 2026 14:28
strawgate added a commit to elastic/ai-github-actions that referenced this pull request Mar 15, 2026
Sets close-older-key from the title-prefix input on every workflow that
creates issues, giving deterministic deduplication that won't collide
when multiple caller workflows share a reusable workflow.

Requires a compiler bump to a gh-aw version that includes
github/gh-aw#21076 (close-older-key support).

Made-with: Cursor
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.

Feature: support explicit custom key for close-older matching

3 participants