# Changelog

All notable changes to Quizgeist are documented in this file.

Public releases use Semantic Versioning labels such as `1.0.0-rc.1`. Moodle's
separate numeric plugin version is monotonically increasing and remains
authoritative for upgrade ordering and browser-asset cache invalidation; it is
not the public product version.

## [1.2.0-rc.1] - 2026-08-01

### Added

- Added a low-stress default for live play. Points can be decoupled from
  answering speed (`even` beside the existing `timed` axis), the leaderboard
  defaults to “own progress only”, and timer and sound can be switched off per
  activity and per session. The ranking is trimmed in the player payload on the
  server, so a hidden score is absent rather than merely unrendered.
- Added an optional reasoning step. Between the answer and the reveal, players
  can be asked for a short written justification, which appears in the session
  report. The step is deliberately unscored: reasons carry zero points and no
  correctness verdict. With the AI addon the same step can be spoken.
- Added a disclosure policy for the worked solution — `immediate`, `atend` or
  `never` — decided in exactly one place for live play and self-study, plus an
  end-of-run “all worked solutions” summary and a batch generator in the AI
  workshop.
- Added a question tag system with the school-wide competence colour code.
  Tags hang on the question root, survive versioning, and contain no personal
  data.
- Added SM-2 spaced repetition for self-study. Answered questions get a
  per-learner due date, and a practice run offers what is due instead of what
  is newest.
- Added interleaved mixed review, so a practice run can draw across topics and
  activities instead of drilling a single block.
- Added a competence report and a public, viewer-agnostic read API for parent
  dashboards. The report aggregates by competence tag; `local_elternkompass`
  and any other viewer pull the digest through the documented interface, so
  Quizgeist declares no dependency on a dashboard plugin and works without one.
- Added a misconception radar. Teachers label distractors with the
  misconception they stand for, and the distribution is shown against a
  configurable follow-up threshold. The radar is host-only and is never sent to
  a player screen.
- Added a question workshop for learners, with curation as a precondition
  rather than an afterthought. A submitted question is an ordinary draft
  question; commenting and returning need the new curate capability, releasing
  it additionally needs the manage capability, and one rating per person and
  question is enforced by a unique index.
- Added spoken answers for open, brainstorm and word-cloud questions. A short
  clip is transcribed locally and graded like a typed answer; typing stays
  available everywhere as a fallback.
- Added curriculum anchoring for AI question generation. Generated questions
  are grounded in the LehrplanPLUS knowledge base through the `local_planung`
  API and carry their competence expectation and source link. The knowledge
  base only covers years 10 to 13; outside that range the plugin generates
  without anchoring and says so plainly instead of implying an anchor it does
  not have.
- Added a speaking trainer as a self-study assignment mode: the prompt is read
  aloud, the learner answers by clip, and the round is assessed and answered
  back in turn. Speech recognition is German-only, and the screen states that
  limit.
- Added the card mode (MVP). Teachers print card sheets, take one photo per
  question, and local vision recognition proposes a code and an answer per
  card. A confirmation screen shows every assignment with its confidence and
  must be accepted before a single answer is written.
- Added the optional addon Bühnen-Check (`quizgeistaddon_buehne`) for
  presenting in front of the camera. Body-language feedback is computed in the
  browser with MediaPipe; no image, no video and no raw landmark is uploaded,
  and the stored report contains aggregate figures only.

### Changed

- Raised the live session settings schema from 3 to 4 for the new `pace`,
  `leaderboard`, `timer` and `sound` fields. The decoder keeps an explicit
  schema-3 branch, so running and stored sessions decode unchanged.
- Made `question_presenter` the only place that may write a worked solution
  into a question DTO. Its default policy is `never`, so the live payload is
  bit-identical to the state before this release unless a teacher opts in.
- Raised the self-study attempt result cache contract to `attempt_result_v3`
  and made the disclosure policy part of the cache key, so entries cannot
  outlive the policy that produced them.
