MacroscopeApp / Macroscope - Correctness Check
completed
Aug 25, 2026 in 12m 53s
14 issues identified (89 code objects reviewed).
• Merge Base:
06de9e9
• Head:d90f78b
Details
| ✅ | File Path | Comments Posted | Reason |
|---|---|---|---|
| ✅ | apps/server/src/attachmentMime.ts |
0 | |
| ❌ | apps/web/src/components/chat/composerDroppedFiles.ts |
2 | |
| ❌ | apps/web/src/types.ts |
5 | |
| ➖ | apps/web/src/components/chat/composerDroppedFiles.test.ts |
Excluded by default ignore patterns | |
| ✅ | apps/web/src/composerPlaceholder.ts |
0 | |
| ✅ | apps/web/src/components/Sidebar.tsx |
0 | |
| ➖ | apps/server/src/attachmentStore.test.ts |
Excluded by default ignore patterns | |
| ✅ | apps/server/src/provider/Layers/GrokAdapter.ts |
0 | |
| ➖ | apps/server/src/provider/Layers/CodexAdapter.test.ts |
Excluded by default ignore patterns | |
| ✅ | apps/server/src/provider/Layers/CodexAdapter.ts |
0 | |
| ➖ | apps/server/src/provider/Layers/ClaudeAdapter.test.ts |
Excluded by default ignore patterns | |
| ❌ | apps/server/src/provider/Layers/CursorAdapter.ts |
1 | |
| ✅ | apps/server/src/attachmentStore.ts |
0 | |
| ✅ | apps/web/src/lib/composerDraftUploads.ts |
0 | |
| ✅ | apps/web/src/historyBootstrap.ts |
0 | |
| ✅ | apps/web/src/hooks/useHandleNewThread.ts |
0 | |
| ✅ | packages/contracts/src/assets.ts |
0 | |
| ✅ | apps/server/src/orchestration/Layers/ProjectionPipeline.ts |
0 | |
| ✅ | apps/web/src/components/ChatView.logic.ts |
0 | |
| ✅ | apps/server/src/assets/AttachmentUpload.ts |
0 | |
| ✅ | apps/web/src/components/chat/MessagesTimeline.tsx |
0 | |
| ➖ | apps/web/src/lib/attachmentUploadQueue.test.ts |
Excluded by default ignore patterns | |
| ✅ | apps/web/src/components/preview/PreviewPanel.tsx |
0 | |
| ✅ | packages/contracts/src/orchestration.ts |
0 | |
| ➖ | apps/server/src/orchestration/Normalizer.attachments.test.ts |
Excluded by default ignore patterns | |
| ✅ | apps/web/src/components/chat/ExpandedImagePreview.tsx |
0 | |
| ✅ | apps/web/src/components/chat/ComposerPreviewAnnotationCards.tsx |
0 | |
| ✅ | apps/web/src/promptStashStore.ts |
0 | |
| ❌ | apps/server/src/provider/Layers/ClaudeAdapter.ts |
1 | |
| ➖ | apps/web/src/components/preview/PreviewView.test.tsx |
Excluded by default ignore patterns | |
| ✅ | apps/web/src/components/preview/PreviewView.tsx |
0 | |
| ❌ | apps/server/src/orchestration/Normalizer.ts |
1 | |
| ✅ | apps/web/src/lib/attachmentUploadQueue.ts |
0 | |
| ❌ | apps/web/src/components/ChatView.tsx |
2 | |
| ➖ | apps/web/src/composerDraftStore.test.ts |
Excluded by default ignore patterns | |
| ✅ | apps/web/src/composerDraftStore.ts |
0 | |
| ❌ | apps/web/src/components/chat/ChatComposer.tsx |
2 |
Filtered Issues Details
apps/web/src/components/ChatView.tsx
- line 1629: The composer persistence effect serializes each
ComposerAttachmentwithout its discriminant: the object written tostagedAttachmentByIdincludesid,name,mimeType,sizeBytes, anddataUrl, but omitstype. SincenormalizePersistedAttachmentdefaults missing types to"image"for backward compatibility, a persisted PDF is rehydrated as an image after reload and is then sent/rendered with the wrong kind. Persisttype: image.typefor new attachments. [ Cross-file consolidated ] - line 2759: Dropped text is read asynchronously, but after
await file.text()the code inserts via the live composer helpers without checking that the composer/thread is still the one that received the drop. If the user navigates to another thread while a text file is being read, the completed contents can be inserted into the new prompt (or the old draft can be updated using the new prompt snapshot), mixing content between drafts. Capture and validate the drop target before insertion, or write directly to the original draft target. [ Skipped comment generation ] - line 2769: After successfully reading text blocks, any insertion refusal is reported as a read failure:
insertComposerTextAtEndreturns false while connecting, during approval/pending input, or when project selection is required, but the code appends all text filenames tounreadableNamesand reportsCould not read .... The file was readable; the user gets misleading recovery guidance and the content is discarded. Handle a busy/blocked composer separately fromfile.text()failures. [ Out of scope (post-validation triage) ] - line 5631: A document-only send uses
IMAGE_ONLY_BOOTSTRAP_PROMPT, whose text explicitly says the user attached images and asks the provider to use image(s). With a PDF-only drop and no prompt, Claude receives a contradictory bootstrap message even though the attachment is a document, which can cause it to misinterpret or ignore the PDF. Choose the bootstrap text based on whether the attachments are images or documents. [ Cross-file consolidated ] - line 5645: The upload preflight error remains image-specific: when a PDF upload fails,
onSendreportsRetry or remove failed image uploads before sending.even though the failed item is a document. This gives the user incorrect recovery guidance for the newly supported file type; use attachment/file wording. [ Out of scope (post-validation triage) ] - line 5692: When attachment uploads are unavailable, the
turnAttachmentsPromisefallback serializes every staged attachment as{ type: "image" }. A newly supported PDF therefore reaches the turn as an image attachment instead of adocument, so the no-upload path cannot send PDFs correctly and may trigger the wrong provider validation/adapter behavior. Preserve the attachment's type when constructing this fallback payload. [ Cross-file consolidated ] - line 5700: The optimistic user message created for a send labels every attachment as
type: "image", including newly supported documents. Until the server message replaces it, a PDF is rendered through the image preview/lightbox path (and its blob URL is treated as an image), producing a broken preview and incorrect attachment semantics. Derive the optimistic attachment type fromimage.type. [ Cross-file consolidated ] - line 5767: For an attachment-only first message, the thread title fallback is hard-coded to
Image: .... A PDF-only drop therefore creates a misleading thread title such asImage: spec.pdf, even though the attachment is a document. Use a generic file/attachment label or branch onfirstComposerImage.type. [ Out of scope (post-validation triage) ]
apps/web/src/components/chat/ChatComposer.tsx
- line 765: Once PDFs are included in
composerAttachments, this call feeds them intoattachmentUploadBlockReason, whose user-facing messages still sayImage still uploading/failed image. A PDF upload failure or pending upload therefore gives the user the wrong object and makes the new file attachment UI misleading. [ Out of scope (post-validation triage) ] - line 1629: The persistence effect serializes every staged attachment without copying its
typefield. A dropped PDF is therefore saved as an attachment with no type, andhydrateAttachmentsFromPersisteddefaults missing types to"image"; after a reload the PDF is rendered and dispatched as an image instead of a document, breaking the new PDF flow and potentially causing provider rejection or corrupted attachment handling. [ Cross-file consolidated ] - line 2392: The intentional document exclusion from the stash records the PDF name in
oversizedImageNames. Restoring that stash consequently tells the user the PDF “exceeded the stash size limit,” even when it is tiny; the actual reason is that documents are unsupported by the image re-encoding stash. This gives a false explanation for a dropped attachment. [ Already posted ] - line 2663: The new document attachment is added to the same list that
ChatViewuses for its image-only title fallback. Sending a PDF without text therefore auto-titles the threadImage: <file>.pdf, which is incorrect for a document attachment and makes the new document support misleading in thread history. [ Out of scope (post-validation triage) ] - line 2663: The new document branch creates a
ComposerAttachmentwithtype: "document", but the existing send path inChatViewconverts every attachment in the no-upload-capability fallback totype: "image"before callingstartThreadTurn. Thus PDFs dropped in an environment wheresupportsAttachmentUploadsis false are sent as image blocks (and the Codex/Cursor/etc. image path), so Claude/OpenCode document support cannot work for that common fallback. [ Cross-file consolidated ] - line 2769: The async text-drop handler captures
activeThreadIdonly for error reporting, then resumes by calling the render'sinsertComposerTextAtEndafter eachfile.text()await. If the user switches threads while the file is being read, that stale callback reads the sharedpromptReffor the new thread but writes through the oldcomposerDraftTarget, mixing the new thread's prompt and the dropped file into the wrong draft (and leaving the visible thread without the file). [ Cross-file consolidated ] - line 2773: When
insertComposerTextAtEndrejects insertion, the handler appends everytextFilesname tounreadableNames, including files that were successfully read and already produced blocks (and even oversized files). The resultingCould not read ...error is false and hides the actual reason insertion was rejected, while the successfully read contents are discarded. [ Out of scope (post-validation triage) ] - line 2776: The text-drop failure expression reports
oversizedNamesinstead ofunreadableNameswhenever both lists are non-empty. Dropping one oversized file and one unreadable file therefore hides the read failure entirely, so the user is not told that the second file was also not inserted. [ Out of scope (post-validation triage) ] - line 2992:
getSendContextnow returns document attachments through the renamed attachments ref, but the existing optimistic-message construction inChatViewhardcodes every returned attachment totype: "image". After sending a PDF on a server that uploads it correctly, the immediate timeline renders the PDF blob as an image (a broken preview) until the server message replaces it, so document attachments are not displayed correctly during the send handoff. [ Cross-file consolidated ]
apps/web/src/components/chat/MessagesTimeline.tsx
- line 1021: The new document branch only runs for
regularImages, butUserTimelineRowpartitions attachments intopreviewImagesby the reservedpreview-annotation-filename prefix before reaching this branch. A normal PDF can legally have that name prefix; if its message has no parsed preview annotation, it is removed fromregularImagesand never rendered at all (and with an annotation it is forced through the image-only annotation card). This makes such document attachments disappear from the timeline. [ Cross-file consolidated ]
apps/web/src/composerDraftStore.ts
- line 147:
PersistedComposerThreadDraftState.attachmentsis widened to persist documents, but the normal composer serialization path writes staged attachments without atypefield. On reload,normalizePersistedAttachment/hydrateAttachmentsFromPersistedtreats an absent type as"image", so a staged PDF is restored as an image; its upload path then rejectsapplication/pdfas an unsupported image and the PDF cannot be sent after a refresh/restart. [ Already posted ]
apps/web/src/lib/attachmentUploadQueue.ts
- line 106: The new document branch routes PDFs through the existing upload-status path, but
attachmentUploadBlockReasonstill reports"Image still uploading"and"Retry or remove the failed image"for every attachment. While a PDF is uploading or fails, the composer therefore shows misleading image-only guidance instead of describing the document, which is especially confusing for the newly supported document flow. [ Out of scope (post-validation triage) ] - line 258: By widening
startAttachmentUploadto acceptComposerAttachment, PDF drops can reach the no-upload fallback used byChatView. That fallback serializes every local attachment as{ type: "image", ... }before reading its data URL, so whensupportsAttachmentUploadsis false a PDF is sent as an image and the server rejects it as an invalid image payload. The UI must either preserveimage.typein that fallback or reject document drops when the capability is unavailable. [ Cross-file consolidated ]
apps/web/src/types.ts
- line 39: PDF uploads are now included in the shared attachment upload wait, but the failure path still reports
Retry or remove failed image uploads before sending.for any failed attachment. A failed PDF consequently gets an inaccurate error message and users are not told that the document upload is the problem. The message should refer to failed attachment/file uploads. [ Out of scope (post-validation triage) ] - line 39: Because documents are now valid
ChatAttachmentvalues but are deliberately routed intodroppedImageNameswhen stashing, the stash UI counts an omitted PDF as an image and displays messages such asSome images were not restored/1 image dropped. This misleads users about what happened to their PDF; the stash metadata and copy should use generic file/attachment wording for document omissions. [ Out of scope (post-validation triage) ] - line 39: The new document member also flows through PDF-only thread creation, but
ChatViewstill uses the attachment-independent fallbacktitleSeed = \Image: ${firstComposerImageName}`whenever there is no text. As a result, dropping and sending onlyspec.pdfcreates a thread titledImage: spec.pdf`, which is incorrect for the newly supported document kind; the fallback must distinguish documents from images. [ Out of scope (post-validation triage) ]
Loading