200 lines
10 KiB
Markdown
200 lines
10 KiB
Markdown
# Session handoff — 2026-06-02 (55)
|
|
|
|
Fifty-fifth handover. **One issue resolved: #11 — copy the output
|
|
panel to the system clipboard.** New ADR-0041; new `copy` app-command;
|
|
first native-clipboard dependency (`arboard`). The session is a clean
|
|
run of the full phased method: all four design axes escalated to and
|
|
decided by the user (twice — a first round of four, then a second
|
|
round of two once a hard technical constraint surfaced), `/runda` on
|
|
the **design** before any code, test-first build, full-stack PTY
|
|
verification at the end.
|
|
|
|
## §1. State at handoff
|
|
|
|
**Branch:** `main`. **HEAD `1ea376b`** (handoff-54). **3 commits ahead
|
|
of `origin/main`** at session start — the three session-54 commits
|
|
(`4cd574b`, `516848f`, `1ea376b`); handoff-53's `#15/#16` and earlier
|
|
are now pushed (origin advanced since handoff-54 was written).
|
|
|
|
**This session's #11 work is committed on top** (see the commit that
|
|
accompanies this handoff). **Tests: 2151 passing / 0 failing / 1
|
|
ignored** (full `cargo test`; lib-only **1578**). Baseline at session
|
|
start was **2134 / 0 / 1** → **+17** new tests, no regressions, no new
|
|
skips. The 1 ignored is the same long-standing `` ```ignore `` doctest
|
|
in `src/friendly/mod.rs`. **Clippy: clean** (nursery, all targets,
|
|
`-D warnings`). Push is the user's step.
|
|
|
|
## §2. #11 — copy the output panel to the clipboard (ADR-0041)
|
|
|
|
**Friction removed:** filing a bug report meant terminal-selecting the
|
|
output panel and fighting wrapping/borders. Now: a typed command.
|
|
|
|
**Command (app-level, sigil-free, both modes — ADR-0003 registry):**
|
|
|
|
| Form | Copies |
|
|
|------|--------|
|
|
| `copy`, `copy all` | the whole output panel |
|
|
| `copy last` | from the most recent echo line to the end |
|
|
|
|
`copy last` boundary is fuzzy by design: app commands push echo-less
|
|
`[system]` lines, so "from the last echo" reaches past them to the
|
|
previous DSL command — documented + accepted in ADR-0041.
|
|
|
|
**Mechanism — OSC 52 *and* native `arboard`, ALWAYS BOTH.** The
|
|
deciding constraint (surfaced in escalation round 2): OSC 52 acceptance
|
|
is **undetectable** — the terminal sends no ack — so a literal "fall
|
|
back when OSC 52 is unsupported" *cannot be built*. So both fire every
|
|
time: emit the OSC 52 escape (`ESC ]52;c;<base64>BEL`; tmux-passthrough
|
|
wrapped when `$TMUX` set), then a best-effort native write whose
|
|
failure is ignored (headless host → OSC 52 carried it). Identical
|
|
content, so the local double-write is harmless. OSC 52 needs **no new
|
|
dep** (`base64` + `crossterm` already present); it works over SSH.
|
|
|
|
**Format — plain text verbatim *as rendered*:** each line's
|
|
`[tag] body ✓/✗` joined by `\n`, **without** the viewport's right-edge
|
|
padding or soft-wrapping (full logical lines, reflow-friendly, no
|
|
trailing whitespace). A **drift-lock test** (`ui.rs`
|
|
`plain_text_matches_rendered_line_content`) pins `OutputLine::
|
|
plain_text` to the concatenated span content of `render_output_line`,
|
|
so the copy can never silently diverge from the screen.
|
|
|
|
**Files (6 new, 18 modified):**
|
|
- `src/clipboard.rs` *(new)* — pure `osc52_sequence` / `emit_osc52` /
|
|
`in_tmux`, the `NativeClipboard` trait + `deliver`, and the
|
|
`SystemClipboard` arboard impl (lazy-init, kept alive — X11 serves
|
|
the selection from a thread owned by the handle).
|
|
- `src/action.rs` — `Action::CopyToClipboard(String)`.
|
|
- `src/app.rs` — `OutputLine::plain_text`, `App::copy_text`,
|
|
`handle_copy_command`, dispatch arm + 6 Tier-1 tests.
|
|
- `src/dsl/command.rs` / `dsl/mod.rs` — `AppCommand::Copy { scope }` +
|
|
`CopyScope { All, Last }` + re-export + source mapping.
|
|
- `src/dsl/grammar/app.rs` + `grammar/mod.rs` — `COPY` CommandNode
|
|
(`copy [all|last]`, unknown→`copy.unknown`), REGISTRY entry.
|
|
- `src/runtime.rs` — long-lived `SystemClipboard` + the
|
|
`Action::CopyToClipboard` arm (thin call to `clipboard::deliver`).
|
|
- `src/ui.rs` — the drift-lock test.
|
|
- `src/friendly/strings/en-US.yaml` + `friendly/keys.rs` —
|
|
`copy.done`/`copy.nothing`/`copy.unknown`, `help.app.copy`,
|
|
`parse.usage.copy`, CLI `--help` banner line.
|
|
- `src/completion.rs` — `copy` added to the app-keyword test list
|
|
(the candidate itself is REGISTRY-driven).
|
|
- `tests/typing_surface/{mod,app_commands}.rs` + 4 new snapshots.
|
|
- `Cargo.toml` / `Cargo.lock` — `arboard` `--no-default-features`.
|
|
- `docs/adr/0041-*` *(new)*, `docs/adr/0003-*` (registry note),
|
|
`docs/adr/README.md` (index + 0003 note), `docs/requirements.md`
|
|
(new **V6**, done).
|
|
|
|
**Coverage (17):** clipboard module 6 (exact OSC 52 bytes, base64
|
|
round-trip incl. non-ASCII, tmux wrap, `deliver` both-paths +
|
|
writer-error-still-fires-native); app 6 (all / bare-=-all / last-slice
|
|
/ empty-nothing / no-echo-nothing / unknown-target-friendly-error);
|
|
drift-lock 1; typing-surface 4 (copy / copy all / copy last parse,
|
|
`copy ` offers all+last).
|
|
|
|
**Full-stack PTY verification (the real binary, not a mock):** drove
|
|
`target/debug/rdbms-playground` through a pseudo-terminal, typed
|
|
`create table …` then `copy`, and confirmed the OSC 52 escape reached
|
|
the tty with a base64 payload decoding to the exact on-screen content
|
|
(echo + `✓` + `[system]` structure + box-drawing table). This is the
|
|
end-to-end proof the unit seams don't give on their own.
|
|
|
|
## §3. The arboard decision — current state (READ if revisiting)
|
|
|
|
`arboard` `3.6.1` (1Password, `MIT OR Apache-2.0`), added
|
|
**`--no-default-features`**. Two deliberate choices are **flagged to
|
|
the user as open-for-correction** — they were made on low-stakes,
|
|
principle-aligned grounds and explicitly surfaced, not silently
|
|
decided:
|
|
|
|
1. **No `wayland-data-control` feature → X11-only native on Linux.**
|
|
That feature ~doubles the dep tree (~30 crates: `wl-clipboard-rs`,
|
|
`wayland-*`, `quick-xml`, `nom`, `petgraph`). Since "always do both"
|
|
makes **OSC 52 the universal path** (covers native-Wayland
|
|
regardless) and most Wayland desktops run XWayland (so plain
|
|
`x11rb` works), the feature was omitted to keep the dependency
|
|
surface minimal (secure-by-default). **If a concrete
|
|
native-Wayland-without-OSC-52 need appears, add the feature** — one
|
|
line in `Cargo.toml`.
|
|
|
|
2. **Copy is verbatim on-screen, including the per-line `[system]`
|
|
tags** (the PTY payload shows `[system] ┌──…`). This is the literal
|
|
reading of the user's "exactly what's rendered" choice. **If tag
|
|
stripping on table rows is wanted, that's a `plain_text` change**
|
|
(+ re-baseline the drift-lock test).
|
|
|
|
**Security posture (re-run before any dep bump):** arboard introduces
|
|
**zero new vulnerabilities** — `cargo audit`, `osv-scanner`, and
|
|
`grype` are all clean for arboard's tree (`log` / `parking_lot` /
|
|
`percent-encoding` / `x11rb`, with `gethostname`/`rustix` already ours).
|
|
Write-only (no OSC 52 *read* — the exfiltration vector is out of play).
|
|
`gitleaks` + `trivy` clean on the new code.
|
|
|
|
**Pre-existing, orthogonal, FLAGGED not fixed:** `osv-scanner` /
|
|
`cargo audit` report **RUSTSEC-2025-0067 (`libyml`)** and
|
|
**RUSTSEC-2025-0068 (`serde_yml`)**. These are a **pre-existing direct
|
|
dependency** (`Cargo.toml` `serde_yml = "0.0.12"` → `libyml`), **not
|
|
pulled by arboard** (`cargo tree -i serde_yml` shows only
|
|
rdbms-playground as parent), known since **handoff-14**. cargo audit
|
|
treats them as allowed warnings (exit 0). **Out of scope for #11**;
|
|
fixing means migrating off `serde_yml` (the YAML lib behind
|
|
`project.yaml`) — its own task, needs user sign-off.
|
|
|
|
## §4. Process notes (what worked)
|
|
|
|
- **Escalate genuine forks — don't decide.** Six questions across two
|
|
rounds. Round 1: mechanism / scope / trigger / format. Round 2 was
|
|
*forced by honesty* — the chosen "OSC 52 + native **fallback**" is
|
|
not literally buildable (no ack), so the coordination had to be
|
|
re-decided (→ "always do both") and the trigger confirmed. Surfacing
|
|
the constraint beat shipping a thing that didn't match the label.
|
|
- **`/runda` on the design.** The DA pass on ADR-0041 found: the
|
|
`copy last` boundary fuzziness (→ documented), the "exactly on
|
|
screen" overclaim (→ reworded to "as rendered, not padded"), and the
|
|
full-stack-coverage obligation (→ `deliver` both-paths test + PTY
|
|
run). Folded in before any code.
|
|
- **Test-first + drift-lock.** The renderer composes the visible line
|
|
at render time (tag, `running:`→`✓/✗`, teaching prefix), so copying
|
|
raw `OutputLine.text` would be wrong; `plain_text` mirrors the
|
|
renderer and the drift-lock test guarantees they stay in step.
|
|
- **Verify by running the app.** The PTY harness proved the seam the
|
|
unit tests can't (OSC 52 bytes actually reaching a real tty).
|
|
|
|
## §5. What's open
|
|
|
|
### Bug reports / enhancements
|
|
|
|
**#11 is resolved this session.** The handoff-50 bug-list trio
|
|
(#10/#11/#14) is now fully closed. No open issues remain on the
|
|
recent list — **confirm on GitHub after push.**
|
|
|
|
### Other tracks (from `requirements.md`)
|
|
|
|
- Track 2 Iter 6 leftovers: `history.log`-based persistent
|
|
input-history hydration polish (§12), migration-framework exercise.
|
|
- C3a (modify relationship), C4 (m:n convenience).
|
|
- H1 friendly DB-error layer (partial); tutorial/lesson system (needs
|
|
its own ADR); **V4** session-log + Markdown export (the broader
|
|
output-panel UX — V6/clipboard is a complementary "get the session
|
|
out" path now shipped); I1/I1b multi-line + readline; I3
|
|
tab-completion polish; I4 highlighting beyond input echo.
|
|
- **Pre-existing security debt:** migrate off `serde_yml`/`libyml`
|
|
(RUSTSEC-2025-0067/0068) — needs user sign-off (§3).
|
|
|
|
## §6. How to take over
|
|
|
|
1. Read this note, then `CLAUDE.md`, then `docs/requirements.md`
|
|
(V6 now done), then `docs/adr/README.md` and any ADR you'll touch
|
|
(ADR-0041 is new; ADR-0003 gained a registry note).
|
|
2. The codebase is on `main`; this session's #11 commit sits on top of
|
|
the 3 unpushed session-54 commits. Closing **#11** on GitHub is
|
|
pending the push (the user's step).
|
|
3. If revisiting the clipboard: §3 has the two open-for-correction
|
|
arboard decisions (no-Wayland-feature; verbatim-with-tags) and the
|
|
security posture. Re-run the scanners before any dep bump.
|
|
4. Honour the process pins: escalate genuine forks (and surface
|
|
technical constraints that contradict a chosen option's label),
|
|
`/runda` the design *and* implementation, test-first with a
|
|
drift-lock where a copy mirrors a renderer, and verify by running
|
|
the real app (the PTY harness pattern is reusable for any
|
|
terminal-side behaviour).
|