Skip to content

Commit 71830a8

Browse files
docs: use export_id for cross-instance taxonomy identity
Reuse the existing Taxonomy.export_id field for cross-instance identity instead of adding a new uuid field, per review feedback.
1 parent 10704bd commit 71830a8

1 file changed

Lines changed: 52 additions & 28 deletions

File tree

docs/openedx_tagging/decisions/0011-cross-instance-taxonomy-identity.rst

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ it. Competency criteria (``CompetencyCriteria``, ``CompetencyCriteriaGroup``) ar
1919
reference are deliberately non-evaluative, unversioned display metadata. Copying a criterion
2020
therefore also means resolving the taxonomy and tags it points to on the target.
2121

22-
No stable, cross-instance identity exists for a ``Taxonomy`` or ``Tag`` today. ``Taxonomy.export_id``
23-
and ``Tag.external_id`` are both editable, instance-scoped identifiers designed for import-file
24-
bookkeeping (see :ref:`openedx-tagging-adr-0006`), not for answering "does the target already have
25-
this taxonomy."
22+
No stable, cross-instance identity exists for a ``Tag`` today: ``Tag.external_id`` is an editable,
23+
instance-scoped identifier designed for import-file bookkeeping (see :ref:`openedx-tagging-adr-0006`).
24+
``Taxonomy.export_id`` is different: per `modular-learning#183
25+
<https://github.com/openedx/modular-learning/issues/183>`_, it was always intended to answer "does
26+
the target already have this taxonomy," which is exactly what this decision needs; it just hasn't
27+
been documented as such, or exercised for identity-matching purposes, until now.
2628

2729
The three copy mechanisms differ significantly in current maturity:
2830

@@ -47,16 +49,18 @@ Decision
4749
Stable identity
4850
~~~~~~~~~~~~~~~~
4951

50-
Add an immutable ``uuid`` field to ``Taxonomy``, generated at creation and preserved through
51-
export/import. ``export_id`` cannot serve this purpose on its own: it is free text, chosen and
52-
editable by whoever administers a taxonomy, so nothing guarantees that the same ``export_id`` on
53-
two different instances refers to the same taxonomy, or that two different taxonomies on two
54-
instances never happen to share one. A ``uuid`` is generated once, never touched by a person, and
55-
so cannot collide or drift the way a human-chosen identifier can. ``export_id`` keeps its existing
56-
role as the human-meaningful identifier; ``uuid`` adds the machine identity it was never designed
57-
to provide. This follows the existing convention in this codebase of using ``uuid`` for a stable
58-
external reference (see ``PublishableEntity.uuid``, :ref:`openedx-content-adr-0003`), applied here
59-
to a model that currently lacks it.
52+
Use the existing ``Taxonomy.export_id`` field as the cross-instance identity, rather than adding a
53+
new field. ``export_id`` is already required, unique, and format-validated
54+
(``^[\w\-.]+$``) at the model level, and the REST API already accepts a caller-supplied value on
55+
taxonomy creation. Two institutions that each set up the same third-party taxonomy (for example,
56+
Lightcast Open Skills) can establish that they're the same taxonomy simply by using the same
57+
``export_id`` (for example, a reverse-DNS-style value like ``io.lightcast.open-skills``), something
58+
an immutable, randomly-generated identifier could never let them do, since two independent imports
59+
would always get two different random values with no way to reconcile them afterward.
60+
61+
``export_id``'s existing mutability (it can be edited after creation) is also a feature here rather
62+
than a gap: it is how the deferred manual-merge case below would actually be performed, by editing
63+
one instance's ``export_id`` to match the other's.
6064

6165
``Tag`` does not need a new identifier: ``Tag.external_id`` (already used by the tag import/export
6266
plan-building logic, see :ref:`openedx-tagging-adr-0006`) is sufficient for within-taxonomy
@@ -67,7 +71,7 @@ Copy semantics
6771
~~~~~~~~~~~~~~
6872

6973
Competency criteria are copied **by reference**: the target's criteria are bound to a taxonomy
70-
sharing the source's ``uuid``, not to an independent duplicate. This is a larger commitment than a
74+
sharing the source's ``export_id``, not to an independent duplicate. This is a larger commitment than a
7175
by-value copy, but a by-value copy would leave the target's competency evaluation permanently
7276
disconnected from the taxonomy it depends on, undermining the goal of this use case.
7377

