Skip to content

feat(update_project): add target_repo for cross-repo project item resolution#21404

Merged
pelikhan merged 8 commits intomainfrom
copilot/add-content-repo-cross-repo-resolution
Mar 17, 2026
Merged

feat(update_project): add target_repo for cross-repo project item resolution#21404
pelikhan merged 8 commits intomainfrom
copilot/add-content-repo-cross-repo-resolution

Conversation

Copy link
Contributor

Copilot AI commented Mar 17, 2026

update_project hardcoded context.repo when resolving content_number to a project item node ID, making it impossible to update fields on items from other repos — a critical gap for org-level projects aggregating issues across multiple repositories.

Changes

JavaScript (update_project.cjs)

  • Import parseRepoSlug, resolveTargetRepoConfig, isRepoAllowed from repo_helpers.cjs
  • Read output.target_repo and use its parsed owner/repo for the GraphQL content resolution query instead of always using context.repo
  • Add targetRepotarget_repo camelCase alias in normalizeUpdateProjectOutput() for consistency with other fields
  • In main(config): extract defaultTargetRepo + allowedRepos via resolveTargetRepoConfig (falls back to current workflow repo when target-repo is not set)
  • In handleUpdateProject(): reject target_repo values not in defaultTargetRepo or allowedRepos before calling updateProject()
  • Updated error message to mention both target-repo and allowed-repos as valid configuration paths

Go (pkg/workflow/update_project.go)

  • Add TargetRepoSlug (target-repo) and AllowedRepos (allowed-repos) to UpdateProjectConfig
  • Parse both fields using existing parseTargetRepoWithValidation / parseAllowedReposFromConfig helpers

Config generation

  • compiler_safe_outputs_config.go: add target-repo and allowed_repos to the update_project handler config builder
  • safe_outputs_config_generation.go: switch from generateMaxConfiggenerateTargetConfigWithRepos

Schema

  • Add optional target_repo (string, ^owner/repo$ pattern) to the update_project MCP tool input schema
  • Add target-repo and allowed-repos to the update-project frontmatter schema, with JSON Schema validation using oneOf to accept both literal values and GitHub Actions expression syntax (${{ ... }}):
    • target-repo: accepts owner/repo format (rejects wildcards) or a ${{ ... }} expression (e.g. ${{ vars.TARGET_REPO }})
    • allowed-repos items: accepts owner/repo, wildcard patterns (org/*, */repo, *), or ${{ ... }} expressions
  • Updated target_repo tool description to accurately reflect that the repo must be permitted by either target-repo or allowed-repos configuration

Tests

  • Added 9 unit tests covering: cross-repo GraphQL resolution, targetRepo camelCase alias, invalid format, fallback to context.repo, allowed-repos rejection, target-repo config match, allowed-repos list match, wildcard pattern, and no target_repo provided
  • Added integration tests (pkg/workflow/update_project_target_repo_integration_test.go) that compile workflows and assert the generated config.json and GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG contain the correct target-repo and allowed_repos values (or correctly omit them when not configured)
  • Added sample agentic workflow (pkg/cli/workflows/test-copilot-update-project-cross-repo.md) demonstrating cross-repo project item updates with target-repo and allowed-repos configured

Usage

safe-outputs:
  update-project:
    github-token: ${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }}
    project: "https://github.com/orgs/myorg/projects/42"
    target-repo: myorg/backend
    allowed-repos: ["myorg/docs", "myorg/frontend"]

GitHub Actions expression syntax is also supported:

safe-outputs:
  update-project:
    github-token: ${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }}
    project: "https://github.com/orgs/myorg/projects/42"
    target-repo: ${{ vars.TARGET_REPO }}
    allowed-repos: ["${{ vars.ALLOWED_REPO }}", "myorg/docs"]

Agent output for an issue in another repo:

{
  "type": "update_project",
  "project": "https://github.com/orgs/myorg/projects/42",
  "content_type": "issue",
  "content_number": 123,
  "target_repo": "myorg/docs",
  "fields": { "Status": "In Progress" }
}

When target_repo is omitted, behaviour is unchanged (resolves against the workflow host repo).


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

…te_project

- Add content_repo field to update_project tool schema (both JSON files)
- Update update_project.cjs to use content_repo for GraphQL content resolution
- Add allowed-repos/target-repo config validation in handleUpdateProject
- Add TargetRepoSlug and AllowedRepos fields to UpdateProjectConfig Go struct
- Update compiler_safe_outputs_config.go to pass target-repo and allowed_repos
- Update safe_outputs_config_generation.go to use generateTargetConfigWithRepos
- Add target-repo and allowed-repos to main_workflow_schema.json
- Update documentation with cross-repo examples and feature tables

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add content_repo for cross-repo project item resolution feat(update_project): add content_repo for cross-repo project item resolution Mar 17, 2026
Copilot AI requested a review from pelikhan March 17, 2026 14:49
@pelikhan
Copy link
Contributor

