Repository navigation
Mobile J5 gap audit: verify findings on the iOS Simulator, then decide scope #337
Description
Activity
Simulator verification of §1
I ran these checks on an iPhone 17 Pro simulator (iOS 26.5) using a J5 Code Dev build from
j5/mainat1ff729bc0d. The server was isolated and seeded with aVACUUM INTOcopy of the live database. The copy had no Crews, proposals, machine senders, or queued deliveries, so I created them on the isolated server with real Claude agents in a scratch project and Squadron ("Audit 337"):- a Captain thread started from mobile
- two spawned peers, Echo (replies) and Mute (never replies)
- an Inbox ask, answered through
/api/j5/a2a/inbox/answer - a
j5 a2amachine participant - a one-seat Crew, approved through the proposals API
All screenshots show only fixture content. The copied data showed the same B1 and B3 rendering on real threads, but those threads belong to a private repo, so I replaced those captures with fixture ones.
Summary: 10 of 13 confirmed (B1–B7, B9, the stuck Captain, and branding; B2 and branding only partly). B10 and B11 not reproduced, B8 not checked (no Android SDK on this host). Exchange-dropped not produced, because the archive door the audit assumes doesn't exist on an iPhone against a current server (see B2 and B7).
B1: confirmed
Peer replies and machine messages both render as the raw envelope in a right-aligned bubble. The only attribution is a "Sent by another agent" label, and the agent-facing trailer ("The platform closed this exchange…", "This message came from an automated sender…") is shown to the person.
- Differs: the label is tappable and opens the sender's thread.
- Differs: machine messages get the same label, "Sent by another agent", even though an automation sent them.
- I didn't capture an open ask carrying the
send_message(...)reply instruction. The replies and machine messages I did capture show the same pattern.
B2: confirmed (silence, Inbox answer, Crew notices); exchange-dropped not produced
These all appear as right-aligned bubbles with no label:
turn-ended-no-replysilence notices (createdBy: system)- the person's Inbox answer, shown as
[Message from human:<uuid>](createdBy: user) <j5_crew_gate>and<j5_seat_finished>, as raw XML
Extra: the platform's PlanHandoff reminder ("Your run ended without the declared PlanHandoff handoff…",
createdBy: system) also looks like the user wrote it.Exchange-dropped: I tried to archive Mute while its Exchange was open. On this server the full left swipe settles the thread (
thread.settled) and doesn't archive it, so no drop happened.B3: confirmed
The full
<j5_spawn_context>and<spawner_brief>XML is shown, including the participant, Squadron, and spawner ids.- Differs: the brief carries the "Sent by another agent" label, but it isn't a link (the message has no
senderThreadId). So "nothing shows who spawned it" holds.
B4: confirmed
The queue sheet shows
[Message from automation machine:audit-wa…with a Steer button beside it. The delivered bubble then carries aqueuedbadge.B5: confirmed
The form has no thread binding. Saving gives no warning, and the row is stored with
thread_idNULL andcreation_source: mobile. The first run failed withScheduled new-thread execution is unsupported until scheduling context can select an explicit existing Squadron.- Differs: the list does show "Last run failed" afterwards.
- The task stays enabled and fails again every interval. I deleted it.
B6: confirmed (the inference was right)
The chip opens the file viewer, which shows "File unavailable: Failed to read workspace file 'artifacts/plans/audit-337-fixture.md' in '/tmp/j5-337-scratch'".
B7: confirmed, through Delete, not Archive
On a server that supports settling, the iPhone thread row has no Archive action. The swipe is Settle and the menu offers Settle, Snooze, Pin, and Delete (
thread-list-v2-items.tsxoffers Archive only for pre-settlement servers). Deleting the seat gets the server refusal: "…Crew members are never deleted one by one; retire the whole Crew with Archive crew on the Fleet page…".New defect: after that refused delete, the seat row stays hidden from the list until the app relaunches. The server never deleted it. I reproduced this twice.
B8: not checked
The host has no Android SDK.
B9: confirmed
I launched "Create playbook" from mobile (scratch workspace, Audit 337 Squadron). The thread was stored with
creationSource: "web"and was in the Audit 337 roster immediately. For comparison, the Captain started from mobile's own new-task door was stored asmobile, had no home, and had to calljoin_squadronbefore it could message or spawn.B10: not reproduced
I couldn't create a read-only connection:
j5 auth pairing createandj5 auth session issuehave no scope option. Only the code reading supports this finding.B11: not reproduced
On a thread started as the Navigator persona, the model chip is disabled ("Assigned model: Codex · gpt-5.6-sol · high"), and I found no other mobile control that changes the model. I didn't exercise the stale-draft
modelSelectionpath the audit points at.Stuck Captain: confirmed
After
propose_crew, the Captain's thread shows only its own prose ("proposal is open, pending your approval") and raw tool rows (mcp__t3-code__propose_crew,mcp__t3-code__send_message). There is no gate or notice, and no approve or decline control anywhere. I approved it through the HTTP API to continue. This also confirms §2's point that the agent's ownsend_messageappears as a raw tool-name row.Branding: partly confirmed
Still T3:
- App icon (confirmed)
- Splash/loading mark (confirmed; one capture, and I didn't separate the native splash from the in-app
BrandMarkloader) - Header lockup "T3 Code" (confirmed)
- Environment detail sheet (confirmed, no capture): "T3 Code · Version 0.0.44" and "Update and restart T3 Code on this machine."
Not observed:
- Live Activity: agent-device brings the app to the foreground on every command, so I couldn't capture the lock screen. The code confirms the title is hard-coded
"T3 Code". - Widget: I didn't add one to the home screen.
§2 items seen along the way
- Squadrons: the new-task sheet is "Choose project" with no Squadron choice. The mobile-created thread had no home, as described.
- Crews: the seat appears as an ordinary top-level row with no Crew or Captain mark.
- Archive (product(j5): decide what archive, unarchive, and settle mean for agents, Crews, and their children #254): "Swipe-archive dispatches
thread.archive" applies only to pre-settlement servers. Against a current server I found no archive door on iPhone at all. I didn't check iPad.
The web-side bugs listed under §1 were not re-checked here.
Other findings
- Composer: in a persona thread's composer, the Send button sits almost entirely under the keyboard. I had to tap its visible sliver.
join_squadronerrors: the agent passed the Squadron id without itssquadron:prefix. The refusal wasSquadronProjectReferenceSquadronNotFoundErrorwith an emptymessage, so the agent couldn't tell what was wrong.
Verification friction (tooling, not product)
scripts/mobile-native-client.tsand.agents/skills/test-t3-mobile/scripts/pair-client.shstill use T3 names: theT3CodeDevworkspace,com.t3tools.t3code.dev, andt3code-dev://. Prebuild now emitsJ5CodeDevwithcodes.jackson.j5code.devandj5code-dev://, soensurefails at xcodebuild and the pairing helper opens the wrong app. I built, installed, and paired by hand with the J5 ids. The skill text also still refers to.t3state.- Device hub: J5 caches its agent-device daemon endpoint (
LocalDeviceHost.ensureAgentReady). The daemon shut down on 2026-09-26, and everydevice_opensince has returned the dead127.0.0.1:50639. The only built-in reset is turning agent access off and on in the live app's device settings, so I ran a private daemon with its own state dir. An Astra lane hit the same failure on 2026-09-26 (its coordinator's note: "J5's proxy to it didn't recover").
🤖 Generated with Claude Code. Verification by Claude Opus 5.5 in J5 Code.
Decisions (Jackson, 2026-09-28) and Phase 0
Decided
- Upstream seams for mobile: FORK.md gets a mobile section. It fixes the set of seams up front, adds one integration test per seam, and treats native changes as their own batched class.
- CI and testing: fix the mobile test loop and add advisory native checks (native lint and an iOS PR build). The Expo fingerprint check is dropped because OTA updates are off.
- Device hub: fix the dead-daemon bug as a temporary in-place patch and offer it upstream through Upstream give-back backlog (hold until V2 merges upstream) #276.
- §4 question 4, older mobile builds: option (b). Mobile must supply a Squadron, the same as web, which closes the
mobile-future-squadronexception. No capability flag for now: there is one mobile user, and servers are updated before the app. Release the server first, then the TestFlight build. B9 is fixed in the same change.
Phase 0 (foundations, in progress)
- Mobile dev tooling still uses T3 ids (native client, pairing helper, showcase) #363 Mobile dev tooling uses J5 ids
- Device hub hands out a dead agent-device daemon endpoint #364 Device hub dead-daemon patch
- FORK.md: mobile section with a fixed seam set and seam tests #365 FORK.md mobile section
- J5 CI: mobile native lint and iOS PR build (advisory) #366 J5 CI: mobile native lint and iOS PR build
- J5 mobile scene harness for fixture-only before/after captures #367 J5 mobile scene harness
- Seam tests for existing J5 edits in upstream mobile files #368 Seam tests for existing J5 mobile edits
Later phases, open decisions noted
- Fix web's A2A deviations first, then lift shared presenters and add the mobile timeline and queue seams. Also: B5–B7, the refused-delete row bug, branding copy. Needs §4 item 2 confirmed and item 8.
- Inbox, Crew approve/decline, and a notification when an agent asks the person. Needs items 3, 6 and 7.
- Squadron-first creation through the resolver seam, with the (b) server change. Needs items 5 and 9.
- Fleet, the full artifact viewer and Crew management are deferred.
🤖 Generated with Claude Code























