Summary
The create-issue safe output accepts a deduplicate-by-title option, but the compiler parses it into the config struct and then never emits it into the runtime handler config. As a result the option is silently ignored: it has no effect at runtime, and the compiler reports no error or warning. Sibling options like close-older-issues and group-by-day work correctly, which makes the gap easy to miss.
I hit this while adding a monthly scheduled workflow that opens one tracking issue per run. I wanted deduplicate-by-title to prevent duplicate issues on manual re-runs, and discovered the setting compiles away with no diagnostic.
Reproduction
Minimal workflow (.github/workflows/dedup-repro.md):
---
name: Dedup Repro
description: minimal repro
on:
workflow_dispatch:
permissions:
contents: read
issues: read
engine:
id: copilot
model: gpt-5-mini
timeout-minutes: 10
safe-outputs:
create-issue:
title-prefix: "Repro - "
max: 1
deduplicate-by-title: true
close-older-issues: true
---
# Dedup Repro
This body is only here to satisfy the compiler minimum length for the agent instructions section.
Compile it and inspect the emitted handler config:
$ gh aw compile dedup-repro
$ grep -oE '"create_issue":\{[^}]*\}' .github/workflows/dedup-repro.lock.yml
"create_issue":{"close_older_issues":true,"max":1,"title_prefix":"Repro - "}
close_older_issues is present, but deduplicate-by-title is absent from the emitted config. The same happens for deduplicate-by-title: 0 and deduplicate-by-title: 1.
Expected vs actual
- Expected: the emitted
create_issue handler config includes the deduplication setting (for example "deduplicate_by_title": true), so the runtime handler deduplicates by title as documented in safe-outputs.md.
- Actual: the field is dropped from the emitted config with no error or warning, so deduplication never runs.
Root cause
I traced this in pkg/workflow. The field is declared on the config struct but is not wired into the emission path:
pkg/workflow/create_issue.go:20 declares DeduplicateByTitle any with the yaml tag deduplicate-by-title.
pkg/workflow/create_issue.go:37-38 passes the field lists that drive normalization and emission:
BoolFields: []string{"close-older-issues", "group", "footer", "group-by-day"},
IntFields: []string{"max"},
deduplicate-by-title is in neither list. Because it can be a boolean or a non-negative integer, it fits neither BoolFields nor IntFields cleanly.
pkg/workflow/create_entity_helpers.go:59-71 normalizes only the fields named in BoolFields and IntFields on the raw config map. Fields outside those lists are not carried into the emitted handler config.
- A search of
pkg/workflow/*.go (non-test) finds DeduplicateByTitle only at that struct declaration, with no reference in the config-generation code. By contrast, close-older-issues, group-by-day, footer, and max are all named in the field lists and do get emitted.
So the value is unmarshaled onto the struct and then never propagated to the runtime handler config.
Proposed fix and implementation plan
deduplicate-by-title accepts either a boolean (true for exact-title match) or a non-negative integer (edit distance), so it needs handling that neither BoolFields nor IntFields provides today.
- Add a normalization path for mixed boolean-or-integer fields (for example a
BoolOrIntFields list in CreateParseOptions, or a dedicated preprocess step) in pkg/workflow/create_entity_helpers.go, so the raw value is normalized and preserved on the emitted config map.
- Register
deduplicate-by-title through that path in parseCreateIssuesConfig (pkg/workflow/create_issue.go), so it is emitted as a stable key (for example deduplicate_by_title).
- Confirm the runtime handler in
actions/setup/js reads that key and applies title deduplication for both the boolean and integer forms.
- Add a compiler test alongside the existing create-issue output tests (see
pkg/workflow/compile_outputs_issue_test.go) asserting the emitted create_issue config contains the deduplication key for deduplicate-by-title: true, 0, and 1.
- Consider a validation error when the value is neither a boolean nor a non-negative integer, following the error template in CONTRIBUTING (
[what's wrong]. [what's expected]. [example]).
As an interim guardrail, the compiler could warn when a schema-valid create-issue field is dropped from the emitted config, so silently-ignored options surface at compile time rather than at runtime.
Environment
- Reproduced with
gh aw extension v0.76.1.
- Root cause traced against
githubnext/gh-aw main (current v0.82.x), where the same field-list omission is present.
Summary
The
create-issuesafe output accepts adeduplicate-by-titleoption, but the compiler parses it into the config struct and then never emits it into the runtime handler config. As a result the option is silently ignored: it has no effect at runtime, and the compiler reports no error or warning. Sibling options likeclose-older-issuesandgroup-by-daywork correctly, which makes the gap easy to miss.I hit this while adding a monthly scheduled workflow that opens one tracking issue per run. I wanted
deduplicate-by-titleto prevent duplicate issues on manual re-runs, and discovered the setting compiles away with no diagnostic.Reproduction
Minimal workflow (
.github/workflows/dedup-repro.md):Compile it and inspect the emitted handler config:
close_older_issuesis present, butdeduplicate-by-titleis absent from the emitted config. The same happens fordeduplicate-by-title: 0anddeduplicate-by-title: 1.Expected vs actual
create_issuehandler config includes the deduplication setting (for example"deduplicate_by_title": true), so the runtime handler deduplicates by title as documented in safe-outputs.md.Root cause
I traced this in
pkg/workflow. The field is declared on the config struct but is not wired into the emission path:pkg/workflow/create_issue.go:20declaresDeduplicateByTitle anywith the yaml tagdeduplicate-by-title.pkg/workflow/create_issue.go:37-38passes the field lists that drive normalization and emission:deduplicate-by-titleis in neither list. Because it can be a boolean or a non-negative integer, it fits neitherBoolFieldsnorIntFieldscleanly.pkg/workflow/create_entity_helpers.go:59-71normalizes only the fields named inBoolFieldsandIntFieldson the raw config map. Fields outside those lists are not carried into the emitted handler config.pkg/workflow/*.go(non-test) findsDeduplicateByTitleonly at that struct declaration, with no reference in the config-generation code. By contrast,close-older-issues,group-by-day,footer, andmaxare all named in the field lists and do get emitted.So the value is unmarshaled onto the struct and then never propagated to the runtime handler config.
Proposed fix and implementation plan
deduplicate-by-titleaccepts either a boolean (truefor exact-title match) or a non-negative integer (edit distance), so it needs handling that neitherBoolFieldsnorIntFieldsprovides today.BoolOrIntFieldslist inCreateParseOptions, or a dedicated preprocess step) inpkg/workflow/create_entity_helpers.go, so the raw value is normalized and preserved on the emitted config map.deduplicate-by-titlethrough that path inparseCreateIssuesConfig(pkg/workflow/create_issue.go), so it is emitted as a stable key (for examplededuplicate_by_title).actions/setup/jsreads that key and applies title deduplication for both the boolean and integer forms.pkg/workflow/compile_outputs_issue_test.go) asserting the emittedcreate_issueconfig contains the deduplication key fordeduplicate-by-title: true,0, and1.[what's wrong]. [what's expected]. [example]).As an interim guardrail, the compiler could warn when a schema-valid
create-issuefield is dropped from the emitted config, so silently-ignored options surface at compile time rather than at runtime.Environment
gh awextensionv0.76.1.githubnext/gh-awmain(currentv0.82.x), where the same field-list omission is present.