Commit Graph
48 Commits
Author SHA1 Message Date
abkslmandNucleic 8d6560496e nvrsion: Add: Fix Xcode build error when running ‘make ios-release-canary’, update ios/VERSION, and refactor session cache implementation for iOS.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 22:38:11 -07:00
abkslmandNucleic e06f0c78f5 nvrsion: Add: Block Sleep feature, forward approval to phone in settings, refactor sync message handling, and update related files.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 22:20:28 -07:00
abkslmandNucleic d67f21b8d6 nvrsion: Add: Fix tab bar animation delay when exiting session chat to ensure immediate slide-up (Models, AdaptiveRootView, SessionsView), update iOS settings to replace “Join a Mac’s group” with “Join an Orbital mesh” and add QR code button for device grouping.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 22:09:25 -07:00
abkslmandNucleic 60cec6d70e Fetch Historical Transcript Data
Nucleic-Session: 11465D90-A8F3-43AA-A1DC-37EA1EC1B909
Co-authored-by: Nucleic <[email protected]>
2026-07-05 19:28:00 -07:00
abkslmandNucleic aecf180a68 Fetch Historical Transcript Data
Nucleic-Session: 11465D90-A8F3-43AA-A1DC-37EA1EC1B909
Co-authored-by: Nucleic <[email protected]>
2026-07-05 19:27:51 -07:00
abkslmandNucleic 2cc7eeb2b1 Fetch Historical Transcript Data
Nucleic-Session: 11465D90-A8F3-43AA-A1DC-37EA1EC1B909
Co-authored-by: Nucleic <[email protected]>
2026-07-05 19:27:45 -07:00
abkslmandNucleic 10e4e27a1a Fetch Historical Transcript Data
Nucleic-Session: 11465D90-A8F3-43AA-A1DC-37EA1EC1B909
Co-authored-by: Nucleic <[email protected]>
2026-07-05 19:27:31 -07:00
abkslmandNucleic eff6127773 Fetch Historical Transcript Data
Nucleic-Session: 11465D90-A8F3-43AA-A1DC-37EA1EC1B909
Co-authored-by: Nucleic <[email protected]>
2026-07-05 19:27:21 -07:00
abkslmandNucleic dabf0cf64a Fetch Historical Transcript Data
Nucleic-Session: 11465D90-A8F3-43AA-A1DC-37EA1EC1B909
Co-authored-by: Nucleic <[email protected]>
2026-07-05 19:27:15 -07:00
abkslmandNucleic a3bb7e5d61 Fetch Historical Transcript Data
Nucleic-Session: 11465D90-A8F3-43AA-A1DC-37EA1EC1B909
Co-authored-by: Nucleic <[email protected]>
2026-07-05 19:27:08 -07:00
abkslmandNucleic e5bc88113a nvrsion: Add shortened 8-char device resolution text to Approval.swift, refactor Session Sync Repair files, and update SyncClient.swift and RemoteStore.swift.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 04:35:47 -07:00
abkslmandNucleic 5168198656 nvrsion: Add: Reconnect Icon Update, fix mesh iPhone discovery for new Macs, fix session order adjustment to pin sidebar sessions for remote projects, fix session status synchronization for remote viewer Macs, fix session order adjustment for iOS app session list, fix session status synchronization for remote sessions, fix iOS app live activity height by removing the “<n> files changed” line, fix Tool Call Card Mesh so AI bash summaries transit the mesh layer to other Macs, replace “Resolved by mac-ui” in transcripts with device ID, fix Chat: Print Wait Message, fix Chat: Reconnect Icon Update, fix Chat: Activity Theme Adjustment for “attention needed” live activity theme.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 04:24:25 -07:00
abkslmandNucleic 4cc54cb18f nvrsion: Add: Unified session/project representation across all hosts in iOS app, refactor views and models to remove selector dependency.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 02:03:17 -07:00
abkslmandNucleic 8b37b8576f nvrsion: Add: Chat: Chat View Refinement to modernize session chat interface by removing vertical space for selectors, Fix: Chat: Remove Face ID Permission Text, Refactor: Chat: Live Activity Content Density to open app directly into waiting sessions when tapped, Refactor: Chat: Live Activity Content Density to refresh live activity info significantly faster, Fix: Chat: Live Activity Content Density to refresh live activity info significantly faster, Fix: Chat: Live Activity Content Density to refresh live activity info significantly faster, Fix: Chat: Network Type Transition Optimization to reduce delay during network type switches, Feature Enhancement Planning: All devices in a mesh group should see and be aware of each other; “pair” reframed into “join”; scanning, Fix: Chat: Session Transcript Sync to show only current frame’s transcript when opening a session on another Mac, Build: Push-update path to stay fresh while locked.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 01:00:03 -07:00
abkslmandNucleic 368978bea8 nvrsion: Add: Global Icon For Remote Projects to display globe icon next to remote projects in sidebar, matching local projects in appearance, Fix: Chat: Remove Face ID Permission Text, Refactor: Chat: Live Activity Content Density to open app directly into waiting sessions when tapped, Refactor: Chat: Live Activity Content Density to refresh live activity info significantly faster, Refactor: Chat: Live Activity Content Density to refresh live activity info significantly faster, Fix: Chat: Live Activity Content Density to refresh live activity info significantly faster, Fix: Chat: Network Type Transition Optimization to reduce delay during network type switches, Feature Enhancement Planning: All devices in a mesh group should see and be aware of each other; “pair” reframed into “join”; scanning
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-05 00:28:21 -07:00
abkslmandClaude Fable 5 3e25ef9559 feat(relay): wire the live Nucleic Private Relay into the desktop + iOS apps (mesh P2 complete)
The relay Worker (nucleic-edge at relay.nucleic.blakeslee.xyz) is deployed, so land the
formerly deploy-gated client side of the data path:

