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
+47
View File
@@ -32,6 +32,7 @@
// (the guard *is* the serialization) or pointless here.
#![allow(clippy::significant_drop_tightening)]
use std::fs;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::{Arc, Mutex, MutexGuard};
@@ -404,6 +405,52 @@ fn back_to_back_insert_after_ddl_still_succeeds() {
app.quit();
}
/// Flow 6 — ADR-0005 Amendment 2 backward-compat: opening a **legacy v1
/// project that declares a `blob` column** migrates it to `text` and opens
/// cleanly. This drives the real runtime's migrate-on-open + rebuild path
/// end-to-end — the post-removal binary would otherwise reject the v1/blob
/// project file. (The integration test covers the migrator + rebuild via
/// direct calls; this covers the runtime glue that decides to run them.)
#[test]
fn opens_a_legacy_v1_blob_project_by_migrating_to_text() {
let dir = TempDir::new().expect("data dir");
let root = dir.path();
// Seed a v1 project (the old format) with a `blob` column, under the
// data root's projects/ dir, and point `--resume` at it. No
// playground.db is shipped, so the open rebuilds from the migrated text.
let proj = root.join("projects").join("Legacy");
fs::create_dir_all(proj.join("data")).expect("create project dirs");
fs::write(
proj.join("project.yaml"),
concat!(
"version: 1\n",
"project:\n created_at: 2026-01-01T00:00:00Z\n mode: simple\n",
"tables:\n",
" - name: Files\n",
" primary_key: [id]\n",
" columns:\n",
" - { name: id, type: serial }\n",
" - { name: payload, type: blob }\n",
"relationships: []\n",
"indexes: []\n",
),
)
.expect("write project.yaml");
fs::write(proj.join("data").join("Files.csv"), "id,payload\n1,aGVsbG8=\n").expect("write csv");
fs::write(root.join("last_project"), format!("{}\n", proj.display())).expect("write resume");
// Open via --resume: the runtime migrates blob→text and rebuilds, so
// the project opens and the table shows in the sidebar.
let mut app = PtyApp::launch_in(root, &["--resume"]);
app.wait_for_table("Files");
// The former blob column is now a normal text column — `show data`
// renders it (header + the base64 value preserved as text).
app.submit("show data Files");
app.wait_for("payload");
app.quit();
}
// ===================== NFR perf (measured, generous) ===================
//
// These run against the DEBUG binary, so the bounds are loose