Skip to content

Bots on phones: status line, Steer/Queue, instant actions (bot chat activity D, Remote rev 30) - #479

Merged
sambitcreate merged 84 commits into
mainfrom
claude/bot-chat-activity-d-phones
Oct 15, 2026
Merged

sambitcreate merged 84 commits into
mainfrom
claude/bot-chat-activity-d-phones

Conversation

@sambitcreate

@sambitcreate sambitcreate commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Slice D of Bot chat activity: Bot chat activity on iPhone and Android, over Aiden Remote revision 30. Slices A, B, C1 and C2 (#475, #477, #476, #478) are already merged.

Plan: docs/superpowers/plans/2026-10-14-bot-chat-activity-d-phones.md. Every ruling (R1–R22), deviation and parked item is recorded in spec §11/§12/§17 and .memory/bot-chat-activity.md.

What changes for people

  • Status line on phones. While a Bot works, iPhone and Android show the Mac's calm status line ("Getting ready", "Reading", "Using Notion"), with elapsed time after 20 s.
    • The elapsed time follows the turn's real start.
    • If the connection drops, the line says "Reconnecting to your Mac".
  • Steer or Queue while busy. Queued messages show as faded "Queued" bubbles.
  • Faces in Bots home. A working Bot's face glances. Under Reduce Motion or Low Power it shows a still badge instead.
  • Chat activity page. Advanced → Chat activity on both phones, saved on each pick. It recovers from conflicts and opens offline from the cache.
  • Instant actions (owner directive). Every phone Bot action is instant and optimistic, uses skeletons, and never waits to connect.
    • A send shows at once, as "Sending…", then Queued, then failed with Try again.
    • Unsent messages persist across leaving the chat and relaunching: 24 h, file protection or no-backup, purged on unpair or access loss.
    • Stop, Resume, Retry, Dismiss, answers and approvals apply at once and roll back if they fail.
    • New Bot, model pick, Advanced, Profile and Memory edits also apply at once.

Contract (revision 30, provisional; main is still 29)

  • Opt-in. A per-device negotiable bot:activity, plus the bot-activity-v1 token. Phones that haven't opted in get a no-op state frame at the same seq, so legacy streams stay gapless.
  • Activity frame. Exactly { phase, app?, startedAt }.
  • Queued frame and snapshot list. Only the person's own inputs, via a fail-closed classifier and an inclusion filter.
  • submissionId on the person's user entries, for exact reconciliation.
  • whenBusy (steer or queue).
  • Detail and PATCH chatActivity, stripped for non-holders on every detail route.
  • Shared Stop/Resume intents. stopping, stopCancelled and resuming, so the Mac and phones agree.
  • Remote ledger. It no longer records pre-execution state refusals, so a same-key retry works once the Bot is fixed.
  • Fixtures, OpenAPI and docs/aiden-remote-api-v1.md are updated. TS, iOS and Android decode the shared fixture strictly.

Verification

  • tsc, lint, test:ci-policy, test:bot-runtime (194), test:bots (820)
  • test:aiden-remote: all pass except aiden-remote-service.test.ts "single-transport profiles lease…", which fails locally with AidenRemotePortInUseError. An unrelated outbound socket holds one of its fixed ports on this Mac; the file is untouched by this PR. CI is the arbiter.
  • iOS AidenOnTheGoTests: 1005 passed, 0 failed, 6 skipped (device-only), run with -test-timeouts-enabled YES
  • Android :app:testDebugUnitTest (892), :app:lintDebug, and Bot instrumented tests 12/12 on a headless AVD
  • Owner: an end-to-end run against a real Mac on this branch, plus VoiceOver and TalkBack passes

Known open items (spec §17)

  • Android: the fixture null-literal raw-null guard.
  • Android: Bot create still waits on the form instead of showing an optimistic Home row.
  • iOS: the Files sheet still uses a spinner.
  • Android: inline error text contrast in some light themes.
  • Cosmetic: overlapping failed Stops.
  • At merge, revision 30 must be re-claimed if main has moved past 29.

CI notes

Built task by task with subagents. Each task went through its own review and fix rounds, then a final whole-branch Opus review and a final fix wave, with each part re-reviewed.

🤖 Generated with Claude Code

sambitcreate and others added 30 commits October 14, 2026 09:15
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… with shared fixtures

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A Stop from the Mac or a phone now goes through one process-wide
botTurnIntents emitter. A Stop that fails takes "Stopping" back
(stopCancelled), and Resume is announced only from the session's
onResumed, after it actually resumed. The desktop live projection
listens through wireBotLiveTurnIntents, an electron-free seam tested in
live-projection.test.ts. liveRunKey is exported for the Remote projector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test

A listener that throws while hearing Stop, a failed Stop or Resume is now
reported through an optional onError dep (logged as a bots warning in the
process-wide emitter) instead of being swallowed, and the other listeners
still hear the intent. A two-Bot case pins that a Stop for one Bot never
reads Stopping on another Bot's running reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r and Queue

Devices holding bot:activity get the turn's phase ({phase, app?,
startedAt}) and the person's waiting messages in the session snapshot and
as activity and queued frames; every other device gets its current state
at those seqs, so seqs stay gapless and revision-29 parsers never see the
new keys. A host projects them, and accepts whenBusy (steer -> steer,
queue -> followUp), only with both turn intents and the Bot service's
Chat activity gate.

