Skip to content

Notes and tags

Notes, tags, and external links attach to almost every entity in Formation through a single shared mechanism. A scheme can carry notes; so can an address, an investment event, an occupier event, a portfolio, a company. The same goes for tags and for external links. From a user perspective they behave the same wherever they appear — what’s allowed and what’s gated is the same across entity types.

A free-form text entry attached to a record. The record could be a scheme, an investment event, an address, or any other entity that supports notes. There’s no fixed format — notes are typed in the Notes panel at the bottom of an entity’s edit view and saved alongside the record.

Any user with edit access to the parent record can add, edit, or remove notes on it. There’s no separate role for note management.

Notes are managed as a polymorphic collection: changes to the parent record (a scheme save, say) don’t disturb the existing notes unless you explicitly change them in the same save. The notes panel reads the current notes when the page opens, lets you add / edit / delete in place, and writes back the modified set on save.

Notes show up:

  • In the entity’s detail view (read-only list).
  • In the entity’s edit panel (where they’re editable).
  • They are not searchable from the global search bar — the @note operator scopes a search to notes, but the notes themselves aren’t indexed for free-text search across entity types.

A reusable label that can be attached to any entity. Tags are managed centrally in the Tag Manager (Settings → Tags). Once a tag exists, it can be applied to as many records as needed.

  • Adding or removing a tag from a record: any user with edit access to the record.
  • Creating, editing, renaming, or deleting tags themselves: Admin only. The Tag Manager UI is rendered read-only for non-Admin users — they can see the list but not change it.

See Permissions and roles for the Admin role.

  • A record can carry any number of tags.
  • A tag can be applied to any number of records.
  • Removing a tag from a record doesn’t delete the tag — it remains in the directory for use elsewhere.
  • Deleting a tag from the directory (Admin) removes it from every record it was applied to.

The global search bar supports #tag syntax: typing #confidential filters list results to records carrying the confidential tag. The tag name in the search is matched case-insensitively. See Search matching for the full operator list.

A URL with an optional title, recorded on an entity to point at related material elsewhere (a planning portal link, a Companies House page, a marketing brochure). The URL is validated to be a well-formed URL on save; the title is free text.

Any user with edit access to the parent record can add, edit, or remove external links. There’s no separate role for link management.

External links are a polymorphic collection just like notes — they’re managed in their own panel and don’t get disturbed by edits elsewhere on the parent record.

How polymorphic collections work behind the scenes

Section titled “How polymorphic collections work behind the scenes”

Notes, tags, and external links share a common machinery: each lives in its own table with a polymorphic foreign key that names the parent entity type and ID. The model layer loads them separately from the parent record’s main query (so they don’t blow up the JOIN), and the write controllers manage their state explicitly in each Create / Patch / Replace / Delete handler.

You’ll never see this from the UI — the load and save happen transparently — but it explains why notes and tags survive an unrelated edit cleanly, and why removing a tag from a record doesn’t touch the tag itself.

  • Permissions and roles — Admin gating for tag management.
  • Search matching — the #tag and @note search operators.
  • Every entity page references notes/tags in passing; the rules sit here.
  • Frontend components: src/services/web/src/lib/components/forms/Notes.svelte, Tags.svelte, Attachments.svelte
  • Tag manager: src/services/web/src/lib/components/system/TagManager.svelte
  • Tags controller (backend): src/services/api/app/app.api/Controllers/TagsController.cs
  • Polymorphic collection load helper: LoadPolymorphicCollectionsAsync in src/services/api/app/app.api/Data/
  • Per-entity wiring: each Create*CommandHandler.cs calls the polymorphic loader after the main save (e.g. CreateSchemeCommandHandler.cs, CreateInvestmentEventCommandHandler.cs)