- NucleicProtocol/Sync/RelayTransport.swift: RelayAPI (one base URL for REST + WS,
  membership -> connection token trade), RelayWebSocket (ordered sends, ping keepalive,
  ping-confirmed connect), RelayFrameChannel (client leg, WireFraming inside WS binary,
  presence fail-fast when the room has no host), RelayPresence.
- NucleicCore/Sync/RelayAccess.swift: X25519 PoP enrollment (RelayEnrollment), room
  credential in the login Keychain (separate from the push credential), membership minting
  with re-enroll-on-401.
- NucleicCore/Sync/RelayListener.swift: host SyncListener demuxing the room socket into
  per-tag virtual FrameChannels via RelayEnvelope; presence-driven reaping; backoff redial;
  injectable RelayRoomSocket seam for tests.
- Wire: additive HostMsg.relayMembership(WireRelayMembership) pushed after every hello
  (SyncHost.register -> AppStore mint) so devices paired before the relay adopt it and the
  ~90-day token refreshes on each connect; the pairing QR also carries a bootstrap
  membership so first contact can ride the relay. Old clients ignore the unknown tag.
- AppStore: .relay joins the listener composite behind the Connection-methods checkbox
  (failure degrades to a status row), advertises relayRoomID in PeerAddresses, mints the
  QR bootstrap in beginPairing.
- Desktop UI: the Nucleic Private Relay toggle is enabled (was "coming soon"); the
  LAN-only banner offers it alongside Tailnet.
- iOS: relay is the last dial candidate in HostConnection pair + reconnect (10s handshake
  watchdog); PairedHost persists relayRoomID/relayMembershipToken/relayURL; the
  relayMembership push updates the registry in place; Settings shows Relay in Transports.

Tests: RelayTransportTests, RelayListenerTests, SyncHostTests relay push + QR bootstrap.
Full suite green (783 core + 113 protocol + 2 new); iOS simulator build succeeds. Live
smoke test against the deployed Worker passed end-to-end (PoP enroll, both token tiers,
two-socket frame round-trip through the Room DO with correct envelope tags).

Known limits: host revoke-on-unpair not wired (endpoint is admin-only); PeerClient
(Mac<->Mac) doesn't dial the relay yet.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-04 21:05:01 -07:00
abkslm b587702ff5 Merge claude/keen-shamir-122122 into dev (mesh P3 multiplexer)
The iOS [HostID: HostConnection] multiplexer: connect to all paired Macs at once, mirror the active
one, instant host switching, cross-host badge/Live Activity. Compile + demo-verified; the live
multi-connection path needs a two-Mac test (may be rolled back).

Resolves the RemoteStore.swift overlap with dev's Tailnet-node-name/auth-key change by taking the
multiplexer version (which moved the tailnet config into HostConnection); dev's authKey removal is
re-applied there.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

# Conflicts:
#	ios/NucleicRemote/NucleicRemote/Models/RemoteStore.swift
2026-07-04 18:56:54 -07:00
abkslmandNucleic 0b9afb22f3 nvrsion: Add: Adjust Tailscale Node Name format and remove “Tailscale auth key” setting to enable immediate browser login when toggle is enabled.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-04 18:37:15 -07:00
abkslmandClaude Opus 4.8 a24f113996 Mesh P3: wire RemoteStore onto the [HostID: HostConnection] multiplexer
Completes the multiplexer rewire begun in f335eb4. RemoteStore no longer runs an inline single
connection — it owns `connections: [HostID: HostConnection]` and connects every paired Mac at once,
so all your Macs are live simultaneously.

- reconnect() connects/reconnects all paired hosts (dropping since-unpaired ones); pair() adds a new
  connection and makes it active while the others keep running; unpair() drops the active Mac and
  switches to a remaining one.
- The flat @Published state (connectivity/sessions/dashboard/capabilities/…) mirrors the *active*
  connection via callbacks, so switchHost() is now instant — every Mac is already connected, so it
  just re-points at the target's live state (no reconnect).
- Aggregate concerns merge across hosts: the app-icon badge counts needs-you across ALL Macs, Live
  Activity summarizes all live sessions, and an approval answered from a notification is broadcast to
  every connection (the owner resolves it; the notification carries no hostID yet). Intents (send /
  open / approvals) route to the active connection.

Removed ~450 lines of connection machine from RemoteStore (now in HostConnection); kept
friendlyTransportError/showError/dismissError. Demo bypasses connections (seeds state directly).

