Connect the attachment cable end-to-end (fase 2b) #113
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fase-2b-kabel"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Stacked on
fase-2-bijlagen(#111). This connects the attachment "cable" thatfase 2a parked into place, then hardens it against an adversarial review pass.
What this does
Attachments now travel as parked refs (owner-scoped uuids) through draft,
immediate reply and scheduled send, and upload to Mattermost only at send-time,
which dodges MM's 7-day unattached-file expiry. The old
POST /cards/{id}/filesroute is retired in favour of the card-lessPOST /outgoing-files; the reply route validates refs at request-time and againat send-time. A daily orphan sweep reaps parked files no row references after a
7-day TTL, wired into the app lifespan and reported in
get_task_statuses.Review findings fixed (security / correctness / WCAG)
Three adversarial reviewers ran over the diff. The confirmed findings:
headline bug). The save side was wired but not the load side: reopening a
draft showed no chips and the first keystroke PUT an empty
file_refslist,which detached and reaped the bytes.
useFileUpload.seedDonerestores parkedfiles as done entries and
useServerDraftSyncseedslastSavedRefsfrom theserver, so no empty list is ever sent.
instead of lingering to the sweep and eating the user's quota.
the 500ms window no longer drops an edit (notably an attachment removal that
would otherwise leave the row pointing at deleted bytes).
refs do not linger and reload later.
aria-liveregion and marked withtext, not colour alone; a failed attachment gives an actionable reason
("verwijder of probeer opnieuw") instead of the misleading "wait until uploads
finish".
_settle_sentdeletes parked files only afterSTATE_SENThas committed;a crash in the old window turned a real send into a FAILED row whose bytes
were already gone.
_delete_orphan_refsis scoped to the owner: refs are per-user-unique, sothe tenant filter is now an explicit invariant rather than a bet on uuid
uniqueness.
stat_ref/delete_reffail closed on an ownerless sidecar (noNone != Nonefall-through).cleanup_runner.__all__.Deferred with a note (out of this PR's scope): a pre-existing
mm_file_idmemoisation that is dead in the scheduled-runner retry path (duplicate MM
uploads on transient failure), the near-unreachable orphan-sweep TOCTOU, and the
residual hard-tab-close window on removal.
Repo hygiene
Separate commit:
docs/superpowers/andartifacts/are now git-ignored(brainstorming specs, plans and their HTML companions are design scratch, not
source). The spec and fase-1 plan that an earlier
git add -Aswept into #111are untracked (kept on disk).
Gates
Postgres for every touched module.
tscclean, vitest green (8 new tests for the fixes), productionbuild ok, eslint 0 errors.
pre-commit run --all-filesgreen (ruff 0.8.6, ruff-format, ty, secrets).Attachments now travel as parked refs (owned uuids) through draft, immediate reply and scheduled send, and upload to Mattermost only at send-time, dodging MM's 7-day unattached-file expiry. The old POST /cards/{id}/files route is retired in favour of the card-less POST /outgoing-files; the reply route validates refs at request-time and again at send-time. A daily orphan sweep reaps parked files no row references after a 7-day TTL, wired into the lifespan and reported in get_task_statuses. Fixes from the adversarial review pass (security / correctness / WCAG): - Draft attachments now rehydrate on load. The save side was wired but not the load side, so reopening a draft showed no chips and the first keystroke PUT an empty file_refs list, detaching and reaping the bytes. useFileUpload.seedDone restores parked files as done entries and useServerDraftSync seeds lastSavedRefs from the server so no empty list is ever sent. - A file removed mid-upload no longer leaks: the finished upload's ref is deleted instead of lingering to the sweep and eating the user's quota. - A pending draft-save is flushed on unmount, so closing the card modal inside the 500ms debounce no longer drops an edit (notably an attachment removal). - An immediate reply now drops the server draft, so its content and now-sent refs do not linger and reload later. - Upload failures are announced in an aria-live region and marked with text, not colour alone; a failed attachment gives an actionable reason instead of the misleading "wait until uploads finish". - An upload response without a usable ref is treated as a failure. - scheduled_runner._settle_sent deletes parked files only after STATE_SENT has committed; a crash in the old window turned a real send into a FAILED row whose bytes were already gone. - _delete_orphan_refs is scoped to the owner: refs are per-user-unique, so the tenant filter is now an explicit invariant, not a bet on uuid uniqueness. - outgoing_files stat_ref/delete_ref fail closed on an ownerless sidecar (no `None != None` fall-through). - The new sweep symbols are exported from cleanup_runner.__all__. Backend stays at 100% coverage; parity verified on Postgres.robbertbos referenced this pull request2026-07-21 04:26:35 +00:00
Pull request closed