Skip to content

feat(ios): Reve/Sense/Inspect tabs, mood, devices, deep-links, Face ID, widget polish, tests#128

Closed
oratis wants to merge 12 commits into
feat/ios-qr-and-widgetfrom
feat/ios-companion-followups
Closed

feat(ios): Reve/Sense/Inspect tabs, mood, devices, deep-links, Face ID, widget polish, tests#128
oratis wants to merge 12 commits into
feat/ios-qr-and-widgetfrom
feat/ios-companion-followups

Conversation

@oratis

@oratis oratis commented Jun 19, 2026

Copy link
Copy Markdown
Owner

The rest of the non-blocked iOS companion backlog (plan §G5/§G6, Appendix B). Stacked on #124 (QR scan + home Widget) — review/merge that first, or this can be retargeted to main once #124 lands. Every commit is build-verified with ./build.sh.

New surfaces

  • Reve tab — "while you were away" + current desire, agent-activity recap (2h/8h/24h), dismissable advisor suggestions.
  • Sense tab — ambient-signal consent (revoke-only from the phone; granting stays a Mac action per §7.2) + recent events.
  • Inspect Lisa (Settings) — read-only Soul / Memory / Skills / Tools.
  • Paired devices (Settings) — per-device tokens; read-only (revoke is loopback-only on the server).
  • Mood indicator in Chat — live via the mood SSE (a stand-in for portrait art, which needs an asset catalog — noted).
  • Dispatch ledger — Lisa's fire-and-forget runs + per-entry log tail, from the Dispatch toolbar.

Robustness / system

  • SSE auto-reconnect with exponential backoff (1→30s) + full resync, and a foreground resync on scenePhase.
  • Widget polish — lock-screen accessory families + .widgetURL tap target; registers the lisapocket:// URL scheme.
  • Deep-links.onOpenURL routes lisapocket://roster (widget) and lisapocket://session?agent=&id= (the ntfy push Click from feat(cli+push): Mac pairing QR (lisa pair) + ntfy deep-links #127) to the right session.
  • Face ID / passcode lock — optional gate over the token (LocalAuthentication); locks at launch + on backgrounding.

Tests

  • A hosted XCTest target (LisaPocketTests) + extracted pure helpers (rosterCounts, AppState.parseDeepLink); covers bucketing, sort ranking, and deep-link parsing. ./build.sh test8 tests, 0 failures on the iOS 17 simulator.

Verification

./build.sh → BUILD SUCCEEDED after every commit; ./build.sh test → TEST SUCCEEDED. Camera / Face ID usage strings, the App Group, and the URL scheme are confirmed present in the built Info.plist.

Honest limits

APNs real-device push + Live-Activity remote refresh (Apple push key) and a real mood portrait (asset catalog) remain follow-ups; the App Group's runtime data only flows on a signed build (compile-verified on the Simulator).

🤖 Generated with Claude Code

