Skip to content

Add a notebook renderer for QDK Learning multiple-choice questions - #3690

Open
Dhairya Patel (HABER7789) wants to merge 7 commits into
mainfrom
HABER7789/mcq-renderer
Open

Dhairya Patel (HABER7789) wants to merge 7 commits into
mainfrom
HABER7789/mcq-renderer

Conversation

@HABER7789

@HABER7789 Dhairya Patel (HABER7789) commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Each section of the IQPE chapter ends with a self-check question. Today it's a dropdown you click to reveal the answer. This turns those 12 into multiple choice questions a learner answers, gets marked on, and can retry.

  • The repo's first notebook renderer, for application/vnd.qdk.learning+json
  • Grading happens in the renderer, so questions work without a running kernel
  • Choices live in _unit.py, so the notebook cell only says quiz("id")
  • details_to_quiz.py bakes a chapter's questions; --check reports stale output
  • A quiz is stored as JSON in the notebook cell's output. That shape is written by hand in schema.ts, _learning_output.py and the package.json contribution with nothing linking them, so checkRendererContract() compares them at build time and fails if the MIME type, the payload fields, the id grammar or the quiz cell tag stop matching

Not in this PR

  • Quizzes don't show in the progress tree. Whether a learner answered correctly never leaves the renderer, so a progress entry would claim tracking that doesn't exist. Tracking them is a natural follow-up the renderer-to-extension channel this PR adds is what it would use.
  • "Why is that wrong?" needs the extension. It's inert in VS Code for Web, where QDK Learning doesn't load. On GitHub and in exported HTML the renderer doesn't run at all, so the static fallback shows instead a question with no buttons, and no answers.
  • The contract check only guards the chemistry emitter. A second course that copies _learning_output.py wouldn't be covered.

Comment thread source/vscode/resources/qdk-learning/courses/chemistry-qpe/_learning_output.py Dismissed
Comment thread source/vscode/resources/qdk-learning/courses/chemistry-qpe/_learning_output.py Dismissed
Comment thread source/vscode/src/notebookRenderer/rendering.ts Dismissed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Untrusted renderer content can reach Copilot, while payload and stale-output validation gaps can produce incorrect quiz behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds an interactive, kernel-independent notebook renderer for QDK Learning quizzes.

Changes:

  • Implements multiple-choice rendering, grading, retry, accessibility, and Copilot actions.
  • Adds Python quiz authoring, validation, conversion, and baked-output tooling.
  • Converts IQPE self-check content and integrates renderer builds and messaging.
File summaries
File Description
source/vscode/tsconfig.json Separates renderer type-checking.
source/vscode/src/notebookRenderer/tsconfig.json Configures renderer TypeScript checks.
source/vscode/src/notebookRenderer/styles.css Styles quiz states and controls.
source/vscode/src/notebookRenderer/schema.ts Defines payload and messaging contracts.
source/vscode/src/notebookRenderer/rendering.ts Adds safe DOM helpers.
source/vscode/src/notebookRenderer/rendererApi.d.ts Declares notebook renderer APIs.
source/vscode/src/notebookRenderer/multipleChoice.ts Implements quiz interaction and grading.
source/vscode/src/notebookRenderer/index.ts Activates rendering and manages lifecycle.
source/vscode/src/notebookRenderer/css.d.ts Types CSS text imports.
source/vscode/src/learning/notebookRendererMessaging.ts Bridges renderer actions to Copilot.
source/vscode/src/learning/notebookExercises.ts Excludes quizzes from tracked activities.
source/vscode/src/learning/index.ts Registers renderer messaging.
source/vscode/resources/qdk-learning/utils/chemistry-qpe/verify_course.py Recognizes baked quiz cells.
source/vscode/resources/qdk-learning/utils/chemistry-qpe/README.md Documents quiz conversion workflow.
source/vscode/resources/qdk-learning/utils/chemistry-qpe/details_to_quiz.py Converts and rebakes quiz outputs.
source/vscode/resources/qdk-learning/courses/chemistry-qpe/06-iterative-phase-estimation/_unit.py Registers IQPE quiz content.
source/vscode/resources/qdk-learning/courses/chemistry-qpe/_learning_output.py Defines quiz payloads and fallbacks.
source/vscode/package.json Registers and type-checks the renderer.
source/vscode/build.mjs Builds the renderer and checks contracts.
source/vscode/authoring-courses.md Documents quiz authoring.
source/npm/qsharp/ux/qdk-theme.css Adds quiz theme colors.
Review details
  • Files reviewed: 19/22 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread source/vscode/src/learning/notebookRendererMessaging.ts Outdated
Comment thread source/vscode/resources/qdk-learning/utils/chemistry-qpe/details_to_quiz.py Outdated
Comment thread source/vscode/src/notebookRenderer/index.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The stale-output checker misses deleted single-quiz calls, and the Copilot action omits the learner’s selected answer.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

source/vscode/resources/qdk-learning/utils/chemistry-qpe/details_to_quiz.py:328

  • When the last quiz(...) call is removed from a cell, ids is empty and this early return skips its existing baked output. The deleted question therefore remains visible while --check reports the notebook as up to date; most generated quiz cells contain only one call, so this is a common deletion case. Detect cells that still contain the custom quiz MIME output even when no IDs remain, and report or clear those outputs.
        ids = _cell_quiz_ids(cell)
        if not ids:
            continue
  • Files reviewed: 20/23 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread source/vscode/src/notebookRenderer/multipleChoice.ts
# Conflicts:
#	source/vscode/resources/qdk-learning/courses/chemistry-qpe/06-iterative-phase-estimation/iterative_phase_estimation.ipynb
#	source/vscode/resources/qdk-learning/utils/chemistry-qpe/README.md

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Stale deleted outputs can remain visible, and the Python emitter can produce payloads that the renderer rejects.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

source/vscode/resources/qdk-learning/utils/chemistry-qpe/details_to_quiz.py:328

  • The leftover-output fix still skips the case where the last quiz() call is removed from a quiz cell: ids is empty, so the function returns before inspecting that cell's baked outputs. In that case --check still reports up to date and VS Code continues rendering the deleted question. Track stale cells (not only surviving quiz IDs) so an empty quiz cell can have its outputs cleared.
        if not ids:
            continue
  • Files reviewed: 20/23 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment thread source/vscode/build.mjs
Comment thread source/vscode/resources/qdk-learning/courses/chemistry-qpe/_learning_output.py Outdated
- Reject empty prompts, empty option text and malformed ids when a quiz is authored, so mistakes fail for the author instead of the learner
- Extend the build-time contract check to the manifest, the runtime kind list, the quiz cell tag, the id grammar and the option cap
- Clear baked questions left behind when a quiz call is removed, and refuse rather than delete when a call cannot be parsed
- Rename the payload's cellId to payloadId - it is a quiz id, and cellId means an ipynb cell id everywhere else in src/learning
- Drop the duplicate workbook check in favour of the existing isCourseWorkbook
)


# ---------------------------------------------------------------------------

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.

I don't love that a bunch of course content now has to live on the side, in _unit.py.


const tryAgainButton = document.createElement("button");
tryAgainButton.type = "button";
tryAgainButton.textContent = "Try again";

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.

What's the try again button for if the solution is already displayed?

? ` I picked ${optionIds.map((id) => `"${id}"`).join(" and ")}.`
: "";
return (
`/qdk-learning I answered the quiz "${quizId}" in this notebook incorrectly.${picked}` +

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.

I know we have to be really explicit for copilot, but the user can see this, so we might want to try to find a gentler way to ask the same thing.

This branch has not been deployed

No deployments
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.

4 participants