Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5e6ccf5
feat: add endpoint to delete or archive a CompetencyCriterion (#674)
ufedaseyeuconsultant Oct 6, 2026
226f958
fix: decide hard-delete or archive inside the transaction (#674)
ufedaseyeuconsultant Oct 16, 2026
fb114a6
fix: archive a refused delete only when the criterion has a status (#…
ufedaseyeuconsultant Oct 22, 2026
777f175
refactor: check permission inside delete_or_archive_competency_criter…
ufedaseyeuconsultant Oct 26, 2026
237aae6
fix: lock tag, ancestors, and criterion instead of retrying (#674)
ufedaseyeuconsultant Oct 26, 2026
dd951d5
fix: write only the archived flag, record the user, stop at archived …
ufedaseyeuconsultant Oct 26, 2026
3ee7d88
fix: untag only the object tag the tagging API reports (#674)
ufedaseyeuconsultant Oct 26, 2026
1b668cb
docs: correct the delete view and cascade docstrings and tidy the tes…
ufedaseyeuconsultant Oct 26, 2026
6489d3d
fix: map validation errors in the delete view and read the lineage un…
ufedaseyeuconsultant Oct 26, 2026
86ccf36
fix: remove only the orphaned object tag when deleting a criterion (#…
ufedaseyeuconsultant Oct 27, 2026
5f6ba29
fix: lock only the tag when deleting a criterion (#674)
ufedaseyeuconsultant Oct 29, 2026
b1e27af
fix: delete an emptied archived group on a criterion delete (#674)
ufedaseyeuconsultant Oct 29, 2026
0a717a2
fix: serialize criterion writers on the competency's Tag row (#674)
ufedaseyeuconsultant Nov 4, 2026
8d66a52
test: cover criterion delete races over two MySQL connections (#674)
ufedaseyeuconsultant Nov 4, 2026
a44fb5b
fix: record the acting user on deletion history rows (#674)
ufedaseyeuconsultant Nov 4, 2026
d0a8f77
refactor: drop the cascade's event parameter, fix lock comments (#674)
ufedaseyeuconsultant Nov 4, 2026
cfa1710
test: assert real behavior instead of stubbed tagging reads (#674)
ufedaseyeuconsultant Nov 4, 2026
5c767e9
docs: record #674's deviations in ADRs 0002, 0003 and 0004 (#674)
ufedaseyeuconsultant Nov 4, 2026
255f891
docs: tighten the new lock docstrings and comments (#674)
ufedaseyeuconsultant Nov 4, 2026
c620f1d
fix: lock in create call, map deleted tag in group view
ufedaseyeuconsultant Nov 4, 2026
3e9a232
fix: check permission before locking in group update
ufedaseyeuconsultant Nov 4, 2026
be29b8f
refactor: set history users through one helper
ufedaseyeuconsultant Nov 4, 2026
2dedf27
docs: tighten docstrings, comments and ADR wording for criterion delete
ufedaseyeuconsultant Nov 4, 2026
3bb7e6d
test: pin lock points and tighten assertions for criterion removal
ufedaseyeuconsultant Nov 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Decision

Boundaries and intended behavior:

- Empty groups: Persisted criteria definitions should not contain empty groups. Authoring flows may temporarily create empty groups while editing, but backend validation must reject them.
- Empty groups: Persisted criteria definitions should not contain empty groups. Authoring flows may temporarily create empty groups while editing. The criterion delete path enforces this rule for the groups it empties: such a group is deleted, or archived when learner progress references it or when only archived children remain, in the same transaction, so that path persists no empty group. Writers outside this path are not covered by it.
- Mixed tree depths: Backend supports deeply nested groups. Current frontend authoring constraint is a maximum depth of 3 layers total, using zero-indexed depth (``0=root``, ``1=course-scope group``, ``2=leaf criteria/group``).
- Retrieval scope: A read always includes the root group, plus any selected subset of course-scoped groups, each returned as a complete subtree, never truncated.
- Practical size and growth: Total rows in ``CompetencyCriteriaGroup`` are expected to grow over time as course runs are added; this ADR sets no global DB row cap. No max total node-count cap is required per root group. Each course-scoped subtree is expected to stay small (realistically <=500 nodes).
Expand Down Expand Up @@ -311,7 +311,7 @@ Decision

- If no related row exists in ``StudentCompetencyCriteriaStatus`` (the leaf-level learner status table, Decision 6) for a competency definition, delete behaves as it does today: a hard delete that cascades through competency metadata tables.
- Once a related row exists in ``StudentCompetencyCriteriaStatus``, deletion of the associated competency definition row still succeeds, but as an archive (soft delete) instead of a hard delete: the row is hidden from authoring and new associations but remains queryable, so existing learner status rows stay resolvable. This archive-vs-hard-delete rule applies to ``oel_tagging_tag``, ``oel_tagging_taxonomy``, ``CompetencyTaxonomy``, ``oel_tagging_objecttag``, ``CompetencyCriteriaGroup``, and ``CompetencyCriteria``; see :ref:`openedx-learning-adr-0003` Decision 3 for ``oel_tagging_objecttag``'s own archive rule and traceability exception.
- ``StudentCompetencyCriteriaStatus`` is what determines whether a record is protected. ``StudentCompetencyCriteriaGroupStatus`` and ``StudentCompetencyStatus`` are roll-up tables derived from it (Decision 6) and are not independently checked for this purpose: :ref:`openedx-learning-adr-0004` writes the leaf table synchronously with the grade but rolls the two roll-up tables up later via an asynchronous task, which can lag behind the leaf or, per that ADR's Decision 5, need manual recovery. Checking only the roll-up tables could therefore miss real learner progress that has not rolled up yet.
- ``StudentCompetencyCriteriaStatus`` is what determines whether a record is protected. ``StudentCompetencyCriteriaGroupStatus`` and ``StudentCompetencyStatus`` are roll-up tables derived from it (Decision 6) and are not independently checked for this purpose, with one exception: the criterion delete cascade checks ``StudentCompetencyCriteriaGroupStatus`` directly before hard-deleting a group, because a group row about to be deleted cannot wait for the roll-up to catch up. :ref:`openedx-learning-adr-0004` writes the leaf table synchronously with the grade but rolls the two roll-up tables up later via an asynchronous task, which can lag behind the leaf or, per that ADR's Decision 5, need manual recovery. Checking only the roll-up tables could therefore miss real learner progress that has not rolled up yet.
- Direct deletion of a ``CompetencyRuleProfile`` is never a hard delete; retirement is always archive-only, via a normal update to its ``archived`` column (Decision 3). However, if a taxonomy or course that is associated with a taxonomy- or course-scoped profile is deleted, then this profile will be deleted along with it.
- For MVP, archiving a ``CompetencyCriteriaGroup`` or a ``CompetencyCriteria`` row is permanent, with one exception: an archived root ``CompetencyCriteriaGroup`` is reactivated when a new criterion is added for its competency (Decision 2). ``oel_tagging_objecttag`` follows :ref:`openedx-learning-adr-0003` Decision 3 instead.

Expand Down Expand Up @@ -490,3 +490,10 @@ Changelog
supersedes the ordered, short-circuited evaluation the earlier text described. Decision 6's update flow now continues past the root group to the
competency's own status row and up through parent competencies, and states what a
competency with no criteria tree of its own receives.

2026-11-04:

* Amended Decisions 2 and 7 for issue #674: the criterion delete cascade, not a backend validation
rejection, keeps the groups it empties out of the database, and it checks
``StudentCompetencyCriteriaGroupStatus`` directly before hard-deleting a group, as #674's
acceptance criteria require.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ For the initial implementation, versioning and traceability of competency achiev

- Before any related learner status exists, edits and deletes are allowed.
- After any related learner status exists, disassociation/deletion is archive-only (soft delete), not hard delete.
- Ordinary reads (tag lists, object-tag-for-an-object lookups used by authoring UIs, and similar) exclude archived rows, so an archived association behaves as gone from those paths.
- Ordinary reads (tag lists, object-tag-for-an-object lookups used by authoring UIs, and similar) exclude archived rows, so an archived association behaves as gone from those paths. For MVP, one exception applies: the ``ObjectTag`` of a criterion archived by the delete endpoint stays live, so the competency tag remains visible on the tagged object while the criterion itself is closed to authoring.
- The read path that resolves a learner status row back to the ``ObjectTag`` it references is exempt from that exclusion: archived rows remain queryable on that path, so learner status records can continue to be traced back to their source association.

4. Authoring guardrails must warn on potentially impactful edits:
Expand Down Expand Up @@ -128,3 +128,11 @@ Rejected Alternatives
11. Drop the database-level foreign key constraint on the learner column.
- Cons:
- This repo's convention is a real foreign key to ``settings.AUTH_USER_MODEL``. Reports of contention on the user row exist elsewhere in the ecosystem, but are not understood well enough here to design around.

Changelog
---------

2026-11-04:

* Amended Decision 3 for issue #674: the ``ObjectTag`` of a criterion archived by the delete endpoint stays live for MVP,
so the competency tag remains visible on the object.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ Decision
rows above it are recalculated and overwritten rather than merged. A later grade change can raise
what an edit lowered, but never lower what an edit raised. The staff correction takes a row lock
on the ``StudentCompetencyCriteriaGroupStatus`` row for that learner and the competency's root
criteria group, the group with no parent; no other path takes a lock.
criteria group, the group with no parent; no other learner-status writer takes a lock. Criterion
authoring writers (create, delete, bulk update, and group update) serialize per competency on
the competency's ``Tag`` row, each taking that lock first and no other explicit locking read on groups or criteria.



Expand Down Expand Up @@ -162,3 +164,11 @@ Rejected Alternatives
before the write completes.
- It also only checks for a race at the moment each parent is read. If the worker crashes mid-cascade before reaching the next read,
nothing notices the rollup was left unfinished. Decision 5's manual recovery mechanism exists to catch that case.

Changelog
---------

2026-11-04:

* Amended Decision 6 for issue #674: the rule that no other path takes a lock covers learner-status
writers only. Criterion authoring writers serialize per competency on the ``Tag`` row.
Loading
Loading