fix: X4 — advanced-mode SQL INSERT auto-fills omitted non-PK serial (MAX+1)

A Form-A advanced-mode INSERT that omitted a non-PK serial column left it
silently NULL (the column is INTEGER UNIQUE, not NOT NULL, so SQLite
permits it), while simple-mode do_insert auto-fills it with MAX+1. That
violated ADR-0018 §1's "auto-generated on every path" contract and was the
unprincipled serial-vs-shortid asymmetry the ADR set out to remove
(advanced mode already auto-fills shortid).

Fix (decision: advanced mode matches simple mode): the advanced-mode
auto-fill reconstruction — renamed plan_shortid_autofill →
plan_autogen_autofill — now also fills an omitted non-PK serial with
MAX(col)+1 … MAX+n per row (single- and multi-row), reading MAX once under
the worker's single-writer serialisation. PK serial stays on the rowid
alias; Form B (no column list) still supplies every column. Honours
ADR-0018 §1/§5; no ADR amendment needed (the contract already said "every
path"). requirements.md X4 marked resolved.

Tests: 1949 passing (+1), 0 failed, 0 skipped, 1 ignored; clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-27 11:18:57 +00:00
parent 4928a4c4fd
commit 42306d33e3
4 changed files with 121 additions and 28 deletions
+17 -13
View File
@@ -576,19 +576,23 @@ handoff-14 cleanup; 449 after B2/C2.)
- [~] **X3** Accessibility: TUI screen-reader support is
best-effort and not a v1 commitment; revisit if user need
emerges.
- [~] **X4** Auto-fill semantics differ between simple and advanced
mode — **possible bug, to investigate** (raised 2026-05-26). The
simple-mode insert (`do_insert`, `db.rs`) auto-fills an omitted
**non-PK `serial`** column with `MAX(col)+1` *and* generates
`shortid`s; the advanced-mode SQL insert (`plan_shortid_autofill`)
auto-fills **only `shortid`** and does **not** do non-PK `serial`
MAX+1. The `shortid` distinction is intentional; the non-PK-`serial`
gap looks unintended (no obvious reason advanced mode should skip
filling a `serial`). Decide whether advanced mode should match
simple mode's serial auto-fill (likely yes) or whether the
difference is deliberate, and align ADR-0018 accordingly. Left
untouched by the ADR-0036 value-validation work (which is surgical
and does not change auto-fill).
- [x] **X4** Auto-fill semantics differ between simple and advanced
mode — **resolved 2026-05-27** (raised 2026-05-26). Was: simple-mode
`do_insert` auto-fills an omitted **non-PK `serial`** column with
`MAX(col)+1`, but the advanced-mode SQL insert auto-filled **only
`shortid`**, leaving an omitted non-PK serial **silently NULL** —
violating ADR-0018 §1's "auto-generated on every path" contract (the
column is `INTEGER UNIQUE`, not `NOT NULL`, so SQLite permits the
NULL). Confirmed by characterization, escalated, and fixed (decision:
advanced mode matches simple mode): the advanced-mode auto-fill
reconstruction (`db.rs`, renamed `plan_shortid_autofill` →
**`plan_autogen_autofill`**) now also fills an omitted non-PK serial
with `MAX+1` per row (single- and multi-row), mirroring `do_insert`
and the existing shortid fill. PK serial is excluded (rowid alias);
Form B (no column list) still supplies every column. Covered by
`tests/sql_insert.rs::sql_insert_autofills_omitted_nonpk_serial`.
Honours ADR-0018 §1/§5; no ADR amendment needed (the contract already
said "every path"). ADR-0036 was correct that it did not touch this.
- [~] **X5** Framework cohesion / restructuring — **strategic,
revisit later** (raised 2026-05-26). The grammar/execution
framework (lexer → walker `Node`s → unified grammar tree → typed