Summary
Clean-ups around the title descriptive field on Device (and the shared DescriptiveMixin), plus surfacing Device metadata in the demo UI. Independent of feature work. Deliver on its own branch off main.
Background
title is not Device-specific. It comes from DescriptiveMixin (app/models/mixins.py), which adds the DCAT / Dublin-Core set title / description / publisher / creator to Device, Dataset, and Collection. title maps to dct:title, a human-readable label distinct from the slug name.
Convention, settled 2026-08-11. title is the short display label: mast gives MAST, mastu gives MAST Upgrade. The expanded name and any prose about the machine belong in description. This reverses what this issue originally proposed (expanding the acronym into title). The short form wins because useDeviceLabel feeds title straight into page headings and breadcrumbs, where a full expansion is unusable.
Already delivered since this issue was filed:
demo/seed_metadata.py sets title on both devices (MAST, MAST Upgrade), so a device's dct:title no longer falls back to "Device: mast".
docs/data-model/device.md documents the convention correctly at the title row, and its register-a-device examples set title.
Tasks
1. Give the demo devices real descriptions
description currently only spells out the acronym ("Mega Ampere Spherical Tokamak (MAST)"), which says nothing the title does not, and that redundancy becomes visible once both render on the landing page (task 4). Replace with a real sentence per machine in demo/seed_metadata.py (register_devices_and_shots). Confirm domain facts with the maintainer rather than inventing them.
2. Document the convention in DescriptiveMixin
docs/data-model/device.md already covers this, so only the code side is outstanding. The DescriptiveMixin docstring (app/models/mixins.py) is a bare one-liner that describes none of its four fields. Write up title as the short display label with description carrying the prose, so the convention is visible where the fields are defined.
3. Stop DeviceUpdate dropping creator
Still reproduces on main. DeviceUpdate (app/models/device.py, ~lines 60 to 84) hand-lists its fields and omits creator (which DescriptiveMixin has), so a PATCH cannot update a device's creator.
- Fix: derive the descriptive fields from the mixin.
class DeviceUpdate(DescriptiveMixin, SQLModel): ... pulls in title / description / publisher / creator, all already X | None = None, exactly the optionality a PATCH DTO wants. This removes the three hand-copied lines and restores creator.
- Keep the non-descriptive fields (
name / type / began_operations / status / access_level / required_scopes / allowed_idps) as explicit optional declarations. DeviceBase makes several of them required, so it cannot be reused wholesale for a PATCH DTO.
- Check
DatasetUpdate and CollectionUpdate for the identical drift (they use the same mixin) and fix consistently.
- Align the approach with ADR-0015 (Pydantic Model Hierarchy); update the ADR if the intended pattern changes.
4. Show Device metadata on the device landing page
demo/ui/src/app/devices/[device]/page.tsx renders the device label as a heading and then goes straight to the tabs. It shows no device metadata at all, while every other entity has a metadata panel.
Mirror the dataset landing page (demo/ui/src/app/devices/[device]/shots/[shot]/datasets/[id]/page.tsx): the "Created At / Media Type / License / Access Level" block, plus the publisher chip next to the heading. Surface the fields DeviceRead already returns and nothing currently displays:
description (the prose from task 1)
type, status, began_operations
publisher
effective_access_level, falling back to access_level
No API change needed. The fields are already on the read model, so this is UI only.
Acceptance criteria
Notes
Doc paths. Resolved: the docs restructure (docs/examples-in-concepts) has merged, so the Device page is docs/data-model/device.md and it carries the tabbed register-a-device examples.
Scope. Standalone change. Branch off main.
Migrated from the internal tracker, where it was #24, opened 2026-07-23.
Summary
Clean-ups around the
titledescriptive field onDevice(and the sharedDescriptiveMixin), plus surfacing Device metadata in the demo UI. Independent of feature work. Deliver on its own branch offmain.Background
titleis not Device-specific. It comes fromDescriptiveMixin(app/models/mixins.py), which adds the DCAT / Dublin-Core settitle / description / publisher / creatortoDevice,Dataset, andCollection.titlemaps todct:title, a human-readable label distinct from the slugname.Convention, settled 2026-08-11.
titleis the short display label:mastgivesMAST,mastugivesMAST Upgrade. The expanded name and any prose about the machine belong indescription. This reverses what this issue originally proposed (expanding the acronym intotitle). The short form wins becauseuseDeviceLabelfeedstitlestraight into page headings and breadcrumbs, where a full expansion is unusable.Already delivered since this issue was filed:
demo/seed_metadata.pysetstitleon both devices (MAST,MAST Upgrade), so a device'sdct:titleno longer falls back to"Device: mast".docs/data-model/device.mddocuments the convention correctly at thetitlerow, and its register-a-device examples settitle.Tasks
1. Give the demo devices real descriptions
descriptioncurrently only spells out the acronym ("Mega Ampere Spherical Tokamak (MAST)"), which says nothing the title does not, and that redundancy becomes visible once both render on the landing page (task 4). Replace with a real sentence per machine indemo/seed_metadata.py(register_devices_and_shots). Confirm domain facts with the maintainer rather than inventing them.2. Document the convention in
DescriptiveMixindocs/data-model/device.mdalready covers this, so only the code side is outstanding. TheDescriptiveMixindocstring (app/models/mixins.py) is a bare one-liner that describes none of its four fields. Write uptitleas the short display label withdescriptioncarrying the prose, so the convention is visible where the fields are defined.3. Stop
DeviceUpdatedroppingcreatorStill reproduces on
main.DeviceUpdate(app/models/device.py, ~lines 60 to 84) hand-lists its fields and omitscreator(whichDescriptiveMixinhas), so aPATCHcannot update a device'screator.class DeviceUpdate(DescriptiveMixin, SQLModel): ...pulls intitle / description / publisher / creator, all alreadyX | None = None, exactly the optionality a PATCH DTO wants. This removes the three hand-copied lines and restorescreator.name / type / began_operations / status / access_level / required_scopes / allowed_idps) as explicit optional declarations.DeviceBasemakes several of them required, so it cannot be reused wholesale for a PATCH DTO.DatasetUpdateandCollectionUpdatefor the identical drift (they use the same mixin) and fix consistently.4. Show Device metadata on the device landing page
demo/ui/src/app/devices/[device]/page.tsxrenders the device label as a heading and then goes straight to the tabs. It shows no device metadata at all, while every other entity has a metadata panel.Mirror the dataset landing page (
demo/ui/src/app/devices/[device]/shots/[shot]/datasets/[id]/page.tsx): the "Created At / Media Type / License / Access Level" block, plus the publisher chip next to the heading. Surface the fieldsDeviceReadalready returns and nothing currently displays:description(the prose from task 1)type,status,began_operationspublishereffective_access_level, falling back toaccess_levelNo API change needed. The fields are already on the read model, so this is UI only.
Acceptance criteria
description;titlestays the short display label.dct:titlereflects the set title, with no"Device: ..."fallback.DescriptiveMixindocstring describes thetitle/descriptionsplit.PATCH /api/v1/devices/{name}can setcreator;DeviceUpdatederives its descriptive fields fromDescriptiveMixin.DatasetUpdate/CollectionUpdateverified, and fixed if they share the bug.creatorPATCH;uv run --all-extras pytestgreen;prek run --all-filesclean.Notes
Doc paths. Resolved: the docs restructure (
docs/examples-in-concepts) has merged, so the Device page isdocs/data-model/device.mdand it carries the tabbed register-a-device examples.Scope. Standalone change. Branch off
main.Migrated from the internal tracker, where it was #24, opened 2026-07-23.