Queued inputs show only once the session classified them as the
person's own (quietInputs, fail closed, throttled retry on the
projector's lane); photo-only inputs are left out. Stop, a failed Stop
and Resume are scoped to one run, which restarts on a new run key too.

The shared derivation now holds "Picking up where it left off" until the
resumed run's first live event, so a killed run's partial text no longer
reads as writing after a crash, on the Mac and on phones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… bot:activity opt-in

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…us words

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t bot:activity

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…Queue in the session model

Optimistic send with an outbox, instant Stopping, reconcile against the
Mac's entries and Queued bubbles, skeleton/connecting state and a
memory-only transcript cache (R14). Folds the Task 4 review: snapshot
activity/queued, bot:activity opt-in tests, one queued-list decoder,
no unreachable display fallback, startedAt 2^53-1 accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…faces

The Bot chat draws the status line, the Steer/Queue busy pill beside Stop,
the Mac's Queued bubbles and the person's own outgoing messages (Sending…,
Queued, Couldn't send · Try again). It never blocks on the connection: the
composer works at once, a chat with no cached transcript shows skeleton
bubbles, and a quiet Connecting… hint replaces the spinner and the blocking
load error. Working Bots' faces glance in Bots home, replacing the green dot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vity devices

The person's own `message` entries (role user) now carry the optional
`submissionId` they were sent as, so phones settle optimistic sends exactly.

- BotSessionRuntime.placedSubmissions(botId) maps each placed input's
  pi.user entry to its submission id, using the same quiet-input check as
  quietInputs, so routine inputs and the self-intro are never listed.
- The Remote projector looks placements up on its serial lane once per new
  pi.user entry and keeps a projector-lifetime memo of the newest 64, so
  reconnect snapshots still name a finished turn's send.
- Only `user` transcript entries are tagged: routine inputs and question
  answers never are. Devices without bot:activity get every snapshot and
  entry frame without the key, at the same seq.
- Protocol type, strict parser (user role only, wire id shape), OpenAPI,
  contract fixture samples and a non-holder privacy sample; iOS decodes the
  optional key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ontrols

Reconcile by submissionId (R16), with text only for entries without it,
unsettled sends and queued-before-receipt; entries are claimed once per
epoch. The outbox (R17) never drops an unsettled message, holds later
messages behind a deferred one, survives leaving the chat (session
cache) and relaunch (protected file, 24h), retries with bounded backoff
while connected, is purged on unpair and access loss, and never blocks
listen(). Retry, Resume, Dismiss, answers, approvals and routine
proposals apply at once and roll back on failure (R18). Receipts apply
only if the session didn't move; a new run clears Stop; a clean close
no longer flashes Reconnecting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bounded 64-entry memo evicted old ids, so the next publish re-sent the
old bubble as an entry frame to every device; phones that no longer held
it appended a ghost bubble at the bottom, shipped revision-29 phones too.

- The memo is keyed by entry id and pruned only when the entry leaves the
  session window (either audience's), so it is bounded by the window.
- A tag change alone is never an entry frame: a bubble is tagged when it
  first shows and only loses the tag once it is out of view.
- A failed placement lookup is reported through the projector's report.
- The parser (TS and iOS) refuses submissionId together with label, with
  a rejected fixture sample.
- Tests: more than a window of messages never re-sends an old bubble and
  every bubble in view keeps its id; a reported lookup failure; a real
  durable steer and follow-up placed mid-turn, two at one boundary, each
  take their queued id in the publish that drops them from Queued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…status words

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eer/Queue in the session controller

Optimistic sends through a durable outbox (R14, R16, R17): exact reconcile
by submissionId with text only as a fallback, per-epoch claims, head-of-line
ordering, bounded backoff, failed (never dropped) on exhaustion or 24h TTL,
noBackup disk persistence keyed by pairing with owner-guarded writes and
per-record decode, purged on unpair, 401/403 and accessChanged. Stop, Resume,
Retry, Dismiss, answers, approvals and proposals apply at once and roll back
only what nothing newer replaced (R18). Status line per R7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Failed Resume, Dismiss, answers and approvals roll back whatever the Mac
hasn't written since the tap, even across unrelated frames. A message
unsent after 24 hours, or out of tries, says Couldn't send instead of
vanishing, on restore and in memory. A stream 401/403 purges with the
notice and a failed refresh keeps it. Outbox writes are owner-guarded
on disk as in the cache, coalesced, ordered before a purge, and stop on
release; load() sends structurally. The outbox file is keyed by the
pairing's deviceId, decodes record by record, refuses a 21st message
visibly and is excluded from backup. An echo settles only on a receipt
with its submissionId; 409 idempotency_in_flight keeps the message key;
Stop clears on a new run's first activity; Retry waits for a load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aidenProgressCapabilitiesToRequest: the opt-in grants as one table, each
  asked only from a Mac announcing its feature and only when not held.
- whenBusy and PATCH chatActivity go only to a Mac that supports
  bot-activity-v1; the session transport reads that from serverInfo.
- aidenBotChatActivity gates a cached detail's chatActivity on support now.
- A turn leaving running drops its activity, so an optimistic Stop/Resume
  or the next turn never shows the run before's phase.
- Fixture frames also decode through AidenBotSessionEventStream.decodeFrame;
  tautological grant-list assertions dropped; two-Mac grant persist/drop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd glancing faces

The chat draws the R14 outbox (Sending, Queued, Couldn't send with Try
again and remove at 48dp), the cached transcript or still/shimmering
skeletons with a Connecting / Try again hint, and the status line with
a 2 s throttled word-only announcement that resets when the line hides.
Waiting bubbles use the opaque primaryContainer pair (>=4.5:1), not alpha.
Bots home replaces the green dot with glancing faces; Reduce Motion,
battery saver and photos wear the glance badge (R19).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ns offline

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ead off main

- The session transport sends whenBusy exactly as the controller froze it,
  so a retry under the same key is byte-identical.
- Exhausted retries remove a message only when an untagged entry stands
  for it; an entry naming another submission leaves it Couldn't send, key
  kept, claim given back, and never settles a failed message.
- The outbox is read on the store's io thread from start(); deliveries wait
  for it and restored messages go first. Writes made before the read wait in
  memory and merge. Unpair keeps purged chats read and empty.
- stopFollowing cancels the delivery under way; a refused store write ends
  sending. AtomicFile (fsync before rename); enqueue refused past the byte
  budget; any activity after a Resume tap clears Resume.
- Typed exception for a chatActivity-only patch to an older Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picks apply at once and save in the background: taps during a save coalesce onto the
returned revision, a failure or conflict rolls back to the saved choice, a connecting
Mac holds a pick for 8 s, and each request is bounded to 12 s. Previews replay what each
level shows on the Mac, pause when not resumed or off-screen, and hold a level-specific
still frame under Reduce Motion.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…uts, Mac step wording

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…box writes

An echoed message out of tries is removed only when its echo names no
submission; otherwise it fails with its key, gives the claim back and
never settles on that foreign id again. Message POSTs, Stop and the R18
controls are bounded at 12 s and a timeout keeps the key. The store owns
one ordered writer per chat, held strongly, ordered with the unpair
purge so a late write never recreates a purged file. The outbox has a
10 MB budget sized for 20 worst-case messages and refuses past it;
oversized files are kept. Cancelled sends spend no try; release cancels
the real sending task; expiry and state-reason failures release echo
claims and keep frozen keys; responses apply by stateWrites; Resume
follows the Mac's first activity. TestGate is a bounded latch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n, Profile, Home)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ndex and memory

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ffline row

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…urvives relaunch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
botActivityPrivacy.rejectedNullLiterals holds activities (app: null),
sessions (queued: null), events (a queued frame with queued: null) and
entries (label: null with submissionId). Each decoder must refuse them;
the TS parser does, and the protocol test pins each reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sambitcreate and others added 8 commits October 14, 2026 23:43
…pens New Bot

Instrumented (headless AVD): the Creating… row shows from the start of a
create, a failure removes it and says so, and Try Again reopens New Bot with
the same answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Bot chat's Files sheet was the last Bot surface still showing
ProgressView("Loading files…") on a cold load (R15). The Files section now
shows file-shaped skeleton rows built from AidenBotSkeletonBlock (still under
Reduce Motion and Low Power), read by VoiceOver as one "Loading files".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y Again

As iOS 9685204: the create flow lived only in memory, so a process death
mid-create lost the answers and the Idempotency-Key. The ask (answers, the
frozen request body and its key) is now on disk per pairing before anything
goes out, beside the pairing's chat outboxes in noBackupFilesDir (AtomicFile),
so an unpair purges it and nothing is written for that pairing after; a
401/403 on the create drops it. On relaunch Bots home shows it as not made:
within a day Try Again sends the same request on the same key, so the Mac
replays its one Bot; after that, or after a refusal, it reopens New Bot as it
was. A Bot the Mac's list already shows clears it quietly; OK, or closing the
reopened New Bot, lets it go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scripted saves in AidenBotChatActivityTests waited on bare continuations,
and one on an unbounded while-sleep loop, so a test that failed before letting
its save go left the pick hanging until the execution allowance. They now wait
on a Hold that fails the test after 5 seconds (checked by leaving one held: it
fails in 5.05 s instead of hanging); the polls for a save to start were already
bounded by untilSaving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pped

A timeout (or a conflict) re-reads the Bot, and a save that landed counts as
saved. The pick made while that save was in flight was cleared before the
re-read, so when the refresh showed the first pick had landed the second one
was silently lost: the page showed the first choice with no error. It is now
cleared only when the save rolls back, so it goes out next on the revision the
refresh read. A pairing that changed during the refresh gives up quietly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…om its page

AidenBotChatActivityReport held a confirmed pick while Advanced was open and
reported it when Advanced's onDisappear ran. Advanced had already disappeared
when Chat activity was pushed over it, so closing the Profile sheet (or popping
back to the chat) straight from the Chat activity page never closed the report:
the held pick, and every later one, never reached the screens behind (the Bot
cache still got them).

The report now tracks both pages. Pushing Chat activity hands the hold over,
the last page to leave reports what is held once, and later saves go straight
behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A create an earlier launch left behind replays its frozen request when Try
Again is chosen. If that replay failed again (the Mac unreachable, or an answer
lost), the next failure reopened New Bot instead, which rebuilds the ask from
the current catalog and could miss the frozen key. A restored attempt now
keeps replaying until it is made or let go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The new Create recovery can target the wrong Mac, replay an expired operation, or lose its retry identity across a crash. These need fixes before merging.

Reviewed changes since 7744dd3, covering the complete incremental artifact, affected full-diff regions, and their host/native consumers. This focused delta review relies on the initial whole-PR review for unchanged implementation rather than repeating the 98-file review.

  • Added durable Create recovery on both phones, retaining answers, frozen requests and idempotency keys across relaunch, with pairing-local storage and cleanup.
  • Changed Android New Bot to close immediately, show a Creating row on Home, and offer retry/reopening after failure.
  • Separated Android optimistic cache state from confirmed disk writes, rejected the shared fixture's optional null literals, and improved Bot error-text contrast.
  • Preserved iOS Chat activity picks during timeout recovery and reported confirmed saves when the sheet closes directly from that page.
  • Replaced iOS Files loading with skeleton rows, bounded held test saves, and contained editor-test pipe errors.
  • Verified available checks: passed 47 host Bot/ledger tests, 20 Ruby and 30 Node iOS release-policy cases, and syntax parsing of eight changed Swift files. Independently confirmed that the default host ledger executes an identical Create key again after 25 hours.
  • Checked native verification limits: local XCTest is unavailable without xcodebuild; Android stopped at Plugin [id: 'org.gradle.toolchains.foojay-resolver-convention', version: '1.0.0'] was not found, and the online retry returned Tool 'shell' timed out after 120000ms. The exact-head hosted Android verification step succeeded; Compose execution was still running and iOS was queued when checked. The worktree IO suite returns early on Linux, so it does not verify the EPIPE change here.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using gpt-6.1-sol | 𝕏

Comment thread ios/AidenOnTheGo/Features/Bots/AidenBotsHomeView.swift Outdated
Comment thread ios/AidenOnTheGo/Features/Bots/AidenBotCreateView.swift
Comment thread android/app/src/main/java/sbtbiswas/AidenOnTheGo/features/bots/AidenBotOutbox.kt Outdated
sambitcreate and others added 7 commits October 15, 2026 00:35
… disk

Create and a restored Try Again queued the pending-create write and sent at
once, so a slow disk queue plus a process death after the Mac committed lost
the recovery identity. The store's save now answers once the AtomicFile write
has synced and landed on its io queue (checking the sync and rename that
finishWrite only logs), and send waits for that answer. A write that fails
sends nothing and offers Try again with the same answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pending create now belongs to one pairing: the Mac, this phone's device id
there and when they were paired. The store records the pairing's time, and a
record an earlier pairing of the same Mac left behind is dropped rather than
restored. The create flow starts, restores and retries nothing while another
pairing is in use: a restore that lands after a switch shows nothing and is
read again on the pairing's next visit, so Try Again never sends one Mac's
answers to another. The Bot save and Chat activity hosts take only the client
made for their pairing, so the moment between a switch and the new client
can't hand one pairing's work the other's client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Mac keeps a settled Create key for 24 hours, and the same key after that
makes a second Bot. A New Bot now replays its key only within 23 hours of
when it began on the phone's clock (and not when the clock has moved back
more than an hour), checked on restore, on Try Again from a prompt left open
across the boundary, and for this launch's own uncertain attempts. Past it,
the store forgets the key on disk while keeping the answers and request, so
no later launch can replay it. Try Again then reopens New Bot with the
answers and says the Bot may already be on the Mac; only the person's own
Create sends a new request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mac A's restore could land after a switch to Mac B and show its prompt
there, and Try Again built its request from the active pairing, so A's
answers could become a new Bot on B. The create flow now keeps failures
and retained attempts per pairing, Bots home shows only the active
pairing's failure and Creating... row, and Try Again (and the New Bot it
reopens) sends only to the failure's own Mac; otherwise the failure waits
for that Mac. The restore is fenced like Chat activity's isCurrent: a
look that lands after its task was cancelled, or after the request
context (instance, device, activation generation) stopped being current,
shows nothing and is looked for again when that Mac is active.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Mac's idempotency ledger forgets a Create key 24 hours after it
answers and then runs the same key again, but every restored frozen
request replayed on Try Again however old it was, and the store kept an
ambiguous record forever. As on Android, a frozen request now replays on
its key only within a 23-hour window (an hour short of the Mac's, for the
two clocks; a clock that went back counts as past it). Past it the store
keeps the answers and what was sent but drops the keys; Try Again first
looks in the Mac's list for a Bot with the same name and subtitle made
since the request went out (allowing two minutes of clock skew) and lets
the failure go quietly if it is there, and otherwise reopens New Bot with
the answers, where Create is a new request. This holds for a prompt left
open across the boundary and for an attempt kept in memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Android review asked for a disk-before-network barrier on Create. iOS
already awaited the pending-create write before the request went out, for
Create and Try Again alike, but it ignored whether the write succeeded,
so a full disk or an unwritable folder still sent a mutation that a
relaunch could not recover. savePendingCreate now reports whether the
record is on disk, the flow's write chain carries that result, and a
create (new or replayed) whose answers or keys couldn't be written stops
before anything reaches the Mac, with "couldn't store this Bot" and Try
Again. The test checks against a second store that each request's exact
body and key are on disk as it leaves, and that nothing goes out when the
write fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nswers

