File tree Expand file tree Collapse file tree
app/workspace/[workspaceId]/files Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -1147,6 +1147,12 @@ export function Files() {
11471147 const handleDrop = async ( e : React . DragEvent ) => {
11481148 if ( ! hasExternalFiles ( e . dataTransfer ) ) return
11491149 e . preventDefault ( )
1150+ /**
1151+ * The upload lands in the folder currently open, so the view must stay there. Without this
1152+ * the window-level teardown treats the drag as unconsumed and returns to the folder it
1153+ * began in — pulling the user out of the folder they just spring-opened to receive it.
1154+ */
1155+ springNav . markDropHandled ( )
11501156 dragCounterRef . current = 0
11511157 setIsDraggingOver ( false )
11521158 const dropped = Array . from ( e . dataTransfer . files )
Original file line number Diff line number Diff line change @@ -200,6 +200,13 @@ export const bulkMoveKnowledgeItems = defineAuthorizedKnowledgeUseCase({
200200 * selected folders plus their descendants, so this rejects both "into itself" and "into its
201201 * own child" before anything is written. Without it the resources move, the folders then fail
202202 * their cycle check, and the caller is left with a half-applied selection.
203+ *
204+ * This is a fast-fail optimization, not the enforcement point. It reads a snapshot taken
205+ * outside the folder mutation lock, so a concurrent reparent can invalidate it between the
206+ * check and the write. The invariant itself is enforced where it must be — `updateFolder`
207+ * re-checks `wouldCreateFolderCycle` inside `acquireFolderMutationLock`, so a cycle is never
208+ * created. Losing that race costs a reported per-folder `failed` alongside resources that
209+ * did move, which is the batch's documented `sequential_best_effort` outcome, not corruption.
203210 */
204211 if ( input . targetFolderId !== null && plan . covered . has ( input . targetFolderId ) ) {
205212 throw new OrchestrationError (
Original file line number Diff line number Diff line change @@ -227,6 +227,13 @@ export const bulkMoveTables = defineAuthorizedTableUseCase({
227227 * selected folders plus their descendants, so this rejects both "into itself" and "into its
228228 * own child" before anything is written. Without it the tables move, the folders then fail
229229 * their cycle check, and the caller is left with a half-applied selection.
230+ *
231+ * This is a fast-fail optimization, not the enforcement point. It reads a snapshot taken
232+ * outside the folder mutation lock, so a concurrent reparent can invalidate it between the
233+ * check and the write. The invariant itself is enforced where it must be — `updateFolder`
234+ * re-checks `wouldCreateFolderCycle` inside `acquireFolderMutationLock`, so a cycle is never
235+ * created. Losing that race costs a reported per-folder `failed` alongside resources that
236+ * did move, which is the batch's documented `sequential_best_effort` outcome, not corruption.
230237 */
231238 if ( input . targetFolderId !== null && plan . covered . has ( input . targetFolderId ) ) {
232239 throw new OrchestrationError (
You can’t perform that action at this time.
0 commit comments