Skip to content

Make add_comment wildcard target misses non-fatal in safe outputs#37041

Merged
pelikhan merged 6 commits into
mainfrom
copilot/aw-failures-fix-add-comment-safety
Jun 5, 2026
Merged

Make add_comment wildcard target misses non-fatal in safe outputs#37041
pelikhan merged 6 commits into
mainfrom
copilot/aw-failures-fix-add-comment-safety

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 5, 2026

Contribution Check and PR Sous Chef were intermittently failing in safe_outputs after successful agent work because a single add_comment with target: "*" and no concrete target number hard-failed the job. This dropped valid outputs and prevented intended comments from posting.

  • Safe-output handler behavior

    • Updated add_comment handling to treat unresolved wildcard targets as skip + info log (non-fatal) instead of a hard failure.
    • Scope is intentionally narrow: only applies when target: "*" and no explicit numeric target field is provided.
    • Preserves warning-level logging for real target-resolution failures.
  • Target detection clarity

    • Centralized accepted explicit wildcard target fields in the handler logic:
      • item_number, issue_number, pull_request_number, pr_number, pr, pull_number
    • Preserves existing strict behavior for other target-resolution errors.
  • Regression coverage

    • Added a complementary test to ensure strict behavior is preserved when an explicit wildcard target field is present but invalid (hard-fail, not skip).
  • Workflow prompt contract alignment

    • Verified workflow guidance and contract expectations remain explicit that emitted add_comment payloads must carry numeric targets in Contribution Check / PR Sous Chef paths.
if (targetResult.shouldFail) {
  const hasExplicitWildcardTargetField = WILDCARD_TARGET_FIELDS.some(field => message[field] != null);
  const missingWildcardTarget = commentTarget === "*" && !hasExplicitWildcardTargetField;
  if (missingWildcardTarget) {
    core.info(targetResult.error);
    return { success: false, skipped: true, error: targetResult.error };
  }
  core.warning(targetResult.error);
  return { success: false, error: targetResult.error };
}

Copilot AI and others added 3 commits June 5, 2026 02:56
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix targetless add_comment to be non-fatal in safe_outputs Make add_comment wildcard target misses non-fatal in safe outputs Jun 5, 2026
Copilot AI requested a review from pelikhan June 5, 2026 03:01
@pelikhan pelikhan marked this pull request as ready for review June 5, 2026 03:20
Copilot AI review requested due to automatic review settings June 5, 2026 03:20
Copy link
Copy Markdown
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

This PR adjusts the add_comment safe-output handler so that a specific target-resolution failure mode is treated as non-fatal: when target: "*" is configured but the emitted safe-output message lacks any explicit numeric target fields, the handler now returns { success: false, skipped: true } (and logs a warning) instead of hard-failing processing.

Changes:

  • Updated actions/setup/js/add_comment.cjs to detect the “wildcard target with no explicit target fields” case and return a skipped result rather than a hard failure.
  • Updated actions/setup/js/add_comment.test.cjs to assert skipped: true for that scenario.
Show a summary per file
File Description
actions/setup/js/add_comment.cjs Treat unresolved target: "*" with no explicit target fields as skip+warning (non-fatal) instead of a hard failure.
actions/setup/js/add_comment.test.cjs Updates the test to expect skipped: true for the unresolved wildcard-target case.

Copilot's findings

Tip

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

  • Files reviewed: 2/2 changed files
  • Comments generated: 0

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

PR Code Quality Reviewer completed the code quality review.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #37041 does not have the implementation label (has_implementation_label=false) and has 0 new lines of code in business logic directories (well under the 100-line threshold). The changes are limited to shell/python skill scripts and an MCP server file, none of which fall in the configured business logic directories.

@github-actions github-actions Bot mentioned this pull request Jun 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 1 test (modified): 1 design, 0 implementation, 0 guideline violations.

📊 Metrics & Test Classification (1 test analyzed)
Metric Value
New/modified tests analyzed 1
✅ Design tests (behavioral contracts) 1 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 1 (100%)
Duplicate test clusters 0
Test inflation detected No (test +2 lines / production +10 lines = 0.2:1)
🚨 Coding-guideline violations 0

Test Classification Details

Test File Classification Issues Detected
should skip (not fail) when target is '*' but no item_number provided actions/setup/js/add_comment.test.cjs:225 ✅ Design None

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 0 tests
  • 🟨 JavaScript (*.test.cjs, *.test.js): 1 test (vitest)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). The modified test correctly verifies the new skipped: true behavioral contract with three observable-output assertions covering the error path (wildcard target with no item_number).

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 868.8K · 129 AIC ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). The modified test directly verifies the new skipped: true behavioral contract with full error-path coverage.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnose and /tdd — approving with minor suggestions on test coverage and log level.

📋 Key Themes & Highlights

Key Themes

  • Narrow, correct fix: The skip-vs-fail branch is tightly scoped to target: "*" + no explicit field. The symmetry with the existing non-shouldFail else branch (which already returns skipped: true) is clean and intentional.
  • Test coverage gap: The skip path is now tested, but the complementary hard-fail path (explicit field present → strict behavior preserved) is not. One extra test case closes this gap.
  • Log level mismatch: core.warning fires before the skip check, so non-fatal skips still appear as yellow warnings in the Actions log. Restructuring the log call to follow the skip decision would better communicate intent.
  • WILDCARD_TARGET_FIELDS list includes pre-handled fields: item_number and issue_number are resolved earlier by resolveSafeOutputIssueTarget and will always be null/absent at the resolveTarget failure site. The list works correctly but silently carries dead entries — worth a clarifying comment.

