From e88fa79f09851bd3f8ebe9e54a01a606dd05b899 Mon Sep 17 00:00:00 2001 From: "claude@clouddev1" Date: Mon, 22 Jun 2026 17:13:12 +0000 Subject: [PATCH] docs: handoff-77, changelog-discipline rule, and #39 changelog entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/handoff/20260622-handoff-77.md — session handoff for the #39 schema-cache-gate fix; records the four open issues (incl. the new #40). - CLAUDE.md — add the changelog-discipline rule: update [Unreleased] in the same change as any user-facing behaviour change (scripted/pasted paths count), under the two copy rules; release-time sweep of commits/handoffs as the backstop. - CHANGELOG.md — [Unreleased] → Fixed bullet for the #39 paste/scripted- input fix. --- CHANGELOG.md | 3 + CLAUDE.md | 14 ++++ docs/handoff/20260622-handoff-77.md | 125 ++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 docs/handoff/20260622-handoff-77.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e09b5..61354c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 on each build. ### Fixed +- Pasting or scripting several commands at once no longer occasionally + rejects a valid simple-mode `insert` — submitted right after adding a + column — as though it were advanced-mode SQL. - **Light theme:** string-literal and flag colours in syntax highlighting were below the WCAG-AA contrast bar; both are now legible. Two dark-theme token colours that were hard to tell apart have been separated. diff --git a/CLAUDE.md b/CLAUDE.md index 6038711..233cd79 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -246,6 +246,20 @@ Key invariants in the code: the specific product (SQLite, STRICT, rusqlite, PRAGMA). ADR-internal prose and code comments may name it where technically necessary for precision. +- **Changelog discipline.** `CHANGELOG.md` (repo root) tracks + notable **user-facing** changes (Keep a Changelog + SemVer). + Update it in the **same change** that introduces a user-facing + behaviour change: add or amend a bullet under `[Unreleased]` + in the right category (Added / Changed / Deprecated / Removed / + Fixed / Security), phrased for end users under the two copy + rules above (no engine name; no "DSL" — say "simple mode" / + "advanced mode"). A change with no user-visible effect (pure + refactor, internal tests, CI plumbing) gets **no** entry — and + the judgement of "user-facing" includes scripted / pasted / + power-user paths, not just the interactive happy path. At + release time, rename `[Unreleased]` to the new version + date, + add the compare link, and **sweep the commits/handoffs since + the last tag** as a backstop for anything missed. - **Confirm commits.** Per the user's global rules, every `git commit` is preceded by an explicit message proposal and user approval. No AI attribution in commit messages. diff --git a/docs/handoff/20260622-handoff-77.md b/docs/handoff/20260622-handoff-77.md new file mode 100644 index 0000000..b3205e0 --- /dev/null +++ b/docs/handoff/20260622-handoff-77.md @@ -0,0 +1,125 @@ +# Session handoff — 2026-06-22 (77) + +Continues from handoff-76. Per the user's direction (handoff-76 §7: clear the +open Gitea issues before resuming feature work), this session took the **bug** +of the four open issues — **#39** (Form-B insert misparse against a stale schema +cache after fast DDL) — and fixed it end-to-end. Three open issues remain +(#36/#37/#38, all enhancements); see §3. + +## §1. State + +**Branch `main`.** Commits this session (on `main`, **not pushed** — push is the +user's step): + +- `07575da` `fix(app)` — schema-refresh submission gate (#39) + Tier-1 and + Tier-4 PTY regression tests + ADR-0022 Amendment 8 + README index. +- this `docs(handoff-77)` commit — folds in the `CHANGELOG.md` `[Unreleased]` + Fixed bullet for #39 **and** a new **changelog-discipline rule** in project + `CLAUDE.md` (see §4). + +**Test baseline: 2521 passed / 0 failed / 1 ignored** (was 2519; +1 lib test, ++1 e2e_pty test → e2e_pty now 7 on Linux). `clippy --all-targets -D warnings` + +`fmt --check` clean. The new PTY test was confirmed to **time out without the +fix** (genuine guard), green with it. + +**Issue #39 is closed** (resolution comment + diagnosis recorded on the issue). + +## §2. What shipped — issue #39 + +**Root cause.** `App::update` is pure-sync and validates submissions against +`App::schema_cache`. The runtime refreshes that cache **asynchronously** after +the worker applies a command — a `SchemaCacheRefreshed` event posted on the +**same FIFO channel as key events** (`runtime.rs:1591`, applied `app.rs` +`SchemaCacheRefreshed` arm). Under faster-than-human input (paste / script / +unpaced PTY) the next Enter is processed *before* the refresh lands, so a +simple-mode **Form-B insert** (`insert into T values (…)`, columns derived from +the cache) submitted right after `add column` sees the pre-DDL columns, its +arity can't match, and the friendly layer tags it *"trying to write SQL?"*. The +worker itself is never wrong (it runs commands serially); the bug was purely in +client-side pre-validation racing the refresh. + +**Fix — submission gate (`src/app.rs`).** Two new private `App` fields: +`awaiting_schema_refresh: bool` + `held_submissions: VecDeque<(String, +EffectiveMode)>`. `dispatch_dsl` arms the flag on **every** `ExecuteDsl` +dispatch; while armed, a new DSL submission is **held** (queued in submission +order) rather than validated. The `SchemaCacheRefreshed` handler clears the flag +and **drains** the queue against the now-fresh cache, stopping as soon as a +drained command re-arms the gate (the rest then wait for *its* refresh — order +preserved). App-lifecycle commands route through `dispatch_app_command` *before* +`dispatch_dsl`, so `quit`/`help`/`load`/`undo`/`rebuild` are never held. + +**Why arm on every dispatch, not just DDL** — it keeps at most one DSL command +in flight, so refreshes are strictly one-per-dispatch and in order, making the +boolean provably correct. Arming only on DDL would let a *preceding* non-DDL +command's refresh clear the gate early and drain a held insert against a +pre-DDL cache (a real corner case). Cost was one existing test +(`walking_skeleton::colon_escape_in_simple_mode_is_one_shot`) updated to model +the post-dispatch refresh — faithful, since it had been assuming it. + +**Scope = interactive only.** The `replay` / history-log / startup +rebuild-from-text batch path already re-snapshots the schema **synchronously, +inline, before every line** (`run_replay` → `build_schema_cache`, +`runtime.rs:2514`), so it always had this ordering guarantee; the fix brings the +interactive path in line with it. No interactive-user impact (the gate clears in +ms); held input is never lost because the runtime sends a `SchemaCacheRefreshed` +after **every** dispatch, success or failure (the unconditional post-match block +in `spawn_dsl_dispatch`). + +**Tests** (both verified red→green): +- `app::tests::form_b_insert_after_ddl_is_held_until_refresh_then_dispatched` — + Tier-1, deterministic: stale cache → submit DDL (arms) → submit insert (held, + no error) → deliver fresh `SchemaCacheRefreshed` → insert dispatches. +- `e2e_pty::back_to_back_insert_after_ddl_still_succeeds` — Tier-4 PTY, the + unpaced inverse of flow 3; asserts the insert's own `('Alice') ✓` echo. + +**Docs.** ADR-0022 **Amendment 8** records the gate (the §9 schema-cache +refresh-vs-validation timing contract); README index updated in the same edit +(ADR-0000 rule). + +## §3. Open / follow-ups + +Per the user's direction the open issues come before feature work. After closing +#39, **four** issues are open. The hint/help trio (#36/#37/#38) are all +**enhancements** on the pedagogy mission (read handoff-76 §7's table for fuller +scope; read **ADR-0053** first — it orients #37/#38). **#40** is a CI/packaging +follow-up filed this session. + +| # | One-line | read | +| --- | --- | --- | +| **#36** | `help ` shows no distinct content — the 7 advanced-mode SQL nodes share `help_id: None` (`src/dsl/grammar/mod.rs:915-918`) | **Contained.** Good next pick; touches H3/`help`, maybe an ADR amendment. | +| **#37** | Clause-concept hints (cursor inside `on delete …`, `with pk`, `1:n`/`m:n`, create-table constraint slots) — deferred ADR-0053 extension | **Medium scope, richest teaching value.** Likely an ADR-0053 amendment. | +| **#38** | Pre-submit-diagnostic F1 route + ~33 `diagnostic.*` tier-3 blocks; needs a `class`/`message_key` threaded through every diagnostic site | **Broad mechanism, most marginal value.** Get the user's do/defer/close call before investing. | +| **#40** | Wire `CHANGELOG.md` into the winget release notes (`komac --release-notes-url`); ADR-0056 area | Filed this session (the changelog→winget thread, see §4). Small, deferred-by-decision originally. | + +**Suggested order (confirm with the user):** #36 (contained) → #37 (highest +on-mission value) → #38 (decision first: do / defer / close). #40 is independent +(release pipeline) and can slot in whenever. #38 in particular should be +escalated for a do-or-close call rather than silently built. + +## §4. Changelog discipline — new rule + open winget thread + +The session surfaced that **no rule existed** for keeping `CHANGELOG.md` current +(it was created in handoff-76's plan but never given a maintenance process), so +the #39 fix wasn't logged until the user asked. Decided with the user: + +- **New `CLAUDE.md` rule (this commit):** update `[Unreleased]` in the **same + change** that alters user-facing behaviour (incl. scripted/pasted/power-user + paths, not just the interactive happy path), under the two copy rules; no entry + for refactor/test/CI-only changes; at release time rename `[Unreleased]` and + **sweep commits/handoffs since the last tag** as a backstop. +- **#39 entry added** under `[Unreleased] → Fixed`. +- **winget release notes → issue #40.** komac supports `--release-notes-url` / + `--release-notes`; the `winget` job (`publish.yaml` ≈ L276) passes neither. + Tracked, not done (it's a release-pipeline / ADR-0056 change, kept out of this + bug-fix session by the user's call). + +## §5. Process pins + +- Commit user-confirmed, no AI attribution, append-only, on `main`; **push is + the user's step** (this commit is unpushed). +- Test-first honored: both regression tests were confirmed RED before the fix + (the PTY one via a `git stash` of `src/app.rs`), GREEN after. A written + Devil's-Advocate pass on the implementation surfaced one comment imprecision + (fixed) and no behavioral findings. +- `cargo sweep` not run this session; the build grew modestly (one extra PTY + test binary). Consider a sweep at the next milestone.