Compiles (iOS Simulator BUILD SUCCEEDED) and the demo host switcher is runtime-verified (screenshots:
switching Macs swaps the session list + tab badge). The LIVE multi-connection path — simultaneous
dials, reconnect/teardown, the shared embedded tailnet node — is compile-verified only; it needs two
real Macs to validate.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 18:22:39 -07:00
abkslmandClaude Opus 4.8 4f12b18d63 Mesh P3 (multiplexer, WIP): extract HostConnection engine
Begins the simultaneous multiplexer. HostConnection.swift is the per-host connection engine lifted
out of RemoteStore: one instance owns a single Mac's SyncClient, LAN→tailnet candidate chain,
reconnect backoff, tailnet-node lifecycle, and event stream, and keeps that Mac's projection
(connectivity, sessions, dashboard, capabilities, meshPeers). It talks back to its owner through a
Callbacks struct so aggregate concerns — the badge, Live Activity, notifications, and the single open
transcript — can be merged across hosts by the coordinator.

Not yet wired: RemoteStore still runs its own inline single-connection machine. The next step swaps
RemoteStore onto a `[HostID: HostConnection]` map (connect all paired Macs, aggregate their sessions,
route intents by host) — the behavior-critical part, to be verified with two real Macs. Compiles
(iOS Simulator BUILD SUCCEEDED); no behavior change yet (the engine is unused until the rewire).

Also: RemoteStore.friendlyTransportError made non-private so the engine shares it.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 17:56:31 -07:00
abkslmandClaude Opus 4.8 7b7e13aa20 Mesh P3: host switcher (switch active Mac) + 2-host demo
The safe, verifiable slice of iOS multi-host: switch which paired Mac the phone views, reusing the
existing (proven) single-connection reconnect — no rewrite of the connection state machine. The
simultaneous [HostID: HostConnection] multiplexer stays deferred (it needs two real Macs to verify).

RemoteStore: an `activeHostID` (the paired Mac the flat projection reflects); `hostChoices` (paired
registry live, mock hosts in demo); `switchHost(to:)` — live re-points via reconnect(), demo swaps
the mock host preserving in-demo edits; `reconnect()`/`pair()` set the active host; `unpair()` now
forgets the *active* Mac and switches to a remaining one if any (identical for a single host). Demo
seeds two mock Macs ("Andrew's Mac", "Studio Mac") with distinct sessions/dashboards.

SessionsView: a host-switcher menu in the toolbar, shown only when >1 Mac.

Verified in the iOS Simulator (demo mode): the switcher lists both Macs, and switching swaps the
whole session projection + the tab badge (screenshots taken). Single-host behavior is unchanged
(one host ⇒ switcher hidden ⇒ flat state exactly as before).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 17:40:00 -07:00
abkslmandClaude Opus 4.8 630380085e Mesh P3 (foundation): iOS paired-host registry + migration
First step of Phase 3 (iOS multi-host), per docs/MESH_TRANSFER.md. Converts IdentityStore from a
single `nucleic.pairedHost` slot to a `nucleic.pairedHosts` registry (ordered [PairedHost], keyed by
fingerprint = hostID), with a one-time migration of the legacy single value on first read (then the
old key is removed) so an upgrade keeps its Mac.

New registry API — pairedHosts(), pairedHost(id:), upsertPairedHost(_:), removePairedHost(id:) — for
the coming RemoteStore multiplexer. A single-host bridge keeps every current caller unchanged and
behavior identical: loadPairedHost() returns the active (most-recently-paired) host, savePairedHost
upserts + makes active, clearPairedHost removes the active. RemoteStore is untouched and still holds
one connection.

Settings gains a "Paired Macs" list (the visible artifact): each registered Mac with its fingerprint,
an "Active" marker, and a per-host remove (removing the active one unpairs the live connection;
removing another just forgets it) — this also gives a removal path now that pairing a new Mac keeps
the old one in the registry instead of overwriting it.

Verified with an iOS Simulator build (BUILD SUCCEEDED). Remaining Phase 3: the RemoteStore
[HostID: HostConnection] multiplexer (simultaneous connections, (hostID, sessionID) keying),
host-qualified notifications/Live Activity, host switcher UI, demo N-hosts, and the two-Mac
tailnet spike.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 17:14:30 -07:00
abkslmandClaude Opus 4.8 ac379a0523 Fix iOS build: handle mesh transfer verbs/events in RemoteStore switches
The P4/P5 mesh work added ClientMsg cases (addressUpdate, transferOffer/Chunk/Commit/Cancel)
and SyncClient.Event cases (transferAccept/Reject/Ready/Committed/ChunkAck) but never updated
the iOS RemoteStore's two exhaustive switches — the demo-simulator ClientMsg handler and the
event handler — so NucleicRemote failed to compile ("switch must be exhaustive"). The app target
isn't built by `swift build`, so this landed unnoticed on the branch.

Both are inert on the phone: it's never a transfer source/destination and demo has no peer Macs,
so the new cases join the passive `break`. Verified with an iOS Simulator build (BUILD SUCCEEDED),
which also confirms this session's moved/arrived-provenance rendering edits compile.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 17:09:20 -07:00
abkslmandClaude Opus 4.8 47d96901a6 Mesh P5: finish transfer feature set — moved/arrived visibility, recovery, bulk hand-off
Completes Phase 5 of the multi-device mesh / session-transfer program (docs/MESH_TRANSFER.md)
except the two-Mac memory-carry spike. All additive + capability-gated; SyncProtocol stays v1.

