docs: handoff 71 — hint content needs a semantic verification pass
User smoke-test found hint.cmd.create_table is semantically wrong: the example `create table Customers with pk id(serial), name(text), email(text)` reads as a 3-column table but actually declares a compound PK (id, name, email) — everything after `with pk` is the PK column list (ADR-0005). Root cause: Phase C examples were syntax-checked but some were extrapolated, not verified to *do* what what/concept claims. Handoff specifies a full per-block semantic pass (run each example / check the ADR) + a ready-to-apply create_table fix.
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# Session handoff — 2026-06-15 (71)
|
||||
|
||||
Short, focused handover. Continues immediately from handoff-70 (which
|
||||
shipped H2 / the contextual `hint`, ADR-0053). **A user smoke-test
|
||||
surfaced a correctness bug in the hint content, and it implicates the
|
||||
whole corpus.** This handoff exists so the next session does a
|
||||
**systematic semantic verification pass over every hint block** — context
|
||||
ran too low to do it now.
|
||||
|
||||
## §1. State
|
||||
|
||||
**Branch:** `main`, clean, all committed (local; push pending). **2499
|
||||
pass / 1 ignored, clippy clean.** Open issues: #35–#38 (see handoff-70).
|
||||
H2 / ADR-0053 is *functionally* complete; the **content is not
|
||||
trustworthy** until the pass below is done.
|
||||
|
||||
## §2. The bug (confirmed)
|
||||
|
||||
`hint.cmd.create_table` (in `src/friendly/strings/en-US.yaml`) reads:
|
||||
|
||||
```
|
||||
What: Create a new table — its columns, their types, and a primary key.
|
||||
Example: create table Customers with pk id(serial), name(text), email(text)
|
||||
Concept: A table is a set of rows that share the same columns. The primary
|
||||
key uniquely identifies each row; a `serial` key numbers the rows for you.
|
||||
```
|
||||
|
||||
**This is wrong.** In the DSL, **everything after `with pk` is the
|
||||
primary-key column list** (a possibly *compound* PK, ADR-0005). So the
|
||||
example does **not** create a table with `pk=id` plus regular columns
|
||||
`name`/`email` — it creates a table whose **compound primary key is
|
||||
(id, name, email)**. Non-key columns are added *separately* with
|
||||
`add column`. The `what` ("its columns, their types") and the example
|
||||
both mislead a learner badly.
|
||||
|
||||
- **Evidence:** real test usage is `create table Orders with pk
|
||||
id(serial), CustId(int)` (a 2-column *compound PK*) and the common form
|
||||
`create table X with pk id(int)` (single-column PK only). The usage
|
||||
template `create table <Name> with pk [<col>(<type>)[, ...]]` is itself
|
||||
misleading — the `[, ...]` is the PK list, not regular columns.
|
||||
- **Correct mental model:** `create table <T> with pk <pk-cols…>` then
|
||||
`add column <T>: <name> (<type>)` for each non-key column. Confirm
|
||||
against ADR-0005 (compound PK) and ADR-0009 (DSL syntax) when fixing.
|
||||
|
||||
## §3. Root cause — why this needs a *full* pass
|
||||
|
||||
During Phase C I verified *some* examples against `parse.usage.*`
|
||||
templates and real test greps, but for others I **extrapolated** beyond
|
||||
verified syntax. For `create_table` I saw `... with pk id(int)` (single
|
||||
col) and wrongly generalised to "pk + more columns," misreading the
|
||||
`with pk` list as a column list. The examples are **syntactically**
|
||||
checked but not **semantically** — i.e. not verified to *do what the
|
||||
`what`/`concept` claims*.
|
||||
|
||||
So the corpus needs a pass that, for **every** `hint.cmd.*` and
|
||||
`hint.err.*` block, checks:
|
||||
1. the `example` parses **and runs**, and
|
||||
2. it actually demonstrates what `what`/`concept` says, and
|
||||
3. `what`/`concept` are factually true of the real behaviour.
|
||||
|
||||
**Don't trust grep+extrapolation.** Prefer: run the example in the app
|
||||
(or a Tier-3 test), or check it against the authoritative ADR.
|
||||
|
||||
## §4. The pass — how to do it (next session)
|
||||
|
||||
The corpus lives in `src/friendly/strings/en-US.yaml` under `hint.cmd.*`
|
||||
(per command form) and `hint.err.*` (per runtime error class). The
|
||||
inventory and authoritative syntax sources:
|
||||
|
||||
- **`hint.cmd.<form>`** — for each, cross-check the example against the
|
||||
matching `parse.usage.<form>` template **and** the form's ADR, and run
|
||||
it. Highest-risk (extrapolated, verify first): **DDL** — `create_table`
|
||||
(known wrong), `add_column`, `add_index`, `add_constraint`,
|
||||
`change_column`, `drop_*`, `create_m2n`; **advanced-SQL** — confirm
|
||||
each is in the supported SQL subset (`select`, `with` CTE,
|
||||
`sql_insert/update/delete`, `sql_create_table`, `sql_alter_table`,
|
||||
`sql_create_index/drop_index/drop_table`, `explain_sql`); **DML** —
|
||||
`seed` forms, `explain`, `show_*`, `update`/`delete` (`--all-rows` /
|
||||
required-WHERE wording). App commands are lower-risk (reference-style).
|
||||
- **`hint.err.<class>`** — verify the fix recipe in `example` is actually
|
||||
the right remedy and `concept` matches the engine's real behaviour
|
||||
(FK sides, `on delete` actions, check/not_null/unique semantics).
|
||||
- Relevant ADRs: 0005 (types + compound PK), 0009 (DSL syntax), 0011 (FK
|
||||
type compat), 0013 (relationships/rebuild), 0014 (data ops +
|
||||
required-WHERE), 0025 (indexes), 0028/0039 (explain), 0030–0036 (SQL
|
||||
subset), 0048 (seed). `docs/requirements.md` for scope.
|
||||
|
||||
**Suggested method:** drive the app (`/run` or a small PTY/Tier-3 harness)
|
||||
and actually execute each example; or add a test that parses+runs every
|
||||
`hint.cmd.*` example and asserts success. The latter would also be a
|
||||
durable regression guard — consider adding it as part of the pass (it
|
||||
upgrades the comprehensiveness coverage test from "a block exists" to
|
||||
"the example actually works").
|
||||
|
||||
## §5. Immediate fix ready to apply
|
||||
|
||||
`create_table` is diagnosed (§2). The corrected block should make the
|
||||
example a PK-only `create table` and move the regular columns to a
|
||||
follow-up `add column`, e.g.:
|
||||
|
||||
```
|
||||
What: Create a new table with its primary key.
|
||||
Example: create table Customers with pk id(serial)
|
||||
Concept: A table is a set of rows sharing the same columns. `with pk`
|
||||
declares the primary key (one column, or several for a compound
|
||||
key); add the other columns afterwards with `add column`.
|
||||
```
|
||||
|
||||
Apply this (and re-check `create_m2n` / `add_*` while there), but only as
|
||||
part of the systematic pass — a one-off fix risks leaving siblings wrong.
|
||||
|
||||
## §6. How to take over
|
||||
|
||||
1. Read handoffs 70 → 71, `CLAUDE.md`.
|
||||
2. Confirm green: `cargo test` (2499 / 1 ignored), `cargo clippy
|
||||
--all-targets`.
|
||||
3. Do the §4 pass (consider the run-every-example test in §4). Test-first,
|
||||
`/runda` before commit, confirm the commit message with the user.
|
||||
4. Pedagogy wins — these are teaching strings; correctness and clarity
|
||||
over cleverness.
|
||||
Reference in New Issue
Block a user