Skip to content

feat: synchronize native metadata on promotion PRs - #73

Merged
v-Kaefer merged 23 commits into
developfrom
feat/issue-72-promotion-native-metadata
Aug 12, 2026
Merged

feat: synchronize native metadata on promotion PRs#73
v-Kaefer merged 23 commits into
developfrom
feat/issue-72-promotion-native-metadata

Conversation

@v-Kaefer

@v-Kaefer v-Kaefer commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Linked Issue

Milestone

  • Mock Milestone

Related PRs

  • <optional related pull request numbers; promotion PRs are auto-detected when possible>

Summary

  • Add native metadata aggregation and Project v2 membership for promotion PRs.
  • Add the implementation PR itself to Project v2 in addition to the linked task, so the native Projects sidebar can represent review lifecycle.
  • Auto-discover an existing Project v2 by the exact configured project-definition name when no project number is supplied.
  • Add configurable issue Linked Branch creation so PRs targeting non-default branches can receive a native GitHub Development relationship.
  • Extend live Q.A so implementation PR Project membership, promotion PR Project membership, and Linked Branch -> Development are verified on real disposable GitHub objects.

How to test

  • Test type: automated + live sandbox
  • Steps: run Repository quality; promote through Q.A; require implementation Project, Promotion Sync, Development linkage, and cleanup live smokes to pass.

Evidence

  • Unit/contract coverage added for promotion consensus/conflicts and Project membership
  • Linked Branch GraphQL creation/query coverage added
  • Implementation PR Project membership/idempotency coverage added
  • Project number explicit precedence + exact-title auto-discovery coverage added
  • Mermaid architecture and PR Sync docs updated in EN/PT-BR
  • Repository quality passed — 110 tests
  • Implementation PR feat: synchronize native metadata on promotion PRs #73 received native labels, milestone and assignee from issue feat: synchronize native metadata and Project membership for promotion PRs #72
  • Installer distributes promotion_sync.py, linked_branch.py, pr_project_sync.py, and project_lookup.py
  • Live implementation PR Project membership sandbox passed after promotion to Q.A
  • Live Promotion Sync sandbox passed after promotion to Q.A
  • Live Linked Branch -> Development sandbox passed after promotion to Q.A

Known risks

  • Project v2 still requires PROJECT_SETUP_PAT; PROJECT_SETUP_PROJECT_NUMBER is now optional when one exact Project title matches projectDefinitionFile.name.
  • GitHub Linked Branch creation is a pre-PR operation. Existing ordinary branches/PRs cannot be retroactively converted by the public API and must use GitHub's manual Development link UI.
  • Single-valued promotion metadata intentionally clears managed values when constituent PRs do not agree.

DoD checklist

  • Scope implemented as defined
  • Tests added
  • Documentation updated
  • Hosted CI passed
  • Live Q.A evidence attached

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Promotion Sync now handles promotion pull requests separately from implementation pull requests. It aggregates metadata, updates Project v2 state and backlinks, and adds unit and live QA coverage.

Changes

Promotion synchronization

Layer / File(s) Summary
Promotion flow contracts
docs/repo/pr-governance-architecture.md, docs/repo/pr-governance-architecture.pt-BR.md, docs/repo/pr-sync.md, docs/repo/pr-sync.pt-BR.md
The documentation defines promotion routing, live refetching, related-PR discovery, metadata consensus, Project v2 handling, authentication, idempotency, and validation.
Promotion metadata aggregation
project_setup/promotion_sync.py
Promotion Sync aggregates labels, milestones, and assignees. It applies native metadata, preserves unmanaged labels, reports conflicts, and renders synchronization summaries.
Project status and routed execution
project_setup/promotion_sync.py, project_setup/pr_sync_router.py
Promotion PRs are added to Project v2 and assigned status options. The router passes project context to the promotion workflow, which updates backlinks and handles lifecycle synchronization.
Unit and live validation
tests/test_promotion_sync.py, tests/qa/live_promotion_sync.py, .github/workflows/qa-live.yml
Tests cover aggregation, metadata, Project status, routing, and workflow configuration. The live QA job creates disposable resources, validates promotion states, and cleans up resources. The job timeout increases to 20 minutes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely summarizes the main change: native metadata synchronization for promotion pull requests.
Description check ✅ Passed The description covers the required sections, summarizes the changes, documents testing and risks, and clearly marks incomplete live QA evidence.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-72-promotion-native-metadata

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