With failures kept per pairing, Mac B's interrupted create can come back
while a Bot is still on its way to Mac A. Its Try Again then reached
submit's one-at-a-time guard and returned, dropping B's failure from
memory. A Try Again (or the New Bot it reopened) that can't go out now
keeps its answers as they were, for the same pairing or another, and
Bots home holds the failure prompt until no Bot is on its way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found in this delta. The three previous findings are addressed; exact-head native CI is not yet complete.

Reviewed changes since 9c35409, covering the complete seven-commit incremental artifact, affected full-diff regions, and relevant host/native consumers. This focused re-review relies on the initial whole-PR review for unchanged implementation rather than repeating the 98-file review.

  • Added disk-before-network barriers: Both phones now require successful persistence of the exact Create request/key before sending, including recovered retries, and report write failures without sending.
  • Isolated pairing authority: Checked iOS per-pairing attempts/failures, cancelled or stale restore rejection, and retry-origin validation; checked Android persisted pairing identity and client-ownership fences during installation switches.
  • Bounded ambiguous Create replay: Checked the 23-hour replay window against the host's 24-hour retention, expiry while a prompt remains open, preservation of answers without unsafe keys, and explicit new-creation intent after expiry.
  • Preserved busy retry answers: Checked that an iOS Try Again rejected while another Create is pending retains its recovery state for a later retry.
  • Verified available checks: Passed 47 host Bot/ledger tests, 20 Ruby and 30 Node release-policy cases, and syntax parsing of all four changed Swift files. These do not execute or type-check XCTest; this Linux runner lacks xcodebuild.
  • Checked Android and hosted verification: Local Android tests stopped at Plugin [id: 'org.gradle.toolchains.foojay-resolver-convention', version: '1.0.0'] was not found; the dependency-enabled retry returned Tool 'shell' timed out after 120000ms. The exact-head Android verification step succeeded, while Compose execution remained in progress and the iOS job remained queued. This review does not establish green overall CI.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

