ADR-0029 (column constraints — NOT NULL / UNIQUE / CHECK / DEFAULT) is fully implemented across the handoff-22 and handoff-23 sessions. Ticks requirement C3, and corrects ADR §10's CHECK-error wording to the compiled-SQL form per the §7 storage deviation.
7.4 KiB
Session handoff — 2026-05-19 (23)
Twenty-third handover. This session finished ADR-0029 —
column constraints (NOT NULL / UNIQUE / CHECK /
DEFAULT). Commits 5–6, planned in full in handoff-22 §4, are
implemented, tested and committed. ADR-0029 is complete;
requirement C3 (schema constraints) is now ticked in
docs/requirements.md.
§1. State at handoff
Branch: main. Working tree clean (after this docs
commit). 3 commits since handoff-22 (102dff0) — two
feature commits plus this handoff — all local; push
asynchronously, not blocking.
<this file> docs: handoff 23 — ADR-0029 complete; tick C3
5e97f6a constraints: CHECK-violation friendly error + typing-surface matrix (ADR-0029 §10)
abce118 constraints: `add constraint` / `drop constraint` on existing columns (ADR-0029 §2.2)
Tests: 1240 passing, 0 failing, 1 ignored (cargo test). The ignored test is the long-standing ```ignore
doc-test in src/friendly/mod.rs. Typing-surface matrix:
188 cells (was 174 — +14 for the constraint grammar).
Clippy: clean (cargo clippy --all-targets -- -D warnings, nursery group).
§2. What ADR-0029 delivered (full picture)
Commits 1–4 (handoff-22) landed the constraint suffix —
not null / unique / default <lit> / check (<expr>)
after a column's (type) group on create table and add column, the db-layer DDL/round-trip, and the §6 add column
routing. This session added the rest:
Commit 5 (abce118) — add constraint / drop constraint.
The §2.2 surface for modifying an existing column's
constraints:
add constraint not null to <T>.<col>
add constraint unique to <T>.<col>
add constraint default <literal> to <T>.<col>
add constraint check ( <expr> ) to <T>.<col>
drop constraint (not null | unique | default | check) from <T>.<col>
- Grammar (
src/dsl/grammar/ddl.rs):ADD_CONSTRAINT/DROP_CONSTRAINTjoin theadd/dropChoices, discriminated by theconstraintform word.add constraintreuses the §2.1COLUMN_CONSTRAINTChoice;drop constraintuses a payload-freeDROP_CONSTRAINT_KINDChoice.CONSTRAINT_TARGETis the dotted<T>.<col>(table_name/column_nameroles — distinct from the CHECK expression'sexpr_column, so the target column never collides with an expression column). - AST (
src/dsl/command.rs):Command::AddConstraint/DropConstraint;Constraint(payload-carrying) /ConstraintKind(payload-free) enums. - Worker (
src/db.rs):do_add_constraint/do_drop_constraintapply the change through the rebuild-table primitive.do_add_constraintruns the §5 dry-run first —dry_run_not_null/dry_run_unique/dry_run_checkscan the existing rows and, on a violation, refuse before any write with a pretty-printed table of offending rows (thedb.diagnostic.add_*_summarycatalog keys +render_diagnostic_table, mirroringdo_change_column_type). §9 redundant-on-PK declarations and §6defaulton aserial/shortidcolumn are friendly refusals. - Both return
TableDescription→CommandOutcome::Schema→ the existing auto-show path (no newAppEvent).
Commit 6 (5e97f6a) — CHECK friendly error + matrix.
- CHECK-violation friendly error (ADR §10). The friendly
layer already had a
translate_check+error.check.*catalog entries (placeholder from an earlier session), but nothing filled the column in.enrich_dsl_failure(src/runtime.rs) gained a CHECK branch:enrich_check_violationreads the column from the engine'sCHECK constraint failed: <column>message, then resolves the table, the offending value, and the column's compiledCHECKexpression.FailureContext/TranslateContextcarry a newcheck_rulefield;translate_checkrenders "the value<v>breaks the rule<rule>" when the rule is known, falling back to the plain hint otherwise. - Typing-surface matrix. New
tests/typing_surface/ constraints.rs— 14 cells covering the create-table / add-column constraint suffix andadd/drop constraint.
§3. Decisions / deviations this session
schema_to_ddlUNIQUE bug fixed (abce118). It suppressedUNIQUEfor every PK column. A compound-PK member is not individually unique, so an explicitUNIQUEon it must survive the rebuild. The condition is now "suppress only for a single-column PK". Caught by theadd_constraint_unique_on_compound_pk_member_is_allowedtest. Pre-existing latent bug.- "Nothing to drop" is a friendly refusal (
abce118) —drop constrainton a constraint the column never carried refuses ("T.colhas no UNIQUE constraint to drop") instead of a silent no-op rebuild. This was not spelled out in handoff-22 §4; it is consistent with ADR-0029 §9's "clarity over permissiveness" and was an autonomous call — flagged here and accepted by the user at commit time. - CHECK friendly error went full ADR §10 (
5e97f6a) — the user chose, when asked, to surface the rule + offending value rather than the minimal handoff-§4 scope. ADR §10's prose said "DSL-form expression"; deviation §7 had removed the DSL-text form, so §10 was updated to say the rule is shown in its compiled-SQL form — consistent with §7/§8.
§4. Key reusable pieces (this session)
src/db.rs:do_add_constraint/do_drop_constraint;read_constraint_dry_run_rows+dry_run_not_null/dry_run_unique/dry_run_check;render_constraint_ dry_runand thedry_run_id_*helpers;value_to_default_ sql(factored out ofdefault_sql_literal).DryRunRowtype alias.src/runtime.rs:enrich_check_violation.src/friendly/:FailureContext.check_rule/TranslateContext.check_rule;error.check.*.hint_with_rulecatalog keys.Operation::AddConstraint/DropConstraintinsrc/friendly/translate.rs.
§5. What's next
ADR-0029 closes requirement C3. No feature is in flight.
Open clusters (prioritisation is a user decision — ask):
- Snapshot / undo / replay
U-series (designed in ADR-0006, not yet implemented). - m:n convenience
C4; modify-relationshipC3a(drop+add covers it today). - Project storage Iter 5–6:
export/import,--resume, persistent input history, migration-framework scaffold. - SQL handling in advanced mode (
sqlparser-rs, its own ADR). - Friendly-error layer
H1(the remaining SQL→English sweep); strong syntax-help in parse errorsH1a. - Session-log / Markdown export
V4; ER diagram exportV3. - CI workflow
TT5; readline shortcutsI1b; multi-line inputI1; Tab completion polishI3.
§6. How to take over
- Read this file, then
CLAUDE.md(working-style rules), thendocs/requirements.md(per-item progress —C3now ticked). - Run
cargo test— 1240 passing, 0 failing, 1 ignored. - Run
cargo clippy --all-targets -- -D warnings— clean. - Pick the next cluster with the user — §5 has no default.
Note on the typing-surface matrix
tests/typing_surface/ is 188 cells. The matrix-snapshot
discipline stands: a failing cell with correct new
behaviour → update its snapshot; with wrong behaviour → the
cell earned its keep. cargo insta is not installed —
regenerate with INSTA_UPDATE=always cargo test --test typing_surface_matrix <filter> and review the written
.snap files before committing.