Positive Highlights

  • ✅ Root cause properly addressed: the exact combination of target: "*" + no numeric field was the intermittent failure trigger
  • ✅ Existing strict behavior for real target-resolution errors is structurally preserved by the early-return ordering
  • ✅ Test description updated to match the new behavioral contract (should skip (not fail))
  • WILDCARD_TARGET_FIELDS constant centralizes the six accepted field names — a good pattern to prevent future drift

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 1.9M · 233.7 AIC

});

it("should fail when target is '*' but no item_number provided", async () => {
it("should skip (not fail) when target is '*' but no item_number provided", async () => {
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] Missing regression test for the "strict behavior preserved" invariant — when target: "*" fires with an explicit PR field present, the handler should still hard-fail.

The updated test only locks in the skip path. Without a complementary test for the hard-fail path, the !hasExplicitWildcardTargetField condition can be accidentally inverted without breaking the suite.

💡 Suggested additional test

Add a test adjacent to the current one:

it("should hard-fail (not skip) when target is * but explicit field resolves nothing meaningful", async () => {
  // Tests that having a recognized field still triggers hard-fail
  // when resolveTarget returns shouldFail (strict behavior preserved)
  const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8");

  const handler = await eval(`(async () => { ${addCommentScript}; return await main({ target: * }); })()`);

  const message = {
    type: "add_comment",
    pull_request_number: 42, // explicit field present — should still hard-fail
    body: "Test comment",
  };

  const result = await handler(message, {});

  expect(result.success).toBe(false);
  expect(result.skipped).toBeUndefined(); // NOT a skip — explicit field was provided
});

This makes the skip-vs-fail boundary explicit and protects against accidental widening of the skip condition.

Comment thread actions/setup/js/add_comment.cjs Outdated
if (targetResult.shouldFail) {
const hasExplicitWildcardTargetField = WILDCARD_TARGET_FIELDS.some(field => message[field] != null);
const missingWildcardTarget = commentTarget === "*" && !hasExplicitWildcardTargetField;
core.warning(targetResult.error);
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.

[/diagnose] core.warning fires unconditionally before the skip check, so skipped wildcard misses still surface as yellow warnings in the GitHub Actions log — contradicting the non-fatal intent.

The PR description explicitly calls this "skip + warning", so if warning-level logging is intentional here, a brief comment would clarify the choice. Otherwise, demoting to core.info for the skip path keeps logs clean.

💡 Suggested restructuring

Move the log level decision after the skip check:

const hasExplicitWildcardTargetField = WILDCARD_TARGET_FIELDS.some(field => message[field] != null);
const missingWildcardTarget = commentTarget === "*" && !hasExplicitWildcardTargetField;
if (missingWildcardTarget) {
  core.info(targetResult.error); // non-fatal: expected when no target field is provided
  return { success: false, skipped: true, error: targetResult.error };
}
core.warning(targetResult.error); // still a real failure
return { success: false, error: targetResult.error };


/** @type {string} Safe output type handled by this module */
const HANDLER_TYPE = "add_comment";
const WILDCARD_TARGET_FIELDS = ["item_number", "issue_number", "pull_request_number", "pr_number", "pr", "pull_number"];
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.

[/diagnose] item_number and issue_number in WILDCARD_TARGET_FIELDS are pre-handled by resolveSafeOutputIssueTarget at line 471 (using the same aliases). By the time resolveTarget fails, those two fields are already absent or null in the message — making their presence in this list technically dead weight.

The fields that can actually reach this code path unaided are pull_request_number, pr_number, pr, and pull_number. A short JSDoc comment would make this intent explicit and prevent future drift.

📋 Reasoning
  • resolveSafeOutputIssueTarget (line 471) uses aliases ["item_number", "issue_number", "pr-number"]
  • If any of these are non-null, it resolves before reaching resolveTarget — we never enter the shouldFail branch
  • If it succeeds with number: null, those fields are absent/null in the message
  • So at the wildcard check site, message.item_number != null and message.issue_number != null are always false

Consider narrowing and documenting:

// Fields recognized by resolveTarget (pull-only path, supportsIssue: false).
// item_number/issue_number/pr-number are handled earlier by resolveSafeOutputIssueTarget.
const WILDCARD_TARGET_FIELDS = ["pull_request_number", "pr_number", "pr", "pull_number"];

Or keep the full list and add a comment explaining it serves as a belt-and-suspenders guard.

@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Jun 5, 2026

@copilot review all comments and reviews
Apply copilot-review skill

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

Copilot AI commented Jun 5, 2026

@copilot review all comments and reviews
Apply copilot-review skill

Reviewed all comments/reviews and applied actionable feedback in 07064c4.

Changes made:

  • Added a regression test for the strict hard-fail path when target: "*" has an explicit but invalid pull_request_number.
  • Adjusted logging so targetless wildcard skips log at info, while true failures remain warning.
  • Added a clarifying note on wildcard target field intent.

Validation run:

  • npm run lint:cjs
  • npm run test:js -- add_comment.test.cjs
  • parallel_validation (Code Review + CodeQL)

@pelikhan pelikhan merged commit f989b3c into main Jun 5, 2026
6 of 7 checks passed
@pelikhan pelikhan deleted the copilot/aw-failures-fix-add-comment-safety branch June 5, 2026 04:32
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.

[aw-failures] Targetless add_comment fails safe_outputs for Contribution Check & PR Sous Chef

3 participants