Part of #40. Related: #128 (Squadron-first creation), #254 (archive semantics), #285 (structured A2A records), #182 (J5 change stream).
This is a read-only code audit of
apps/mobileagainst the J5 product docs, the web app, and J5's commit history. Nothing in it has been observed in a running app yet. The audit host is Linux and can't run the iOS Simulator. Every "what the user sees" statement below was read from code, and items marked inferred were reasoned rather than traced.All
file:linereferences are againstorigin/j5/mainat89c5547b75(2026-09-27). Check out that commit to read them. Line numbers will drift on newer heads.For the agent picking this up on the Mac
Your job is verification and evidence, not implementation or decisions. Leave §4 (Decisions) for Jackson.
Follow the
test-t3-mobileskill. On the simulator host, runnode scripts/mobile-native-client.ts ensure ios <device-id>before starting Metro.Seed an isolated server with a copy of real data (
VACUUM INTOfrom~/.j5code/userdata/statev2.sqlite; see AGENTS.md → Test data). Never point the app at the live install. An empty database will not reproduce most of these. You need threads that contain:propose_crewproposalartifacts/…link in a messageIf the copy lacks one of these, create it on the isolated server with real agents rather than hand-writing SQL.
For each checkbox in §1, mark it confirmed or not reproduced, attach a screenshot, and add one line on anything that differs from the description. Push images to
j5/evidenceundermobile-audit-40/(fetch and rebase first; other agents push there too). Embed them as raw links. Check each withfileandcurl -sIbefore posting.Post the results as one comment on this issue. If a finding is wrong, say so plainly.
1. Defects visible today (verify each)
B1. Every A2A delivery renders as a raw envelope in a right-aligned "you" bubble. Peer messages show
[Cross-agent message from agent:j5:a2a:<id> in squadron <id>], the body, then the agent-facingsend_message(...)instructions. There is no sender name and no reply state. A tappable "Sent by another agent" line is the only attribution. Evidence:apps/mobile/src/features/threads/ThreadFeed.tsx:1606-1660;lib/threadActivity.ts:1546-1566does no A2A classification. Size M.B2. Platform notices look like the user wrote them. These get a right-aligned bubble with no label at all:
createdBy: "system")createdBy: "user")Same evidence as B1.
B3. Spawn briefs show raw
<j5_spawn_context>/<spawner_brief>XML with participant, squadron, and spawner ids. fix(web): peer-agent spawn briefs render as attributed briefs instead of raw XML #186 fixed this on web only. Nothing on mobile shows who spawned a thread. Size S–M.B4. Queued A2A deliveries show the raw envelope in the queue control. Web shows "From — ". Evidence:
ThreadQueueControl.tsx:211-230,threadQueueControlPresentation.ts:38. Size S.B5. Scheduled tasks created on mobile can never run. The mobile form has no thread binding and always sends
threadId: null. J5's server refuses null-thread tasks when they fire ("Scheduled new-thread execution is unsupported…"), and nothing warns at save. Evidence:SettingsScheduledTasksRouteScreen.tsx:612;apps/server/src/scheduledTasks/ScheduledTaskService.ts:517-526. Size S–M.B6.
artifacts/…links in chat open as workspace files. Artifacts live in the state directory, not the workspace, so the file isn't there. Inferred: file-not-found. Evidence:ThreadFeed.tsx:2222-2248→features/files/filePath.ts:71. Size S.B7. The Crew-seat archive refusal points at a page mobile doesn't have ("retire the whole Crew with Archive crew on the Fleet page"). Evidence:
crewSeatArchiveGuard.ts:25-26, shown byuseThreadListActions.ts:82-88. Size S.B8. Android notification settings copy says "select T3 Code", but the OS lists the app as J5 Code. Evidence:
SettingsNotificationsRouteScreen.tsx:337,489. Size S. (Android only. Note it if you can't check it.)B9. The Playbook Author launch records the thread as web-created. It omits
creationSource, which defaults to"web". That default is how it gets a Squadron home while every other mobile launch doesn't. Evidence:packages/client-runtime/src/j5/playbooks.ts:88-140;operations/commands.ts:670-671. Size S. Verify by launching an author from mobile and checking the thread's creation source and home.B10. The Playbook Author Squadron picker ignores read-only connections. It hard-codes
available: true, so it offers a launch the server will refuse. Evidence:j5/playbooks/PlaybookLibrarySettingsScreen.tsx:49-53. Size S.B11. Inferred, needs a repro: a persona thread can send a non-persona model selection on mobile. Web forces
resolvedModelSelectionthroughpersonaComposerSelection.ts; mobile doesn't, and the server rejects changes to an immutable route. Evidence:ThreadRouteScreen.tsx:477;ThreadComposer.tsx:796. Size S.Stuck Captain. When a Captain calls
propose_crew, the mobile user has no way to approve, edit, or decline. The proposal never expires, and no agent tool resolves it. Confirm what the Captain's thread shows on mobile.Branding. Confirm which surfaces still show T3:
components/CompactBrandTitle.tsx:33-39)BrandMark.tsx:30)remoteRegistration.ts:531,ThreadComposer.tsx:617)Web-side bugs found along the way
These affect the shared presenters. Fix them before lifting code for mobile.
ThreadA2ARenderer.tsx:243-250vsLifecycleService.ts:159-163.thread-a2a-rendering.md. They show "Closed your exchange" and "Reply" badges, a rawrecipientIdon sent cards, andVia Inbox · human:<uuid>. Sender names aren't links.fleet.logic.ts:196-200). fleet-page AC23 says it counts measured problems and never open asks.isSidebarMemberhides every agent-spawned thread unless pinned (SquadronScope.logic.ts:35-42).2. Missing features by area
Squadrons (#128)
Mobile has no Squadron UI. The only Squadron read is the Playbook Author picker.
ChatView.tsx:8481-8490,10744,10767;SquadronPicker.logic.ts;useSquadronNewThreadOnBranch.tshome-list-filter-menu.ts:44,68)Sidebar.tsx:2452,2532,4470;ThreadCardIdentitySquadronCreate*,SquadronActions.logic.ts,FirstRunGatecanOperate, and show old servers as unsupportedreadSources.ts;ChatView.logic.tscanSelectDraftEnvironmentWhy mobile threads have no home.
lib/projectThreadStartTurn.ts:51sendscreationSource: "mobile"and nosquadronId. The server maps that to a no-home exception (SquadronLaunchPolicy.ts:49-55) and silently drops anysquadronIdthat does arrive. As a result:join_squadronShared code. The J5 atoms are already in
packages/client-runtime/src/j5/, and mobile already instantiates them (apps/mobile/src/j5/state.ts). They coversquadrons,createSquadron,renameSquadron,deleteSquadron, thread homes, andlistThreadHomes.Pure logic that is still web-local and needs lifting:
SquadronPicker.logic.ts(drop the webProjecttype)SquadronScope.logic.tsSquadronCreate.logic.tsFirstRunGate.logic.tsThreadHomesClient.tsandSquadronDirectory.tsFor refresh-on-focus, mobile can reuse the
AppStatepattern inj5/playbooks/useActivePlaybookRefresh.tsin place of web'sdocument.visibilityState.A2A messages and the Inbox (see #285)
ThreadA2ARenderer.tsxare pure and need only{id, role, text, createdBy}, so lift them to client-runtime. Peer deliveries now carrysenderThreadId, so sender links need no server change./api/j5/a2a/client-reads/participant-identitiesis web-local and missing fromJ5_API_PATHS. Add it to contracts and to client-runtimehttp.ts.send_messaget3McpToolPresentation.ts.crewNotices.logic.tshas no imports and lifts as-is.HumanInboxPage.tsxis web-local. FORK.md case 34 defers it: "Native mobile screens are deferred".mergeOpenInboxCountsis shared.pendingRuntimeRequest, and a J5 ask isn't one, so the user likely gets "finished" rather than "needs you". This needs new server work on the relay.Archive (#254)
Swipe-archive dispatches
thread.archivewith no preflight, so there is no warning for open Exchanges, placed children, or live Crews. The server still cascades correctly; the person just never sees the facts first. FORK.md case 21 defers this: "Mobile archive doors remain explicitly deferred". The pre-archive facts schemas and route are web-local. A port must use the 2026-09-22 "placed agents keep running" wording. Size M.Crews
Mobile has no Crew surface. The only written deferral is in the user guide (
docs/user/personas.md:57).crews.mdno longer mentions mobile.propose_crew)crewProposals,previewCrewProposal, andresolveCrewProposal, but nothing calls them.Artifacts
Mobile has no artifact surface: no list, viewer, or delete. Web has
routes/artifacts.tsx,j5/artifacts/*, and a right-panel tab. The contracts are shared;artifactClient.ts,artifactPath.ts, andartifactPreview.logic.tsare web-local. The handoff chip on mobile is static text.docs/user/artifacts.mdsays nothing about mobile. Size M read-only, L full.Fleet
Mobile has no Fleet page. The doc and web now agree on the layout (the whole fleet, in Active, Settled, and Retired sections), which suits a phone. The
fleetatom is shared;fleet.logic.tsis web-local and pure. The Fleet page also hosts the Playbook runs overview and Stop/Archive crew. Size L.Branding
App name, scheme, and bundle id are J5. Still T3:
assets/{prod,dev,nightly}and the Android layers)components/CompactBrandTitle.tsx)The icons need a native rebuild. Size S for the copy and lockup, M for the icons.
Smaller gaps, documented as web/desktop only
docs/user/skills.mdsays mobile uses skills in chat but has no settings page. Size M for a read-only inventory.docs/user/playbooks.mdlimits these to web and desktop. Size S for import; the runs overview needs Fleet.3. Already at parity (no work)
@persona:mentions, draft-as-persona launch, identity chips, and the persona lock./playbookexpansion, and the library (create, rename, delete, author).4. Decisions needed (Jackson)
Stopgap or full A2A rendering? A small change could fix the misleading parts now: left-align and label deliveries and notices, and strip the envelope. Or go straight to lifted card presenters. Or wait for Carry J5 A2A metadata as a message-context record instead of text envelopes #285, which would make parser work on both clients throwaway.
Fix web's A2A deviations from its own doc first? Recommended, so one presenter gets lifted to client-runtime instead of two diverged copies.
Lift the "native mobile screens deferred" rulings? That covers FORK.md case 34 (Squadron screens, Inbox), case 21 (archive), and the user guide's Crew deferral. feat(mobile): J5 daily-use parity — Squadrons, inbox, A2A, and lifecycle #40 and fix(mobile): replace project-first creation with Squadron creation and selection #128 suggest Squadrons and Inbox are wanted; confirm the order.
What happens to older mobile builds once mobile sends a Squadron?
squadronIdis present, and keep the no-home exception when it's absent.fix(mobile): replace project-first creation with Squadron creation and selection #128 requires this to be decided explicitly. Fix B9 in the same change.
Mobile Squadron UX. fix(mobile): replace project-first creation with Squadron creation and selection #128 already says to replace project-first creation with Squadron-first. Still open:
Minimum mobile Crew surface.
The stuck Captain argues for at least (b).
Notifications for asks to the person.
Which list-membership rule is right, and what the Fleet badge counts. Settle both (see the web-side bugs in §1) before porting either.
Scheduled tasks on mobile (B5).
Artifacts, Fleet, Skills, icons. Build each for mobile, or document it as web/desktop only and fix just the misleading parts (B6, B7, B8).
Tracking. feat(mobile): J5 daily-use parity — Squadrons, inbox, A2A, and lifecycle #40 doesn't cover artifacts, branding, scheduled tasks, Fleet, or the defects in §1. Split them out once the decisions above are made.
🤖 Generated with Claude Code. Audit by Claude Opus 5.5 in J5 Code.