You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* fix(approvals+storage): surface decision attachments with name, open, and correct download filename (#3504)
The approval inbox timeline showed a nameless "附件" chip that did nothing on
click, and even the downloaded file was named after the opaque URL token. Two
root causes, both in the framework:
approvals — `sys_approval_action.attachments` (a `Field.file`) stores rich
descriptors `{ id, name, url, mimeType, size }`, but `rowFromAction` mapped them
with `.map(String)`, collapsing each to "[object Object]". `ApprovalActionRow.
attachments` is now `ApprovalActionAttachment[]`; the descriptor carries name +
url, so consumers label/open attachments without reading the system `sys_file`.
storage — presigned downloads served `application/octet-stream` with no
`Content-Disposition`. `getSignedUrl`/`getPresignedDownload` now take
`PresignedDownloadOptions { filename, contentType, disposition }`; the REST
download routes pass the `sys_file` name+mime; the local adapter carries them in
the token and `_local/raw` emits an RFC 5987 Content-Disposition; S3 bakes the
same into the signed URL. Default `inline` preserves in-browser preview.
Verified end-to-end in app-showcase. Refs objectui #2820.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(spec): regenerate API surface snapshot for the two added interfaces
Additive only (0 breaking): ApprovalActionAttachment + PresignedDownloadOptions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(approvals): correct the attachment read-shape rationale and share the id rule
Follow-up on this PR's own change, after ADR-0104 D3 wave 2 landed on main.
The fix in this PR is right; its stated cause was inverted. It says the
`sys_approval_action.attachments` column "stores rich descriptors — a fileId is
resolved to a full descriptor on write". The code says otherwise:
approval-service.ts writes `input.attachments` verbatim (a fileId string[]),
and there is no write-side descriptor resolution anywhere in the repo. What
actually happens is that the column stores an opaque sys_file id — the stored
form of every media field — and the ObjectQL read path expands it into
`{ id, name, size, mimeType, url }` on the way out. That resolver was already
in this PR's base commit, so the descriptors observed in listActions came from
the read path, not from storage.
Left as written, that inverted account would have been a false statement about
storage sitting in the protocol contract, in the changeset, and in a regression
test comment — and PR-5a has since made the stored form explicitly an id, so it
would also have been contradicted by the schema. Corrected in all three places.
Also names the three read forms the normalizer actually handles, rather than
one: the expanded value (normal), a bare id (nothing to expand it into), and a
legacy inline blob written before the cutover, whose keys are snake_case
(`file_id`, `mime_type`). The last of those had no test; it has one now. The
id-token test reuses `isFileIdToken` from @objectstack/spec/data — the
platform's single arbiter of "opaque id or URL?" — so this normalizer and the
engine's read resolver cannot drift apart on that question, and a non-id string
is now surfaced as a url rather than mislabelled an id.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
* docs(storage): record the presigned-download options in the hand-written contract docs
The drift check flagged these: both storage-service pages transcribe
IStorageService by hand, and this PR adds an optional PresignedDownloadOptions
argument to getSignedUrl / getPresignedDownload. Left alone they would describe
a signature the code no longer has — the same declared-vs-actual gap the rest of
this PR is about, one level up in the docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
0 commit comments