ContentAssigner
ContentAssigner is a reconciliation engine. Its purpose is to turn content assignment intent into effective user study state.
Current source: knowspread/docs/content_assigner.md.
Mental model
Section titled “Mental model”flowchart LR Event[Business event] --> Dispatcher[ContentAssigner::Dispatcher] Dispatcher --> Job[ContentAssigner::Job] Job --> AssignmentJob[ContentAssignmentJob] AssignmentJob --> Assigner[ContentAssigner::Assigner] Assigner --> Sources[ParticipationSource] Assigner --> Participations[ContentParticipation] Assigner --> Licenses[ContentLicense] Assigner --> Emails[EmailNotifierJob]
| Term | Role in the process |
|---|---|
| AssignedContent | Intent that content should be studied by a user or group. |
| ContentParticipation | Effective study record for a concrete user. |
| ParticipationSource | Explanation of why a ContentParticipation exists. |
| ContentLicense | Consumed seat for a user and content in a company space. |
Further work
Section titled “Further work”- Migrate the verified parts from the old document.
- Add current links to classes and specs.
- Split architecture, edge cases, troubleshooting, and API impact.
- Mark the owner and last review date.
When ContentAssigner runs
Section titled “When ContentAssigner runs”ContentAssigner should not be called because a controller wants to manually
create ContentParticipation. It should run when the domain intent changed:
- content was assigned to a group,
- content was removed from a group,
- a user was added to a group,
- a user was removed from a group,
- content was assigned directly to a user,
- a user was added or returned to a company space,
- a new active content version was promoted,
- a user completed repeated training.
The output should not be just a row creation. It should be a consistent set of participations, sources, licenses, notifications, and future recalculations.
Main invariant
Section titled “Main invariant”AssignedContent is intent. ContentParticipation is effective state. If there
is a mismatch, ContentAssigner resolves it, not the controller.