PR Sync

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@project_setup/promotion_sync.py`:
- Around line 148-158: The sync_promotion_native_metadata assignee logic only
adds missing assignees and never removes stale ones, including when
metadata.assignees is empty. Reconcile the issue’s current assignees to the
aggregate metadata.assignees union by removing assignees absent from the union
and adding missing ones, while preserving dry-run behavior and any established
manual-assignee policy. Update tests/test_promotion_sync.py lines 95-164 to
cover stale-assignee removal and an empty-assignee aggregate.

In `@tests/qa/live_promotion_sync.py`:
- Around line 549-556: Extend the promotion sync validation after the promotion
comment check to iterate through every PR in related_prs, fetch its comments via
client.list_issue_comments, and require a constituent backlink comment whose
stage marker references promotion_number and whose state is merged. Raise a
RuntimeError identifying the related PR when the expected backlink is missing or
incorrect, while preserving the existing promotion-comment assertion.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d599fd06-5bd0-4ab5-b0eb-5c5bb6362b50

📥 Commits

Reviewing files that changed from the base of the PR and between cf25ed6 and 7b6e8b3.

📒 Files selected for processing (9)
  • .github/workflows/qa-live.yml
  • docs/repo/pr-governance-architecture.md
  • docs/repo/pr-governance-architecture.pt-BR.md
  • docs/repo/pr-sync.md
  • docs/repo/pr-sync.pt-BR.md
  • project_setup/pr_sync_router.py
  • project_setup/promotion_sync.py
  • tests/qa/live_promotion_sync.py
  • tests/test_promotion_sync.py

Comment on lines +148 to +158
if config.get("syncAssignees", True):
current_assignees = set(issue_assignee_logins(pr_issue))
missing = [login for login in metadata.assignees if login not in current_assignees]
if missing:
if dry_run:
print(f"[DRY-RUN] Would assign promotion PR #{ctx.number} to {', '.join(missing)}")
else:
add_assignees(client, repo, ctx.number, missing)
notes.append(
"assignees=" + (", ".join(f"`{login}`" for login in metadata.assignees) if metadata.assignees else "none")
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect available assignee mutation helpers before adding reconciliation.
ast-grep outline project_setup/pr_sync.py --items all
rg -n -C 4 'add_assignees|update_issue|/assignees|assignees' project_setup tests/test_promotion_sync.py

Repository: v-Kaefer/Github-Project-Automation

Length of output: 21456


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- promotion_sync.py focused source ---'
cat -n project_setup/promotion_sync.py | sed -n '1,175p'

printf '%s\n' '--- related tests ---'
cat -n tests/test_promotion_sync.py | sed -n '1,190p'

printf '%s\n' '--- assignee configuration/docs ---'
rg -n -C 5 'syncAssignees|assignee|manual.assignee|assignAuthorWhenTaskUnassigned' \
  --glob '!*.pyc' --glob '!node_modules' .

printf '%s\n' '--- issue-assignee API client methods ---'
rg -n -C 5 'def (request_json|update_issue)|issues/.*/assignees|assignees' project_setup

Repository: v-Kaefer/Github-Project-Automation

Length of output: 50392


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
cases = [
    ("stale", {"alice", "stale"}, ["alice"]),
    ("aggregate_empty", {"stale"}, []),
    ("already_converged", {"alice", "bob"}, ["alice", "bob"]),
]
for name, current, desired in cases:
    missing = [login for login in desired if login not in current]
    print(name, {"current": sorted(current), "desired": desired, "missing": missing,
                 "result_after_current_logic": sorted(current | set(missing)),
                 "stale_remaining": sorted(current - set(desired))})
PY

printf '%s\n' '--- documented promotion assignee contract ---'
sed -n '54,92p;199,211p' docs/repo/pr-sync.md

Repository: v-Kaefer/Github-Project-Automation

Length of output: 2710


🌐 Web query:

GitHub REST API update an issue assignees array remove assignee endpoint

💡 Result:

To remove assignees from a GitHub issue, you have two primary options using the GitHub REST API depending on whether you want to remove specific users or clear all assignees entirely. 1. Removing specific assignees: You can remove one or more specific users from an issue by sending a DELETE request to the following endpoint [1][2]: DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees In the request body, provide an array of strings containing the usernames of the assignees you wish to remove [3][2]: { "assignees": ["username1", "username2"] } 2. Updating (replacing or clearing) assignees: You can also manage assignees using the Update an issue endpoint, which uses a PATCH request [4][5]: PATCH /repos/{owner}/{repo}/issues/{issue_number} When using the "assignees" parameter in the body, the provided list replaces the entire set of current assignees [4][6]. To remove all assignees from an issue, send an empty array ([]) [4][7]: { "assignees": [] } Please note that only users with push access to the repository can modify assignees [4][2]. Without appropriate permissions, these changes are typically silently ignored or dropped [1][4].

Citations:


Reconcile promotion assignees to the aggregate union.

sync_promotion_native_metadata leaves stale assignees when a Related PR removes an assignee or when the aggregate union becomes empty.

  • Reconcile assignees with the aggregate union, or define a manual-assignee policy.
  • Add tests for stale and empty-assignee cases.
📍 Affects 2 files
  • project_setup/promotion_sync.py#L148-L158 (this comment)
  • tests/test_promotion_sync.py#L95-L164
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@project_setup/promotion_sync.py` around lines 148 - 158, The
sync_promotion_native_metadata assignee logic only adds missing assignees and
never removes stale ones, including when metadata.assignees is empty. Reconcile
the issue’s current assignees to the aggregate metadata.assignees union by
removing assignees absent from the union and adding missing ones, while
preserving dry-run behavior and any established manual-assignee policy. Update
tests/test_promotion_sync.py lines 95-164 to cover stale-assignee removal and an
empty-assignee aggregate.

