Commit Graph
516 Commits
Author SHA1 Message Date
abkslmandNucleic b0b041f268 Network Type Transition Optimization
Nucleic-Session: 40F8C53A-F001-423A-8607-EAE181DBE14B
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:39:36 -07:00
abkslmandNucleic ce4028ffc8 Network Type Transition Optimization
Nucleic-Session: 40F8C53A-F001-423A-8607-EAE181DBE14B
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:39:26 -07:00
abkslmandNucleic f988f660fe Network Type Transition Optimization
Nucleic-Session: 40F8C53A-F001-423A-8607-EAE181DBE14B
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:39:19 -07:00
abkslmandNucleic 36e146f258 Network Type Transition Optimization
Nucleic-Session: 40F8C53A-F001-423A-8607-EAE181DBE14B
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:39:14 -07:00
abkslmandNucleic 030d814ffe Network Type Transition Optimization
Nucleic-Session: 40F8C53A-F001-423A-8607-EAE181DBE14B
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:39:08 -07:00
abkslmandNucleic 63a1c9dd9e Network Type Transition Optimization
Nucleic-Session: 40F8C53A-F001-423A-8607-EAE181DBE14B
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:38:55 -07:00
abkslmandNucleic 6ff0a3fb91 Remove Face ID Permission Text
Nucleic-Session: 3EF7F067-2199-41C2-997C-5546C1A8A5C4
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:31:40 -07:00
abkslmandNucleic fdb346f41a Remove Face ID Permission Text
Nucleic-Session: 3EF7F067-2199-41C2-997C-5546C1A8A5C4
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:31:33 -07:00
abkslmandNucleic 32ef11b000 Remove Face ID Permission Text
Nucleic-Session: 3EF7F067-2199-41C2-997C-5546C1A8A5C4
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:31:24 -07:00
abkslmandNucleic d0b63121d3 Remove Face ID Permission Text
Nucleic-Session: 3EF7F067-2199-41C2-997C-5546C1A8A5C4
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:31:18 -07:00
abkslmandNucleic 8bedbc8d98 Remove Face ID Permission Text
Nucleic-Session: 3EF7F067-2199-41C2-997C-5546C1A8A5C4
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:31:14 -07:00
abkslmandNucleic 92e7c59dd6 Remove Face ID Permission Text
Nucleic-Session: 3EF7F067-2199-41C2-997C-5546C1A8A5C4
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:31:10 -07:00
abkslmandNucleic 693ca29691 Live Activity Content Density
Nucleic-Session: B3643133-163C-4C5D-BE20-1A6B7FB83DA9
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:30:53 -07:00
abkslmandNucleic a45795184e Live Activity Content Density
Nucleic-Session: B3643133-163C-4C5D-BE20-1A6B7FB83DA9
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:30:15 -07:00
abkslmandNucleic 0dda487167 Live Activity Content Density
Nucleic-Session: B3643133-163C-4C5D-BE20-1A6B7FB83DA9
Co-authored-by: Nucleic <[email protected]>
2026-07-04 23:29:52 -07:00
abkslm 0996afc47b release(ios/canary): v0.2.0 (build 6) 2026-07-04 22:49:17 -07:00
abkslm 861ca34f06 Merge branch 'dev' into nucleic/trunk 2026-07-04 22:43:59 -07:00
abkslm 88329de96a Merge branch 'dev' into canary 2026-07-04 22:11:37 -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 f8ce672af6 Merge branch 'dev' into nucleic/trunk 2026-07-04 20:15:01 -07:00
abkslm fad4f23b60 Merge branch 'dev' into canary 2026-07-04 19:14:47 -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
abkslm ce8771fa99 Merge branch 'dev' into nucleic/trunk 2026-07-04 18:37:15 -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
abkslmandNucleic e463489b62 Tailscale Auth Key Logic
Nucleic-Session: 5360EC95-9A29-4CB4-8061-E869AC2BC518
Co-authored-by: Nucleic <[email protected]>
2026-07-04 18:36:00 -07:00
abkslmandNucleic 60c59bc8ba Tailscale Auth Key Logic
Nucleic-Session: 5360EC95-9A29-4CB4-8061-E869AC2BC518
Co-authored-by: Nucleic <[email protected]>
2026-07-04 18:35:49 -07:00
abkslmandNucleic f98c78ec2e Tailscale Auth Key Logic
Nucleic-Session: 5360EC95-9A29-4CB4-8061-E869AC2BC518
Co-authored-by: Nucleic <[email protected]>
2026-07-04 18:35:32 -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
abkslm 420b363d1c Merge branch 'dev' into nucleic/trunk 2026-07-04 18:03:50 -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
abkslm fb6a68fd34 Merge claude/keen-shamir-122122 into dev (mesh P5 finish + P3 host switcher)
Mesh P5 feature-complete except the two-Mac memory-carry spike: moved/arrived-session
visibility, relaunch recovery, bulk hand-off, stranded-arrival activate-anyway. Plus the
iOS build fix (equivalent to dev's f021ef8) and P3 foundation: paired-host registry +
migration and a host switcher (switch active Mac; 2-host demo, simulator-verified).

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

# Conflicts:
#	ios/NucleicRemote/NucleicRemote/Models/RemoteStore.swift
2026-07-04 17:49:10 -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 5b3f006e3b Fix iOS build: handle mesh P5 transfer/peer wire cases on the phone
Mesh P5 (Mac↔Mac session transfer) added ClientMsg cases (addressUpdate,
transferOffer/Chunk/Commit/Cancel) and SyncClient.Event cases (transferAccept,
transferReject, transferReady, transferCommitted, transferChunkAck) but didn't
update the phone's demoHandle / event-handle switches, leaving NucleicRemote
non-exhaustive and failing to build (pre-existing on dev). The phone isn't a
transfer participant, so handle all of them as no-ops. Second time mesh work has
broken the iOS target this way (cf. the listPeers fix).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 15:38:41 -07:00
abkslmandClaude Opus 4.8 8c115e3af1 iPad: lead the dashboard with a "Needs you" section
The Home dashboard only surfaced running sessions and rollup counts, so chats
blocking on you (an approval or your next prompt) were visible only as sidebar
washes. Add a "Needs you" card pinned above the stat cards (amber header +
border) listing sessions where status.needsYou(disposition) holds, each tapping
into its detail. Mirrors the Mac home's "Needs attention" lead. Shared by
iPhone and iPad; hidden when nothing's waiting. Verified in the iPad simulator.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 15:33:25 -07:00
abkslmandClaude Opus 4.8 005776685d iPad: more keyboard shortcuts, a New-chat button, and hover polish
Round out the Mac-parity keyboard/pointer affordances on the iPad split view.

Keyboard shortcuts (hidden buttons in SplitRootView carry them):
- ⌘N — start a new chat: select Home and focus the composer (also defaults the
  project). A one-shot Bool binding threads Home→StartChatComposer and is
  consumed on use, so it grabs focus on ⌘N but never on a later Home appearance
  (handles both "already on Home" via onChange and "switched from a session"
  via onAppear).
- ⌘1–4 — jump to Home / Projects / To-dos / Settings.
- ⌘R — refresh sessions.
- ⌘. — interrupt a running session (Mac's "stop"; otherwise only in the ⋯ menu).

UI:
- A visible "New chat" (compose) button in the sidebar toolbar runs the same
  action, so touch users get the affordance too (gated on control scope).

Hover (pointer):
- .hoverEffect on the transcript tool-call / tool-block rows and the Home
  in-progress session rows (native List rows already hover).

Verified in the iPad simulator: the New-chat action scrolls to + focuses the
composer with the project defaulted. Builds clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 01:18:35 -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 Opus 4.8 f8fdf98517 iPad: hardware keyboard, pointer, and a camera-free pairing path (Phase 3)
Make the remote feel native with a Magic Keyboard / trackpad, and give iPad a
pairing path that survives a blocked camera.

Keyboard shortcuts (mirroring the Mac's .commands):
- Cmd+Return sends in the session follow-up composer and the start-chat composer
  (plain Return stays a newline in the multiline fields).
- On the approval card, Return allows and Esc denies -- the approval bar replaces
  the composer, so Return is unclaimed there. Allow stays gated on the biometric.

Pointer:
- .hoverEffect on the diff file-list rows (native List rows already hover).

Camera-free pairing (ManualPairingView):
- "Enter code manually" in both the pairing intro and Settings opens a sheet to
  paste the Mac's nucleic://pair?d=... code, parsed with the same
  PairingPayload(qrString:) the scanner uses (+ a Paste-from-clipboard button).
  The iPad scanner can't run while mirrored to an external display or in some
  Stage Manager states; this always works.

Note: the Mac's RemoteAccessView currently shows only the QR, so a follow-up host
change is needed to surface a copyable pairing link for this to be end-to-end.

Builds clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 00:48:56 -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 149af312dc iPad: show the commit card for host-run and pending-approval commits too
Extend the git commit card beyond the Bash transcript path to the other two
surfaces a commit appears on, so all three read like the Mac:

- ApprovalCardView: a pending `git commit` approval (Bash or host_exec) renders
  the structured commit card (subject + Markdown body) with the literal command
  under "Show command", instead of a raw blob or the generic host breakdown --
  so you see exactly the message you're granting.
- HostExecToolCard: a host-run `git commit` surfaces the commit card in its
  expanded body (the `$ command` already sits in the header), rather than only
  the generic "Commit changes" purpose.

Both reuse GitCommitSummary.parse + GitCommitCard from the previous commit.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 00:27:20 -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
abkslm a641aed081 Merge branch 'dev' into nucleic/trunk 2026-07-03 18:57:19 -07:00
abkslmandNucleic 6a268abbb9 nvrsion: Add re-sync from dev for VERSION and adjust Appcast releases; fix permission flow by removing the “allow always” button from approval views and docs; fix build script error by removing obsolete sh dependency from Makefile.
Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
2026-07-03 18:57:19 -07:00
abkslmandNucleic 33f404555a Remove Permission Button
Nucleic-Session: E0927223-36BE-4F01-9E69-575442FC3E5A
Co-authored-by: Nucleic <[email protected]>
2026-07-03 18:54:41 -07:00
abkslm 3d9a5b5506 fixing version conflict 2026-07-03 17:54:16 -07:00