- Moved-session visibility: additive SessionSummary.movedTo (MovedDestination), decode-defaulted.
  A moved session no longer silently vanishes — the source keeps a read-only "Moved to <Mac>"
  tombstone under Archived (name resolved live from paired Macs), surfaced on relaunch without
  rebuilding a runnable controller, and sent on the wire so phones see it too.

- Relaunch recovery driven from launch (+ on every peer reconnect, single-flight):
  AppStore.recoverInterruptedTransfers clears abandoned pre-tombstone locks, discards orphaned
  inbound staging, and re-drives a tombstoned commit via SessionTransferCoordinator.recoverTombstoned
  (bounded, idempotent; a dest that lost staging leaves the lock, never revives the source).

- Bulk "Hand off active sessions…": transferableSessions + moveSessionsToPeer (sequential, rollup
  error) behind a "Hand off…" button → HandoffSheet checklist in RemoteAccessView.

- Arrived-from provenance (mirror of moved-to): GRDB v24 arrived_from_device_id/arrived_at; the
  importer stamps them at staging; additive SessionSummary.arrivedFrom (ArrivedFrom); a subtle
  "Arrived from <Mac>" marker on the sidebar (live name) + iOS row (host-baked name).

- Stranded-arrival "Activate anyway": the importer persists the staged Session to the staging dir
  at .ready, so a destination that relaunches before commit can recoverableInboundTransfers() and
  activateRecoveredTransfer()/clearInboundStaging(). AppStore surfaces pendingArrivedTransfers with
  activate/discard, shown in a new "Interrupted arrivals" section. (A .ready lock with no manifest
  is now cleared as unrecoverable.)

Tests: +6 core, +2 protocol across WireMessageTests, SessionTransferTests, AppStoreTests,
AppStoreSyncBridgeTests. Full package builds; Swift suites green. iOS NucleicRemote edits reviewed
but not compiled here (separate Xcode target).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 17:02:15 -07:00
abkslmandClaude Opus 4.8 fb7aeffcf0 Fix iOS build: handle ClientMsg.listPeers in the demo simulator
The mesh/session-transfer work added `ClientMsg.listPeers`, but the phone's
demo-mode `demoHandle` switch (which lists its cases explicitly, no default)
was never updated — leaving the NucleicRemote target non-exhaustive and failing
to build (pre-existing on dev, surfaced by the iPad-port merge). Demo mode has
no real mesh peers, so treat `.listPeers` as passive alongside the other
read-only requests.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 00:59:15 -07:00
abkslm 8edd9734bd Merge claude/elastic-haibt-f29115 into dev (iPad remote port: adaptive sidebar+detail shell, Mac-style two-pane diff, structured commit/command transcript cards, keyboard/pointer affordances, camera-free manual pairing) 2026-07-04 00:54:14 -07:00
abkslmandClaude Fable 5 25fb2eb8ad Mesh + session transfer: P1 done, P2 core, P4 foundation
Multi-device mesh + session-transfer program (docs/MESH_TRANSFER.md).

P1 (multi-select connection methods, Mac): SyncTransportSet with legacy
migration; CompositeSyncListener partial-failure tolerant + per-method health;
AppStore listens on all enabled methods; HostInfo.hostID = DeviceIdentity.hostID
(key hash, not display name); Settings 3 method toggles + LAN-only recommendation
banner.

P2 core (relay hardening, security-critical, tested): nucleic-edge relayEnroll.ts
X25519 proof-of-possession enroll + server-side roomId derivation; room.ts
role-routed per-peer forwarding with deviceId-tag envelope + frame cap +
one-host-per-room eviction; host-bearer minting bound to the PoP room. Swift
cross-stack contracts pinned to test vectors: RelayEnrollment (PoP proof),
RelayEnvelope (routing tag), PairingPayload relay fields. Socket transport +
iOS un-gating remain deploy-gated.

P4 foundation (peer model + listPeers, tested): PeerTypes (PeerKind/
PeerCapabilities/PeerSummary); Hello.deviceKind+clientCaps;
WireCapabilities.canListPeers; ClientMsg.listPeers -> HostMsg.peerList;
PairedDevice.kind+capabilities (decode-defaulted); ConnectionHandler records
kind at pairing; SyncHost.connectedDeviceIDs(); AppStore.peerSummaries();
SyncClient .peerList event; iOS RemoteStore.meshPeers. PeerClient + Mac<->Mac
pairing UI remain.

All additive + capability-gated; SyncProtocol.version stays 1; pre-mesh stores
and clients unaffected. Swift 724 core + 91 protocol tests green; edge 50 green.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-04 00:39:30 -07:00
abkslmandClaude Opus 4.8 15ea0f1dd2 iPad: render multi-step / destructive shell pipelines as a step list
The desktop CommandStepsCard breaks a shell pipeline (a rm/git chain) into a
step list with the destructive delete flagged, instead of a raw blob. Rather
than duplicate a parser, deliver that value by reusing the existing, tested
HostCommandSummary + HostCommandBreakdown (a general command parser whose
Invocation already carries a `destructive` flag for rm/rmdir) on the two paths
that still showed raw text:

