feat(types)!: drop the blob column type (ADR-0005 Amendment 2)
ci / gate (push) Failing after 35s
ci / manifests (push) Successful in 3s
website / deploy (push) Successful in 35s

blob was a dead-end: declarable but never fillable (no literal in either
mode, seed-unsupported), so a blob column could only ever hold NULL. Remove
Type::Blob + Value/CellValue::Blob + the base64 CSV path + the grammar slot
+ completion/render/type-change/seed handling + the binder refusal (whose
message also carried a user-facing "DSL" copy-rule bug). The vocabulary is
now nine types; base64 stays (clipboard OSC-52).

Backward compat is the ADR-0015 migration framework's first real use: a
v1->v2 format bump (CURRENT_SCHEMA_VERSION across serializer/parser/skeleton)
with a migrator that rewrites `type: blob` -> `type: text`, and a forced .db
rebuild from the migrated text when a blob column was actually converted
(the stale .db keeps a STRICT BLOB engine column). Conversion to text is
non-destructive and CSV-free. Covered by a full-stack integration test and a
Tier-4 PTY test that opens a real legacy v1-blob project.

Also sweeps the nine-type vocabulary through CLAUDE.md, requirements.md, the
website (type reference, seed doc, highlight grammar), and the ADR-0030/0033/
0035 cross-references; CHANGELOG Removed entry; handoff-79.

