Skip to content

Fix close_pull_request target repo resolution at safe-output ingestion and alias handling#37073

Closed
Copilot wants to merge 9 commits into
mainfrom
copilot/close-pull-request-output-fix
Closed

Fix close_pull_request target repo resolution at safe-output ingestion and alias handling#37073
Copilot wants to merge 9 commits into
mainfrom
copilot/close-pull-request-output-fix

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 5, 2026

close_pull_request was not consistently resolving the intended target repository in dynamic safe-output paths, causing operations to fall back to the workflow host repo instead of the requested target repo.

  • Target repo resolution hardening

    • Updated repo lookup in repo_helpers.cjs to support all accepted config key variants for default target repo:
      • target-repo
      • target_repo
      • targetRepo
    • Updated per-message repo override resolution to also honor alias fields (in priority order with repo as canonical):
      • repo
      • target-repo
      • target_repo
      • targetRepo
    • This keeps close-pull-request aligned with dynamic config/message shapes that may normalize keys differently.
  • Safe outputs MCP ingestion fix

    • Updated strict-schema argument sanitization in safe_outputs_tools_loader.cjs so repo alias inputs are normalized at ingestion time:
      • target-repo
      • target_repo
      • targetRepo
      • → canonical repo
    • Added repo to close_pull_request tool schema in safe_outputs_tools.json so canonical repo survives strict schema filtering.
    • This ensures target repo routing is preserved through MCP ingestion instead of falling back to the workflow host repo.
  • Regression coverage

    • Added focused tests in repo_helpers.test.cjs to assert alias handling for:
      • config aliases: target_repo, targetRepo
      • message aliases: target_repo, targetRepo
    • Added close_pull_request.test.cjs coverage to verify PR close operations route to the aliased message target repo (target_repo) rather than the workflow host repository.
    • Added safe_outputs_tools_loader.test.cjs coverage for strict-schema ingestion behavior:
      • repo alias normalization (target_reporepo)
      • canonical repo precedence over alias fields.

Copilot AI and others added 2 commits June 5, 2026 06:15
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 close_pull_request to interpolate dynamic input for target repo Fix close_pull_request default target repo resolution for dynamic config aliases Jun 5, 2026
Copilot AI requested a review from pelikhan June 5, 2026 06:16
@pelikhan pelikhan marked this pull request as ready for review June 5, 2026 10:56
Copilot AI review requested due to automatic review settings June 5, 2026 10:56
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 hardens default target repository resolution for safe-output handlers by teaching repo_helpers.getDefaultTargetRepo to recognize additional dynamic-config key variants (target_repo, targetRepo) in addition to target-repo, aligning close_pull_request behavior with other handlers that already resolve these shapes correctly.

Changes:

  • Extend getDefaultTargetRepo(config) to accept target_repo (snake_case) and targetRepo (camelCase) aliases.
  • Expand repo_helpers config JSDoc to document the supported alias keys.
  • Add regression tests covering both new alias variants.
Show a summary per file
File Description
actions/setup/js/repo_helpers.cjs Accepts target_repo and targetRepo config keys when resolving the default target repo.
actions/setup/js/repo_helpers.test.cjs Adds focused tests ensuring alias keys resolve to the expected default target repo.

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

🧠 Matt Pocock Skills Reviewer has completed the skills-based 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 #37073 does not have the 'implementation' label (has_implementation_label=false) and has 0 new lines of code in business logic directories (default_business_additions=0, threshold=100). Neither Condition A nor Condition B is met.

@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

🧪 Test Quality Sentinel completed test quality analysis.

Copy link
Copy Markdown
Collaborator

@pelikhan pelikhan left a comment

Choose a reason for hiding this comment

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

@copilot the fox looks incorrect read the issue:

When safe outputs attempts to close the requested PR, it attempts to close it from the working repo where the agentic workflow resides, instead of the target repo. This results in a failure.

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.

📋 Key Themes & Highlights

Key Themes

  • Precedence not tested: the target-repo > target_repo > targetRepo priority order is not exercised by any test case, so it only lives in the implementation.
  • Falsy-value fallthrough: truthiness guards silently skip empty strings/null — worth documenting the intent (or switching to !== undefined).
  • Upstream normalization opportunity: multi-alias checks in helpers compound over time; a single normalizeConfig() at ingestion would prevent future drift.

Positive Highlights

  • ✅ Root cause correctly identified and fixed in the one place that was inconsistent.
  • ✅ JSDoc typedef updated alongside the implementation — good hygiene.
  • ✅ Regression tests added for both new alias forms before the PR landed.
  • ✅ Fix is non-breaking: existing callers using target-repo are unaffected.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 1.5M · 186.8 AIC

const config = { targetRepo: "camel-org/camel-repo" };
const result = getDefaultTargetRepo(config);
expect(result).toBe("camel-org/camel-repo");
});
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] No test covers the precedence rule when multiple aliases coexist — e.g. { "target-repo": "a/b", targetRepo: "c/d" }. Without it, the priority order (target-repo > target_repo > targetRepo) lives only in implementation, not in the spec.

💡 Suggested test
it("should prefer target-repo over targetRepo when both are set", async () => {
  const { getDefaultTargetRepo } = await import("./repo_helpers.cjs");
  const config = { "target-repo": "kebab-org/repo", targetRepo: "camel-org/repo" };
  const result = getDefaultTargetRepo(config);
  expect(result).toBe("kebab-org/repo");
});

This makes the tie-breaking rule explicit and guards against accidental reordering.