- ToolCallCard.details: a shell tool whose command has >1 invocation or is
  destructive now renders the compact step breakdown (deletes glyphed/tinted in
  red) with the literal command under "Show command"; simple one-liners keep the
  plain input block.
- ApprovalCardView: the parsed breakdown (with its sudo/deletes risk banner) now
  covers any Bash pipeline / destructive approval, not just host_exec.

Demo transcript gains a destructive cleanup pipeline (rm -rf && git worktree
prune && git branch -D) so the step list is exercisable offline.

Verified in the iPad simulator: the pipeline expands to a step list with
`rm -rf` flagged in red.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 00:38:59 -07:00
abkslmandClaude Opus 4.8 850ee0cd96 iPad: render git commits as a structured card in the transcript
Port the desktop GitBlockCard's commit rendering (+ CommandDisclosure) to the
remote transcript: a Bash `git commit` now shows the commit subject as a
headline and the message body as Markdown, with the raw command one tap away
under "Show command" -- instead of a raw `git commit -F - <<'EOF' ...` blob.

- GitCommitCard.swift: the card + the CommandDisclosure helper (portable
  SwiftUI, mirroring Sources/NucleicApp/{GitBlockCard,CommandDisclosure}).
- GitCommitSummary.parse (in HostCommandSummary.swift) extracts the message
  from -m/--message args or a `-F -` heredoc body, reusing the file-private
  shell Lexer already used for host-exec summaries; skips env prefixes and git
  global flags, and only fires for real `git commit` segments.
- Hooked into ToolCallCard.details, gated to shell tools so non-shell input is
  never misread as a commit.
- Demo transcript gains a git-commit call so the card is exercisable offline.

SummaryCard is intentionally not ported -- it's the host-only Apple-Intelligence
recap, which a remote has no way to generate.

Verified in the iPad simulator (demo mode); parser covered by a standalone test.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 00:21:32 -07:00
abkslmandClaude Opus 4.8 34aa01a724 iPad: adaptive sidebar+detail shell and Mac-style diff (remote Phases 1-2)
Grow the universal NucleicRemote app into a width-adaptive shell so a
regular-width iPad renders the macOS sidebar+detail IA while the iPhone
keeps its TabView -- both over the same RemoteStore projection (one host
authority, N renderers).

Phase 1 (adaptive shell):
- AdaptiveRootView branches on horizontalSizeClass: CompactRootView (the
  existing iPhone TabView, moved verbatim) vs SplitRootView
  (NavigationSplitView) on regular width.
- SplitSidebar: Home/Projects/To-dos/Settings destinations + sessions
  grouped under their projects (attention-sorted), connection chip footer.
- SplitDetail selects a destination or a session; a selected session reuses
  SessionDetailView keyed .id(sessionID) so switching drives open/close.
- RemoteStore.closeOpen(_:) is now id-guarded so a split-view A->B switch
  (onAppear(B) before onDisappear(A)) can't tear down B's fresh subscription.
- IdentityStore.deviceID idiom-tags the prefix (ipad-/iphone-) for new
  installs so the host lists a paired iPad correctly.

Phase 2 (width tuning + diff):
- readableColumn() caps+centers Home and the transcript on wide layouts;
  a no-op at phone/portrait width.
- SessionDiffView switches on available width (GeometryReader): a Mac-style
  two-pane diff (file list + selected file's patch) on wide/landscape, the
  phone stack otherwise. UnifiedPatch splits the combined patch per file.
  Read-only, same wire, no protocol change.
- Demo diff fixture now carries both files' patches.

iPhone layout and behavior unchanged. Builds clean; verified in the iPad
simulator (demo mode).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-03 23:59:29 -07:00
abkslmandClaude Fable 5 e623247117 Merge branch 'claude/vigorous-nash-457773' into dev
Tailscale (Tailnet) sync transport: transport picker in Settings ▸ Remote,
interactive browser login, and LAN↔tailnet fallback.

Semantic merge fixes (both sides compiled alone but not together):
- handleTransportFailure (dev's friendly transport errors) now checks
  connectivity.isLive — .connected gained a transport payload on the branch —
  and stays silent while the branch's connect chain still has candidates to
  try, so a LAN probe failing over to the tailnet doesn't flash a red error.
- Chain-exhaustion paths keep a friendlier transport-level failure message
  when onFailed already surfaced one instead of clobbering it with the
  generic handshake error.

Verified on the merge: swift build + 37 sync/transport/store tests green
(incl. dev's FilePairedDeviceStore + the now-fixed contract tests), iOS
simulator build green.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 16:22:13 -07:00
abkslmandClaude Fable 5 272039cca5 Tailnet: interactive browser login + LAN↔tailnet fallback
Browser login — the auth key is now optional on both platforms. When the
embedded node starts with no key, TailnetNode asks the backend (LocalAPI
backendStatus — IPN state, deliberately not filesystem heuristics: tsnet
writes logs and a machine key on every start, registered or not) whether a
login is needed, triggers login-interactive, surfaces the auth URL through a
new statusStream()/.needsLogin, and waits for Running (4-minute deadline,
generation-fenced against stop/restart). The Mac auto-opens the login page
from Settings ▸ Remote and shows a re-open button; the iPhone auto-opens only
during user-initiated pairing (a background reconnect that suddenly needs a
login must not eject the user to Safari — Settings ▸ Tailscale carries the
link). The remote-access toggle now reflects the in-flight start instead of
snapping off for the whole login window, and toggling off mid-start is
honored at both commit points (before and after host.start).

LAN↔tailnet fallback — picking Tailnet now keeps LAN on too: the host runs
both listeners under a new CompositeSyncListener (merged accept stream; one
child ending doesn't end the rest) and the pairing QR carries both hints.
The phone builds an ordered candidate chain — LAN first (QR hint or Bonjour),
tailnet second — and walks it on pair and reconnect, so a phone that leaves
the Mac's Wi‑Fi rolls over to the tailnet and rolls back when it returns.
A per-channel 4s connect guard (readiness-checking, bound to exactly its
channel) keeps a stale LAN hint from hanging the chain; a stale-client guard
in consume() keeps a replaced client's tail events from advancing it; chain
exhaustion during pairing lands in a terminal failure instead of spinning on
"Connecting…"; routine pre-fallback handshake errors no longer flash the red
error bubble. "Connected · LAN / Tailnet" shows whichever transport won.

Multi-agent review: 11 confirmed findings (incl. the login gate being dead
code via tsnet's eager state-dir writes, and two connect-timeout races), all
fixed and re-verified. Suite green (24 sync-related tests incl. 3 new
CompositeSyncListener tests); macOS + iOS builds clean.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 16:14:45 -07:00
abkslmandClaude Fable 5 94a962bd20 Add Tailscale (Tailnet) as a sync transport between Mac and iPhone
Settings ▸ Remote gains a "Connect via" picker — LAN (default), Tailscale
(tailnet), or Relay (disabled, coming soon). On Tailnet, both devices run an
embedded tsnet node via TailscaleKit (tailscale/libtailscale) and sync frames
flow over the user's tailnet, so the phone can connect from anywhere the
tailnet reaches; Noise E2EE runs above the transport unchanged.

- NucleicTailnet (new target, macOS + iOS): TailnetNode wraps TailscaleKit's
  node lifecycle (auth-key login, generation-fenced start/stop since up() is
  un-cancellable) and drops to the framework's public C API for the data path
  — tailscale_dial/listen/accept hand back full-duplex socketpair fds, wrapped
  by FDFrameChannel (DispatchIO) into the shared FrameChannel seam. The Swift
  wrapper's one-way connection actors can't carry a bidirectional stream.
- Host: TailnetListener adopts SyncListener; startSyncServer is single-flight
  and honors toggle-off/picker changes at the commit point; pairing QRs carry
  transport + tailnet IP/port hints (PairingPayload additive optional fields,
  forward/backward compatible over CBOR).
- iPhone: pair/reconnect dial over whichever transport the pairing recorded;
  Settings gains a Tailscale auth-key field (Keychain, committed on editing
  end); connectivity chip shows "Connected · Tailnet".
- TailscaleKit has no SwiftPM distribution: scripts/build-tailscalekit.sh
  builds a pinned libtailscale commit into an untracked local xcframework;
  Package.swift links it only when present (everything builds without it, the
  picker then reports Tailscale support as not built in), and the script
  clears SwiftPM's content-keyed manifest cache so the toggle is picked up.
- iOS floor 17.0 → 18.1 (TailscaleKit requires the iOS 18 Swift runtime);
  package-app.sh embeds the framework in the .app like Sparkle.

703-test suite: no new failures (the 7 fake-claude/fake-grok staging issues
reproduce identically on an untouched checkout — pre-existing, tracked
separately). New coverage: FDFrameChannel over socketpairs, pairing-payload
version-skew both directions, transport-setting resolution.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 03:50:45 -07:00
abkslmandNucleic 22d0752329 nvrsion: Add fix for QR Code Scan Failure in AppStore, Sync, and Tests; explore connection failures over local network; update Info.plist, Models, and SettingsView.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-03 01:42:40 -07:00
abkslmandNucleic 7b5bdc9d0b nvrsion: Add: Adjust Canary Build Auto-Update to run every hour, fix Keychain permission audit by updating files: ControlAuth.swift, GitHubCredentials.swift, HeartbeatReporter.swift, KeychainOwnedAccess.swift, HostIdentityStore.swift, PushRelayClient.swift, refactor Remote Build Icon to use “bell.and.waves.left.and.right.fill” in ios/NucleicRemote/NucleicRemote/Views/BuildBanner.swift, add a Remote Release Channel Check in AppStore.swift, Sync/ConnectionHandler.swift, Sync/SyncHostBridge.swift, Sync/ReleaseChannel.swift, Sync/SyncClient.swift, Sync/WireMessages.swift, tests/SyncHostTests.swift, tests/SyncTestSupport.swift, and align build number hash in ios/NucleicRemote/NucleicRemote.xcodeproj/project.pbxproj, ios/NucleicRemote/NucleicRemote/Info.plist, ios/NucleicRemote/NucleicRemote/Views/BuildBanner.swift.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-02 21:54:40 -07:00
abkslmandNucleic 07cf01b4e6 nvrsion: Add review feature access for iOS remote: Enable reviewers to test all features in App Store (or TestFlight) reviews by updating BUILD.md, docs/APP_REVIEW_NOTES.md, RemoteStore.swift, RemoteApp.swift, BuildBanner.swift, and SettingsView.swift.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-02 19:26:43 -07:00
abkslmandClaude Fable 5 761dca5f1d ios: notifications, actionable approvals, Live Activity, and the relay push path
Brings the phone's ambient surfaces (UX_IOS §5/§8) to maturity:

- Notification pipeline (NotificationRouter): local notifications for
  approvals and needs-input transitions while backgrounded; low-risk
  approvals are actionable from the banner (Allow requires device
  auth, high-risk must open the app's Face ID gate); taps deep-link
  to the session; app-icon badge = NEEDS YOU count; resolutions
  withdraw the notification (first-responder-wins). The relay's
  content-free approval.pending tickle localizes via
  Localizable.strings.
- Live Activity: new NucleicRemoteWidgets extension target (lock
  screen + Dynamic Island) rendering one aggregate Activity —
  N running / M waiting + the most urgent session — started/updated/
  ended by LiveActivityManager as session state changes.
- Out-of-band push path: nucleic-edge gains POST /v1/push/register
  (admin) so the host can upload tokens for LAN-only pairings; the
  host's new PushRelayClient (config-gated on NUCLEIC_RELAY_URL +
  NUCLEIC_RELAY_ADMIN_SECRET) mirrors Hello.pushToken to the relay
  and wakes non-connected phones when an approval arrives, throttled
  per device. Everything stays off until the relay is provisioned.

Worker tests (23) and Swift suites pass apart from the pre-existing
fixture gaps and nvrsion flake. Simulated APNs delivery is blocked in
this environment (notification auth can't be granted headlessly).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-02 16:06:33 -07:00
abkslmandClaude Fable 5 480b29207f sync: project quota, provider status, todo triage, and full diffs to the phone
Four Mac surfaces the iOS remote couldn't render now ride the wire,
forward-compatibly (decodeIfPresent defaults; unknown HostMsg tags
already decode to .unknown):

- DashboardSnapshot gains `usage` (WireSubscriptionUsage — the Mac's
  5-hour/weekly quota gauges) and `statusFeeds` (WireStatusFeed —
  active provider incidents); the host re-pushes the dashboard when a
  poll changes either.
- WireTodo gains `triage` (raw TriageLevel name); the phone renders
  the Mac's TriageBadge honoring the encouraging/classic label
  setting.
- ClientMsg.fetchDiff / HostMsg.sessionDiff deliver the full worktree
  patch on demand (capped at 512 KB with a truncated flag), gated on
  the new WireCapabilities.canFetchDiff so a new phone never sends it
  to an old host. iOS renders a file list + colored unified patch,
  falling back to the diffstat summary against older hosts.

Round-trip and legacy-decode tests cover the new fields; the full
suite passes apart from the pre-existing fake-backend fixture gaps
and the flaky nvrsion lock-domain test (same failure rate on the
base commit).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-02 16:06:32 -07:00
abkslmandClaude Fable 5 a33ab80313 ios: full feature/appearance parity with the macOS app (client-side)
Port the Mac's MarkdownText renderer, AppPalette (color-vision modes +
night-softening), appearance settings (theme/text size/color vision),
and BuildBanner channel strip to the iPhone remote. Add session-row
attention/unseen-completion washes and marker icons, Discard action,
branch/worktree options in the new-chat composer, and a transient
error bubble for host wire errors. Stamp the build channel via
NucleicChannel in Info.plist (ios-release.sh passes NUCLEIC_CHANNEL,
default beta). Demo mode gains NUCLEIC_DEMO_SESSION and skips the
notifications prompt so UI previews are scriptable.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-02 16:06:32 -07:00
abkslmandNucleic 79a28605b3 nvrsion: Add: Fix truncation of “Run now” button text in New idea composer, add model selector support for Claude Fable 5, refactor Project Workflow Enhancement, add Wait Button Logic, update iOS remote to match Chat Message Queue Display, block sandboxed/controlled agents from running git commands via mcp__nucleic__host_exec using container-level shims, add chat icon animation to bounce when chat is blocked for >60 seconds, add “Cmd+Shift+N” shortcut to open a floating chat composer over the window, remove “Run now” button from “New idea” composer, and shield Git command execution by scanning inside ‘-c’ strings.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-01 19:01:20 -07:00
abkslmandClaude Opus 4.8 8dd26a4483 Merge nvrsion promote (trunk → dev); land iOS host_exec formatting
Concludes an in-progress merge the auto-lander could not finish with
partial commits. Lands the iOS host_exec command breakdown and the
RemoteStore preview-data missing-return fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-27 19:45:56 -07:00
abkslmandNucleic ded07c3692 nvrsion: promote trunk to dev
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-06-27 19:33:40 -07:00
abkslmandNucleic 7459276ec0 Activity Chart Intensity Logic
Nucleic-Session: 76D64900-B16F-45BE-A6CB-E3D2EBC7C73A
Co-authored-by: Nucleic <[email protected]>
2026-06-27 19:15:37 -07:00
NucleicandClaude Opus 4.8 cc97c26256 Signing: rebrand bundle IDs to xyz.blakeslee.* and correct the Team ID
Desktop channels → xyz.blakeslee.nucleic.desktop.{dev,beta,rc,release} (the stable channel keyword is unchanged; only its bundle-id suffix is "release"). iOS remote → xyz.blakeslee.nucleic.remote, including its Keychain account namespaces, the scanner log subsystem, and the coupled APNS topic.

Correct the Apple Developer Team ID to L7UDTQ6F5W across the Xcode project, ExportOptions, the APNS config + tests, and the signing/cloud docs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-26 16:52:14 -07:00
Nucleic 16c7b2cc7e nvrsion: promote trunk to dev
Nucleic-Promote: 1
2026-06-26 16:18:02 -07:00
NucleicandClaude Opus 4.8 e6ef59e830 iOS remote: transcript fidelity + mid-session controls & model catalog
Bring NucleicRemote closer to desktop parity in two areas (the core
sync loop was already at parity — shared protocol, control scope).

Transcript fidelity (iOS): a client-side TranscriptProjection coalesces
streaming text by messageID and folds each tool call's lifecycle
(start/deltas/complete/result/fileChange) into one expandable card —
fixing the duplicate started+completed rows. Adds Markdown bubbles, the
gold Orchestra card for Task/Agent spawns, and the previously-dropped
usage/cost, rate-limit, file-change, turn-boundary and session-started
rows, plus a context-window % header badge.

Mid-session controls + model catalog (protocol/host/iOS): project the
host ModelCatalog over the wire as WireModelCatalog (in Welcome); add 5
control-scope setters (setSessionModel/Effort/Auto/AutoShip/ShipBranch)
backed by the existing AppStore.mutateSession + SessionController hooks;
enrich WireSessionSummary with model/effort/auto/autoShip/shipBranch/
contextInputTokens (all forward-compatible). The composer gains a model
picker and a catalog-driven effort menu (per-backend caps: Codex→xhigh,
Grok→auto), and the session header gains a model/effort/auto/autoship
control bar.

Tests: CBOR round-trips for the new messages, Welcome.modelCatalog, the
new summary fields, forward-compat decode of old bytes, and the setters
reaching the host. Verified in the Simulator (NUCLEIC_DEMO=1).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-21 04:03:53 -07:00
abkslmandClaude Opus 4.8 a70b21cd3e M4+: mirror the desktop design language onto mobile (Home/Projects/To-Dos/Sessions)
Design system: Palette (teal accent + the Mac's categorical status colors, status→color
refined by turn disposition), StatusStyle glyphs/labels, a card surface modifier, app-wide
teal tint.

Fixes the reported bug: Sessions now hides archived chats and uses disposition to split
NEEDS YOU from DONE (a finished-the-work turn no longer shows as needing you). Rows gain
status dots, favorite star, approval-count badge, diffstat; swipe to favorite/archive/delete.

New surfaces, full control-scope parity with the Mac:
- Home: greeting + day-streak flame, GitHub-style activity grid, stat cards, quick to-dos,
  and a start-chat composer (project picker + Auto).
- Projects: per-project session counts → project detail with a scoped composer.
- To-dos: capture, group by project, complete/dispatch/delete.
- Session detail: a control menu (rename, favorite, interrupt, integrate merge/squash/rebase,
  archive, delete).

RemoteStore handles the dashboard event + exposes all control intents; claims control scope.
A NUCLEIC_DEMO/NUCLEIC_TAB env seam seeds mock state for offline UI preview.

iOS app builds for the iOS 27 simulator; Home + Sessions verified rendering via screenshots.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-13 02:20:53 -07:00
abkslmandClaude Opus 4.8 6f24b42004 M4: NucleicRemote iPhone app — SwiftUI client over the shared protocol
A real iOS Xcode app (ios/NucleicRemote) linking the NucleicProtocol SwiftPM
library as a local package. Builds for the iOS 27 simulator and launches to the
pairing screen.

- Transport: NWFrameChannel (NWConnection) + LANDiscovery (Bonjour _nucleic._tcp).
- Engine: drives NucleicProtocol.SyncClient (Noise XXpsk0 pair / IK reconnect,
  hello/welcome, HostMsg→Event stream).
- State: RemoteStore (ObservableObject) — the single on-device projection of host
  state; IdentityStore persists the device identity (Keychain) + pinned host.
- UI (UX_IOS): attention-first SessionsView, SessionDetailView (transcript/diff +
  status-driven action area / composer), ApprovalCardView with Face ID gate on
  high-risk approvals + allow-always menu, PairingScannerView (AVFoundation QR),
  SettingsView, connection chip. Same status glyphs/semantics as the Mac.

Add-iPhone QR display + server start live on the macOS side (follow-up); push /
Live Activity are M5 (needs the relay). gitignore keeps this .xcodeproj despite
the blanket *.xcodeproj rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-13 01:12:55 -07:00