From d51c84b7a342dc5b2fa353875e48ced15fa33aa3 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Tue, 4 Aug 2026 10:40:43 -0700 Subject: [PATCH 1/6] feat(agents): add pull request creation skill Signed-off-by: Miyoung Choi --- .../SKILL.md | 310 ++++++++++++++++++ .gitignore | 5 +- 2 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 .agents/skills/nemo-platform-contributor-create-pr/SKILL.md diff --git a/.agents/skills/nemo-platform-contributor-create-pr/SKILL.md b/.agents/skills/nemo-platform-contributor-create-pr/SKILL.md new file mode 100644 index 0000000000..9d55feccee --- /dev/null +++ b/.agents/skills/nemo-platform-contributor-create-pr/SKILL.md @@ -0,0 +1,310 @@ +--- +name: nemo-platform-contributor-create-pr +description: Create, publish, or advance a GitHub pull request for NVIDIA-NeMo/nemo-platform with repository-aware branch checks, targeted validation, DCO sign-off enforcement, trusted PR-template completion, conventional titles, safe pushes, draft or ready-for-review creation, CI and CodeRabbit follow-up, and merge-conflict handling. Use when the user asks to commit and push work, create or open a PR, submit changes for review, mark a draft ready, monitor a PR, address automated review or CI feedback, or update a conflicted PR in nemo-platform. +--- + + + + +# Create a NeMo Platform pull request + +Create and follow up on pull requests for `NVIDIA-NeMo/nemo-platform`. Treat the refreshed default branch as the source of repository policy and use the host-authenticated `gh` session for GitHub operations. + +## Non-negotiable rules + +- Read the root `AGENTS.md`, any `AGENTS.local.md`, and every nested `AGENTS.md` that governs a changed file before preparing the PR. +- Never create a PR from the default branch. +- Never commit or amend without `-s`. This includes normal commits, `--amend`, and `--fixup` commits. +- Preserve sign-offs during history updates with `git rebase --signoff` or `git merge --signoff`. +- Audit every commit in the PR range for an appropriate `Signed-off-by:` trailer before every push and immediately before PR creation. +- Do not forge another contributor's sign-off. Stop when a commit author must provide or correct their own DCO declaration. +- Never use `git reset --soft origin/main` or reset to any commit that is not an ancestor of `HEAD`. +- Never use plain `--force`. Use an exact `--force-with-lease` only after the user approves rewriting a published branch. +- Stop on GitHub authentication, authorization, SSO, remote-access, or push-permission failures. Do not search for tokens, change credentials, switch remote protocols, or try another identity. +- Do not alter an unrelated branch or PR. Do not create a duplicate PR for a branch that already has one. + +## 1. Authenticate and establish the trusted base + +Run these checks before a GitHub read or write: + +```bash +NMP_REPO=NVIDIA-NeMo/nemo-platform +gh auth status +gh repo view "$NMP_REPO" --json nameWithOwner,defaultBranchRef,url +git remote get-url origin +``` + +Require an active host-authenticated account and `nameWithOwner` equal to `NVIDIA-NeMo/nemo-platform`. If `gh` fails only because a sandbox blocks host or network access, retry the same command with narrowly scoped host/network approval. If authentication or authorization still fails outside the sandbox, report the command and error and stop. + +Resolve and refresh the default branch instead of assuming a stale local `main`: + +```bash +NMP_BASE_BRANCH="$(gh repo view "$NMP_REPO" --json defaultBranchRef --jq '.defaultBranchRef.name')" +git fetch --prune origin "$NMP_BASE_BRANCH" +NMP_BASE_REF="origin/$NMP_BASE_BRANCH" +git show -s --format='%H %cs %s' "$NMP_BASE_REF" +``` + +Treat authentication or access errors from `git fetch` as hard stops. + +## 2. Verify the branch and worktree + +Inspect the exact worktree before modifying Git state: + +```bash +git rev-parse --show-toplevel +git worktree list --porcelain +git status --short --branch +git branch --show-current +git log --oneline "$NMP_BASE_REF..HEAD" +git diff --stat "$NMP_BASE_REF...HEAD" +``` + +Require all of the following: + +- Use a named feature branch, not detached `HEAD` and not the default branch. +- Follow `[git-issue-number]-/` from `AGENTS.md`. Omit the issue prefix only when no issue is known; keep kebab case and the username suffix. +- Keep only the intended PR changes in the worktree. Do not stash, discard, or absorb unrelated changes without the user's direction. +- Confirm that the branch has commits ahead of the trusted base or intended uncommitted changes that the user asked to include. +- Confirm that another worktree or active PR branch is not being repurposed. + +If the current worktree is on the default branch, an unrelated PR branch, or a protected worktree, leave it untouched and create an isolated worktree from the refreshed base: + +```bash +git worktree add -b "/" "" "$NMP_BASE_REF" +``` + +Do not carry changes across worktrees implicitly. Ask before moving uncommitted work. + +## 3. Load repository-owned policy + +Read policy from the trusted base and compare it with the working tree when the PR changes policy itself: + +```bash +git show "$NMP_BASE_REF:AGENTS.md" +git show "$NMP_BASE_REF:CONTRIBUTING.md" +git show "$NMP_BASE_REF:.github/CI_README.md" +git show "$NMP_BASE_REF:.github/workflows/ci.yaml" +git show "$NMP_BASE_REF:.github/workflows/security.yaml" +git show "$NMP_BASE_REF:.github/workflows/semantic-pull-requests.yaml" +git show "$NMP_BASE_REF:.github/workflows/dco-war.yaml" +``` + +Also read changed-area workflows, build files, package scripts, existing repo-local skills, and nested instructions. Do not copy commands from another repository when nemo-platform provides its own command. + +## 4. Inspect the change and select validation + +Review both committed and uncommitted changes: + +```bash +git diff --name-status "$NMP_BASE_REF...HEAD" +git diff "$NMP_BASE_REF...HEAD" +git diff --check "$NMP_BASE_REF...HEAD" +git diff --check +git diff --cached --check +``` + +Run the smallest checks that directly verify each changed behavior, plus the repository-mandated pre-commit gate. Record every command and result for the PR body. + +- **All changes before commit:** Run `uv run pre-commit run -a` as required by `AGENTS.md`. Rerun it after a hook modifies files. +- **Python:** Run the directly affected `uv run --frozen pytest -v`; use `make test-package PACKAGE=` or `make test-service SERVICE=` when a package or service boundary is the right scope. Run targeted `uv run ruff check ` and `uv run ruff format --check `. Run `uv run --frozen ty check` when type behavior changes. +- **Studio or other `web/` changes:** From `web/`, run the affected package test. Also use the PR-scoped commands that mirror CI when applicable: `pnpm --filter="...[$NMP_BASE_REF]" run --parallel --if-present typecheck` and `pnpm --filter="...[$NMP_BASE_REF]" run --parallel --if-present test:ci`. Run `pnpm lint` and `pnpm format` for broad web changes. +- **Fern docs:** Follow `docs/AGENTS.md`. Run `make docs-check` and `make docs-broken-links`; run `make docs-check-python-snippets DOCS_PATH=` or `make docs-run-notebook DOCS_PATH=` when those surfaces change. +- **API, schema, generated SDK, CLI, or config-reference changes:** Follow the regeneration rules in `AGENTS.md`, the applicable nested instructions, and the exact Make targets. Verify generated output is committed and stable after rerunning the generator. +- **OPA, Helm, Docker, or workflow changes:** Use the relevant checks such as `make check-policy`, `tools/lint/lint-helm.sh`, Docker bake graph commands, or `actionlint`. Read the workflow before choosing a command. +- **Broad validation infrastructure changes:** Run `make lint` and the applicable broad test target, such as `make test-unit`, when targeted evidence is insufficient. + +Do not claim a skipped check passed. For a draft, state missing or blocked validation. For a ready PR, fix required failures first unless the user explicitly accepts a documented, non-required limitation. + +## 5. Stage and commit with mandatory sign-off + +Inspect the staged patch before every commit: + +```bash +git add -- +git diff --cached --stat +git diff --cached +git diff --cached --check +git commit -s -m "" +``` + +Local skill installs may also appear under `.agents/skills/nemo-*/`. Stage only explicitly intended repo-owned files by exact path; never use a broad add that can capture unrelated installed skills. + +Use only sign-off-preserving forms: + +```bash +git commit --amend -s --no-edit +git commit -s --fixup= +git rebase --signoff +git merge --signoff +``` + +Never run `git commit`, `git commit --amend`, or a fixup commit without `-s`. If hooks change the patch, restage the intended files, rerun affected validation, and commit with `-s` again. + +## 6. Audit every PR commit for DCO + +Run this gate after any commit, amend, rebase, cherry-pick, conflict resolution, or automated fix, and before every push or `gh pr create`. It requires a syntactically valid sign-off whose email matches the commit author's email, case-insensitively: + +```bash +bash -euo pipefail <<'BASH' +: "${NMP_BASE_REF:?set NMP_BASE_REF to the refreshed origin default branch}" +NMP_DCO_FAILED=0 +NMP_COMMIT_COUNT=0 + +while IFS= read -r NMP_SHA; do + NMP_COMMIT_COUNT=$((NMP_COMMIT_COUNT + 1)) + NMP_AUTHOR_EMAIL="$(git show -s --format=%ae "$NMP_SHA")" + NMP_SIGNOFF_EMAILS="$( + git show -s --format=%B "$NMP_SHA" \ + | git interpret-trailers --parse \ + | awk -F '[<>]' 'tolower($1) ~ /^signed-off-by:[[:space:]]/ && NF >= 3 { print $2 }' + )" + + if ! printf '%s\n' "$NMP_SIGNOFF_EMAILS" \ + | awk -v want="$NMP_AUTHOR_EMAIL" 'tolower($0) == tolower(want) { found=1 } END { exit(found ? 0 : 1) }'; then + printf 'DCO FAIL %s: no Signed-off-by trailer matches author email %s\n' "$NMP_SHA" "$NMP_AUTHOR_EMAIL" >&2 + NMP_DCO_FAILED=1 + else + printf 'DCO OK %s\n' "$NMP_SHA" + fi +done < <(git rev-list --reverse "$NMP_BASE_REF..HEAD") + +test "$NMP_COMMIT_COUNT" -gt 0 +test "$NMP_DCO_FAILED" -eq 0 +BASH +``` + +Do not treat a PR-body declaration or GitHub's cryptographic `Verified` badge as a substitute for commit trailers. Nemo-platform's required gate is DCO sign-off. + +If the audit fails: + +- Amend the last unpushed, self-authored commit with `git commit --amend -s --no-edit`. +- Repair multiple unpushed, self-authored commits with `git rebase --signoff --force-rebase "$NMP_BASE_REF"`; `--force-rebase` is required when Git would otherwise report an already-current branch and skip replaying the unsigned commits. +- Stop before rewriting a published branch and request explicit approval. +- Stop rather than adding a sign-off for a commit authored by someone else. +- Rerun the entire audit after repair. + +## 7. Validate the title and complete the trusted template + +Use a title no longer than 100 characters in this form: + +```text +[optional scope][!]: +``` + +Use one of the conventional types accepted by the pinned title action: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, or `revert`. Use an accurate optional scope and `!` only for a breaking change. + +Locate the PR template in the trusted base, not in the feature branch and not in a recent PR body: + +```bash +git ls-tree -r --name-only "$NMP_BASE_REF" \ + | rg -i '(^|/)(pull_request_template)(\.md|/.*\.md)$' +``` + +- If exactly one template applies, copy it with `git show "$NMP_BASE_REF:"` into a file created by `mktemp /tmp/nemo-platform-pr-body.XXXXXX`. +- If multiple templates exist, select the repository-defined template that matches the change. Ask when selection is ambiguous. +- If no template exists on the trusted base, report the missing repository dependency and stop before `gh pr create`. Do not invent a template or silently reuse a branch-modified or historical body. +- If the PR changes the template, still populate the trusted base version and explain the template change in that body. + +Preserve the template's section order, comments, and checkbox semantics. Complete every applicable section from `git diff "$NMP_BASE_REF...HEAD"`. Check only items backed by command, hook, or CI evidence. Use `Fixes #NNN` or `Closes #NNN` only for a real related issue. Do not add a PR-body sign-off unless the trusted template requests it; the commit-level DCO audit remains mandatory. + +## 8. Push safely + +Immediately before pushing, rerun the DCO audit, confirm the branch and remote, and inspect the outgoing commits: + +```bash +NMP_BRANCH="$(git branch --show-current)" +git remote get-url origin +git log --oneline "$NMP_BASE_REF..HEAD" +git push --set-upstream origin "HEAD:refs/heads/$NMP_BRANCH" +``` + +Use a normal push first. On a non-fast-forward rejection, fetch the exact remote branch and inspect both histories. Stop if the remote contains unexpected work. + +Only after the user approves rewriting a published branch, pin the lease to the fetched remote SHA: + +```bash +git fetch origin "$NMP_BRANCH" +NMP_EXPECTED_REMOTE_SHA="$(git rev-parse "origin/$NMP_BRANCH")" +git push \ + --force-with-lease="refs/heads/$NMP_BRANCH:$NMP_EXPECTED_REMOTE_SHA" \ + origin "HEAD:refs/heads/$NMP_BRANCH" +``` + +Never bypass branch protection or required checks. Stop on any access error. + +## 9. Create a draft or ready PR + +Check for an existing open PR first: + +```bash +gh pr list --repo "$NMP_REPO" --head "$NMP_BRANCH" --state open --json number,title,url,isDraft +``` + +Rerun the DCO audit immediately before creation. Use explicit metadata; do not use `--fill` because it bypasses trusted-template completion. + +Create a draft when work, validation, or a required decision remains: + +```bash +gh pr create --repo "$NMP_REPO" \ + --base "$NMP_BASE_BRANCH" \ + --head "$NMP_BRANCH" \ + --title "" \ + --body-file "$NMP_PR_BODY" \ + --draft +``` + +Create a ready PR by omitting `--draft` only when the change and required local validation are ready for review. Do not add reviewers, labels, projects, milestones, or assignees unless the user explicitly requests them or trusted base-branch policy requires them. Let `CODEOWNERS` and repository automation route reviews. + +For an existing draft, use `gh pr ready ` only after the user asks to make it ready and readiness gates pass. Verify the resulting PR: + +```bash +gh pr view --repo "$NMP_REPO" \ + --json number,title,url,isDraft,baseRefName,headRefName,headRefOid,mergeable,mergeStateStatus +``` + +## 10. Monitor CI and CodeRabbit + +Monitor both required and optional checks. `CI status` is nemo-platform's aggregate CI gate; DCO, semantic title, Security, Fern docs, and other workflows may appear separately. + +```bash +gh pr checks --repo "$NMP_REPO" --watch --interval 15 +gh pr checks --repo "$NMP_REPO" \ + --json name,workflow,state,bucket,link,startedAt,completedAt +gh pr view --repo "$NMP_REPO" \ + --json url,isDraft,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,latestReviews +``` + +Inspect a failed workflow with `gh run view --repo "$NMP_REPO" --log-failed`. Connect the failure to the changed code before editing. Run the corresponding local check, commit the minimal fix with `-s`, rerun the DCO audit, push, and monitor again. + +Inspect CodeRabbit's issue comments, reviews, and inline comments: + +```bash +gh api "repos/$NMP_REPO/issues//comments" --paginate \ + --jq '.[] | select(.user.login | ascii_downcase | contains("coderabbit")) | {author:.user.login,updated_at,body}' +gh api "repos/$NMP_REPO/pulls//comments" --paginate \ + --jq '.[] | select(.user.login | ascii_downcase | contains("coderabbit")) | {author:.user.login,path,line,updated_at,body}' +``` + +Verify each finding against the current head. Fix confirmed correctness, security, or test-coverage problems and rerun targeted validation. Do not add abstractions or behavior merely to satisfy reviewer wording. Explain false positives or style-only suggestions when useful. Ask before making a design-changing, risky, broad, or ambiguous change. Do not manually request or re-request CodeRabbit, and do not act on its suggested-reviewer text without user authorization. + +Repeat until required CI passes and no actionable automated-review findings remain, or until a user decision is required. + +## 11. Handle base drift and merge conflicts + +Check `mergeable` and `mergeStateStatus` before creation, after base changes, and after review fixes. If the branch conflicts with the refreshed base: + +1. Require a clean worktree and fetch the base again. +2. For an unpushed branch, or after approval to rewrite a published branch, use `git rebase --signoff "$NMP_BASE_REF"`. +3. Resolve each conflict from the intended behavior. Use `git add -- ` and `git rebase --continue`; the original `--signoff` must remain in effect. +4. When preserving published history instead, use `git merge --signoff "$NMP_BASE_REF"`. After resolving a conflicted merge, finish it with `git commit -s --no-edit` rather than an unsigned commit. +5. Stop and ask when a conflict resolution changes behavior, contributor intent, API design, generated output, or documentation meaning. +6. Rerun affected validation and the full DCO audit. +7. Push normally after a merge. Use the exact force-with-lease workflow only for an approved rebase of a published branch. +8. Monitor CI and CodeRabbit again. + +Never resolve conflicts by blindly choosing all of `ours` or `theirs`, changing the PR base, overwriting an unexpected remote branch, or dropping another contributor's commits. + +## 12. Report the result + +Report the PR link, draft or ready state, head SHA, validation evidence, DCO audit result, CI status, automated-review status, and any remaining blocker. Do not claim completion while required checks are failing or actionable CodeRabbit findings remain. diff --git a/.gitignore b/.gitignore index 0c3bd69c06..a952e17002 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,7 @@ AGENTS.local.md tmp .idea .cursor/rules/personal/ -# Local NeMo skill installs created by `nemo setup` / `nemo skills install`. -# Existing tracked repo-owned skills remain tracked; use `git add -f` for new -# repo-owned NeMo skills under these paths. -.agents/skills/nemo-*/ +# Local NeMo Cursor rules created by `nemo setup` / `nemo skills install`. .cursor/rules/nemo-*/ CLAUDE.local.md **/worktrees/ From d953c3e4bbd359ed361f4aa7f26f5eb46363fd39 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Tue, 4 Aug 2026 10:48:47 -0700 Subject: [PATCH 2/6] fix(agents): rename pull request skill Signed-off-by: Miyoung Choi --- .../SKILL.md | 2 +- .gitignore | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) rename .agents/skills/{nemo-platform-contributor-create-pr => contributor-create-pr}/SKILL.md (99%) diff --git a/.agents/skills/nemo-platform-contributor-create-pr/SKILL.md b/.agents/skills/contributor-create-pr/SKILL.md similarity index 99% rename from .agents/skills/nemo-platform-contributor-create-pr/SKILL.md rename to .agents/skills/contributor-create-pr/SKILL.md index 9d55feccee..6747359f20 100644 --- a/.agents/skills/nemo-platform-contributor-create-pr/SKILL.md +++ b/.agents/skills/contributor-create-pr/SKILL.md @@ -1,5 +1,5 @@ --- -name: nemo-platform-contributor-create-pr +name: contributor-create-pr description: Create, publish, or advance a GitHub pull request for NVIDIA-NeMo/nemo-platform with repository-aware branch checks, targeted validation, DCO sign-off enforcement, trusted PR-template completion, conventional titles, safe pushes, draft or ready-for-review creation, CI and CodeRabbit follow-up, and merge-conflict handling. Use when the user asks to commit and push work, create or open a PR, submit changes for review, mark a draft ready, monitor a PR, address automated review or CI feedback, or update a conflicted PR in nemo-platform. --- diff --git a/.gitignore b/.gitignore index a952e17002..0c3bd69c06 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,10 @@ AGENTS.local.md tmp .idea .cursor/rules/personal/ -# Local NeMo Cursor rules created by `nemo setup` / `nemo skills install`. +# Local NeMo skill installs created by `nemo setup` / `nemo skills install`. +# Existing tracked repo-owned skills remain tracked; use `git add -f` for new +# repo-owned NeMo skills under these paths. +.agents/skills/nemo-*/ .cursor/rules/nemo-*/ CLAUDE.local.md **/worktrees/ From 6e37684a8602afc51d86427b5f6120bf81590469 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Tue, 4 Aug 2026 11:08:31 -0700 Subject: [PATCH 3/6] feat(agents): add pull request template Signed-off-by: Miyoung Choi --- .github/PULL_REQUEST_TEMPLATE.md | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..f0a5b133f2 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,39 @@ + +## Summary + + +## Related Issue + + +## Changes + + +## Type of Change + +- [ ] Code change (feature, bug fix, or refactor) +- [ ] Code change with documentation updates +- [ ] Documentation only +- [ ] Contributor tooling or automation +- [ ] CI, build, or test infrastructure + +## Quality Gates + + +- [ ] Tests added or updated for changed behavior +- [ ] Existing tests cover changed behavior — justification: +- [ ] Tests not applicable — justification: +- [ ] Documentation updated for user-visible behavior +- [ ] Documentation not applicable — justification: + +## Verification + + +- [ ] Pull request title follows the repository's Conventional Commit format +- [ ] Every commit includes an appropriate `Signed-off-by:` trailer +- [ ] `uv run pre-commit run -a` passes, or any blocked checks are identified below +- [ ] Targeted tests pass, or tests are marked not applicable above +- [ ] No secrets, API keys, or credentials are included + +Targeted validation: + + From 555096e0cbbfed8303edc5b7183a2591921fd5b6 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Wed, 5 Aug 2026 13:45:15 -0700 Subject: [PATCH 4/6] fix(agents): support fork-based pull requests Signed-off-by: Miyoung Choi --- .agents/skills/contributor-create-pr/SKILL.md | 106 +++++++++++++++--- 1 file changed, 91 insertions(+), 15 deletions(-) diff --git a/.agents/skills/contributor-create-pr/SKILL.md b/.agents/skills/contributor-create-pr/SKILL.md index 6747359f20..d4c385a588 100644 --- a/.agents/skills/contributor-create-pr/SKILL.md +++ b/.agents/skills/contributor-create-pr/SKILL.md @@ -18,7 +18,7 @@ Create and follow up on pull requests for `NVIDIA-NeMo/nemo-platform`. Treat the - Preserve sign-offs during history updates with `git rebase --signoff` or `git merge --signoff`. - Audit every commit in the PR range for an appropriate `Signed-off-by:` trailer before every push and immediately before PR creation. - Do not forge another contributor's sign-off. Stop when a commit author must provide or correct their own DCO declaration. -- Never use `git reset --soft origin/main` or reset to any commit that is not an ancestor of `HEAD`. +- Never use `git reset --soft "$NMP_BASE_REF"` or reset to any commit that is not an ancestor of `HEAD`. - Never use plain `--force`. Use an exact `--force-with-lease` only after the user approves rewriting a published branch. - Stop on GitHub authentication, authorization, SSO, remote-access, or push-permission failures. Do not search for tokens, change credentials, switch remote protocols, or try another identity. - Do not alter an unrelated branch or PR. Do not create a duplicate PR for a branch that already has one. @@ -31,21 +31,63 @@ Run these checks before a GitHub read or write: NMP_REPO=NVIDIA-NeMo/nemo-platform gh auth status gh repo view "$NMP_REPO" --json nameWithOwner,defaultBranchRef,url -git remote get-url origin + +nmp_repo_for_url() { + case "$1" in + https://github.com/*|git@github.com:*|ssh://git@github.com/*) ;; + *) printf 'Unsupported or unencrypted Git remote URL: %s\n' "$1" >&2; return 1 ;; + esac + gh repo view "$1" --json nameWithOwner --jq '.nameWithOwner' +} + +NMP_UPSTREAM_CANDIDATES="" +for NMP_REMOTE in $(git remote); do + NMP_REMOTE_IS_UPSTREAM=true + NMP_REMOTE_HAS_FETCH_URL=false + while IFS= read -r NMP_URL; do + NMP_REMOTE_HAS_FETCH_URL=true + NMP_URL_REPO="$(nmp_repo_for_url "$NMP_URL")" || { + NMP_REMOTE_IS_UPSTREAM=false + break + } + if [ "$NMP_URL_REPO" != "$NMP_REPO" ]; then + NMP_REMOTE_IS_UPSTREAM=false + break + fi + done < <(git remote get-url --all "$NMP_REMOTE") + + if "$NMP_REMOTE_HAS_FETCH_URL" && "$NMP_REMOTE_IS_UPSTREAM"; then + NMP_UPSTREAM_CANDIDATES="${NMP_UPSTREAM_CANDIDATES}${NMP_UPSTREAM_CANDIDATES:+$'\n'}${NMP_REMOTE}" + fi +done + +NMP_UPSTREAM_COUNT="$(printf '%s\n' "$NMP_UPSTREAM_CANDIDATES" | sed '/^$/d' | wc -l | tr -d ' ')" +if printf '%s\n' "$NMP_UPSTREAM_CANDIDATES" | grep -qx upstream; then + NMP_UPSTREAM_REMOTE=upstream +elif printf '%s\n' "$NMP_UPSTREAM_CANDIDATES" | grep -qx origin; then + NMP_UPSTREAM_REMOTE=origin +elif [ "$NMP_UPSTREAM_COUNT" -eq 1 ]; then + NMP_UPSTREAM_REMOTE="$NMP_UPSTREAM_CANDIDATES" +else + printf 'Could not select one encrypted remote for %s; found %s candidates\n' "$NMP_REPO" "$NMP_UPSTREAM_COUNT" >&2 + exit 1 +fi ``` -Require an active host-authenticated account and `nameWithOwner` equal to `NVIDIA-NeMo/nemo-platform`. If `gh` fails only because a sandbox blocks host or network access, retry the same command with narrowly scoped host/network approval. If authentication or authorization still fails outside the sandbox, report the command and error and stop. +Require an active host-authenticated account, `nameWithOwner` equal to `NVIDIA-NeMo/nemo-platform`, and a configured remote whose fetch URLs all resolve to that repository over encrypted HTTPS or SSH. Prefer a matching `upstream`, then a matching `origin`, then a sole matching remote. A contributor fork may remain `origin`; use a separate canonical remote such as `upstream` for the trusted base. If no canonical remote exists or unnamed candidates remain ambiguous, stop and ask the user to configure or select one rather than adding or rewriting remotes implicitly. Keep `NMP_REPO`, `NMP_UPSTREAM_REMOTE`, and `nmp_repo_for_url` available for later sections. + +If `gh` fails only because a sandbox blocks host or network access, retry the same command with narrowly scoped host/network approval. If authentication or authorization still fails outside the sandbox, report the command and error and stop. Resolve and refresh the default branch instead of assuming a stale local `main`: ```bash NMP_BASE_BRANCH="$(gh repo view "$NMP_REPO" --json defaultBranchRef --jq '.defaultBranchRef.name')" -git fetch --prune origin "$NMP_BASE_BRANCH" -NMP_BASE_REF="origin/$NMP_BASE_BRANCH" +git fetch --prune "$NMP_UPSTREAM_REMOTE" "$NMP_BASE_BRANCH" +NMP_BASE_REF="$NMP_UPSTREAM_REMOTE/$NMP_BASE_BRANCH" git show -s --format='%H %cs %s' "$NMP_BASE_REF" ``` -Treat authentication or access errors from `git fetch` as hard stops. +Never derive `NMP_BASE_REF` from a fork merely because that fork is named `origin`. Treat authentication or access errors from `git fetch` as hard stops. ## 2. Verify the branch and worktree @@ -147,7 +189,7 @@ Run this gate after any commit, amend, rebase, cherry-pick, conflict resolution, ```bash bash -euo pipefail <<'BASH' -: "${NMP_BASE_REF:?set NMP_BASE_REF to the refreshed origin default branch}" +: "${NMP_BASE_REF:?set NMP_BASE_REF to the refreshed canonical upstream default branch}" NMP_DCO_FAILED=0 NMP_COMMIT_COUNT=0 @@ -214,21 +256,55 @@ Immediately before pushing, rerun the DCO audit, confirm the branch and remote, ```bash NMP_BRANCH="$(git branch --show-current)" -git remote get-url origin +NMP_PUSH_REMOTE="$(git config --get "branch.$NMP_BRANCH.pushRemote" || true)" +if [ -z "$NMP_PUSH_REMOTE" ]; then + NMP_PUSH_REMOTE="$(git config --get remote.pushDefault || true)" +fi +if [ -z "$NMP_PUSH_REMOTE" ]; then + NMP_PUSH_REMOTE="$(git config --get "branch.$NMP_BRANCH.remote" || true)" +fi +if [ -z "$NMP_PUSH_REMOTE" ] || [ "$NMP_PUSH_REMOTE" = "." ]; then + NMP_PUSH_REMOTE=origin +fi + +NMP_PUSH_REPO="" +NMP_PUSH_URL="" +while IFS= read -r NMP_URL; do + NMP_URL_REPO="$(nmp_repo_for_url "$NMP_URL")" || exit 1 + if [ -z "$NMP_PUSH_REPO" ]; then + NMP_PUSH_REPO="$NMP_URL_REPO" + NMP_PUSH_URL="$NMP_URL" + elif [ "$NMP_URL_REPO" != "$NMP_PUSH_REPO" ]; then + printf 'Push URLs for %s resolve to different repositories\n' "$NMP_PUSH_REMOTE" >&2 + exit 1 + fi +done < <(git remote get-url --push --all "$NMP_PUSH_REMOTE") + +if [ -z "$NMP_PUSH_REPO" ]; then + printf 'Remote %s has no push URL\n' "$NMP_PUSH_REMOTE" >&2 + exit 1 +fi +NMP_PUSH_PARENT="$(gh api "repos/$NMP_PUSH_REPO" --jq '.parent.full_name // ""')" +if [ "$NMP_PUSH_REPO" != "$NMP_REPO" ] && [ "$NMP_PUSH_PARENT" != "$NMP_REPO" ]; then + printf 'Push repository %s is neither %s nor its fork\n' "$NMP_PUSH_REPO" "$NMP_REPO" >&2 + exit 1 +fi +NMP_HEAD_OWNER="${NMP_PUSH_REPO%%/*}" + git log --oneline "$NMP_BASE_REF..HEAD" -git push --set-upstream origin "HEAD:refs/heads/$NMP_BRANCH" +git push --set-upstream "$NMP_PUSH_REMOTE" "HEAD:refs/heads/$NMP_BRANCH" ``` -Use a normal push first. On a non-fast-forward rejection, fetch the exact remote branch and inspect both histories. Stop if the remote contains unexpected work. +Validate every configured push URL for the selected push remote. Permit the canonical repository or a GitHub fork whose parent is `NMP_REPO`; reject plaintext transports, unrelated repositories, and mixed push targets. Use a normal push first. On a non-fast-forward rejection, fetch the exact branch from the validated push URL and inspect both histories. Stop if the remote contains unexpected work. Only after the user approves rewriting a published branch, pin the lease to the fetched remote SHA: ```bash -git fetch origin "$NMP_BRANCH" -NMP_EXPECTED_REMOTE_SHA="$(git rev-parse "origin/$NMP_BRANCH")" +git fetch "$NMP_PUSH_URL" "refs/heads/$NMP_BRANCH:refs/remotes/nmp-push/$NMP_BRANCH" +NMP_EXPECTED_REMOTE_SHA="$(git rev-parse "refs/remotes/nmp-push/$NMP_BRANCH")" git push \ --force-with-lease="refs/heads/$NMP_BRANCH:$NMP_EXPECTED_REMOTE_SHA" \ - origin "HEAD:refs/heads/$NMP_BRANCH" + "$NMP_PUSH_REMOTE" "HEAD:refs/heads/$NMP_BRANCH" ``` Never bypass branch protection or required checks. Stop on any access error. @@ -238,7 +314,7 @@ Never bypass branch protection or required checks. Stop on any access error. Check for an existing open PR first: ```bash -gh pr list --repo "$NMP_REPO" --head "$NMP_BRANCH" --state open --json number,title,url,isDraft +gh pr list --repo "$NMP_REPO" --head "$NMP_HEAD_OWNER:$NMP_BRANCH" --state open --json number,title,url,isDraft ``` Rerun the DCO audit immediately before creation. Use explicit metadata; do not use `--fill` because it bypasses trusted-template completion. @@ -248,7 +324,7 @@ Create a draft when work, validation, or a required decision remains: ```bash gh pr create --repo "$NMP_REPO" \ --base "$NMP_BASE_BRANCH" \ - --head "$NMP_BRANCH" \ + --head "$NMP_HEAD_OWNER:$NMP_BRANCH" \ --title "" \ --body-file "$NMP_PR_BODY" \ --draft From 39b43b1343829e6d93bf9eb0703c550aedbd71be Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Wed, 5 Aug 2026 14:09:26 -0700 Subject: [PATCH 5/6] fix(agents): simplify remote and DCO checks Signed-off-by: Miyoung Choi --- .agents/skills/contributor-create-pr/SKILL.md | 124 ++++++++---------- 1 file changed, 53 insertions(+), 71 deletions(-) diff --git a/.agents/skills/contributor-create-pr/SKILL.md b/.agents/skills/contributor-create-pr/SKILL.md index d4c385a588..5282e6f854 100644 --- a/.agents/skills/contributor-create-pr/SKILL.md +++ b/.agents/skills/contributor-create-pr/SKILL.md @@ -40,41 +40,40 @@ nmp_repo_for_url() { gh repo view "$1" --json nameWithOwner --jq '.nameWithOwner' } -NMP_UPSTREAM_CANDIDATES="" -for NMP_REMOTE in $(git remote); do - NMP_REMOTE_IS_UPSTREAM=true - NMP_REMOTE_HAS_FETCH_URL=false - while IFS= read -r NMP_URL; do - NMP_REMOTE_HAS_FETCH_URL=true - NMP_URL_REPO="$(nmp_repo_for_url "$NMP_URL")" || { - NMP_REMOTE_IS_UPSTREAM=false - break - } - if [ "$NMP_URL_REPO" != "$NMP_REPO" ]; then - NMP_REMOTE_IS_UPSTREAM=false - break - fi - done < <(git remote get-url --all "$NMP_REMOTE") - - if "$NMP_REMOTE_HAS_FETCH_URL" && "$NMP_REMOTE_IS_UPSTREAM"; then - NMP_UPSTREAM_CANDIDATES="${NMP_UPSTREAM_CANDIDATES}${NMP_UPSTREAM_CANDIDATES:+$'\n'}${NMP_REMOTE}" +nmp_require_url_repo() { + NMP_ACTUAL_REPO="$(nmp_repo_for_url "$1")" || return 1 + if [ "$NMP_ACTUAL_REPO" != "$2" ]; then + printf 'Git URL resolves to %s, expected %s\n' "$NMP_ACTUAL_REPO" "$2" >&2 + return 1 fi -done +} -NMP_UPSTREAM_COUNT="$(printf '%s\n' "$NMP_UPSTREAM_CANDIDATES" | sed '/^$/d' | wc -l | tr -d ' ')" -if printf '%s\n' "$NMP_UPSTREAM_CANDIDATES" | grep -qx upstream; then - NMP_UPSTREAM_REMOTE=upstream -elif printf '%s\n' "$NMP_UPSTREAM_CANDIDATES" | grep -qx origin; then +NMP_PUSH_REMOTE=origin +NMP_PUSH_REPO="$(nmp_repo_for_url "$(git remote get-url "$NMP_PUSH_REMOTE")")" +while IFS= read -r NMP_URL; do + nmp_require_url_repo "$NMP_URL" "$NMP_PUSH_REPO" || exit 1 +done < <( + git remote get-url --all "$NMP_PUSH_REMOTE" + git remote get-url --push --all "$NMP_PUSH_REMOTE" +) +NMP_PUSH_URL="$(git remote get-url --push "$NMP_PUSH_REMOTE")" +NMP_PUSH_PARENT="$(gh api "repos/$NMP_PUSH_REPO" --jq '.parent.full_name // ""')" + +if [ "$NMP_PUSH_REPO" = "$NMP_REPO" ]; then NMP_UPSTREAM_REMOTE=origin -elif [ "$NMP_UPSTREAM_COUNT" -eq 1 ]; then - NMP_UPSTREAM_REMOTE="$NMP_UPSTREAM_CANDIDATES" +elif [ "$NMP_PUSH_PARENT" = "$NMP_REPO" ]; then + NMP_UPSTREAM_REMOTE=upstream + while IFS= read -r NMP_URL; do + nmp_require_url_repo "$NMP_URL" "$NMP_REPO" || exit 1 + done < <(git remote get-url --all "$NMP_UPSTREAM_REMOTE") else - printf 'Could not select one encrypted remote for %s; found %s candidates\n' "$NMP_REPO" "$NMP_UPSTREAM_COUNT" >&2 + printf 'origin is neither %s nor its fork\n' "$NMP_REPO" >&2 exit 1 fi +NMP_HEAD_OWNER="${NMP_PUSH_REPO%%/*}" ``` -Require an active host-authenticated account, `nameWithOwner` equal to `NVIDIA-NeMo/nemo-platform`, and a configured remote whose fetch URLs all resolve to that repository over encrypted HTTPS or SSH. Prefer a matching `upstream`, then a matching `origin`, then a sole matching remote. A contributor fork may remain `origin`; use a separate canonical remote such as `upstream` for the trusted base. If no canonical remote exists or unnamed candidates remain ambiguous, stop and ask the user to configure or select one rather than adding or rewriting remotes implicitly. Keep `NMP_REPO`, `NMP_UPSTREAM_REMOTE`, and `nmp_repo_for_url` available for later sections. +Require a conventional checkout: `origin` is the validated push target; when `origin` is a fork of `NMP_REPO`, an `upstream` remote must fetch from the canonical repository. Require every used URL to resolve to its expected GitHub repository over encrypted HTTPS or SSH. Stop on custom or mismatched layouts rather than mutating remotes implicitly. Keep the `NMP_*` variables and helper functions available for later sections. If `gh` fails only because a sandbox blocks host or network access, retry the same command with narrowly scoped host/network approval. If authentication or authorization still fails outside the sandbox, report the command and error and stop. @@ -188,7 +187,16 @@ Never run `git commit`, `git commit --amend`, or a fixup commit without `-s`. If Run this gate after any commit, amend, rebase, cherry-pick, conflict resolution, or automated fix, and before every push or `gh pr create`. It requires a syntactically valid sign-off whose email matches the commit author's email, case-insensitively: ```bash -bash -euo pipefail <<'BASH' +if [ "$(git rev-parse --is-shallow-repository)" = true ]; then + git fetch --unshallow "$NMP_PUSH_REMOTE" +fi +if [ "$(git rev-parse --is-shallow-repository)" = true ] \ + || ! git merge-base "$NMP_BASE_REF" HEAD >/dev/null; then + printf 'DCO audit requires complete feature-branch ancestry\n' >&2 + exit 1 +fi + +NMP_BASE_REF="$NMP_BASE_REF" bash -euo pipefail <<'BASH' : "${NMP_BASE_REF:?set NMP_BASE_REF to the refreshed canonical upstream default branch}" NMP_DCO_FAILED=0 NMP_COMMIT_COUNT=0 @@ -239,13 +247,22 @@ Use one of the conventional types accepted by the pinned title action: `feat`, ` Locate the PR template in the trusted base, not in the feature branch and not in a recent PR body: ```bash -git ls-tree -r --name-only "$NMP_BASE_REF" \ - | rg -i '(^|/)(pull_request_template)(\.md|/.*\.md)$' +NMP_TEMPLATE_PATHS="$(git ls-tree -r --name-only "$NMP_BASE_REF")" +NMP_TEMPLATE_PATHS="$( + printf '%s\n' "$NMP_TEMPLATE_PATHS" \ + | awk 'tolower($0) ~ /(^|\/)pull_request_template(\.md|\/.*\.md)$/' +)" +NMP_TEMPLATE_COUNT="$(printf '%s\n' "$NMP_TEMPLATE_PATHS" | sed '/^$/d' | wc -l | tr -d ' ')" +if [ "$NMP_TEMPLATE_COUNT" -ne 1 ]; then + printf 'Expected one trusted PR template, found %s\n' "$NMP_TEMPLATE_COUNT" >&2 + exit 1 +fi +NMP_TEMPLATE_PATH="$NMP_TEMPLATE_PATHS" +NMP_PR_BODY="$(mktemp /tmp/nemo-platform-pr-body.XXXXXX)" +git show "$NMP_BASE_REF:$NMP_TEMPLATE_PATH" > "$NMP_PR_BODY" ``` -- If exactly one template applies, copy it with `git show "$NMP_BASE_REF:"` into a file created by `mktemp /tmp/nemo-platform-pr-body.XXXXXX`. -- If multiple templates exist, select the repository-defined template that matches the change. Ask when selection is ambiguous. -- If no template exists on the trusted base, report the missing repository dependency and stop before `gh pr create`. Do not invent a template or silently reuse a branch-modified or historical body. +- If the trusted base does not have exactly one template, report the missing or ambiguous repository dependency and stop before `gh pr create`. Do not invent a template or silently reuse a branch-modified or historical body. - If the PR changes the template, still populate the trusted base version and explain the template change in that body. Preserve the template's section order, comments, and checkbox semantics. Complete every applicable section from `git diff "$NMP_BASE_REF...HEAD"`. Check only items backed by command, hook, or CI evidence. Use `Fixes #NNN` or `Closes #NNN` only for a real related issue. Do not add a PR-body sign-off unless the trusted template requests it; the commit-level DCO audit remains mandatory. @@ -256,46 +273,11 @@ Immediately before pushing, rerun the DCO audit, confirm the branch and remote, ```bash NMP_BRANCH="$(git branch --show-current)" -NMP_PUSH_REMOTE="$(git config --get "branch.$NMP_BRANCH.pushRemote" || true)" -if [ -z "$NMP_PUSH_REMOTE" ]; then - NMP_PUSH_REMOTE="$(git config --get remote.pushDefault || true)" -fi -if [ -z "$NMP_PUSH_REMOTE" ]; then - NMP_PUSH_REMOTE="$(git config --get "branch.$NMP_BRANCH.remote" || true)" -fi -if [ -z "$NMP_PUSH_REMOTE" ] || [ "$NMP_PUSH_REMOTE" = "." ]; then - NMP_PUSH_REMOTE=origin -fi - -NMP_PUSH_REPO="" -NMP_PUSH_URL="" -while IFS= read -r NMP_URL; do - NMP_URL_REPO="$(nmp_repo_for_url "$NMP_URL")" || exit 1 - if [ -z "$NMP_PUSH_REPO" ]; then - NMP_PUSH_REPO="$NMP_URL_REPO" - NMP_PUSH_URL="$NMP_URL" - elif [ "$NMP_URL_REPO" != "$NMP_PUSH_REPO" ]; then - printf 'Push URLs for %s resolve to different repositories\n' "$NMP_PUSH_REMOTE" >&2 - exit 1 - fi -done < <(git remote get-url --push --all "$NMP_PUSH_REMOTE") - -if [ -z "$NMP_PUSH_REPO" ]; then - printf 'Remote %s has no push URL\n' "$NMP_PUSH_REMOTE" >&2 - exit 1 -fi -NMP_PUSH_PARENT="$(gh api "repos/$NMP_PUSH_REPO" --jq '.parent.full_name // ""')" -if [ "$NMP_PUSH_REPO" != "$NMP_REPO" ] && [ "$NMP_PUSH_PARENT" != "$NMP_REPO" ]; then - printf 'Push repository %s is neither %s nor its fork\n' "$NMP_PUSH_REPO" "$NMP_REPO" >&2 - exit 1 -fi -NMP_HEAD_OWNER="${NMP_PUSH_REPO%%/*}" - git log --oneline "$NMP_BASE_REF..HEAD" git push --set-upstream "$NMP_PUSH_REMOTE" "HEAD:refs/heads/$NMP_BRANCH" ``` -Validate every configured push URL for the selected push remote. Permit the canonical repository or a GitHub fork whose parent is `NMP_REPO`; reject plaintext transports, unrelated repositories, and mixed push targets. Use a normal push first. On a non-fast-forward rejection, fetch the exact branch from the validated push URL and inspect both histories. Stop if the remote contains unexpected work. +Use the validated `origin` push target from section 1. On a non-fast-forward rejection, fetch the exact branch from its validated push URL and inspect both histories. Stop if the remote contains unexpected work. Only after the user approves rewriting a published branch, pin the lease to the fetched remote SHA: @@ -357,9 +339,9 @@ Inspect CodeRabbit's issue comments, reviews, and inline comments: ```bash gh api "repos/$NMP_REPO/issues//comments" --paginate \ - --jq '.[] | select(.user.login | ascii_downcase | contains("coderabbit")) | {author:.user.login,updated_at,body}' + --jq '.[] | select(.user.login == "coderabbitai[bot]") | {author:.user.login,updated_at,body}' gh api "repos/$NMP_REPO/pulls//comments" --paginate \ - --jq '.[] | select(.user.login | ascii_downcase | contains("coderabbit")) | {author:.user.login,path,line,updated_at,body}' + --jq '.[] | select(.user.login == "coderabbitai[bot]") | {author:.user.login,path,line,updated_at,body}' ``` Verify each finding against the current head. Fix confirmed correctness, security, or test-coverage problems and rerun targeted validation. Do not add abstractions or behavior merely to satisfy reviewer wording. Explain false positives or style-only suggestions when useful. Ask before making a design-changing, risky, broad, or ambiguous change. Do not manually request or re-request CodeRabbit, and do not act on its suggested-reviewer text without user authorization. From 7efc34158eacaf0310902b1803054d65f638b913 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Wed, 5 Aug 2026 14:42:33 -0700 Subject: [PATCH 6/6] fix(agents): refresh PR descriptions after push Signed-off-by: Miyoung Choi --- .agents/skills/contributor-create-pr/SKILL.md | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/.agents/skills/contributor-create-pr/SKILL.md b/.agents/skills/contributor-create-pr/SKILL.md index 5282e6f854..9a8ca5e78e 100644 --- a/.agents/skills/contributor-create-pr/SKILL.md +++ b/.agents/skills/contributor-create-pr/SKILL.md @@ -1,6 +1,6 @@ --- name: contributor-create-pr -description: Create, publish, or advance a GitHub pull request for NVIDIA-NeMo/nemo-platform with repository-aware branch checks, targeted validation, DCO sign-off enforcement, trusted PR-template completion, conventional titles, safe pushes, draft or ready-for-review creation, CI and CodeRabbit follow-up, and merge-conflict handling. Use when the user asks to commit and push work, create or open a PR, submit changes for review, mark a draft ready, monitor a PR, address automated review or CI feedback, or update a conflicted PR in nemo-platform. +description: Create, publish, or advance a GitHub pull request for NVIDIA-NeMo/nemo-platform with repository-aware branch checks, targeted validation, DCO sign-off enforcement, trusted PR-template completion and post-push description refresh, conventional titles, safe pushes, draft or ready-for-review creation, CI and CodeRabbit follow-up, and merge-conflict handling. Use when the user asks to commit and push work, create or open a PR, submit changes for review, mark a draft ready, monitor a PR, address automated review or CI feedback, or update a conflicted PR in nemo-platform. --- @@ -267,12 +267,16 @@ git show "$NMP_BASE_REF:$NMP_TEMPLATE_PATH" > "$NMP_PR_BODY" Preserve the template's section order, comments, and checkbox semantics. Complete every applicable section from `git diff "$NMP_BASE_REF...HEAD"`. Check only items backed by command, hook, or CI evidence. Use `Fixes #NNN` or `Closes #NNN` only for a real related issue. Do not add a PR-body sign-off unless the trusted template requests it; the commit-level DCO audit remains mandatory. -## 8. Push safely +## 8. Push safely and refresh an existing PR Immediately before pushing, rerun the DCO audit, confirm the branch and remote, and inspect the outgoing commits: ```bash NMP_BRANCH="$(git branch --show-current)" +NMP_HEAD_SELECTOR="$NMP_BRANCH" +if [ "$NMP_PUSH_REPO" != "$NMP_REPO" ]; then + NMP_HEAD_SELECTOR="$NMP_HEAD_OWNER:$NMP_BRANCH" +fi git log --oneline "$NMP_BASE_REF..HEAD" git push --set-upstream "$NMP_PUSH_REMOTE" "HEAD:refs/heads/$NMP_BRANCH" ``` @@ -291,12 +295,27 @@ git push \ Never bypass branch protection or required checks. Stop on any access error. +After every successful push to a branch with an open PR, rebuild `NMP_PR_BODY` from the trusted template as in section 7 and update it from the current diff and validation results. Refresh the summary, change list, checklist evidence, limitations, and related issue; remove claims that are no longer true. Then update and verify the live description: + +```bash +NMP_PR_NUMBER="$( + gh pr list --repo "$NMP_REPO" --head "$NMP_HEAD_SELECTOR" \ + --state open --limit 1 --json number --jq '.[0].number // empty' +)" +if [ -n "$NMP_PR_NUMBER" ]; then + gh pr edit "$NMP_PR_NUMBER" --repo "$NMP_REPO" --body-file "$NMP_PR_BODY" + gh pr view "$NMP_PR_NUMBER" --repo "$NMP_REPO" --json url,headRefOid,body +fi +``` + +Do not leave the PR body describing an older head after a review fix, conflict resolution, validation change, or scope change. + ## 9. Create a draft or ready PR Check for an existing open PR first: ```bash -gh pr list --repo "$NMP_REPO" --head "$NMP_HEAD_OWNER:$NMP_BRANCH" --state open --json number,title,url,isDraft +gh pr list --repo "$NMP_REPO" --head "$NMP_HEAD_SELECTOR" --state open --json number,title,url,isDraft ``` Rerun the DCO audit immediately before creation. Use explicit metadata; do not use `--fill` because it bypasses trusted-template completion. @@ -306,7 +325,7 @@ Create a draft when work, validation, or a required decision remains: ```bash gh pr create --repo "$NMP_REPO" \ --base "$NMP_BASE_BRANCH" \ - --head "$NMP_HEAD_OWNER:$NMP_BRANCH" \ + --head "$NMP_HEAD_SELECTOR" \ --title "" \ --body-file "$NMP_PR_BODY" \ --draft