Skip to content

Show QTI questions read-only when an edit would drop item-level content - #6306

Merged
AlexVelezLl merged 1 commit into
learningequality:unstablefrom
rtibblesbot:issue-6290-74ac01
Oct 15, 2026
Merged

AlexVelezLl merged 1 commit into
learningequality:unstablefrom
rtibblesbot:issue-6290-74ac01

Conversation

@rtibblesbot

@rtibblesbot rtibblesbot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • QTI questions open read-only when the root holds content the editor doesn't write, e.g. <qti-stylesheet>, <qti-modal-feedback>, a non-hint catalog, adaptive="true", or an outcome reference.
  • Custom scoring stays editable; save regenerates it.
  • Saves write xsi:schemaLocation, tool-name, tool-version, xml:lang for legacy language, and keep label.

References

Fixes #6290.

Reviewer guidance

  • Allowlist of root attributes and children: validateItem.js; <qti-catalog-info> may hold only hint cards parseHints reads in full.
  • Read-only items breaking an editor rule show Incomplete with the delete prompt.
  • Open risk: an edit drops a non-zero SCORE default or custom partial credit.
  • Known gap, QTI editor drops prefixed MathML from a question prompt on save #6308: MathML in a prompt saves as plain text.

Manual QA

QA team: questions now open read-only when an edit would lose content. Look for Studio-made questions that open read-only.

Import "Skeletal Structure - Practice" (Kolibri QA Channel, Courses > Hummingbird Biology > Anatomy & Physiology > Skeletal Structure) into an editable channel. Open Questions.

  1. Edit: Edit an answer in question 1 two times. Mark another answer correct in question 2. Close and reopen each. The changes stay.
  2. New questions: Add a choice and a Text entry question. Close and reopen each. Both stay as set.
  3. German Numeric question: In Details, set Language to "Deutsch (de)". Add a Numeric question with the answers 0,5 and 1.234,5. Click Close, then Edit. The answers show as "0,5" and "1234,5". No read-only message or "Incomplete" shows.
  4. Unreadable answer: Click Add acceptable answer, type 1,2,3, then move on. "Must be a valid number" shows.
  5. Read-only messages: Look at the read-only questions under Evidence. Is each message clear? No QA Channel question opens read-only.

Evidence

Step Screenshot
Before: printed-SCORE question editable Before: printed-SCORE question editable
Root extras: read-only Root extras: read-only
German Numeric: 0,5, 1234,5, complete German Numeric: 0,5, 1234,5, complete
German Numeric reopened editable German Numeric reopened editable
1,2,3 flagged 1,2,3 flagged
Read-only Numeric: answerless one Incomplete Read-only Numeric: answerless one Incomplete

Rule-breaking read-only questions deleted:

readonly-rules-delete-flagged.webm

Scoring extras stay editable:

s1x-edit-correct-answer-save-reopen.webm
More captures (27)
Step Screenshot
Before: printed-SCORE question, Beta marked Before: printed-SCORE question, Beta marked
Before: printed-SCORE question saved Before: printed-SCORE question saved
Rule-breaking questions Incomplete Rule-breaking questions Incomplete
One deleted One deleted
Both deleted: complete Both deleted: complete
Scoring extras editable Scoring extras editable
Scoring extras: Bravo correct after reload Bravo correct after reload
MAXSCORE question reopened MAXSCORE question reopened
Root extras exercise details Root extras exercise details
adaptive="false", time-dependent="0": editable adaptive false, time-dependent 0: editable
false/0 question in the editor false/0 question in the editor
Legacy language: Edit enabled Legacy language: Edit enabled
Legacy language in the editor Legacy language in the editor
Legacy language reopened Legacy language reopened
Converted question, two hints Converted question, two hints
One hint left One hint left
Reopened: hint with image Reopened: hint with image
Empty question list Empty question list
New choice question New choice question
Choice question reopened Choice question reopened
New text entry question New text entry question
Text entry reopened Text entry reopened
German Numeric in the side panel German Numeric in the side panel
Answerless question: Options, Delete Answerless question: Options, Delete

Legacy language, converted hints, new questions:

s3-legacy-language-edit-save-reopen.webm
s4-edit-delete-hint-save-reopen.webm
s5-text-entry-create-reopen.webm

Deviations from the issue spec

Criterion Why not
Edits, including removing every hint, keep <qti-stylesheet>, <qti-modal-feedback>, non-hint catalogs Read-only instead, as asked