- Grew the addon family from five to six components. The complete free loop —
  authoring, classic live play, Kahoot import, session report and CSV — remains
  available without any addon or licence file, and every new premium surface
  fails closed with a local status instead of an error.
- Split provider validation into separate text and speech assertions instead of
  loosening the existing `ollama:` hardening for the new speech endpoint.

### Fixed

- Repaired the parent digest query. `learner_activities()` used the `:userid`
  placeholder twice while binding it once; Moodle counts placeholder
  occurrences, so every digest call failed internally and the parent dashboard
  tile could never show data, while the contract-true empty form masked the
  reason. The second occurrence binds its own parameter now, the digest names
  the reason via developer debugging instead of swallowing it, and a derived
  check counts placeholder occurrences against bound parameters across all
  query sites.
- Keep a learner's workshop question out of play until it is released. The
  editor set `status = 'ready'` as soon as validation passed, so an ordinary
  autosave would have made a complete submission playable without any curation
  at all. The state decision now also asks whether the submission is still
  pending, and it asks at the question root, so a new version inherits the
  lock.
- Delete stage reports when the activity itself is deleted. Course reset was
  covered, but `quizgeist_delete_instance()` does not go through reset, so
  personal rows referencing deleted answers would have been left behind. Both
  paths now share one deletion routine.
- Load the clip exception class at all. It promoted a `code` property, which
  `Exception` already declares as non-readonly, so every request that touched
  it died with “Cannot redeclare non-readonly property” — a fatal at load time
  that `php -l` cannot see. The property is now `clipcode`, following the
  established `live_domain_exception` pattern.
- Replace Moodle placeholders in front-end strings. Seven shipped language
  keys use the `{$a->name}` notation, which `get_string()` leaves untouched
  without an `$a`, while the panels only substituted `{$name}`. Learners and
  teachers would have read the raw placeholder on screen; both notations are
  now substituted.
- Repair the vision availability probe. `gpuq_client::VISION_PROBE_PNG` was a
  malformed PNG with an invalid IDAT checksum and an invalid zlib checksum.
  Browsers and GD skip both, so the image looked healthy everywhere it was ever
  opened, while the strict decoder behind the gateway rejected it and the
  gateway reported the rejection as a transport error. A byte-valid 1×1 image
  additionally crashes the model runner, so the probe is now a byte-valid
  64×64 image.
- Add the missing `tests/generator/lib.php`. Eight test files ask Moodle's data
  generator to create a `quizgeist` module, which Moodle refuses with a
  `coding_exception` when the plugin ships no generator — none of those tests
  could ever have run.

## [1.1.0-rc.1] - 2026-08-01

### Added

- Introduced the Moodle subplugin type `quizgeistaddon` and the five optional
  components Fragetypen Plus, Spielmodi & Motivation, Selbstlernen & Üben,
  Berichte Pro, and KI-Werkstatt.
- Added a single server-authoritative feature gate that combines installed
  addon code with locally verified entitlements.
- Added strict offline Ed25519 licence verification, canonical JSON and
  instance binding checks, atomic revision-aware licence storage, MUC caching,
  and an administrator upload/status page.
- Added staged P10 acceptance coverage for the base package, addon lifecycle,
  expiry, tampering, missing-addon backup/privacy, and the full P9 regression.

### Changed

- Kept the complete free loop — authoring, classic live play, Kahoot import,
  session report and CSV — available without addons or a licence file.
- Moved self-study request handlers and the complete AI workshop implementation
  into their respective addon components. Addon providers now extend the base
  registries through Moodle component discovery.
- Hide uninstalled capabilities from authoring and navigation surfaces. An
  installed but unentitled addon fails closed for new premium content with a
  clear local status.
- Preserve existing premium questions, sessions, assignments, reports and
  exports in grace, read-only, missing-file and invalid-file states; only
  creating new premium content is denied.
- Explain a missing managed Quizgeist AI server clearly while retaining the
  established rule-based fallback and a respectful reference to Moodle's
  built-in AI configuration path.

### Fixed

- Label the base report source filter as “Sessions” when the self-study addon
  is not installed, while retaining “Sessions and assignments” with the addon.
