Skip to content

feat(mecatui): modernize /connect with a bounded surface - #2617

Open
jbeda wants to merge 1 commit into
mainfrom
impl/2176-connect-surface
Open

jbeda wants to merge 1 commit into
mainfrom
impl/2176-connect-surface

Conversation

@jbeda

@jbeda jbeda commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Move /connect onto the shared surface lifecycle with a bounded multiline saved-target list, stable target identity, and standard picker styling/navigation.
  • Pin confirmation to the exact target/action; Escape returns to the preserved list and a second Enter confirms. Wheel scrolling changes the window without selecting; click-to-select remains deferred to mecatui: click-to-select a row in the TUI picker/overlay lists (skills, models, agents, MCP, schedule, etc.) #769.
  • Preserve authentication/recovery policy and host restart semantics across normal opening, recovery-only startup, initial authentication failures, and live authentication recovery. Reject stale results and close replaced modal lifetimes.
  • Add routed regression coverage for geometry, selection reveal, wrapped styling, confirmation, wheel isolation, and exactly-once recovery inventory loading.

Closes #2176.
Relates to #1742, #1589, and #2237. This is one focused slice, not completion of either umbrella.

API compatibility

UI-local ownership, geometry, and confirmation corrections only. No wire protocol or remote-observable server semantics change; no API major, feature identifier, exported Go interface, or stored-data change. Existing target-bound recovery/resume and host authentication policy are unchanged.

Verification

  • task test
  • task test:race
  • task lint
  • task build
  • go run ./cmd/mecademo — verified tool call, permission ask/approval, and result
  • Independent Spec, Standards, Test adequacy, and Domain panel review, with scoped rechecks: no unresolved findings

The first full run exposed a geometry-less host test fixture, now fixed. An intermediate full run hit an unrelated daemon readiness timeout; its focused rerun and the final full/race suites passed without daemon changes.

Manual UI verification has not been performed. No tracked documentation changes: the interaction corrections are explained by the UI hints.

Implementation plan

Full implementation plan archive. Planned verification in that archive is distinct from the completed checks above.

Move saved-target browsing onto the shared surface lifecycle and bounded multiline list. Pin confirmation to the selected restart intent, preserve list state on Back, and isolate wheel input.

Co-authored-by: Mecatl <noreply@mecatl.dev>
@jbeda

jbeda commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Implementation plan archive — /connect modernization

  • Slug: 2176-connect-surface
  • Source plan path (local provenance): .scratch/2176-connect-surface-plan.md
  • Implementation branch: impl/2176-connect-surface
  • Base branch: main; recorded start commit: d1e29fc4129a1be0b921e22d6864f28eb020f7c7
  • Published implementation head: 39dfc6bcf4bf0000f6eb134e03ceb09c726d2e78
  • Archive timestamp: 2026-10-29T22:35:52Z
  • SHA-256 of the complete plan portion below: 7585185fd24edfa79cc2907f63767e9712be4788e5088893ea5c00277518eee4

This is the full approved planning record, including its pre-implementation wording. Verification commands below describe planned checks; completed checks and review outcomes are reported separately in the PR description. Creation of this PR was subsequently authorized. No plan content was omitted for publication.


/connect surface and bounded picker

Slug: 2176-connect-surface
Implementation branch: impl/2176-connect-surface
Base branch: main

Goal and scope