AI usage

Claude Code wrote and self-reviewed it; Jest and pre-commit pass.

🤖 Generated with Claude Code


Written by rtibblesbot, an LLM-based coding agent.

@rtibblesbot

🟡 Waiting for feedback

Last updated: 2026-10-15 05:18 UTC

@rtibblesbot
rtibblesbot marked this pull request as ready for review October 8, 2026 06:20
@rtibblesbot
rtibblesbot requested review from a team and leq-bot October 8, 2026 06:20
@rtibblesbot
rtibblesbot force-pushed the issue-6290-74ac01 branch 2 times, most recently from 90c5b61 to dbe8a96 Compare October 8, 2026 18:58
@rtibblesbot

Copy link
Copy Markdown
Contributor Author

Reworked per @AlexVelezLl's direction: items with item-level content the editor doesn't write now open read-only instead of carrying it. The PR description above still describes the carry-over approach.

  • Read-only: <qti-stylesheet>, <qti-modal-feedback>, outcomes other than SCORE/RAW_SCORE, catalogs or cards other than hints, response declarations no interaction uses, any other root child, unknown root attributes.
  • Still editable, kept through an edit: xsi:schemaLocation (with xmlns:xsi), label, tool-name, tool-version. The converter writes these on every legacy question, so making them read-only would lock all converted items.
  • keepsItemContent in assembleItem.js decides; QTIItemEditor and validateQtiItem both call it.

Stylesheet item read-only; xsi:schemaLocation item editable

Deleting a hint on the xsi:schemaLocation item, then closing it. The stored raw_data keeps xsi:schemaLocation and has one hint left:

edit-schemalocation.webm

@rtibblesbot
rtibblesbot force-pushed the issue-6290-74ac01 branch 2 times, most recently from f6778b6 to 0f4e0d1 Compare October 9, 2026 02:39
@rtibblesbot rtibblesbot changed the title Keep QTI item content the editor doesn't write through an edit Show QTI questions read-only when an edit would drop item-level content Oct 9, 2026

@AlexVelezLl AlexVelezLl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The high level strategy proposed for this issue was too risky and prone to errors, where new introducedn editor capabilities could potentially drop support for questions created with older versions of it. Let's allow some flexibility in favor of a safer strategy for 99.99999% of cases.

@rtibblesbot
rtibblesbot force-pushed the issue-6290-74ac01 branch 2 times, most recently from fa52f67 to b6cffde Compare October 10, 2026 13:20
@rtibblesbot

Copy link
Copy Markdown
Contributor Author

Python unit tests red: test_restore_channel.py QTIRestoreTestCase, 7 failures — #6305. Same 7 fail on base afc8296ac locally; fix is in #6262. test_convert.py passes.

@rtibblesbot

Copy link
Copy Markdown
Contributor Author

Python unit tests: the 7 QTIRestoreTestCase failures in test_restore_channel.py reproduce on base afc8296ac too (#6305, fixed by #6262). This branch's Python change, test_convert.py, passes (49 tests).

@AlexVelezLl AlexVelezLl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rebase.

- An item opens read-only when its root has an attribute or child the
  editor doesn't write; scoring outcomes and processing are regenerated.
- An item that reads an outcome (feedback, printed variable) opens
  read-only: the rich-text editors drop those elements.
- holdsOnlyHints (hints.js) accepts only hint cards parseHints reads in
  full.
- assembleItemXml writes the converter's root metadata, including
  xsi:schemaLocation.
- parseItem reads a pre-c51c5e035 root `language`, so an edit writes it
  as xml:lang.
- validateQtiItem still applies the editor's rules to these items, so an
  unanswerable one stays incomplete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rtibblesbot

Copy link
Copy Markdown
Contributor Author

Rebased onto unstable (bdd38bb). Conflicts were only adjacent test additions in validateItem.spec.js and QTIItemEditor.spec.js; kept both sides. QTIEditor Jest suites pass (1714 tests).

@AlexVelezLl AlexVelezLl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@AlexVelezLl
AlexVelezLl merged commit 98b560e into learningequality:unstable Oct 15, 2026
13 checks passed
@rtibblesbot

Copy link
Copy Markdown
Contributor Author

Quick check on rtibblesbot's work on this PR: react 👍 helpful · 😕 mixed · 👎 not helpful. Reply with anything specific.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[QTI] Editing a question keeps the item-level content the editor doesn't write

2 participants