@copilot rename content_repo to target_repo to avoid confusing. Review all code and add tests. Ensure consistency. Reuse helpers.

- Rename content_repo -> target_repo in update_project.cjs, both schema JSON files,
  main_workflow_schema.json, Go struct comment, and all documentation
- Add camelCase alias targetRepo -> target_repo in normalizeUpdateProjectOutput
- Add 9 tests covering: target_repo content resolution, camelCase alias,
  invalid format, fallback to context.repo, allowed-repos validation,
  target-repo config match, allowed-repos list, wildcard pattern, no target_repo

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

Copilot AI commented Mar 17, 2026

@copilot rename content_repo to target_repo to avoid confusing. Review all code and add tests. Ensure consistency. Reuse helpers.

Done in a3d3735. Renamed content_repotarget_repo across all files (JS, schemas, Go struct comment, docs). Also added:

  • targetRepo camelCase alias in normalizeUpdateProjectOutput (consistent with other fields)
  • 9 new tests covering: cross-repo GraphQL resolution, camelCase alias, invalid format, fallback to context.repo, and allowed-repos validation (reject, match target-repo, match list, wildcard, no field)

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:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha --write ../../../**/*.json /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet --ignore-path ../../../.prettirev-parse n-dir/git /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -ato�� -bool -buildtags 675375/b312/vet.cfg -errorsas -ifaceassert -nilfunc /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha te &#39;scripts/**/*.js&#39; --ignore-path .prettierignore --log-level=e!../../../pkg/workflow/js/**/*.jgit cfg 64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha -bool -buildtags /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert -nilfunc /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -uns�� -unreachable=false /tmp/go-build882675375/b303/vet.cfg /usr/bin/git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha &#34;prettier&#34; --write &#39;scripts/**/*.js&#39; --ignore-path .prettierigno-errorsas (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha --write scripts/**/*.js ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet .prettierignore --log-level=errorev-parse (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha se 675375/b119/vet.cfg ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet tierignore (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha &#34;prettier&#34; --write &#39;scripts/**/*.js&#39; --ignore-path .prettierigno-errorsas (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha se 675375/b164/vet.cfg ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet e\|orgProjectV2Rgit (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha &#34;prettier&#34; --write &#39;scripts/**/*.js&#39; --ignore-path .prettierignore --log-level=e!../../../pkg/wogit (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha from .github/aw to pkg/workflow/data/action_pins.json...&#34; (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha --noprofile 1539a8ca:go.mod 64/pkg/tool/linux_amd64/vet nore (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha --noprofile est.cjs 64/pkg/tool/linux_amd64/vet nore 1539a8ca (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title feat(update_project): add content_repo for cross-repo project item resolution feat(update_project): add target_repo for cross-repo project item resolution Mar 17, 2026
@pelikhan
Copy link
Contributor

@copilot review changes, added tests

@pelikhan pelikhan marked this pull request as ready for review March 17, 2026 15:27
Copilot AI review requested due to automatic review settings March 17, 2026 15:27
@pelikhan
Copy link
Contributor

@copilot create integration tests with agentic workflows that use the feature and make sure the compiled code config.json contains the target-repo configured

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 cross-repository support to the update_project safe-output by introducing a target_repo field (agent output) and corresponding allowlisting configuration, enabling org-level Projects to resolve issues/PRs from repos other than the workflow host repo.

Changes:

  • Add target_repo support to update_project (GraphQL content resolution uses the specified repo instead of always context.repo) plus camelCase alias normalization.
  • Introduce target-repo / allowed-repos config parsing and propagation (Go compiler + generated runtime config) and enforce repo allowlisting in the JS handler.
  • Update schemas/docs and add tests covering cross-repo resolution, normalization, validation, and allowlist behavior.

Reviewed changes

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

