mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-10 02:30:39 +01:00
Write PR titles, commit messages and branch names in plain English (GST-1825) (#4146)
* Add a check that keeps tracker codes and internal jargon out of PR titles and branch names gstack-plain-title checks the readable front of a title, branch name, PR opening or changelog entry against the repository's .gstack/plain-titles.json; one trailing issue code stays allowed. A Plain title workflow runs it on every new pull request, and /ship's push refuses a never-pushed branch whose name fails it. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Move the PR body's machine-read lines into a closing Agent notes section The ship receipt, its runs block and the Plan line now live in one collapsed section at the end of the body, and every reader looks there first while still accepting the old receipt-at-top layout. gstack-post refuses to post a title the plain-title check refuses, and land uses the checked PR title as main's commit title. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Teach /ship to write plain titles, PR openings, commit messages and changelog entries The README now lists what gstack is for, so a PR opening can name the goal it serves. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Keep tests, size budgets and generated files in step with plain titles and Agent notes Copy lists that install result-code modules now include the plain-title codes, the ship goldens and size ledger are refreshed, the new workflow is classified, and the pinned prose tests read the new title and body layout. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Record the test-profile list change as non-behavioral for the shared test harness Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Keep text people add below the PR notes, and fix three smaller review findings The notes section now ends at its own closing tag, so a collapsible added after it survives the next post. The opening check splits the body the same way the notes reader does. Wave PR titles no longer name the area, which the plain-title check could refuse. The Capy line in the PR-body steps now puts Approvals in the notes. Review-Finding: p1/specialist-batched#1 reader=testing Review-Finding: p1/red-team#1 reader=red-team Review-Finding: p1/red-team#2 reader=red-team Review-Finding: p1/red-team#3 reader=red-team Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Mask credentials in refused-title lines and keep older PRs' plan lines readable A refused title's printed match is masked by the secret scanner first. The Plan line reader falls back to the whole body when the Agent notes section has none, so a body written before this change keeps its plan when its first receipt lands. The notes section also ends at its own closing tag when its opener is split or marked open, or when a collapsible inside it is left unclosed. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Print a fixed placeholder when a credential in a refused title cannot be masked Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Keep a plain-title policy edit from scheduling every paid eval Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Record the paid-eval list change as non-behavioral for the shared test harness Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Recheck the PR title on every edit so a base change cannot turn a refused title green Review-Finding: s11/targeted#1 reader=targeted Review-Finding: s11/adversarial-native#1 reader=adversarial-native Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
892c3b13cd
commit
914e7b6fb3
78 files changed
+1770
-174
No files matched your search
@@ -0,0 +1,89 @@
|
||||
name: Plain title
|
||||
|
||||
# Checks that a PR's title reads in everyday words (R76, docs/ship-policy.md#plain-titles).
|
||||
# The title is refused in the front: tracker codes, project-only words and a
|
||||
# type(scope): prefix. One trailing group of references like (ABC-123) or (#4122)
|
||||
# is fine. The branch name, the PR opening and the commit subjects only warn.
|
||||
# PRs created before the policy's since date are exempt.
|
||||
#
|
||||
# WHY pull_request_target: it runs the base repository's copy of this workflow,
|
||||
# of bin/gstack-plain-title and of .gstack/plain-titles.json, so a PR cannot
|
||||
# change the rule it is checked against, and a title edit reruns the check.
|
||||
#
|
||||
# WHY this is SAFE:
|
||||
# - The job checks out the BASE repo (no ref:) and never runs PR code.
|
||||
# - The token is read-only (contents: read, pull-requests: read).
|
||||
# - The title, branch, body and created_at arrive only through env: and are
|
||||
# written to files with printf '%s'. No PR field is inlined into run:.
|
||||
# - Only the checker's match lines reach the log; PR text is never echoed.
|
||||
# test/plain-title-workflow-safety.test.ts is the static tripwire for all of it.
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, synchronize, reopened, ready_for_review]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
plain-title:
|
||||
name: Plain title
|
||||
# No job-level if: every event, a base change included, reruns the title
|
||||
# check, because a skipped run would count as passing over a refused title.
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
# A newer event for the same PR cancels the older run.
|
||||
concurrency:
|
||||
group: plain-title-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
# Base repo only: trusted code. No PR-head checkout.
|
||||
- name: Checkout base repo (trusted)
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: 1.4.2
|
||||
|
||||
- name: Write the PR fields to files
|
||||
env:
|
||||
# Attacker-controlled on fork PRs: env only, never inlined into run:.
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_NUM: ${{ github.event.pull_request.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
printf '%s' "$PR_TITLE" > "$RUNNER_TEMP/pr-title.txt"
|
||||
printf '%s' "$PR_BODY" > "$RUNNER_TEMP/pr-body.txt"
|
||||
# Commit subjects are a warning only: skip quietly when the API read fails.
|
||||
if ! gh api --paginate "repos/$REPO/pulls/$PR_NUM/commits" \
|
||||
--jq '.[] | select((.parents | length) < 2) | .commit.message | split("\n")[0]' \
|
||||
> "$RUNNER_TEMP/pr-commits.txt" 2>/dev/null; then
|
||||
: > "$RUNNER_TEMP/pr-commits.txt"
|
||||
fi
|
||||
|
||||
- name: Check the title
|
||||
env:
|
||||
PR_CREATED_AT: ${{ github.event.pull_request.created_at }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bun --no-env-file --config=/dev/null bin/gstack-plain-title check \
|
||||
--title-file "$RUNNER_TEMP/pr-title.txt" --created-at "$PR_CREATED_AT" --mode refuse
|
||||
|
||||
- name: Check the branch name, opening and commit subjects (warnings only)
|
||||
if: ${{ !cancelled() }}
|
||||
env:
|
||||
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
PR_CREATED_AT: ${{ github.event.pull_request.created_at }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bun --no-env-file --config=/dev/null bin/gstack-plain-title check \
|
||||
--branch "$PR_BRANCH" --body-file "$RUNNER_TEMP/pr-body.txt" \
|
||||
--commits-file "$RUNNER_TEMP/pr-commits.txt" --created-at "$PR_CREATED_AT" --mode warn
|
||||
Reference in new issue
Block a user