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.
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+ itskeyword/sqlite/all()/from_sql_namealias arms;Value::Blob;CellValue::Blob(Vec<u8>)+ the base64 CSV encode/decode; theBLOB_SLOTgrammar slot; completion'sblobcandidate;output_render'sBLOB; the type-change↔ blobstatic refusal; the seed blob arms; the binder refusal. User-facing strings: droppedblobfromhelp.types_referenceand removed thehint.value_slot_blobslot hint.base64stays (clipboard OSC-52). - Backward-compat migration (the ADR-0015 framework's first real use):
a v1→v2 format bump.
parse_schemahard-pinned the version to 1, so this rippled into aCURRENT_SCHEMA_VERSION = 2constant used by the serializer + parser + skeleton, the first registered migrator (migrate_v1_to_v2: rewritestype: blob→type: text+ bumps the version), and ~30version: 1test fixtures. The runtime (already wired toensure_project_yaml_migrated) forces a.dbrebuild from the migrated text only when a blob column was actually converted (body_declares_blob_columnpre-read at both open sites) — the stale.dbkeeps aSTRICT … BLOBengine 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+ ablobsubstring in a CHECK left untouched); a full-stack integration testtests/it/blob_removal_migration.rs(v1 blob project → migrate →textcolumn + row data preserved +.bak); a Tier-4 PTY end-to-end teste2e_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 +textcolumn) — added in the implementation/rundapass to cover the runtime's migrate-on-open glue; updated all-types tests to nine; regenerated 5 typing-surface snapshots (reviewed — only theblob/datacandidate vanished). - Docs: ADR-0005 Amendment 2 (committed
1a2002d) + README index; sweptten-type→nine-type/ removedblobfrom CLAUDE.md,requirements.md, the website (reference/types.md, the seed doc's stalenot null blobbullet, therdbms.mjshighlight 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/*.csvpast 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. BlobLitgrammar 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_columnmatch the serializer's inline column format (- { name: …, type: … }). A hand-written block-styleproject.yamlwithtype: blobwouldn't convert (and would then fail to parse).project.yamlis 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
blobcontent (type-change matrix,binary/varbinaryaliases) 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. /rundawas 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-migrationparse_schemacallers fail-safe.- Consider a
cargo sweepat 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:
fmtIS gated now (cargo fmt --check, ADR-ci-002 Amendment 1 / issue #35) — butCLAUDE.mdstill said "fmt is intentionally not gated yet." That stale note bred complacency. CorrectedCLAUDE.md(the gate is fmt + clippy + test, run vianix develop -c).- A broken local check masked the failure:
cargo fmt --check 2>&1 | tail -1 && echo "fmt clean"teststail'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.