feat(LUKE-55): observe Conductor cloud sessions via a settings-managed API key
Merged
DA
2mo ago+2636+2636+0123456789012345678901234567890123456789-37-37-01234567890123456789
Summary
- Add
ConductorSessionAdapter, Luke's first cloud provider: it walks the public Conductor API (/me→ projects → workspaces → sessions → session status) with GET requests only and feeds each session into the existing observation pipeline. Sessions are labeled by repository (Conductor: luke), following the Codex adapter's precedent that prompt-derived text never reaches an observation. Observation is scoped to workspaces whosecreatorIdmatches the authenticated user. - Add a Settings view in the expanded panel and a
SettingsStore: the Conductor API key is user-specific, so it belongs in the app rather than the environment. It is encrypted at rest via ElectronsafeStorage(Keychain-derived) into a mode-0600settings.json, withCONDUCTOR_API_KEYas an env fallback. The renderer can set or clear the key but never reads it back — it only learns where a key came from. - Session recency is judged from each session's own status
updatedAt, not the workspace'slastActivityAt(which masks per-session recency behind a running sibling), and aworkingstatus is trusted as live server state instead of aging out tounknownmid-turn. Both were caught against live data. - Provider refreshes now run concurrently with per-adapter error isolation, so a stalled network provider can't hold the refresh lock and stop Claude Code/Codex from being observed.
- Under
--fixture, the Conductor adapter is excluded from the adapter list entirely, keeping fixture runs deterministic and credential-free.
Evidence
- Platform-independent checks:
./scripts/check.shgreen — 59 desktop, 31 core, 5 harness tests. Adapter tests cover status mapping, both timestamp bugs above, credential rejection clearing state, transient failures preserving the last snapshot, GET-only enforcement, and that prompt text never reaches an observation. Settings-store tests cover encryption at rest, file mode, decrypt caching, and that no accessor leaks the key. - macOS Electron verification (
./scripts/verify.sh):not runin this (Linux) workspace — covered by CI's macOS job. - Live-data probe: 25 requests (all GET), 12 observations — 1 working, 3 waiting, 8 observed.
Automated visual evidence
Download the deterministic macOS evidence · workflow run
- Commit:
53af8a4f00f52d8b3b52afddfa816f6b0104de12 - Scenario:
smoke - Physical-notch check: not performed by CI
Physical-device evidence
- Screenshot or screen recording:
not attached(the capture pipeline has no way to open the Settings view; a--profile settingscapture flag is a possible follow-up) - Physical-notch check: manually verified by @dastratakos on device — Settings reachable via notch hover → footer, key entry works (panel focus steal), rows appear on save, notch capsule stays put
- Device/display configuration:
not recorded - On-disk check:
settings.jsonconfirmed mode600, key present only as base64 ciphertext, no plaintext match
Notes
- Dev runs (
@luke/desktop) and the packaged app (Luke) derive differentuserDatadirectories, so a key saved viarun.shwon't be visible to a packaged build. Pre-existing Electron behavior;app.setName("Luke")would unify them but relocates existing state, so it's left for a separate decision (andrun.sh's lock-holder lookup would need updating in the same commit). - Follow-up worth a ticket: cache the projects/workspaces layers with a minutes-long TTL — a full re-crawl every 15s is ~25 requests to keep 12 rows current, and only the per-session status changes on that timescale.
- Follow-up worth a ticket:
positiveInteger/nonNegativeNumberand the filesystem-error helpers now exist in several copies across adapters; hoisting them intosidecar-coretouches files outside this PR's scope.
🤖 Generated with Claude Code