oratis and others added 12 commits June 19, 2026 15:04
Settings → "Inspect Lisa" links to four GET-only screens (plan §G6,
Appendix B): Soul (identity / purpose / constitution + mood bars + values /
opinions / desires), Memory (user + memory text), Skills, Tools. A small
AsyncContent loader renders loading / error / content uniformly so each view
stays a thin wrapper over its fetch. Models mirror src/soul/types.ts and
/api/{soul,memory,skills,tools}, lenient (all-optional) so a missing field
never breaks decode.

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New tab (plan Appendix B): a "while you were away" idle note + current desire
(from /api/island/ping), an agent-activity recap over a 2h/8h/24h window
(/api/agents/recap), and advisor suggestions (/api/advisor/latest) that can be
dismissed — POST /api/advisor/dismiss feeds the server's down-weighting loop.
Loader re-runs when the window changes or pairing flips on.

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New tab (plan §G6): per-signal consent state from /api/consent and the last
ambient events from /api/sense/recent. Revoke-only by design — tightening
consent is safe from anywhere, but granting a sensitive signal remotely would
widen the surface, which the privacy floor (§7.2) keeps a Mac action; the
footer points grants at the Mac. Revoke / revoke-all reload the list.

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A mood chip above the chat transcript, seeded from /api/island/ping and kept
live via the `mood` SSE event. Honest scope: this wires the mood *data* (the
unblocked part); it's a stand-in for the mood *portrait* — bundling the
mac-client's portrait art is a follow-up (the iOS app has no asset catalog yet).

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Settings → "Paired devices" lists each per-device token (/api/devices) with
platform + last-seen. Read-only on the phone: revoking is loopback-only on the
server (a Mac-owner action), so the footer points there rather than offering a
button that would 403.

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Dispatch tab's toolbar opens Lisa's own fire-and-forget dispatches (the
ledger, distinct from the observed-agent roster): pid / task / alive from
/api/dispatch/list, and a captured log tail per entry from
/api/dispatch/status. The status endpoint is policy-gated, so a 403/404 just
leaves the tail empty.

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The roster stream now reconnects on drop with exponential backoff (1→30s cap),
doing a full /api/agents/sessions resync before each retry so transitions during
the gap aren't lost; healthy traffic resets the backoff. On return to foreground
(scenePhase → .active) it resyncs and reconnects, since iOS suspends SSE in the
background. Previously a dropped stream just went silent until the next appear.

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AgentCountWidget now also supports the lock-screen accessory families
(.accessoryInline "▶ N active · ⏸ M stuck", .accessoryRectangular) alongside
systemSmall/Medium, and the whole widget is a tap target via
.widgetURL(lisapocket://roster). Registers the `lisapocket://` URL scheme
(CFBundleURLTypes) — the app's deep-link handler (next commit) routes it; the
same scheme backs the ntfy push Click URL.

Verified: ./build.sh -> BUILD SUCCEEDED; CFBundleURLTypes present in the built
Info.plist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The app now handles its URL scheme (.onOpenURL): lisapocket://roster selects
the Dispatch tab (the Widget's tap target); lisapocket://session?agent=&id=
(the ntfy push Click) selects Dispatch and opens that session. RosterView drives
a NavigationStack path and resolves the pending session once it's in the roster,
tolerating either arrival order (link before or after the roster loads).

Verified: ./build.sh -> BUILD SUCCEEDED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The device token grants full control of the Mac's agents, so Settings →
Security can require biometric unlock (LocalAuthentication, .deviceOwnerAuth →
Face ID/Touch ID with passcode fallback). When armed + paired, the app locks at
launch and re-locks on backgrounding; a full-screen LockView auto-prompts and
offers a manual retry. If no auth is enrolled, it doesn't trap the user. Adds
NSFaceIDUsageDescription.

Verified: ./build.sh -> BUILD SUCCEEDED; NSFaceIDUsageDescription in the built
Info.plist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a hosted XCTest target (LisaPocketTests) covering the pure logic, and
extracts that logic so it's testable in isolation:
- rosterCounts(_:at:) — the Widget snapshot bucketing (pulled out of
  RosterModel.publishSnapshot).
- AppState.parseDeepLink(_:) -> DeepLinkRoute — the lisapocket:// parse (pulled
  out of handleDeepLink), nonisolated so it runs off the main actor.
Tests also cover sortRows ranking. build.sh gains a `test` action
(`./build.sh test`) and project.yml a LisaPocket scheme wiring the test target.

Verified: ./build.sh test -> Executed 8 tests, 0 failures (TEST SUCCEEDED) on
the iOS 17 simulator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reve / Sense tabs, mood indicator, Inspect Lisa, paired devices, Face ID lock,
dispatch ledger, SSE auto-reconnect, widget accessory families + deep-links, and
`./build.sh test`. Notes the remaining follow-ups (APNs Live-Activity refresh;
real mood portrait art).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@oratis oratis deleted the branch feat/ios-qr-and-widget June 19, 2026 10:52
@oratis oratis closed this Jun 19, 2026
@oratis oratis deleted the feat/ios-companion-followups branch June 19, 2026 10:56
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.

1 participant