sambitcreate and others added 4 commits October 15, 2026 02:18
Revisiting a running chat starts the local run watcher. When the
route-detached run then ended (Stop, or its reply landed), the watcher
reported the run's end after the terminal had already cleared the
detached marker, and that marker was the only proof the run was this
window's own. The run came back as an observed (phone-style) run, so the
composer re-locked with "Waiting for the response…" right after it had
unlocked, and a text-only reply could flash a second streaming row and
Stop. Remember the streams this document starts (bounded) and never
mirror them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stop and the Steer/Queue pill appear as soon as a send begins, but two
things made the pill unreliable until main had finished starting the run:

- handleSend awaited the chat:start acknowledgement, which kept the
  composer in its sending state. The pill could not submit, so picking
  Steer or Queue only switched the mode and left the draft. The send now
  finishes at the durable append; a start rejection still sets the error.
- Pi opens its run-input queue at agent_start. Before that main answers
  run_not_active, which the composer reported as "The current response
  has ended". Admission now retries that answer, with a bound, while the
  pane's own run is still live. A rejected attempt writes nothing, so a
  retry cannot duplicate guidance. Detached runs are not retried.

Under a six-worker load the Steer specs in chat-message-queue failed 8 of
30 runs before and 0 of 60 after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two hosted flakes in chat-message-queue (Stop missing after a revisit;
no request with the edited queue text) predate Bot slice C2 and have not
reproduced locally or in a Linux container. Make the next hosted failure
explain itself:

