Enforce provenance on every agent change
test / test (pull_request) Successful in 7s

Authored-By: Codex (GPT-5) <noreply@openai.com>
This commit is contained in:
2026-08-27 16:56:05 +00:00
parent cc0bee61a4
commit 8a1d71c4e9
4 changed files with 52 additions and 12 deletions
+10 -6
View File
@@ -18,6 +18,7 @@ Title: [bug-x7k2m9] Fix terminal resize loss (chore PRs: no [bug-id] prefix
- `<bug-id>` is `bug-` followed by lowercase alphanumeric characters - `<bug-id>` is `bug-` followed by lowercase alphanumeric characters
- `<kebab-description>` is lowercase alphanumeric with hyphens, starting with a letter or digit - `<kebab-description>` is lowercase alphanumeric with hyphens, starting with a letter or digit
- When both the branch and the title carry a bug-id they **must match** - When both the branch and the title carry a bug-id they **must match**
- Titles are plain-language imperatives without Conventional Commit syntax
- Break-glass: PRs authored by `dfritz` are exempt from all checks - Break-glass: PRs authored by `dfritz` are exempt from all checks
## Usage ## Usage
@@ -57,7 +58,7 @@ job — the step still fails, but the job cannot block the PR.
## Behavior ## Behavior
The check validates four things for a Bug-backed Agent PR: The check validates four things for every non-break-glass Agent PR:
1. **Branch form** — must be `<role>/bug-<id>/<kebab>` or `chore/<kebab>`. 1. **Branch form** — must be `<role>/bug-<id>/<kebab>` or `chore/<kebab>`.
Unknown roles, missing `bug-` segment, uppercase bug-ids, and empty Unknown roles, missing `bug-` segment, uppercase bug-ids, and empty
@@ -65,14 +66,17 @@ The check validates four things for a Bug-backed Agent PR:
2. **Title form** — for a `role/bug` branch the title must start with 2. **Title form** — for a `role/bug` branch the title must start with
`[bug-<id>] `. For a `chore` branch the title must have no `[bug-id]` `[bug-<id>] `. For a `chore` branch the title must have no `[bug-id]`
prefix. If both carry a bug-id they must match. prefix. If both carry a bug-id they must match. Neither form accepts
Conventional Commit syntax.
3. **Tracking and attribution**`## Tracking` contains both the literal 3. **Tracking and attribution** on Bug-backed work, `## Tracking` contains both the literal
`Fixes bug-<id>` automation token and the matching navigable `Fixes bug-<id>` automation token and the matching navigable
`https://agenthub.fritzlab.net/bug-<id>` URL. A separate `## Attribution` `https://agenthub.fritzlab.net/bug-<id>` URL. Every PR has a separate
section contains the canonical `Authored-By` product/model watermark. `## Attribution` section containing the canonical `Authored-By`
product/model watermark.
4. **Commit attribution** — every commit in `base-sha..head-sha` ends with 4. **Commit attribution** — every commit in `base-sha..head-sha`, including
commits on `chore/` branches, ends with
the canonical `Authored-By` trailer, separated from the message body by a the canonical `Authored-By` trailer, separated from the message body by a
blank line. The naming job must check out full history before this action. blank line. The naming job must check out full history before this action.
+1 -1
View File
@@ -8,7 +8,7 @@ description: |
Standard: Standard:
branch: <role>/bug-<id>/<kebab> e.g. dev/bug-x7k2m9/fix-terminal-resize branch: <role>/bug-<id>/<kebab> e.g. dev/bug-x7k2m9/fix-terminal-resize
chore: chore/<kebab> bug-less trivia only chore: chore/<kebab> bug-less trivia only
title: [bug-<id>] Description (chore PRs: no [bug-id] prefix) title: [bug-<id>] Description (chore PRs: no [bug-id] prefix; no Conventional Commit syntax)
Role must be a roster handle: dev | ux | ops | security | perf | architect | support Role must be a roster handle: dev | ux | ops | security | perf | architect | support
Break-glass: PRs authored by dfritz are exempt. Break-glass: PRs authored by dfritz are exempt.
+15 -5
View File
@@ -17,7 +17,7 @@ VISIBLE_BODY=$(printf '%s\n' "${BODY}" | awk '
{ {
line=$0 line=$0
fence_pos=1 fence_pos=1
while (fence_pos <= 4 && substr(line, fence_pos, 1) == " ") fence_pos++ while (fence_pos <= 3 && substr(line, fence_pos, 1) == " ") fence_pos++
fence_char=substr(line, fence_pos, 1) fence_char=substr(line, fence_pos, 1)
fence_run=0 fence_run=0
if (fence_char == "`" || fence_char == "~") { if (fence_char == "`" || fence_char == "~") {
@@ -58,7 +58,8 @@ VISIBLE_BODY=$(printf '%s\n' "${BODY}" | awk '
close_pos=pos + ticks close_pos=pos + ticks
found_close=0 found_close=0
while (close_pos <= length(line)) { while (close_pos <= length(line)) {
if (substr(line, close_pos, ticks) == substr(line, pos, ticks) && if (substr(line, close_pos - 1, 1) != "`" &&
substr(line, close_pos, ticks) == substr(line, pos, ticks) &&
substr(line, close_pos + ticks, 1) != "`") { substr(line, close_pos + ticks, 1) != "`") {
found_close=1 found_close=1
break break
@@ -116,6 +117,12 @@ if echo "${TITLE}" | grep -qE "^\[bug-[a-z0-9]+\] ."; then
TITLE_BUG=$(echo "${TITLE}" | sed -E 's|^\[(bug-[a-z0-9]+)\].*|\1|') TITLE_BUG=$(echo "${TITLE}" | sed -E 's|^\[(bug-[a-z0-9]+)\].*|\1|')
fi fi
PLAIN_TITLE=$(printf '%s\n' "${TITLE}" | sed -E 's/^\[bug-[a-z0-9]+\][[:space:]]+//')
if printf '%s\n' "${PLAIN_TITLE}" | grep -qE '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([^)]*\))?!?:[[:space:]]'; then
echo "FAIL[check-naming]: title must be a plain-language imperative without Conventional Commit syntax"
FAILED=1
fi
if [ "${BRANCH_KIND}" = "role-bug" ]; then if [ "${BRANCH_KIND}" = "role-bug" ]; then
if [ -z "${TITLE_BUG}" ]; then if [ -z "${TITLE_BUG}" ]; then
echo "FAIL[check-naming]: title must lead with [${BRANCH_BUG}]" echo "FAIL[check-naming]: title must lead with [${BRANCH_BUG}]"
@@ -146,6 +153,12 @@ if [ "${BRANCH_KIND}" = "role-bug" ]; then
fi fi
fi fi
elif [ "${BRANCH_KIND}" = "chore" ] && [ -n "${TITLE_BUG}" ]; then
echo "FAIL[check-naming]: chore branch should not carry a [bug-id] title prefix"
FAILED=1
fi
if [ "${BRANCH_KIND}" != "invalid" ]; then
attribution=$(printf '%s\n' "${VISIBLE_BODY}" | awk ' attribution=$(printf '%s\n' "${VISIBLE_BODY}" | awk '
/^## Attribution[[:space:]]*$/ { in_section=1; next } /^## Attribution[[:space:]]*$/ { in_section=1; next }
/^## / && in_section { exit } /^## / && in_section { exit }
@@ -192,9 +205,6 @@ if [ "${BRANCH_KIND}" = "role-bug" ]; then
echo "FAIL[check-naming]: base-sha and head-sha must be lowercase hexadecimal commit SHAs" echo "FAIL[check-naming]: base-sha and head-sha must be lowercase hexadecimal commit SHAs"
FAILED=1 FAILED=1
fi fi
elif [ "${BRANCH_KIND}" = "chore" ] && [ -n "${TITLE_BUG}" ]; then
echo "FAIL[check-naming]: chore branch should not carry a [bug-id] title prefix"
FAILED=1
fi fi
if [ "${FAILED}" -eq 0 ]; then if [ "${FAILED}" -eq 0 ]; then
+26
View File
@@ -73,6 +73,22 @@ check_contract_pass() {
fi fi
} }
check_chore_contract() {
local desc="$1" body="$2" base="$3" head="$4" diagnostic="$5"
local out rc=0
out=$(cd "${FIXTURES}" && HEAD_BRANCH="chore/contract" \
PR_TITLE="Improve delivery contract" PR_AUTHOR="dev" \
PR_BODY="$body" BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
if [ "$rc" -eq 1 ] && printf '%s\n' "$out" | grep -Fq "$diagnostic"; then
echo "PASS [$desc]"
PASS=$((PASS + 1))
else
echo "FAIL [$desc]: expected exit=1 and diagnostic '$diagnostic', got exit=$rc"
echo " output: $out"
FAIL=$((FAIL + 1))
fi
}
# break-glass # break-glass
check "dfritz exempt — invalid branch" 0 "totally/wrong-branch" "no prefix" "dfritz" check "dfritz exempt — invalid branch" 0 "totally/wrong-branch" "no prefix" "dfritz"
check "dfritz exempt — mismatch" 0 "dev/bug-abc/thing" "[bug-xyz] Thing" "dfritz" check "dfritz exempt — mismatch" 0 "dev/bug-abc/thing" "[bug-xyz] Thing" "dfritz"
@@ -98,6 +114,8 @@ check "bug-id mismatch" 1 "dev/bug-x7k2m9/fix-resize" "[bug-
# fail: chore branch with [bug-id] title prefix # fail: chore branch with [bug-id] title prefix
check "chore with bug-id title" 1 "chore/bump-deps" "[bug-abc123] Bump deps" "dev" check "chore with bug-id title" 1 "chore/bump-deps" "[bug-abc123] Bump deps" "dev"
check "role/bug Conventional title" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): fix resize" "dev"
check "chore Conventional title" 1 "chore/bump-deps" "chore(deps): bump deps" "dev"
# fail: invalid branch forms # fail: invalid branch forms
check "invalid — no role prefix" 1 "feature/foo-bar" "Add feature" "dev" check "invalid — no role prefix" 1 "feature/foo-bar" "Add feature" "dev"
@@ -126,6 +144,12 @@ check_contract "hidden provenance does not satisfy the visible contract" \
check_contract "comment removal cannot synthesize section headings" \ check_contract "comment removal cannot synthesize section headings" \
$'<!-- hidden -->## Tracking\n- Fixes bug-x7k2m9 — https://agenthub.fritzlab.net/bug-x7k2m9\n\n<!-- hidden -->## Attribution\n- Authored-By: Codex (GPT-5) <noreply@openai.com>' \ $'<!-- hidden -->## Tracking\n- Fixes bug-x7k2m9 — https://agenthub.fritzlab.net/bug-x7k2m9\n\n<!-- hidden -->## Attribution\n- Authored-By: Codex (GPT-5) <noreply@openai.com>' \
"$BASE" "$GOOD_HEAD" "PR body must contain a non-empty ## Tracking section" "$BASE" "$GOOD_HEAD" "PR body must contain a non-empty ## Tracking section"
check_contract "four-space indented backticks do not expose comments" \
$' ````\n<!--\n## Tracking\n- Fixes bug-x7k2m9 — https://agenthub.fritzlab.net/bug-x7k2m9\n\n## Attribution\n- Authored-By: Codex (GPT-5) <noreply@openai.com>\n-->' \
"$BASE" "$GOOD_HEAD" "PR body must contain a non-empty ## Tracking section"
check_contract "unequal backtick runs do not expose comments" \
$'`<!--``\n## Tracking\n- Fixes bug-x7k2m9 — https://agenthub.fritzlab.net/bug-x7k2m9\n\n## Attribution\n- Authored-By: Codex (GPT-5) <noreply@openai.com>\n-->' \
"$BASE" "$GOOD_HEAD" "PR body must contain a non-empty ## Tracking section"
check_contract_pass "inline code containing comment opener stays visible" \ check_contract_pass "inline code containing comment opener stays visible" \
$'Use `<!--` when documenting an HTML comment opener.\n\n## Tracking\n- Fixes bug-x7k2m9 — https://agenthub.fritzlab.net/bug-x7k2m9\n\n## Attribution\n- Authored-By: Codex (GPT-5) <noreply@openai.com>' \ $'Use `<!--` when documenting an HTML comment opener.\n\n## Tracking\n- Fixes bug-x7k2m9 — https://agenthub.fritzlab.net/bug-x7k2m9\n\n## Attribution\n- Authored-By: Codex (GPT-5) <noreply@openai.com>' \
"$BASE" "$GOOD_HEAD" "$BASE" "$GOOD_HEAD"
@@ -136,6 +160,8 @@ check_contract_pass "fenced code containing comments stays visible" \
git -C "${FIXTURES}" commit --allow-empty -q -m "unwatermarked change" git -C "${FIXTURES}" commit --allow-empty -q -m "unwatermarked change"
BAD_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD) BAD_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
check_contract "agenthub 779 commits reject missing watermark" "$BODY" "$GOOD_HEAD" "$BAD_HEAD" "must end with an Authored-By" check_contract "agenthub 779 commits reject missing watermark" "$BODY" "$GOOD_HEAD" "$BAD_HEAD" "must end with an Authored-By"
check_chore_contract "chore requires PR attribution" "" "$BASE" "$GOOD_HEAD" "## Attribution must contain"
check_chore_contract "chore commits require watermark" "$(canonical_body bug-unused)" "$GOOD_HEAD" "$BAD_HEAD" "must end with an Authored-By"
git -C "${FIXTURES}" commit --allow-empty -q -m $'misplaced trailer\nAuthored-By: Codex (GPT-5) <noreply@openai.com>' git -C "${FIXTURES}" commit --allow-empty -q -m $'misplaced trailer\nAuthored-By: Codex (GPT-5) <noreply@openai.com>'
MISPLACED_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD) MISPLACED_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)