fix(cli): respect atomic END boundaries (CLI-2405) - #6587
Conversation
There was a problem hiding this comment.
Superseded by a newer AI review
🤖 AI Review
Five of Claude's seven findings are confirmed; two are refuted. The most significant issue is a pre-existing but real BEGIN ATOMIC splitting bug for top-level CASE expressions. The digit-before-dollar guard also diverges from PostgreSQL tokenization, while three confirmed nits concern maintainability or documentation. Codex reported no findings.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟠 MAJOR | apps/cli/src/command-internal/sql-split.ts:142 |
correctness |
claude | A top-level CASE expression inside BEGIN ATOMIC is mistaken for the function body's closing END, causing valid SQL to be split prematurely. |
| 🟡 MINOR | apps/cli/src/command-internal/sql-split.ts:42 |
correctness |
claude | The new dollar-quote guard mistakes a preceding numeric literal's digit for proof that an identifier is open, unlike PostgreSQL's lexer. |
| ⚪ NIT | apps/cli/src/command-internal/sql-split.ts:142 |
maintainability |
claude | The pendingEnd identifier-continuation branch bypasses the nested state, coupling correctness to ReadyState's current identifier-rune behavior. |
| ⚪ NIT | apps/cli/src/command-internal/sql-split.ts:18 |
documentation |
claude | The non-obvious PostgreSQL-specific reason for treating every code point at or above 0x80 as an identifier character is undocumented. |
| ⚪ NIT | apps/cli/src/command-internal/sql-split.ts:23 |
maintainability |
claude | hasIdentifierRuneBefore leaves its two-code-unit window unexplained and handles negative offsets inconsistently with its Go counterpart. |
Refuted findings (kept for transparency, not posted as review comments)
apps/cli/src/command-internal/sql-split.unit.test.ts:68(test-coverage): The new identifier-before-$ behavior lacks meaningful observable regression coverage outside BEGIN ATOMIC tests.
Refuted: The existing cases exercise the guard through observable splitter output and fail without it. A standalone example would cover the same path using malformed SQL, contrary to the trusted convention against redundant assertions.apps/cli-go/pkg/parser/state.go:218(maintainability): AtomicState's delimiter comparisons should be replaced by a separate boolean or enum because its delimiter has only two possible values.
Refuted: The delimiter is not merely a discriminator: it is the value endsWithDelimiter must match. Adding a separate boolean would duplicate state and create a possible inconsistency without correcting any demonstrated behavior; the small string conversions are not a substantive defect.
Stats
Claude findings: 7 · Codex findings: 0 · Confirmed: 5 · Refuted: 2 · Uncertain: 0
Models: claude-opus-5 + gpt-5.6-sol · Trigger: auto · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
|
/ai-review |
There was a problem hiding this comment.
🤖 AI Review
Both independent reviews completed: Claude reported six findings and Codex reported none. All six concerns are confirmed, though the comment-density issue predates this PR and the overlapping-comment bug requires malformed SQL. The most substantive issue is a regression that can leave a nested AtomicState stale and swallow subsequent statements.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟡 MINOR | apps/cli-go/pkg/parser/state.go:271 |
correctness |
claude | A nested END-delimited AtomicState that returns nil on its confirming semicolon remains stored in the enclosing AtomicState, preventing the enclosing parenthesized state from closing. |
| 🟡 MINOR | apps/cli-go/pkg/parser/state.go:66 |
parity |
claude | isBeginAtomic uses broader, differing Unicode whitespace classes in Go and TypeScript instead of the PostgreSQL lexer whitespace class introduced by this change. |
| 🟡 MINOR | apps/cli/src/command-internal/sql-split.ts:68 |
correctness |
claude | isCommentsAndWhitespace and BlockState handle overlapping block-comment delimiters differently, allowing END to close a body after the FSM has treated text as statement content but the helper has consumed it as an unterminated comment. |
| ⚪ NIT | apps/cli-go/pkg/parser/state.go:51 |
dead-code |
claude | Parenthesis-delimited AtomicState instances initialize and update END-specific bookkeeping that cannot affect their behavior. |
| ⚪ NIT | apps/cli/src/command-internal/sql-split.ts:220 |
comments |
claude | Several newly added inline comment blocks exceed the trusted repository convention of one or two lines, while the file remains above the documented comment-density limit. |
| ⚪ NIT | apps/cli/src/command-internal/sql-split.unit.test.ts:109 |
test-coverage |
claude | The TypeScript tests cover pending END only when the confirming character is a semicolon, leaving the non-semicolon and EOF paths untested. |
Stats
Claude findings: 6 · Codex findings: 0 · Confirmed: 6 · Refuted: 0 · Uncertain: 0
Models: claude-opus-5 + gpt-5.6-sol · Trigger: manual · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
TL;DR
Prevents valid
BEGIN ATOMICfunctions from being truncated when an identifier containsendProb/sol
The splitter treated
endinside names such aspendingas the closingEND,causing migrations to fail with
SQLSTATE 42601now fixed by:
Recognizing
ENDonly at PostgreSQL identifier boundaries in both SQL splittersref:
end— the closer lacks the boundary check the opener already has #6562