Modernize the saved remote-target picker (#2176), coordinating bounded adoption (#1742/#1589) and shared surface ownership (#2237). Keep a centered focused picker, not a fill management browser. Match existing worktrees/effort UI conventions rather than inventing another interaction pattern.

User decisions: confirmation pins the chosen target/action; Escape returns to the unchanged list; a second Enter commits. Wheel scrolls without selecting. Click-to-select remains deferred to #769. Align existing shared UI behavior and styling.

No new connection/authentication policy, credential handling, target persistence, filtering, refresh command, browser launch, or transport/API changes. Keep existing target ordering and startup/live recovery behavior. Existing AddTarget is an instructional restart (main.go:765), not an actual in-app enrollment flow; preserve it. Earlier conversational wording suggesting direct OIDC enrollment was imprecise.

Current behavior and approach

cmd/mecatui/ui/connect.go keeps open/loading/confirmation/error state and an index cursor in Model, handles input/results separately, and renders every wrapped target without a height budget. Confirmation currently permits cursor changes and Escape closes despite saying Back. Model owns routing in model.go, update.go, view.go, selection.go. The public ConnectRestartIntent is consumed by main after teardown.

Reuse worktrees.go for owner/request-correlated asynchronous loading, copied confirmation, Escape-back and wheel isolation; reuse effort.go for content-measured capped card dimensions, fresh geometry preparation and compact fallback. Use bounded.List, existing navigation/wheel helpers and presentListRow; no new shared widget/disabled-row API.

Ownership and lifetime

  • Surface owns list/loading/safe errors, recovery display context, copied confirmation, request validation/cancellation and geometry caches. Capture collaborators/theme/keys at open.
  • Parent owns one modal lifetime, framing/placement, input coordinate mapping, recovery lifecycle and final restart effect. Replace an existing modal via closeModal, not direct overwrite.
  • Keep ConnectRestartIntent and action/resume semantics unchanged. Transfer a narrow sealed surface intent to root; root records it and returns tea.Quit, stopping outgoing close/refocus processing.
  • Root live-auth recovery continues settling lifecycle hooks, ending runs and disarming readers. Pass failed target/reason/session candidate into construction rather than keeping dormant picker state on Model.
  • Recovery-only New/Init must work with no Session collaborator: install the surface in New and schedule exactly one metadata load in Init without falling through to session creation. Handle normal command open, post-session-ready ConnectOpen, initial connection auth failure and live auth recovery through the same constructor/open seam.
  • Surface-instance plus active-request identity rejects old/duplicate loads, including after close/reopen. Cancellation alone is insufficient. Unrelated messages pass to root. Do not introduce refresh UI merely to exercise identity.

Browsing and presentation

  • Each saved target is one multiline item keyed by raw target identity; sign-in is a distinct collision-free synthetic identity. Keep display sanitization separate from action identity. Apply terminaltext sanitization and tab expansion before geometry; use standard selected/unselected styles and gutter helper.
  • Preserve restrictions: only the target matching failedTarget when reason == AuthRejected is excluded from actionable selection and remains visibly explained; no AddTarget row for AuthRejected. Other failed-target reasons retain existing Reauthenticate or RetryAfterCleanup actions and target-bound resume semantics. Keep unavailable entries outside actionable selection as needed locally, with no new bounded API. Only actual displayed enabled choices can produce intents; do not preserve the test-only invisible synthetic-row loophole in connect_test.go:99.
  • List failure shows only the existing safe unavailable message, suppresses sign-in and blocks activation. Loading, empty inventory and auth hints retain honest actions. Empty successful inventory may offer the existing new-target instructional action when allowed.
  • Use remappable shared navigation, physical-height paging/top/end and cursor reveal. Wheel changes window only, not cursor; subsequent keyboard navigation reveals selection. Return nil clickable regions; pointer input cannot activate or reach underlying content.
  • Measure wrapped title/warnings/auth hints/footer/frame before list capacity. Use stable dimensions across selection/window changes at a fixed offer; content-measured width capped at shared 128-cell maximum. Budget overflow indicators and blank spacing, dropping decoration before useful content. Respect width and height including styled frames, long multiline/tab/control-bearing metadata.
  • Zero/tiny offers are close-only with no hidden activation; preserve list state for restoration. Prepare geometry before input through shared modal dispatch; resizing before Enter must not use stale usability.
  • Confirmation is a separate view storing the exact copied ConnectRestartIntent. Show the selected target and specific action; navigation/wheel cannot mutate the pending action or originating list. Escape returns to selection/window unchanged; next Escape closes. Apply compact safety to confirmation too. A confirmed response cannot silently change the pending choice.

Observable acceptance

  • Many wrapped saved targets remain bounded; every enabled target and permitted sign-in item is keyboard reachable; paging/top/end reveal selection. Target identity survives reordered item reconciliation without being replaced by index identity.
  • Narrow/short and resize-before-input offers stay bounded and cannot activate hidden choices; larger offers restore browsing state. Auth/error chrome does not displace actionable content outside the offer.
  • First Enter only opens confirmation for the exact target/action; arrows/wheel do not change it; Escape returns with list selection/window retained; second Enter emits exactly that intent and quits through root routing.
  • Wheel changes list offset without changing selection or underlying transcript; clicks do not select/activate. Loading/compact states swallow relevant input while unrelated lifecycle messages still reach root.
  • Closed/replaced/reopened surfaces reject late and duplicate load results. Idle live-auth recovery closes the previous modal and clears its hits. Running/approval recovery preserves existing cancellation/hook/reader behavior.
  • Recovery-only startup requires no Session and issues one list request. Existing target-bound resume candidate/action matrix, rejected auth, storage failure, sanitized error and no-browser guarantees remain covered through UI and host restart boundaries.

Implementation tasks

  1. Scenario coverage and reusable fixture setup. Add focused routed acceptance cases for constrained geometry, confirmation pinning/Back, wheel isolation and stale loads; give geometry-less connect fixtures real WindowSize offers. Preserve main/UI action and recovery tests. Completion: regression cases demonstrate old failures without relying on source-text or arbitrary prose matching.
  2. Surface/list/confirmation and root integration (one coherent change). Implement above ownership and geometry using existing patterns, wire all startup/recovery entries and sealed restart intent, remove legacy connect state/key/result/render routes and redundant open predicates. Preserve public interfaces and host policy; migrate affected test fixtures/assertions to actual modal routing. Add ownership guard alongside existing surface architecture checks. Completion: focused connect, lifecycle, UI and bounded tests pass, with no legacy parallel ownership path.
  3. Integration and review. Run project gates, inspect whole diff and complete independent panel review against the implementation branch start commit. Apply necessary fixes and focused regression rechecks; do not rebase unless requested. Update user docs only if the touched behavior adds information beyond existing UI hints; if needed activate tech-writer/read user-docs guidance and run task docs. No architecture edit unless a stable claim becomes false.

Verification and delivery

Integrator owns full suites. Iterate with focused connect/UI/bounded and direct host boundary tests, then task test once for integrated change set (1800s). Before PR-ready: task lint (600s), task test:race (1800s), task build (600s), and go run ./cmd/mecademo (600s), verifying tool call, permission ask/approval and result. Run task docs (600s) for any Markdown change. Tests offline with test-owned stores/UserModelDir/environment, no operator credentials or real login/network.

API compatibility: UI-local ownership, geometry and confirmation correction only. No wire protocol/remote semantics, feature identifier, API major, exported Go interface or stored-data changes planned. Keep host action/resume contracts unchanged; reassess if implementation crosses that boundary. Include this assessment in any eventual PR.

No commit, push or PR without authorization. Initial panel review compares to the recorded implementation start OID (capture at branch creation; not invented here). If a PR is later authorized, retain a publishable full plan in one identifiable PR conversation comment using plan-implement's archival/readback procedure; keep comment metadata outside the plan copy. No automatic merge.

Decisions and risks

The two user-facing choices are settled. UI patterns exist; no new framework is needed. Main risks are recovery-only Init, modal replacement during idle auth failure, geometry-less tests, and retaining sanitized rendering while replacing helpers. Raw metadata and identity must not be conflated. Fresh-context static review found one important wording contradiction: the original restriction could disable non-rejected recovery actions. Corrected it to the exact AuthRejected + failedTarget condition, preserving the existing action matrix. No other design gaps were found. Review did not run tests or verify runtime behavior; no implementation has started.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mecatui: evaluate bounded /connect target picker

1 participant