Bind a listing to what it came from and what arrives about it: a capture, an email, a document, a message #270
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
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#270
Loading…
Add table
Add a link
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?
A listing can be bound to exactly one other thing today: the capture it came from, through
Capture.posting. Beyond that it carries aurl, asourcestring ("Found via") andnothing else. Everything that arrives about a job before you apply for it has nowhere to
go.
None of those can be attached to the listing. They are kept somewhere else or lost, and the
listing — the thing the person is actually deciding about — stays a title, a company and a
link.
The asymmetry this is really about
An application has a timeline. A listing has nothing.
ApplicationEventis append-only, scoped through its parent, and already has the rightvocabulary:
EMAIL_SENT,EMAIL_RECEIVED,CALL,NOTE,INTERVIEW,OTHER. Everythingafter you apply is recorded, and
record_eventis the one way in.Everything before you apply is not recorded at all. That is the gap, and it is why this is
not a request for an attachments box: the listing stage has no history, and half the
decisions a job seeker makes happen there.
The question that has to be answered before any code
Is this an event, or an attachment?
CLAUDE.mdstates the principle: the event log is the truth. That argues for extending thelog downward rather than adding a parallel table — two histories for one record drift, and
#217 is what happens when a record's past has more than one home.
But
ApplicationEventwas deliberately built the way it is:It scopes through
application__owner. Giving an event two possible parents means scopingthrough either, which is exactly the kind of thing that is cheap to write and expensive to
get wrong — and
tests/security/would need to prove it for both paths.Three shapes, and one of them should be chosen here rather than discovered:
whichever parent it has. Fewest concepts, most careful scoping.
ListingEvent. Simpler scoping, simpler cascade, two models that will beasked to converge later — particularly when a listing becomes an application.
kind of thing as "status changed", but reintroduces the second history the principle warns
against.
Whichever wins has to answer the same question: when a listing becomes an application, what
happens to what was bound to it? Carried over, referenced, or left behind — that decision
determines the model, not the other way round.
Precedents in the tree, so a fourth shape is not invented
core.PhoneNumber,core.PostalAddressandcore.WebLinkattach to holders throughGenericRelation, with the cascade that implies.RenderedDocument.sourceis aGenericForeignKeywith no reverseGenericRelation,and the docstring says why: a reverse relation would give a cascade, and "the whole point
of this model is that a PDF an employer received survives somebody tidying up their
drafts".
That second one is the more interesting precedent here. A forwarded job description should
probably survive the listing being discarded, and a note about it probably should not.
What else it touches
client, a future messaging source — all of them need a way to say "this belongs to that".
The plugin is handed a record it may already see and never chooses the owner. This is
squarely #229's surface, and should be designed with it rather than after it.
logos/{owner_id}/does, is served byserve_private_file, and is removed from disk whenthe record goes — #217 is the issue that establishes this is not automatic.
what #218 was about: text a stranger wrote reaching CSV, ICS, the MCP plugin and the page.
Anything bound here inherits that whole problem, and #218's fixes have to cover it.
jobs/known.pyalready asks whether an advert has beenseen before, and deliberately tells rather than refuses (#178). A second capture of the
same job is a natural thing to bind rather than discard — the same advert on two boards
is evidence about the job, not noise.
three) are the two neighbours. #239 in particular is asking for structure on the same
record; these should be read together before either is built.
Scope
What this is not: a file manager, a mailbox, or a place to keep documents that belong in
documents/. A binding says this artefact relates to that listing and points at somethingalready stored where it belongs.
A first consumer for whatever this issue settles: #271 proposes a Thunderbird MailExtension that binds the message you are reading to a listing, with no mailbox credentials on the server.
It raises one thing that belongs here rather than there: none of the existing API scopes fits. Binding a mail is not capturing, and the write scope covers applications, listings, notes, reminders and letters — far more than a mail client should hold. Whatever binding shape this issue chooses should come with a scope narrow enough to hand to a client that only attaches things.