Document the regression-hardening work from this session: - ADR-0008 Amendment 1 — Tier 4 realized: the portable-pty + vt100 harness, expectrl dropped, serial/fail-fast design, the four flows, sidebar-region reads, command pacing (issue #39), CI default-target (advances TT5). - ADR-0057 (new) — NFR verification strategy: contrast + ΔE2000 gates (NFR-5/7), startup/RSS measured (NFR-1/3: release ~29 ms / ~10 MB), NFR-2 by architecture, NFR-4/6 reviewer note. Records the two shipped light-theme contrast defects this work caught and fixed. - requirements.md: TT4 [~]->[x], TT5 [/] (only a Windows exec runner left), NFR-1/2/3/5/7 ->[x], NFR-4/6 ->[/], each with evidence. - CHANGELOG.md (new): Keep a Changelog + SemVer, [Unreleased] + [0.2.0]. - README ADR index updated; handoff-76; the working plan. Docs-only (CI gate skips via paths-ignore).
10 KiB
Plan — TT4 (Tier-4 PTY tests) + NFR verification + CHANGELOG
Status: approved 2026-06-22, implementation pending. Supersedes the
transient harness plan-mode copy. This is the persistent, version-controlled
plan per project convention (docs/plans/).
Context
RDBMS Playground now ships public binaries (v0.2.0 on crates.io, plus Scoop/Homebrew/winget). With real users pulling updates, the priority is regression protection before the next release. Three tracked gaps are taken up together:
- TT4 (
requirements.md, ADR-0008 Tier 4): the four critical end-to-end flows are specified but no PTY harness exists — no deps, no tests. Tiers 1–3 never exercise the real built binary through a terminal. - NFR-1..7: quality bars (startup, latency, memory, contrast, distinctive design, parity) that were never formally verified despite shipping.
- CHANGELOG: none exists; useful now that releases are public (and lets the
CI
wingetjob pass--release-notes-urllater).
Baseline (Phase 1): cargo test = 2509 passed / 0 failed / 1 ignored;
clippy + fmt clean. This is the number Phase 5 verifies against (new tests add
to it; nothing may regress).
User decisions (Phase 3):
- TT4 in CI: own
e2e_ptytest target, runs by default incargo test(so the Linux gate exercises it every push). Tight, fail-fast timeouts — the runner is self-hosted, low-parallelism, low expected flakiness (so a failure surfaces fast rather than hanging on a generous timeout). - NFR: WCAG contrast = hard gated test; startup + idle-memory = measured via
the PTY harness against generous bounds + numbers recorded; NFR-2/4/6 =
verify-by-argument + reviewer note. Each requirement →
[x]/[/]with evidence. - CHANGELOG: Keep a Changelog + SemVer;
[Unreleased]+ a single consolidated[0.2.0]only (0.1.0 treated as pre-history).
Workstream 1 — TT4: Tier-4 PTY harness + 4 flows
Tooling (refines ADR-0008's stated portable-pty+expectrl+vt100)
portable-pty(wezterm, actively maintained) — spawn the real binary in a PTY with a fixed window size.vt100— parse the PTY output stream into an inspectable cell grid.- Drop
expectrl— it bundles its own PTY abstraction (conflicts with portable-pty) and is line-oriented, a poor fit for a full-screen TUI. Replace it with a small hand-rolledwait_for(predicate, timeout)polling the vt100 screen. This deviation is recorded in the ADR-0008 amendment. - Checkpoint at implementation: verify
vt100is still maintained/current (global currency rule). If stale, evaluateavtas the screen parser before committing the dep.cargo addpicks latest; pin sensibly.
Harness (tests/e2e_pty.rs — new [[test]] target)
A support module providing:
struct PtyApp— opens a PTY (e.g. 100×30, the wide three-region layout), spawnsenv!("CARGO_BIN_EXE_rdbms-playground")with a per-test temp--data-dir(tempfile::TempDir),--theme dark,TERM=xterm-256color; a reader thread feeds bytes into avt100::Parser.send(bytes)(raw;\r= Enter,\x03= Ctrl-C),send_line(s)=s+\r.wait_for(&str, Duration)— poll the vt100 screen text until the substring appears or the (tight, ~3 s) timeout fires; on timeout panic with a full screen dump (fail-fast + debuggable). Ascreen_text()helper for assertions.quit()—send("\x03"), wait for child exit, assert success.- Run serially (
serial_testdep, or a sharedMutex/single-threaded target) — PTYs + temp dirs shouldn't race; also keeps timing stable. - Instrument with
eprintln!/screen dumps on every wait so a CI failure is diagnosable from logs (per the project's logging discipline).
The four flows (one #[test] each, mirroring ADR-0008 §Tier-4)
- Cold launch → DDL → quit. Launch fresh;
wait_for("SIMPLE")+wait_for("(none yet)");send_line("create table Customers with pk id(serial)");wait_for("Customers")in the Tables panel;quit(). - Save → restart → reopen. Launch project path P under the temp data-dir;
create a table;
quit()(autosave already persisted per-command); relaunch same P;wait_for("Customers")— schema restored from text/rebuild. - Export → import → rebuild. Project A: create table + insert a row;
send_line("export A.zip")(explicit path under the temp dir);wait_forexport-success note;quit(). Fresh project B:send_line("import <path>/A.zip");wait_forimport note (rebuild auto-runs on missing.db); assert the table + row are present (show data/ Tables panel). - Undo after DROP. Create table;
send_line("drop table Customers"); confirm gone;send_line("undo");wait_for("Restore that earlier state?")(the real modal);send("y");wait_for("Customers")restored.
CI
No workflow edit needed for execution: e2e_pty is a default target, so the
existing gate job's cargo test --no-fail-fast runs it on the Linux
container. Validate locally first that a PTY opens inside the
rdbms-playground-ci image (container /dev/ptmx); if not, add --init/pts
mount notes to the ADR. Advances TT5 (Tier-4-in-CI on Linux); Windows
execution runner stays out of scope (no runner).
Workstream 2 — NFR verification
Gated: WCAG contrast (NFR-5, NFR-7) — src/theme.rs #[cfg(test)]
- Add a
relative_luminance(Color) -> f64+contrast_ratio(a,b) -> f64helper (WCAG 2.x sRGB formula) in the test module; amatch Color::Rgbchannel extractor (panics on non-RGB — also a guard against a future named-colour regression). - For both
Theme::light()andTheme::dark(), assert ≥ 4.5:1 for normal-text foregrounds onbg:fg,error,warning,system,plan_efficient,mode_simple,mode_advanced, and the info-carrying syntax tokens (tok_keyword/identifier/type/number/string/flag/function/error). muted/tok_punct(deliberately dim secondary text) and borders (border/border_advanced, non-text UI): assert the WCAG non-text 3:1 threshold, with a comment citing the WCAG large-text/non-text allowance. If any fails even 3:1, that's a real finding → surface to the user, don't silently relax. (Supersedes the existing inequality-only theme tests.)- This runs in the existing gate — permanent regression protection on the palette.
Measured + documented: startup (NFR-1) + idle memory (NFR-3)
Two measurement tests in e2e_pty.rs:
- Startup: time from spawn to
wait_for("SIMPLE")(first rendered frame); assert a generous bound (e.g. < 1500 ms locally) andeprintln!the actual. - Idle memory: after the app is idle, read the child's
/proc/<pid>/statusVmRSS(Linux); assert a generous bound and print actual. (Linux-gated via#[cfg(target_os = "linux")].) The point is gross-regression detection, not a tight SLA gate (user decision).
Verify-by-argument + reviewer note (NFR-2, NFR-4, NFR-6)
- NFR-2 (input off-thread): already architecturally true — DB runs on the
worker thread (ADR-0010) and input on a separate Tokio task (confirmed in
runtime.rs). Record the evidence; optionally a test that a query doesn't block a subsequent keystroke render. - NFR-4 (distinctive design) / NFR-6 (cross-platform parity): qualitative / partly-CI. Written reviewer verification (DA-hat) in the NFR verification doc; parity partly evidenced by the CI matrix (Linux+macOS execute, Windows builds).
Requirements bookkeeping
Move NFR-5/7 → [x] (gated test), NFR-1/3 → [x] (measured + bounded test +
recorded numbers), NFR-2 → [x] (architectural evidence), NFR-4/6 → [/] with
the reviewer note (honest: not fully automatable). Each with a commit/test ref.
Workstream 3 — CHANGELOG
- New
CHANGELOG.mdat repo root, Keep a Changelog + SemVer. [Unreleased](empty/seeded) + one consolidated[0.2.0] - <release date>with Added/Changed/Fixed of the notable user-facing changes (install methods, packaging,--version/version, seed improvements, hint feature, readline keys, cross-mode history) mined from ADRs 0053–0056 + handoffs 73–75. 0.1.0 = pre-history (a one-line note).- Optional follow-up (flag, don't auto-do): wire
--release-notes-urlinto the CIwinget/release path — out of scope unless you want it now.
Docs / ADR updates (project discipline)
- Amend ADR-0008 (Tier-4 section): record the realized tooling
(
portable-pty+vt100+ hand-rolledwait_for,expectrldropped), serial execution, tight-timeout/fail-fast stance, the default-target CI wiring, and the four implemented flows. Updatedocs/adr/README.mdin the same edit. - New ADR-0057 — NFR verification strategy (next free number; assigned at
merge per ADR-0000): the contrast-gate + thresholds (incl. the 3:1
muted/border allowance), the measured-not-tightly-gated stance for
startup/memory, and the verify-by-argument treatment of NFR-2/4/6. Add a
short NFR verification record (the measured numbers + reviewer notes) —
either in the ADR or a
docs/companion. Update the README index. - Update
docs/requirements.md: TT4[~]→[x], TT5[/]note (Tier-4 now in Linux CI; Windows-exec still pending), NFR rows as above. - A handoff note (next sequence number) per project convention.
Verification (Phase 5)
cargo test— full suite green; new e2e_pty + contrast tests pass; 2509 + new, 0 failed, ≤1 ignored; no regressions vs baseline.cargo clippy --all-targets -- -D warnings+cargo fmt --checkclean.- Run
e2e_ptyrepeatedly (e.g. 5–10×) locally to confirm non-flaky under the tight timeouts before relying on the gate. - Confirm a PTY opens inside the CI image (or document the fix).
- DA-hat pass over each workstream + the NFR reviewer judgements, written down.
cargo sweepat the milestone (CLAUDE.md build hygiene).
Risks / checkpoints
- vt100 currency — verify before adding; fall back to
avtif stale. - PTY-in-container — validate early; the whole TT4 CI value depends on it.
- muted/border contrast — may fail even 3:1; if so it's a finding to escalate, not silently relax (could mean a small palette tweak — touches a decided area, so escalate).
- Commits — confirm each message; no AI attribution; append-only; never push.