-
Notifications
You must be signed in to change notification settings - Fork 26
Competency Mastery Concurrency ADR #657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jesperhodge
wants to merge
10
commits into
openedx:main
Choose a base branch
from
jesperhodge:jesperhodge/competency-adr-4
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
f96a350
docs: competency ADR 4
jesperhodge 847b789
docs: reject partition-key variant
jesperhodge a08bf5b
docs: fix RST indentation breaking readthedocs build
jesperhodge 7c77aea
docs: make ADR 4 work without event bus
jesperhodge dc01a87
docs: finalize concurrency approach ADR
jesperhodge 808a228
docs: concurrency and storage docs and diagrams
jesperhodge 78bc16c
docs: add reasoning for rejecting per-learner batching
jesperhodge 4cfb092
docs: choose monotonic non-locking option
jesperhodge c4354d5
docs: improve atomicity pattern
jesperhodge 59c5467
docs: clean up ADR
jesperhodge File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
150 changes: 150 additions & 0 deletions
150
docs/openedx_learning/decisions/0002-competency-criteria-model-diagram.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| # Competency data model (updated) | ||
|
|
||
| The authoritative data model for CBE competency criteria and learner mastery. It builds on the | ||
| original `images/CompetencyCriteriaModel.png` overview, corrected against ADRs 0001-0003 (which are | ||
| the source of truth where the PNG diverges) and updated for the storage decisions in | ||
| `0005-competency-mastery-storage.rst`. | ||
|
|
||
| Notable differences from the PNG: | ||
|
|
||
| - Each learner-status level is now two tables: an **ACTIVE** table (one current row per learner and | ||
| node, updated in place) and an append-only **HISTORY** table (one row per genuine status advance, | ||
| bounded by monotonicity). The PNG showed a single table per level. | ||
| - The `mastery_level_id` column the PNG drew on `StudentCompetencyCriteriaStatus` is **not** part of | ||
| the model (mastery level is a future rule type; see ADR 0002). | ||
| - Learner-status tables use 64-bit primary keys and are reachable through a dedicated database alias | ||
| (defaulting to the main database). Their references to the definition tables and to the user are | ||
| **logical foreign keys without database-level constraints**, so the tables can live in a separate | ||
| database (see ADR 0005). | ||
|
|
||
| Existing tagging tables (`oel_tagging_*`) are shown minimally, only enough to anchor the | ||
| relationships they participate in. | ||
|
|
||
| ```mermaid | ||
| erDiagram | ||
| oel_tagging_taxonomy ||--|| CompetencyTaxonomy : "MTI subtype" | ||
| oel_tagging_taxonomy ||--o{ oel_tagging_tag : "taxonomy_id" | ||
| oel_tagging_tag ||--o{ oel_tagging_objecttag : "tag_id" | ||
| CompetencyTaxonomy ||--o{ CompetencyRuleProfile : "scopes" | ||
| oel_tagging_tag ||--o{ CompetencyCriteriaGroup : "competency" | ||
| CompetencyCriteriaGroup ||--o{ CompetencyCriteriaGroup : "parent_id (nesting)" | ||
| CompetencyCriteriaGroup ||--o{ CompetencyCriteria : "contains" | ||
| oel_tagging_objecttag ||--o{ CompetencyCriteria : "association" | ||
| CompetencyRuleProfile ||--o{ CompetencyCriteria : "default rule" | ||
| CompetencyCriteria ||--o{ StudentCompetencyCriteriaStatus : "leaf ACTIVE (no DB FK)" | ||
| StudentCompetencyCriteriaStatus ||--o{ StudentCompetencyCriteriaStatusHistory : "advances (same key)" | ||
| CompetencyCriteriaGroup ||--o{ StudentCompetencyCriteriaGroupStatus : "group ACTIVE (no DB FK)" | ||
| StudentCompetencyCriteriaGroupStatus ||--o{ StudentCompetencyCriteriaGroupStatusHistory : "advances (same key)" | ||
| oel_tagging_tag ||--o{ StudentCompetencyStatus : "competency ACTIVE (no DB FK)" | ||
| StudentCompetencyStatus ||--o{ StudentCompetencyStatusHistory : "advances (same key)" | ||
| CompetencyMasteryStatuses ||--o{ StudentCompetencyCriteriaStatus : "status_id" | ||
| CompetencyMasteryStatuses ||--o{ StudentCompetencyCriteriaGroupStatus : "status_id" | ||
| CompetencyMasteryStatuses ||--o{ StudentCompetencyStatus : "status_id" | ||
|
|
||
| oel_tagging_taxonomy { | ||
| int id PK | ||
| } | ||
| oel_tagging_tag { | ||
| int id PK | ||
| int taxonomy_id FK | ||
| } | ||
| oel_tagging_objecttag { | ||
| int id PK | ||
| int tag_id FK | ||
| } | ||
| CompetencyTaxonomy { | ||
| int taxonomy_ptr_id PK "1:1 to oel_tagging_taxonomy" | ||
| } | ||
| CompetencyRuleProfile { | ||
| int id PK | ||
| int organization_id "nullable scope" | ||
| varchar course_id "nullable scope" | ||
| int competency_taxonomy_id FK "nullable scope" | ||
| varchar rule_type "Grade (View, MasteryLevel future)" | ||
| json rule_payload | ||
| } | ||
| CompetencyCriteriaGroup { | ||
| int id PK | ||
| int parent_id FK "self; null = root" | ||
| int oel_tagging_tag_id FK "competency" | ||
| varchar course_id "nullable course scope" | ||
| varchar name | ||
| int ordering | ||
| varchar logic_operator "AND / OR / null" | ||
| } | ||
| CompetencyCriteria { | ||
| int id PK | ||
| int competency_criteria_group_id FK | ||
| int oel_tagging_objecttag_id FK | ||
| int competency_rule_profile_id FK "nullable" | ||
| varchar rule_type_override "nullable" | ||
| json rule_payload_override "nullable" | ||
| } | ||
| CompetencyMasteryStatuses { | ||
| int id PK | ||
| varchar status UK "Demonstrated / AttemptedNotDemonstrated / PartiallyAttempted" | ||
| } | ||
| StudentCompetencyCriteriaStatus { | ||
| bigint id PK "64-bit" | ||
| int user_id "logical FK, no DB constraint" | ||
| int competency_criteria_id "logical FK, no DB constraint" | ||
| int status_id FK "leaf: Demonstrated / AttemptedNotDemonstrated" | ||
| datetime effective_source_timestamp | ||
| datetime created | ||
| datetime modified | ||
| } | ||
| StudentCompetencyCriteriaStatusHistory { | ||
| bigint id PK "64-bit" | ||
| int user_id | ||
| int competency_criteria_id | ||
| int status_id FK | ||
| datetime effective_source_timestamp | ||
| datetime created "one row per advance" | ||
| } | ||
| StudentCompetencyCriteriaGroupStatus { | ||
| bigint id PK "64-bit" | ||
| int user_id "logical FK, no DB constraint" | ||
| int competency_criteria_group_id "logical FK, no DB constraint" | ||
| int status_id FK | ||
| datetime effective_source_timestamp | ||
| datetime created | ||
| datetime modified | ||
| } | ||
| StudentCompetencyCriteriaGroupStatusHistory { | ||
| bigint id PK "64-bit" | ||
| int user_id | ||
| int competency_criteria_group_id | ||
| int status_id FK | ||
| datetime effective_source_timestamp | ||
| datetime created "one row per advance" | ||
| } | ||
| StudentCompetencyStatus { | ||
| bigint id PK "64-bit" | ||
| int user_id "logical FK, no DB constraint" | ||
| int oel_tagging_tag_id "logical FK, no DB constraint" | ||
| int status_id FK "Demonstrated / PartiallyAttempted only" | ||
| datetime effective_source_timestamp | ||
| datetime created | ||
| datetime modified | ||
| } | ||
| StudentCompetencyStatusHistory { | ||
| bigint id PK "64-bit" | ||
| int user_id | ||
| int oel_tagging_tag_id | ||
| int status_id FK | ||
| datetime effective_source_timestamp | ||
| datetime created "one row per advance" | ||
| } | ||
| ``` | ||
|
|
||
| Notes on the diagram: | ||
|
|
||
| - Each ACTIVE table is **unique on `(user_id, node_id)`** (one current row per learner and node): | ||
| `(user_id, competency_criteria_id)`, `(user_id, competency_criteria_group_id)`, and | ||
| `(user_id, oel_tagging_tag_id)` respectively. HISTORY tables have no such uniqueness; they carry | ||
| one row per advance. | ||
| - The ACTIVE-to-HISTORY relationship is drawn as one-to-many but is not a literal foreign key: a | ||
| HISTORY row shares the ACTIVE row's `(user_id, node_id)` identity rather than pointing at its | ||
| primary key. | ||
| - `status_id` references the shared `CompetencyMasteryStatuses` lookup so status semantics live in | ||
| one place. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,7 +81,7 @@ Decision | |
| 4. ``course_id``: Nullable foreign key to ``openedx_catalog_courserun.id`` for the course that scopes this criteria tree. | ||
| 5. ``name``: string | ||
| 6. ``ordering``: Indicates evaluation sequence number for this criteria group. This defines deterministic evaluation order for siblings during read-time evaluation and event-driven recomputation, and enables short-circuit evaluation. | ||
| 7. ``logic_operator``: Either “AND” or “OR” or null. This determines how children are combined at a group node ("AND" or "OR"). | ||
| 7. ``logic_operator``: Either “AND” or “OR” or null. This determines how children are combined at a group node ("AND" or "OR"). Deliberately no negation operator: keeping every combinator monotone (advancing a child status can only advance or leave unchanged its parent, never regress it) is what lets :ref:`openedx-learning-adr-0004` record concurrently without coordination. | ||
|
|
||
| Example: A root group uses "OR" with two child groups. | ||
|
|
||
|
|
@@ -155,29 +155,58 @@ Decision | |
| 3. ``oel_tagging_objecttag(object_id)`` | ||
| 4. ``CompetencyCriteria(oel_tagging_objecttag_id)`` | ||
| 5. ``CompetencyCriteria(competency_criteria_group_id)`` | ||
| 6. ``StudentCompetencyCriteriaStatus(user_id, competency_criteria_id)`` | ||
| 7. ``StudentCompetencyCriteriaGroupStatus(user_id, competency_criteria_group_id)`` | ||
| 8. ``StudentCompetencyStatus(user_id, oel_tagging_tag_id)`` | ||
| 9. ``CompetencyRuleProfile(competency_taxonomy_id, course_id, organization_id)`` | ||
| 10. ``CompetencyMasteryStatuses(status)`` (unique) | ||
| 6. ``StudentCompetencyCriteriaStatus(user_id, competency_criteria_id)`` (unique; ACTIVE current row) | ||
| 7. ``StudentCompetencyCriteriaGroupStatus(user_id, competency_criteria_group_id)`` (unique; ACTIVE current row) | ||
| 8. ``StudentCompetencyStatus(user_id, oel_tagging_tag_id)`` (unique; ACTIVE current row) | ||
| 9. ``StudentCompetencyCriteriaStatusHistory(user_id, competency_criteria_id, created)`` (point-in-time reconstruction) | ||
| 10. ``StudentCompetencyCriteriaGroupStatusHistory(user_id, competency_criteria_group_id, created)`` | ||
| 11. ``StudentCompetencyStatusHistory(user_id, oel_tagging_tag_id, created)`` | ||
| 12. ``CompetencyRuleProfile(competency_taxonomy_id, course_id, organization_id)`` | ||
| 13. ``CompetencyMasteryStatuses(status)`` (unique) | ||
|
|
||
| 6. Learner progress status concepts (``StudentCompetency*Status`` database tables) | ||
|
|
||
| When a completion event (graded, completed, mastered, etc.) occurs for an object, determine and track the learner's status in earning the competency. To reduce recalculation frequency, store results at each level. | ||
| When a completion event (a subsection grade change, delivered as described in | ||
| :ref:`openedx-learning-adr-0004`) is evaluated for an object, determine and track the learner's | ||
| status in earning the competency. Results are stored at each level (leaf, group, competency) so | ||
| reads do not recompute. Each level is stored as two tables: an ACTIVE table holding one current | ||
| row per learner and node, updated in place, and an append-only HISTORY table that records one row | ||
| per genuine status advance. Current status is a direct lookup on the ACTIVE row; the HISTORY table | ||
| is the audit and point-in-time record. Because mastery is monotonic | ||
| (:ref:`openedx-learning-adr-0005`), the number of advances per node is bounded by the status | ||
| lattice, so the HISTORY tables grow with learners and nodes, not with time. | ||
|
|
||
| These are the large per-learner tables in this model, so they follow the scale posture of | ||
| :ref:`openedx-learning-adr-0005` (mirroring edx-platform's persistent grades): a 64-bit | ||
| ``BigAutoField`` primary key, narrow rows, per-read-path composite indexes (Decision 5), and a | ||
| dedicated database alias reached through a router that defaults to the main database. Their foreign | ||
| keys to the criteria-definition tables and to the user are logical references declared without | ||
| database-level constraints, so the tables can live in a different database from the definitions; | ||
| referential integrity and the delete protection of Decision 7 are enforced in application code. | ||
|
|
||
| Relationship to other concepts: | ||
|
|
||
| - ``StudentCompetencyCriteriaStatus`` tracks status at ``CompetencyCriterion`` leaf level. | ||
| - ``StudentCompetencyCriteriaGroupStatus`` tracks status at ``CompetencyCriteriaGroup`` node level. | ||
| - ``StudentCompetencyStatus`` tracks top-level competency demonstration state. | ||
| - All learner status rows use a shared lookup table (``CompetencyMasteryStatuses``) so status semantics live in one place and student status tables stay structurally consistent. | ||
|
|
||
| Intended update flow (bottom-up materialization): | ||
|
|
||
| - A learner event updates one ``StudentCompetencyCriteriaStatus`` row. | ||
| - Recompute ancestor ``CompetencyCriteriaGroup`` statuses upward to the root. | ||
| - At each group, evaluate children in ``ordering`` sequence and short-circuit when the group's result is already determined by its ``logic_operator``. | ||
| - Persist only rows whose status changed. | ||
| - ``StudentCompetencyCriteriaStatus`` / ``StudentCompetencyCriteriaStatusHistory`` track status at | ||
| the ``CompetencyCriterion`` leaf level. A leaf is atomic, so its status is only ``Demonstrated`` | ||
| or ``AttemptedNotDemonstrated``. | ||
| - ``StudentCompetencyCriteriaGroupStatus`` / ``StudentCompetencyCriteriaGroupStatusHistory`` track | ||
| status at the ``CompetencyCriteriaGroup`` node level. | ||
| - ``StudentCompetencyStatus`` / ``StudentCompetencyStatusHistory`` track top-level competency | ||
| demonstration state. | ||
| - All learner status rows use a shared lookup table (``CompetencyMasteryStatuses``) so status | ||
| semantics live in one place and the status tables stay structurally consistent. | ||
|
|
||
| Intended update flow (bottom-up roll-up): | ||
|
|
||
| - A learner event evaluates one leaf; if its status advances, its ACTIVE row is updated in place | ||
| and a leaf HISTORY row is appended. | ||
| - Recompute ancestor ``CompetencyCriteriaGroup`` statuses upward to the root, reading the stored | ||
| child rows directly. | ||
| - At each group, evaluate children in ``ordering`` sequence and short-circuit when the group's | ||
| result is already determined by its ``logic_operator``. | ||
| - Persist only nodes whose status changed: update the ACTIVE row in place and append a HISTORY row. | ||
| A banked (``Demonstrated``) status is never regressed, so a downward grade correction advances | ||
| nothing and writes no HISTORY row. | ||
|
|
||
| 1. Add new database table for ``CompetencyMasteryStatuses`` with these columns: | ||
|
|
||
|
|
@@ -188,34 +217,51 @@ Decision | |
|
|
||
| - This table is system-owned lookup data and should be treated as immutable configuration, not user-authored rows. | ||
|
|
||
| 2. Add new database table for ``StudentCompetencyCriteriaStatus`` with these columns: | ||
| 2. Add the leaf-level tables ``StudentCompetencyCriteriaStatus`` (ACTIVE) and | ||
| ``StudentCompetencyCriteriaStatusHistory`` (HISTORY). Both tables have these columns: | ||
|
|
||
| 1. ``id``: unique primary key | ||
| 2. ``competency_criteria_id``: Foreign key to ``CompetencyCriterion.id`` | ||
| 3. ``user_id``: Foreign key pointing to user_id (presumably the learner's id, although it appears that it is possible for staff to get grades as well) in ``auth_user`` table | ||
| 4. ``status_id``: Foreign key to ``CompetencyMasteryStatuses.id`` | ||
| 5. ``created``: The timestamp at which the student's criterion status was set. | ||
| 1. ``id``: 64-bit ``BigAutoField`` primary key | ||
| 2. ``competency_criteria_id``: logical foreign key to ``CompetencyCriterion.id`` (no database-level constraint) | ||
| 3. ``user_id``: logical foreign key to the learner's user id (no database-level constraint) | ||
| 4. ``status_id``: foreign key to ``CompetencyMasteryStatuses.id`` | ||
| 5. ``effective_source_timestamp``: the timestamp of the grade change this status was computed from, used for the out-of-order defense in :ref:`openedx-learning-adr-0004` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is effective_source_timestamp different from created? |
||
| 6. ``created``: on ACTIVE, when the row was first written; on HISTORY, when this advance was appended | ||
|
|
||
| 3. Add a new database table for ``StudentCompetencyCriteriaGroupStatus`` with these columns: | ||
| The ACTIVE table additionally has a ``modified`` timestamp (last in-place update) and is unique | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like these |
||
| on ``(user_id, competency_criteria_id)`` (one current row per learner and leaf). The HISTORY | ||
| table has no such uniqueness constraint: it holds one row per advance and is not written for a | ||
| suppressed downward correction. | ||
|
|
||
| 1. ``id``: unique primary key | ||
| 2. ``competency_criteria_group_id``: Foreign key to ``CompetencyCriteriaGroup.id`` | ||
| 3. ``user_id``: Foreign key pointing to user_id (presumably the learner's id, although it appears that it is possible for staff to get grades as well) in ``auth_user`` table | ||
| 4. ``status_id``: Foreign key to ``CompetencyMasteryStatuses.id`` | ||
| 5. ``created``: The timestamp at which the student's criteria-group status was set. | ||
| 3. Add the group-level tables ``StudentCompetencyCriteriaGroupStatus`` (ACTIVE) and | ||
| ``StudentCompetencyCriteriaGroupStatusHistory`` (HISTORY). Both tables have these columns: | ||
|
|
||
| 4. Add a new database table for ``StudentCompetencyStatus`` with these columns: | ||
| 1. ``id``: 64-bit ``BigAutoField`` primary key | ||
| 2. ``competency_criteria_group_id``: logical foreign key to ``CompetencyCriteriaGroup.id`` (no database-level constraint) | ||
| 3. ``user_id``: logical foreign key to the learner's user id (no database-level constraint) | ||
| 4. ``status_id``: foreign key to ``CompetencyMasteryStatuses.id`` | ||
| 5. ``effective_source_timestamp``: as in the leaf tables | ||
| 6. ``created``: as in the leaf tables | ||
|
|
||
| 1. ``id``: unique primary key | ||
| 2. ``oel_tagging_tag_id``: Foreign key pointing to Tag id | ||
| 3. ``user_id``: Foreign key pointing to user_id (presumably the learner's id, although it appears that it is possible for staff to get grades as well) in ``auth_user`` table | ||
| 4. ``status_id``: Foreign key to ``CompetencyMasteryStatuses.id``. This table should have a constraint to only allow status values of “Demonstrated” and “PartiallyAttempted” since it represents overall competency demonstration state, not in-progress states. | ||
| 5. ``created``: The timestamp at which the student's competency status was set. | ||
| The ACTIVE table additionally has a ``modified`` timestamp and is unique on | ||
| ``(user_id, competency_criteria_group_id)``. | ||
|
|
||
| 4. Add the competency-level tables ``StudentCompetencyStatus`` (ACTIVE) and | ||
| ``StudentCompetencyStatusHistory`` (HISTORY). Both tables have these columns: | ||
|
|
||
| 1. ``id``: 64-bit ``BigAutoField`` primary key | ||
| 2. ``oel_tagging_tag_id``: logical foreign key to the competency ``Tag`` id (no database-level constraint) | ||
| 3. ``user_id``: logical foreign key to the learner's user id (no database-level constraint) | ||
| 4. ``status_id``: foreign key to ``CompetencyMasteryStatuses.id``. Constrained to “Demonstrated” and “PartiallyAttempted” only, since this represents overall competency demonstration state, not in-progress states. | ||
| 5. ``effective_source_timestamp``: as in the leaf tables | ||
| 6. ``created``: as in the leaf tables | ||
|
|
||
| The ACTIVE table additionally has a ``modified`` timestamp and is unique on | ||
| ``(user_id, oel_tagging_tag_id)``. | ||
|
|
||
| 7. Delete protection boundaries | ||
|
|
||
| - If no learner status rows exist for a competency definition, hard delete is allowed and cascades through competency metadata tables. | ||
| - Once any related learner status exists in ``StudentCompetencyStatus``, ``StudentCompetencyCriteriaGroupStatus``, or ``StudentCompetencyCriteriaStatus``, deletion of associated competency definition rows is blocked. | ||
| - Once any related learner status exists (an ACTIVE or HISTORY row at any level), deletion of associated competency definition rows is blocked. Because the learner-status foreign keys carry no database-level constraint (Decision 6), this protection is enforced in application code, not by a database cascade or constraint. | ||
| - This delete protection applies to ``oel_tagging_taxonomy``, ``CompetencyTaxonomy``, ``oel_tagging_tag``, ``oel_tagging_objecttag``, ``CompetencyCriteriaGroup``, ``CompetencyCriteria``, and ``CompetencyRuleProfile``. | ||
| - Once any related learner status exists, retiring definitions may be archive-only (hidden from authoring and new associations), not hard delete. | ||
|
|
||
|
|
@@ -224,6 +270,15 @@ Decision | |
| :width: 80% | ||
| :align: center | ||
|
|
||
| .. note:: | ||
|
|
||
| The PNG above is the original overview. It predates the ACTIVE/HISTORY split introduced here and | ||
| in :ref:`openedx-learning-adr-0005`, and it shows a ``mastery_level_id`` column on | ||
| ``StudentCompetencyCriteriaStatus`` that is not part of this decision (mastery level is a future | ||
| rule type). The current data model, including the ACTIVE and HISTORY tables, is diagrammed in | ||
| ``0002-competency-criteria-model-diagram.md``, which is authoritative where it differs from the | ||
| PNG. | ||
|
|
||
|
|
||
| Example | ||
| ------- | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless others disagree, I think I'd recommend updating the existing PNG or trading it out for the mermaid diagram in the existing ADR 0002, as well as making it clearer that we already supported the active vs history dynamic and how the mastery levels fit in and anything else needed, rather than adding this extra file. We want to try to keep the ADRs light and concise and only change what really needs to be changed. We've also been asked to try to keep the implementation detail in ADRs to a minimum, which may also be relevant here.