Show a summary per file
File Description
actions/setup/js/update_project.cjs Uses target_repo for issue/PR node resolution and validates it against configured allowlist before executing.
actions/setup/js/update_project.test.cjs Adds test coverage for cross-repo resolution, aliasing, and allowlist enforcement.
actions/setup/js/safe_outputs_tools.json Extends tool input schema with optional target_repo.
pkg/workflow/update_project.go Adds target-repo + allowed-repos to update-project frontmatter config parsing.
pkg/workflow/safe_outputs_config_generation.go Emits runtime config including target repo + allowed repos for the update_project handler.
pkg/workflow/compiler_safe_outputs_config.go Includes target-repo and allowed_repos in handler-manager config export.
pkg/workflow/js/safe_outputs_tools.json Extends tool input schema with optional target_repo.
pkg/parser/schemas/main_workflow_schema.json Adds target-repo + allowed-repos to update-project frontmatter schema.
docs/.../safe-outputs.md Documents cross-repo usage and configuration.
docs/.../safe-outputs-specification.md Updates capability metadata to reflect cross-repo support.
docs/.../multi-repo.md Marks update-project as cross-repo capable.
.changeset/patch-update-project-content-repo.md Records a patch-level release note for the new behavior.

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

Comment on lines +4634 to +4639
"description": "Default repository in format 'owner/repo' for cross-repository content resolution. When specified, the agent can use 'target_repo' in agent output to resolve issues or PRs from this repository. Wildcards ('*') are not allowed."
},
"allowed-repos": {
"type": "array",
"items": {
"type": "string"
"target_repo": {
"type": "string",
"pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$",
"description": "Repository containing the issue or pull request, in \"owner/repo\" format (e.g., \"github/docs\"). Use this when the issue or PR belongs to a different repository than the one running the workflow. Requires safe-outputs.update-project.allowed-repos to include this repository."
// additional repos must be listed in allowed-repos.
const isDefaultRepo = targetRepoSlug === defaultTargetRepo;
if (!isDefaultRepo && !isRepoAllowed(targetRepoSlug, allowedRepos)) {
const errorMsg = `Repository "${targetRepoSlug}" is not allowed for cross-repo content resolution. Configure safe-outputs.update-project.allowed-repos in the workflow frontmatter to permit this repository.`;
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@pelikhan
Copy link
Contributor

@copilot review comments

…arget_repo cross-repo config

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

Copilot AI commented Mar 17, 2026

@copilot create integration tests with agentic workflows that use the feature and make sure the compiled code config.json contains the target-repo configured

Done in b5f5ee3. Added:

  • pkg/cli/workflows/test-copilot-update-project-cross-repo.md — sample agentic workflow using target-repo + allowed-repos for cross-repo project item updates
  • pkg/workflow/update_project_target_repo_integration_test.go — 4 integration tests that compile workflows and assert the compiled config.json and GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG contain (or correctly omit) target-repo and allowed_repos:
    • target-repo + allowed-repos both present in compiled config
    • Without either field, neither appears in compiled output
    • target-repo only (no allowed-repos)
    • Sample workflow file compiles successfully and produces correct config values

…error message, tool description

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

Copilot AI commented Mar 17, 2026

@copilot review comments

Done in 46513fb. Addressed all three review comments:

  1. Schema patterns (main_workflow_schema.json): Added pattern: ^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$ to target-repo (rejecting wildcards like *) and an item-level pattern ^(\\*|([a-zA-Z0-9_.-]+|\\*)/([a-zA-Z0-9_.-]+|\\*))$ to allowed-repos items (accepting owner/repo, org/*, */repo, and *).

  2. Tool description (safe_outputs_tools.json): Updated target_repo description to say it requires safe-outputs.update-project.target-repo to match or safe-outputs.update-project.allowed-repos to include the repository.

  3. Error message (update_project.cjs): Updated to mention both target-repo (for single-repo config) and allowed-repos (for multi-repo list) as configuration options.

@pelikhan
Copy link
Contributor

@copilpt make sure patterns support github macron expression syntax ${{...

@pelikhan
Copy link
Contributor

@copilot make sure patterns support github action macro expression syntax ${{...

…arget-repo and allowed-repos schema patterns

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

Copilot AI commented Mar 17, 2026

@copilot make sure patterns support github action macro expression syntax ${{...

Done in 293a659. Updated the target-repo and allowed-repos schema fields in update-project to use oneOf with both the literal pattern and a ^\\$\\{\\{.*\\}\\}$ alternative, so values like ${{ vars.TARGET_REPO }} are now accepted alongside the owner/repo / wildcard formats.

@pelikhan pelikhan merged commit c717c8b into main Mar 17, 2026
82 checks passed
@pelikhan pelikhan deleted the copilot/add-content-repo-cross-repo-resolution branch March 17, 2026 19:56
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.

update_project safe output: add content_repo for cross-repo project item resolution

3 participants