Adding features¶
A walkthrough for the most common kinds of change.
Adding a new schedule item to houses or vehicles¶
- Open
apps/profiles/schedules/house.py(orvehicle.py). - Add a new
ScheduleItem(...)to theHOUSE_SCHEDULE(orVEHICLE_SCHEDULE) list. Required fields:key,label,section,expected_document_type,why_matters,how_to_get. Optional:conditioncallable,when_needed("now" / "at_sale" / "at_let" / "annual"). - If the item should only apply to certain owners (e.g. modern builds), define a
_xxxpredicate at the top of the file or reuse an existing one. - Add a test in
apps/profiles/test_facts.py(houses) orapps/profiles/test_vehicle_lookup.py(vehicles) covering both the applies-to and the doesn't-apply-to case.
The schedule item's expected_document_type matches against Document.document_type_id to decide whether the slot is filled. If the document type doesn't exist yet, no extra setup is needed — the matching is done by string equality.
Adding a new dashboard tile / area¶
The dashboard renders three kinds of tile:
- Named-item tiles (Houses, Vehicles) — each kind has its own first-class section with an "+ Add another" tile.
- Populated topic tiles — only render if the topic has at least one document.
- Empty topic links — collapsed pill links inside the accent-tint guidance card.
To add a new top-level kind (e.g. Pets):
- Add
PETS = "pets", "Pets"toapps/households/models.py:NamedItem.Kind. - Migrate.
- Add a kind dispatch in
apps/profiles/services.py:_schedule_for()and_profile_for(). - Add a
PetScheduleand (optionally)PetProfilemodel. - Update the dashboard view (
apps/households/views.py:dashboard()) to computepet_tiles. - Add a Pets section to
templates/dashboard.htmlmirroring the Houses / Vehicles sections. - Update
apps/documents/views.py:named_item_viewto include Pets in the schedule branch.
To add a new topic (e.g. BOOKS):
- Add to
apps/documents/models.py:Topic. - Add to
SEEDED_TOPICSif you want it to render on the dashboard. - Update the classifier prompt (
apps/classifier/services.py) so Claude knows about it. - Update the topic inference mapping in
apps/emailscan/topic_inference.py. - Migrate.
User-defined topics aren't supported today — the enum is hardcoded by design (classifier routing depends on a known set).
Adding a new ingestion route (e.g. email-in)¶
The "Get documents in" panel on the dashboard already has a slot for email-in marked Coming soon. The plumbing for the actual ingestion would be:
- Pick a provider — Postmark Inbound is simplest + cheapest.
- Allocate a per-household forwarding address (e.g.
<token>@inbound.lifefile.app). - Configure MX records on
inbound.lifefile.appto point at Postmark's servers. - Add a webhook endpoint to
apps/api/that Postmark POSTs to with the parsed email + attachments. - Reuse
apps/documents/services.py:create_document()(the same pathdocuments.views.upload()uses) to file each attachment. - Add abuse limiting per household (e.g. cap at 50 emails / hour).
- Surface the address in the "Forward by email" panel row + the account dropdown.
- Add tests covering: valid email with attachment, no attachments (skip), unknown token (404), rate limit exceeded (429).
This is a real slice — see status.md for the backlog entry.
Adding a new lookup-driven profile¶
Vehicles use a registration plate to populate VehicleProfile from DVLA + DVSA. To add similar for another kind:
- Pick the API. Free is best (DVLA, DVSA, OS Places). Paid is OK (~£0.05–£0.20/lookup).
- Add a new
XxxProfilemodel inapps/profiles/models.py. - Add a service module
apps/profiles/xxx_lookup.pywith amockandlivemode toggled by an env-var setting. - Wire
_profile_for()and_schedule_for()inapps/profiles/services.py. - Add a UI panel in
templates/documents/named_item.html(the per-named-item page). - Add tests covering plate normalisation, mock determinism, schedule conditioning, and the create flow.
Pattern's in apps/profiles/vehicle_lookup.py — copy the structure verbatim.
Adding a new bulk action on the topic page¶
The topic page (/documents/topics/<x>/) has a sticky bottom bulk toolbar (templates/documents/_partials/bulk_toolbar.html). To add a new action:
- Add a button to the toolbar with
name="action" value="your_action". - Add a branch to
apps/documents/views.py:bulk_action()calling a new service function. - Add a service function in
apps/documents/services.py:bulk_<your_action>(). Keep it household-scoped. - Tests in
apps/documents/test_bulk.py.
If the action also implies "the user reviewed these" — e.g. moving a doc to a property is itself an assertion that it belongs there — stamp Document.reviewed_at = timezone.now() in the service.