Spencer's answer on issue 57 (open question 53): the copy must succeed before the packet counts as filed. If it fails, Admin is told with details and the packet stays in its previous stage so filing can be tried again.
What changes
FILE copies in the filing transaction. After the data book is stored, FILE checks the archive folder and copies the seal (data book, documents, manifest), then records the archive_copies row as done. Code is in domain/archive.py (copy_at_filing) and domain/pipeline.py.
A failed check or copy blocks the filing. The whole filing rolls back: no seal, no data book, and the packet stays complete. The person filing gets 503 archive_failed with the reason.
Admin is told. In its own transaction the server writes an archive.filing_blocked audit row and raises the critical archive_failed condition for that packet (subject filing:<packet id>). The alert names the packet, who tried, the folder and the error. A failed retry only counts on the open condition. The next successful filing clears it.
The worker job only catches up.archive-copy now handles filings made while the archive was off, with the same retries and its own archive_failed condition (subject archive).
Archive off works as before. With ARCHIVE_ROOT unset, or no folder saved, filing is unchanged.
Docs. PLAN question 53, CHANGELOG, FEED.md and SECURITY.md are updated.
Tests
test_archive.py is reworked. FILE makes the copy itself. Filing is refused when the folder is gone, and goes through once it is back, with the condition cleared. Filing is refused on a copy error or an unexpected error. No folder saved means no copy.
ruff, mypy and the non-PostgreSQL tests pass locally. The PostgreSQL tests run in branch CI.
One known edge: if the database commit fails after the files are written, an orphan folder stays in the archive. Packet Station never deletes there, and a refile writes a new seal folder.
Spencer's answer on issue 57 (open question 53): the copy must succeed before the packet counts as filed. If it fails, Admin is told with details and the packet stays in its previous stage so filing can be tried again.
## What changes
- **FILE copies in the filing transaction.** After the data book is stored, FILE checks the archive folder and copies the seal (data book, documents, manifest), then records the `archive_copies` row as done. Code is in `domain/archive.py` (`copy_at_filing`) and `domain/pipeline.py`.
- **A failed check or copy blocks the filing.** The whole filing rolls back: no seal, no data book, and the packet stays `complete`. The person filing gets 503 `archive_failed` with the reason.
- **Admin is told.** In its own transaction the server writes an `archive.filing_blocked` audit row and raises the critical `archive_failed` condition for that packet (subject `filing:<packet id>`). The alert names the packet, who tried, the folder and the error. A failed retry only counts on the open condition. The next successful filing clears it.
- **The worker job only catches up.** `archive-copy` now handles filings made while the archive was off, with the same retries and its own `archive_failed` condition (subject `archive`).
- **Archive off works as before.** With `ARCHIVE_ROOT` unset, or no folder saved, filing is unchanged.
- **Docs.** PLAN question 53, CHANGELOG, FEED.md and SECURITY.md are updated.
## Tests
- `test_archive.py` is reworked. FILE makes the copy itself. Filing is refused when the folder is gone, and goes through once it is back, with the condition cleared. Filing is refused on a copy error or an unexpected error. No folder saved means no copy.
- ruff, mypy and the non-PostgreSQL tests pass locally. The PostgreSQL tests run in branch CI.
One known edge: if the database commit fails after the files are written, an orphan folder stays in the archive. Packet Station never deletes there, and a refile writes a new seal folder.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
FILE checks the archive folder and copies the packet inside the filing
transaction. A failed check or copy rolls the filing back, so the packet
stays in its previous stage; the person filing gets 503 archive_failed,
and in its own transaction the server writes an archive.filing_blocked
audit row and raises the critical archive_failed condition to Admin for
that packet (who tried, folder, error). The next successful filing clears
it. The archive-copy job now only catches up filings made while the
archive was off. PLAN question 53, CHANGELOG, FEED and SECURITY updated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Spencer's answer on issue 57 (open question 53): the copy must succeed before the packet counts as filed. If it fails, Admin is told with details and the packet stays in its previous stage so filing can be tried again.
What changes
archive_copiesrow as done. Code is indomain/archive.py(copy_at_filing) anddomain/pipeline.py.complete. The person filing gets 503archive_failedwith the reason.archive.filing_blockedaudit row and raises the criticalarchive_failedcondition for that packet (subjectfiling:<packet id>). The alert names the packet, who tried, the folder and the error. A failed retry only counts on the open condition. The next successful filing clears it.archive-copynow handles filings made while the archive was off, with the same retries and its ownarchive_failedcondition (subjectarchive).ARCHIVE_ROOTunset, or no folder saved, filing is unchanged.Tests
test_archive.pyis reworked. FILE makes the copy itself. Filing is refused when the folder is gone, and goes through once it is back, with the condition cleared. Filing is refused on a copy error or an unexpected error. No folder saved means no copy.One known edge: if the database commit fails after the files are written, an orphan folder stays in the archive. Packet Station never deletes there, and a refile writes a new seal folder.
🤖 Generated with Claude Code