Reference letters: who wrote one, when, and which applications it went with #666
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#666
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
From the list of 2026-10-02, under Applications: "Add reference letters." Many applications ask for a letter of reference, and today the best Postulo can do is hold the PDF as an anonymous file. What a person needs to know about one, who wrote it, when, whether it is still good to send and which applications it went with, is not recorded anywhere. This is not the same thing as the career section "References" (the people who will vouch for you), which is #696; the two are kept apart below.
What exists today
DocumentKind.REFERENCE("Reference") exists and is registered as a kind that Postulo holds rather than composes (src/postulo/documents/kinds.py:65,:180;authored=False). The Files page and the upload form offer it with the other kinds.UploadedDocumentcarries a title, a kind, the file, notes, a language, a checksum and a version chain (documents/models.py:564-640). Nothing says who wrote the letter, when, or whether it may still be sent. The uploads list has no filter by kind either, though the form promises one (D2, #667).documents/forms.py:535-541); the choice is stored onApplication.sent_uploads(applications/models.py:411-417) and a "Documents sent" line is written on the timeline (documents/slow.py:117-125). The application's documents page lists them (documents/views.py:880-895).LetterKindcover, motivation, speculative and follow-up (documents/models.py:428-446).Contactmay have no company (jobs/models.py:604-640), and an application records who referred you (Application.referred_by,applications/models.py:377-387, #239): somebody who put your name forward, which is a different act from somebody who wrote about you.EventKindhas nothing for a reference requested or received (applications/models.py:502-515).core/gdpr.py:155-181,:277-316). A letter is data about its author, and no row exists today to find it by.documents/stores.py:112-125), so a connected Paperless would receive a reference letter the day it is uploaded.Checked against
mainatf09b6010b.Proposal
ReferenceLetterside record, one-to-one with anUploadedDocumentof kind Reference: the referee (aContactof the person's own, which may have no company), the date it was written (optional), a date after which it should not be sent (optional), and how it reaches the employer (you send it, or the referee sends it themselves). The file is the existing upload, so storage, checksum, copies and the version chain are unchanged. Deleting the upload deletes the record.contact_documentcarries the letters' metadata (referee, dates, file name, applications), and the erasure screen counts them.Decisions
Contact; an entry of the career's "References" section (#696); free text. Recommended: aContact, which exists today and which #696 is expected to link too, so the two are joined through the person and neither depends on the other being built first. Free text would leave nothing for an erasure to find.unlinked(the wayErasureReportalready counts applications and listing entries,core/gdpr.py:188-235). Deleting an upload that applications say they sent already warns (documents/views.py:628-650, #217), and that warning applies.kind_specsfrom "all on" to "all on but this one" and is recorded here as a deviation from #13's rule that every kind goes.Contactand nothing else.Builds on
#28 and #133 (letter and document kinds, closed), #239 (who referred you, closed), #217 (what was sent stays true, closed).
Needed by
Nothing waits on it. Related: #667 (the Documents page does not link to Files, where this is entered), #663 (an uploaded letter's integrity), #696.
Done when
tests/test_documents.py(new tests): an upload of kind Reference takes a referee and dates; another kind ignores and does not show them; deleting the upload deletes the record; deleting the referee unlinks it.tests/test_applications.pyandtests/test_document_record.py: a letter attached through Record what you sent shows its referee on the application's documents page; one the referee sends themselves is not offered as an attachment; a past "do not send after" date is shown.tests/test_gdpr.py: the contact's document lists the letters and the erasure screen counts them; unlinking is the default.tests/test_stores.py: a new connection has the Reference switch off, and an existing one keeps the setting it has.core/export.pyFORMAT_VERSION, 29 onf09b6010b; one more than whatever it is when the commit lands) with an importer test that older archives still load; the candidate format (4) is unchanged (a letter is not part of the career block); the API (api/routers/documents.py) returns the record on an upload; search (core/search.py:390) finds a letter by its referee's name; CV print choice (#308) does not apply; the processing record (core/gdpr.py, drawn from plugins and connections) needs nothing, whilecontact_documentand the erasure do (above);tests/test_wiki_surface.pydoes not apply (no plugin-surface name);tests/test_page_coverage.pyandtests/security/test_isolation_sweep.pyfor any new page, and a foreign contact is refused as referee; strings in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349).From the list of improvements of 2026-10-02.
Landed on main with a green Forgejo CI run. Its entry in the CHANGELOG's Unreleased section ends in (#666).