@@ -77,9 +81,9 @@ Resolution on import
7781
On import, whether the source and target are the same deployment or two different organizations'
7882
instances, the behavior is uniform:
7983

80-
- If no taxonomy with a matching ``uuid`` exists on the target, auto-create one, seeded from the
84+
- If no taxonomy with a matching ``export_id`` exists on the target, auto-create one, seeded from the
8185
tags that traveled with the export.
82-
- If a taxonomy with a matching ``uuid`` already exists, reconcile it (see below) rather than
86+
- If a taxonomy with a matching ``export_id`` already exists, reconcile it (see below) rather than
8387
creating a duplicate.
8488

8589
A single uniform rule was chosen over branching by deployment relationship because the
@@ -121,6 +125,23 @@ same import: whoever is watching sees the existing failure message.
121125
Alternatives Considered
122126
------------------------
123127

128+
Add a new immutable ``uuid`` field
129+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130+
131+
Add a new, randomly-generated ``uuid`` field on ``Taxonomy``, instead of reusing ``export_id``.
132+
133+
**Pros:** guaranteed collision-free by construction; no reliance on a human choosing a consistent
134+
value.
135+
136+
**Cons:** a randomly-generated identifier can never let two independently-created taxonomies on
137+
two different instances establish that they're the same one, exactly the "manual merge" case
138+
deferred below, since two independent imports always produce two different random values with no
139+
way to reconcile them. ``export_id`` already exists for this purpose (`modular-learning#183
140+
<https://github.com/openedx/modular-learning/issues/183>`_), is already required, unique, and
141+
format-validated, and its editability is what makes the deferred manual-merge case possible at
142+
all. Not chosen, since it would have duplicated ``export_id``'s role while being strictly less
143+
capable for the independently-created-taxonomies case.
144+
124145
Copy by value (independent duplicate)
125146
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
126147

@@ -166,22 +187,25 @@ Real versioning for taxonomies and tags
166187
``Taxonomy``/``Tag`` could gain version history (e.g. via ``django-simple-history``, matching
167188
``CompetencyCriteria``), which would let drift between source and target be tracked precisely
168189
rather than detected only as additive or not. Likely needed eventually, but not required for this
169-
use case, since the additions-only reconciliation policy already satisfies it without history.
190+
use case, since the additions-only reconciliation policy already satisfies it without history. See
191+
`openedx-core#455 <https://github.com/openedx/openedx-core/issues/455>`_ for the tentative plan for
192+
taxonomy versioning.
170193

171194
Manual merge of independently-created taxonomies
172195
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173196

174197
Two institutions may independently author what they each consider the same taxonomy on their own
175-
instances, without ever having copied content between each other. Since a ``uuid`` establishes
176-
identity only once two taxonomies have actually shared a copy operation, importing between them
177-
today would create a second, unrelated taxonomy on the receiving side, not recognize them as the
178-
same one. Automatic matching cannot safely resolve this: falling back to matching by name or
179-
content similarity would reintroduce the false-positive risk ``uuid`` was introduced to avoid, two
180-
instances with genuinely different taxonomies that happen to look similar would be silently
181-
merged. The eventual resolution is a deliberate, human-initiated action: an administrator manually
182-
reassigns one instance's taxonomy to adopt the other's ``uuid``, retroactively establishing shared
183-
identity going forward. Not designed here, since it is a distinct, rare operation, orthogonal to
184-
the copy-time behavior this decision covers.
198+
instances, without ever having copied content between each other. If they haven't both deliberately
199+
set the same ``export_id``, importing between them today would create a second, unrelated taxonomy
200+
on the receiving side, not recognize them as the same one. Automatic matching cannot safely resolve
201+
this: falling back to matching by name or content similarity would reintroduce the false-positive
202+
risk a deliberate, unique identifier was introduced to avoid, two instances with genuinely different
203+
taxonomies that happen to look similar would be silently merged. The eventual resolution is a
204+
deliberate, human-initiated action: an administrator manually edits one instance's taxonomy to
205+
adopt the other's ``export_id``, retroactively establishing shared identity going forward. Not
206+
designed here, since it is a distinct, rare operation, orthogonal to the copy-time behavior this
207+
decision covers. Today this requires a direct API call: Studio's Taxonomy Editing UI has no field
208+
to set or edit ``export_id``.
185209

186210
Changelog
187211
---------

0 commit comments

Comments
 (0)