fix(process): #317 docs-only exemption stays passthrough, not silent auto-grant [decisions-edit]
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m22s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m30s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m33s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m36s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m33s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m22s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m30s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m33s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m36s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m33s
Adversarial-review nit on the first commit: auto-granting the docs-only exemption silently self-merges process-control PRs (.claude/.gitea/.husky — including the gate hook itself) with no prompt and no review, bypassing human-in-the-loop for exactly the files that control the gate. Restrict auto-grant to the genuinely- satisfied (a+b+c) merge path; the docs/process exemption reverts to bare exit-0 passthrough (one normal prompt). Corrects this PR's own decisions.md entry accordingly ([decisions-edit]: a not-yet-merged draft entry, not history). 8 pipe tests green (adds docs-only -> passthrough). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #318.
This commit is contained in:
@@ -79,7 +79,13 @@ body=$(printf '%s' "$prjson" | jq -r '.body // ""' 2>/dev/null || true)
|
||||
# --- Docs-only exemption: if every changed file is docs/process, skip the gate. ---
|
||||
files=$(gq "repos/$owner/$repo/pulls/$pr/files?limit=100" | jq -r '.[].filename // empty' 2>/dev/null || true)
|
||||
if [ -n "$files" ] && ! printf '%s\n' "$files" | grep -qvE '^(docs/|\.claude/|\.husky/|\.gitea/|.*\.md$)'; then
|
||||
decide grant "H6/H10 merge gate: PR #$pr changes only docs/process files — gate not applicable, auto-granted."
|
||||
# Docs/process-only PR: the Done-when + review-verdict gate doesn't apply — but this exemption is a
|
||||
# file-TYPE bypass, NOT the a+b+c "provably reviewed & ready" proof, so it does NOT auto-grant. It
|
||||
# passes through to normal permissioning (one prompt). This deliberately keeps a human in the loop for
|
||||
# process-control files (.claude/ / .gitea/ / .husky/ — the gate, CI, and git hooks themselves): a PR
|
||||
# that weakens the gate must not silently self-merge (ersatztv#317 review nit). Only the satisfied
|
||||
# merge path below auto-grants.
|
||||
decide allow "" # passthrough (exit 0 → normal prompt), NOT grant
|
||||
fi
|
||||
|
||||
# --- Linked issue: Gitea auto-close keywords in the PR body. ---
|
||||
|
||||
+9
-5
@@ -1537,14 +1537,18 @@ still fires (the merge tool isn't allow-listed). So the gate only ever *added* a
|
||||
confirmed twice — once conversationally (the per-session merge-consent norm) and again by a redundant
|
||||
mechanical prompt the gate was supposed to have subsumed.
|
||||
|
||||
**Fix:** the satisfied and docs-exempt paths now emit
|
||||
**Fix:** ONLY the genuinely-satisfied merge path (a+b+c all true) now emits
|
||||
`{"hookSpecificOutput":{"permissionDecision":"allow", ...}}` (a new `grant` decision), which actually
|
||||
suppresses the prompt. Deny (unticked/red/negative/stale) and ask (non-derivable: no creds, Gitea
|
||||
down, no linked issue, no `## Done-when`, no verdict) are unchanged — the gate still fails closed, not
|
||||
open. Non-merge `pull_request_write` methods keep the bare `exit 0` **passthrough** (normal
|
||||
permissioning still applies) — auto-grant is scoped to the merge decision only, not broadened to
|
||||
PR-create/edit. Verified by 7 pipe tests (satisfied→allow, unticked→deny, stale→deny, red-CI→deny,
|
||||
no-verdict→ask, no-creds→ask, non-merge→passthrough).
|
||||
open. Two paths deliberately do **not** auto-grant and keep the bare `exit 0` **passthrough** (normal
|
||||
permissioning → one prompt): non-merge `pull_request_write` methods (auto-grant is scoped to
|
||||
method=merge only), and the **docs/process-only exemption**. The exemption is a file-TYPE bypass, not
|
||||
the a+b+c "provably reviewed & ready" proof, so it must not *silently* self-merge — critically, its set
|
||||
includes `.claude/`/`.gitea/`/`.husky/` (the gate, CI workflows, and git hooks themselves), so a PR
|
||||
that weakens the gate still gets a human prompt (ersatztv#317 review nit). Verified by 8 pipe tests
|
||||
(satisfied→allow, docs-only→passthrough, unticked→deny, stale→deny, red-CI→deny, no-verdict→ask,
|
||||
no-creds→ask, non-merge→passthrough).
|
||||
|
||||
**Process consequence:** the state-derived gate *is* the consent on the satisfied path — do **not**
|
||||
also ask conversationally to merge a PR whose gate auto-grants. A separate human confirmation is still
|
||||
|
||||
Reference in New Issue
Block a user