- On failure the fixture prints the on-screen alerts, status lines and
  toasts, plus the dev log's warn and error records, to stderr. Hosted
  artifacts keep only the sanitized receipt, and the reporter truncates
  the dev-log attachment to two lines.
- The queue-editor spec checks that the first reply saved and the queue is
  not paused before it saves the edit. A response that failed upstream now
  fails at that step instead of reading as an editor bug.
- The rejected-steer spec waits for the closing menu to return focus to
  its trigger before typing. Radix restores that focus a task later, and
  under load an Enter sent straight to the composer could land on the
  trigger and reopen the menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found in this delta. Exact-head CI is still incomplete.

Reviewed changes since 492eb1a, covering the complete four-commit incremental artifact, affected full-diff hunks, and lifecycle/admission/composer consumers. This focused re-review relies on prior reviews for unchanged implementation; commit artifacts supplied patches omitted from the full diff.

  • Preserved run ownership after settlement: Checked that delayed observer updates cannot mirror this document's own run after detached cleanup, while subsequent phone/window runs remain visible.
  • Enabled Steer/Queue during startup: Checked completion at the durable append and bounded admission retries, including Stop, terminal, committed-refusal and unknown-outcome boundaries.
  • Improved failure diagnostics: Reviewed bounded notice/log output and the queue spec's focus and saved-reply assertions.
  • Verified available checks: Passed 133 focused tests, project and E2E type-checks, and changed-file lint; collected all 19 queue specs. Electron execution requires macOS under the repository's E2E skill, so collection does not establish runtime acceptance. Exact-head CI remains pending.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

