lody: let a box attach a file — the cloud-token guard no longer preempts the local transport - #156
Merged
Merged
Conversation
…pts the local transport Seam patch 8. Every `+` attachment on a BlitzOS box failed with "Missing workspace or auth token" before a single MKCOL or PUT was issued (canary QA COMPB-1, BUG-CA-01), and "Retry upload" re-entered the same guard and did nothing (BUG-CA-02). The cause is upstream and it is an ordering bug: at each of the three entry points that HAVE a local handoff, `if (!workspaceId || !authToken)` stood in front of it. That handoff needs no cloud token — seam patch 3 already opened it to a non-Electron bridge, and `lody-attachments.test.ts` already proved it stages bytes on the box. A local-only Electron build has no token either, so it loses the same control. Five hunks in two vendored files, all one idea. The two file paths move the handoff above the guard; the in-session image path admits a missing token when the local transport is there and throws into the degrade-to-file `catch` upstream already wrote. Every hunk is inert with a token present. `use-chat-landing-image-draft.ts` is deliberately not patched: it has no local path to move in front of, so an image staged on the LANDING still fails. BLITZ-PATCHES.md records that gap. Declared as seam patch 8 in vendor/lody/BLITZ-PATCHES.md, with the upstream sketch at plans/evidence/lody-attachment-guard-pr.md. Pinned by packages/webapp/test/lody-attachment-guard.test.tsx, which drives the real vendored landing hook over a stub bridge across all three token/bridge cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013voh4zgczP2jmPrS2ERrav
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
Canary QA reported COMPB-1. Every
+attachment on a box fails. The message is"Missing workspace or auth token". No MKCOL and no PUT reaches dufs
(BUG-CA-01). "Retry upload" does nothing, because a retry enters the same guard
(BUG-CA-02). The verifier reproduced this twice in a real browser.
The cause
The cause is upstream, and it is an order-of-operations bug. Three attachment
entry points hold a local handoff. At each one,
if (!workspaceId || !authToken)stands in front of that handoff.
The local handoff needs no cloud token. Seam patch 3 opened it to a non-Electron
bridge, and
test/lody-attachments.test.tsproves that the channel behind itstages bytes on the box. The verifier also called
localProjects.sendSessionFileLocalby hand on the same page: dufs answeredMKCOL 201 and PUT 201. A box holds no cloud token, so the guard rejects the
attachment before that path can run.
This is not a BlitzOS-only concern.
apps/electroncomposes the same localtransport, and the root
AGENTS.mdforbids authenticated product-cloud requestsfrom the OSS desktop entry. A local-only Electron build holds no token either,
and loses the same control.
The change
Seam patch 8, declared in
vendor/lody/BLITZ-PATCHES.md. Five hunks in twovendored files, and all five carry one idea.
components/sessions/session-chat-input-area.tsxcatchthat upstream already wrote.hooks/use-chat-landing-file-draft.tsThe guard is moved, not deleted. It still owns the cloud path below the local
one. A browser with no token and no bridge still fails with the same message.
plans/evidence/lody-attachment-guard-pr.mdholds the upstream PR sketch. Dropthis seam patch when upstream merges the fix.
Safety when a cloud token IS present
Every hunk is inert with a token. Both moved blocks keep their original
condition and gain only
workspaceId &&, which the guard below them assertedanyway. Upstream already ran the local fast path FIRST whenever
canSendFileLocallywas true, so a token holder on a machine with a bridgetakes the same branch as before. A token holder with no bridge declines the
local block and falls into the unchanged guard and the unchanged cloud upload.
The image guard only widens:
!authTokenalone no longer bails, but a requestwith a token never reached that bail.
test/lody-attachment-guard.test.tsxchecks this rather than asserts it. Itmounts the REAL vendored landing hook over a stub bridge and stages a file in
three cases: no token with a bridge (the fix), no token with no bridge (the same
refusal), and a token with a bridge (unchanged). The in-session half needs a
daemon that CI does not have, so the test pins that half at the source, under
the rule
lody-surface-tabs.test.tsxalready applies.What this does not do
hooks/use-chat-landing-image-draft.tscarries the same guard and is leftalone. That hook has no local handoff and no degrade-to-file fallback, so there
is nothing to move in front of the guard. An image on the LANDING therefore
still fails on a box, while the same image inside a session becomes a file
attachment.
BLITZ-PATCHES.md§8 records that gap. A fix for it adds behaviourupstream does not have, and it belongs in a change of its own.
Gates
npm run typecheck— clean.npm run lint:gate— passed; 74 anti-slop findings, all at baseline.BLITZDEV_MANAGED=1 npm test— 91 files, 765 passed, 6 skipped, exit 0.An earlier run failed two daemon-backed suites on boot timeouts. Two other full
suites ran on the same box at that moment, and both of those runs failed as
well. The clean run above ran alone.
🤖 Generated with Claude Code
https://claude.ai/code/session_01P92fhMxx375D1oYp5gPqW6