Offer a full pond archive before deletion and before purge #305

Closed
opened 2026-08-01 06:25:53 +02:00 by opus-5 · 0 comments
Contributor

Deleting a pond should offer a last full archive to download.

What already exists — and where the real gap is

A safety prompt is already in place: DeletePondSection
(apps/web/src/ponds/DeletePondSection.tsx) requires typing the pond
name exactly and keeps the button disabled until it matches, the same
backstop as the backup restore. That stays as it is — it is stricter than
a confirm dialog and needs no focus management.

Deletion is a soft delete into the site-level trash
(PondsService.softDelete, apps/api/src/ponds/ponds.service.ts:180),
and a site admin can restore. But purgeDuePonds()
(apps/api/src/trash/trash.module.ts:46) removes trashed ponds for good
after trash.retentionDays (default 30), and a site admin can purge
manually (#193). The purge test states the standard: after it, nothing
referencing the pond survives — neither rows nor files on disk.

The gap is therefore not the missing prompt. It is that the person who
deletes the pond loses access the moment they do
: the pond disappears
from their view, only a site admin can bring it back, and the existing
pond export is no longer reachable for them. The archive has to be
offered inside the deletion flow, not afterwards.

Scope of the archive: full, not just pages

The existing exportPond (apps/api/src/import-export/export.service.ts:78)
ships Markdown plus the images referenced by pages. As a last resort
that is not enough — an attachment nobody embedded would vanish
unnoticed. The archive for this flow contains:

  • Page Markdown, as today.
  • All attachments of the pond, not only referenced ones.
  • Pond settings, labels, comments and the page hierarchy, as a
    machine-readable sidecar.

Build on the archive-level manifest.json that already exists (#210,
export.service.ts:206) — extend it rather than adding a second
descriptor. Give it an explicit format version so a later reader can tell
what it is looking at.

Re-import is out of scope for this issue. The archive is a
preservation format: complete, documented and versioned, so an importer
can be written later without guesswork. Building that importer is a
separate piece of work. Say so in the UI text too — the user must not
believe they are holding a one-click restore.

Where the offer appears

  1. In the pond deletion flow, next to the type-to-confirm field, before
    the pond is trashed.
  2. In the site admin's manual purge dialog (#193) — the genuinely
    irreversible step.

The automatic purge after the retention period gets no server-side
archive. That was considered and rejected: it would consume storage on
the instance indefinitely and raises the unanswerable question of who
deletes those archives.

Not downloading is allowed. When the archive has not been fetched, the
confirmation area states plainly that after the retention period nothing
of the pond remains. No forced download — a pond full of test pages
should not require one, and the server cannot tell whether a file
actually arrived anyway.

Completeness depends on who is asking

appendPondMarkdown filters by the requester's read permissions
(export.service.ts:113), so a pond admin who cannot read every page
gets an incomplete archive. This must be visible, not implied:

  • When the archive would omit pages the requester cannot read, say so in
    the UI with the number of omitted pages.
  • The site admin's archive from the purge dialog is complete by
    construction.

An archive silently missing content is worse than no archive, because it
ends the search.

VS-NfD obligations

The archive is a bulk egress channel and the read trail already treats
the pond ZIP as one: appendPondMarkdown records an export event per
classified page before any classified bytes enter the stream, so a
failed write aborts the download with the evidence intact (ADR 0023).
The full archive must keep this property — including for the newly added
attachments, whose pages carry the classification.

Classification markings and the VS-NfD_ filename prefix stay as they
are; the extended manifest keeps carrying the per-file level and the
archive's highest level.

Check whether "pond archive downloaded" warrants its own audit action. If
so, it needs an entry in apps/api/src/audit/audit-actions.ts and in
docs/architecture/audit-events.md with a catalogue minor bump — the
gate audit-catalogue.test.ts fails otherwise.

Accessibility

  • The archive button is a normal control in the flow, reachable by
    keyboard, with a localised name that says what it produces.
  • Generating a large archive takes time: the progress and completion are
    announced, not only shown by a spinner.
  • The finality hint and the "N pages omitted" warning are text, announced
    by assistive technology — not conveyed by colour or an icon alone.
  • The type-to-confirm field keeps its label and its association with the
    button's disabled state.
  • Covered in apps/web/e2e/a11y.spec.ts per the standing rule.

Acceptance criteria

  • A pond admin can download a full archive from the deletion flow
    before the pond is trashed.
  • The archive contains every page they may read, all attachments,
    and a versioned manifest with settings, labels, comments and
    hierarchy.
  • Unpacking the archive of a real pond yields the expected files —
    verified by hand, not only asserted in a test.
  • A pond admin with restricted read access sees how many pages are
    omitted before downloading.
  • The site admin's purge dialog offers the same archive, complete.
  • Skipping the download is possible and shows the finality hint.
  • Classified pages produce read-trail export events before any
    classified bytes are streamed, attachments included.
  • The existing type-to-confirm behaviour is unchanged.
  • The archive format is documented under docs/, with its version.
  • pnpm lint, pnpm typecheck, pnpm i18n:check, api suite and the
    a11y spec pass.
Deleting a pond should offer a last full archive to download. ## What already exists — and where the real gap is A safety prompt is already in place: `DeletePondSection` (`apps/web/src/ponds/DeletePondSection.tsx`) requires typing the pond name exactly and keeps the button disabled until it matches, the same backstop as the backup restore. That stays as it is — it is stricter than a confirm dialog and needs no focus management. Deletion is a soft delete into the site-level trash (`PondsService.softDelete`, `apps/api/src/ponds/ponds.service.ts:180`), and a site admin can restore. But `purgeDuePonds()` (`apps/api/src/trash/trash.module.ts:46`) removes trashed ponds for good after `trash.retentionDays` (default 30), and a site admin can purge manually (#193). The purge test states the standard: after it, nothing referencing the pond survives — neither rows nor files on disk. The gap is therefore not the missing prompt. It is that **the person who deletes the pond loses access the moment they do**: the pond disappears from their view, only a site admin can bring it back, and the existing pond export is no longer reachable for them. The archive has to be offered inside the deletion flow, not afterwards. ## Scope of the archive: full, not just pages The existing `exportPond` (`apps/api/src/import-export/export.service.ts:78`) ships Markdown plus the images *referenced by pages*. As a last resort that is not enough — an attachment nobody embedded would vanish unnoticed. The archive for this flow contains: - Page Markdown, as today. - **All** attachments of the pond, not only referenced ones. - Pond settings, labels, comments and the page hierarchy, as a machine-readable sidecar. Build on the archive-level `manifest.json` that already exists (#210, `export.service.ts:206`) — extend it rather than adding a second descriptor. Give it an explicit format version so a later reader can tell what it is looking at. **Re-import is out of scope for this issue.** The archive is a preservation format: complete, documented and versioned, so an importer can be written later without guesswork. Building that importer is a separate piece of work. Say so in the UI text too — the user must not believe they are holding a one-click restore. ## Where the offer appears 1. In the pond deletion flow, next to the type-to-confirm field, before the pond is trashed. 2. In the site admin's manual purge dialog (#193) — the genuinely irreversible step. The automatic purge after the retention period gets no server-side archive. That was considered and rejected: it would consume storage on the instance indefinitely and raises the unanswerable question of who deletes those archives. Not downloading is allowed. When the archive has not been fetched, the confirmation area states plainly that after the retention period nothing of the pond remains. No forced download — a pond full of test pages should not require one, and the server cannot tell whether a file actually arrived anyway. ## Completeness depends on who is asking `appendPondMarkdown` filters by the requester's read permissions (`export.service.ts:113`), so a pond admin who cannot read every page gets an incomplete archive. This must be visible, not implied: - When the archive would omit pages the requester cannot read, say so in the UI with the number of omitted pages. - The site admin's archive from the purge dialog is complete by construction. An archive silently missing content is worse than no archive, because it ends the search. ## VS-NfD obligations The archive is a bulk egress channel and the read trail already treats the pond ZIP as one: `appendPondMarkdown` records an `export` event per classified page **before** any classified bytes enter the stream, so a failed write aborts the download with the evidence intact (ADR 0023). The full archive must keep this property — including for the newly added attachments, whose pages carry the classification. Classification markings and the `VS-NfD_` filename prefix stay as they are; the extended manifest keeps carrying the per-file level and the archive's highest level. Check whether "pond archive downloaded" warrants its own audit action. If so, it needs an entry in `apps/api/src/audit/audit-actions.ts` **and** in `docs/architecture/audit-events.md` with a catalogue minor bump — the gate `audit-catalogue.test.ts` fails otherwise. ## Accessibility - The archive button is a normal control in the flow, reachable by keyboard, with a localised name that says what it produces. - Generating a large archive takes time: the progress and completion are announced, not only shown by a spinner. - The finality hint and the "N pages omitted" warning are text, announced by assistive technology — not conveyed by colour or an icon alone. - The type-to-confirm field keeps its label and its association with the button's disabled state. - Covered in `apps/web/e2e/a11y.spec.ts` per the standing rule. ## Acceptance criteria - [ ] A pond admin can download a full archive from the deletion flow before the pond is trashed. - [ ] The archive contains every page they may read, **all** attachments, and a versioned manifest with settings, labels, comments and hierarchy. - [ ] Unpacking the archive of a real pond yields the expected files — verified by hand, not only asserted in a test. - [ ] A pond admin with restricted read access sees how many pages are omitted before downloading. - [ ] The site admin's purge dialog offers the same archive, complete. - [ ] Skipping the download is possible and shows the finality hint. - [ ] Classified pages produce read-trail `export` events before any classified bytes are streamed, attachments included. - [ ] The existing type-to-confirm behaviour is unchanged. - [ ] The archive format is documented under `docs/`, with its version. - [ ] `pnpm lint`, `pnpm typecheck`, `pnpm i18n:check`, api suite and the a11y spec pass.
opus-5 added this to the M33 — Tweaks & Feinschliff milestone 2026-08-01 06:25:53 +02:00
opus-5 added the
effort:L
backend
frontend
area:export
labels 2026-08-01 06:25:53 +02:00
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#305
No description provided.