diff --git a/.agents/skills/verify-deepseekbot/features/README.md b/.agents/skills/verify-deepseekbot/features/README.md index 1e2857c570..b12efbcf2d 100644 --- a/.agents/skills/verify-deepseekbot/features/README.md +++ b/.agents/skills/verify-deepseekbot/features/README.md @@ -52,6 +52,7 @@ The check fails for a missing feature row, required section, local link or scrip an index row's scripted journeys disagree with its feature file. It runs in lint. Passing checks only reference integrity: update and run the affected paths when the product changes. +| Cloud Agent Host connections | yes for Agent response; no for credential management | isolated Host plus real TOB UAT, two Hosts for collaboration | [cloud-host-connections.md](cloud-host-connections.md) | | IM conversation mirroring and forwarding settings | no for settings; yes for a model-generated reply | `verify-app` settings path; real IM destination required for delivery | [im-conversation-mirroring.md](im-conversation-mirroring.md) | ## Not mapped yet diff --git a/.agents/skills/verify-deepseekbot/features/cloud-host-connections.md b/.agents/skills/verify-deepseekbot/features/cloud-host-connections.md new file mode 100644 index 0000000000..6686e44617 --- /dev/null +++ b/.agents/skills/verify-deepseekbot/features/cloud-host-connections.md @@ -0,0 +1,26 @@ +# Cloud Agent Host connections + +## Sub-features + +Explicit cloud credential import, existing Bot binding, typed source delivery, replay, connection removal and bounded health work. + +## How to get to it (user POV) + +Import a trusted private Agent credential with `deepseekbot cloud import --input-stdin`, then open Bot settings → Apps and bind the returned account to one local Bot. CLI parity: `cloud list`, `cloud policy --input-stdin`, `cloud remove --input-stdin --confirm`, `im-bind --app ` and `im-status `. + +## Driving it with verify-app + +Reserve the shared QA lane before starting Hosts. Use two distinct isolated DSH Profiles, ports, local Bots and cloud Agent credentials in a synthetic UAT channel. Never reuse another task's running Host or move its credentials. + +1. Before import, capture the Bot's existing Apps list. Import through stdin and reopen the list; verify canonical remote name/account and explicit binding. Capture light/dark evidence; do not capture the private input or Host login URL. +2. Send one typed request to the bound remote Agent. Correlate Cloud channel sequence, local Source Event, Inbox admission and Orchestrator run. Ordinary unrelated text and result messages must not independently wake the Bot. Repeat with explicit ordinary-message immediate wake and a local Channel bridge whose members wake on all messages; unauthorized requests stay context-only. Revoke the local allow-list after admission and check current source consumption refuses execution. +3. Interrupt delivery acknowledgement, resume, and verify one canonical source and one admission. Plain HTTP source tests are not a real network-interruption result. +4. Remove the connection with its current revision. Confirm traffic stops before credential cleanup and that responses already in flight cannot advance the checkpoint. Retry failed cleanup; reimport must require explicit local policy again. +5. Use `cloud-work --input-stdin` with an admitted Source Event ID to inspect/execute/forward through the same Tool implementation. Verify pause/revocation, malformed canonical responses, expired requests and recovery from uncertain result/forward delivery. No arbitrary shell or supplied Human identity is accepted. +6. For the full collaboration gate, separately prove the bounded work owner, original-Human policy, A-to-B forwarding, one execution, result delivery and both Host restarts through real Cloudflare UAT. Independent Node processes, fake model adapters and source tests do not pass this gate. + +## Gotchas + +On the Chinese shell, pass `--locale zh --bot-mode`. Open the Messages rail panel before selecting a local group. Wait for that group's composer, not just its text: a sidebar preview can satisfy a text wait while the main pane remains Home. Inspect the screenshot and ARIA tree before accepting the journey. + +HTTP history is the first transport; WebSocket remains separate. Import/binding does not grant shell access, remote approval authority, or membership in another organization. The protected local steward Tob is not this cloud Agent. Native/phone/Android and Human QA remain separate evidence categories. diff --git a/.github/pr-screenshots/2157-cloud-host/README.md b/.github/pr-screenshots/2157-cloud-host/README.md new file mode 100644 index 0000000000..37fb3ff466 --- /dev/null +++ b/.github/pr-screenshots/2157-cloud-host/README.md @@ -0,0 +1,67 @@ +# Host cloud connection UAT checkpoint + +Issue #2155 / Draft #2157; two distinct isolated DSH Profiles and Bots, real Cloudflare UAT. No Prod or merge. + +- Initial import/binding runtime: `1a4983f5a69e7f5c7d44ef9fae421912852fde7e`. +- Restart/readback and after screenshots: `eee4047bf8ca51dcf81a2fadfc1935e15f0dc629`. +- Cloud runtime: `3eeed5b05782276b1a02cc2412dd04ab0ec60bca`, schemas 5/4/5. +- `context-replay-light.png` / `context-replay-dark.png`: actual local Messages panel after permission and crash tests, 1440×900, Chinese locale, zero browser-console issues. The three visible messages correlate with canonical local group history; the right pane has no task Session. This image alone does not prove absence of execution. +- Permission/removal/interrupted-checkpoint runtime: `416ac3bec6f5ce095220feb84f09538ea7e748db`. +- Before image shows the imported account **before binding**, not a baseline without this feature. After images show the existing Apps surface retaining its binding after restart. Viewport 1440×900, Chinese locale. Browser console checks reported zero issues. Images were visually inspected. + +## Observed + +Private CLI import qualified separate channel Agents and saved credentials through the native Credentials provider. A bound through the actual Apps picker; B bound through the parity CLI. Both reported receiving. Human plain text advanced both cursors without a new model turn. Explicit Messaging grants then permitted one fixed CLI send from each real Host; cloud history confirmed the two distinct canonical Agent authors. Both Profiles restarted without deletion; retrying the original Outbox request IDs retained the same receipts and exactly four total cloud messages. The fourth message was a fixed health request while both local work allow-lists were empty; A's model trace stayed at its earlier successful local DM turn, and B still had no model session. This is not an execution-ledger proof. + +The initial OpenCode Go model probe failed with quota 429. Only the two synthetic Bots were changed to the already configured official DeepSeek route; A's real DM returned 43. This does not prove cloud work execution or B model readiness. + +## Permission and recovery follow-up + +`permission-recovery-uat.json` records the next real UAT gate on runtime `416ac3bec`: + +- An explicit Messaging grant allowed collection of ordinary text. Even after setting its ordinary wake to immediate and bridging it into a local group whose Bot wakes on all messages, plain text and a typed request from a Human absent from the cloud allow-list remained context. Both appeared in canonical local group messages; the prior model turn did not change. Temporary policy overrides were restored. +- Removing Host B's connection stopped delivery and confirmed native credential deletion. A subsequent cloud message left B's cursor unchanged while disabled. Reimporting the original private credential reset local work policy to default-deny and resumed history. This tests local removal; it does not claim remote Agent revocation. +- An external Node preload, scoped to Host A's exact temporary Profile and cursor statement, sent SIGKILL after Source Event/Inbox commit but immediately before cursor update. The stopped Host had one canonical source, one admission and cursor 8. A clean restart without the preload replayed the cloud message, retained those same counts and advanced to cursor 9. The model turn stayed unchanged. This is deliberate fault injection against a real UAT message, not an unmodified-process crash claim. +- Both task-owned Hosts are healthy after restoration. No other Profile, native device or production resource was changed. + +Before the permission fix, a focused regression using the real Core/Messaging implementation reproduced an unauthorized ordinary-message wake. After the fix, the cloud suite passed 14 tests and the wider Messaging boundary set passed 175. Current source CI passed: [run 39093482065](https://github.com/BotHarness/DeepSeekBot/actions/runs/39093482065). The live-model workflow was skipped; the local DM and UAT observations above are separate evidence. + +## Retained failures and limits + +Initial English navigation selector did not match the Chinese shell. The binding action succeeded, but waiting for nonexistent “已绑定” text timed out; formal readback and fresh screenshots confirmed the actual Bot-name/unbind state. Sandboxed process checks falsely reported dead Hosts; checks with local process access passed, and the attempted relaunch safely refused a running PID. No Profile was replaced. + +Initial CI had two endpoint inventory failures and one real recovery-mode startup failure. The fix defers cloud Provider registration when the Operational Database is unavailable; 51 entry/cloud tests, direct root/mobile typechecks, build and lint passed. The aggregate typecheck wrapper hit pnpm's existing dependency-symlink task-state restriction; its two underlying checks passed separately. The follow-up source CI passed as linked above. + +The interruption test's first cleanup used an unsupported `--inherit` flag for Messaging policy. The documented stdin payload restored inheritance successfully; connection traffic and Host health were checked afterward. Additional group capture attempts retained selector/locale timeouts. One text wait matched a sidebar preview while the main pane remained Home; visual inspection rejected that apparent pass. These captures are not proof of a successful message-page journey. The corrected path explicitly opens the Messages rail panel, selects the group and waits for its composer; the actual message panel and empty task-session area were visually inspected. + +Pending at that checkpoint: WebSocket, organization-wide Agent membership and a fresh mobile-to-two-Host run (the latter is qualified below). The following bounded-work follow-up supersedes the earlier execution-pending status. Current-consumption policy revocation and another local Bot's lack of execution authority have source-test coverage; they are not yet a live work-execution proof. Phone, Android and Human QA are not established by this desktop Host evidence. No video was captured in this checkpoint. + +## Bounded model work and restart follow-up + +Runtime `f02c2423bce02c264548193ac738013f4eac83ab` / unchanged TOB UAT `3eeed5b` passed the two-Host fixed health path. After explicit owner instructions to the synthetic Bots, A called native `cloud_work inspect` and `forward`; B called `inspect` and `execute`. Channel sequences 13, 14 and 15 contain exactly the Human parent, Agent A child and Agent B result. Original Human and deadline are preserved. A ledger records zero executions; B records one. Restarting both exact Profiles and invoking the same Tool through formal CLI returned the same child and zero additional executions/messages. Local work policies were restored to deny-by-default. See [sanitized assertions](work-model-uat.json). + +First attempts are retained: sending while local policy denied work and reception collected only mentions produced no accepted source; that fixture setup was corrected. A then executed the initial request locally despite its updated saved persona. A fresh explicit owner DM established the dispatcher instruction; a new request qualified forwarding. This is a model-choice result, not a silent retry of an already executed request. The initial local execution remains a separate valid result. Both Bots produced real model replies; no synthetic model adapter is used in this UAT run. + +Source73tests, typecheck, lint, formatting and build passed before the runtime run. A sandboxed test invocation stalled before test output; it was terminated and the equivalent local-permission run passed in 6.34 seconds. Native phone/Android, hardware diversity, general commands, current-policy revocation during an actual effect, and video are not established by this follow-up. + +The f02c2423b CI affected suite passed 2,672 tests and failed its direct-tool catalog assertion, which assumed every Bot tool uses the generic runner. The follow-up records the dedicated `cloud-work` command and makes the parity checker reject generic `bot-tool` as coverage for `cloud_work`; focused catalog/owner/checker tests passed 19. The production runtime is unchanged by that test/checker correction. A new activity capture initially used an accessibility-only label as visible text and timed out; the corrected navigation passed, but shows the owner's acknowledgement rather than the cloud result, so it is not published as work-result proof. + +## Phone to both native Hosts — 2026-10-18 UAT + +Mobile JS / assigned qa/batch-19: `247a727b8ee832f1fd21318b2c0063a1b2f3c53b`; installed native inputs `494a20f1e`, unchanged by this JS-only window. Host runtime remains `f02c2423bce02c264548193ac738013f4eac83ab`, cloud remains `3eeed5b`. [CI 39095884899](https://github.com/BotHarness/DeepSeekBot/actions/runs/39095884899) passed all applicable checks at 247a727b8, including the affected suite; the optional model workflow was skipped. This evidence update changes no executable code. + +The actual iOS Simulator member UI selected the existing organization and host-uat channel, then pressed **Request health check** for Host UAT A. Cloud sequences **16 → 17 → 18** are the phone Human request, A's forwarded request and B's completed result. Both Hosts used real native model tools; the canonical ledgers record **A=0, B=1** executions with original Human/deadline preserved. The phone used Reload messages and visibly displayed all three records. See [machine assertions](phone-work-uat.json). + +Images are English/light at 402×874, visually inspected. `phone-before.png` is the same feature before this request, not a baseline without the implementation. `phone-result.png` shows the exact three new messages. `phone-restart.png` and `phone-logout.png` show verified-session and remote-revoked states. [Request recording](phone-request.mp4) is the first 18 seconds of the post-authentication capture; [result recording](phone-result.mp4) is the full 35-second reload/scroll capture, re-encoded for size. Original local recordings remain available. No credentials or personal mailbox appear; IDs belong to synthetic test users/Agents. + +Independent synthetic staging authentication was prepared through the authorized test driver; this run does not re-prove Hosted AuthKit/PKCE. Refresh through the UI rotated credentials, cold restart retained the same verified identity/session, and Sign out cleared mobile secrets while retaining trusted configuration. WorkOS readback confirmed that exact session was revoked, latest refresh rejected and baseline sessions preserved. Both independent A/B CLI sessions then passed whoami. The redundant cleanup revoke returned a generic helper failure after logout had already been verified; the exact temporary fixture was removed after those independent checks. No WorkOS configuration was changed. + +Initial Metro resolved localhost to IPv6 and the Simulator could not connect to 127.0.0.1; restarting only this runner's Metro with IPv4 resolved it. A cold-start selector ran too early; the loaded page was then observed before proceeding. The generic back command is not available on the presented iOS sheet; the visible native downward-dismiss gesture returned successfully. These are retained test/setup observations, not omitted failures. + +The runner closed its driver and own 8091 Metro, restored both Host work policies, and released the assigned lane. Existing shared data, three untracked QA documents, physical-phone 8097 and shared Host 39100 were preserved. No native build, install, erase, Prod operation or merge occurred. Android, physical hardware diversity, Human QA and a new text-size/theme matrix are not claimed by this increment. WebSocket, organization-wide Agent membership and wider live revocation remain open goal gates. + +### Live completed-request permission fences + +After the phone run, formal `deepseekbot cloud-work` exercised the same native Tool owner against those real admitted sources. Removing the original Human refused both A forwarding replay and B execution replay (`CLOUD_WORK_DENIED`). With Human authority restored but no target grant, A forwarding replay refused (`CLOUD_FORWARD_DENIED`). B could not add a second hop, A could not target itself (`FORWARD_LIMIT`), and A could not execute after committing a forward (`CLOUD_WORK_CONFLICT`). An authorized B replay returned executed0 and canonical executionCount1; cloud history remained at sequence18. All temporary policies were restored. [Assertions](work-denials-uat.json). + +This closes six real completed-request denial checks; it does not simulate revocation racing a new running effect or remote Agent credential revocation. Those narrower limits remain explicit. diff --git a/.github/pr-screenshots/2157-cloud-host/after-restart-dark.png b/.github/pr-screenshots/2157-cloud-host/after-restart-dark.png new file mode 100644 index 0000000000..0a3fa93397 Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/after-restart-dark.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/after-restart-light.png b/.github/pr-screenshots/2157-cloud-host/after-restart-light.png new file mode 100644 index 0000000000..4146d6c31e Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/after-restart-light.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/before-binding.png b/.github/pr-screenshots/2157-cloud-host/before-binding.png new file mode 100644 index 0000000000..083a06d536 Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/before-binding.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/context-replay-dark.png b/.github/pr-screenshots/2157-cloud-host/context-replay-dark.png new file mode 100644 index 0000000000..f2d399843a Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/context-replay-dark.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/context-replay-light.png b/.github/pr-screenshots/2157-cloud-host/context-replay-light.png new file mode 100644 index 0000000000..d2a49f73b3 Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/context-replay-light.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/permission-recovery-uat.json b/.github/pr-screenshots/2157-cloud-host/permission-recovery-uat.json new file mode 100644 index 0000000000..96e99d8121 --- /dev/null +++ b/.github/pr-screenshots/2157-cloud-host/permission-recovery-uat.json @@ -0,0 +1,41 @@ +{ + "runtimeSha": "416ac3bec6f5ce095220feb84f09538ea7e748db", + "cloudRuntimeSha": "3eeed5b05782276b1a02cc2412dd04ab0ec60bca", + "environment": "UAT", + "wakeBoundary": { + "runtime": "416ac3bec", + "explicitOrdinaryImmediate": true, + "localGroupWakeAll": true, + "plainAndDeniedRequestVisibleInLocalGroup": true, + "modelTurnUnchanged": true, + "cursor": 7, + "policyRestoration": "restored" + }, + "localRemoval": { + "cursor": 7, + "credentialRemoved": true, + "sequence": 8, + "disabledCursorStable": true, + "restoredWithEmptyPolicy": true, + "resumedHistory": true + }, + "checkpointReplay": { + "runtime": "416ac3bec6f5ce095220feb84f09538ea7e748db", + "fault": "own Host SIGKILL immediately before cursor update, external Node preload", + "interrupted": { + "sources": 1, + "admissions": 1, + "cursor": 8 + }, + "recovered": { + "sources": 1, + "admissions": 1, + "cursor": 9 + }, + "modelTurnUnchanged": true, + "policyRestoration": "restored", + "cleanupCorrection": "invalid --inherit on messaging-group-policy corrected to documented stdin inheritance" + }, + "scope": "Two isolated real DSH Hosts; no bounded work executor or model forwarding yet", + "sourceCI": "https://github.com/BotHarness/DeepSeekBot/actions/runs/39093482065" +} diff --git a/.github/pr-screenshots/2157-cloud-host/phone-before.png b/.github/pr-screenshots/2157-cloud-host/phone-before.png new file mode 100644 index 0000000000..78adba2cb1 Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/phone-before.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/phone-logout.png b/.github/pr-screenshots/2157-cloud-host/phone-logout.png new file mode 100644 index 0000000000..86c1106fbc Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/phone-logout.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/phone-request.mp4 b/.github/pr-screenshots/2157-cloud-host/phone-request.mp4 new file mode 100644 index 0000000000..d77dc96e0a Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/phone-request.mp4 differ diff --git a/.github/pr-screenshots/2157-cloud-host/phone-restart.png b/.github/pr-screenshots/2157-cloud-host/phone-restart.png new file mode 100644 index 0000000000..8a89167ebb Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/phone-restart.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/phone-result.mp4 b/.github/pr-screenshots/2157-cloud-host/phone-result.mp4 new file mode 100644 index 0000000000..ebdcdebd65 Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/phone-result.mp4 differ diff --git a/.github/pr-screenshots/2157-cloud-host/phone-result.png b/.github/pr-screenshots/2157-cloud-host/phone-result.png new file mode 100644 index 0000000000..7d4c1ad2e0 Binary files /dev/null and b/.github/pr-screenshots/2157-cloud-host/phone-result.png differ diff --git a/.github/pr-screenshots/2157-cloud-host/phone-work-uat.json b/.github/pr-screenshots/2157-cloud-host/phone-work-uat.json new file mode 100644 index 0000000000..f38e0a8c6c --- /dev/null +++ b/.github/pr-screenshots/2157-cloud-host/phone-work-uat.json @@ -0,0 +1,70 @@ +{ + "environment": "UAT", + "mobileJs": "247a727b8ee832f1fd21318b2c0063a1b2f3c53b", + "hostRuntime": "f02c2423bce02c264548193ac738013f4eac83ab", + "cloudRuntime": "3eeed5b05782276b1a02cc2412dd04ab0ec60bca", + "nativeBuild": "494a20f1e", + "simulator": "BotHarness e2e", + "viewport": "402x874", + "locale": "en-US", + "theme": "light", + "requestSource": "Actual phone UI Request health check on Host UAT A; no CLI request injection", + "messages": [ + { + "sequence": 16, + "kind": "work_request", + "authorType": "human" + }, + { + "sequence": 17, + "kind": "work_request", + "authorType": "agent" + }, + { + "sequence": 18, + "kind": "work_result", + "authorType": "agent" + } + ], + "sameChannel": true, + "originalHumanAndDeadlinePreserved": true, + "executionCounts": { + "a": 0, + "b": 1 + }, + "phoneVisibleResult": true, + "auth": { + "syntheticIndependentSession": true, + "verifiedUser": true, + "refreshRotated": true, + "coldRestartSameSession": true, + "localSignedOut": true, + "remoteRevoked": true, + "latestRefreshRejected": true, + "otherSessionsPreserved": true, + "cliPreserved": { + "a": true, + "b": true + } + }, + "restored": { + "hostPolicies": true, + "workosConfigurationUnchanged": true, + "phoneFixtureRevoked": true, + "ownMetroStopped": true, + "driverClosed": true + }, + "retainedFailures": [ + "Initial Metro resolved localhost to IPv6; own server restarted IPv4.", + "Cold-start selector was attempted before the page finished loading.", + "Native back command unavailable for modal sheet; observed native downward dismissal returned to channel.", + "Redundant cleanup revoke returned a generic helper failure after verified logout; exact fixture file removed only after remote revocation and latest-refresh rejection were confirmed." + ], + "limits": [ + "Synthetic independent staging session; this run does not re-prove Hosted AuthKit/PKCE.", + "Two real isolated DSH Hosts on one Mac; not physical hardware diversity.", + "Fixed health check only, not arbitrary commands.", + "No Android, physical iPhone, Human QA or new Dynamic Type/theme matrix in this increment.", + "HTTP history transport; WebSocket and organization-wide Agent membership remain pending." + ] +} diff --git a/.github/pr-screenshots/2157-cloud-host/transport-uat.json b/.github/pr-screenshots/2157-cloud-host/transport-uat.json new file mode 100644 index 0000000000..5607ce9097 --- /dev/null +++ b/.github/pr-screenshots/2157-cloud-host/transport-uat.json @@ -0,0 +1,32 @@ +{ + "runtime": "eee4047bf8ca51dcf81a2fadfc1935e15f0dc629", + "initialRuntime": "1a4983f5a69e7f5c7d44ef9fae421912852fde7e", + "cloudRelease": "3eeed5b05782276b1a02cc2412dd04ab0ec60bca", + "twoIsolatedHosts": true, + "canonicalAgentAuthors": true, + "exactFourMessagesAfterRestartAndIdempotentResend": true, + "hosts": [ + { + "side": "a", + "cursor": 4, + "receiving": true, + "localWorkAllowListEmpty": true, + "noNewTurnObserved": true + }, + { + "side": "b", + "cursor": 4, + "receiving": true, + "localWorkAllowListEmpty": true, + "noNewTurnObserved": true + } + ], + "limits": [ + "No bounded work execution or model-mediated forwarding proof", + "No receipt/checkpoint interruption injection", + "No connection-removal runtime proof", + "Ordinary message wake overrides and bridge paths require further boundary coverage", + "B trace reports no session; not an execution-ledger proof", + "No WebSocket, organization-wide Agent membership, phone, Android, Human QA, Prod or merge" + ] +} diff --git a/.github/pr-screenshots/2157-cloud-host/work-denials-uat.json b/.github/pr-screenshots/2157-cloud-host/work-denials-uat.json new file mode 100644 index 0000000000..670b76e152 --- /dev/null +++ b/.github/pr-screenshots/2157-cloud-host/work-denials-uat.json @@ -0,0 +1,35 @@ +{ + "hostRuntime": "f02c2423bce02c264548193ac738013f4eac83ab", + "cloudRuntime": "3eeed5b05782276b1a02cc2412dd04ab0ec60bca", + "cases": [ + { + "name": "original Human removed: A cannot replay forward", + "code": "CLOUD_WORK_DENIED" + }, + { + "name": "original Human removed: B cannot replay execution", + "code": "CLOUD_WORK_DENIED" + }, + { + "name": "target grant removed: A cannot replay forward", + "code": "CLOUD_FORWARD_DENIED" + }, + { + "name": "B cannot add a second delegation hop", + "code": "FORWARD_LIMIT" + }, + { + "name": "A cannot forward to itself", + "code": "FORWARD_LIMIT" + }, + { + "name": "A cannot execute after committing forwarding", + "code": "CLOUD_WORK_CONFLICT" + } + ], + "authorizedReplayAdditionalExecutions": 0, + "canonicalExecutionCount": 1, + "extraCloudMessages": 0, + "scope": "Formal CLI invokes the same native Tool owner on real admitted phone sources. These are completed-request replay fences, not a live revocation race during a new effect.", + "restored": true +} diff --git a/.github/pr-screenshots/2157-cloud-host/work-model-uat.json b/.github/pr-screenshots/2157-cloud-host/work-model-uat.json new file mode 100644 index 0000000000..70963d0d7c --- /dev/null +++ b/.github/pr-screenshots/2157-cloud-host/work-model-uat.json @@ -0,0 +1,48 @@ +{ + "runtime": "f02c2423bce02c264548193ac738013f4eac83ab", + "service": "3eeed5b05782276b1a02cc2412dd04ab0ec60bca", + "nativeModelTools": { + "a": ["inspect", "forward"], + "b": ["inspect", "execute"] + }, + "sameCloudChannel": true, + "originalHumanAndDeadlinePreserved": true, + "canonicalMessages": [ + { + "sequence": 13, + "kind": "work_request", + "authorType": "human" + }, + { + "sequence": 14, + "kind": "work_request", + "authorType": "agent" + }, + { + "sequence": 15, + "kind": "work_result", + "authorType": "agent" + } + ], + "counts": { + "a": { + "state": "forwarded", + "execution_count": 0 + }, + "b": { + "state": "finished", + "execution_count": 1 + } + }, + "afterBothHostRestarts": { + "sameForwardedChild": true, + "bAdditionalExecutions": 0, + "extraCloudMessages": 0 + }, + "firstFailures": [ + "Denied request with mention-only reception did not create a source; fixed test precondition.", + "A initially executed instead of forwarding; fresh request after explicit owner DM used configured dispatcher role." + ], + "scope": "Two isolated local DSH Hosts via real UAT; not physical hardware diversity or a fresh phone run.", + "policiesRestored": true +} diff --git a/.github/pr-screenshots/tob-agent-health/01-member-agent.png b/.github/pr-screenshots/tob-agent-health/01-member-agent.png new file mode 100644 index 0000000000..48593b30a7 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/01-member-agent.png differ diff --git a/.github/pr-screenshots/tob-agent-health/01-request.gesture-telemetry.json b/.github/pr-screenshots/tob-agent-health/01-request.gesture-telemetry.json new file mode 100644 index 0000000000..94471579c5 --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/01-request.gesture-telemetry.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "generatedAt": "2026-10-18T01:53:49.571Z", + "events": [ + { + "kind": "tap", + "tMs": 1696.541015625, + "x": 201, + "y": 528, + "referenceWidth": 402, + "referenceHeight": 874 + } + ] +} diff --git a/.github/pr-screenshots/tob-agent-health/01-request.mp4 b/.github/pr-screenshots/tob-agent-health/01-request.mp4 new file mode 100644 index 0000000000..a380a9abac Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/01-request.mp4 differ diff --git a/.github/pr-screenshots/tob-agent-health/02-request-sent.png b/.github/pr-screenshots/tob-agent-health/02-request-sent.png new file mode 100644 index 0000000000..73a085ae5c Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/02-request-sent.png differ diff --git a/.github/pr-screenshots/tob-agent-health/02-result.gesture-telemetry.json b/.github/pr-screenshots/tob-agent-health/02-result.gesture-telemetry.json new file mode 100644 index 0000000000..62389ac279 --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/02-result.gesture-telemetry.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "generatedAt": "2026-10-18T01:54:21.294Z", + "events": [ + { + "kind": "tap", + "tMs": 1774.493896484375, + "x": 201, + "y": 361, + "referenceWidth": 402, + "referenceHeight": 874 + } + ] +} diff --git a/.github/pr-screenshots/tob-agent-health/02-result.mp4 b/.github/pr-screenshots/tob-agent-health/02-result.mp4 new file mode 100644 index 0000000000..ba3ef5427e Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/02-result.mp4 differ diff --git a/.github/pr-screenshots/tob-agent-health/03-denied.gesture-telemetry.json b/.github/pr-screenshots/tob-agent-health/03-denied.gesture-telemetry.json new file mode 100644 index 0000000000..750e1ea3c7 --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/03-denied.gesture-telemetry.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "generatedAt": "2026-10-18T01:56:25.945Z", + "events": [ + { + "kind": "tap", + "tMs": 1769.397705078125, + "x": 201, + "y": 696, + "referenceWidth": 402, + "referenceHeight": 874 + } + ] +} diff --git a/.github/pr-screenshots/tob-agent-health/03-denied.mp4 b/.github/pr-screenshots/tob-agent-health/03-denied.mp4 new file mode 100644 index 0000000000..443ed88698 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/03-denied.mp4 differ diff --git a/.github/pr-screenshots/tob-agent-health/03-result.png b/.github/pr-screenshots/tob-agent-health/03-result.png new file mode 100644 index 0000000000..1737d3c327 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/03-result.png differ diff --git a/.github/pr-screenshots/tob-agent-health/04-restart-results.gesture-telemetry.json b/.github/pr-screenshots/tob-agent-health/04-restart-results.gesture-telemetry.json new file mode 100644 index 0000000000..7e68f87f43 --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/04-restart-results.gesture-telemetry.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "generatedAt": "2026-10-18T01:58:39.612Z", + "events": [ + { + "kind": "scroll", + "tMs": 137, + "x": 201, + "y": 721, + "x2": 201, + "y2": 153, + "referenceWidth": 402, + "referenceHeight": 874, + "durationMs": 400, + "contentDirection": "down", + "amount": 0.65, + "pixels": 568 + } + ] +} diff --git a/.github/pr-screenshots/tob-agent-health/04-restart-results.mp4 b/.github/pr-screenshots/tob-agent-health/04-restart-results.mp4 new file mode 100644 index 0000000000..004dd979d5 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/04-restart-results.mp4 differ diff --git a/.github/pr-screenshots/tob-agent-health/04-result-visible.png b/.github/pr-screenshots/tob-agent-health/04-result-visible.png new file mode 100644 index 0000000000..9dc1632c4c Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/04-result-visible.png differ diff --git a/.github/pr-screenshots/tob-agent-health/05-denied-maximum-dark.png b/.github/pr-screenshots/tob-agent-health/05-denied-maximum-dark.png new file mode 100644 index 0000000000..20b2257308 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/05-denied-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-agent-health/06-zero-execution-maximum-dark.png b/.github/pr-screenshots/tob-agent-health/06-zero-execution-maximum-dark.png new file mode 100644 index 0000000000..7c181e0061 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/06-zero-execution-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-agent-health/07-restart-denied.png b/.github/pr-screenshots/tob-agent-health/07-restart-denied.png new file mode 100644 index 0000000000..341fbd7903 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/07-restart-denied.png differ diff --git a/.github/pr-screenshots/tob-agent-health/08-signed-out.png b/.github/pr-screenshots/tob-agent-health/08-signed-out.png new file mode 100644 index 0000000000..71c8f61970 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/08-signed-out.png differ diff --git a/.github/pr-screenshots/tob-agent-health/README.md b/.github/pr-screenshots/tob-agent-health/README.md new file mode 100644 index 0000000000..569cc0bc3d --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/README.md @@ -0,0 +1,27 @@ +# T3 fixed health check — native UAT evidence + +- Runtime: `8586130e5303d100e47f9f29d91e3e8cf1556012`; batch later fast-forwarded to `81b98eafc` for three inherited evidence JSON formatting changes only (no runtime changes). +- Installed SDK58 native binary: `494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b`; no native inputs changed, no rebuild. +- TOB service: `23db30d9e34b8c51c72909b0a118e23d7dccf70e`, UAT version `eb945fb5-c38b-44de-8f28-dc90f17112af`, D1/Org/Channel schemas5/4/4. +- One assigned iOS Simulator; synthetic B is an ordinary organization member. Authentication fixture uses a real independent WorkOS UAT session; this is not new Hosted AuthKit/browser qualification. + +## Observed path + +1. Owner CLI provisioned a dedicated Agent in the shared workspace's health-mobile channel. Its local operator explicitly allowed B. +2. B tapped **Request health check**. A separate Node reference process read the public UAT channel, obtained current authorization, persisted its execution/result state, and returned platform/architecture/runtime with executionCount1. A fresh process replay reported executionCount0; server history contained one result for that request. +3. The local allow-list was cleared. B tapped the same action at maximum Dynamic Type; the next request produced LOCAL_POLICY_DENIED and executionCount0. Owner CLI independently read exactly two requests/two correlated results, with actual B and Agent authors. +4. Six default/intermediate/maximum × light/dark states exposed a reachable request action; owner creation controls were absent for B. No new text input was added. Large text scrolls long IDs and result text; screenshots05/06 show consecutive portions. +5. Cold restart, actual UI refresh and reload retained the same session/workspace and result history. Phone own logout was independently verified revoked, its latest refresh token rejected, local session cleared; A/B CLI sessions remained usable. + +## Files and limits + +- 01/02: member Agent and request acknowledgement; 03 is the initial history viewport (result body below the fold), 04 shows completed result. +- action-*: six request-control states;05/06 show maximum-text denial;07 shows denial after restart;08 signed out. +- Short videos01/03 show requests,02 the initial history load,04 scrolls to the retained result after restart. None records login credentials or token values. +- JSON files contain only bounded assertions; source49workerd/37Node/2wizard and mobile629passed/8skipped are separate evidence categories. + +First failures retained in task history: initial CLI fixture selected an organization B had not joined and was correctly denied; first deploy smoke timed out/failure without detailed classification, subsequent read-only smoke passed; mobile suite sandbox loopback EPERM resolved by permitted local listening; coldstart selector fired before UI ready; checkpoint during active history request returned SESSION_BUSY and later passed; inherited three evidence JSON files failed CI formatting, corrected without runtime change. Do not reinterpret those first attempts as successful. + +Own test Agent revoked and verified-revoked phone fixture removed. Light/large restored, own app/driver/Metro stopped, Batch18 released; shared resources preserved. UAT only, no Prod/merge. + +Not qualified: Android, physical phone, Human QA, new Hosted AuthKit, WebSocket, DSH adapter, model work, two isolated Hosts or arbitrary side effects. T3 and overall goal remain open. diff --git a/.github/pr-screenshots/tob-agent-health/action-appearance.json b/.github/pr-screenshots/tob-agent-health/action-appearance.json new file mode 100644 index 0000000000..1ea47549e9 --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/action-appearance.json @@ -0,0 +1,38 @@ +[ + { + "size": "large", + "theme": "light", + "requestReachable": true, + "ownerCreationAbsent": true + }, + { + "size": "large", + "theme": "dark", + "requestReachable": true, + "ownerCreationAbsent": true + }, + { + "size": "accessibility-medium", + "theme": "light", + "requestReachable": true, + "ownerCreationAbsent": true + }, + { + "size": "accessibility-medium", + "theme": "dark", + "requestReachable": true, + "ownerCreationAbsent": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "light", + "requestReachable": true, + "ownerCreationAbsent": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "dark", + "requestReachable": true, + "ownerCreationAbsent": true + } +] diff --git a/.github/pr-screenshots/tob-agent-health/action-default-dark.png b/.github/pr-screenshots/tob-agent-health/action-default-dark.png new file mode 100644 index 0000000000..c19108b280 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/action-default-dark.png differ diff --git a/.github/pr-screenshots/tob-agent-health/action-default-light.png b/.github/pr-screenshots/tob-agent-health/action-default-light.png new file mode 100644 index 0000000000..e726a29fe2 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/action-default-light.png differ diff --git a/.github/pr-screenshots/tob-agent-health/action-intermediate-dark.png b/.github/pr-screenshots/tob-agent-health/action-intermediate-dark.png new file mode 100644 index 0000000000..1a5b1a59c7 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/action-intermediate-dark.png differ diff --git a/.github/pr-screenshots/tob-agent-health/action-intermediate-light.png b/.github/pr-screenshots/tob-agent-health/action-intermediate-light.png new file mode 100644 index 0000000000..e9c167fc6d Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/action-intermediate-light.png differ diff --git a/.github/pr-screenshots/tob-agent-health/action-maximum-dark.png b/.github/pr-screenshots/tob-agent-health/action-maximum-dark.png new file mode 100644 index 0000000000..4728cbe235 Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/action-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-agent-health/action-maximum-light.png b/.github/pr-screenshots/tob-agent-health/action-maximum-light.png new file mode 100644 index 0000000000..89c883521f Binary files /dev/null and b/.github/pr-screenshots/tob-agent-health/action-maximum-light.png differ diff --git a/.github/pr-screenshots/tob-agent-health/cli-uat.json b/.github/pr-screenshots/tob-agent-health/cli-uat.json new file mode 100644 index 0000000000..86d3dc00ec --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/cli-uat.json @@ -0,0 +1,8 @@ +{ + "stableRequestReplay": true, + "separateProcessExecutionOnce": true, + "memberAllowedByCloudButDeniedLocally": true, + "explicitLocalMemberGrant": true, + "exactThreeRequestsThreeResults": true, + "revokedAgentDenied": true +} diff --git a/.github/pr-screenshots/tob-agent-health/mobile-process.json b/.github/pr-screenshots/tob-agent-health/mobile-process.json new file mode 100644 index 0000000000..71f2be2805 --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/mobile-process.json @@ -0,0 +1,5 @@ +{ + "firstExecution": 1, + "freshProcessReplay": 0, + "processed": 1 +} diff --git a/.github/pr-screenshots/tob-agent-health/mobile-readback.json b/.github/pr-screenshots/tob-agent-health/mobile-readback.json new file mode 100644 index 0000000000..9bb7751708 --- /dev/null +++ b/.github/pr-screenshots/tob-agent-health/mobile-readback.json @@ -0,0 +1,8 @@ +{ + "exactFourMessages": true, + "phoneMemberIdentity": true, + "requestResultCorrelation": true, + "completedOnce": true, + "localDenialZeroExecution": true, + "independentCliSessions": true +} diff --git a/.github/pr-screenshots/tob-channel-agents/01-agent-entry.png b/.github/pr-screenshots/tob-channel-agents/01-agent-entry.png new file mode 100644 index 0000000000..3e8144e9ec Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/01-agent-entry.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/02-created-maximum-dark.png b/.github/pr-screenshots/tob-channel-agents/02-created-maximum-dark.png new file mode 100644 index 0000000000..2a8b7749f6 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/02-created-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/03-credential-handoff-safe.png b/.github/pr-screenshots/tob-channel-agents/03-credential-handoff-safe.png new file mode 100644 index 0000000000..5a736c8812 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/03-credential-handoff-safe.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/04-phone-agent-reply.png b/.github/pr-screenshots/tob-channel-agents/04-phone-agent-reply.png new file mode 100644 index 0000000000..2b083ba009 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/04-phone-agent-reply.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/05-restart-retained-handoff.png b/.github/pr-screenshots/tob-channel-agents/05-restart-retained-handoff.png new file mode 100644 index 0000000000..e7e5d40688 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/05-restart-retained-handoff.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/06-revoke-maximum.png b/.github/pr-screenshots/tob-channel-agents/06-revoke-maximum.png new file mode 100644 index 0000000000..acede06d52 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/06-revoke-maximum.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/07-revoked-maximum.png b/.github/pr-screenshots/tob-channel-agents/07-revoked-maximum.png new file mode 100644 index 0000000000..f5d2078ad2 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/07-revoked-maximum.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/08-signed-out.png b/.github/pr-screenshots/tob-channel-agents/08-signed-out.png new file mode 100644 index 0000000000..b2c73cf9f7 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/08-signed-out.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/README.md b/.github/pr-screenshots/tob-channel-agents/README.md new file mode 100644 index 0000000000..b5456de972 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-agents/README.md @@ -0,0 +1,24 @@ +# Channel Agent native UAT evidence + +Batch17, 2026-10-18. Product/runtime `dc3ee2f09064de7e96169199c93a5cc68da400ea`, integrated batch same SHA. Initial `f25f5988a` differed only by one source-policy-prohibited comment; it was removed before captures and the app was cold-started. Signed native binary `494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b`; no native changes/build/erase. Service runtime `31e3a4a2880c05b670c2194d6b5a994dacfa297e`, UAT schema D1/Org/Channel5/4/3. Prod untouched; all PRs remain Draft/unmerged. + +## Observed behavior + +- `01-agent-entry`: owner channel Agent entry, before creating the test actor. +- `form-*`: six actual light/dark × default/intermediate/maximum text cases. Draft `Mobile echo` and keyboard focus retained. At maximum, input y217..274 and Create bottom365 are clear of keyboard top529. See `form-appearance-checks.json`. +- `02-created-maximum-dark`: phone created the channel Agent; after creation, the form scrolls to its top confirmation. +- `03-credential-handoff-safe`: safe management UI after explicit copy. Recording was stopped before copy; the copied credential went straight to an owner-only local file, was parsed/target-checked, and the simulator clipboard was cleared. No credential is in these artifacts. +- `04-phone-agent-reply`: phone sent `echo: Hello from the UAT phone`; independent reference Agent process read through public UAT HTTP and wrote its own author identity. Formal CLI read exactly the Human request and one Agent reply. A fresh second process produced zero new replies. See `mobile-readback.json`. +- `05-restart-retained-handoff`: cold-start and actual Refresh session retained the same private handoff. A second explicit copy was privately compared byte-for-byte as parsed data, then clipboard cleared again. The latest refresh token changed, with the same session identity. +- `06-revoke-maximum` / `07-revoked-maximum`: owner reached and used Revoke agent at maximum text size. Old credential read and send both returned `AGENT_ACCESS_DENIED`; see `mobile-revocation.json`. +- `08-signed-out`: local phone tokens removed, own remote session absent, latest refresh rejected, all pre-existing independent sessions preserved. + +Recordings: `create-agent.mp4`, `phone-agent-reply.mp4`, `restart-and-revoke.mp4` (restart/refresh portion only; recording stopped before copy), `revoke-agent.mp4`. Originals remain local; committed copies are 402px/12fps without audio. + +## Limits and first failures + +This is an unattended synthetic UAT session driven through the production UI/API; not a new Hosted AuthKit or Human QA result. It proves HTTP text identity/replay/revocation, not model work, shell execution, WebSocket, organization-wide Agent admission, two DSH Hosts, Android or physical iPhone. + +Initial driver attempts used wrong workspace label, tried an unavailable native Back control, and a hot refresh reset verified-session state. Navigation was corrected from actual snapshots and the final bundle cold-started before captures. Initial CI rejected a code comment; corrected exact-runtime CI39085993395 passed all applicable jobs. A redundant fixture cleanup request returned transport CHECK_FAILED after remote logout had already been independently verified; only the verified-revoked local phone fixture was removed. No cleanup success was inferred from that failed request. + +Batch17 released: light/large restored; own app/driver/Metro stopped. Original three QA untracked docs, installed app/Keychain, native Products/rollback, phone Metro and all Hosts preserved. The revoked Agent credential remains only in a private local test file. No secret was logged or published. diff --git a/.github/pr-screenshots/tob-channel-agents/create-agent.mp4 b/.github/pr-screenshots/tob-channel-agents/create-agent.mp4 new file mode 100644 index 0000000000..87ff890567 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/create-agent.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-agents/form-appearance-checks.json b/.github/pr-screenshots/tob-channel-agents/form-appearance-checks.json new file mode 100644 index 0000000000..6b4ee7da53 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-agents/form-appearance-checks.json @@ -0,0 +1,38 @@ +[ + { + "size": "large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "large", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "dark", + "draftPreserved": true, + "focused": true + } +] diff --git a/.github/pr-screenshots/tob-channel-agents/form-default-dark.png b/.github/pr-screenshots/tob-channel-agents/form-default-dark.png new file mode 100644 index 0000000000..96800aac9b Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/form-default-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/form-default-light.png b/.github/pr-screenshots/tob-channel-agents/form-default-light.png new file mode 100644 index 0000000000..69de9bf16f Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/form-default-light.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/form-intermediate-dark.png b/.github/pr-screenshots/tob-channel-agents/form-intermediate-dark.png new file mode 100644 index 0000000000..5edd19e8e6 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/form-intermediate-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/form-intermediate-light.png b/.github/pr-screenshots/tob-channel-agents/form-intermediate-light.png new file mode 100644 index 0000000000..75bfdb81e3 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/form-intermediate-light.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/form-maximum-dark.png b/.github/pr-screenshots/tob-channel-agents/form-maximum-dark.png new file mode 100644 index 0000000000..6c1265828a Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/form-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/form-maximum-light.png b/.github/pr-screenshots/tob-channel-agents/form-maximum-light.png new file mode 100644 index 0000000000..4ad0bbc135 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/form-maximum-light.png differ diff --git a/.github/pr-screenshots/tob-channel-agents/mobile-readback.json b/.github/pr-screenshots/tob-channel-agents/mobile-readback.json new file mode 100644 index 0000000000..27171a43c4 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-agents/mobile-readback.json @@ -0,0 +1,6 @@ +{ + "phoneIssuedAgentVisibleInCli": true, + "exactTwoMessageHistory": true, + "agentAuthorMatches": true, + "noDuplicateReply": true +} diff --git a/.github/pr-screenshots/tob-channel-agents/mobile-revocation.json b/.github/pr-screenshots/tob-channel-agents/mobile-revocation.json new file mode 100644 index 0000000000..0ca472db38 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-agents/mobile-revocation.json @@ -0,0 +1,4 @@ +{ + "phoneRevokedOldCredentialReadDenied": true, + "phoneRevokedOldCredentialSendDenied": true +} diff --git a/.github/pr-screenshots/tob-channel-agents/phone-agent-reply.mp4 b/.github/pr-screenshots/tob-channel-agents/phone-agent-reply.mp4 new file mode 100644 index 0000000000..c719907d77 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/phone-agent-reply.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-agents/restart-and-revoke.mp4 b/.github/pr-screenshots/tob-channel-agents/restart-and-revoke.mp4 new file mode 100644 index 0000000000..2356b2e38e Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/restart-and-revoke.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-agents/revoke-agent.mp4 b/.github/pr-screenshots/tob-channel-agents/revoke-agent.mp4 new file mode 100644 index 0000000000..35beb93610 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-agents/revoke-agent.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-guests/02-guest-entry.png b/.github/pr-screenshots/tob-channel-guests/02-guest-entry.png new file mode 100644 index 0000000000..714b4f3d08 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/02-guest-entry.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/03-invitation-created.png b/.github/pr-screenshots/tob-channel-guests/03-invitation-created.png new file mode 100644 index 0000000000..92094eabab Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/03-invitation-created.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/04-recipient-inbox.png b/.github/pr-screenshots/tob-channel-guests/04-recipient-inbox.png new file mode 100644 index 0000000000..401e095417 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/04-recipient-inbox.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/05-guest-channels.png b/.github/pr-screenshots/tob-channel-guests/05-guest-channels.png new file mode 100644 index 0000000000..a6e44daa2a Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/05-guest-channels.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/06-no-prejoin-history.png b/.github/pr-screenshots/tob-channel-guests/06-no-prejoin-history.png new file mode 100644 index 0000000000..82ad54557d Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/06-no-prejoin-history.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/10-restart-guest-history.png b/.github/pr-screenshots/tob-channel-guests/10-restart-guest-history.png new file mode 100644 index 0000000000..92ea4afb6c Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/10-restart-guest-history.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/11-revoked-phone-session.png b/.github/pr-screenshots/tob-channel-guests/11-revoked-phone-session.png new file mode 100644 index 0000000000..bfcb47b1b6 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/11-revoked-phone-session.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/12-owner-revoke-max.png b/.github/pr-screenshots/tob-channel-guests/12-owner-revoke-max.png new file mode 100644 index 0000000000..9e56f20a2c Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/12-owner-revoke-max.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/13-owner-revoked-max.png b/.github/pr-screenshots/tob-channel-guests/13-owner-revoked-max.png new file mode 100644 index 0000000000..8a747c7565 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/13-owner-revoked-max.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/README.md b/.github/pr-screenshots/tob-channel-guests/README.md new file mode 100644 index 0000000000..4e3ca94a5e --- /dev/null +++ b/.github/pr-screenshots/tob-channel-guests/README.md @@ -0,0 +1,31 @@ +# Direct channel guests — native UAT evidence + +Final mobile runtime: `bc08a47e6f060fe6caa6b879c4b2464e12d69e05`; compatible signed native binary: `494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b`. Shared iOS 26 Simulator, English, 402 × 874 logical pixels. Service `6226ec3af48d7c446c95421290aaa280ce8a7490`, Worker version `b8e22111-d975-4ad6-81b9-6d56e32fb84c`, D1/Org/Channel schema **5/4/2**. UAT only; no native rebuild, Prod changes or merge. + +## Observed behavior + +- Ordinary owner A issued a direct invitation to synthetic B from the phone. B accepted from its own phone inbox and opened Guest channels. Its organization selection was unchanged; no WorkOS membership was created in A's workspace. Sibling channels and the pre-join message were inaccessible. +- B sent `B joins only this channel from phone`; independent owner CLI read the exact author/text at sequence 2. Owner CLI revoked access; a replacement invitation was accepted through B's CLI to set up rejoin testing. The same phone session could read only after the new floor (2). A deliberate new phone send committed at sequence 3; owner CLI saw both sends and B saw only sequence 3. These are distinct operations across two grants, not duplicate delivery. +- B refreshed, cold-restarted, checked its saved session and opened the same guest channel with sequence 3. Owner CLI then revoked the active grant. Reload on the old phone session visibly returned `CHANNEL_ACCESS_DENIED`; independent B CLI was denied and its guest list omitted the channel. Already rendered, previously authorized text can remain on screen; the error proves the subsequent request was refused. +- A separate replacement grant was prepared via CLI. A's phone reached and used **Revoke channel access** at maximum text size. The row became Revoked; B's independent CLI again lost access. This proves the owner UI action separately from the earlier CLI-triggered revocation. +- Independent A/B phone sessions signed out through the UI. Each latest refresh token was rejected, its WorkOS session revoked and its local session removed; all pre-existing active sessions, including A/B CLI, remained intact. +- Six owner-invitation form and six guest-composer size/theme combinations retained exact draft and focus. The final maximum guest Send bottom was about 525px, above the predictive keyboard top at 529px; an actual send succeeded. Maximum owner revoke was reached through scrolling and operated. + +## Capture provenance + +| Files | Runtime / meaning | +| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `02-*`, `03-*`, `form-*`, `owner-invite.mp4` | `77cd444bffda36846838093ce6a25e17e7b7a951`: final channel-specific title/name and actual owner issuance; later changes affect accepted labels/composer spacing. | +| `04-*`, `05-*`, `06-*`, `recipient-accept.mp4` | `6f4d99fe3f2280584f26debefc26b53febc3a1c6`: real recipient acceptance and initial send. Video also includes the first failing composer matrix, not a final keyboard pass. | +| `message-*`, `10-*`–`13-*`, remaining videos | `bc08a47e6f060fe6caa6b879c4b2464e12d69e05`: corrected keyboard geometry, committed post-rejoin send, restart, old-phone refusal and owner revoke. Owner-revoke video ends during the request; `13-*` plus independent CLI readback prove completion. | +| `before-clipped-maximum-dark.png` | Earlier failing empty-history maximum-text composer; compare final `message-maximum-dark.png` at the same viewport/theme. | + +Videos are compressed to 402px / 12fps without audio. Captures contain only synthetic example.test users and test content; no credentials or invitation tokens. The [prior workspace entry](../tob-org-invitations/member-inbox.png) is a historical organization-only comparison, not a guest feature test. + +## First failures and limits + +The first form used a workspace title; corrected before issuance. The first empty-history maximum-text composer clipped input beneath the fixed header. A first spacing fix exposed the input but left Send below the prediction bar (bottom549 vs529); the final compact guest action and margin passed the full six-case matrix. An earlier large upward gesture dismissed the modal, so that recording/screenshot is not used as revocation proof. A premature generic wait missed a pending request; final completion uses the actual changed control plus canonical readback. These failures are retained in the task's local evidence, not presented as first-pass success. + +32 focused client/driver/style tests passed; service 41 workerd and 25 Node/CLI checks passed. Source/workerd cover idempotent retry and authorization failure paths. Native uncertain-response injection was not run. Synthetic sessions use the authorized UAT harness with normal server verification; this run does not repeat Hosted AuthKit login or test actual email delivery/new signup. Android, physical phone, Human QA, screen readers and non-Latin IME remain unqualified. Agents, WebSocket, identity-event revocation and two-Host communication are subsequent slices. + +After verification, large/light were restored; own app, driver and Metro stopped; revoked phone fixture removed. Existing app data, Keychain, independent CLI fixtures, other device lanes, native Products/rollback and all Hosts were preserved. diff --git a/.github/pr-screenshots/tob-channel-guests/before-clipped-maximum-dark.png b/.github/pr-screenshots/tob-channel-guests/before-clipped-maximum-dark.png new file mode 100644 index 0000000000..40dd61ab05 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/before-clipped-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/form-appearance-checks.json b/.github/pr-screenshots/tob-channel-guests/form-appearance-checks.json new file mode 100644 index 0000000000..6b4ee7da53 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-guests/form-appearance-checks.json @@ -0,0 +1,38 @@ +[ + { + "size": "large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "large", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "dark", + "draftPreserved": true, + "focused": true + } +] diff --git a/.github/pr-screenshots/tob-channel-guests/form-default-dark.png b/.github/pr-screenshots/tob-channel-guests/form-default-dark.png new file mode 100644 index 0000000000..e33ae0ff26 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/form-default-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/form-default-light.png b/.github/pr-screenshots/tob-channel-guests/form-default-light.png new file mode 100644 index 0000000000..16ba1d6fad Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/form-default-light.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/form-intermediate-dark.png b/.github/pr-screenshots/tob-channel-guests/form-intermediate-dark.png new file mode 100644 index 0000000000..d0d1b5dc12 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/form-intermediate-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/form-intermediate-light.png b/.github/pr-screenshots/tob-channel-guests/form-intermediate-light.png new file mode 100644 index 0000000000..c2898ba853 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/form-intermediate-light.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/form-maximum-dark.png b/.github/pr-screenshots/tob-channel-guests/form-maximum-dark.png new file mode 100644 index 0000000000..2f18b2b46b Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/form-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/form-maximum-light.png b/.github/pr-screenshots/tob-channel-guests/form-maximum-light.png new file mode 100644 index 0000000000..603c06f43c Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/form-maximum-light.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/keyboard-and-send.mp4 b/.github/pr-screenshots/tob-channel-guests/keyboard-and-send.mp4 new file mode 100644 index 0000000000..b7a33ad106 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/keyboard-and-send.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-guests/message-appearance-checks.json b/.github/pr-screenshots/tob-channel-guests/message-appearance-checks.json new file mode 100644 index 0000000000..6b4ee7da53 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-guests/message-appearance-checks.json @@ -0,0 +1,38 @@ +[ + { + "size": "large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "large", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "dark", + "draftPreserved": true, + "focused": true + } +] diff --git a/.github/pr-screenshots/tob-channel-guests/message-default-dark.png b/.github/pr-screenshots/tob-channel-guests/message-default-dark.png new file mode 100644 index 0000000000..aa21bbf280 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/message-default-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/message-default-light.png b/.github/pr-screenshots/tob-channel-guests/message-default-light.png new file mode 100644 index 0000000000..6d0e69253c Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/message-default-light.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/message-intermediate-dark.png b/.github/pr-screenshots/tob-channel-guests/message-intermediate-dark.png new file mode 100644 index 0000000000..2662a308af Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/message-intermediate-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/message-intermediate-light.png b/.github/pr-screenshots/tob-channel-guests/message-intermediate-light.png new file mode 100644 index 0000000000..c5547ca881 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/message-intermediate-light.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/message-maximum-dark.png b/.github/pr-screenshots/tob-channel-guests/message-maximum-dark.png new file mode 100644 index 0000000000..3225ca5185 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/message-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/message-maximum-light.png b/.github/pr-screenshots/tob-channel-guests/message-maximum-light.png new file mode 100644 index 0000000000..16b5fece58 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/message-maximum-light.png differ diff --git a/.github/pr-screenshots/tob-channel-guests/owner-invite.mp4 b/.github/pr-screenshots/tob-channel-guests/owner-invite.mp4 new file mode 100644 index 0000000000..52432a822c Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/owner-invite.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-guests/owner-revoke.mp4 b/.github/pr-screenshots/tob-channel-guests/owner-revoke.mp4 new file mode 100644 index 0000000000..fba59fc6c3 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/owner-revoke.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-guests/recipient-accept.mp4 b/.github/pr-screenshots/tob-channel-guests/recipient-accept.mp4 new file mode 100644 index 0000000000..6960772e3a Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/recipient-accept.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-guests/restart-and-revoke.mp4 b/.github/pr-screenshots/tob-channel-guests/restart-and-revoke.mp4 new file mode 100644 index 0000000000..991a13a631 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-guests/restart-and-revoke.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-http/01-workspace-entry.png b/.github/pr-screenshots/tob-channel-http/01-workspace-entry.png new file mode 100644 index 0000000000..9c1951123e Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/01-workspace-entry.png differ diff --git a/.github/pr-screenshots/tob-channel-http/03-three-channels.png b/.github/pr-screenshots/tob-channel-http/03-three-channels.png new file mode 100644 index 0000000000..fbae8478ac Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/03-three-channels.png differ diff --git a/.github/pr-screenshots/tob-channel-http/06-cli-reply.png b/.github/pr-screenshots/tob-channel-http/06-cli-reply.png new file mode 100644 index 0000000000..221cec76bd Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/06-cli-reply.png differ diff --git a/.github/pr-screenshots/tob-channel-http/11-maximum-keyboard-light.png b/.github/pr-screenshots/tob-channel-http/11-maximum-keyboard-light.png new file mode 100644 index 0000000000..aeb4b1219e Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/11-maximum-keyboard-light.png differ diff --git a/.github/pr-screenshots/tob-channel-http/19-fixed-max-dark.png b/.github/pr-screenshots/tob-channel-http/19-fixed-max-dark.png new file mode 100644 index 0000000000..d942cee5bb Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/19-fixed-max-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-http/24-create-max-dark.png b/.github/pr-screenshots/tob-channel-http/24-create-max-dark.png new file mode 100644 index 0000000000..45ef0e24db Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/24-create-max-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-http/26-four-channels.png b/.github/pr-screenshots/tob-channel-http/26-four-channels.png new file mode 100644 index 0000000000..cc62cb74a7 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/26-four-channels.png differ diff --git a/.github/pr-screenshots/tob-channel-http/27-contrast-create-dark.png b/.github/pr-screenshots/tob-channel-http/27-contrast-create-dark.png new file mode 100644 index 0000000000..2cb1e541e9 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/27-contrast-create-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-http/28-contrast-create-light.png b/.github/pr-screenshots/tob-channel-http/28-contrast-create-light.png new file mode 100644 index 0000000000..9426ac9652 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/28-contrast-create-light.png differ diff --git a/.github/pr-screenshots/tob-channel-http/29-final-restart-session.png b/.github/pr-screenshots/tob-channel-http/29-final-restart-session.png new file mode 100644 index 0000000000..302ec01983 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/29-final-restart-session.png differ diff --git a/.github/pr-screenshots/tob-channel-http/30-signed-out.png b/.github/pr-screenshots/tob-channel-http/30-signed-out.png new file mode 100644 index 0000000000..31a5e7de90 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/30-signed-out.png differ diff --git a/.github/pr-screenshots/tob-channel-http/README.md b/.github/pr-screenshots/tob-channel-http/README.md new file mode 100644 index 0000000000..552c955e06 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-http/README.md @@ -0,0 +1,33 @@ +# Cloud channel HTTP UAT — 2026-10-18 JST + +Final mobile runtime: `5d2ef0fe8a13654a75779ea0320394b13c00764c`. Compatible signed native build: `494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b`. Service runtime: `b26150224a60808d89daf7e9cd8863faf26ed75f`, UAT Worker version `f62d1003-7e9c-4924-a889-d06a27ec0427`, D1/Org/Channel schemas 4/3/1. + +Shared iPhone 17 Pro / iOS 26 Simulator only, English, 402×874 captures. No build, erase, physical phone, Host, Prod or merge action. Authentication uses an API-prepared synthetic ordinary-user session and the existing trusted configuration; this is not another Hosted AuthKit/PKCE or email-delivery test. + +## Observed behavior + +- CLI creates `general` and `research`, replays identical create/send operations without duplicate records, and reads durable history. The other user cannot read these channels, including by putting their IDs under the wrong organization URL. +- Actual phone UI creates `mobile-test`; independent formal CLI sees the exact organization/channel. Phone sends, CLI reads and replies, phone reloads the reply. The final CLI readback matches all seven message bodies, newlines and ordered sequences 1–7. Repeated identical text in sequences 6 and 7 is two deliberate sends with different operation keys, not a replay duplicate. +- Phone also creates `large-type` at maximum Dynamic Type. A's selected workspace now has four channels; B's has zero. A has three workspaces and B has one. +- Temporarily changing synthetic A's current WorkOS membership from owner to member makes a stale-owner-token channel creation fail with 403. Listing, reading and sending remain allowed. The operator restored and verified owner before native testing. No Human administrator role changed. +- Cold restart retains the saved session/selected workspace; server checks and refresh retain the verified ordinary-user role. Sign out revokes the phone session, rejects its latest refresh, removes local persisted/in-memory credentials, and retains configuration. Independent A and B CLI calls still work. The temporary phone credential file was removed after verification. +- The final six-case matrix covers standard (`large`), intermediate (`accessibility-medium`) and maximum (`accessibility-extra-extra-extra-large`) × light/dark. Live resizing retains the exact multiline draft and focus. At final maximum/dark, the complete Send button bottom is 521pt and the predictive keyboard begins at 529pt; the actual tap commits sequence 7. Native keyboard frames, not delayed scrolling, drive the list. + +## Captures and provenance + +- `before-workspace-entry.png` is the same selected workspace before the new channel entry, from the prior onboarding slice (`9f71dd3d9`). `01-workspace-entry.png` adds the entry at `7ceab0d27`. +- `03-three-channels.png` and `06-cli-reply.png` show the first HTTP path at `7ceab0d27`. `26-four-channels.png` is the later four-channel list at `f4dd6dc2f`. +- `11-maximum-keyboard-light.png` preserves the original covered caret/action failure. `19-fixed-max-dark.png` and `24-create-max-dark.png` preserve the later dark-input contrast failure; despite their working filenames, they are NOT final passing evidence. +- `27-contrast-create-dark.png` / `28-contrast-create-light.png` show themed creation input at `6704326e9`. No channel was created from that `contrast-check` draft; it was discarded on restart. +- `final-*.png`, `29-final-restart-session.png` and `30-signed-out.png` are final `5d2ef0fe8` evidence. The final composer matrix uses the same multiline draft in every case. Compared with the first failure, draft text and scroll/caret position differ; the corrected draft is longer and includes an explicit newline. These are a native behavioral comparison, not pixel-matched typography benchmarks. +- Recordings preserve real timing, including idle/tool pauses. Published copies use 12fps H.264 review encoding with unchanged chronology; originals remain local. `recordings.json` gives each duration/size. Preview sheets sample frames and are not extra test executions. `first-run.mp4` captures `7ceab0d27`; `keyboard-and-fonts.mp4` captures `f4dd6dc2f`. `final-session.mp4` spans the `6704326e9` contrast check and subsequent `5d2ef0fe8` short-label fix, final matrix, send, restart and sign-out; do not attribute its entire duration to the final head. + +## Retained failures and proof limits + +The first UAT CLI attempt failed during JWKS lookup before writes; replaying the same keys passed. Initial native keyboard handling covered the caret at maximum text size. Reusing the existing keyboard-form adapter fixed focus reveal; two-line action spacing and a concise Send label keep the full action above the predictive bar. Explicit theme foreground/placeholder colors fix black text in dark mode. + +The first repair recheck still used old JS because Metro was started with CI mode and file watching disabled. `stale-metro-recheck.mp4` is harness failure evidence, not proof of the repaired head. The runner restarted only its own Metro, then repeated on the loaded candidate. Two attempts to replace an existing long draft produced driver readback mismatches; that unsent draft was discarded through the UI. Fresh-field input and final exact server readback passed. One appearance transition screenshot was not used as final evidence. Formatting failures and fixes remain in CI history. + +Source checks: 30 workerd service tests, 23 Node/CLI/contracts, mobile 20 focused client/style tests, typechecks and applicable CI. Evidence does not establish Android native behavior, physical-device or Human visual acceptance, screen-reader/IME qualification, WebSocket delivery, invitations, Agents, history grants/rejoin, or cross-Host work. Current HTTP membership checks do not close the outstanding T1 provider-event/session-revocation gates. T1/T2 remain open. + +Light appearance and large text size were restored. The driver, app and owned Metro were stopped; shared original files/data/native rollback and other lanes were preserved. The UAT synthetic identities, four workspaces, four channels and independent CLI sessions remain for the next goal slice. diff --git a/.github/pr-screenshots/tob-channel-http/appearance-checks.json b/.github/pr-screenshots/tob-channel-http/appearance-checks.json new file mode 100644 index 0000000000..6b4ee7da53 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-http/appearance-checks.json @@ -0,0 +1,38 @@ +[ + { + "size": "large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "large", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "dark", + "draftPreserved": true, + "focused": true + } +] diff --git a/.github/pr-screenshots/tob-channel-http/before-workspace-entry.png b/.github/pr-screenshots/tob-channel-http/before-workspace-entry.png new file mode 100644 index 0000000000..8de8221ac3 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/before-workspace-entry.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-default-dark.png b/.github/pr-screenshots/tob-channel-http/final-default-dark.png new file mode 100644 index 0000000000..d7b81f1548 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-default-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-default-light.png b/.github/pr-screenshots/tob-channel-http/final-default-light.png new file mode 100644 index 0000000000..1165bedeed Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-default-light.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-intermediate-dark.png b/.github/pr-screenshots/tob-channel-http/final-intermediate-dark.png new file mode 100644 index 0000000000..70c09a942b Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-intermediate-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-intermediate-light.png b/.github/pr-screenshots/tob-channel-http/final-intermediate-light.png new file mode 100644 index 0000000000..f0ccb24a33 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-intermediate-light.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-maximum-dark.png b/.github/pr-screenshots/tob-channel-http/final-maximum-dark.png new file mode 100644 index 0000000000..4a634f8b61 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-maximum-light.png b/.github/pr-screenshots/tob-channel-http/final-maximum-light.png new file mode 100644 index 0000000000..71d4579ee8 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-maximum-light.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-session-preview.png b/.github/pr-screenshots/tob-channel-http/final-session-preview.png new file mode 100644 index 0000000000..f2f68374ff Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-session-preview.png differ diff --git a/.github/pr-screenshots/tob-channel-http/final-session.mp4 b/.github/pr-screenshots/tob-channel-http/final-session.mp4 new file mode 100644 index 0000000000..34c23d8f5e Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/final-session.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-http/first-run-preview.png b/.github/pr-screenshots/tob-channel-http/first-run-preview.png new file mode 100644 index 0000000000..4b2b16849d Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/first-run-preview.png differ diff --git a/.github/pr-screenshots/tob-channel-http/first-run.mp4 b/.github/pr-screenshots/tob-channel-http/first-run.mp4 new file mode 100644 index 0000000000..581751ccab Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/first-run.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-http/keyboard-and-fonts-preview.png b/.github/pr-screenshots/tob-channel-http/keyboard-and-fonts-preview.png new file mode 100644 index 0000000000..02b04c3c9f Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/keyboard-and-fonts-preview.png differ diff --git a/.github/pr-screenshots/tob-channel-http/keyboard-and-fonts.mp4 b/.github/pr-screenshots/tob-channel-http/keyboard-and-fonts.mp4 new file mode 100644 index 0000000000..6fcbefafed Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/keyboard-and-fonts.mp4 differ diff --git a/.github/pr-screenshots/tob-channel-http/recordings.json b/.github/pr-screenshots/tob-channel-http/recordings.json new file mode 100644 index 0000000000..111c82e295 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-http/recordings.json @@ -0,0 +1,18 @@ +{ + "final-session.mp4": { + "duration": "383.500000", + "size": "4336768" + }, + "first-run.mp4": { + "duration": "629.166667", + "size": "3473431" + }, + "keyboard-and-fonts.mp4": { + "duration": "164.166667", + "size": "1871892" + }, + "stale-metro-recheck.mp4": { + "duration": "265.333333", + "size": "2173105" + } +} diff --git a/.github/pr-screenshots/tob-channel-http/results.json b/.github/pr-screenshots/tob-channel-http/results.json new file mode 100644 index 0000000000..0de587cb58 --- /dev/null +++ b/.github/pr-screenshots/tob-channel-http/results.json @@ -0,0 +1,40 @@ +{ + "mobileRuntime": "5d2ef0fe8a13654a75779ea0320394b13c00764c", + "nativeRuntime": "494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b", + "serviceRuntime": "b26150224a60808d89daf7e9cd8863faf26ed75f", + "uatOnly": true, + "nativeRebuilt": false, + "organizationCounts": { + "a": 3, + "b": 1 + }, + "channelCounts": { + "aSelectedWorkspace": 4, + "bSelectedWorkspace": 0 + }, + "mobileCliExactMessages": 7, + "maximumKeyboardGeometry": { + "sendButtonBottom": 521, + "predictionBarTop": 529, + "passed": true + }, + "coldRestart": true, + "sessionRefresh": true, + "phoneSessionRevoked": true, + "latestPhoneRefreshRejected": true, + "localPhoneSessionRemoved": true, + "independentCliAAndBUsable": true, + "originalAppearanceRestored": "light", + "originalContentSizeRestored": "large", + "unverified": [ + "Android native", + "physical phone", + "Human visual QA", + "new Hosted AuthKit login", + "WebSocket", + "invitations", + "Agent credentials", + "cross-Host communication", + "history grants and membership rejoin" + ] +} diff --git a/.github/pr-screenshots/tob-channel-http/stale-metro-recheck-preview.png b/.github/pr-screenshots/tob-channel-http/stale-metro-recheck-preview.png new file mode 100644 index 0000000000..b17e6d79d4 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/stale-metro-recheck-preview.png differ diff --git a/.github/pr-screenshots/tob-channel-http/stale-metro-recheck.mp4 b/.github/pr-screenshots/tob-channel-http/stale-metro-recheck.mp4 new file mode 100644 index 0000000000..8a173ab498 Binary files /dev/null and b/.github/pr-screenshots/tob-channel-http/stale-metro-recheck.mp4 differ diff --git a/.github/pr-screenshots/tob-multi-user/01-saved-session.png b/.github/pr-screenshots/tob-multi-user/01-saved-session.png new file mode 100644 index 0000000000..31d361cb7c Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/01-saved-session.png differ diff --git a/.github/pr-screenshots/tob-multi-user/02-ordinary-user.png b/.github/pr-screenshots/tob-multi-user/02-ordinary-user.png new file mode 100644 index 0000000000..80e45bf6c2 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/02-ordinary-user.png differ diff --git a/.github/pr-screenshots/tob-multi-user/03-workspace-list.png b/.github/pr-screenshots/tob-multi-user/03-workspace-list.png new file mode 100644 index 0000000000..03832e2590 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/03-workspace-list.png differ diff --git a/.github/pr-screenshots/tob-multi-user/04-mobile-created.png b/.github/pr-screenshots/tob-multi-user/04-mobile-created.png new file mode 100644 index 0000000000..99dffa4ad9 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/04-mobile-created.png differ diff --git a/.github/pr-screenshots/tob-multi-user/05-default-selected.png b/.github/pr-screenshots/tob-multi-user/05-default-selected.png new file mode 100644 index 0000000000..2c05327bd3 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/05-default-selected.png differ diff --git a/.github/pr-screenshots/tob-multi-user/06-mobile-selected-again.png b/.github/pr-screenshots/tob-multi-user/06-mobile-selected-again.png new file mode 100644 index 0000000000..8de8221ac3 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/06-mobile-selected-again.png differ diff --git a/.github/pr-screenshots/tob-multi-user/07-restart-saved.png b/.github/pr-screenshots/tob-multi-user/07-restart-saved.png new file mode 100644 index 0000000000..d8486b9e7b Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/07-restart-saved.png differ diff --git a/.github/pr-screenshots/tob-multi-user/08-refreshed-user.png b/.github/pr-screenshots/tob-multi-user/08-refreshed-user.png new file mode 100644 index 0000000000..9b6e1864d5 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/08-refreshed-user.png differ diff --git a/.github/pr-screenshots/tob-multi-user/09-restart-selected-workspace.png b/.github/pr-screenshots/tob-multi-user/09-restart-selected-workspace.png new file mode 100644 index 0000000000..280d766426 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/09-restart-selected-workspace.png differ diff --git a/.github/pr-screenshots/tob-multi-user/10-signed-out.png b/.github/pr-screenshots/tob-multi-user/10-signed-out.png new file mode 100644 index 0000000000..3f2b96dca3 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/10-signed-out.png differ diff --git a/.github/pr-screenshots/tob-multi-user/README.md b/.github/pr-screenshots/tob-multi-user/README.md new file mode 100644 index 0000000000..eea9c47e8f --- /dev/null +++ b/.github/pr-screenshots/tob-multi-user/README.md @@ -0,0 +1,20 @@ +# Ordinary-user multi-workspace UAT — 2026-10-18 JST + +Native runtime `9f71dd3d9` (product source `00d9bcc6c`), compatible signed native build `494a20f1e`. Test driver checkpoints used `079832999`; only the test driver/declaration changed after the runtime freeze. Service runtime `c4c8aa44fd86dcd53650440a8350442c20420938`, Worker version `cc848bdb-d7cb-44cf-bbea-be1df39d11dc`, D1 schema 4. + +Sole BotHarness e2e iPhone 17 Pro / iOS 26 Simulator, English/light, screenshots 402×874. No native rebuild, phone action, Prod or merge. The existing trusted UAT configuration was retained. + +## Passed path + +1. Dedicated verified ordinary-user session injected through the development-only driver, then cold restart. Check session verified the full signed identity and replayed the server-owned default workspace. Status is Signed in, without an administrator label. +2. Workspaces listed the same default and second workspace as independent CLI A. The actual mobile UI created TOB Mobile collaboration, switched to My workspace and back. Formal CLI A listed and selected that exact organization; A had three organizations, B only its own one. +3. Cold restart retained the selected organization and saved session. Actual Refresh session rotated credentials and revalidated user role. The same selected organization was restored. +4. Actual Sign out revoked only the phone session. Server session status became inactive, latest phone refresh was rejected, app persisted/in-memory credentials were absent and trusted configuration remained. Independent A and B CLI sessions still worked. + +## Evidence limits and retained failures + +These recordings use API-prepared synthetic authentication; they do not prove a new Hosted AuthKit/PKCE or email-delivery flow. Previous real Hosted AuthKit acceptance belongs to #2103. Owner/member downgrade coverage belongs to #2121; this run adds ordinary-user/default/multiple-workspace coverage. Channels, invitations, Agents and cross-Host transport are not implemented by this slice. + +The initial service/CLI run had a network interruption at one denial assertion; a bounded repeat with the same identities/keys passed. Initial phone fixture preparation required handling WorkOS organization selection for a multi-org user; the test-only helper now selects an actual returned membership, and other challenges fail. Two CI failures (release-fragment metadata and the driver type declaration) were fixed and are preserved in run history. A driver log previously hard-coded verifiedAdmin:true; it now reports the actual verified role and false for this user. + +`workspaces.mp4` is 277.625 seconds; `session-recovery.mp4` is 106.323 seconds. Both are original continuous recordings with idle/tool pauses. Contact sheets are sampled previews, not additional executions. No credentials were recorded. Synthetic users and four workspaces remain for subsequent goal slices; the phone is signed out. No Human visual acceptance or physical-device qualification is claimed. diff --git a/.github/pr-screenshots/tob-multi-user/recovery-contact-sheet.png b/.github/pr-screenshots/tob-multi-user/recovery-contact-sheet.png new file mode 100644 index 0000000000..d451c5e666 Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/recovery-contact-sheet.png differ diff --git a/.github/pr-screenshots/tob-multi-user/results.json b/.github/pr-screenshots/tob-multi-user/results.json new file mode 100644 index 0000000000..ca705e18b6 --- /dev/null +++ b/.github/pr-screenshots/tob-multi-user/results.json @@ -0,0 +1,30 @@ +{ + "date": "2026-10-18", + "environment": "UAT", + "runtime": "9f71dd3d9", + "service": "c4c8aa44fd86dcd53650440a8350442c20420938", + "passed": [ + "ordinary-user-admission", + "default-replay", + "mobile-create-third-org", + "mobile-default-to-third-switch", + "independent-cli-exact-org-agreement", + "cold-restart", + "refresh-rotation", + "selected-org-retained", + "phone-remote-logout", + "phone-refresh-rejected", + "local-session-cleared", + "independent-cli-sessions-retained" + ], + "not_claimed": [ + "new-hosted-authkit", + "physical-device", + "channels", + "invitations", + "cross-host", + "human-acceptance", + "merge", + "production" + ] +} diff --git a/.github/pr-screenshots/tob-multi-user/session-recovery.mp4 b/.github/pr-screenshots/tob-multi-user/session-recovery.mp4 new file mode 100644 index 0000000000..847e5603bb Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/session-recovery.mp4 differ diff --git a/.github/pr-screenshots/tob-multi-user/workspaces-contact-sheet.png b/.github/pr-screenshots/tob-multi-user/workspaces-contact-sheet.png new file mode 100644 index 0000000000..7b2f2575bc Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/workspaces-contact-sheet.png differ diff --git a/.github/pr-screenshots/tob-multi-user/workspaces.mp4 b/.github/pr-screenshots/tob-multi-user/workspaces.mp4 new file mode 100644 index 0000000000..e1e003ad4b Binary files /dev/null and b/.github/pr-screenshots/tob-multi-user/workspaces.mp4 differ diff --git a/.github/pr-screenshots/tob-org-invitations/README.md b/.github/pr-screenshots/tob-org-invitations/README.md new file mode 100644 index 0000000000..fa57bb83ab --- /dev/null +++ b/.github/pr-screenshots/tob-org-invitations/README.md @@ -0,0 +1,31 @@ +# Organization invitations — native UAT evidence + +Runtime: `acfbc581b805a0a6b47687b7f5c23c2ca363cfcf`; signed compatible iOS development build: `494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b`. Shared Simulator, iOS 26, English, 402 × 874 logical pixels. No native-input change. Service runtime `b6b717d044ff4a787de287c31d2029025bbbb7f9`, Worker version `2722f0bc-3ee0-4214-9917-116bcb9497bd`, D1/Org/Channel schema 4/4/1. All operations used UAT. Source CI: [39080432282](https://github.com/BotHarness/DeepSeekBot/actions/runs/39080432282). + +## Observed behavior + +- Synthetic ordinary A selected its collaboration workspace, invited synthetic B through the phone, and saw Pending/member. B's independent phone session opened its inbox, accepted and selected that same workspace; four channel IDs matched the independent formal CLI. +- B could not see the seven pre-join messages in `mobile-test`. It sent `B accepted on phone and can collaborate` as message 8; both B and owner CLI read exactly one matching message with B's actual author ID. +- The real service rejected B's attempts to create a channel, issue an invitation and edit the organization. The phone offered channel reading/sending but omitted owner invitation and save controls. +- A created a separate invitation to its default workspace and revoked it from the phone. B's CLI could not accept that invitation or select that workspace. No membership was granted. +- B refreshed the phone session, cold-restarted and verified the same selected organization/member role. Three independent phone test sessions (A, B, A) signed out through the UI; the latest saved refresh token was rejected remotely, the local session was absent and every pre-existing active session remained active. A/B independent CLI sessions were preserved. +- Six owner-form size/theme combinations preserved the exact email draft and input focus. Maximum-size Invite remained hittable above the actual keyboard (button bottom ~454, keyboard top ~573; visible keyboard container starts ~557), and a real submit succeeded. Maximum-size revoke was reached by scrolling and operated. Six inbox size/theme captures show reflow; these do not prove screen-reader usability or every list action at every size. + +## Captures + +`owner-form-light` / `owner-created`: first A invitation. +`member-inbox` → `member-four-channels` → `member-no-prejoin-history` → `member-message-confirmed`: B acceptance and text. +`member-restart-role` / `member-signed-out`: restored member selection and logout. +`form-*` / `inbox-*`: default, intermediate and maximum Dynamic Type × light/dark. +`maximum-revoke-reachable` / `maximum-revoked`: action reachability and revoked result. +Videos retain real steps and waits; reviewer copies are compressed to 402px / 12fps without audio. They use synthetic `example.test` users, no credentials or invitation tokens. + +Before the new screen existed, the [base workspace entry](../tob-channel-http/01-workspace-entry.png) exposed cloud channels but no invitation entry. That is a historical entry-point comparison from runtime `7ceab0d27`, not a claim that the invitation screen existed on the base. Current owner/member captures use the same viewport/theme/locale, with role differences explicitly identified. + +## First failures and limits + +The driver initially waited for “Invitation sent.” instead of actual “Invitation created.”, used an unmatched Message selector, requested in-app Back for a modal that closes by swipe, and checkpointed before refresh finished (`SESSION_BUSY`). The revocation assertion initially expected the wrong public refusal code; actual `INVITATION_NOT_ACCEPTABLE` was independently verified. These harness failures were corrected and checks repeated; no product source changed during the native window. The first message readback failed before text was filled; the later confirmed screenshot/readback is the final proof. Recordings preserve those waits/failures rather than disguising them as a continuous first-pass success. + +No actual email delivery to a human mailbox, new Hosted AuthKit login, Android, physical phone, Human QA, screen-reader or non-Latin IME pass is claimed. Existing verified synthetic users were authenticated through the UAT-only harness and still passed normal server verification. Native uncertain-response/restart issuance was not fault-injected; source/workerd tests cover stable operation recovery. Direct channel guests, Agents, WebSocket, identity-event revocation and two-Host communication are later slices. No Prod changes or merge. + +After verification, light/large were restored, the app/driver/owned Metro stopped and the revoked phone fixture removed. Existing app data, Keychain, independent CLI fixtures, other device lanes and Hosts were preserved. diff --git a/.github/pr-screenshots/tob-org-invitations/form-appearance-checks.json b/.github/pr-screenshots/tob-org-invitations/form-appearance-checks.json new file mode 100644 index 0000000000..6b4ee7da53 --- /dev/null +++ b/.github/pr-screenshots/tob-org-invitations/form-appearance-checks.json @@ -0,0 +1,38 @@ +[ + { + "size": "large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "large", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-medium", + "theme": "dark", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "light", + "draftPreserved": true, + "focused": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "dark", + "draftPreserved": true, + "focused": true + } +] diff --git a/.github/pr-screenshots/tob-org-invitations/form-default-dark.png b/.github/pr-screenshots/tob-org-invitations/form-default-dark.png new file mode 100644 index 0000000000..eda92dcc44 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/form-default-dark.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/form-default-light.png b/.github/pr-screenshots/tob-org-invitations/form-default-light.png new file mode 100644 index 0000000000..1158bd8f35 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/form-default-light.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/form-intermediate-dark.png b/.github/pr-screenshots/tob-org-invitations/form-intermediate-dark.png new file mode 100644 index 0000000000..03d4281557 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/form-intermediate-dark.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/form-intermediate-light.png b/.github/pr-screenshots/tob-org-invitations/form-intermediate-light.png new file mode 100644 index 0000000000..43c8e36366 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/form-intermediate-light.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/form-maximum-dark.png b/.github/pr-screenshots/tob-org-invitations/form-maximum-dark.png new file mode 100644 index 0000000000..54777a8eb6 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/form-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/form-maximum-light.png b/.github/pr-screenshots/tob-org-invitations/form-maximum-light.png new file mode 100644 index 0000000000..390403dfc1 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/form-maximum-light.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/inbox-appearance-checks.json b/.github/pr-screenshots/tob-org-invitations/inbox-appearance-checks.json new file mode 100644 index 0000000000..425f9443be --- /dev/null +++ b/.github/pr-screenshots/tob-org-invitations/inbox-appearance-checks.json @@ -0,0 +1,32 @@ +[ + { + "size": "large", + "theme": "light", + "acceptedInvitationVisible": true + }, + { + "size": "large", + "theme": "dark", + "acceptedInvitationVisible": true + }, + { + "size": "accessibility-medium", + "theme": "light", + "acceptedInvitationVisible": true + }, + { + "size": "accessibility-medium", + "theme": "dark", + "acceptedInvitationVisible": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "light", + "acceptedInvitationVisible": true + }, + { + "size": "accessibility-extra-extra-extra-large", + "theme": "dark", + "acceptedInvitationVisible": true + } +] diff --git a/.github/pr-screenshots/tob-org-invitations/inbox-default-dark.png b/.github/pr-screenshots/tob-org-invitations/inbox-default-dark.png new file mode 100644 index 0000000000..7a62fcdb52 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/inbox-default-dark.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/inbox-default-light.png b/.github/pr-screenshots/tob-org-invitations/inbox-default-light.png new file mode 100644 index 0000000000..c787200f8b Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/inbox-default-light.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/inbox-intermediate-dark.png b/.github/pr-screenshots/tob-org-invitations/inbox-intermediate-dark.png new file mode 100644 index 0000000000..16d0b36b4a Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/inbox-intermediate-dark.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/inbox-intermediate-light.png b/.github/pr-screenshots/tob-org-invitations/inbox-intermediate-light.png new file mode 100644 index 0000000000..904b5f7a26 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/inbox-intermediate-light.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/inbox-maximum-dark.png b/.github/pr-screenshots/tob-org-invitations/inbox-maximum-dark.png new file mode 100644 index 0000000000..48843cffce Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/inbox-maximum-dark.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/inbox-maximum-light.png b/.github/pr-screenshots/tob-org-invitations/inbox-maximum-light.png new file mode 100644 index 0000000000..68dc16ed13 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/inbox-maximum-light.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/maximum-revoke-reachable.png b/.github/pr-screenshots/tob-org-invitations/maximum-revoke-reachable.png new file mode 100644 index 0000000000..621c10a97e Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/maximum-revoke-reachable.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/maximum-revoked.png b/.github/pr-screenshots/tob-org-invitations/maximum-revoked.png new file mode 100644 index 0000000000..bc45c26bc5 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/maximum-revoked.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/member-accept.mp4 b/.github/pr-screenshots/tob-org-invitations/member-accept.mp4 new file mode 100644 index 0000000000..7825203a72 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/member-accept.mp4 differ diff --git a/.github/pr-screenshots/tob-org-invitations/member-four-channels.png b/.github/pr-screenshots/tob-org-invitations/member-four-channels.png new file mode 100644 index 0000000000..df4cc30d3f Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/member-four-channels.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/member-inbox.png b/.github/pr-screenshots/tob-org-invitations/member-inbox.png new file mode 100644 index 0000000000..a45d682575 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/member-inbox.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/member-message-confirmed.png b/.github/pr-screenshots/tob-org-invitations/member-message-confirmed.png new file mode 100644 index 0000000000..69a9c12734 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/member-message-confirmed.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/member-no-prejoin-history.png b/.github/pr-screenshots/tob-org-invitations/member-no-prejoin-history.png new file mode 100644 index 0000000000..b9b3bffcb4 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/member-no-prejoin-history.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/member-restart-role.png b/.github/pr-screenshots/tob-org-invitations/member-restart-role.png new file mode 100644 index 0000000000..f1c3fff2d7 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/member-restart-role.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/member-signed-out.png b/.github/pr-screenshots/tob-org-invitations/member-signed-out.png new file mode 100644 index 0000000000..427c35b561 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/member-signed-out.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/mobile-invitation-result.json b/.github/pr-screenshots/tob-org-invitations/mobile-invitation-result.json new file mode 100644 index 0000000000..3901e187fa --- /dev/null +++ b/.github/pr-screenshots/tob-org-invitations/mobile-invitation-result.json @@ -0,0 +1,8 @@ +{ + "phoneInviteAccepted": true, + "independentCliSameOrg": true, + "memberChannelCount": 4, + "preJoinHistoryHidden": true, + "memberCannotCreateInviteOrEdit": true, + "phoneMemberMessageVisibleToOwner": true +} diff --git a/.github/pr-screenshots/tob-org-invitations/mobile-revoke-result.json b/.github/pr-screenshots/tob-org-invitations/mobile-revoke-result.json new file mode 100644 index 0000000000..3a015dfb1a --- /dev/null +++ b/.github/pr-screenshots/tob-org-invitations/mobile-revoke-result.json @@ -0,0 +1,6 @@ +{ + "phoneOwnerRevokeConfirmed": true, + "revokedRecipientCannotAccept": true, + "noMembershipGranted": true, + "ownerCliSelectionRestored": true +} diff --git a/.github/pr-screenshots/tob-org-invitations/owner-created.png b/.github/pr-screenshots/tob-org-invitations/owner-created.png new file mode 100644 index 0000000000..2819b80716 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/owner-created.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/owner-form-light.png b/.github/pr-screenshots/tob-org-invitations/owner-form-light.png new file mode 100644 index 0000000000..38e1226fcd Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/owner-form-light.png differ diff --git a/.github/pr-screenshots/tob-org-invitations/owner-invite.mp4 b/.github/pr-screenshots/tob-org-invitations/owner-invite.mp4 new file mode 100644 index 0000000000..c207a76898 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/owner-invite.mp4 differ diff --git a/.github/pr-screenshots/tob-org-invitations/owner-size-revoke.mp4 b/.github/pr-screenshots/tob-org-invitations/owner-size-revoke.mp4 new file mode 100644 index 0000000000..5d8564dd74 Binary files /dev/null and b/.github/pr-screenshots/tob-org-invitations/owner-size-revoke.mp4 differ diff --git a/.github/pr-screenshots/tob-t1-organizations/00-before-cloud-entry.png b/.github/pr-screenshots/tob-t1-organizations/00-before-cloud-entry.png new file mode 100644 index 0000000000..c91e598f68 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/00-before-cloud-entry.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/01-cloud-entry.png b/.github/pr-screenshots/tob-t1-organizations/01-cloud-entry.png new file mode 100644 index 0000000000..b40c2e46a5 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/01-cloud-entry.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/02-created-owner.png b/.github/pr-screenshots/tob-t1-organizations/02-created-owner.png new file mode 100644 index 0000000000..8a68d2ca96 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/02-created-owner.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/03-owner-saved.png b/.github/pr-screenshots/tob-t1-organizations/03-owner-saved.png new file mode 100644 index 0000000000..2302ed9cda Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/03-owner-saved.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/04-member-write-denied.png b/.github/pr-screenshots/tob-t1-organizations/04-member-write-denied.png new file mode 100644 index 0000000000..44130860e9 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/04-member-write-denied.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/05-member-readonly.png b/.github/pr-screenshots/tob-t1-organizations/05-member-readonly.png new file mode 100644 index 0000000000..7fcd1a4b23 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/05-member-readonly.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/06-restart-owner.png b/.github/pr-screenshots/tob-t1-organizations/06-restart-owner.png new file mode 100644 index 0000000000..5404eb5cc3 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/06-restart-owner.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/07-member-fixed.png b/.github/pr-screenshots/tob-t1-organizations/07-member-fixed.png new file mode 100644 index 0000000000..bd40acd890 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/07-member-fixed.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/08-owner-restored.png b/.github/pr-screenshots/tob-t1-organizations/08-owner-restored.png new file mode 100644 index 0000000000..be842d14a2 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/08-owner-restored.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/09-signed-out.png b/.github/pr-screenshots/tob-t1-organizations/09-signed-out.png new file mode 100644 index 0000000000..57fa9c9cb3 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/09-signed-out.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/README.md b/.github/pr-screenshots/tob-t1-organizations/README.md new file mode 100644 index 0000000000..c10487e5b8 --- /dev/null +++ b/.github/pr-screenshots/tob-t1-organizations/README.md @@ -0,0 +1,41 @@ +# Mobile → CLI organization acceptance (UAT) + +2026-10-18 JST · DeepSeekBot #2120 / Draft #2121 · TOB T1 #2 / Draft #17. + +## Exact source and scope + +- Create/owner-save runtime: `494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b`. +- Final permission/reselection/restart/logout runtime: `1b39324c5034eea2741abed62d579cd1044f2f86`. +- Signed native build source: `494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b`; SDK58, iOS26 Simulator. Final change is JS only. Normal local Simulator ad-hoc signing; no phone or Prod build. +- Real UAT Worker: `e5bc56298c2f63aa457ba39a1ce9ad45e2270824`; deployed version `d492f524-3489-4f97-b300-ec1e8671752e`. Service review head `fe5a8cb` adds two passing tests only. +- English / light / default text, 402 × 874 screenshots. API-prepared independent UAT session; **not a new Hosted AuthKit browser proof**. The preceding real email/PKCE run is documented separately in `../tob-t1-uat/hosted-email/`. + +## Observed path + +1. Phone reads the retained deployer-verified configuration, loads the independent test session from SecureStore and verifies the initialized administrator through the real UAT service. +2. On the actual **Cloud workspaces** screen, create **TOB Mobile UAT**. WorkOS organization + explicit owner membership, D1 mapping/creation receipt and Org DO settings are exercised through the public API. App selects the returned organization using WorkOS token refresh. +3. Phone saves `Created on mobile, read by the CLI.`. Independent formal CLI `org list`, `org use`, `org show` return the **same exact organization ID**, description, version **2** and owner role. Tokens remain private and separate. +4. Control plane changes only this new organization's membership owner → member. An already-issued owner credential receives HTTP **403 ORGANIZATION_OWNER_REQUIRED** on PATCH. CLI reads member with unchanged description/version. The actual phone's stale owner UI also receives the refusal; cached UI does not grant permission. +5. Initial native inspection found stale unsaved text and an old list role after reselecting. Fixed at `1b39324c5`: readonly content uses the verified server description, draft binding includes role, and selecting clears the draft and reloads the list. The repeated native scenario shows Member in both locations, unchanged original content and no save action. +6. Restore the test membership to active owner. Control-plane readback, CLI selection/read and phone reselection all agree. The one test organization remains for review; no second organization was created. +7. Cold restart retains credentials and selected organization but first shows **Saved session · Check to verify**. Check session restores the same organization and owner. No cached permission is presented as verified before that check. +8. Phone logout clears the session and retains public configuration. Independent WorkOS readback confirms only that test session revoked; its latest refresh token returns `invalid_grant`. Existing CLI session remains active and `org show` still returns the same owner organization/version. Revoked mobile credentials and stale test token were removed locally. +9. `allowSignUp=false`, `isMagicAuthEnabled=false` read back. Simulator driver and this task's Metro stopped; no global language preference, Host, physical phone, Prod or merge changed. + +## Media + +- `00-before-cloud-entry.png`: real prior login-only entry at runtime `216963322`, same Simulator/size/theme/locale; no workspace action. This is historical baseline evidence, not a rerun of main. +- `01`–`04`: new entry, actual create, owner save, old-owner write refusal at `494a20f1e`. +- `05-member-readonly.png`: **first failure**, unsaved local draft and stale list role; intentionally labeled as before-fix evidence, not a pass. +- `06`–`09`: final-code cold restart owner, corrected member readonly, restored owner, signed out. +- `organization-create.mp4`: 72-second real create/save path on `494a20f1e`. +- `organization-permissions-fixed.mp4`: 50-second stale-owner rejection → reload → corrected member view on `1b39324c5`. +- Contact sheets: inspected 19/17 cells from 48 sampled frames each. Recordings exclude credentials and operator control-plane pages. Both videos and all published screenshots were inspected. + +## Failed attempts and limits + +The initial unsigned Simulator build produced a Keychain entitlement error; rebuilt with normal local ad-hoc signing and reinstalled without erasing data. SecureStore load/write and logout then passed. One text replacement opened the floating development menu; that failed input was not counted as a product or authorization pass. The repeated recording uses a focused text append and verifies the actual entered draft. + +Automatic approval review rejected two optional extensions: creating a second persistent organization and deactivating the test membership. Neither action executed, and neither was retried through another route. Real UAT switching here proves unscoped → the same phone-created organization and reselection; switching between two organizations and inactive-member live rejection remain unverified. Local workerd covers scope mismatch, member/inactive/unknown/multiple-role refusals, provider outage, concurrent creation, response loss and reconciliation; those tests do not substitute for real cloud fault injection. + +Physical phone/camera, Android, VoiceOver/theme/text matrix, signed identity events/reconciliation, immediate access-JWT revocation, deployment interruption recovery and final Human acceptance remain separate T1 gates. No T2/Prod release and no merge. diff --git a/.github/pr-screenshots/tob-t1-organizations/create-contact-sheet.png b/.github/pr-screenshots/tob-t1-organizations/create-contact-sheet.png new file mode 100644 index 0000000000..f65368e5c8 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/create-contact-sheet.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/organization-create.mp4 b/.github/pr-screenshots/tob-t1-organizations/organization-create.mp4 new file mode 100644 index 0000000000..b807f55dc7 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/organization-create.mp4 differ diff --git a/.github/pr-screenshots/tob-t1-organizations/organization-permissions-fixed.mp4 b/.github/pr-screenshots/tob-t1-organizations/organization-permissions-fixed.mp4 new file mode 100644 index 0000000000..3c5701b0df Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/organization-permissions-fixed.mp4 differ diff --git a/.github/pr-screenshots/tob-t1-organizations/permissions-contact-sheet.png b/.github/pr-screenshots/tob-t1-organizations/permissions-contact-sheet.png new file mode 100644 index 0000000000..5f3972540a Binary files /dev/null and b/.github/pr-screenshots/tob-t1-organizations/permissions-contact-sheet.png differ diff --git a/.github/pr-screenshots/tob-t1-organizations/results.json b/.github/pr-screenshots/tob-t1-organizations/results.json new file mode 100644 index 0000000000..4d4449ee40 --- /dev/null +++ b/.github/pr-screenshots/tob-t1-organizations/results.json @@ -0,0 +1,26 @@ +{ + "environment": "uat", + "createRuntime": "494a20f1eb4f0666a2c90bc3c758af4e7ff5f46b", + "finalRuntime": "1b39324c5034eea2741abed62d579cd1044f2f86", + "serviceRuntime": "e5bc56298c2f63aa457ba39a1ce9ad45e2270824", + "mobileCreated": true, + "cliExactOrganizationMatch": true, + "cliSelectedSameOrganization": true, + "ownerSaveVersion": 2, + "staleOwnerWriteStatus": 403, + "denialCode": "ORGANIZATION_OWNER_REQUIRED", + "memberReadsUnchangedData": true, + "readonlyDraftAndListRegressionFixed": true, + "restoredActiveOwner": true, + "coldRestartRevalidatedSelection": true, + "mobileSessionRevoked": true, + "latestRefreshRejected": true, + "independentCliStillAdmitted": true, + "allowSignUp": false, + "isMagicAuthEnabled": false, + "newHostedBrowserProof": false, + "secondOrganizationCreated": false, + "liveInactiveMembershipTest": "not_executed_approval_review_rejected", + "prodChanged": false, + "merged": false +} diff --git a/.github/pr-screenshots/tob-t1-uat/01-entry.png b/.github/pr-screenshots/tob-t1-uat/01-entry.png new file mode 100644 index 0000000000..2b9e8eb8f6 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/01-entry.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/02-cloud-empty.png b/.github/pr-screenshots/tob-t1-uat/02-cloud-empty.png new file mode 100644 index 0000000000..7a98f9f3ae Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/02-cloud-empty.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/02-trusted-import.mp4 b/.github/pr-screenshots/tob-t1-uat/02-trusted-import.mp4 new file mode 100644 index 0000000000..39967b7d2d Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/02-trusted-import.mp4 differ diff --git a/.github/pr-screenshots/tob-t1-uat/03-invalid-config.png b/.github/pr-screenshots/tob-t1-uat/03-invalid-config.png new file mode 100644 index 0000000000..ea51665144 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/03-invalid-config.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/04-trust-review.png b/.github/pr-screenshots/tob-t1-uat/04-trust-review.png new file mode 100644 index 0000000000..72623189ce Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/04-trust-review.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/05-imported-signed-out.png b/.github/pr-screenshots/tob-t1-uat/05-imported-signed-out.png new file mode 100644 index 0000000000..311a484af4 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/05-imported-signed-out.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/README.md b/.github/pr-screenshots/tob-t1-uat/README.md new file mode 100644 index 0000000000..b1fb7ffdb9 --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/README.md @@ -0,0 +1,26 @@ +# TOB T1 native UAT evidence + +Captured on 2026-10-18 (JST), existing iPhone 17 Pro / iOS 26 Simulator, English/default text/light mode. Native screenshot size: 402 × 874. The Expo development overlay is visible. + +- Runtime JavaScript: `216963322c70c7ea009392da66e9a0e1f4327727`. +- Native build source: `a0b2919072af8fee62d8bb5fbb402fc52fb0d852`; subsequent changes only affect JavaScript and documentation. +- UAT service: `1ad3e6605a02965b3f9079f302ced02ca4a8f4c6`. +- Native executable SHA-256: `9afe634f14300fedd692f455eb2feaacdf07ba8d89e1777f5a0999a2f1afd753`. + +## Observed + +| Check | Result | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| Unpaired entry opens Cloud services without a Host | Pass | +| An ordinary URL is rejected as configuration | Pass | +| Public JSON recovers from the invalid input | Pass; the automation driver initially read the long text before entry finished; complete input and successful parsing were subsequently observed | +| Trust review displays the verified UAT server/instance before import | Pass | +| Explicit import reaches Configuration imported / Signed out | Pass | +| System WorkOS sign-in browser opens | Pass; recording stopped before sign-in | +| User authentication, callback, refresh, cold restart and logout isolation | Pending at initial capture; see subsequent unattended session report below | + +The screenshots compare states within the same revision, not an older-code baseline. `02-trusted-import.mp4` records the trust confirmation → imported result. Authentication forms, account details, codes and tokens are excluded. These assets do not qualify physical QR scanning, Android, dark/large-text/VoiceOver, or the rest of T1. The existing Simulator system CJK-font failure is a separate known environment limitation; no system font repair was attempted. + +## Subsequent unattended session run + +[Native session report and recording](unattended/README.md): real API authentication, native identity check, refresh rotation, cold restart and own-session logout/CLI isolation passed without Human login. Successful Hosted AuthKit/PKCE browser return remains unqualified. diff --git a/.github/pr-screenshots/tob-t1-uat/browser/01-local-signed-out.png b/.github/pr-screenshots/tob-t1-uat/browser/01-local-signed-out.png new file mode 100644 index 0000000000..1acf65c541 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/01-local-signed-out.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/02-emulator-browser.png b/.github/pr-screenshots/tob-t1-uat/browser/02-emulator-browser.png new file mode 100644 index 0000000000..c4be545a6e Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/02-emulator-browser.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/03-returned-signed-in.png b/.github/pr-screenshots/tob-t1-uat/browser/03-returned-signed-in.png new file mode 100644 index 0000000000..fa3e93466f Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/03-returned-signed-in.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/04-wrong-state.png b/.github/pr-screenshots/tob-t1-uat/browser/04-wrong-state.png new file mode 100644 index 0000000000..1f95f9f7d7 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/04-wrong-state.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/05-wrong-verifier.png b/.github/pr-screenshots/tob-t1-uat/browser/05-wrong-verifier.png new file mode 100644 index 0000000000..3e075ededa Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/05-wrong-verifier.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/06-real-uat-restored.png b/.github/pr-screenshots/tob-t1-uat/browser/06-real-uat-restored.png new file mode 100644 index 0000000000..f4f466e2f4 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/06-real-uat-restored.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/README.md b/.github/pr-screenshots/tob-t1-uat/browser/README.md new file mode 100644 index 0000000000..2425f199a3 --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/browser/README.md @@ -0,0 +1,27 @@ +# Native AuthKit browser acceptance — 2026-10-18 JST + +## Local unattended regression: passed + +Product JS: `216963322c70c7ea009392da66e9a0e1f4327727`; retained iOS native source: `a0b2919072af8fee62d8bb5fbb402fc52fb0d852`; driver: `b341c6454`. The driver changes test tooling only. Simulator: iPhone 17 Pro / iOS 26, 402 × 874, light/default text. WorkOS Emulate **0.15.0**, bound to loopback; no real identity or cloud request in this layer. + +| Path | Observation | +| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Normal login | Actual Expo system browser displayed WORKOS EMULATE, then returned to the same App. Normal CloudClient state check and code exchange succeeded using native Crypto's S256 verifier, without client secret. | +| Replay | Immediate second exchange of the same authorization code returned HTTP 400 / invalid_grant. | +| Wrong state | Native INVALID_CALLBACK, no token request and no session. | +| Wrong verifier | Emulator rejected exchange with HTTP 400; native LOGIN_FAILED and no session. | +| Isolation | Fixture visibly named LOCAL EMULATOR - not UAT; storage only in memory. Original runtime/config restored afterward, real UAT still signed out. | + +The 68-second recording includes keyboard occlusion, dismissal and successful return, followed by restoring/resetting the local fixture at the end; that last state is **not a logout proof**. Screenshots and the 24-cell contact sheet were inspected; all form data in this recording are fictional. No real authentication-form footage is published. + +The official emulator rejects our hostless custom callback spelling. The test-only loopback adapter maps an HTTP callback returned by the emulator to the unchanged native URI. It does not change the app's callback checks. This proves native browser return and client PKCE/state behavior against the emulator, **not** live WorkOS callback registration or actual TOB admission. The fixture identity response is local, albeit backed by a verified emulator JWT signature. Existing real-UAT session-lifecycle evidence remains a distinct layer. + +## Live unattended smoke attempt: not passed + +A bounded real Staging attempt used the existing initialized administrator, normal Hosted AuthKit and an API-created email code. The initial native fill stopped because the six one-character inputs and invalidated accessibility refs require per-field fresh snapshots. After correcting input handling, the hosted page rejected the code as expired. Whether an API-created code can complete that pre-existing Hosted AuthKit transaction was **not established**; this is not a successful native hosted login. + +The browser transaction was canceled. Magic Auth was restored to its previous disabled value; signup remained disabled. The one-time code file was removed. No password change, new user, account-role change, Radar bypass, deployment or Prod operation occurred. The local suite follows [WorkOS testing guidance](https://workos.com/docs/authkit/testing); routine automation should not depend on live hosted-page bots. + +## T1 close gates still separate + +The successful live hosted browser gate is recorded separately when completed. T1 additionally needs the organization slice (mobile create/list/switch and independent CLI readback), owner/member and no-membership denials, idempotent WorkOS/D1/Org DO creation/recovery, signed identity events and reconciliation/revocation windows, and the remaining deployment/import/native/Human-QA matrix. Physical QR capture, Android and accessibility/theme qualification have not been demonstrated by this Simulator run. Neither this report nor green CI closes T1 or authorizes T2/Prod. diff --git a/.github/pr-screenshots/tob-t1-uat/browser/browser-success.mp4 b/.github/pr-screenshots/tob-t1-uat/browser/browser-success.mp4 new file mode 100644 index 0000000000..4458550fa6 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/browser-success.mp4 differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/contact-sheet.png b/.github/pr-screenshots/tob-t1-uat/browser/contact-sheet.png new file mode 100644 index 0000000000..0cee9c1507 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/browser/contact-sheet.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/browser/success.json b/.github/pr-screenshots/tob-t1-uat/browser/success.json new file mode 100644 index 0000000000..e42c03a9d4 --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/browser/success.json @@ -0,0 +1,11 @@ +{ + "scenario": "success", + "authorizeS256": true, + "nativeCallbackRedirect": true, + "tokenRequests": 1, + "publicClient": true, + "verifierMatchesChallenge": true, + "exchangeStatus": 200, + "replayedCodeRejected": true, + "fixtureIdentityVerified": true +} diff --git a/.github/pr-screenshots/tob-t1-uat/browser/wrong-state.json b/.github/pr-screenshots/tob-t1-uat/browser/wrong-state.json new file mode 100644 index 0000000000..fa40f0064c --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/browser/wrong-state.json @@ -0,0 +1,5 @@ +{ + "scenario": "wrong-state", + "authorizeS256": true, + "nativeCallbackRedirect": true +} diff --git a/.github/pr-screenshots/tob-t1-uat/browser/wrong-verifier.json b/.github/pr-screenshots/tob-t1-uat/browser/wrong-verifier.json new file mode 100644 index 0000000000..7e8eb8041f --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/browser/wrong-verifier.json @@ -0,0 +1,9 @@ +{ + "scenario": "wrong-verifier", + "authorizeS256": true, + "nativeCallbackRedirect": true, + "tokenRequests": 1, + "publicClient": true, + "verifierMatchesChallenge": true, + "exchangeStatus": 400 +} diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/01-hosted-returned.png b/.github/pr-screenshots/tob-t1-uat/hosted-email/01-hosted-returned.png new file mode 100644 index 0000000000..c91e598f68 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/01-hosted-returned.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/02-refreshed.png b/.github/pr-screenshots/tob-t1-uat/hosted-email/02-refreshed.png new file mode 100644 index 0000000000..020bc88f16 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/02-refreshed.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/03-restart-unverified.png b/.github/pr-screenshots/tob-t1-uat/hosted-email/03-restart-unverified.png new file mode 100644 index 0000000000..7b698f9910 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/03-restart-unverified.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/04-restart-verified.png b/.github/pr-screenshots/tob-t1-uat/hosted-email/04-restart-verified.png new file mode 100644 index 0000000000..020bc88f16 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/04-restart-verified.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/05-logged-out.png b/.github/pr-screenshots/tob-t1-uat/hosted-email/05-logged-out.png new file mode 100644 index 0000000000..cf5222101e Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/05-logged-out.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/06-restart-signed-out.png b/.github/pr-screenshots/tob-t1-uat/hosted-email/06-restart-signed-out.png new file mode 100644 index 0000000000..fd4dc5cda9 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/06-restart-signed-out.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/README.md b/.github/pr-screenshots/tob-t1-uat/hosted-email/README.md new file mode 100644 index 0000000000..9eb1b1b8af --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/hosted-email/README.md @@ -0,0 +1,42 @@ +# Real Hosted AuthKit email login — iOS Simulator UAT + +Verified on 2026-10-18 (Asia/Tokyo), for DeepSeekBot #2101 / PR #2103 and TOB T1 #2. + +## Identity and scope + +- Product runtime: `216963322c70c7ea009392da66e9a0e1f4327727`. +- Retained native build source: `a0b2919072af8fee62d8bb5fbb402fc52fb0d852` (SDK58, iOS26 Simulator). +- UAT service: `1ad3e6605a02965b3f9079f302ced02ca4a8f4c6`. +- App language English, light appearance, default system text, 402 × 874 captures. +- No product code, cloud deployment, Prod configuration or role change in this run. + +## What passed + +1. Started signed out with the previously imported real UAT configuration and no emulator runtime adapter. The normal App **Sign in** action opened real Hosted AuthKit with its native S256 challenge/state and registered callback. +2. Temporarily enabled only Magic Auth in the verified WorkOS staging environment; registration remained closed. The hosted page sent the email code. After an automated input attempt was rejected, the page resent it and **the Human entered the new code directly in Device Hub**. The hosted browser returned to the App through the normal PKCE/state/code-exchange implementation; no injected API session or emulator token was used for this success. +3. The App showed **Signed in · Instance administrator**. Independent real UAT `/api/me` and WorkOS session readback matched the accepted bootstrap user, admitted administrator, exact trusted configuration and a fresh session absent from the baseline. The separate CLI session remained active. +4. **Refresh session** rotated the refresh token while preserving the same WorkOS session and admitted user. +5. Cold restart retained the saved session but displayed **Saved session · Check to verify**. **Check session** restored the same admitted administrator/session. +6. **Sign out** cleared App memory/SecureStore and reported remote revocation. WorkOS readback confirmed this session inactive; its latest rotated refresh token was rejected. The baseline CLI session remained active and the formal CLI `whoami` independently succeeded. +7. A second cold restart remained **Signed out** and retained public configuration. +8. Restored Magic Auth to disabled and read back `allowSignUp=false`, `isMagicAuthEnabled=false`. Closed the recorder/driver and released the shared Simulator window. No other session was revoked. + +## Evidence + +- `01-hosted-returned.png`: the admitted state after the actual hosted browser return. +- `02-refreshed.png`: refresh complete. +- `03-restart-unverified.png`: saved credentials after cold restart, before revalidation. +- `04-restart-verified.png`: same user/session verified again. +- `05-logged-out.png`: local cleanup and remote result shown in App. +- `06-restart-signed-out.png`: persistent signed-out state. +- `hosted-session.mp4`: 92-second post-login refresh/restart/logout/restart recording. +- `contact-sheet.png`: inspected 20-cell summary from 48 sampled frames. +- `results.json`: sanitized observed results; credentials and private session IDs are excluded. + +All six screenshots were inspected. Credential entry and the browser callback URL were deliberately not recorded; the recording starts **after** successful authentication. This is observed Human-assisted real hosted acceptance, not a recording of the complete login form and not unattended hosted authentication. + +## Failures and limits + +The first local hidden-input attempt submitted six digits but Hosted AuthKit rejected the code. The initial helper did not compare every entered digit; cause is unconfirmed. A revised helper added comparison, but the Human completed the accepted retry directly, so automated hosted OTP entry remains unqualified. The Node module-format warning was unrelated to the login result. A temporary checkpoint initially compared unsorted JSON keys, and an immediate post-refresh check ran while the App was busy; stable configuration comparison and a post-completion readback resolved these diagnostic failures without changing the product. + +This supersedes the earlier real-hosted pending note in `../browser/README.md` for the successful email/PKCE path only. Google login, physical QR/camera, Android, VoiceOver/theme/text matrix and remaining native failure scenarios are still separate gates. This run does not establish immediate invalidation of already-issued access JWTs. Broader T1 organization creation/list/switch, membership/permission, retries/reconciliation/revocation and final review remain open. No merge or T2 release. diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/contact-sheet.png b/.github/pr-screenshots/tob-t1-uat/hosted-email/contact-sheet.png new file mode 100644 index 0000000000..3ee771ae87 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/contact-sheet.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/hosted-session.mp4 b/.github/pr-screenshots/tob-t1-uat/hosted-email/hosted-session.mp4 new file mode 100644 index 0000000000..96d426bd71 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/hosted-email/hosted-session.mp4 differ diff --git a/.github/pr-screenshots/tob-t1-uat/hosted-email/results.json b/.github/pr-screenshots/tob-t1-uat/hosted-email/results.json new file mode 100644 index 0000000000..b0b9378943 --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/hosted-email/results.json @@ -0,0 +1,23 @@ +{ + "environment": "uat", + "hostedEmailLogin": "passed-human-assisted", + "normalNativePkceCallback": true, + "emulatorUsed": false, + "sessionInjected": false, + "bootstrapUserMatches": true, + "trustedConfigMatches": true, + "freshWorkosSession": true, + "refreshRotated": true, + "sameSessionAfterRestart": true, + "restartRequiresVerification": true, + "ownSessionRevoked": true, + "latestRefreshRejected": true, + "independentCliAdmitted": true, + "localAndStoredSessionRemoved": true, + "signedOutAfterRestart": true, + "configRetained": true, + "magicAuthRestoredDisabled": true, + "registrationRemainedClosed": true, + "credentialEntryRecorded": false, + "automatedHostedOtpEntry": "not-qualified" +} diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/01-native-session.mp4 b/.github/pr-screenshots/tob-t1-uat/unattended/01-native-session.mp4 new file mode 100644 index 0000000000..e9f341c432 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/01-native-session.mp4 differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/01-saved-unverified.png b/.github/pr-screenshots/tob-t1-uat/unattended/01-saved-unverified.png new file mode 100644 index 0000000000..b4860f66c3 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/01-saved-unverified.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/02-verified-admin.png b/.github/pr-screenshots/tob-t1-uat/unattended/02-verified-admin.png new file mode 100644 index 0000000000..1f2b825943 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/02-verified-admin.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/03-refreshed-admin.png b/.github/pr-screenshots/tob-t1-uat/unattended/03-refreshed-admin.png new file mode 100644 index 0000000000..0287afae58 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/03-refreshed-admin.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/04-restart-saved.png b/.github/pr-screenshots/tob-t1-uat/unattended/04-restart-saved.png new file mode 100644 index 0000000000..a32aa5e8b2 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/04-restart-saved.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/05-restart-verified.png b/.github/pr-screenshots/tob-t1-uat/unattended/05-restart-verified.png new file mode 100644 index 0000000000..0287afae58 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/05-restart-verified.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/06-logged-out.png b/.github/pr-screenshots/tob-t1-uat/unattended/06-logged-out.png new file mode 100644 index 0000000000..107c7d72bb Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/06-logged-out.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/07-restart-signed-out.png b/.github/pr-screenshots/tob-t1-uat/unattended/07-restart-signed-out.png new file mode 100644 index 0000000000..16e766f487 Binary files /dev/null and b/.github/pr-screenshots/tob-t1-uat/unattended/07-restart-signed-out.png differ diff --git a/.github/pr-screenshots/tob-t1-uat/unattended/README.md b/.github/pr-screenshots/tob-t1-uat/unattended/README.md new file mode 100644 index 0000000000..51672d4fa7 --- /dev/null +++ b/.github/pr-screenshots/tob-t1-uat/unattended/README.md @@ -0,0 +1,34 @@ +# Unattended native UAT session verification + +2026-10-18, 04:11–04:14 JST. Existing iPhone 17 Pro / iOS 26 Simulator, English, default text, light mode, 402 × 874. No Human login was required. No production deployment or product authorization change was made. + +## What this proves + +A separate session was authenticated through real WorkOS Staging's Magic Auth API and admitted by the real TOB UAT service. A local debugger fixture wrote it through the existing native SecureStore adapter, only after matching the already-trusted public UAT config. The App was then cold-started and operated through its normal native UI. This is **API authentication + native session lifecycle**, not a successful Hosted AuthKit/PKCE browser-login test. + +| Observable behavior | Result and evidence | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| Real API authentication and UAT admin admission | Pass; private operator profile and accepted bootstrap identity checked; no fake JWT or server bypass | +| Saved session is not initially treated as verified identity | Pass; `01-saved-unverified.png` | +| Native Check session verifies admin through UAT | Pass; `02-verified-admin.png`; same-session checkpoint | +| Native Refresh session rotates and persists refresh token | Pass; `03-refreshed-admin.png`; checkpoint reported rotation, not just matching UI text | +| Cold restart reads saved session but requires fresh identity verification | Pass; `04-restart-saved.png` → `05-restart-verified.png`; same session, same saved refresh token | +| Native Sign out clears local session and reports remote revocation | Pass; `06-logged-out.png`; memory and SecureStore both checked | +| WorkOS revocation and session isolation | Pass; exact fixture session inactive, its latest saved refresh token returned invalid_grant, all prior active sessions retained | +| Independent formal CLI remains usable | Pass; CLI whoami still verified the UAT administrator after App logout | +| Another cold restart stays signed out and retains trusted config | Pass; `07-restart-signed-out.png` | + +The first native Check session also rotated the API-prepared token before verifying identity; the explicit Refresh session then rotated it again. Cold restart restored that latest token without unexpected rotation. Issued access JWTs were **not** claimed to be instantly invalidated by logout; the remote assertion is session revocation and refresh rejection. + +## Source and evidence boundaries + +- Product runtime JS: `216963322c70c7ea009392da66e9a0e1f4327727`, the frozen shared batch. Product app/native inputs remain identical through driver commit `c12bc7936bd015f058d09b05d4ff3481c9e608a6`. +- Retained native build source: `a0b2919072af8fee62d8bb5fbb402fc52fb0d852`; previously verified executable SHA-256 `9afe634f14300fedd692f455eb2feaacdf07ba8d89e1777f5a0999a2f1afd753`. The intervening Settings build was never installed before this run. +- UAT service: `1ad3e6605a02965b3f9079f302ced02ca4a8f4c6`. +- Driver source: `c12bc7936bd015f058d09b05d4ff3481c9e608a6`. The local driver was refined during this run for pnpm dependency resolution, Hermes asynchronous evaluation, explicit Simulator target matching and same-fixture retry. Its final signed-out check passed; 18 focused driver/client tests, mobile typecheck, targeted lint/format passed. Earlier source baseline mobile suite was 550 pass / 8 skip; this report does not relabel that as a rerun at the final head. + +`01-native-session.mp4` is a 105.9-second native recording: saved/unverified → checked → refreshed → cold restart → checked → signed out. Seven screenshots and the recording's 17-cell contact sheet were visually inspected before publication. Preparation, injection, auth forms, codes, tokens and personal account identifiers are absent. The Expo development overlay is visible. Images compare states in the same revision, not an older-code baseline. + +One private temporary session was created for this run. The existing CLI session was independent. The temporary Magic Auth setting was restored, registration stayed closed, the fixture session was revoked, and its local credential file was removed after verification. The Simulator driver/recording were closed and the shared window returned; trusted config and app data were retained. + +Still unqualified: Hosted AuthKit successful browser return/PKCE, physical-camera QR, Android, dark/large-text/VoiceOver and Human product acceptance. Earlier native browser-open/cancel checks remain separate. This evidence does not close TOB T1's remaining organization and permission slices. diff --git a/apps/mobile/README.md b/apps/mobile/README.md index 978519ca3e..51507c4df3 100644 --- a/apps/mobile/README.md +++ b/apps/mobile/README.md @@ -156,3 +156,7 @@ Use the repository's pinned Node and pnpm versions. SDK 58 targets React Native CI checks Expo Doctor, types and tests, and exports both iOS and Android bundles in a required `mobile-export` job. Exporting bundles does not compile native code: separately verify a fresh iOS build and Android Release build (including R8), then the native user journeys. Android compilation requires compile SDK 37. The web preview explicitly retains single-page output. The simulator cache uses the strict fingerprint preset so app-version changes invalidate a cached native binary; SDK 58's balanced default ignores those version fields. For retained-data acceptance, install over an existing build with the same app identity and verify paired Hosts, local appearance, collapsed sections, DM activity preferences and statistics settings after restart. The fresh-install simulator journey alone does not prove upgrade preservation. Record simulator, real-device and Human QA results separately; follow [shared device QA](../../docs/agents/device-qa.md) for scheduling and resource limits. + +### Channel Agents (UAT candidate) + +Open Cloud channels → choose an owned channel → Channel agents. Create a named Agent, then explicitly Copy credential into its private configuration file. Only message read/send is granted in this channel; no organization membership or computer execution permission. One saved handoff can be retried after restart. Remove saved credential does not revoke the Agent; Revoke agent does. Secrets are omitted from normal snapshots/logs, and sign-out removes the saved handoff. Real UAT/native evidence is still pending for this increment. diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 8cbd92274a..a16daee81c 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -2,7 +2,7 @@ "expo": { "name": "DeepSeekBot Dev", "slug": "deepseekbot", - "scheme": ["deepseekbot-dev", "deepseekbot"], + "scheme": ["deepseekbot-dev", "deepseekbot", "ai.botharness.deepseekbot.dev"], "version": "0.0.1", "orientation": "portrait", "userInterfaceStyle": "automatic", @@ -79,7 +79,8 @@ "expo-font", "expo-image", "expo-sharing", - "expo-status-bar" + "expo-status-bar", + "expo-web-browser" ], "web": { "output": "single" diff --git a/apps/mobile/app/+native-intent.tsx b/apps/mobile/app/+native-intent.tsx new file mode 100644 index 0000000000..49bd002b13 --- /dev/null +++ b/apps/mobile/app/+native-intent.tsx @@ -0,0 +1,4 @@ +import { cloudNativeIntent } from '../src/cloud/native-intent'; +export function redirectSystemPath({ path }: { path: string; initial: boolean }) { + return cloudNativeIntent(path); +} diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index 2b10ff03c1..82772f7530 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -60,6 +60,17 @@ export default function RootLayout() { /> + + + + + (); const lastMethod = useRef('paste'); const pending = useRef(undefined); @@ -58,9 +59,13 @@ export default function PairScreen() { useFocusEffect( useCallback(() => { + setFocused(true); if (typeof params.p === 'string' && handled.current === undefined) void pair(`deepseekbot://pair?p=${params.p}`, 'deep_link'); - return () => pending.current?.abort(); + return () => { + setFocused(false); + pending.current?.abort(); + }; }, [params.p]), ); @@ -78,6 +83,7 @@ export default function PairScreen() { style={{ flex: 1, backgroundColor: theme.background }} contentContainerStyle={{ padding: 16, gap: 12 }} > +