A data-quality check for ingest: which objectives say close to the same thing. Compares the words, as the overlap between two statements' token sets once stopwords are removed, within a kind.
What this deliberately does not claim
Overlap between objectives is how curricula work. A spiral course revisits a
topic at increasing depth, and a session that reinforces an earlier one is
doing its job. There is no similarity above which overlap becomes "too
much", because that judgement needs the instructor's intent. So this reports
pairs and marks where they sit; it does not call them redundant, and
report_curriculum() does not raise them as findings to act on.
scope is what makes a row worth attention:
"course objective"– a course's outcome list is meant to enumerate distinct claims, so a near-duplicate pair there is a drafting error."session <id>"– one session listing the same outcome twice is an editing error."across sessions"– two sessions covering related ground. Expected. Reported so it can be seen, and normally left alone.