sambitcreate and others added 2 commits October 15, 2026 02:29
On Stop the harness reports each journal write it stops waiting for to
onJournalError as a PiManagedCancellationError, so every Stop logged
"Could not append Pi session messages" at error level, usually three
times. The write is detached and may still land, but the visible-turn
lease rolls the turn back and re-journals the visible chat, so the
session stays intact. Keep marking the journal unhealthy, log the Stop
once at info, and keep real write failures at error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… without awaiting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found in this delta. Exact-head CI is still incomplete.

Reviewed changes since f7b919c, covering both new commits, all four changed files, and their cancellation, persistence-recovery and send-path consumers. This focused incremental review relies on prior reviews for unchanged implementation rather than repeating the 111-file review; commit artifacts supplied the three Pi patches omitted from the full diff.

  • Distinguished Stop from write failure: Checked the exported cancellation classifier and once-per-stream info logging while preserving unhealthy-journal marking, real-failure error logs, and rollback/resync recovery.
  • Extended journal recovery coverage: Checked cancellation during streaming, detached-write settlement, exact lease rollback and visible-history reconstruction, plus the existing fail-closed write-failure case.
  • Corrected transition-test expectations: Checked that the existing assertions follow the non-awaited generation start and guarded asynchronous error callback without changing production send behavior.
  • Verified focused checks: Passed 287 tests across seven harness, durability, compaction and renderer suites, project type-check, and changed-file lint. Four initial compaction failures were resolved by building the required VCC worker before rerunning that suite; Electron and native mobile execution were not performed for this desktop logging/test-only delta. Exact-head CI remains pending, so this review does not establish green CI or merge readiness.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

