Skip to content

Annotations

Annotations reads a Drupal site's structure — content types, fields, taxonomies, user roles, anything that's an entity — and maps that to a structured annotation system.

This is the core package: entities, plugin system, scope UI, editing UI, and in-context help overlays. AI context/Tool API integration, documentation export, audit/coverage reporting, and third-party-entity integrations (Webform, Profile) live in the separate drupal/annotations_extras package — install it alongside this one for those features.

Annotations can be consumed in several ways

User onboarding / Editing:

  • contextual help overlays while creating/editing entities - help onboard new users with complex forms
  • same for when viewing content - help text on learning materials or new user orientation
  • annotation insertion into entity select screens - additional help text on e.g. /node/add to assist with selection

AI and documentation (requires drupal/annotations_extras):

  • scoped input for an AI agent - returns component-specific human-curated context via Tool API plugins for function-calling agents
  • export your annotations and ship them with your module/product! - exported as default content, only core module plus a consumer reqd. Ship once via a recipe, or keep already-installed sites in sync across releases via AnnotationSyncService (see DEVELOPING.md)
  • human- or machine-readable documentation export - provides drush commands to get scoped annotations on the CLI

Annotation entities support

  • multiple, fieldable annotation types
  • revisions (diff support provided)
  • content moderation
  • workflows (via supplied integration module)
  • translation (via language switcher)
  • dynamic permissions system for edit/consume annotations

Annotation type entities support

  • 'behaviors' driven by third party settings for interfacing with other modules. (see DEVELOPING.md)
  • an optional readonly lock, for types meant to hold only machine-managed content shipped by a module. Blocks create/edit/delete by anyone but administer annotations — see DEVELOPING.md

Annotation target entities

  • can be created on any entity target that exposes a plugin via the extensible system provided. (see DEVELOPING.md)
  • target plugins already provided: [generic], paragraphs, media, node, view, user, taxonomy, etc. drupal/annotations_extras provides interoperability with webform, profile.

Requirements

  • Drupal 11
  • PHP 8.3+

This core module suite has no hard contributed module dependencies. annotations_overlay and annotations_type_ui need nothing beyond core; annotations_ui optionally uses the contributed diff module for "Compare with previous" links on the revision history page — install it if you want that, otherwise those links simply don't appear.

For content moderation workflows, AI drafting/documentation/Tool API integration, audit/coverage reporting, or Webform/Profile support, see drupal/annotations_extras and its own module-by-module requirements.

Annotations is optimized slightly for the Gin theme, given that the expectation is this will be the core admin theme from Drupal 12 moving forwards.


Installation

Install annotations base module; enable submodules as needed.

Please note that there are several demonstration recipes available, with documentation, in the recipes folder of this repository.


Setup sequence

Annotation types must exist before annotations can be created. The annotation content entity uses type_id as its bundle key — no types means no annotations can be written, regardless of how well targets are configured. Dynamic permissions (edit {type} annotations, delete {type} annotations, consume {type} annotations) are also generated from types, so a cache rebuild is needed after new types are added.

  1. Create annotation types — use annotations_type_ui, apply the annotations_demo_types recipe, or import type config directly. Choose them deliberately: they define your audiences and permissions (see Annotation types).
  2. Configure annotation targets — opt entity types and bundles into scope via /admin/config/annotations/targets.
  3. Assign permissions — grant edit {type} and consume {type} permissions per role, then rebuild the cache.
  4. Create annotations — use annotations_ui or the Drush commands.

The module suite

Module Who it is for Status Purpose
annotations All Stable Core — entities, plugin system, scope UI. Always required.
annotations_type_ui Agency / setup only Stable Browser-based CRUD for annotation types. Site-building tool only — use during initial setup; not for general production use.
annotations_ui Agency + editors Stable Annotation editing UI with revision history and moderation controls. The primary authoring interface.
annotations_overlay Editors / end users Largely stable In-context help overlays: field-level and bundle-level "?" triggers on entity edit forms, opt-in view-page overlays (via Manage Display), bundle chooser page descriptions, and paragraph subform support.

annotations_context, annotations_explorer, annotations_export, annotations_tool, annotations_docs, annotations_workflows, annotations_webform, annotations_profile, and annotations_audit all moved to the separate drupal/annotations_extras package — see that project's README for its module table.


Core concepts

Annotation types

Define the category of annotation — editorial guidance, tech notes, compliance rules, etc. Add, rename, or remove types via the annotations_type_ui browser interface.

