|
| 1 | +# Controller Handoff — edaptor batch: MaskedInput adoption + form/browser fixes |
| 2 | + |
| 3 | +> Starter pack for the next controller session. This handoff lives in ONE |
| 4 | +> worktree — run `git worktree list` first and confirm this is the workstream |
| 5 | +> you're resuming. Read this first, then `git log <handoff-commit>..HEAD` for |
| 6 | +> everything that changed since. Detail is NOT here — it's in git + the reports |
| 7 | +> (§6). Before you rewrite this file at your own handoff: read the previous |
| 8 | +> version (`git show HEAD:docs/controller-handoff.md`) and carry forward any |
| 9 | +> lesson in §4/§5 that is still true. Fresh synthesis, not blank page. On merge |
| 10 | +> into another branch, rewrite that branch's handoff to the merged reality. |
| 11 | +
|
| 12 | +Handoff commit: 9589a21 (branch `integrate/2026-07`) Date: 2026-07-24 Reason: milestone before rollover |
| 13 | +Worktree / branch: main checkout `/home/oetiker/checkouts/edaptor` @ integrate/2026-07 |
| 14 | +Sibling worktrees: none. rstv (tvision-rs source) sits at `/home/oetiker/checkouts/rstv` (sibling repo, not a worktree). |
| 15 | + |
| 16 | +## 1. Mission |
| 17 | +edaptor is a schema-driven TUI LDAP editor on **tvision-rs**. This session did two |
| 18 | +things: (A) shipped **tvision-rs 0.14.0** — native `InputLine` password masking + |
| 19 | +a `RevealEye` toggle + a `MaskedInput` composite (the structural cure for a class |
| 20 | +of password bugs), now **released to crates.io**; and (B) a batch of edaptor fixes |
| 21 | +on top of Track A (the carried-over paste/password bugfixes), all combined into |
| 22 | +**`integrate/2026-07`** and opened as **edaptor PR #6**. |
| 23 | + |
| 24 | +## 2. Where we are now |
| 25 | +- **tvision-rs 0.14.0 — DONE & RELEASED.** PR #21 merged, tag v0.14.0 published. |
| 26 | + edaptor pins `tvision-rs = "0.14.0"`. |
| 27 | +- **edaptor `integrate/2026-07` — PR #6 open, green, NOT merged.** It merges Track A |
| 28 | + + three reviewed fixes (all green together: 890 lib tests, clippy `-D warnings`, |
| 29 | + fmt clean): |
| 30 | + 1. Track A (`fix/multivalue-paste`): multi-value paste + password paste/confirm/ |
| 31 | + backspace fixes. |
| 32 | + 2. `feat/adopt-masked-input`: delete the hand-rolled password mirror, adopt |
| 33 | + native `MaskedInput` (−377 lines). Supersedes Track A's password-mirror fixes. |
| 34 | + 3. `fix/launch-value-scroll`: read-only multi-value blocks now scroll line-by-line. |
| 35 | + 4. `fix/container-classification`: containers classified by objectClass, empty OUs |
| 36 | + in the tree, sub-containers in the entry pane. |
| 37 | +- `main` was clean/synced with origin at handoff. The four sibling branches are all |
| 38 | + merged into `integrate/2026-07`. |
| 39 | +- **carbo still runs plain v1.3.0 — the live panic + silent-password bugs are STILL |
| 40 | + in production.** Redeploy is the last pending action (needs explicit go-ahead). |
| 41 | + |
| 42 | +## 3. Do this next |
| 43 | +1. **Get PR #6 merged** (oposs/edaptor#6) — user reviews/merges to `main`. |
| 44 | +2. **Redeploy carbo** from the merged binary (recipe in §4) — it has live bugs. |
| 45 | + Production + ssh: confirm before running. |
| 46 | +3. Optional follow-ups (not blocking): the two tangential shuttle bugs (§7), and |
| 47 | + sub-container navigate-on-Enter (§7). |
| 48 | + |
| 49 | +## 4. Lessons & traps ← the irreplaceable part |
| 50 | +- **The whole password-bug cluster was ONE root cause: an unhandled `Event::Paste` |
| 51 | + in a custom view.** tvision delivers terminal bracketed paste as `Event::Paste` |
| 52 | + (routed like a KeyDown), distinct from `Event::Command(Command::PASTE)` (Ctrl+V). |
| 53 | + A `match` on only `KeyDown` silently drops it. Now moot in the password field |
| 54 | + (native `MaskedInput`), but the pattern recurs — when "paste doesn't work", look |
| 55 | + for a view that doesn't handle `Event::Paste`. |
| 56 | +- **Rust method-resolution trap in `MaskedInput` (carry forward for ANY tvision |
| 57 | + composite):** an inherent `&mut self value()` is silently shadowed by the |
| 58 | + `View::value(&self)` default (which returns `None`) at the dot-call site → your |
| 59 | + getter returns `None` and no error. Fix: make the inherent getter `&self`, and |
| 60 | + **explicitly override `value`/`set_value`/`set_value_ctx` in `impl View`** so the |
| 61 | + `#[delegate]` macro doesn't forward to `Group`'s no-op defaults. A dedicated |
| 62 | + round-trip-through-`Box<dyn View>` test guards it. |
| 63 | +- **tvision facts (0.14):** timers are **synchronous** via `Context::set_timer`/ |
| 64 | + `kill_timer` over an injected `TimerQueue` — there is **no** `Deferred::SetTimer`. |
| 65 | + `InputLine::SURFACE_ROLES` is **not pub**. **Theme glyphs live in `Glyphs`** |
| 66 | + (`reveal_eye_hidden`/`reveal_eye_revealed`); widgets read `ctx.glyphs()` — do NOT |
| 67 | + bake glyphs into a widget config (that was a review finding). Masked clipboard |
| 68 | + guard keys on `mask.is_some()` (blocks copy even when *revealed* — a deliberate |
| 69 | + security choice), not on `masking()`. |
| 70 | +- **rstv release hygiene:** `.github/workflows/release.yml` OWNS version bumps + |
| 71 | + CHANGELOG rollover. A feature branch must ONLY add notes under `## Unreleased` |
| 72 | + and must NOT bump `Cargo.toml`/tag. (We tripped this and had to revert.) |
| 73 | +- **The "form scroll" bug was NOT the shuttle** (the shuttle works). It was the |
| 74 | + form's read-only **`LaunchValueView`** launch block: single focus stop, passes |
| 75 | + Up/Down straight through, no internal scroll → tall blocks unreachable. Fix is |
| 76 | + **geometry-driven in the form** (`ScrollGroup::scroll_block_edge`) — the block |
| 77 | + exposes **no cursor** (a caret would wrongly imply editability). Don't add a caret |
| 78 | + to a read-only block. |
| 79 | +- **Container classification:** a node is a container if its objectClass ∈ |
| 80 | + {organizationalUnit, organization, dcObject, domain, container} (case-insensitive) |
| 81 | + OR it has children. **`is_branch` (pure has-children) is STILL load-bearing for the |
| 82 | + promote/demote child-count reflow — do NOT replace it.** Only tree membership |
| 83 | + (`branch_dns`), panel-2 exclusion, AND the `upsert` **tree-dirty signal** moved to |
| 84 | + container terms. The upsert-signal gap (still keyed on `is_branch` after the rest |
| 85 | + moved) was a real review catch: a live-created/relabelled empty OU didn't refresh |
| 86 | + panel 1 until a rescan. Snapshot `was_container` before mutating, like `was_branch`. |
| 87 | +- **`.superpowers/` is now gitignored in edaptor** (it was NOT — a subagent report |
| 88 | + file leaked into a commit; cleaned in the integration merge). Reports are scratch. |
| 89 | +- **carbo deploy recipe:** cargo target dir is redirected to |
| 90 | + `/home/oetiker/scratch/cargo-target` (NOT `./target`) — use `cargo metadata` to |
| 91 | + find the binary. edaptor has **no `--version`**. Build release locally (this box |
| 92 | + glibc 2.39 → carbo Ubuntu 26.04 glibc 2.43; older→newer is safe), `scp` to |
| 93 | + `ds-carbo-feh-adm:/tmp`, `ssh -t ds-carbo-feh-adm` (passwordless sudo) → back up |
| 94 | + `/usr/local/bin/edaptor` to a dated `.bak`, then `install -m0755`. Config: |
| 95 | + `/etc/edaptor/ds-carbo-feh.toml`. |
| 96 | + |
| 97 | +## 5. Don'ts & constraints |
| 98 | +- **≤ 4 cores** for all compiles/tests (shared 128-core box). Never two building |
| 99 | + subagents at once. `podman` not docker; `pnpm` not npm. |
| 100 | +- **Confirm before ssh / carbo redeploy** — production, outward-facing; back up the |
| 101 | + old binary first. |
| 102 | +- **Don't push/merge/redeploy without the user's explicit go-ahead.** |
| 103 | +- **Settled — do not relitigate:** container rule = objectClass set OR has-children; |
| 104 | + panel 2 shows sub-containers (▸) + leaves; a sub-container row **opens its entry** |
| 105 | + (navigate-on-Enter is a *future option*, not a bug); `MaskedInput` reveal is |
| 106 | + **non-sticky** (Space = 1 s peek); eye glyphs are **theme-controlled**; the masked |
| 107 | + clipboard block covers the revealed state too. |
| 108 | +- Don't reintroduce the removed `[profile.picker.<attr>]` / `[profile.password]` |
| 109 | + config layers. |
| 110 | + |
| 111 | +## 6. Where the detail lives |
| 112 | +- Change history: `git log 70eb910..HEAD` on `integrate/2026-07` (the whole batch). |
| 113 | +- **edaptor PR #6** (oposs/edaptor#6); **tvision PR #21** (oposs... `oetiker/tvision-rs#21`, MERGED). |
| 114 | +- Subagent reports (gitignored scratch): `.superpowers/adopt-masked-input-report.md`, |
| 115 | + `.superpowers/launch-scroll-report.md`, `.superpowers/container-classification-report.md`. |
| 116 | +- Key files: `src/ui/pw_editor.rs` (MaskedInput adoption), `src/ui/panes/form.rs` + |
| 117 | + `src/ui/scroll_group.rs` (`scroll_block_edge` launch-block scroll), |
| 118 | + `src/workflows/structure.rs` (`is_container`, `upsert` tree-dirty signal), |
| 119 | + `src/ui/panes/leaf.rs` + `src/workflows/labels.rs` + `leaf_search.rs` (panel-2 |
| 120 | + children incl. sub-containers). |
| 121 | + |
| 122 | +## 7. Open questions / pending decisions |
| 123 | +- Merge PR #6 + redeploy carbo — pending user. |
| 124 | +- **Sub-container navigate-on-Enter**: selecting a sub-container row currently opens |
| 125 | + its entry (like `‹self›`). Navigate-the-tree-into-it was considered and left out |
| 126 | + (ambiguous UX, not requested). Revisit if the user wants folder-style nav. |
| 127 | +- **Two tangential shuttle bugs** found during investigation, deliberately NOT fixed: |
| 128 | + (1) list scrollbar step goes stale after a dialog resize (framework: `Group`/ |
| 129 | + `Window::on_bounds_changed` doesn't cascade to children); (2) the Available list |
| 130 | + resets to the top on every refresh/move (`new_list` always `reset_focus=true`). |
| 131 | + |
| 132 | +## 8. Staleness watch |
| 133 | +- PR #6 may already be merged — check `gh pr view 6`; if merged, `integrate/2026-07` |
| 134 | + and the four sibling branches may be deleted. |
| 135 | +- carbo may already be redeployed — verify (no `--version`; check the `.bak` date |
| 136 | + on `ds-carbo-feh-adm`). |
| 137 | +- `main` was clean at handoff; re-check `git log main` and `git status`. |
| 138 | +- This file was written on the integration branch; after PR #6 merges, `main`'s |
| 139 | + handoff IS this file — keep it honest or rewrite to the merged reality. |
0 commit comments