…g send

4813a5c settles a send once its message is saved, so Steer and Queue
work while main is still starting the response. The image-queue spec held
only the start receipt and expected Attach to stay disabled through it.
That expectation followed from the old send settling at the start
acknowledgement. Locally it still passed because its first check landed
before the save finished. On Linux CI the save was already done, and it
failed both attempts (run 38692828176).

The attach gate exists so that a failed send can restore its own payload
without merging in newly added files. Once the message is saved, a start
failure no longer restores anything, so intake may reopen during start
like Steer and Queue. The spec now holds the save receipt and then the
start receipt. Attach is disabled until the save lands and enabled while
the start is still held. The image and text are queued in that window,
and Stop still pauses both without sending either. Each phase is now
deterministic: the old send path fails the new check every time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found in this delta. Exact-head CI is still incomplete.

Reviewed changes since fee9ae3, covering the complete one-commit, one-file delta and its composer, send-lifecycle, IPC-handler and mock-provider consumers. This focused incremental review used the authoritative incremental/full-diff hunks and commit artifact, relying on prior reviews for unchanged implementation rather than repeating the 111-file review.

  • Added separate receipt gates: Held the first-message save and generation-start acknowledgements independently, checking attachment intake stays closed before save acknowledgement and reopens while start acknowledgement remains held.
  • Preserved queue and Stop coverage: Queued image and text follow-ups during the held-start window, then retained the checks that Stop leaves both paused without sending either to the model.
  • Verified available local checks: Passed E2E type-checking, changed-file lint, collection of all 19 queue specs, and 60 focused attachment/composer, queue and transition tests. These checks do not execute the changed Electron test; Electron execution was not attempted on Linux under the repository's macOS-only E2E guidance. Exact-head CI was queued when checked, so this review does not establish green CI or merge readiness.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

@sambitcreate
sambitcreate merged commit 75952ea into main Oct 15, 2026
26 checks passed
@sambitcreate
sambitcreate deleted the claude/bot-chat-activity-d-phones branch October 15, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants