From f88018b4be8c5dca09040775b26ed5186bf4f4a5 Mon Sep 17 00:00:00 2001 From: "claude@clouddev1" Date: Wed, 10 Jun 2026 14:28:50 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20session=20handoff=2062=20=E2=80=94=20C4?= =?UTF-8?q?=20m:n=20convenience=20command=20+=20issue=20#19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/handoff/20260610-handoff-62.md | 185 ++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 docs/handoff/20260610-handoff-62.md diff --git a/docs/handoff/20260610-handoff-62.md b/docs/handoff/20260610-handoff-62.md new file mode 100644 index 0000000..a73f864 --- /dev/null +++ b/docs/handoff/20260610-handoff-62.md @@ -0,0 +1,185 @@ +# Session handoff — 2026-06-10 (62) + +Sixty-second handover. Continues from handoff-61 (X1 logging full sweep ++ T3 residuals). This session was a **list-trimming + one-feature run**: +it closed **C4** (the `create m:n relationship` convenience command, +**ADR-0045**) and, in passing, resolved **Gitea issue #19** (drop-PK +guard). Handoff-61 itself was written mid-session, so the X1 / T3 work +it describes is also part of this session's commit range. + +## §1. State at handoff + +**Branch:** `main`. **HEAD `8bd43cc`.** Push is the user's step. + +**Tests: 2237 passing / 0 failing / 1 ignored** (the 1 ignored is the +long-standing doc-test). **Clippy clean** (nursery, all targets). +30 +over the handoff-60 baseline of 2207. + +**This session's commits** (8, on top of session-60's 5): +``` +8bd43cc feat: create m:n relationship convenience command (C4, ADR-0045) +e598008 docs: ADR-0045 m:n convenience command (C4); accepted +e44d298 test+docs: lock drop-PK-refused on advanced surface; document no-PK advanced mode (#19) +b803468 docs: session handoff 61 — X1 logging full sweep + T3 residuals closed +5a33f2a fix(fk): compound-FK violation message names every column pair +6985a43 fix(fk): inline FK referencing a compound PK points at the table-level form +0a7612e feat: comprehensive logging across parser, app, persistence, runtime (X1) +a8ad0c6 feat(db): comprehensive logging across worker + executors (X1) +``` + +**Requirements closed this session:** **X1** `[x]` (logging), **T3** +residuals (both ADR-0043 messaging items), **C4** `[x]` (m:n). Gitea +**#19 closed**. + +## §2. X1 — comprehensive logging (closed) — see handoff-61 §2 + +Full detail in handoff-61. In brief: ~75 → **137** `tracing` sites under +a documented level discipline (read the **`src/logging.rs` module doc** +before adding logs). Logs go to a **file** (`--log-file` > +`RDBMS_PLAYGROUND_LOG_FILE` > `~/.rdbms-playground/playground.log`); +level via the separate `RDBMS_PLAYGROUND_LOG` env (default `info`). +`debug` = per-command detail (off by default), `trace` = hot paths +(per-keystroke parse). + +## §3. T3 residuals (both closed) — see handoff-61 §3 + +`6985a43` inline-FK arity wording (points at the table-level form; +added `inline: bool` to `SqlForeignKey`). `5a33f2a` compound-FK +violation names every column pair (comma-joined in the single-column +facts slots; `enrich_fk_violation`). ADR-0043 now has no residuals. + +## §4. Issue #19 — drop-PK guard (closed, `e44d298`) + +A parallel check the user requested. **Finding: dropping a PK column is +already refused in both modes** via the shared `do_drop_column` guard +(*"cannot drop primary-key column …"*) — simple `drop column` and +advanced `ALTER … DROP COLUMN` both route through it. Added end-to-end +coverage (`tests/it/sql_alter_table.rs`: single + compound PK, refusal +for the right reason). **Corrected a long-standing misconception:** the +issue's premise ("we don't support creating a table with no PK") is true +only in **simple** mode — advanced SQL `create table t (a int)` makes a +real **PK-less** table (SQLite's implicit `rowid` keys it; only +`WITHOUT ROWID` lacks one, which this app never creates). The simple-mode +`with pk` requirement is **pedagogical** (ADR-0029), not an engine +constraint. Documented in `docs/simple-mode-limitations.md`. + +## §5. C4 — `create m:n relationship` (the feature, ADR-0045) + +`create m:n relationship from to [as ]` generates a +**junction table**: one FK column per parent PK column +(`{table}_{pkcol}`, typed via `fk_target_type` — ADR-0011), a **compound +PK** over all of them, and **two `CASCADE` 1:n relationships** — all in +**one `do_create_table` call = one undo step** (no batch needed; +`do_create_table` already takes `foreign_keys` + writes per-FK +relationship metadata). Auto-named `{T1}_{T2}` (optional `as`), available +in **both modes**, compound-parent PKs supported (ADR-0043). + +**Forks (all user-confirmed):** compound-over-FKs PK (vs surrogate / +none); `CASCADE` actions; auto-name + optional `as`; both modes; FK +columns `{table}_{pkcol}`. **Refused:** self-referential m:n (`from T to +T` — full stop, OOS); PK-less parent; internal `__rdbms_*` junction +name; name collision. + +**Where the code lives:** +- Grammar: a **separate `CREATE_M2N` `CommandNode`** in + `dsl/grammar/ddl.rs` (entry `create`, opener `Node::Literal("m")` — + not a keyword, so it never shadows an identifier), registered Simple + in `grammar/mod.rs` `REGISTRY`. `build_create_m2n` → + `Command::CreateM2nRelationship { t1, t2, name }`. +- Worker: `Request::CreateM2nRelationship`, + `Database::create_m2n_relationship`, executor + `do_create_m2n_relationship` (reads each PK, guards self-ref / + PK-less, builds columns + compound PK + 2 `SqlForeignKey`s, calls + `do_create_table`). +- Runtime: `execute_command_typed` arm. Echo: + `echo::render_create_m2n` (advanced-mode DSL→SQL teaching echo, ADR- + 0038 — the generated `CREATE TABLE … FOREIGN KEY …`, round-trips as + valid SQL), wired in `build_schema_echo`. +- Surfaces: completion `("m","m:n")` composite; `help.ddl.create_m2n` + + `parse.usage.create_m2n` catalog (+ `keys.rs` declarations); + highlighting is grammar-driven (automatic). + +**Tests:** 14 integration (`tests/it/m2n.rs`), 7 typing-surface matrix +(`tests/typing_surface/create_m2n.rs` — completion/hint/highlight/parse), +plus echo / highlight / usage-disambiguator / internal-name units. + +## §6. Framework fixes the C4 build + two `/runda` passes surfaced + +C4's "separate node" design rested on an ADR premise that proved **only +half true**: *"the walker already dispatches multiple nodes per entry +word"* held in **advanced** mode but not **simple**. Three latent +simple-mode assumptions ("≤1 DSL form per entry word") were generalized, +**all behaviour-preserving for existing single-form commands**: + +1. **Dispatch** (`walker/mod.rs` `decide`) committed `simple.first()` + unconditionally → now tries simple candidates (so `create table` no + longer shadows `create m:n`). Reduces to the old single-candidate + commit when there is one. +2. **Completion continuation-merge** (`walker/mod.rs`) was gated + `if mode == Advanced` → now runs in simple mode too, **gated on + `simple_count > 1`** so single-form entry words are untouched. +3. **Usage disambiguator** (`grammar/mod.rs` `usage_key_for_input`) + knew the `1:n` opener but not `m:n` → added an explicit branch. + +Plus a **root-cause bug fix** (user-chosen scope): `do_create_table` +now rejects internal `__rdbms_*` names. This closed both the C4 `as +__rdbms_*` hole **and a pre-existing hole** — simple-mode DSL `create +table __rdbms_*` was accepted at parse (the `TABLE_NAME_NEW` slot had no +guard; only the advanced-SQL path rejected internal names). The shared +executor is the single choke point; the SQL path still rejects earlier +at parse. + +**Process note:** the two `/runda` passes were worth it. The first +(pre-build) corrected the inverted "no PK-less tables" assumption and +confirmed the `do_create_table` reuse against code. The second +(pre-commit) closed **five** test-coverage gaps — two of which +(highlighting, persistence round-trip) had been **wrongly claimed +verified** (the typing-surface `Assessment` has no highlight field; +"transitively covered" was a hand-wave) — and found the two bugs above. +Lesson re-confirmed: verify a claimed-tested surface actually has an +assertion; "transitively covered" is a DA red flag. + +## §7. Remaining open landscape + +**Closed since handoff-60:** X1, both T3 residuals, C4, #19. ADR-0043 and +ADR-0045 fully landed. + +**Still open (by readiness, unchanged otherwise):** +1. **TT5 CI** — test infra solid (2237 green); no pipeline. **Gitea + Actions / Woodpecker** (a fresh decision tied to the migration + + ADR-0001's reopened distribution question). **Friction:** the + requirement is Linux/macOS/Windows on stable — self-hosted Gitea can + do Linux easily, but mac/Windows runners need machines that may not + exist; likely needs a Linux-first scope decision. +2. **SD1 `seed`** then **H2 `hint`** — the two unblockers for **A1** + app-commands; both net-new, own ADR (SD2 is the seed-generator design + ADR). SD1 should now seed **m:n junctions** too (valid FK refs from + parent rows) — C4 makes that concrete. +3. **V2/S3 multi-result tabs** or **V4 journal** — larger output-model + redesign, design-first, own ADR. V4 also unlocks diagram live-reflow. +4. **C3a modify relationship** — small follow-up (drop+add covers it + today; ADR pending). + +**ADR-0045 OOS for later:** self-referential m:n (deliberate non-goal); +per-relationship action overrides; extra junction payload columns; +m:n-as-diagram echo. **Pre-existing, now-fixed:** the internal-name hole +(§6) — no separate issue needed, it's closed. + +## §8. How to take over + +1. Read handoffs 60 → 61 → 62, then `CLAUDE.md`, `docs/requirements.md` + (X1/C4 now `[x]`), `docs/adr/README.md`. +2. **Before adding logging:** the level discipline in the + `src/logging.rs` module doc. +3. **For grammar/command work:** an entry word can now carry **multiple + DSL forms** in simple mode (C4 generalized the dispatch + completion + + usage paths). `create` is the first such entry word (table + m:n). +4. **For relationship/FK work:** ADR-0013/0043/0044/0045 are all landed; + `SqlForeignKey` carries `inline`; `do_create_table` now guards + internal names. +5. Codebase on `main` at `8bd43cc`, clean. Commits user-confirmed, + append-only, no AI attribution. Process pins that paid off: **two + `/runda` passes per feature** (design + pre-commit) — both found real + bugs and gaps every time; **verify a claimed-tested surface has an + actual assertion**; **escalate genuine forks** (every C4 design choice + + the internal-name fix scope was the user's).