fix(documents): tighten archive/restore idempotency + document fire-and-forget

Three follow-ups from Task 6 code review:
1. applyEntityArchivedSuffix short-circuits when the folder is already
   archived — prevents archivedAt drift on backfill replay.
2. applyEntityRestoredSuffix short-circuits when the folder was never
   archived — matches the docstring's "no-op" claim.
3. Inline comment on archiveClient's fire-and-forget hook documents
   why Task 6 uses void (archive UI doesn't depend on folder sync)
   while Task 5 uses await (rename should be visible to the next
   read).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 11:38:18 +02:00
parent 4c5dc7ec17
commit 0412107d86
2 changed files with 6 additions and 0 deletions

View File

@@ -557,6 +557,10 @@ export async function archiveClient(id: string, portId: string, meta: AuditMeta)
await softDelete(clients, clients.id, id);
// fire-and-forget: archive UI does not depend on the folder suffix
// being stamped before the HTTP response returns. Task 5 (rename
// hook) uses await because the rename should be visible to the
// next read; archive does not.
void applyEntityArchivedSuffix(portId, 'client', id).catch((err) => {
logger.warn({ err, clientId: id }, 'Failed to apply archived suffix to client folder');
});

View File

@@ -600,6 +600,7 @@ export async function applyEntityArchivedSuffix(
const newName = folder.name.endsWith(ARCHIVED_SUFFIX)
? folder.name
: `${folder.name}${ARCHIVED_SUFFIX}`;
if (newName === folder.name && folder.archivedAt) return; // Already archived, no-op.
await db
.update(documentFolders)
.set({ name: newName, archivedAt: new Date(), updatedAt: new Date() })
@@ -628,6 +629,7 @@ export async function applyEntityRestoredSuffix(
const newName = folder.name.endsWith(ARCHIVED_SUFFIX)
? folder.name.slice(0, -ARCHIVED_SUFFIX.length)
: folder.name;
if (newName === folder.name && !folder.archivedAt) return; // Wasn't archived, no-op.
await db
.update(documentFolders)
.set({ name: newName, archivedAt: null, updatedAt: new Date() })