Restore the workspace hostSubscribe arm the browser and workspace merges
dropped between them, retire the settings capability the workspace lane
moved onto ui.get/ui.set, replace the task lane's page RPC sender with the
shared one, and re-pin the census, grant and correlation tests to the
merged registry.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
A terminal artifact is now addressed by the tab plus the terminal text that
named it. The desktop re-resolves the path and re-earns the file grant on every
chunk, so a retired terminal cannot keep serving bytes and there is no token,
TTL or record cap to keep. `artifactRelease` has nothing left to release.
An agent session is addressed by the agent and provider session id the scan
already reports, and paging is a caller-supplied offset. The vault's own row id
embeds the transcript path, so the projection publishes the provider id instead
of the composite. The resume mutation key is derived from the session, and the
host's existing create-result TTL owns its lifetime.
The agent-history host calls now go through the typed runtime functions the
sibling RPC handlers call, with no dispatch by method name and no re-parsing.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Clearing page readiness in an effect let one paint show the outgoing page's
readiness against the incoming document. Keying the state on the session and
view epoch resets it in the same render that changes them.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The page now drives the same desktop requests the native app makes, through
an RpcClient-shaped sender over workspace.hostRequest, so the shell's read and
source translators and their contracts are gone. Creation itself stays in the
shell: a workspace no catalog page has listed has no page handle, and only the
authority can mint one. Repo ids cross as host ids, so the re-lookups that
existed to re-resolve opaque repo handles are deleted with them.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The shell capped every forwarded call at its 15s default, which is shorter
than an SSH connect needs. The envelope now carries an optional deadline the
caller sets through the same request options it already passes, clamped to
180s so a page cannot park a host call indefinitely.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The download now buffers each asset in memory, verifies it, and hands the store
one complete file, so the chunk protocol and its per-chunk hashes go with the
stage ids. The session/recovery/refresh/capability hook chain collapses into one
reducer-driven hook with no prop-drilled refs, and A/B rollback goes with it:
there is no earlier generation to promote, so a page that cannot load makes the
shell delete that host's cache and download again, once per host selection.
That retires the health deadline, the process-failure tracker, the cached-build
probe, rejected build ids, and the whole recovery toolbar.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The device harnesses polled `activation.json` to learn which build was live.
A host now keeps exactly one committed generation, so that directory is the
record. The crash-loop and corrupt-cache drills only ever asserted the A/B
rollback and are removed with it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
A strict zod schema on a parsed value already rejects `__proto__`, unknown keys,
and unsafe numbers, so the 280-line hand-written scanner only ever bought two
things: a repeated key, which `JSON.parse` silently resolves before the schema
sees it, and an unpaired surrogate, which parses into a perfectly valid string.
The test states both, and the replacement checks only those.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The desktop was parsing the page's payload contract under a placeholder
workspace handle, because the contract named a workspace the desktop never
learns. Each payload is now built once per scope from one field shape: the page
keeps `workspaceId`, the desktop gets `worktree`. The wrappers declare the real
schema instead of a passthrough, and results carry no workspace handle at all.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The wrappers called the RPC dispatcher by method name and got `unknown` back, so
the projection rebuilt every field with typeof checks and regex before a final
parse. That is the shell's sanitizer moved rather than retired: the desktop is
where these values come from.
Each wrapper now calls the typed runtime command directly, so a review is
`HostedReviewInfo` plus a discriminated `GitHubWorkItemDetails` or
`GitLabWorkItemDetails`, and the projection is a field map with a provider split.
The clipping stays, because comment, file, check-job and diff-row counts are real
size caps the provider does not honour.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The wrappers called sibling RPC handlers, whose return type is `unknown`, so
every projection rebuilt its result field by field out of `isRecord` checks and
per-field schema parses. They now call the runtime commands directly, which are
typed, and project by field mapping.
What survives is bounding, which is the only thing the page contract actually
needs from the Desktop: list caps, response budgets, text limits, and the
narrowing of a Git string into the object id, ref name or workspace-relative
path the page schema admits.
The projections are Desktop-only, so they no longer sit in the shared directory
the page package compiles.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The moved module exceeded the root 300-line ceiling, so the repo/folder/project
host-status derivation is its own concept file and the target module keeps only
the session-to-worktree resolution.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Session handles and page cursors are now desktop state keyed by connection, so
the page never sees a cwd, a transcript path or a provider session id. The pure
resume target, launch plan, scope-path and session-worktree modules move to
src/shared so the desktop and the released native app run one implementation.
A cross-workspace resume no longer navigates: the page can only route to a
workspace it already holds a shell-minted handle for, so the desktop reports
whether the resume landed in the current workspace instead of naming another.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The Source Control journey looked for a `sourceControl.reviewOpen` bridge
error, which the generic host lane no longer reports under that name. The
observation now records the method a host request named, and the journey
matches on it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The tolerant rewrite existed to absorb skew between a shell and a page that
ship separately, but the protocol version is the compat gate and hybrid has no
released users. Deleting it means the host must emit exactly what the page
declares, so the mobile-web transcript read reshapes blocks to the contract
instead of relying on the page to strip host-only detail. Two gaps the rewrite
was hiding are now closed in the contract: the pending-snapshot flag the shared
chat view consumes, and the block fields the page never named.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Nothing issues a task targetId now that the shell no longer mediates, so the
target builders, view state and result schemas stop carrying a field that is
always absent.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The shell no longer knows what a Source Control operation is. Sixteen
translator modules, their preflights, the branch-compare continuation cache and
the capability grants they needed are gone; forwarding, the byte envelope and
the workspace-handle rewrite are all that is left, and they are the generic
host lane's job.
Commit-message generation keeps its operation. Hosted review creation still
reads the repository in the shell, so the read it shared with Source Control
moves into the provider module that still needs it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
accounts.list, the three selection methods and accounts.subscribe now reach
the desktop through the generic lane, and the page parses the snapshot with
its own schema. The shell keeps only the reset-credit arms, which mint a
native idempotency key and so cannot be a plain forward.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The shell no longer knows what a review is. `mobileWeb.review.*` reads the
branch's hosted review, projects the provider work item into the page contract,
and runs every comment, management, submission and creation action; the page
reaches all nine through the generic host request and parses the answer with the
same shared contracts. GitHub and GitLab split only inside the desktop handlers.
Check-run job logs and review file diffs are the two unbounded results, so both
are clipped on the desktop rather than sanitized twice on the way across.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The artifact token registry now lives on the desktop keyed by connection, so
host paths and terminal file grants never reach the page. Markdown tab reads
and saves resolve the tab, clip past the edit ceiling, and fall back to disk
on the desktop; only device-local drafts stay in the shell.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Same shape as iOS: writeStagedAsset/commitGeneration/abortGeneration replace the
stage protocol, the manifest is the canonical document the build id hashes, and
the host keeps one generation under a four-host LRU cap. Drops the activation
record, the cache quota policy, and the exact-JSON scanner with them.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The shell no longer knows a task operation, an opaque work-item handle or a
project-table cursor. The desktop socket gate is the only allowlist, and the
page addresses repositories, work items and projects by their host ids.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The shell collapsed six transport states into four, and every page route
expanded them back. Both mappings are gone; the page reads the state the
transport reported.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Each Source Control call now names the Desktop method and parses the answer
against its contract, so the page no longer depends on a shell translator to
reshape a result it already knows how to read.
The optimistic-concurrency fields the page sent only so the shell could re-read
Desktop state before writing are gone; the Desktop reauthorizes the write and
reports the refusal. Commit-message generation keeps its shell operation
because it outlives the host lane's request deadline and needs a cancel.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The browser capability is gone from the bridge registry, so the shell no longer
names a browser RPC, sanitizes a tab URL or chunks a frame. hostRequest carries
pointer traffic now, so its bucket takes over the retired pointer grant's rate.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The page addresses the browser page id directly through requestMobileWebHost and
parses each result with the shared contract, so no browser translator remains
between it and the desktop.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
worktree.activate/set/sleep/rm, repo.list and ui.get/ui.set now reach the
desktop through workspace.hostRequest, and a mobileWeb.workspace.subscribe
wrapper reduces the client-event firehose to bare catalog change types. The
shell keeps only the worktree.ps catalog read that mints workspace handles;
repo handles are gone from that path, so the page addresses repos by host id.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The shell translated every Source Control operation, so the page's own
projections lived on the phone and the Desktop answered raw Git. Move the
bounding and the mobile projection to the Desktop, where the result can be
clipped before it crosses the bridge, and expose the writes the page reaches
directly through the generic host allowlist.
Branch compare answers in one clipped response, so the shell's continuation
cache and its revision handshake have nothing left to do.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb