Files
rdbms-playground/docs/handoff/20260622-handoff-79.md
T
claude@clouddev1 e845a6ee72
ci / gate (push) Successful in 2m6s
ci / manifests (push) Successful in 4s
docs: correct the stale fmt-gate note in CLAUDE.md; handoff-79
CLAUDE.md said "fmt is intentionally not gated yet" — stale; the gate is
now fmt + clippy + test (ADR-ci-002 Amendment 1 / issue #35), run via
`nix develop -c`. Add CI-exact local-verification guidance and flag the
exit-code-vs-piped-output trap that masked the fmt failure this session.
handoff-79 records the blob removal + the CI/fmt lesson.
2026-06-22 21:43:51 +00:00

7.3 KiB

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 (1a2002d ADR-0005 Am2 + 6b4c4dc blob removal, plus the earlier 07575da #39 · e88fa79 handoff-77 · 3ad4aff #36 · 64818c0 handoff-78) — the user pushed these. A follow-up style: rustfmt commit + this CLAUDE.md/handoff doc commit land on top (see §5) and are the only unpushed work at handoff time.

Suite green: 2521 passed / 0 failed / 1 ignored (1810 lib + 8 e2e_pty + 503 it + 200 typing), fmt --check + clippy -D warnings clean — verified via nix develop -c (the CI-exact way; see §5's lesson).

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 passthroughes 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: blobtype: 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-typenine-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.
  • /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.
  • 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).

§5. CI/fmt lesson (cost a red CI run — don't repeat)

The blob commit 6b4c4dc failed the CI fmt gate (run 92, ci.yaml). Two compounding causes, both fixed here:

  1. fmt IS gated now (cargo fmt --check, ADR-ci-002 Amendment 1 / issue #35) — but CLAUDE.md still said "fmt is intentionally not gated yet." That stale note bred complacency. Corrected CLAUDE.md (the gate is fmt + clippy + test, run via nix develop -c).
  2. A broken local check masked the failure: cargo fmt --check 2>&1 | tail -1 && echo "fmt clean" tests tail's exit code (always 0), not fmt's — so "fmt clean" printed over real diffs. Always read the exit code, never a piped … | tail && echo clean.

Going forward, verify the CI way: nix develop -c cargo fmt --check / nix develop -c cargo clippy --all-targets -- -D warnings / nix develop -c cargo test — matching the pinned 1.95.0 toolchain and the devShell env, and checking exit codes. (A bare host cargo happened to be 1.95.0 this time, so the divergence was the masking pipe, not the toolchain — but nix develop -c guards both.) The style: rustfmt fix + this doc update land on top of 6b4c4dc; CI re-runs green on the next push.