@@ -19,10 +19,12 @@ it. Competency criteria (``CompetencyCriteria``, ``CompetencyCriteriaGroup``) ar
1919reference are deliberately non-evaluative, unversioned display metadata. Copying a criterion
2020therefore 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
2729The three copy mechanisms differ significantly in current maturity:
2830
@@ -47,16 +49,18 @@ Decision
4749Stable 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
6266plan-building logic, see :ref: `openedx-tagging-adr-0006 `) is sufficient for within-taxonomy
@@ -67,7 +71,7 @@ Copy semantics
6771~~~~~~~~~~~~~~
6872
6973Competency 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
7175by-value copy, but a by-value copy would leave the target's competency evaluation permanently
7276disconnected from the taxonomy it depends on, undermining the goal of this use case.
7377
@@ -77,9 +81,9 @@ Resolution on import
7781On import, whether the source and target are the same deployment or two different organizations'
7882instances, 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
8589A 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.
121125Alternatives 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+
124145Copy 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
168189rather 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
171194Manual merge of independently-created taxonomies
172195~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173196
174197Two 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
186210Changelog
187211---------
0 commit comments