- Preserve complete Kahoot imports byte-for-byte across same-course activity
  restore while still rewriting the documented destination provenance.
- Diagnose the first failed restore-consistency subcondition without exposing
  source data, and quarantine restored imports only when destination content
  or provenance media is demonstrably incomplete.
- Compare restored Moodle file sizes numerically, avoiding false restore
  failures when the database driver returns a numeric string.
- Restore the append-only answer ledger unchanged. A restore no longer clamps a
  `scorevoid` compensation row to zero points and no longer raises the
  deliberate `maxpoints = 0` of speed-scored live answers to the points earned.
  Restored session reports, CSV exports and player totals therefore keep the
  figures of the original activity.
- Accept an addon that declares no question type and no AJAX action. Returning
  an empty declaration map is the documented way to say “nothing to add”, but
  the registry rejected it as a malformed list and aborted every request as
  soon as more than the question-type addon was installed.
- Break unbreakable question text on narrow screens. A single long token in a
  teacher's question — a URL, an identifier, a long compound — defined the
  intrinsic minimum width of the live host question screen and stretched
  header, question, response and control bar to more than twice the phone
  canvas, so the host stage scrolled sideways while the page itself still
  reported no overflow. Host question text, host reveal question, player
  question title, self-study question title and live slide titles now wrap
  such tokens.
- Keep the live host screens inside their stage on narrow canvases. Three
  independent causes produced the same sideways scroll: host and player top
  bars whose brand, phase chips, score chip and sound control could not shrink;
  answer-distribution and ranking rows with fixed `rem` minimum columns of
  400 px and 464 px on a stage roughly 260 px wide; and a podium name held at
  224 px by `max-width: 14rem` while its place was 92 px wide. The rows now
  stack, the bars wrap and the podium name is bounded by its place. Setup and
  summary screens stack by container width instead of window width, so a narrow
  stage inside a wide window no longer overflows.
- Let the server own the pedagogical difficulty marker in reports. The report
  screen recomputed `difficult` from the graded count and a hard-coded 40 %
  threshold, ignoring both site settings the reports addon exposes
  (`report_difficult_threshold` and the evidence floor
  `report_difficult_min_sample`). Administrators could change either setting
  without any effect on screen, questions with too thin a sample were flagged
  as difficult, and the screen contradicted the DTO and the XLSX export of the
  very same report. Worse, `report_service::apply_feature_visibility()` clears
  the flag when the reports addon is absent, so the recomputation showed a paid
  feature in the free base package. The screen now renders the flag the server
  sends.
- Mark every defect of a generated question already on the AI workshop card.
  The card derived its warnings from the shape normaliser alone, while the
  editor judges a stored question with the shape normaliser *and* the media
  validation. A generated question with an empty answer option, or an image
  question type without the image a model can never supply, therefore reached
  the teacher looking flawless, was pre-selected for confirmation, and only
  admitted its defect once it had been imported. Such a question was never
  playable — it stays a draft, and both live sessions and self-study
  assignments refuse it — but the workshop no longer stays silent about it.
- Make the licence administration page reachable at all. `licence.php` called
  `admin_externalpage_setup()` without including `lib/adminlib.php`, which
  Moodle does not load on ordinary module pages. Every single request to the
  page died with `Call to undefined function admin_externalpage_setup()` and
  returned HTTP 404, so no paying school could upload a licence file and no
  addon could be activated through the interface at all.
- Name every core library whose symbols the plugin uses instead of inheriting
  it by luck from a caller. `classes/form/media_form.php` and
  `classes/form/ai_source_form.php` extend `\moodleform` and now include
  `lib/formslib.php` themselves — autoloaded on their own they died with
  `Class "moodleform" not found`. `view.php` and both self-study attempt
  services include `lib/completionlib.php` before using `completion_info` and
  `COMPLETION_UNKNOWN`, `quizgeist_pluginfile()` includes `lib/filelib.php`
  before `send_stored_file()`, and the AI slide importer includes it before
  `file_get_unused_draft_itemid()`.
