One installation = one client: a single set of definitions on the Tags page that applies to the entire database. A workspace does not have its own separate tag list. Do not touch Custom Strings in Configuration — those are labels for standard columns.
There is no separate Component entity: component fields already live on Study. TagScope.Component is only a UI layout concept (left column of the editor); the values are stored in StudyTag.
TagScope — enum Component, Study, Series.TagDefinition — tag definition.
Title — UI label (max 64). Not a key and not unique.Tag — uint, shown in the UI as 8 hex digits.Vr — DICOM VR (2 characters). In the UI it’s a Select: AE, AS, CS, DA, DS, DT, IS, LO, LT, PN, SH, ST, TM, UI, UT.Scope — TagScope (Component / Study / Series): only controls where to show the field (left / right / SeriesEdit). In the DICOM file, there is still only one element with this number; Scope does not create a second copy in the dataset.StudyTag — value on a study (both Scope = Component and Scope = Study):
StudyId + TagDefinitionId (definitions with Scope Component or Study), unique pair.Value — nvarchar(64), never empty. Canonical form by VR: IS / DS / DA (yyyyMMdd) / TM / DT fit with margin. There is no empty value: no StudyTag/SeriesTag row means empty. Empty field in the editor, missing tag in DICOM on import, or key not provided in PUT Tags → remove the row.SeriesTag — value on a series:
SeriesId + TagDefinitionId (only Scope = Series), unique pair.Value — same rules.Import. Only when creating the entity — like standard fields.
Reading files. Study update overlays all Study.Tags (both scopes); series update overlays Series.Tags (explicit VR, incl. private tags). Download / Q/R / ReadFile will pick up edits.
Editor. Save into StudyTag / SeriesTag. Empty text, cleared date/number → delete the row; do not write "". Files on disk are not rewritten.
Do not extend Worklist / Order.
Existing DTOs get one new field Tags: a dictionary tag number → value, as additional fields next to standard ones.
Key is the DICOM/DICONDE number (uint; in JSON object keys — 8 hex digits, like in the UI). Value is not a string, but typed by VR:
IS — integerDS — decimalDA / DT — DateTimeTM — TimeSpanstring