Conversation
…#issue)
Add a first-class `label-names` filter at the `on:` level that generates a
job-level `if:` condition on the `pre_activation` job. When the triggering
label does not match, the entire pre-activation job is skipped (gray ⊘) rather
than failing (red ❌), eliminating noise for unrelated label events.
Usage:
on:
pull_request_target:
types: [labeled]
label-names: [panel-review] # single label or array
Generated condition:
if: github.event.label.name == 'panel-review' || github.event_name == 'workflow_dispatch'
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/69ff1987-fb41-4159-bfd6-d039703c41a2
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ndition string Agent-Logs-Url: https://github.com/github/gh-aw/sessions/69ff1987-fb41-4159-bfd6-d039703c41a2 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
label-names filter to eliminate red ❌ noise on unrelated label events
|
@copilot rename label-names to labels |
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/20a11ac1-45ec-45c4-ab0c-13c1281c364e Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
label-names filter to eliminate red ❌ noise on unrelated label eventslabels filter to eliminate red ❌ noise on unrelated label events
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/89fc8f24-b129-4a19-ba12-8bad7c697df9 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
…ger_label to aw_context Agent-Logs-Url: https://github.com/github/gh-aw/sessions/4db8d600-2c63-49c9-b93e-906ce675b69c Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in f6f919c:
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
🧪 Test Quality Sentinel ReportTest Quality Score: 80/100✅ Excellent
Test Classification Details
Flagged Tests — Requires Review
|
There was a problem hiding this comment.
✅ Test Quality Sentinel: 80/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All three table-driven tests verify observable behavioral contracts, the required build tag is present, and no mock libraries are used.
There was a problem hiding this comment.
Pull request overview
Adds a first-class on.labels frontmatter filter so workflows triggered by labeled/unlabeled events can be cleanly skipped (instead of failing) when an unrelated label is applied.
Changes:
- Adds
on.labelsparsing and plumbsLabelNamesthrough compiler workflow data. - Injects a job-level
if:into thepre_activationjob to skip when the triggering label doesn’t match. - Updates schema/docs and extends agent context with
trigger_label; adds unit tests.
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/role_checks.go | Extracts on.labels and excludes labels from safe-event counting. |
| pkg/workflow/label_names_test.go | Adds tests for label extraction and compiled pre_activation condition behavior. |
| pkg/workflow/compiler_types.go | Adds LabelNames to WorkflowData. |
| pkg/workflow/compiler_pre_activation_job.go | Builds the label-based job-level if: and label condition rendering. |
| pkg/workflow/compiler_orchestrator_workflow.go | Wires extracted label names into WorkflowData. |
| pkg/parser/schemas/main_workflow_schema.json | Documents/validates on.labels in the frontmatter schema. |
| docs/src/content/docs/reference/frontmatter-full.md | Documents the new on.labels option. |
| actions/setup/js/aw_context.cjs | Adds trigger_label into the action’s context payload. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 8/8 changed files
- Comments generated: 6
| github: | ||
| allowed: [get_pull_request] | ||
| ---`, | ||
| expectedIf: "github.event.label.name", |
There was a problem hiding this comment.
This negative test case sets expectedIf to the very broad substring "github.event.label.name" and then asserts it is not present. That’s fragile because the generated workflow could legitimately include github.event.label.name elsewhere (e.g., concurrency grouping or other label-related features), causing unrelated failures. Prefer asserting the absence of the specific label-filter expression (e.g., "github.event.label.name == ''" / "github.event.label.name == '...'") or scoping the search to the pre_activation` job section.
| expectedIf: "github.event.label.name", | |
| expectedIf: "github.event.label.name == ''", |
| # Filter workflows triggered by pull_request_target (or other labeled events) to | ||
| # only fire when the triggering label matches one of these names. Generates a | ||
| # job-level if: condition on the pre-activation job so unmatched label events show | ||
| # as Skipped (⦸) rather than Failed (❌). |
There was a problem hiding this comment.
Documentation uses the skip symbol ⦸ here, but other new/updated comments in this PR use ⊘ (e.g. compiler_pre_activation_job.go). Please standardize on one symbol across docs/schema/code to avoid confusion.
| # as Skipped (⦸) rather than Failed (❌). | |
| # as Skipped (⊘) rather than Failed (❌). |
| } | ||
| }, | ||
| "labels": { | ||
| "description": "Filter workflows triggered by pull_request_target (or other labeled events) to only fire when the triggering label matches one of these names. Generates a job-level if: condition on the pre-activation job so unmatched label events show as Skipped (\u29b8) rather than Failed (\u274c).", |
There was a problem hiding this comment.
Schema description uses the skip symbol \u29b8 (⦸), while other new/updated comments in this PR use ⊘. For consistency with the docs and code comments, consider standardizing on a single symbol (and update both this schema description and the docs text together).
| "description": "Filter workflows triggered by pull_request_target (or other labeled events) to only fire when the triggering label matches one of these names. Generates a job-level if: condition on the pre-activation job so unmatched label events show as Skipped (\u29b8) rather than Failed (\u274c).", | |
| "description": "Filter workflows triggered by pull_request_target (or other labeled events) to only fire when the triggering label matches one of these names. Generates a job-level if: condition on the pre-activation job so unmatched label events show as Skipped (⊘) rather than Failed (\u274c).", |
| // When labels is specified, add a job-level if: condition to the pre-activation job. | ||
| // This causes the entire job to be skipped (gray ⊘) rather than failed (red ❌) when | ||
| // the triggering label does not match, keeping CI dashboards noise-free. | ||
| // workflow_dispatch is always allowed so manual runs are not blocked. | ||
| if len(data.LabelNames) > 0 { | ||
| labelIfCondition := buildLabelNamesCondition(data.LabelNames) | ||
| if jobIfCondition != "" { | ||
| jobIfCondition = RenderCondition(BuildAnd( | ||
| &ExpressionNode{Expression: labelIfCondition}, | ||
| &ExpressionNode{Expression: jobIfCondition}, | ||
| )) | ||
| } else { | ||
| jobIfCondition = labelIfCondition | ||
| } | ||
| } |
There was a problem hiding this comment.
on.labels filtering is implemented as a job-level if: on pre_activation, but pre_activation is only created when other checks are present. If a workflow sets on.labels on an otherwise “safe” trigger (no role check, stop-time, on.steps, etc.), pre_activation may not be generated at all, so the label filter would never apply and unrelated label events would still run. Consider treating LabelNames as a reason to create pre_activation (see buildPreActivationAndActivationJobs in compiler_jobs.go) or moving the label condition to a job that is always present (e.g., activation).
| // - the event has no label data (github.event.label.name is empty, which covers | ||
| // workflow_dispatch, push, schedule, and any other non-labeled events), OR | ||
| // - the triggering label name matches any of the specified names. | ||
| // | ||
| // This ensures the filter only acts on events that actually carry label information, | ||
| // leaving all other events (e.g. workflow_dispatch, push) unaffected. | ||
| func buildLabelNamesCondition(labelNames []string) string { | ||
| // Pass through events without label data: github.event.label.name is empty | ||
| // for workflow_dispatch, push, and any event that does not carry a label payload. | ||
| noLabelEvent := BuildEquals( | ||
| BuildPropertyAccess("github.event.label.name"), | ||
| BuildStringLiteral(""), |
There was a problem hiding this comment.
The “no label data” branch relies on github.event.label.name == '', but GitHub Actions missing context properties commonly evaluate to null (this repo already uses == null checks in other generated expressions). If github.event.label/.name is null on non-labeled events (push, schedule, workflow_dispatch, etc.), this condition will be false and the job would be skipped unexpectedly. Suggest expanding the pass-through guard to handle null (e.g., github.event.label == null / github.event.label.name == null) or guarding on github.event.action not being labeled/unlabeled.
| // - the event has no label data (github.event.label.name is empty, which covers | |
| // workflow_dispatch, push, schedule, and any other non-labeled events), OR | |
| // - the triggering label name matches any of the specified names. | |
| // | |
| // This ensures the filter only acts on events that actually carry label information, | |
| // leaving all other events (e.g. workflow_dispatch, push) unaffected. | |
| func buildLabelNamesCondition(labelNames []string) string { | |
| // Pass through events without label data: github.event.label.name is empty | |
| // for workflow_dispatch, push, and any event that does not carry a label payload. | |
| noLabelEvent := BuildEquals( | |
| BuildPropertyAccess("github.event.label.name"), | |
| BuildStringLiteral(""), | |
| // - the event has no label data (github.event.label is null, github.event.label.name | |
| // is null, or github.event.label.name is empty, which covers workflow_dispatch, | |
| // push, schedule, and any other non-labeled events), OR | |
| // - the triggering label name matches any of the specified names. | |
| // | |
| // This ensures the filter only acts on events that actually carry label information, | |
| // leaving all other events (e.g. workflow_dispatch, push) unaffected. | |
| func buildLabelNamesCondition(labelNames []string) string { | |
| // Pass through events without label data: non-labeled events may expose | |
| // github.event.label or github.event.label.name as null, and some contexts may | |
| // normalize the missing label name to an empty string. | |
| noLabelEvent := BuildOr( | |
| ConditionNode(BuildEquals( | |
| BuildPropertyAccess("github.event.label"), | |
| BuildLiteral("null"), | |
| )), | |
| BuildOr( | |
| ConditionNode(BuildEquals( | |
| BuildPropertyAccess("github.event.label.name"), | |
| BuildLiteral("null"), | |
| )), | |
| BuildEquals( | |
| BuildPropertyAccess("github.event.label.name"), | |
| BuildStringLiteral(""), | |
| ), | |
| ), |
| for _, name := range labelNames { | ||
| result = BuildOr(result, BuildEquals( | ||
| BuildPropertyAccess("github.event.label.name"), | ||
| BuildStringLiteral(name), | ||
| )) | ||
| } |
There was a problem hiding this comment.
Label names are rendered into single-quoted GitHub Actions string literals, but the string literal renderer doesn’t escape embedded ' characters. A label like can't-repro would generate an invalid expression (or change the expression meaning). Consider escaping single quotes in name before calling BuildStringLiteral (GitHub Actions uses doubled quotes inside single-quoted strings) or centralizing escaping in the string literal renderer.
Generated by Design Decision Gate — author must review and finalize before the PR can merge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Commit pushed:
|
🏗️ Design Decision Gate — ADR RequiredThis PR makes significant changes to core business logic (314 new lines in AI has analyzed the PR diff and generated a draft ADR to help you get started: 📄 Draft ADR: What to do next
Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision. 📋 Draft ADR SummaryThe draft captures the following decision framework inferred from the diff: Decision: Add a first-class Driver: GitHub Actions provides no native label-name filter for Alternatives documented:
Key consequences:
📋 Michael Nygard ADR Format ReferenceAn ADR must contain these four sections to be considered complete:
All ADRs are stored in
References: §25006216146
|
|
@copilot review all comments. Only apply with event has label info |
…uote escaping, symbol, test Agent-Logs-Url: https://github.com/github/gh-aw/sessions/08916acf-6edb-4176-925a-c95f33a82f6e Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/08916acf-6edb-4176-925a-c95f33a82f6e Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Addressed all review comments in e57b589 and 109e7dd:
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot merge main and recompile |
…-class-label-filter # Conflicts: # pkg/workflow/compiler_types.go Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in fd3f8f4 — merged Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
| "description": "Filter workflows triggered by pull_request_target (or other labeled events) to only fire when the triggering label matches one of these names. Generates a job-level if: condition on the pre-activation job so unmatched label events show as Skipped (\u2298) rather than Failed (\u274c).", | ||
| "oneOf": [ | ||
| { | ||
| "type": "string", |
There was a problem hiding this comment.
@copilot define a non-empty string type and use it here to avoid labels: ['']
There was a problem hiding this comment.
Done in 0a70bdc — added a non_empty_string $def ({ "type": "string", "minLength": 1 }) to the schema's $defs section and updated the on.labels field to use $ref: "#/$defs/non_empty_string" for both the scalar and array-item forms, preventing labels: [''].
…event empty label names Agent-Logs-Url: https://github.com/github/gh-aw/sessions/afdedf8f-08bb-4c0e-aa2b-d9613130ff93 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
pull_request_target(and other labeled events) had no native label-name filter — the only workaround wasexit 1inon.steps, which causes a red ❌ failure on every unrelated label add instead of a clean gray ⊘ skip.Changes
on.labels— new top-levelon:field (mirrorsroles:/bots:shape); accepts a string or array of label namesif:— whenlabelsis set,buildPreActivationJob()injects a job-level condition that skips the entirepre_activationjob when the triggering label doesn't match; events without a label payload (github.event.label == null) are always allowed throughpre_activationalways created withlabels—hasLabelNamesadded to thebuildPreActivationAndActivationJobscondition so a labels-only workflow (no roles, stop-time, etc.) correctly gets apre_activationjobhasSafeEventsOnly()—labelsnow excluded from event counting (same pattern asroles,bots)labelsadded tomain_workflow_schema.jsonunderon:trigger_labelin agent context —buildAwContext()inaw_context.cjsnow includestrigger_label: context.payload?.label?.name ?? ""so the AI agent receives the triggering label name as part of its context payloadStringLiteralNode.Render()now escapes embedded'as''(GitHub Actions doubling convention) so label names likecan't-reproproduce valid expressionsExample
Compiled output on
pre_activation:The condition uses
github.event.label == nullto pass through any event that doesn't carry a label object (workflow_dispatch, push, schedule, etc.), and only applies the label name check for events that actually have a label payload.Unmatched label events now show as ⊘ Skipped across the whole workflow rather than ❌ Failed on
pre_activation.