if (config["target-repo"]) {
return config["target-repo"];
}
if (config.target_repo) {
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] if (config.target_repo) silently skips falsy values — an empty string "" or null would fall through to the next alias or env var without any signal. The original target-repo path had the same gap, but extending the logic is a good moment to clarify the intended behavior with a test.

💡 Suggested test (or inline comment)
// Option A: document that empty string intentionally falls through
it("should fall through to env var when target_repo is empty string", async () => {
  process.env.GH_AW_TARGET_REPO_SLUG = "env-org/env-repo";
  const { getDefaultTargetRepo } = await import("./repo_helpers.cjs");
  const result = getDefaultTargetRepo({ target_repo: "" });
  expect(result).toBe("env-org/env-repo");
});

Alternatively, switch to !== undefined checks if an explicit empty override should be respected. Either approach is fine — the key is making it explicit.

if (config && config["target-repo"]) {
return config["target-repo"];
// First check if there's an explicit target repo in config
if (config) {
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] The root issue is that config keys are not normalized at ingestion — callers can produce target-repo, target_repo, or targetRepo interchangeably. The fix is correct here, but if allowed_repos or future keys develop the same variant shapes, each helper will need the same multi-alias treatment independently.

💡 Follow-up to consider

A single upstream normalizeConfig(config) step — called before any handler receives the config — would canonicalize all keys to one form (e.g. kebab-case) and prevent the pattern from spreading. This is not blocking, but worth tracking as a follow-up.

function normalizeConfig(config) {
  if (!config) return config;
  return {
    ...config,
    "target-repo": config["target-repo"] ?? config.target_repo ?? config.targetRepo,
    // normalize other keys here
  };
}

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 70/100 — Acceptable

Analyzed 2 test(s): 2 design, 0 implementation, 0 guideline violation(s).

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

Test Classification Details

Test File Classification Issues Detected
should support target_repo alias in config actions/setup/js/repo_helpers.test.cjs:89 ✅ Design Happy-path only; no edge cases (empty string, both aliases set)
should support targetRepo alias in config actions/setup/js/repo_helpers.test.cjs:96 ✅ Design Happy-path only; no edge cases (empty string, priority conflict)

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 0 tests
  • 🟨 JavaScript (*.test.cjs, *.test.js): 2 tests (vitest)
⚠️ Flagged Tests — Suggestions (2 item(s))

⚠️ should support target_repo alias in config (actions/setup/js/repo_helpers.test.cjs:89)

Classification: Design test ✅
Issue: Happy-path only. The test confirms the new target_repo alias resolves correctly, but does not cover priority ordering (e.g., what happens when both target-repo and target_repo are set simultaneously) or edge inputs (empty string "").
What design invariant does this test enforce? That getDefaultTargetRepo accepts target_repo (underscore form) as a valid config key and returns its value.
What would break if deleted? A regression where the underscore alias silently stops working would go undetected.
Suggested improvement: Add a row (or separate test) for the case where config = { "target-repo": "a/b", target_repo: "c/d" } to document which alias wins, and a case where target_repo is an empty string.


⚠️ should support targetRepo alias in config (actions/setup/js/repo_helpers.test.cjs:96)

Classification: Design test ✅
Issue: Same happy-path-only concern. Does not test priority (e.g., target_repo vs targetRepo both set) or empty-string guard.
What design invariant does this test enforce? That getDefaultTargetRepo accepts targetRepo (camelCase form) as a valid config key.
What would break if deleted? A regression where camelCase alias lookup breaks would not be caught.
Suggested improvement: Add a conflict-resolution test: config = { target_repo: "a/b", targetRepo: "c/d" } to document the precedence contract.

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). Both tests verify observable return values of getDefaultTargetRepo against the two newly supported config key aliases, directly covering the behavioral contract introduced by this PR.

📖 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.

References: §27010862152

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 2.1M · 269.4 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: 70/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%). Both new tests verify the observable return value of getDefaultTargetRepo for the two newly supported config key aliases (target_repo, targetRepo), directly covering the behavioral contract this PR introduces.

Copilot AI and others added 3 commits June 5, 2026 11:24
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>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 5, 2026

@copilot the fox looks incorrect read the issue:

...

Fixed. close_pull_request now honors target_repo/targetRepo message aliases (with repo as canonical priority), so requests route to the target repository instead of the workflow host repo. Covered by regression tests in close_pull_request.test.cjs and repo_helpers.test.cjs.

Addressed in commits: b9a9c89, bd9d629, 65494cb.

Copilot AI changed the title Fix close_pull_request default target repo resolution for dynamic config aliases Fix close_pull_request target repo resolution for config and message aliases Jun 5, 2026
Copilot AI requested a review from pelikhan June 5, 2026 11:26
@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Jun 5, 2026

@copilot correct issue at ingestion in safe outputs MCP server

Copilot AI and others added 3 commits June 5, 2026 11:49
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>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 5, 2026

@copilot correct issue at ingestion in safe outputs MCP server

Addressed at safe-outputs MCP ingestion.

close_pull_request now accepts repo in the MCP tool schema, and strict-schema sanitization normalizes target-repo/target_repo/targetRepo into canonical repo before writing safe outputs, so target repo routing is preserved during ingestion.

Covered by new regression tests in safe_outputs_tools_loader.test.cjs.

Fix commits: f404b37, 7ef7664, 51759f3.

Copilot AI changed the title Fix close_pull_request target repo resolution for config and message aliases Fix close_pull_request target repo resolution at safe-output ingestion and alias handling Jun 5, 2026
@pelikhan pelikhan closed this Jun 5, 2026
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.

close_pull_request safe output doesn't interpolate dynamic input

3 participants