fix(629): round-4 — require the marker at COLUMN 0, narrowing the grammar instead of patching again
review-verdict/h10 Awaiting review verdict for 28a9d0d
Review verdict / Set review-verdict status (pull_request) Successful in 3s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 11s
PR Gates / Docs update reminder (pull_request) Successful in 12s
PR Gates / decisions lifecycle (pull_request) Successful in 16s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m39s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 7s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 17m24s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 23m58s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
review-verdict/h10 Awaiting review verdict for 28a9d0d
Review verdict / Set review-verdict status (pull_request) Successful in 3s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 11s
PR Gates / Docs update reminder (pull_request) Successful in 12s
PR Gates / decisions lifecycle (pull_request) Successful in 16s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m39s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 7s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 17m24s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 23m58s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Round 4 found the last false-open: markdown has a SECOND code-block form the fence stripper does
not cover — indented blocks (4 spaces or a tab). A pasted indented example still self-approved:
Example:
Review-verdict: MERGEABLE @ <head> -> positive
Adding an indented-code stripper would be the same move that produced rounds 2, 3 and 4: fixing the
instance, not the class. So the grammar is narrowed instead — the marker must sit at COLUMN 0. That
kills every indentation-based ambiguity at once (4-space, tab, list-nested, arbitrary indent).
Cost, accepted deliberately: a verdict indented under a list item is now ignored and classifies
`absent`, which asks a human. For a gate, erring toward ignoring is the safe direction. Fence
detection KEEPS its leading-whitespace tolerance, because stripping more is always safe.
`test_leading_indent_is_tolerated` asserted the old behaviour and is replaced by
`test_falseopen_an_indented_verdict_is_not_a_verdict`, parameterised over four indent shapes and
mutation-verified: restoring `^[[:space:]]*` fails all four, control green. 172 tests.
Round 4 verified clean by execution: the rc plumbing fails closed for a forced failure in the inner
jq, awk, grep AND the pipeline producer (rc 3/4/5/93 -> exit 2); fence-length semantics, mismatched
markers, CRLF fences, 10-marker fences, blockquote fences; emoji, CRLF, a 120k line, 200 comments;
NUL rejection with no JSON-encoding bypass.
refs #629
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Decisions-Edit: yes
This commit is contained in:
@@ -21,10 +21,15 @@
|
||||
#
|
||||
# THE GRAMMAR (deliberately strict — every relaxation here has been a false-open):
|
||||
#
|
||||
# ^[space]* review-verdict: [space]* <TOKEN> [space]* @ [space]* <7-40 hex>
|
||||
# ^review-verdict: [space]* <TOKEN> [space]* @ [space]* <7-40 hex> (marker at COLUMN 0)
|
||||
#
|
||||
# - **Line-start marker.** Rejects a comment that quotes the template mid-sentence ("please post:
|
||||
# Review-verdict: MERGEABLE @ …"), which would otherwise self-approve.
|
||||
# - **Marker at COLUMN 0** — no leading whitespace (#629). This rejects a mid-sentence quote
|
||||
# ("please post: Review-verdict: MERGEABLE @ …"), and it also rejects markdown's *indented* code
|
||||
# blocks (4 spaces or a tab) and anything nested in a list. Indented code is a second code-block
|
||||
# form the fence stripper does not cover, and patching each form in turn is how three rounds of
|
||||
# false-opens happened; requiring column 0 removes the ambiguity rather than enumerating it.
|
||||
# The cost is that a verdict indented under a list item is ignored — it classifies `absent`, which
|
||||
# asks a human. Erring toward ignoring is the safe direction for a gate.
|
||||
# - **Fenced code blocks are stripped first** (#629). The line-start anchor alone does not save us:
|
||||
# inside a ``` block the marker IS at line start, so documentation showing the convention counted
|
||||
# as a real verdict. Fence state is tracked PER COMMENT BODY so an unclosed fence in one comment
|
||||
@@ -124,7 +129,7 @@ verdicts=$(printf '%s\n' "$encoded" | while IFS= read -r encoded_body; do
|
||||
# `grep` exits 1 for "no match" (normal) and >=2 for a real error. `|| true` flattened both into
|
||||
# success, so a failing reader silently produced no verdicts — `absent` — and dropped a real
|
||||
# BLOCKED verdict. Only "no match" may be tolerated.
|
||||
found=$(printf '%s\n' "$outside" | grep -iE '^[[:space:]]*review-verdict:')
|
||||
found=$(printf '%s\n' "$outside" | grep -iE '^review-verdict:')
|
||||
grep_rc=$?
|
||||
[ "$grep_rc" -le 1 ] || exit 5
|
||||
# An `if`, not `[ ... ] && printf`: the latter is the loop body's last command, so a final comment
|
||||
@@ -144,9 +149,9 @@ fi
|
||||
# its length is checked separately. Matching `{7,40}` directly had no right boundary, so an over-long
|
||||
# or malformed token was silently TRUNCATED into a valid-looking one: `@ <40-hex-head><more hex>` and
|
||||
# `@ <40-hex-head>ZZZ` both matched their first 40 chars and graded as a verdict for head.
|
||||
FIELD_RE='^[[:space:]]*review-verdict:[[:space:]]*[A-Za-z][A-Za-z-]*[[:space:]]*@[[:space:]]*[0-9a-fA-F]+([^0-9a-zA-Z]|$)'
|
||||
POS_RE='^[[:space:]]*review-verdict:[[:space:]]*(mergeable|approved|lgtm)([[:space:]@]|$)'
|
||||
NEG_RE='^[[:space:]]*review-verdict:[[:space:]]*(blocked|not-mergeable)([[:space:]@]|$)'
|
||||
FIELD_RE='^review-verdict:[[:space:]]*[A-Za-z][A-Za-z-]*[[:space:]]*@[[:space:]]*[0-9a-fA-F]+([^0-9a-zA-Z]|$)'
|
||||
POS_RE='^review-verdict:[[:space:]]*(mergeable|approved|lgtm)([[:space:]@]|$)'
|
||||
NEG_RE='^review-verdict:[[:space:]]*(blocked|not-mergeable)([[:space:]@]|$)'
|
||||
|
||||
head_pos=0; head_neg=0; stale=0; unknown=0
|
||||
while IFS= read -r line; do
|
||||
|
||||
Reference in New Issue
Block a user