This commit is contained in:
Jonas B. 2026-08-15 18:29:03 -04:00 committed by GitHub
commit ce70c4db2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 44 additions and 9 deletions

View File

@ -41,7 +41,7 @@ gh pr view --json url,number,state -q 'if .state == "OPEN" then "PR #\(.number):
glab mr view -F json 2>/dev/null | jq -r 'if .state == "opened" then "MR_EXISTS" else "NO_MR" end' 2>/dev/null || echo "NO_MR"
```
If an **open** PR/MR already exists: **update** the PR body using `gh pr edit --body-file "$PR_BODY_FILE"` (GitHub) or `glab mr update -d ...` (GitLab). Always regenerate the PR body from scratch using this run's fresh results (test output, coverage audit, review findings, adversarial review, TODOS summary, documentation_section from Step 18). Never reuse stale PR body content from a prior run. **Run the same redaction scan-at-sink (PR body + title) as the create path (Step 19) before editing — scan the temp file, then `gh pr edit --body-file` from it.**
If an **open** PR/MR already exists: **update** the PR body using GitHub's REST API (`gh api -X PATCH "repos/$REPO_NWO/pulls/$PR_NUMBER" --input -`) or `glab mr update -d ...` (GitLab). Do not use `gh pr edit` for GitHub updates; it can hit deprecated Projects-classic GraphQL fields on some repos. Always regenerate the PR body from scratch using this run's fresh results (test output, coverage audit, review findings, adversarial review, TODOS summary, documentation_section from Step 18). Never reuse stale PR body content from a prior run. **Run the same redaction scan-at-sink (PR body + title) as the create path (Step 19) before editing — scan the temp file, then send those exact bytes through the REST PATCH path.**
**REST fallback (#1079):** on some repos `gh pr edit` hard-errors with a GraphQL deprecation mentioning `repository.pullRequest.projectCards` ("Projects (classic) is being deprecated..."). That is a `gh` GraphQL-path problem, not a permissions problem — do not re-ask for auth. Fall back to the REST endpoint, which never touches the deprecated field, using the SAME already-scanned temp file: `PR_NUMBER=$(gh pr view --json number -q .number)` then `gh api "repos/{owner}/{repo}/pulls/$PR_NUMBER" -X PATCH -F body=@"$PR_BODY_FILE"` for the body, and `gh api "repos/{owner}/{repo}/pulls/$PR_NUMBER" -X PATCH -f title="$NEW_TITLE"` when the title edit below hits the same error. Verify with the same self-checks as the primary path.
@ -49,7 +49,14 @@ If an **open** PR/MR already exists: **update** the PR body using `gh pr edit --
1. Read the current title: `CURRENT=$(gh pr view --json title -q .title)` (or `glab mr view -F json | jq -r .title`).
2. Compute the corrected title: `NEW_TITLE=$(~/.claude/skills/gstack/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" "$CURRENT")`. The helper handles three cases: title already correct (no-op), title has a different `v<X.Y.Z.W>` prefix (replace it), or title has no version prefix (prepend one).
3. If `NEW_TITLE` differs from `CURRENT`, run `gh pr edit --title "$NEW_TITLE"` (or `glab mr update -t "$NEW_TITLE"`).
3. If `NEW_TITLE` differs from `CURRENT`, update GitHub via REST:
```bash
PR_NUMBER=$(gh pr view --json number -q .number)
REPO_NWO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
jq -n --arg title "$NEW_TITLE" '{title:$title}' \
| gh api -X PATCH "repos/$REPO_NWO/pulls/$PR_NUMBER" --input -
```
For GitLab, run `glab mr update -t "$NEW_TITLE"`.
4. **Self-check:** re-fetch the title and assert it starts with `v$NEW_VERSION `. If it does not, retry the edit once. If still wrong, surface the failure to the user.
This keeps the title truthful when Step 12's queue-drift detection rebumps a stale version, and forces the format on PRs that were created without it.
@ -179,7 +186,17 @@ printf '%s' "v$NEW_VERSION <type>: <summary>" | ~/.claude/skills/gstack/bin/gsta
```
HIGH blocks (exit 3, no skip). MEDIUM → AskUserQuestion (PII subset offers
`--auto-redact`). Same scan runs before the `gh pr edit --body` path (Step 17).
`--auto-redact`). Same scan runs before the GitHub REST PATCH body path (Step 17).
**If a GitHub PR already exists:** update from the SCANNED file with REST (exact bytes scanned = bytes sent):
```bash
PR_NUMBER=$(gh pr view --json number -q .number)
REPO_NWO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
jq -n --rawfile body "$PR_BODY_FILE" '{body:$body}' \
| gh api -X PATCH "repos/$REPO_NWO/pulls/$PR_NUMBER" --input -
rm -f "$PR_BODY_FILE"
```
**If GitHub:** create from the SCANNED file (exact bytes scanned = bytes sent):

View File

@ -39,7 +39,7 @@ gh pr view --json url,number,state -q 'if .state == "OPEN" then "PR #\(.number):
glab mr view -F json 2>/dev/null | jq -r 'if .state == "opened" then "MR_EXISTS" else "NO_MR" end' 2>/dev/null || echo "NO_MR"
```
If an **open** PR/MR already exists: **update** the PR body using `gh pr edit --body-file "$PR_BODY_FILE"` (GitHub) or `glab mr update -d ...` (GitLab). Always regenerate the PR body from scratch using this run's fresh results (test output, coverage audit, review findings, adversarial review, TODOS summary, documentation_section from Step 18). Never reuse stale PR body content from a prior run. **Run the same redaction scan-at-sink (PR body + title) as the create path (Step 19) before editing — scan the temp file, then `gh pr edit --body-file` from it.**
If an **open** PR/MR already exists: **update** the PR body using GitHub's REST API (`gh api -X PATCH "repos/$REPO_NWO/pulls/$PR_NUMBER" --input -`) or `glab mr update -d ...` (GitLab). Do not use `gh pr edit` for GitHub updates; it can hit deprecated Projects-classic GraphQL fields on some repos. Always regenerate the PR body from scratch using this run's fresh results (test output, coverage audit, review findings, adversarial review, TODOS summary, documentation_section from Step 18). Never reuse stale PR body content from a prior run. **Run the same redaction scan-at-sink (PR body + title) as the create path (Step 19) before editing — scan the temp file, then send those exact bytes through the REST PATCH path.**
**REST fallback (#1079):** on some repos `gh pr edit` hard-errors with a GraphQL deprecation mentioning `repository.pullRequest.projectCards` ("Projects (classic) is being deprecated..."). That is a `gh` GraphQL-path problem, not a permissions problem — do not re-ask for auth. Fall back to the REST endpoint, which never touches the deprecated field, using the SAME already-scanned temp file: `PR_NUMBER=$(gh pr view --json number -q .number)` then `gh api "repos/{owner}/{repo}/pulls/$PR_NUMBER" -X PATCH -F body=@"$PR_BODY_FILE"` for the body, and `gh api "repos/{owner}/{repo}/pulls/$PR_NUMBER" -X PATCH -f title="$NEW_TITLE"` when the title edit below hits the same error. Verify with the same self-checks as the primary path.
@ -47,7 +47,14 @@ If an **open** PR/MR already exists: **update** the PR body using `gh pr edit --
1. Read the current title: `CURRENT=$(gh pr view --json title -q .title)` (or `glab mr view -F json | jq -r .title`).
2. Compute the corrected title: `NEW_TITLE=$(~/.claude/skills/gstack/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" "$CURRENT")`. The helper handles three cases: title already correct (no-op), title has a different `v<X.Y.Z.W>` prefix (replace it), or title has no version prefix (prepend one).
3. If `NEW_TITLE` differs from `CURRENT`, run `gh pr edit --title "$NEW_TITLE"` (or `glab mr update -t "$NEW_TITLE"`).
3. If `NEW_TITLE` differs from `CURRENT`, update GitHub via REST:
```bash
PR_NUMBER=$(gh pr view --json number -q .number)
REPO_NWO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
jq -n --arg title "$NEW_TITLE" '{title:$title}' \
| gh api -X PATCH "repos/$REPO_NWO/pulls/$PR_NUMBER" --input -
```
For GitLab, run `glab mr update -t "$NEW_TITLE"`.
4. **Self-check:** re-fetch the title and assert it starts with `v$NEW_VERSION `. If it does not, retry the edit once. If still wrong, surface the failure to the user.
This keeps the title truthful when Step 12's queue-drift detection rebumps a stale version, and forces the format on PRs that were created without it.
@ -177,7 +184,17 @@ printf '%s' "v$NEW_VERSION <type>: <summary>" | ~/.claude/skills/gstack/bin/gsta
```
HIGH blocks (exit 3, no skip). MEDIUM → AskUserQuestion (PII subset offers
`--auto-redact`). Same scan runs before the `gh pr edit --body` path (Step 17).
`--auto-redact`). Same scan runs before the GitHub REST PATCH body path (Step 17).
**If a GitHub PR already exists:** update from the SCANNED file with REST (exact bytes scanned = bytes sent):
```bash
PR_NUMBER=$(gh pr view --json number -q .number)
REPO_NWO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
jq -n --rawfile body "$PR_BODY_FILE" '{body:$body}' \
| gh api -X PATCH "repos/$REPO_NWO/pulls/$PR_NUMBER" --input -
rm -f "$PR_BODY_FILE"
```
**If GitHub:** create from the SCANNED file (exact bytes scanned = bytes sent):

View File

@ -120,7 +120,7 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// ...while the full create/update procedure stays carved into pr-body.md
// (out of the skeleton, present in the union). Asserts BOTH PR paths
// survive: the create path and the idempotent update path.
mustMoveToSection: ['gh pr create --base', 'gh pr edit --title'],
mustMoveToSection: ['gh pr create --base', 'gh api -X PATCH'],
// ship is operational (multi-STOP, not a plan review); no single post-STOP gate.
gateAfterStop: undefined,
},

View File

@ -32,8 +32,9 @@ describe("/ship redaction wiring", () => {
test("creates from the scanned temp file (exact bytes)", () => {
expect(TMPL).toMatch(/gh pr create[\s\S]{0,120}--body-file "\$PR_BODY_FILE"/);
});
test("edit path also scans before sending", () => {
expect(TMPL).toMatch(/gh pr edit --body-file "\$PR_BODY_FILE"/);
test("REST edit path also scans before sending", () => {
expect(TMPL).toMatch(/gh api -X PATCH "repos\/\$REPO_NWO\/pulls\/\$PR_NUMBER" --input -/);
expect(TMPL).not.toContain("gh pr edit --body-file");
expect(TMPL).toMatch(/same redaction scan-at-sink.*before editing/i);
});
test("HIGH blocks the PR (exit 3), no skip", () => {