docs: ADR-0048 Phase 2 implemented + handoff 66

- ADR-0048: status → Phase 1 + Phase 2 implemented; D2 amendment
  (quoted dates, no date-literal token) and the override × UNIQUE
  capacity-guard decision; phasing/Status blocks marked done.
- README index: 0048 entry updated (Phase 2 shipped, 2400 tests).
- requirements.md: SD2 → [x] (the override-hooks core + column-fill).
- handoff 66: this session's Phase 2 build + the two /runda passes.
This commit is contained in:
claude@clouddev1
2026-06-12 09:44:36 +00:00
parent a12facc784
commit 30b2677bf3
4 changed files with 243 additions and 41 deletions
+79 -26
View File
@@ -2,7 +2,7 @@
## Status
**Accepted (2026-06-11); Phase 1 implemented (2026-06-11).** Design
**Accepted (2026-06-11); Phase 1 + Phase 2 implemented (2026-06-11).** Design
settled with the user across an extended fork dialogue (every decision
below was escalated and user-chosen), then hardened by a pre-build
`/runda` Devil's-Advocate pass that found six blockers — undo
@@ -26,11 +26,39 @@ ADR decisions (D5/D15/D16/D17 + atomicity + zero-count), all closed.
**Implemented in Phase 1:** the whole-row `seed <table> [count]
[--seed <n>]` form and every D1D18 decision *except* the two
deferred-to-Phase-2 surfaces below. **Deferred to Phase 2** (designed
here, not yet built): the **`set` override clause** (D2) and the
**`<table>.<column>` column-fill** form (D1 form 2). Further SD2
increments (custom user generators, NULL injection, multi-locale,
recursive parent auto-seed) remain out of scope (see Out of scope).
Phase-2 surfaces.
**Phase 2 implemented (2026-06-11):** both remaining surfaces — the
**`set` override clause** (D2: fixed value / pick-list / named
generator / range, quoted literals, type-aware) and the
**`<table>.<column>` column-fill** form (D1 form 2: an UPDATE over
existing rows, refusing PK/autogen targets, empty-table no-op, one undo
step). The named-generator vocabulary (D9) lives in `src/seed`
(`KNOWN_GENERATORS` / `generator_for_name`); a new range `Generator`
(`src/seed/generators.rs`) backs `between`; the override clause is
folded from the flat matched path (`build_seed_overrides`,
`src/dsl/grammar/data.rs`) and applied to the per-column plan
(`apply_seed_overrides`, `src/db.rs`), with column-fill in
`do_seed_column_fill`. Full ambient wiring: completion (the generator
vocabulary after `as`, the `set`/`.col` column slots), highlighting
(`HighlightClass::Function``tok_function`, the generator slot), the
validity indicator (`IdentSource::Generators` — an unknown name flagged
`[ERR]`), help, and parse-error pedagogy rows. The D13 advisory now
carries its Phase-2/3 wording (points at `set` and the column-fill
repair). A post-implementation `/runda` pass then added one
user-chosen refinement: a **bounded override on a UNIQUE column** (a
fixed value / too-short pick-list) is now a **friendly error** rather
than a silent uniqueness cap (see D2). **2400 tests pass / 0 fail / 0
skip; clippy clean.** Two
implementation refinements vs. this ADR's wording, both met the
user-facing contract: dates in the range form are **quoted** (the D2
amendment, above — no date-literal token exists); and the `set` value
slots reuse `update`'s typed `current_column_value` (no spurious
column-ref match) rather than the raw expression operand.
Further SD2 increments (custom user generators, NULL injection,
multi-locale, recursive parent auto-seed) remain out of scope (see Out
of scope).
Closes `requirements.md` **SD1** and delivers the core of **SD2**
(per-type generators, determinism, the `fake`-backed catalogue). It
@@ -158,15 +186,37 @@ is nothing new to learn:
| Fixed value | `set status = 'pending'` | every row gets the constant |
| Pick-from-list | `set role in ('admin', 'editor', 'viewer')` | uniform random choice from the list |
| Explicit generator | `set work_addr as email` | force a named generator (D9) |
| Range | `set price between 10 and 100` | uniform in range; **also dates**`set signup between 2023-01-01 and 2024-12-31` |
| Range | `set price between 10 and 100` | uniform in range; **also dates**`set signup between '2023-01-01' and '2024-12-31'` |
Multiple clauses combine: `seed users 20 set role in ('admin',
'user'), status = 'active', signup between 2023-01-01 and 2024-12-31`.
'user'), status = 'active', signup between '2023-01-01' and
'2024-12-31'`.
**Override × UNIQUE capacity (post-implementation `/runda`, user-chosen:
"friendly error").** A *bounded* override — a fixed value, or a
pick-list — on a **single-column-UNIQUE** target (a `UNIQUE` column or a
single-column PK) that offers fewer **distinct** values than the row
count cannot fill the run; rather than let the D10 uniqueness machinery
silently cap it (e.g. `seed users 100 set email = 'x'` → 1 row), seed
**refuses up front** with a friendly error pointing at the fixes (use a
generator, or a longer list). Generators and ranges are treated as
effectively unbounded sources — if one genuinely exhausts, the D14
distinct-combination cap still applies. Compound uniqueness is exempt
(the *other* key columns can still vary).
**Quoting (fork, user-chosen: "quoted, grammar-consistent").** Text
values and list items are **quoted string literals** (`'admin'`),
exactly as everywhere else in the DSL — numbers and dates stay
unquoted. This reuses the ADR-0026 expression grammar **unchanged**:
exactly as everywhere else in the DSL — only **numbers** stay
unquoted. **Amendment (2026-06-11, Phase 2 build):** the original
wording said "numbers *and dates* stay unquoted", but this DSL has
**no date-literal token**`Value` is `Number`/`Text` only, and a
date is a **quoted string** validated by `bind_date` (`'2023-01-01'`)
everywhere else (insert / update / `where`). An unquoted `2023-01-01`
lexes as `2023`,`-`,`01`,… and cannot parse. So **dates in the range
form are quoted** (`between '2023-01-01' and '2024-12-31'`) — which is
in fact *more* faithful to this decision's own "quoted,
grammar-consistent" principle. Numbers remain unquoted (`NumberLit`).
This reuses the ADR-0026 expression grammar **unchanged**:
the DA pass confirmed that the `in (...)` form's operands are typed
value slots, so a *bare* `admin` would parse as a **column reference**
(→ "unknown column"), not a string. Quoting is therefore not a style
@@ -521,23 +571,26 @@ ADR-0045 showed "claimed verified" is not verified):
Design is whole; the **implementation** is phased into reviewable,
test-first commits:
1. **Core whole-row seed** — grammar/AST/executor; type-based
generation + the `fake`-backed name heuristics (D7/D8/D11);
identifier uniqueness (D10) + constraint uniqueness; FK sampling
(joint tuples) + empty-parent error + junction distinct-combos
(D14); `--seed` determinism (D4); default count + cap + zero-no-op
(D6/D1); required-column block guard (D1); **undo batch (D15)**;
**replay-as-data-write classification (D16)**; **CHECK derive /
friendly-fail (D17)**; **capped auto-show (D18)**; the enum/CHECK
advisory in its **Phase-1 wording** (D12/D13); full ambient wiring;
both modes.
2. **The `set` override clause** (D2) — value / list / generator /
range, type-aware, with completion + highlight + validity for the
generator-name slot.
3. **Column-fill mode** (`seed <table>.<column>`, D1 form 2) — the
UPDATE path.
1. **Core whole-row seed** *(done, Phase 1)* — grammar/AST/executor;
type-based generation + the `fake`-backed name heuristics
(D7/D8/D11); identifier uniqueness (D10) + constraint uniqueness; FK
sampling (joint tuples) + empty-parent error + junction
distinct-combos (D14); `--seed` determinism (D4); default count + cap
+ zero-no-op (D6/D1); required-column block guard (D1); **undo batch
(D15)**; **replay-as-data-write classification (D16)**; **CHECK
derive / friendly-fail (D17)**; **capped auto-show (D18)**; the
enum/CHECK advisory in its **Phase-1 wording** (D12/D13); full
ambient wiring; both modes.
2. **The `set` override clause** (D2) *(done, Phase 2)* — value / list /
generator / range, type-aware, with completion + highlight +
validity for the generator-name slot.
3. **Column-fill mode** (`seed <table>.<column>`, D1 form 2) *(done,
Phase 2)* — the UPDATE path.
Each phase is independently green before the next.
Each phase is independently green before the next. (Phases 2 and 3
landed together — they share the `set`-override executor machinery, so
splitting them risked a state where `set` parsed but column-fill
silently no-op'd.)
## Testing (ADR-0008 tiers 13; test-first)