feat: bring simple-mode insert arity diagnostics to parity with advanced

A wrong-count simple-mode insert now shows the friendly per-column arity
message at typing time (instead of a bare "expected `,`/`)`") and is
blocked from dispatch at submit — unifying simple and advanced mode onto
the one ADR-0027 model (structural parse + ERROR diagnostic), where they
had diverged.

Grammar: a simple-mode-only arity gate (dsl_insert_value_list) routes a
wrong-count DSL insert tuple to the type-blind fallback so it matches
structurally and the per-tuple arity diagnostic fires. The gate is gated
to simple mode, so advanced behaviour is unchanged. count_tuple_values
and the target-column selection (insert_target_columns) are now shared
by both grammars.

Diagnostic: dml_insert_arity_diagnostics is mode-aware — advanced Form B
expects all columns; simple Form B/C expects the user-fillable columns
(serial/shortid auto-fill). It counts the DSL Form A role and scans the
keyword-less Form C tuple. New catalog keys name the fillable/auto split
and the all-auto-table case.

Submit: a wrong-count DSL insert now parses Ok + carries the ERROR
diagnostic, so a unified Ok-arm pre-flight (dsl_insert_count_mismatch_notes)
blocks dispatch and teaches; the previous Err-arm note retires.
advanced_alternative_note's gate now reads the validity verdict so it
still fires for the parse-Ok-with-error shape.

Docs: ADR-0036 Amendment 2 (+ README index) and requirements.md H1a.
This commit is contained in:
claude@clouddev1
2026-05-29 20:45:21 +00:00
parent 7cccf4eabb
commit 10e5197c19
16 changed files with 812 additions and 240 deletions
@@ -1,37 +1,20 @@
---
source: tests/typing_surface/insert_form_b.rs
assertion_line: 186
assertion_line: 193
description: "input=\"insert into Customers values (1, 'Alice', 'a@b.c')\" cursor=50"
expression: "& a"
---
Assessment {
input: "insert into Customers values (1, 'Alice', 'a@b.c')",
cursor: 50,
state: DefiniteErrorAt(
30,
),
state: Valid,
hint: Some(
Prose(
"for `Name`: Type a quoted string (e.g. 'Alice') or null (`id` auto-generated — skipped here; list columns explicitly, e.g. `insert into T (...) values (...)`, to set it.) (trying to write SQL? switch with `mode advanced`, or prefix `:` to run once)",
"without a column list, supply 2 value(s) for `Name`, `Email` — `id` auto-generated; 3 given (trying to write SQL? switch with `mode advanced`, or prefix `:` to run once)",
),
),
completion: Some(
Completion {
replaced_range: (
50,
50,
),
partial_prefix: "",
candidates: [
Candidate {
text: "null",
kind: Keyword,
mode: Both,
},
],
},
),
parse_result: Err(
"Invalid(definite)",
completion: None,
parse_result: Ok(
"Insert",
),
}
@@ -1,22 +1,20 @@
---
source: tests/typing_surface/insert_form_b.rs
assertion_line: 168
assertion_line: 173
description: "input=\"insert into Customers values ('Alice')\" cursor=38"
expression: "& a"
---
Assessment {
input: "insert into Customers values ('Alice')",
cursor: 38,
state: DefiniteErrorAt(
37,
),
state: Valid,
hint: Some(
Prose(
"after `insert into Customers values ('Alice'`, expected `,` — usage: insert into <Table> [(<col>[, ...])] [values] (<value>[, ...])",
"without a column list, supply 2 value(s) for `Name`, `Email` — `id` auto-generated; 1 given",
),
),
completion: None,
parse_result: Err(
"Invalid(definite)",
parse_result: Ok(
"Insert",
),
}
@@ -1,22 +1,35 @@
---
source: tests/typing_surface/insert_form_c.rs
assertion_line: 101
assertion_line: 104
description: "input=\"insert into Customers ('Alice', 'a@b.c', 'extra')\" cursor=49"
expression: "& a"
---
Assessment {
input: "insert into Customers ('Alice', 'a@b.c', 'extra')",
cursor: 49,
state: DefiniteErrorAt(
39,
),
state: Valid,
hint: Some(
Prose(
"after `insert into Customers ('Alice', 'a@b.c'`, expected `)` — usage: insert into <Table> [(<col>[, ...])] [values] (<value>[, ...])",
"without a column list, supply 2 value(s) for `Name`, `Email` — `id` auto-generated; 3 given",
),
),
completion: None,
parse_result: Err(
"Invalid(definite)",
completion: Some(
Completion {
replaced_range: (
49,
49,
),
partial_prefix: "",
candidates: [
Candidate {
text: "values",
kind: Keyword,
mode: Both,
},
],
},
),
parse_result: Ok(
"Insert",
),
}