- Show readable sentences instead of machine codes. The AI workshop card
  rendered every validation finding as `field: code` — the server never sends a
  message field, so the raw branch always won and a teacher read
  `options.answers: exactly_one_correct` where the regular editor beside it
  already showed “Markieren Sie genau eine richtige Antwort.” The same card
  listed import hints as bare snake_case (`pdftotext_unavailable`,
  `blank_slide_7`); the server even enforces that shape by regular expression,
  so there was no text to show at all. Hints are now resolved by family, which
  keeps a future code out of the interface instead of only covering today's
  vocabulary. Kahoot source failures, the report moderation trail, the
  Quizgeist question type column and the licence lock on saving activity
  settings — which showed the developer sentence “Quizgeist feature modes is
  read_only.” — read as German or English sentences as well.
- Stop falling back to the string key itself when a language string is missing.
  Every surface resolved an unknown key to the key, which is how a raw
  identifier reached the screen in the first place; the learner view had no
  protection at all. A missing key now yields a readable fallback in the
  interface and names the key in the browser console, so diagnosis stays
  possible without putting identifiers in front of a class.
- Deliberately unchanged: CSV and XLSX exports keep their machine tokens
  because they are an interface for other software, the licence diagnosis keeps
  its code for support because only administrators see that page and every
  emitted code is translated, and the Kahoot report keeps its error-code line
  next to the German explanation.

### Security

- Reject oversized, ambiguous, non-canonical, incorrectly signed, revoked,
  instance-mismatched and revision-conflicting licence files without replacing
  the last accepted file.
- Keep the normative licence test key confined to tests. The production
  keyring intentionally fails closed until the licensing workstream supplies
  the real release trust anchor.

## [1.0.0-rc.1] - 2026-07-29

### Changed

- Published the first externally named release candidate with consistent
  Moodle installation metadata and a fresh browser-asset cache key.
- Added deliberate local system-font fallback stacks for every theme. Quizgeist
  still ships and fetches no font files, while optional site-local families can
  retain the intended branded typography.
- Added a site-wide, enabled-by-default control for the optional server-side
  URL and German Wikipedia sources, including a clear disabled-state notice in
  the AI workshop.
- Made the installation guidance portable across standard Moodle layouts and
  clarified the actual eligibility rules for assignment deadline reminders and
  the plugin's optional outbound connections.
- Localised CSV and XLSX column headings for German and English sites while
  retaining the established spreadsheet format.

### Fixed

- Included the final host-podium contrast polish in a versioned, cache-safe
  release.
- Restored correct-answer markers in protected teacher report distributions
  while keeping exported report content solution-free.
- Corrected singular course-star wording in both language packs.

## [0.8.5-reviewfix12] - 2026-07-29

### Fixed

- Sized the mobile host answer response from its constrained content grid
  track so answer tiles remain inside the narrow question canvas.

## [0.8.4-reviewfix11] - 2026-07-29

### Fixed

- Kept the live response within the narrow host-question canvas on mobile
  screens.
- Raised scale-aggregate bars to an AA-compliant, theme-stable strong-color
  pairing.

## [0.8.3-reviewfix10] - 2026-07-29

### Fixed

- Kept deadline reminders available to eligible participants without a group
  when an activity uses separate groups.
- Raised the light-surface secondary-text token to the AA-compliant
  `ink-700` value across reports, the editor, and live presentation screens.
- Kept the visible host lobby and question controls within narrow mobile
  canvases instead of positioning them beyond the horizontal viewport.

## [0.8.2-reviewfix9] - 2026-07-29

### Fixed

- Removed solution material and live tokens recursively from question and
  aggregate DTOs at the report distribution boundary, keeping teacher report
  payloads and CSV/XLSX exports solution-free.
- Raised the host PIN-card hint to the AA-compliant `ink-700` design token.

## [0.8.1-reviewfix8] - 2026-07-29

### Changed

- Made Kahoot retries atomic, capability-checked across affected activities,
  crash-recoverable through one reserved hidden target, and isolated per
  source with concrete solution-free failure reports.
