Skip to content

fix(gate-check): a blank line inside the Decisions table no longer ends it (#676) - #693

Merged
MendixMau merged 4 commits into
masterfrom
fix/decisions-blank-line-676
Nov 6, 2026
Merged

MendixMau merged 4 commits into
masterfrom
fix/decisions-blank-line-676

Conversation

@MendixMau

Copy link
Copy Markdown
Owner

Fixes #676

What changed

  • bin/gate-check.sh: the five Decisions-table readers all ended the table at any non-pipe line, blank lines included: has_confirmed_decision, has_auto_assumed_decision, auto_hand_pending, stage_exit_recorded, plus a fifth copy in check_stage_7 that the issue did not list. A CONFIRMED row after a blank line was therefore ignored. Stage 3/4 FAILed with "no Stage-N CONFIRMED decision", and Stage 7 read PENDING.
  • The five readers now share one awk prefix, DECISIONS_TABLE_AWK, and read the register in two passes ("$f" "$f"). The first pass records which lines are separator rows, so the second can look one line ahead.
    • Blank lines are skipped.
    • A heading or prose line still ends the table.
    • So does a new header row (one followed by a separator) whose first cell is not Stage. An Open-questions table after a blank line is therefore still never read as Decisions.
    • auto_hand_pending now uses FNR for its line ids, because NR counts both passes.
  • New fixture tests/wave2/test-decisions-blank-line.sh:
    • D1: a blank line, then a Stage-4 CONFIRMED row. Expected: Stage 4 PASS.
    • D2: the same for Stage 7.
    • D3: a blank line, then an Open-questions table with | 4 |…CONFIRMED| and | 7 |…CONFIRMED|. Expected: Stages 4 and 7 do NOT pass.
    • D4: an auto-mode ✋ row after a blank line. Expected: it is listed at PROJECT.md:6.

Evidence

  • Positive control: against origin/master ee12a61, D1, D2 and D4 FAIL and D3 passes. D3 is the guard: against a blanks-only variant of the fix, D3's two assertions FAIL, because the Open-questions rows then pass Stages 4 and 7.
  • Fixtures on this branch:
    • test-decisions-blank-line: 5 ok
    • test-bug02-register: 8 ok
    • test-bug03-gates: 99 ok
    • test-auto-mode-hand-gates: 28 passed
    • test-wrong-verdicts: 25 passed
    • test-pipefail-grep-q (bin/lib/skill-routing.sh): 3 passed
    • check-scripts: 129/129 parse
  • Leak guard is clean, and check-pr-discipline is clean.
  • I reproduced the bug the same way the issue reports it, on a scratch register: Stage 4 FAIL / Stage 7 PENDING → PASS / PASS. I did not run it on a real project.

Time cost (#687)

  • No new check, gate or obligation. This only changes how existing table reads parse.
  • Runs once per gate-check invocation (each reader reads PROJECT.md twice instead of once). Measured on the same scratch project: gate-check.sh p1 4 averaged 921 ms before and 844 ms after, so any difference is below the noise.
  • It cannot add a red verdict for a correctly written register. It removes a false FAIL, and with it the gate fight of a CONFIRMED row in plain sight.

Regression risk after sync

  • Verdicts that flip: a register whose Decisions table is split by blank lines now has the later rows counted. Some Stage 3/4 FAIL lines become PASS, a Stage 7 PENDING can become PASS, and stage_exit_recorded can now raise the "approved over a failing gate" flag for such rows. The auto-mode ✋ list (and with it the Stage 6 FAIL) can list rows it used to miss.
  • No new FAIL for content that was right, apart from the auto-mode ✋ rows above, which were already supposed to be listed.
  • Unchanged:
    • A register with no | Stage | header falls back to scanning every table, as before.
    • Header rows without "Stage" are skipped, and they never hold CONFIRMED.
    • Exit codes and the output format are the same. Line ids from auto_hand_pending are unchanged.
  • Portability: POSIX awk only (FNR == NR, array lookahead), no gawk-isms. It does not depend on the project layout: the register comes from the existing REGISTER resolver.

Review: pending independent review


Generated by Claude Code

…ds it (#676)

The five Decisions-table readers ended the table at any non-pipe line, so a
CONFIRMED row after a blank line was ignored and Stage 3/4 FAILed (Stage 7
read PENDING). They now share DECISIONS_TABLE_AWK, a two-pass prefix that
skips blank lines and still ends the table at a heading, prose, or a new
header row whose first cell is not "Stage" (an Open-questions table).

Fixture: tests/wave2/test-decisions-blank-line.sh — D1, D2, D4 FAIL on
origin/master ee12a61; D3 FAILs on a blanks-only fix.

Fixes #676

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
…review)

On a CRLF register the separator row did not match, so after a blank line
the Open-questions header was not recognised and its rows read as Decisions
(a false PASS). Fixture case D3c: the CRLF variant of D3; it FAILs (false
PASS on Stages 4 and 7) without the \r.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
…ne (bash 3.2)

check-portability flagged 5 heredocs opened inside command substitution.
mkproj now sets P itself and is called as `mkproj <name> <<'EOF'`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
Keep both sides of CHANGELOG.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
@MendixMau
MendixMau merged commit 2c47a87 into master Nov 6, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gate-check stops reading the Decisions table at a blank line, later CONFIRMED rows are ignored

2 participants