Choosing annotation types is a design decision, not a naming exercise, because the type sets who can consume an annotation, where it appears, what voice it's written in, and who keeps it accurate. Access is granted per type (consume {type} annotations), so a type is effectively an audience boundary. Mix audiences into one type and you either expose notes to people who shouldn't see them or hide them from people who need them. Sometimes user-facing and editorial notes can share a type, and sometimes they can't, so the split should follow your real readers rather than a template. The demo types (editorial, technical, business rules) are arbitrary for exactly this reason. An AI-only "Agent guidance" type shows the point. It might hold terse instructions such as "Treat the Eligibility field as authoritative over body text; never quote fees; link to the tariff page instead." That wording would confuse an editor or learner, so opt the type into AI context (in_ai_context) and grant consume to the agent's role only. It then reaches agents through the Tool API without cluttering what editors and learners see. Merge it into editorial notes and you have to choose between polluting the human UI and starving the agent. Too few types blurs audiences, and too many leaves authors guessing where a note belongs. Aim for one type per distinct audience and maintenance owner.

Annotation targets

One annotation target per annotatable unit — one per content type, taxonomy vocabulary, user role, etc. Defines which entity types and which fields are in scope for annotation.

Annotation content

Annotation content is stored per target and per field. Each annotation belongs to a type. Annotations are created and edited via annotations_ui.

Overview annotation

Every annotation target has one implicit bundle-level slot: the overview. This is an annotation about the target as a whole - what the content type, role, or entity is for - rather than any specific field. It surfaces as the first row in the add-new table in annotations_ui, as a bundle-level trigger at the top of entity forms and view pages in annotations_overlay, and as the opening description in context output from annotations_context.

Role-based type layering

Because every annotation type generates a consume {type} annotations permission, you can author once and serve role-appropriate context automatically. Assign consume permissions to different roles to control which types surface in overlays, context payloads, and AI prompts for each audience.

Example: With three types — editorial, technical, and rules:

Role Permissions granted Sees in overlays / context
Editor consume editorial annotations Tone and style guidance
Developer consume technical annotations Field config and schema notes
Authenticated user consume rules annotations Mandatory compliance notes
Administrator (all permissions implicitly) Everything

Annotations are authored against the same targets and fields regardless of type - the role determines which types are visible, not where the annotation lives. Types stack freely: grant multiple consume permissions to a role to combine audiences. The annotations_demo_types recipe ships editorial, technical, and rules types as a starting point.


Scope management

Entity types that you wish to target can be added and removed via Manage target types at /admin/config/annotations/targets.

Individual fields can be selected as targets via Manage targets at /admin/config/annotations/targets.

Each Drupal entity type appears as an accordion section. Select (check) a row to bring a bundle into scope — this creates an annotation_target config entity with all available fields pre-included. Use Configure to adjust which fields are included.


Permissions

The permission model maps to a non-standard CRUD because annotations are editorial content, not access-controlled content:

  • edit = create + update. There is no separate "create" permission — if you can edit a type you can also create it. "View" in the Drupal sense is moot: anyone who can reach the annotation UI can read annotation values; the access question is whether you can write them.
  • delete = delete. Kept separate from edit so destructive operations are restricted independently.
  • consume = "read" for end-users. Controls which annotation types appear in context output, overlays, and AI payloads for a given role. Entirely separate from the editorial write layer.
Permission Defined in Notes
administer annotation types annotations CRUD access for annotation type config entities; the browser UI ships in annotations_type_ui. restrict access: true.
administer annotations annotations Full admin. restrict access: true.
administer annotation targets annotations Manage opted-in targets and field scope. restrict access: true.
consume {type} annotations annotations Per annotation type; controls which types appear in context output and overlays for a given role.
edit {type} annotations annotations_ui Per annotation type; write and create access. Generated dynamically.
delete {type} annotations annotations_ui Per annotation type; delete access. Generated dynamically.
edit any annotation annotations_ui Supersedes all per-type edit permissions.
delete any annotation annotations_ui Supersedes all per-type delete permissions. Gates bulk-delete routes.
access annotation collection annotations_ui Read access to /admin/content/annotations and the add-type picker.
view annotation revisions annotations_ui View revision history and individual revision pages. Does not grant revert or delete.

Dynamic permissions (edit {type} annotations, delete {type} annotations, consume {type} annotations) require a cache rebuild after new annotation types are created.

A type marked readonly (see above) overrides edit/delete {type} annotations for everyone except administer annotations — the per-type permissions still exist and still gate other types normally.

Each submodule documents its own permissions — see the relevant module's README.


Drush commands

Commands are provided by several modules in the suite. To list all available annotations Drush commands:

drush list --filter=annotations
Command Alias Purpose
annotations:targets ann:targets List annotation target config entities, with optional entity type filter
annotations:types ann:types List annotation type config entities
annotations:show ann:show Show stored annotation content with optional target/type/field filters
annotations:stats ann:stats Annotation coverage counts per target, broken down by type

All commands support --format=json and --format=yaml. Run drush help <command> for full option details.

Additional commands (annotations:scan, annotations:export, etc.) are documented in the README of the submodule that provides them.