Skip to content

Adding features

A walkthrough for the most common kinds of change.

Adding a new schedule item to houses or vehicles

  1. Open apps/profiles/schedules/house.py (or vehicle.py).
  2. Add a new ScheduleItem(...) to the HOUSE_SCHEDULE (or VEHICLE_SCHEDULE) list. Required fields: key, label, section, expected_document_type, why_matters, how_to_get. Optional: condition callable, when_needed ("now" / "at_sale" / "at_let" / "annual").
  3. If the item should only apply to certain owners (e.g. modern builds), define a _xxx predicate at the top of the file or reuse an existing one.
  4. Add a test in apps/profiles/test_facts.py (houses) or apps/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):

  1. Add PETS = "pets", "Pets" to apps/households/models.py:NamedItem.Kind.
  2. Migrate.
  3. Add a kind dispatch in apps/profiles/services.py:_schedule_for() and _profile_for().
  4. Add a PetSchedule and (optionally) PetProfile model.
  5. Update the dashboard view (apps/households/views.py:dashboard()) to compute pet_tiles.
  6. Add a Pets section to templates/dashboard.html mirroring the Houses / Vehicles sections.
  7. Update apps/documents/views.py:named_item_view to include Pets in the schedule branch.

To add a new topic (e.g. BOOKS):

  1. Add to apps/documents/models.py:Topic.
  2. Add to SEEDED_TOPICS if you want it to render on the dashboard.
  3. Update the classifier prompt (apps/classifier/services.py) so Claude knows about it.
  4. Update the topic inference mapping in apps/emailscan/topic_inference.py.
  5. 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:

  1. Pick a provider — Postmark Inbound is simplest + cheapest.
  2. Allocate a per-household forwarding address (e.g. <token>@inbound.lifefile.app).
  3. Configure MX records on inbound.lifefile.app to point at Postmark's servers.
  4. Add a webhook endpoint to apps/api/ that Postmark POSTs to with the parsed email + attachments.
  5. Reuse apps/documents/services.py:create_document() (the same path documents.views.upload() uses) to file each attachment.
  6. Add abuse limiting per household (e.g. cap at 50 emails / hour).
  7. Surface the address in the "Forward by email" panel row + the account dropdown.
  8. 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:

  1. Pick the API. Free is best (DVLA, DVSA, OS Places). Paid is OK (~£0.05–£0.20/lookup).
  2. Add a new XxxProfile model in apps/profiles/models.py.
  3. Add a service module apps/profiles/xxx_lookup.py with a mock and live mode toggled by an env-var setting.
  4. Wire _profile_for() and _schedule_for() in apps/profiles/services.py.
  5. Add a UI panel in templates/documents/named_item.html (the per-named-item page).
  6. 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:

  1. Add a button to the toolbar with name="action" value="your_action".
  2. Add a branch to apps/documents/views.py:bulk_action() calling a new service function.
  3. Add a service function in apps/documents/services.py:bulk_<your_action>(). Keep it household-scoped.
  4. 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.