Comment on lines +549 to +556
comments = client.list_issue_comments(args.repo, promotion_number)
promotion_comment = next(
(comment for comment in comments if "<!-- project-setup-related-prs -->" in (comment.get("body") or "")),
None,
)
if not promotion_comment or "State: `merged`" not in (promotion_comment.get("body") or ""):
raise RuntimeError("Promotion Sync sticky comment did not converge to merged state")
print("promotion_sync_backlinks=passed")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate backlinks on each constituent PR.

This code reads comments only from the promotion PR. It does not verify the backlinks written to the PRs in related_prs. A broken constituent-backlink loop can pass this test.

Fetch each related PR comment list. Assert that its stage marker links to this promotion and reports the merged state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/qa/live_promotion_sync.py` around lines 549 - 556, Extend the promotion
sync validation after the promotion comment check to iterate through every PR in
related_prs, fetch its comments via client.list_issue_comments, and require a
constituent backlink comment whose stage marker references promotion_number and
whose state is merged. Raise a RuntimeError identifying the related PR when the
expected backlink is missing or incorrect, while preserving the existing
promotion-comment assertion.

@v-Kaefer
v-Kaefer merged commit 5d73602 into develop Aug 12, 2026
10 checks passed
@v-Kaefer
v-Kaefer deleted the feat/issue-72-promotion-native-metadata branch August 12, 2026 15:57
@github-actions github-actions Bot mentioned this pull request Aug 12, 2026
7 tasks
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Promotion linkage

@github-actions

Copy link
Copy Markdown

Promotion linkage

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.

1 participant