BREAKING CHANGE: the `blob` column type is removed. Existing projects that
declared a blob column are migrated on first open (the column becomes text;
the original project.yaml is kept as a .v1.bak).
This commit is contained in:
claude@clouddev1
2026-06-22 21:25:38 +00:00
parent 1a2002dbf6
commit 6b4c4dcea4
41 changed files with 506 additions and 390 deletions
+2 -2
View File
@@ -172,7 +172,7 @@ representation; the SQL surface does not round-trip through it.
### 5. Type vocabulary — the playground's, not the engine's
Advanced-mode DDL uses the playground's own ten-type
Advanced-mode DDL uses the playground's own nine-type
vocabulary (ADR-0005). There is **no fallback to engine
storage types**: a column created in advanced mode is a
first-class `serial` / `decimal` / `date` / … exactly as a
@@ -373,7 +373,7 @@ ADR when taken up (ADR-0026-style).
- ADR-0002 — the engine is an implementation detail; "no
engine name in user-facing strings" — §7 extends it.
- ADR-0003 — the simple / advanced mode model this builds on.
- ADR-0005 — the ten-type vocabulary advanced DDL uses (§5).
- ADR-0005 — the nine-type vocabulary advanced DDL uses (§5).
- ADR-0009 — the DSL conventions; the DSL stays usable in
advanced mode.
- ADR-0012 / ADR-0013 — the metadata tables the `Command` core
+1 -1
View File
@@ -1649,7 +1649,7 @@ more.
## See also
- ADR-0005 — the ten-type vocabulary INSERT works with.
- ADR-0005 — the nine-type vocabulary INSERT works with.
- ADR-0006 — the auto-snapshot before destructive ops.
- ADR-0014 — the DSL DML model + cascade-summary precedent.
- ADR-0015 — the persistence write-through path.
+3 -3
View File
@@ -58,7 +58,7 @@ Two things from the earlier phases shape this one:
executed as-is, the engine would make the table, but the
playground would lose what the user meant: that `id` is `serial`,
that a `REFERENCES` clause is a *named relationship*, that `STRICT`
applies, that the ten-type vocabulary governs. Recovering that
applies, that the nine-type vocabulary governs. Recovering that
needs the parsed statement either way.
ADR-0030 §4 said "DDL → a `Command` … run the typed executor." That
@@ -92,7 +92,7 @@ mode. Unlike the DML `Sql*` commands they **execute structurally**:
the handler reads the parsed structure and performs the schema change
through the playground's metadata-maintaining machinery — writing
`__rdbms_playground_columns` / `__rdbms_playground_relationships`,
applying `STRICT`, using the ten-type vocabulary — so an
applying `STRICT`, using the nine-type vocabulary — so an
advanced-mode-created object is a first-class playground object,
identical to a simple-mode-created one (ADR-0030 §5).
@@ -772,5 +772,5 @@ honouring it is not the lesson.
- **ADR-0017** — the column type-change classification §7 shares.
- **ADR-0029** — column constraints; **ADR-0025** — indexes;
**ADR-0011** — FK column-type compatibility; **ADR-0005** — the
ten-type vocabulary.
nine-type vocabulary.
- **ADR-0006** — undo; each DDL statement is one undo step (§10).
+107
View File
@@ -0,0 +1,107 @@
# Session handoff — 2026-06-22 (79)
A long session continuing the open-issue sweep (handoff-77/78). Cleared the
**help** issue (#36) and the **schema-cache** bug (#39) earlier; this note
mainly records the big piece: **dropping the `blob` column type** (ADR-0005
Amendment 2), which grew out of a one-line copy-rule fix.
## §1. State
**Branch `main`.** Commits this session so far (all on `main`, **not pushed**):
`07575da` (#39 gate) · `e88fa79` (handoff-77 + changelog rule) · `3ad4aff`
(#36 help) · `64818c0` (handoff-78) · `1a2002d` (**ADR-0005 Amendment 2** —
the blob-drop decision record).
**Uncommitted: the blob-removal implementation** (39 files) — staged for review,
commit proposed at session end (see §4). **Full suite green: 2520 passed / 0
failed / 1 ignored** (1810 lib + 7 e2e_pty + 503 it + 200 typing).
`clippy --all-targets -D warnings` + `fmt --check` clean.
**Closed this session:** #36, #39. **Open:** #37 (clause hints), #38
(diagnostic route — needs a do/defer/close call), #40 (winget release notes).
## §2. What shipped — drop `blob` (ADR-0005 Amendment 2)
**Why** (recorded in the ADR): `blob` was a dead-end type — declarable but
never fillable (no literal in either mode, `seed`-unsupported), so a blob
column could only ever hold NULL. It rode along as the engine's fourth
storage class and never got the pedagogical scrutiny UUIDs (excluded) and
compound PKs (defended) did. The vocabulary is now **nine types**.
**Discovered via:** the `value.rs:106` binder message *"…not supported in DSL
yet"* — a user-facing copy-rule violation (it `passthrough`es to the user via
`DbError::InvalidValue`). Rather than reword, we removed the type; the message
is deleted outright.
**Implementation (test-first throughout):**
- **Removal** (compiler-guided): `Type::Blob` + its `keyword`/`sqlite`/`all()`/
`from_sql_name` alias arms; `Value::Blob`; `CellValue::Blob(Vec<u8>)` + the
base64 CSV encode/decode; the `BLOB_SLOT` grammar slot; completion's `blob`
candidate; `output_render`'s `BLOB`; the type-change `↔ blob` static refusal;
the seed blob arms; the binder refusal. User-facing strings: dropped `blob`
from `help.types_reference` and removed the `hint.value_slot_blob` slot hint.
`base64` **stays** (clipboard OSC-52).
- **Backward-compat migration** (the ADR-0015 framework's **first real use**):
a **v1→v2 format bump**. `parse_schema` hard-pinned the version to 1, so this
rippled into a `CURRENT_SCHEMA_VERSION = 2` constant used by the serializer +
parser + skeleton, the first registered migrator (`migrate_v1_to_v2`:
rewrites `type: blob``type: text` + bumps the version), and ~30 `version: 1`
test fixtures. The runtime (already wired to `ensure_project_yaml_migrated`)
**forces a `.db` rebuild** from the migrated text **only when a blob column
was actually converted** (`body_declares_blob_column` pre-read at both open
sites) — the stale `.db` keeps a `STRICT … BLOB` engine column + `"blob"`
metadata it would otherwise use as-is. Conversion target **text** (not drop)
was the user's call (non-destructive, CSV-free, PK/FK-safe).
- **Tests:** migrator unit tests (incl. a column *named* `blob` + a `blob`
substring in a CHECK left untouched); a **full-stack** integration test
`tests/it/blob_removal_migration.rs` (v1 blob project → migrate → `text`
column + row data preserved + `.bak`); a **Tier-4 PTY** end-to-end test
`e2e_pty::opens_a_legacy_v1_blob_project_by_migrating_to_text` (the real
binary opens a seeded legacy v1-blob project via `--resume` → migrate +
rebuild + `text` column) — added in the implementation `/runda` pass to
cover the runtime's migrate-on-open glue; updated all-types tests to nine;
regenerated 5 typing-surface snapshots (reviewed — only the `blob`/`data`
candidate vanished).
- **Docs:** ADR-0005 Amendment 2 (committed `1a2002d`) + README index; swept
`ten-type``nine-type` / removed `blob` from CLAUDE.md, `requirements.md`,
the website (`reference/types.md`, the seed doc's stale `not null blob`
bullet, the `rdbms.mjs` highlight grammar), and the cross-reference counts in
ADR-0030/0033/0035. CHANGELOG `[Unreleased] → Removed`.
## §3. Decisions & notes for next time
- **convert-to-text, not drop-the-column** (user, 2026-06-22): drop would force
rewriting every `data/*.csv` past the rebuild's strict header check — not
worth it for a few-days-old, few-users tool.
- **Full v2 framework migration, not parse-time leniency** (user): the proper
recorded migration with `.bak`, exercising the dormant framework.
- **`BlobLit` grammar terminal deliberately left** — a pre-existing,
`#[allow(dead_code)]` speculative token never used by any grammar; orthogonal
to the type vocabulary. Its "blob literal" string is unreachable. Removing it
is a separate optional cleanup (touches the grammar core).
- **Known assumption:** the migrator + `body_declares_blob_column` match the
serializer's *inline* column format (`- { name: …, type: … }`). A
hand-written *block-style* `project.yaml` with `type: blob` wouldn't convert
(and would then fail to parse). `project.yaml` is always machine-serialized
inline, so this is fine in practice — noted in case a future format change
moves away from inline columns.
- **Other ADRs (0030/0035) keep their historical `blob` content** (type-change
matrix, `binary`/`varbinary` aliases) per the project's supersede-don't-rewrite
ADR philosophy; ADR-0005 Amendment 2 is the authoritative record.
## §4. Process pins / next
- Commits user-confirmed, no AI attribution, append-only, on `main`; **push is
the user's step** (everything this session is unpushed).
- **`/runda` was run on both the ADR (design) and the implementation.** The
implementation pass found one real gap — the runtime's migrate-on-open glue
was untested — closed with the new Tier-4 PTY test. It also verified the
import path migrates (extract→migrate→rebuild) and the non-migration
`parse_schema` callers fail-safe.
- Proposed commit split for the implementation: (1) `feat(types)!` — code +
tests + CHANGELOG; (2) `docs` — the CLAUDE.md/requirements/website/ADR-x-ref
sweep; (3) `docs(handoff-79)`.
- Consider a `cargo sweep` at this milestone.
- **Next open issues:** #37 (clause hints, on-mission), #38 (escalate
do/defer/close first), #40 (winget release notes). Plus: blob removal means a
`cargo sweep`-worthy build and a good moment to push.
+6 -5
View File
@@ -404,7 +404,7 @@ since ADR-0027.)
referenced column), then `ALTER TABLE … ALTER COLUMN TYPE` (4f —
runtime-decomposed to `change_column_type` with `ForceConversion`, the
§7 advanced policy: lossy converts with a note, incompatible + static
refusals (`↔ blob`, non-`int → serial`) refuse, `int → serial` allowed;
refusals (non-`int → serial`) refuse, `int → serial` allowed;
the internal-`__rdbms_*` guard folded into `do_change_column_type`),
then `ALTER TABLE` add/drop constraint + add FK (4g — `ADD [CONSTRAINT
<name>] (CHECK | UNIQUE | FOREIGN KEY)` + `DROP CONSTRAINT <name>`;
@@ -430,7 +430,7 @@ since ADR-0027.)
refused with an engine-neutral message naming the construct.
Implementation pending.)*
- [x] **Q3** User-facing simplified types map transparently to
SQLite STRICT types in generated DDL. *(All ten types implemented
SQLite STRICT types in generated DDL. *(All nine types implemented
and tested.)*
- [x] **Q4** Supported SQL subset specification — **ADR-0030**.
Advanced mode is a standard-SQL surface, engine-neutral; the
@@ -462,10 +462,11 @@ since ADR-0027.)
## Type system (per ADR-0005)
- [x] **T1** All ten user-facing types implemented: `text`,
`int`, `real`, `decimal`, `bool`, `date`, `datetime`, `blob`,
- [x] **T1** All nine user-facing types implemented: `text`,
`int`, `real`, `decimal`, `bool`, `date`, `datetime`,
`serial`, `shortid`. *(Mapping to SQLite STRICT covered by
ADR-0005; FK target type rule by ADR-0011.)*
ADR-0005; FK target type rule by ADR-0011; `blob` dropped by
ADR-0005 Amendment 2.)*
- [x] **T2** `shortid` generation: base58, 1012 characters,
omits ambiguous characters; generated client-side at insert.
*(Implemented per ADR-0014; auto-fills omitted shortid