- Added native mappings for open-ended, word-cloud, brainstorming, slider, and
  content-slide records; aligned import question and upload limits with the
  editor and bounded one-at-a-time media staging.
- Memoized repeated live-report projections and split restore reconciliation
  and report metrics into focused internal services without changing public
  entry signatures.
- Derived Geistopia progress only from activities visible and accessible to
  the current user, removed its unused shared cache, and narrowed report user
  reads to the fields actually rendered.

### Fixed

- Acquired content locks before importer transactions and moved visibility and
  course-cache publication after a successful commit.
- Kept import history visible after a batch, surfaced partial failures in web
  and CLI output, corrected CLI documentation, and documented the intentional
  omission of media bytes from report payloads.

## [0.8.0-p8] - 2026-07-29

### Added

- Audited Kahoot export migration from JSON or ZIP, available to teachers in
  Moodle and to administrators through a command-line script.
- Persistent import provenance, idempotence by source UUID, media verification,
  and a per-quiz report of retained, adapted, and omitted content.
- A lightweight course-wide Geistopia roadmap that derives shared decoration
  unlocks from completed-session stars without a second personal currency.
- Public distribution documentation and a Moodle Plugins Directory description.

### Changed

- Finalised the public product and component name as Quizgeist
  (`mod_quizgeist`) before external distribution.
- Corrected report access, group scoping, headline metrics, caching, and
  spreadsheet export behaviour found during the P6 review; large distribution
  exports now use a bounded temporary spool and native browser downloads.
- Localised every site-administration setting in German and English.
- Standardised German UI address: students are addressed informally, while
  teacher and administration surfaces use formal or role-neutral wording.
- Promoted the plugin metadata to beta maturity for the final build before
  acceptance.

### Fixed

- Preserved opaque, import-independent identifiers for ordered puzzle items so
  answer order cannot be derived from player payloads.
- Applied the normal editor media checks to imported files, including MIME and
  extension validation, upload limits, and SVG sanitisation.
- Bound CLI media limits to the actual target course and made restored import
  provenance fail closed on missing, unsafe, or checksum-mismatched media.
- Preserved fully functional Moodle activity copies without duplicating the
  canonical course-wide Kahoot UUID.
- Removed the obsolete initial scaffold and unused language strings.

## [0.7.0-p7] - 2026-07-29

### Added

- Teacher-reviewed AI workshop through the local GPUQ gateway, with a
  deterministic rule-based fallback.
- PDF and PPTX slide import, document-to-question workflows, URL and Wikipedia
  sources, and optional handwriting recognition.
- Question explanation drafts and optional text-to-speech through
  `local_voces`.

## [0.6.0-p6] - 2026-07-29

### Added

- Session, assignment, combined, and course reports.
- Question distributions, participant metrics, difficult-question indicators,
  and review views for open responses.
- Authorised CSV and XLSX exports.

## [0.5.0-p5] - 2026-07-28

### Added

- Self-paced assignments, solo practice, practice tests, and flashcards.
- Deadlines, bounded reminders, weekly learning goals, Moodle completion, and
  gradebook integration.
- Backup, restore, reset, and privacy handling for self-study data.

## [0.4.0-p4] - 2026-07-28

### Added

- The complete set of interactive question and content types.
- Accuracy, team, and security modes, including Moodle-group teams.
- Local sound, presentation effects, avatars, and participation rewards.

## [0.3.0-p3] - 2026-07-28

### Added

- Server-authoritative live sessions with lobby, join code, host controls,
  reconnect support, scoring, scoreboard, and podium.
- Adaptive AJAX polling without WebSockets.

## [0.2.0-p2] - 2026-07-28

### Added

- Full question editor, validation, autosave, previews, themes, and local media.
- School-wide template publishing, search, and import.

## [0.1.0-p1] - 2026-07-28

### Added

- Moodle activity foundation, capabilities, routing, database schema, events,
  completion hooks, privacy provider, backup and restore, and frontend toolchain.
