6.6 KiB
Compact Menu and Session Indicator Implementation Plan
Intent: Implement the approved compact macOS menu mockup with Yandex Key import and a small configurable session indicator.
Current Behavior: MenuBarExtra uses a plain menu, shield plus H:MM, dense module diagnostics and a login Terminal action. CLI stores started_at but does not expose it to events/status and resets it on repeated expiry lines.
Expected Outcome: A native 298-point popover matching the approved layout; import/login actions, preserved connection actions, a persisted ring/clock/time preference (16-point ring default), exact remaining time in menu, elapsed fill and green/amber/red urgency.
Target-Perspective Output: Synthetic CLI transcript produces stable started_at plus real expiry; compiled presentation checks demonstrate progress/time/color boundaries, reset/legacy handling and correctly quoted Terminal commands. Release app compiles. Native visual acceptance remains explicitly untested because no GUI testing was requested.
Truth Owner: CLI owns connection start/expiry metadata; Swift session model computes derived presentation. UserDefaults owns the indicator preference. CLI/Keychain still own import and credentials.
Contract Boundary: Add optional started_at to existing events and connected status; preserve all old fields and auth modes. Swift falls back to the first observed connection time with older CLI, never to an assumed duration.
Cutover: Replace the existing plain menu and shield+time connected presentation in place with the approved popover and selected indicator. Reuse the Terminal launcher for import; no second credential UI.
Displaced Path: Unconditional shield+time becomes a selected style; dense healthy module details leave the default surface, actionable setup/tunnel warnings remain visible. No new VPN connection/reconnect logic.
Value Density: Native SwiftUI window menu, AppStorage and AppKit image drawing; no dependencies, app installation or platform rewrite.
Acceptance Evidence: Automated event/status transcript, pure Foundation session checks, generated Zsh script execution against a fake CLI, existing regression suites and Swift build.
Evidence Lane: Automated only. No app/Terminal launch, GUI, screenshot, live VPN, real Keychain or installation.
Kill Criteria: No fixed 8h duration, repeated timing events cannot reset fill, disabled/disconnected states cannot show stale time, imported secret never enters Swift, new sources ship in installer, existing auth/cancel/copy/log/quit actions remain reachable.
Architecture Slice: bin/vpn-lemanapro.sh -> VPNEvent -> VPNManager.swift -> new Foundation SessionTiming.swift -> LemanaVPNApp.swift; installer explicitly downloads the new source; focused tests and README describe behavior. Existing dirty work is preserved.
Plan Review Gate: Requires PRE review before execution.
Tasks
- Root: persist one start timestamp per
_filter_outputconnection, include it in connected/session_info/status output, test repeated expiry lines and status round-trip with fake tools. No SSO/DNS/reconnect changes. - Root: add session timing model, optional event timestamp, reset on lifecycle transitions, update presentation once per second, preserve legacy timestamp fallback; fix JSON line buffering if required to deliver the new contract through real pipe chunks. Test timing independently and via decoded event fixtures.
- Root: approved native popover, 16px ring/clock and compact time option, persisted preference, localized accessible status, visible setup/tunnel failures, all existing actions. Reuse Terminal script builder for import and login, fixing the existing read-only Zsh
statusvariable and using per-action success text; setup launch failure must not replace an active VPN state. - Root: update source packaging, README and narrow smoke wiring expectations; run automated checks and POST correctness/maintainability review. Review agents are read-only; implementation stays with root.
Behavior details
- Fill = clamp((now - started_at) / (expires - started_at), 0...1) for valid positive duration. Unknown/invalid expiry uses a normal connection symbol and “Срок сессии неизвестен”.
- Remaining >60min green; <=60min amber; <=15min red, including expiry. Exact menu text and accessibility labels accompany color. No continuous decorative spinner.
- Expiry alone does not start another session or pretend the process ended; keep disconnect while the process is still connected. Disconnected/reconnecting/error use distinct system symbols and no stale countdown.
- Start/end reset on an actual new connection attempt and terminal states; repeated connected/session_info data in one attempt retains its origin. RefreshStatus remains a module refresh, without attaching to external VPN processes.
- Approved reference:
/Users/dokril/.codex/visualizations/2026/10/07/01a117e8-cae9-7f02-9dc7-c177c368a223/vpn-menu.html. Mock-specific demonstration controls are not shipped.
Completed implementation and evidence
- Native
.windowmenu, 298 pt layout, AppStorage ring/clock/time preference, 16 pt AppKit ring/clock image, exact remaining-time labels, import/login Terminal actions and all connection/cancel/copy/log/quit paths implemented. Healthy raw module summaries removed; actionable failures remain visible. - CLI event, status file and status response share a stable start timestamp. Foundation timing logic handles observed legacy starts, real duration, clamping, unknown/invalid intervals and 60/15-minute thresholds. Lifecycle transitions reset timing, and the UI updates once per second.
tests/menu.py: PASS. Real shell filter/status transcript with synthetic dates, repeated expiry and reconnect; compiled Swift model, every byte boundary in UTF-8/JSON framing, old CLI sole manual-full action; generated Terminal scripts executed in Zsh against a fake quoted-path CLI for success and exit 7. No Terminal application launched.tests/smoke.sh,tests/logging.py,tests/configuration.py(10 checks),tests/yandex_import.py, shell syntax andgit diff --check: PASS.- Final
swift build -c release --package-path app: PASS (3.07 s). One restricted attempt was blocked by the compiler cache sandbox; an approved build escalation succeeded. - PRE and POST correctness/maintainability reviews completed. Fixed the sole legacy-action regression and made invalid timing consistently unknown; removed newly unused statusBarTitle/moduleSummary/summary paths.
- Native UI is implemented but unproven visually: no GUI, screenshots, real Terminal/import, installation or live VPN acceptance was run. Existing unrelated dirty changes remain intact.