Skip to content

2. Functional requirements

Every requirement is identified F-AREA-nn, with its original planning phase P1 / P2 / P3 and priority M / S / C (must / should / could). These labels are not implementation status; the original phase plan is history. A requirement must be verifiable by a test or a demonstration.

F-AUTH Authentication and accounts

Id Requirement Phase Prio
F-AUTH-01 The user signs in through edu-ID with OpenID Connect. No local password. P1 M
F-AUTH-02 On first sign-in, the account is created with name, email and a role derived from the edu-ID affiliation: a staff affiliation scoped to one of our institutions (STAFF_AFFILIATION_DOMAINS) gives teacher, unless a student affiliation of one of those institutions blocks it (a student elsewhere does not); anything else — another institution's staff, an unscoped staff — gives student. The default lists heig-vd.ch and hes-so.ch, because edu-ID releases HEIG-VD staff as staff@hes-so.ch; as an accepted consequence, the staff of the other HES-SO schools qualify too. The role is recomputed at every login by the same rule as any later change (roles.ts). Whether the account is created at all is F-AUTH-06. P1 M
F-AUTH-03 The admin is identified by a list of edu-ID identifiers in the configuration. They may promote or demote a user to teacher. P1 M
F-AUTH-04 The session persists for 30 days. An explicit sign-out is available. A running exam never asks to sign in again. P1 M
F-AUTH-05 A participant without an account may join an evaluation in poll mode through a session code, anonymously: no pseudonym is asked for nor stored (see 06 no. 18). Only an anonymous poll, which belongs to no classroom, takes such a participant; a classroom's poll admits its roster and its staff, signed in (ADR-014, addendum 2026-09-27). P2 M
F-AUTH-06 Admission (ADR-111): when LOGIN_DOMAINS lists institutions, an account is created at a first sign-in only for a member of one — an edu-ID affiliation scoped on a listed domain (never an unscoped one), or a verified address at one, matched exactly — for a person invited by a pending roster line or an admin's teacher grant on one of their verified addresses, and for the super administrator. An existing account, and a login that matches a heig-classroom placeholder account (ADR-061, adopted or flagged ambiguous), sign in whatever the list. When edu-ID's userinfo fails, a newcomer the ID token alone does not admit is refused and asked to retry. A refusal writes nothing and lands on a public page that names no institution. Empty (the default) admits everyone. eduid.ch is refused at startup. The staging allowlist (ADR-028 §3) is checked as well; the super administrator passes both. P2 S

F-ORG Courses, classrooms, rosters

Id Requirement Phase Prio
F-ORG-01 A teacher creates a course with a name and a short code. They may add other teachers to it. P1 M
F-ORG-02 A course references one or more pools. The course's evaluations draw from these pools. P1 M
F-ORG-03 A teacher creates a classroom in a course with a name and a period: a free label and, optionally, a first and a last month (both or neither, the last not before the first). The form offers the current-or-next semester and the one after as presets and prefills the first; a preset fills the label only while it is empty or still the previous preset's name. The sidebar's flat list of classrooms shows those not archived, of a course the teacher has not hidden (F-ORG-11), whose period covers today (the browser's local date), with one month of margin on each side; an undated classroom always shows. The course cards, the course tree, the palette and the student home list every non-archived classroom. A classroom may be archived: it leaves the course list, the sidebar and the palette, and stays reachable from its course behind "Show archived". P1 M
F-ORG-04 The roster is imported from CSV with at least the edu-ID email. Lines already known are merged, never duplicated. P1 M
F-ORG-05 A student whose email appears in a roster is attached to the classroom at their sign-in or their own next request, never by a staff action (ADR-053 addendum). Until then the line is pending and the roster shows nothing of any account. P1 M
F-ORG-06 Removed (ADR-053): no classroom join code; a student enters a roster only through the teacher (import or manual add) and is matched when they act (F-ORG-05). — —
F-ORG-07 Every roster line carries an extra time in percent, 0 by default, and a free note. It never applies to a project's deadline (D13): a project repository gets an individual deadline from its staff instead (F-PROJ-09, D13 as amended 2026-10-02). P1 M
F-ORG-08 The teacher may remove a student from a roster. Their past attempts are kept. P1 M
F-ORG-09 Deleting a classroom deletes its evaluations, attempts, answers and gradings after a confirmation that names the classroom. The questions of the pool and the course's evaluation templates are not affected: a template belongs to the course and survives every classroom (ADR-031). Deleting a course deletes its templates, and its confirmation names how many. The classroom's GitHub link and its journal go with it: a journal in Quiz is deleted with its pages, revisions and assets, and the confirmation says so with the number of pages (when there are pages, the teacher types the classroom's name); for a journal in a GitHub repository only the copy goes, the repository is kept (F-JRN-04, ADR-057). Its projects go too, their rows only: no repository is ever deleted on GitHub, and the confirmation says so (F-PROJ-16, D19). P1 M
F-ORG-10 A classroom may be duplicated to a new period, without roster nor attempts, with its evaluations as drafts. The copy's months are shifted by one semester (shiftSemester in packages/domain): a period that is exactly a semester becomes the next one (autumn 2026 → spring 2027), any other moves six months; an undated classroom stays undated. P2 S
F-ORG-11 A teacher (or an admin) may hide a course they reach from their OWN navigation — the course list, both sections of the sidebar and the command palette — and show it again. Hiding is per user: the other staff members see nothing change, and nothing about the course changes either (classrooms, evaluations, pools). Pickers and the MCP list_courses keep listing a hidden course, and the course list brings hidden courses back behind "Show hidden" (#155, ADR-032, 06 no. 26). P1 S
F-ORG-12 Every course has a page of its own (/courses/:id), reached from its name on the course list (card and table), from its row in the sidebar's course tree and from the command palette ("Go to the course"). It is in tabs, as the classroom's (F-ORG-13), each a path of its own (/courses/:id/<tab>): Classrooms, the default (the archived ones behind "Show archived"), Templates (F-EVAL-19), Linked pools (link and unlink), Members (the staff, each with their role — shown as Teacher (the stored role stays owner, ADR-068) or Assistant; product owner, 2026-10-10) and Settings. The Templates tab has an empty state that names the way to create one ("New template", "Save as template" in an evaluation's menu); the course list's card no longer lists templates, so the page is their one surface. The header's one primary action is the open tab's — New classroom, New template, Link a pool, Add a staff member — and Settings has none. Settings holds, in settings rows (D24), the name and the code (a code another course holds is refused, 409 duplicate_code), hiding the course (F-ORG-11) and deleting it (F-ORG-09), which returns to the course list; the header shows the name and no longer edits it. Settings also holds the course's Concepts ("Notions"; ADR-081, sixth addendum): a plain list of the concepts the course teaches, sorted by label, edited with the question editor's concept picker (a proposed concept may be listed, and a new one created there). Only an owner replaces the list (403 owner_required); every member reads it (concepts in GET /courses/:id, so the MCP get_course and the teacher assistant read it); it is never part of a student payload. A concept a course lists cannot be deleted (409 concept_in_use), and a merge of concepts replaces it by the target in the course's list. Audited as course.concepts_update. Every staff member reaches the whole course (D04), with a role (ADR-068, product owner, 2026-10-04): an owner also adds and removes members and changes their roles, renames and deletes the course, links and unlinks its pools, creates and deletes its classrooms, and releases, withdraws and publishes the correction of its evaluations — and, since 2026-10-10 (ADR-068 §3), deletes an evaluation that is released, has its correction published or holds a student attempt (a poll: once released), and changes any grading of a released evaluation; an assistant does everything else, and is offered none of those actions (the server refuses them, 403 owner_required). A new member is an assistant unless the Add-staff form says owner; an account already on the staff is refused (409 already_staff). The last owner can be neither removed nor made an assistant (409 last_owner), and the screens offer neither; any member may leave the course from their own row. An admin with Super Powers is an owner of every course. A course the caller does not reach reads as one that does not exist (ADR-031 addendum of 2026-09-28). P1 S
F-ORG-13 The teacher's classroom page has a Settings tab, in settings rows (D24). It holds what the header held — rename, archive, delete (F-ORG-09) and the drill switch (F-DRILL-04) — plus a GitHub section (F-GH-02, F-GH-03) and a Journal section (F-JRN-02 to F-JRN-05), available whether the classroom is connected or not (a journal in Quiz needs no GitHub, ADR-057); the header keeps the name and the period. Its one accent is "Connect to GitHub" while the classroom is not connected, and nothing once it is: a classroom connected for its projects is not pushed towards a journal. A classroom that is never connected is a plain Quiz classroom, and nothing else of the page changes. P2 M
F-ORG-14 The student's Courses entry (sidebar and bottom bar) is a route of its own that lists the student's classrooms — the classrooms where they hold a claimed seat, archived ones excepted — as cards; a card opens the classroom's page (F-ORG-15), and so does every classroom card of the student home (D07). Activities stays the summary of the active activities of every classroom, today's home: what is open now and what is coming, the latter grouped by day — Today, Tomorrow, This week, Later — by the calendar day each activity opens, in the browser's time zone ("This week" runs from the day after tomorrow to Sunday, weeks starting on Monday; an opening already passed is Today, an undated one Later), the soonest first in each, an empty day not drawn (product owner, 2026-10-01; no calendar view until the projects bring deadlines, D07). Grades (sidebar and bottom bar) is a route of its own, /grades: the student's finished work — what the home's "Past" listed, which the home no longer shows — grouped by classroom, the classroom of the newest row first, archived classrooms included and marked; per row the activity, its kind, its date, a status (released, grade not shared — released under the policy none —, results available, results pending, handed in, not taken), and the points and the Swiss grade where F-RES-04 lets the student read them; a "results available" row shows its points marked indicative, and no grade. A row opens the feedback page where it has something to show. No average there unless the teacher publishes the classroom's mean (F-GBOOK-05). Within a classroom the rows come in two groups, named by what they are worth, never by a kind, an empty one not drawn: Count toward your average, the rows that count toward the classroom's mean — the gradebook column's counts, or its kind's default where no column is stored: an exam and a project count, an exercise does not (ADR-074 §6), so an exercise the teacher included goes here —, then Indicative, the rows that do not (an exercise by default, an exam the teacher left out), under one line saying that these grades do not count toward the average unless the teacher includes them; such a grade is written in the ink, never in the fail or borderline colour. The student reads that flag alone, before the mean is published too, never a column's weight (product owner, 2026-10-13, #803; ADR-074 amendment of 2026-10-13). A project row shows under its classroom like an evaluation's once its scores are released (F-PROJ-14). P2 M
F-ORG-15 A student has a page per classroom at /classrooms/:id, the same address as the teacher's, which opens the student's page for a student, for a teacher in the student view (ADR-018) and in an impersonation session (ADR-034). Header: the classroom's name, the course's code and name, the period, the teachers, the student's time bonus. Its tabs are routes: Activities (by default) in three groups, Open now, Upcoming (grouped by day as on the home, F-ORG-14), Past, whose one accent is the button of the single most urgent open activity; Journal only when the classroom has one (F-JRN-07); Groups (/classrooms/:id/groups) only while a group set of the classroom reaches its students, where they form their groups (F-PROJ-22; product owner, 2026-10-05); Grades, the student's own cells of the classroom's gradebook (F-GBOOK-05). A group set open to the students is a row "Form your group until …" in Open now, here and on the home, leading to the Groups tab; it never takes the accent (F-PROJ-22, S3). A published project is listed in Activities like an evaluation, in the group its dates put it in (F-PROJ-04), and counts for the most urgent accent by its deadline. Empty state: nothing to do in this classroom right now. An archived classroom's page stays reachable by its address, as it is. A classroom where the caller is neither on the course's staff nor holding a claimed seat reads as one that does not exist (404). P2 M
F-ORG-16 A course keeps a catalog of conditions (kind and text, as F-EVAL-33), in a section of its page's Settings tab, that the course's owners manage — add, edit, reorder, archive and restore — and its assistants read (product owner, 2026-10-09; ADR-079 §5); an entry is archived, never deleted. The conditions editor of an evaluation or a template offers the active entries of its course as checkboxes: ticking one copies its kind and text there, as a snapshot that later edits of the entry never change, and records which entry it came from; unticking removes that copy. A condition whose entry is archived, or belongs to another course, reads as a one-off condition. Neither the catalog nor that reference reaches a student view (ADR-079 §5, issue #584). P2 S
F-ORG-17 The training course (ADR-105, issue #776): one course per teacher, provisioned on request (POST /app/api/training, a teacher's or an admin's portal session) with the ordinary services and marked courses.training_for (unique: a second call opens the existing one) — one classroom on the current period, no roster, no seat, a read link to the ONE public training pool (pools.is_training, owned by the super admin, its content shipped as code and installed by the admin task training.install), and two evaluations from the content definitions: the welcome quiz, an exercise scheduled until the end of the period with one question per automatically graded type, and the live quiz, a draft in Live exam mode with a rich and a diagram question. Refused 409 training_not_installed while the content is not installed; audited training.provision and training.install. A progress card on the training classroom's Activities tab, for the teacher it was provisioned for only, shows the four missions (be a student, run a class, make it yours, the tools around — the last optional) as todo, done or unavailable, DERIVED from existing tables and never stored (GET /app/api/training); its one accent leads to the next mission's screen and never performs the gesture. Offered from the empty Courses home as a text link under New course (the one amendment of DESIGN.md's empty-state rule), from the account menu and from the palette's Help group (declared with an effect, never run by the assistant); hidden everywhere while the content is not installed. Restart is the course's ordinary deletion by its owner, then a new provisioning. Nothing of it reaches a student. P2 M

F-POOL Pools and organisation of questions

Id Requirement Phase Prio
F-POOL-01 Every teacher has a private pool created automatically and may create others. The personal pool (Polls) is created on first use, by the first question the teacher keeps after a live poll (ADR-014, addenda item 6); a teacher who never keeps one never sees it. P1 M
F-POOL-02 A pool contains hierarchical categories and questions. A question is in a single category or at the root. P1 M
F-POOL-03 The list of questions can be filtered by type, concepts, difficulty, category, free text on the internal name and the statement. A typed #word resolves to every concept it may designate (either language, an alias, the qualified form) and filters on any of them and on their narrower concepts (a selected concept covers its narrower ones, sixth addendum §10); the address carries concept ids (amended by ADR-081, third addendum §7). It can also be filtered by a course linked to the pool that the caller staffs (course: in the search box; sixth addendum §8): the questions on the concepts the course lists and their narrower ones. P1 M
F-POOL-04 A question may be copied to another pool. The copy references its origin. P1 S
F-POOL-05 A pool may be shared with other teachers with a reader, contributor or owner role. P2 S
F-POOL-06 A public pool is readable by every teacher. Who writes in it is decided by its owner through members and linked courses (ADR-013 §4, superseding "the admin designates its contributors"; open question 9). Publishing is the owner's switch (F-POOL-12), the catalogue and subscriptions are F-POOL-13. P2 S
F-POOL-07 A pool exports as an archive of YAML files in the canonical format, one file per question, the category tree as folders. Importing this archive recreates the pool or merges into an existing pool by id. P1 S
F-POOL-08 Import of GIFT and Moodle XML files for the supported types. Questions that cannot be converted are listed with the reason. P2 C
F-POOL-09 Deleting a pool is refused while an evaluation or an evaluation template pins one of its question versions; the refusal names the holders the caller can open and counts the others (ADR-031). P1 M
F-POOL-10 A teacher stars questions of any pool they can see — a reader and a public pool included — as personal favourites: a colleague never sees nor clears them, and nothing is audited. On the pool screen, a star on each row and card, Space on the focused one, "Star" in the bulk bar and "Clear favourites" (this pool, after a confirm that counts them). In the question picker of an evaluation or a template, the starred questions of the shown pool come first, and "Add favourites" adds every one that can be added, says what it skipped and why, then offers to unstar them. A star follows its question when it is moved to another pool; a copy starts without one; a soft-deleted question's star stays hidden until it is restored (ADR-040). P1 S
F-POOL-11 Report a question (issue #680, level 1). Anyone who can read a question in the pool screen — a reader, a public reader, the staff of a linked course — reports a problem with it, with a message of at most 1 000 characters, from the question's menu; never a student nor a guest (a teacher route behind poolAccess, nothing of it reaches an attempt or toStudent). The pool's writers (owner, contributors) read every report of their pool; a reporter reads their own. A question with an open report shows an indicator on its row and in its header, beside (never merged with) the findings of the nightly review (ADR-060), and its Reports tab lists them. A writer — or an admin under Super Powers, which covers a public pool left without writers — resolves a report with an optional reply. Reports travel with a moved question (ADR-017); the open ones of a soft-deleted question close with it. Audited: question.report, question.report_resolve. Suggested edits are deferred. P2 M
F-POOL-12 Derived visibility, publication and description (issue #680, lot 1; ADR-013 decisions 8 and 9). The owner decides one thing, whether the pool is published (a switch; never the personal pool); the displayed visibility is derived: public if published, shared if it has a member or a linked course whose staff includes someone other than the owner, private otherwise. A public pool offers contributor and owner only when inviting. The card shows the visibility badge bottom-left, a description of at most three lines and, on a pool that is not yours, its owner beside the name. The owner writes a 280-character description in Settings and may accept an AI proposal; nothing is written without that act. Audited: pool.publish, pool.unpublish; an accepted description is a pool.update. P2 M
F-POOL-13 Public pool catalogue, subscriptions and read-only links (issue #680, lot 2; ADR-095). "My pools" lists the pools a teacher owns, sits on, reaches through a linked course or subscribed to; a public pool they have none of those ties to is read and searched but not listed. The pools page has a catalogue panel of public pools only (never widened by Super Powers), searched over name, description, inferred domain and the concept labels (French and English) of the published questions, ranked by subscribers plus members, then use. Any teacher subscribes to a public pool (never a member row); the owner and the members read the subscribers by name, and cannot remove one; a public card shows a users counter. A course owner links a public pool to a course read-only without subscribing (its staff are readers; an edit link keeps making them contributors, and upgrading needs contributor access); PUT /courses/:id/pools carries each link's mode, keeps existing modes and lets the stronger win. Unpublishing first counts the subscribers, the read-linked courses and their templates that use the pool, then drops the read links and subscriptions, notifies each and revokes their streams. The pool's domain is a short bilingual label inferred from its concept labels on publication and nightly. Audited: pool.subscribe, pool.unsubscribe; course.pools_update records the modes. P2 L
F-POOL-14 The pool's Concepts tab (ADR-081 third addendum §6, ADR-116). Read-only: it lists the concepts the pool's live questions use, as a table or as a map of the vocabulary's hierarchy, one level per view. A concept's number is what its click opens: the live questions linked to it or to any of its narrower concepts, counted once. A click opens the Questions tab filtered on that concept; in the map, a concept with narrower ones zooms in first. Read by whoever reads the pool (owner, members, the staff of a linked course, any teacher on a public pool, the admin with Super Powers), never by a student. P2 M

F-QST Questions and versions

Id Requirement Phase Prio
F-QST-01 A question carries a type, an internal name visible to the teacher only, its concepts — taken from the vocabulary shared by the whole instance, never free strings — and a difficulty from 1 to 5 (amended by ADR-081). On demand and only with a model configured, the editor suggests up to five existing concepts and two new labels for the open draft; the teacher ticks, and a new label goes through the explicit create form (ADR-081 sixth addendum §6). P1 M
F-QST-02 The editor works on the draft. Every change is saved automatically. P1 M
F-QST-03 Publishing validates the draft against the type's schema and creates version N+1 with an optional change note. The draft stays equal to the published version until the next change. P1 M
F-QST-04 The version history can be browsed with a diff of the configuration. A version may be restored into the draft. P1 S
F-QST-05 A version may be marked deprecated with a reason. The evaluations that use it show this to the teacher. P1 S
F-QST-06 The statement and the explanation are markdown with a WYSIWYG editor: headings, bold, lists, code, pasted or dragged images, inline and block KaTeX equations. P1 M
F-QST-07 Pasted images are stored on the server and referenced by a stable id. The canonical export embeds them. P1 M
F-QST-08 A question declares whether it can be shuffled and whether it supports random values. P1 M
F-QST-09 The editor offers a preview of the player exactly as the student will see it, with the possibility to answer and to see the grading. P1 M
F-QST-10 A question with random values (parameterized) declares variables as one-line expressions with a format, and an optional condition. [[expr]] in the statement, the key and the explanation is evaluated with values drawn from the attempt's seed, stored the first time the item is served. See 04-types-de-questions.md §4.3 and ADR-056. P2 M
F-QST-11 Deleting a question hides it from the pool. It is still resolved by past evaluations. Permanent deletion is possible only when no evaluation references it. P1 M
F-QST-12 A math question takes a formula typed in a math field and accepts it in any form that takes the same values as an accepted answer at sample points drawn in the declared variables' intervals; the field's rules are told while typing, and an optional graph draws the expression. See 04-types-de-questions.md §4.16 and ADR-102. P2 M

F-EVAL Configuring an evaluation

Id Requirement Phase Prio
F-EVAL-01 The teacher creates an evaluation in a classroom with a title, a mode exam, exercise or poll, and an ordered list of questions taken from the course's pools. The mode of an exam or an exercise stays editable until students are let in (F-EVAL-24, F-EVAL-25; ADR-092, 2026-10-09). P1 M
F-EVAL-02 Every item carries a number of points. By default: 1 for the simple types, the number of test cases for code, the number of blanks for cloze. P1 M
F-EVAL-03 Adding a question freezes its current published version. The evaluation shows when a more recent version exists and offers "Update" item by item or globally, as long as no attempt exists. P1 M
F-EVAL-04 Time settings: duration per-student duration from their start, deadline common end time, manual closed by the teacher. The teacher answers one question, who drives the clock — Scheduled, labelled Automatic (« Automatique », product owner 2026-10-14: the platform opens at opens_at and closes at closes_at, no waiting room) or Live (« En direct »: the teacher opens the waiting room, starts and closes; the date is a calendar hint only) — and one switch, a time limit per student, at most 8 hours (product owner, 2026-10-09); both are derived from and written to timing and lobby, never stored. A manual evaluation may carry an optional safety deadline in closes_at, which the ticker enforces like a common end; a duration attempt is cut at closes_at when there is one, even with time left. An exam must end by itself: duration, deadline, or manual with its safety deadline. exercise has a due date (amended by ADR-086, 2026-10-08). P1 M
F-EVAL-05 The roster's extra time applies to duration; when a closes_at cuts the duration, it is added after the cut, so it carries the student past that end (ADR-086 §3). In deadline mode it extends the individual end beyond the common end, by a share of the announced window (closes_at − opens_at as the teacher set it, decision D8): time added to everybody during the run — an extension to all, a pause — moves the common end and never grows that share, for a late starter or a reopened attempt alike. The announced window follows the teacher's timing edits while the configuration is editable, and is frozen once it locks (F-EVAL-03) (#253). P1 M
F-EVAL-06 Waiting room: skip the student starts alone, auto start when everybody present is there, manual the teacher starts. A Scheduled evaluation has none (skip); a Live one starts at manual, auto and (without a time limit) skip being advanced options (ADR-086). P1 M
F-EVAL-07 Navigation: free, forward_only, milestones with a flag per item beyond which there is no going back. P1 M
F-EVAL-08 Presentation: zen one question per screen, continuous scrolling, student_choice only with free navigation. P1 M
F-EVAL-09 Shuffling: question order yes / no, choice order yes / no for shuffleable questions. The order is derived from the attempt's seed and stable for a student. P1 M
F-EVAL-10 Grade scale: grade = 1 + 5 × points / total, capped at 6, rounded to the tenth, configurable method: nearest by default. An item may be marked bonus in the builder: its points are left out of the total, so they can only lift a student (a score may read "21 / 18"); under negative marking its score is floored at 0; the flag is locked like the points, and an exam or exercise whose total is 0 is not opened (no_graded_points). The threshold scale is gone (ADR-052). Amended 2026-10-12 (product owner, ADR-108): an item holds ONE counting state — normal, bonus or neutralized (out of the total and out of every student's points); the builder still offers only normal ↔ bonus, the grading panel changes it once the list is frozen (F-GRADE-10); a bonus item's negative points are floored when the student's points are summed, never by rewriting a grading; the total leaves out bonus and neutralized items. P1 M
F-EVAL-11 Feedback: none, on_release after the teacher releases the results, immediate once the student hands the attempt in (per hand-in, not per question: ADR-067, question 50), allowed for a poll or an exercise without a waiting room; refused for an exam or an exercise with an auto or manual waiting room (isFeedbackAllowed, open question 5, settled). The feedback includes or not the answer key and the explanation, two distinct options. A correction published while an exercise runs counts as the release for on_release (F-EVAL-27). P1 M
F-EVAL-12 An evaluation may restrict IP addresses to a list of IP addresses and CIDR ranges, matched on the address's bits (amended 2026-10-11, security audit L10: text prefixes are refused; a stored legacy prefix of whole IPv4 octets, 10.20., reads as its range, 10.20.0.0/16; an IPv4-mapped IPv6 address is its IPv4). The access code a student used to type is removed: a code on the board proves no presence (ADR-053); access_code is a poll's session code only (ADR-014). P1 S
F-EVAL-13 An evaluation may enable the recommended full-screen mode and the integrity journal (logVisibility): leaving the page (tab hidden, window switched) is recorded; so is pasting a text of at least 20 characters not copied on the page (in the same tab), with the paste's length and whether the page had just lost the focus (derived by the server), never its content. On by default for an exam, off for an exercise (the teacher may switch it on); a poll never journals. The server stores nothing when it is off, nor from a delegated (impersonation) session; a seb or kiosk session journals like the portal. The student is told by the conditions (F-EVAL-33) and, outside SEB and the kiosk, by a toast when it happens (at most one per kind every five minutes); the teacher sees a discreet list (student, time, kind). Nothing is blocking, and the journal is a hint, never proof. It is deleted at the release of the grades, or six months after the close of an evaluation never released (N-DATA-03; amended by ADR-088, 2026-10-09). P2 S
F-EVAL-14 An evaluation can be duplicated to the same classroom or to another one, provided the target course links the pools of all its questions (F-EVAL-01). P1 S
F-EVAL-15 An exercise evaluation may allow several attempts, keeping the best or the last one (the teacher's choice), with an optional maximum. A retake starts blank, with a new question order, once the previous attempt is handed in and while the evaluation is open; the student sees the score of each attempt, not the correction, until the evaluation closes — or until the teacher publishes the correction (F-EVAL-27). The teacher may let a retake ask only the questions to review (retakes.scope: to_review, free navigation only, 422 retake_scope_navigation otherwise): the questions at full marks in the latest attempt (or worth 0) are carried over read-only with their answer and validated grading into a new whole attempt, which counts against the maximum; between two attempts the student then also reads each question's standing — acquired, to review, awaiting correction — and nothing else of it, whatever the feedback policy, and chooses between Redo the questions to review (n) and Redo everything (ADR-091, product owner, 2026-10-09). Results (their per-question statistics included), grade and CSV use the kept attempt; the pool's question statistics count exams only (F-STAT-01). An exam keeps one attempt (ADR-025). P2 C
F-EVAL-16 An exam or exercise evaluation may use negative marking: every choice question is then scored so that a wrong answer costs points (single answer: +1 or −1/(n − 1); several: c/C − w/W, not floored) and no answer costs nothing. Per-question points may be negative; the evaluation total is floored at 0 and the grade computed from it. The student is told in the waiting room — among the conditions (F-EVAL-33) — and on each choice question. Frozen once an attempt exists (ADR-026, issue #130). P1 S
F-EVAL-17 An exam may require Safe Exam Browser. The student's card then downloads a one-time .seb that opens the exam in SEB under their identity, with no second sign-in; the session so opened reaches that exam only, and a portal session cannot sit the exam (ADR-027, issue #139). While sat in SEB, every request of the session carries SEB's Config Key header, checked against the session's own file (ADR-051 §3). Since SEB may begin the attempt directly (ADR-076 §4), the dialog before the download states the exam's conditions (F-EVAL-33) above its one action, the list scrolling and the action staying in view on a phone (amended by ADR-079 §7, 2026-10-07). P2 S
F-EVAL-18 An exam or exercise evaluation may be saved as a template of its course. The template keeps the items (frozen versions, points, order, milestones, bonus flags), the settings, the grade scale, the feedback policy, the MCQ policy and the duration, and drops the opening and closing dates and the IP list. A poll cannot be a template (422). The template starts at revision 1, and the source evaluation is linked to it at that revision, as an instance of it (F-EVAL-26 then applies); a source that was already an instance of another template is relinked, and the origin it loses is recorded in the template.create audit entry (previousOrigin). Offered in the evaluation's menu and in its row menu of the classroom's list (ADR-031, issue #151; addendum of 2026-09-30). P2 S
F-EVAL-19 The course lists its templates to every member of its staff, with their mode, number of questions, points and revision; anyone else gets the 404 of a missing template. A template is never opened, answered, graded, nor listed among a classroom's evaluations. P2 S
F-EVAL-20 Instantiate makes a draft evaluation in a classroom of the SAME course from a template, recording the template and its revision. A question whose pool is no longer linked to the course blocks it with the list of such items; a deprecated version only warns. An instance with deadline timing is born without dates and cannot be opened until they are filled (F-EVAL-04). P2 S
F-EVAL-21 Deleting a template leaves its instances untouched; they only lose their link to it. P2 S
F-EVAL-22 At evaluation creation, "Start from a template" is offered only when the course has at least one template; otherwise the creation dialog is unchanged (08). P2 S
F-EVAL-23 The last configuration step is a pre-flight checklist: what the API would refuse (no question, incomplete timing) blocks the launch; stale question versions, a template that moved since (F-EVAL-26), an empty roster, roster conflicts and a common end already past are warnings, each with its fix; the rest of the rules is shown for information, the number of conditions announced among them (F-EVAL-33) — never a warning when there is none —, beside a preview of the waiting room as the students will see it, conditions included. From there the teacher opens the waiting room now — the evaluation itself when it has none (lobby: skip) — or schedules the opening: a scheduled evaluation carries an opening time (opensAt), the move to scheduled is refused without one, and the ticker opens it at that time (issue #152). P1 M
F-EVAL-24 From the course page (F-ORG-12), a member of the course's staff creates an empty template: a title, a mode (exam or exercise) and a preset, exactly as for an evaluation's creation but in the course. A poll is refused (422 template_poll). The dialog that starts an evaluation from a template shows the template's mode, read-only (ADR-092, 2026-10-09). The template starts at revision 1 with no question, and is filled in place (F-EVAL-25). Audited as template.create, without from (ADR-031 addendum of 2026-09-28). P2 S
F-EVAL-25 A template is edited in place by any member of its course's staff, through routes of its own (/templates/:id/...), never through an evaluation's: its title; its mode (exam or exercise, always changeable on a template; the change moves the revision and rewrites nothing but the forced consequences of ADR-092, 2026-10-09); its items (add from the course's linked pools only, remove, reorder, points, milestones, update to the latest published version); its settings, grade scale, feedback policy, MCQ policy and duration, under the same rules as an untouched draft. Nothing of a run is editable: a patch carrying opensAt, closesAt, ipAllowlist, accessCode (which no evaluation has any more, ADR-053) or any unknown top-level field is a 400 (an unknown key inside settings or feedbackPolicy is dropped, as on an evaluation). Every committed request that changes the content (anything but the title) moves the revision by exactly one — "update all to the latest version" included — and a title-only or no-op request does not; each write is audited as template.update with what changed and the revision. The editor shows, per item, a newer published version (stale), a deprecated version (which Instantiate warns about) and a pool no longer linked to the course (which blocks it). Instantiate copies the template under a lock, so the revision it records is the one it copied. Instances are never changed by an edit; they take a newer revision only by a pull (F-EVAL-26). P2 S
F-EVAL-26 An instance whose template has moved (its origin_revision below the template's revision) may pull the template's current revision. A pull replaces the instance's QUESTIONS ONLY — the items with their frozen versions, points, order, milestones, bonus flags (ADR-052) and intros (F-EVAL-34) — and keeps everything else of the instance: title, dates, IP list, settings, grade scale, feedback and MCQ policies, duration and state; it then records the revision copied as origin_revision (a template that moved only in its settings is pulled as that record alone). It is allowed exactly where the item list is editable (F-EVAL-03): draft or scheduled, with no attempt of anybody, a teacher's own included (409 items_frozen / 409 locked otherwise); a scheduled instance must still hold a question afterwards (409 illegal_transition, reason no_items). As for Instantiate, a question whose pool the course no longer links blocks it (422 template_pool_unlinked with the items) and a deprecated version only warns. The template must still be the instance's and a template of its course (409 no_template otherwise), at the revision the teacher confirmed (409 template_moved). It is offered where it would be accepted, and nowhere else: a "template rev. N → M" badge on the evaluation's row of the classroom's list, and a warning with its fix in the launch checklist (F-EVAL-23); both open a confirmation that lists the questions added, removed and changed (version, points, milestone, bonus, intro), whether the order moves, and "rev. N → M", and says that the instance's own question changes are replaced while its settings, conditions included (F-EVAL-33), stay — or, when no question differs, that only the revision is recorded. Audited as template.pull (ADR-031, addendum of 2026-09-28, PR B). P2 S
F-EVAL-27 Publish the correction of an exercise that is running (running or paused), without closing it: an explicit, irreversible act of the teacher, from the live dashboard's overflow menu, behind a confirmation that says what the feedback policy will show. It opens the class debrief and its projection over the papers handed in so far (F-RES-03), counts as the release for the feedback policy on each student's results page (the key and the explanation per their options; nothing under none), lifts the score-only view between two attempts (F-EVAL-15), and grades every paper handed in from then on at hand-in; retakes continue, and no attempt is reopened any more (409 correction_published). Refused on an exam or a poll (422 correction_not_allowed) and in the waiting room or after the close (409 correction_not_open); a repeat answers the first instant. Audited as evaluation.correction_publish (ADR-050). P2 S
F-EVAL-28 An exam accepts trusted clients: Safe Exam Browser (F-EVAL-17), an attested kiosk station (F-EVAL-29), or either; with neither, it is sat in the portal as before. A session opened by a trusted client reaches its exam only; opening a new one for the same student and exam ends the previous one, which the supervisor sees; the student's portal sessions are untouched (ADR-051 §1–4). P2 S
F-EVAL-29 A kiosk station is a school Chromebook locked in a web kiosk on /kiosk. It attests itself to the platform through Chrome Verified Access, then shows its label, an 8-character code XXXX-XXXX (no ambiguous characters, 5 minutes, single use, renewed on expiry) and its QR code. The student, signed in on their phone, opens /pair, checks the station's label, picks one of the exams they can start now that accept the kiosk, reads its conditions (F-EVAL-33, shown once the exam is chosen; amended by ADR-079 §7, 2026-10-07), and confirms; the station then opens that exam, confined to it. When the attempt ends (submit, close, expiry), the station's session ends and it returns to the pairing screen. A station never carries two sessions (ADR-051 §5, §7). P2 M
F-EVAL-30 The station registry belongs to the admin: a station that attests for the first time appears as unnamed and cannot be paired until the admin names it; the admin may rename or retire a station (ADR-051 §5). P2 S
F-EVAL-31 During a kiosk sitting the station re-attests every 10 minutes and before the submit. A refused attestation suspends the sitting (answers kept, writes refused) until the next accepted one; an attestation Google cannot answer suspends nothing. The supervisor sees an alert in both cases, and for every student the access mode (portal, SEB, or the station's label). From the dashboard, the supervisor may pair a station for a student without a phone, with the code the station shows (ADR-051 §6–8). P2 S
F-EVAL-32 An exam or exercise evaluation may provide a calculator on the student's screen: none (the default), standard or scientific. It opens from a button at the bottom right of the player, is announced in the waiting room, follows the order of operations in both modes, and records nothing. It provides a tool and forbids none: only a trusted client confines the machine (F-EVAL-28). Refused on a poll (ADR-069). P2 S
F-EVAL-33 An exam or exercise evaluation states its conditions to the student. The teacher announces up to 20, each a kind — allowed, forbidden, provided, good to know — and a text of 1 to 200 characters, in their order: the teacher's own words, a snapshot, shown as plain text (never markdown nor HTML), never translated. The platform adds the lines it enforces or records, derived from the settings in a fixed order and never stored: the trusted client in force (other applications unavailable), the calculator provided (nothing for none), the notepad provided and, when it blocks them, that copy and paste are disabled in it (F-EVAL-35; amended by ADR-090, 2026-10-09), the duration with the student's extra time or the closing instant, the number of attempts, a retake of the questions to review only (ADR-091), a navigation that does not go back, negative marking, the journal of leaving the page and of pasting from outside it (ADR-088), the autosave; never the network allowlist nor the shuffles. The student reads them grouped by kind — forbidden, allowed, provided, then good to know, a kind without a line left out — the teacher's first inside each kind, then the platform's, each block headed by its kind's word and each line marked by its icon (amended by ADR-079 §6, 2026-10-08), in the waiting room and on the ready screen (F-LIVE-01, F-LIVE-02), and can reopen them from the player's bar during the attempt; no acknowledgment is asked. The settings editor shows, under the teacher's list, the lines the platform will add. Refused on a poll (422 conditions_not_allowed), frozen with the settings (F-EVAL-03), carried by a duplicate and a template, not by a pull (F-EVAL-26) (ADR-079; product owner, 2026-10-07). The per-course catalog of frequent conditions is F-ORG-16. P2 S
F-EVAL-34 An item of an exam or exercise evaluation may carry an intro: markdown text of at most 10 000 characters, the teacher's own, shown to the student as a passage screen before that item, with one primary action, Continue. It is not an item: no points, no answer, no segment in the question list. Under free navigation it can be reopened from the question it precedes; under forward_only and milestones it is not reopenable once passed. Edited in the builder as "+ Text" between items, written through the item routes and frozen with the item list (F-EVAL-03); carried by a duplicate, a template, an instance and a pull, and a change of it is a change of the item (a new template revision, changed in a pull's summary, F-EVAL-25, F-EVAL-26). Shown in the teacher's preview, never in grading, feedback, results nor the dashboard (ADR-084, issue #455). P2 S
F-EVAL-35 An exam or exercise evaluation may provide a notepad on the student's screen: none (the default), provided, or provided with copy and paste disabled inside it (the answer fields untouched). Plain text, up to 20 pages of 10 000 characters; it opens from a button at the bottom right of the player, stacked above the calculator's when both are on, one panel at a time. The notes stay on the student's device (localStorage) and never reach the server — no request, no event, no audit; the teacher's preview and an impersonation session keep them in memory only. They are deleted at every end of the attempt, at sign-out, when a kiosk station loads (not the student's phone on /pair), and after 24 h untouched; under milestones navigation each crossed checkpoint empties the notepad, which the checkpoint's confirmation says. Announced among the conditions (F-EVAL-33). Refused on a poll (422 notepad_not_allowed); frozen with the settings, carried by a duplicate and a template, kept by a pull (ADR-090; product owner, 2026-10-09). P2 S

F-LIVE Live run

Id Requirement Phase Prio
F-LIVE-01 The student sees on their home page the open evaluations of their classrooms and enters one in a single click; an attempt begins only by an explicit Start, except for trusted clients (SEB, kiosk) and a retake (ADR-076). The ready screen before Start shows the evaluation's conditions (F-EVAL-33; amended by ADR-079, 2026-10-07). P1 M
F-LIVE-02 In the waiting room they see the number of students present out of the number enrolled, as a progress ring, and the evaluation's conditions (F-EVAL-33), the duration with their own extra time among them (amended by ADR-079, 2026-10-07). P1 M
F-LIVE-03 Before starting, the teacher sees the list of those present, those absent, and the students with extra time, and confirms. P1 M
F-LIVE-04 The start is propagated to every client in under one second. P1 M
F-LIVE-05 Every change to an answer is sent to the server with a batching delay of at most 300 ms, and acknowledged. The interface shows an unsaved or offline state. P1 M
F-LIVE-06 After a reload or a reconnection, the student finds exactly the state of their answers and their position. P1 M
F-LIVE-07 The countdown is computed on the server clock. The client corrects its offset on every tick. The server accepts a write until the deadline plus 3 seconds of grace, then refuses. P1 M
F-LIVE-08 A question counts as answered as soon as it holds an answer — the question type decides what an empty answer is — with no click. The student may leave an empty question unanswered ("Leave unanswered", a toggle under the answer): it is settled on purpose, stored with the attempt, taken back by writing an answer, and graded like an empty answer (0). The student may flag a question for review (an icon toggle in the corner of the question's card): stored with the attempt, no effect on grading, shown to the staff on the live dashboard (F-DASH-01). A multiple-choice question offers Clear, which withdraws the selection (with negative points, a student must be able to); the other types are emptied by hand. In forward_only, an explicit "Validate and continue" step, confirmed, is irreversible and moves to the next question. In milestones, validating a checkpoint question asks for a confirmation and closes everything up to it. On an empty question this step reads "Leave blank and continue" (in forward_only its confirmation adds that the question will count as unanswered; at a checkpoint it keeps saying what the checkpoint closes); where it is offered, "Leave unanswered" is not, so the screen never shows two near-synonyms. There is no separate "skip" button: "skip for now" is what the flag is for. Every one of these writes goes through the write gate of F-LIVE-07. Decided in issue #89, which replaced the former "Mark as done"; labels and the flag's place revised in 2026-09. P1 M
F-LIVE-09 The question list shows every question with a distinct style, and a symbol besides the colour, for answered / not answered / left unanswered / flagged, marks the current and the closed ones, and allows going to one when the navigation permits it. P1 M
F-LIVE-10 The student may hand in before the end after a confirmation. At the deadline the attempt is closed by the server. P1 M
F-LIVE-11 The teacher may pause, resume, add 1, 5 or 10 minutes to everybody or to one student, and close. Every action is propagated immediately and logged. P1 M
F-LIVE-12 A late student starts with the full duration in duration mode — cut at closes_at when the evaluation has one (ADR-086) — or until the common end in deadline mode. The teacher may grant them time individually. P1 M
F-LIVE-13 poll mode: one question, aggregated results live on the teacher's screen, possibility to relaunch the same question. Only End closes the vote. Two independent, reversible switches decide what is shown, on the wall and on the phones alike: the votes (the distribution, off by default) and the reveal of the correct answer; neither closes the vote, and while the poll runs every phone keeps the question and its Send button, the personal verdict appearing only after End; an answer given after the reveal counts like any other (ADR-014, addendum 2026-09-29). A question written for the poll alone may have no correct answer (an opinion poll): it has no reveal, only its votes, and nothing is graded (ADR-014, addendum 2026-09-23). Its audience is ONE choice: anyone with the code, anonymously, in no classroom and reached by its owner only; or one classroom, by name, restricted to its roster and staff (ADR-014, addendum 2026-09-27). A running poll with no answer for 12 hours is ended by the server, as its teacher's End would (ADR-014, addendum 2026-09-28). P2 M
F-LIVE-14 The teacher may project a "presentation" view without student names: completion rate, and in poll the distribution of answers. P2 S

F-DASH Teacher's live dashboard

Id Requirement Phase Prio
F-DASH-01 Grid with students as rows, questions as columns. Every cell shows the state: not opened, opened, answered, left unanswered, validated (forward_only, a crossed checkpoint), and after grading correct / partial / wrong. A cell the student flagged for review carries a flag, and each column header counts the students who flagged it — a question many students flag may be unclear (issue #89). Staff only: a student never sees another student's flags. P1 M
F-DASH-02 Toggles: show names, show answers, show results. Hidden names give a stable pseudonym per row. All three are off on a first visit — the safe frame for a projected screen — and each browser remembers the teacher's choice (issues #80, #227). On an exam, answers and results ignore that memory: they are off when each exam's dashboard first opens, and the teacher's choice holds for that exam in that tab only (issue #796). P1 M
F-DASH-03 Columns: progress, running score (shown once at least one row has points), connection state, last event. The remaining time is one countdown in the header, on the common deadline; a row shows its individual remaining time beside the name only when its deadline differs from the common one (an individual extension, a time bonus, a late start in duration mode, F-LIVE-12) (issue #227). Sort by column. A running score may exceed 100 % of the total when bonus items (ADR-052) are answered. P1 M
F-DASH-04 Total row per question: completion rate — the share of started students who answered the question, said they would not, or validated it — and after grading success rate. P1 M
F-DASH-05 A click on a cell opens that student's answer to that question read-only, and nothing else; the row's inspect button opens the student's whole paper, every answer in the order of the quiz, and the answer view offers the step to it. Both show the key and the cell's state to the staff. The single answer follows the "show answers" toggle of F-DASH-02 like F-DASH-07 (answers hidden: the cell's state and a note, nothing fetched); the whole paper, a deliberate step, is not held to it (issue #353). P1 M
F-DASH-06 Full-screen mode suited to projection. P1 S
F-DASH-07 A question's column header holds a button that opens that question with every student's answer: the column counted by state first, then one card per started student of the class (a staff test seat counts in neither, as in the column statistics), the answer drawn by the type's review with the key. It follows the toggles of F-DASH-02 — pseudonyms when names are hidden, and no answer fetched or shown while answers are hidden (only each student's cell state, with a note saying how to show them). It is a snapshot read once on opening, with a manual refresh; the states beside the answers follow the grid. The header itself stays free for a sort (F-DASH-03) (issue #353). P1 M

F-GRADE Grading

Id Requirement Phase Prio
F-GRADE-01 Every answer of the deterministic types is graded automatically and validated: an exercise attempt as soon as it is handed in, expires or is closed by the teacher (a reopen stands its automatic gradings down until the next hand-in), an exam on close (ADR-067). A missing answer is worth 0. P1 M
F-GRADE-02 The answers of LLM-graded types — essays and diagrams; circuits are never graded by a model — are sent anonymised to the configured provider, automatically after the close, with the statement, the criteria grid and the reference answer; the criteria are the model's reading of the free-text rubric (ADR-063). The proposal contains the points per criterion, a justification and a confidence. P2 M
F-GRADE-03 Grading panel: one question at a time, as a table of every student's answer to it — the expected answer pinned first, the question type's own columns, sortable — and a side panel for one answer in full (ADR-044). Anonymise is on at every visit and never remembered: the server then sends no name at all (no pseudonym), and the rows are shuffled once per visit. The per-student view is the results page's. P1 M
F-GRADE-04 The teacher validates one proposal, a filtered batch, for example every high-confidence proposal, or every proposal of a question, or changes the points and a comment before validating. P1 M
F-GRADE-05 The teacher may override any grading, including an automatic one, with a mandatory comment. The previous grading is kept as superseded. The points lie in [0, item points], or [−item points, item points] for a choice question under negative marking (F-EVAL-16). P1 M
F-GRADE-06 Re-grading: after publishing a new version of a question, the teacher may re-run the automatic grading of an item on every attempt. Every re-run grading carries the annotation "re-graded with version N" and the old one is kept. P1 M
F-GRADE-07 One teacher comment per answer is visible to the student according to the feedback policy. P1 S
F-GRADE-08 A student may report a question from their results with a reason. The teacher sees the reports on the grading panel. P2 S
F-GRADE-09 Releasing the results freezes the grades and notifies the students. A re-grading after release updates the grades and marks the evaluation "changed after release". P1 M
F-GRADE-10 How an item counts, at grading time (product owner, 2026-10-12, ADR-108). Once an evaluation's item list is frozen (an attempt exists, or it has been opened), the grading panel's question bar offers, in a secondary menu, to count the question normally, as a bonus or not at all (neutralized), after a confirmation that states the new total and, once released, that the students whose grade changes are told. Never on a poll; never down to a total of 0 (409 illegal_transition, no_graded_points); after the release, the course owner's (ADR-068 §3); reversible any number of times; stored gradings are never rewritten; audited (evaluation.item_counting). After the release it marks the evaluation "changed after release" (F-GRADE-09) and notifies results_updated only to the students whose shown grade changes. Allowed while an exam or exercise is still open (an exercise runs for weeks). A neutralized item creates no drill card from then on (cards already created are kept) and its ungraded answers hold back no grade nor count on the grading progress; item statistics are unchanged. A copy (duplicate, template, instance, pull) counts a neutralized item normally and keeps a bonus. P1 M

F-RES Results and grades

Id Requirement Phase Prio
F-RES-01 Grades view of an evaluation: per student, points, grade to the tenth, time used, mean, median, standard deviation, histogram. P1 M
F-RES-02 CSV export: email, name, points per item, total, grade. Semicolon separator, UTF-8 encoding with BOM for Excel. A bonus item's column header ends with (bonus); the total includes its points (ADR-052). A neutralized item's header ends with (not counted): its points are shown and left out of the total (ADR-108). P1 M
F-RES-03 Per-question view: statement, correct answer, explanation, distribution of answers, for a multiple choice the percentage per choice, success rate, mean time. Linear scrolling for going through the answers in class: a full-screen projection, one screen per question, key and verdicts hidden until revealed, served once the evaluation is closed (ADR-033) — or, for an exercise, once its correction is published, over the papers handed in so far (F-EVAL-27, ADR-050). P1 M
F-RES-04 The student sees their released results: points per question, grade, and according to the feedback their answer, the key, the explanation, the teacher's comment. A correction published while an exercise runs counts as the release (F-EVAL-27). A bonus question is labelled "Bonus question", as in the player (ADR-052); a neutralized one stays on the page with the answer, labelled "Not counted", its points struck through (ADR-108). The server filters: a grade (and its points) reaches a student — on the feedback page, the home's card, the Grades page (F-ORG-14) or a results_updated notification — only once the results are released and the feedback policy lets the results through (resultsState is available); a released evaluation under the policy none shows NO grade. Points (never a grade) are visible before the release only where the feedback page already shows them — the immediate policy, a published correction — and on the Grades page they are marked indicative: exactly the feedback page's points for the attempt that counts. A student who never took a released evaluation reads the scale minimum under the same rule (F-RES-02); a teacher's staff seat that took no attempt is not listed (ADR-018 §3). On the Grades page and the classroom's Grades tab a released grade of an evaluation that does not count toward the mean (an exercise, by default) stays visible but reads as indicative, in the Indicative group (F-ORG-14, #803); the feedback page and the results_updated notification show it as any other grade. P1 M
F-RES-05 Cumulative view per classroom: promoted to the gradebook, F-GBOOK (D06). P2 S

F-NOTIF Notifications

What a user is told, and where (ADR-030 and its addendum of #198). A notification never carries a grade, a student's name, an e-mail or question content.

Id Requirement Phase Prio
F-NOTIF-01 Three channels per notification kind: App (an entry in the bell, plus a toast in every open tab), e-mail and Microsoft Teams. The settings page shows one grid of kinds × channels, limited to the kinds the user's role receives. P2 M
F-NOTIF-02 Each kind has its own default per channel. App is on for every kind. The background-noise kinds (student_joined, pool_question_added, activity_scheduled, results_updated) are off by e-mail and Teams. A toggle the user never moved follows the kind's default. P2 M
F-NOTIF-03 A toast is shown only for a notification that arrived while the tab was open: the first read after a connect or a reconnect toasts nothing, and nothing toasts while the student is in an attempt or on a full-screen page (poll projection, live dashboard, correction projection); the bell still counts it. One entry toasts at most once every 5 minutes, however often it is folded. P2 S
F-NOTIF-04 activity_scheduled: the students of a classroom are told when an exercise is scheduled, as one entry per classroom with a count ("16 exercises scheduled in PRG1-2026"). It is sent once per exercise, never again when the exercise is rescheduled or taken back to draft and scheduled again, and never for an exam. Only claimed student seats are told. The entry opens the student home. Off by e-mail and Teams by default. P2 S
F-NOTIF-05 activity_available: the students of a classroom are told when an exercise starts running, by the clock or by hand. Not for an exam, not for a poll. E-mail and Teams only for a take-home exercise (isTakeHome); an in-class exercise is told in the app only. The entry opens the exercise. P2 S
F-NOTIF-06 deadline_approaching: 24 h before closesAt, each student of the classroom (claimed seat) who has not submitted is reminded once per evaluation, exam or exercise, while it is running. "Has not submitted" means no submitted or expired attempt; a student who submitted once is not reminded while a retake is open. There is no reminder when the evaluation actually started running (startedAt, not opensAt) less than 24 h before it closes, when it has no closesAt, after closesAt, or for a poll. A late scan catches up; a moved closesAt does not send it again. The entry opens the attempt. On by e-mail and Teams by default. P2 S
F-NOTIF-07 results_updated: a student is told when their grade changes after release: the final grade on the evaluation's scale (the one the results page shows), compared before and after each grading write — a validation, an override, a batch, the automatic pass, a runner job. Points that move without changing the rounded grade tell nobody; no snapshot is stored. Only a student who can read the grade is told (not under the feedback policy none, not for a teacher's own test). One entry per evaluation, folded with a count; it opens the feedback page, carries no grade, and is withdrawn with the release. Off by e-mail and Teams by default. P2 S
F-NOTIF-08 student_joined and roster_conflict: the course's staff seats are told, as one entry per classroom with a count ("3 students joined PRG1-2026"). The entry opens the roster. student_joined is sent when a student takes the seat (their claim, at a sign-in or their own next request, F-ORG-05). P2 S
F-NOTIF-09 grading_ready: the course's staff seats are told when an evaluation's automatic grading has finished and proposals remain to validate, with their count. Finished means every attempt × item cell has a standing grading: neither a runner job nor an LLM job is still owed (ADR-063). Once per grading pass: a pass run again with nothing new tells nobody, and nothing is sent while the evaluation still runs. The entry opens the grading panel. P2 S
F-NOTIF-10 pool_question_added: the owner and the contributors (and co-owners) of a shared pool are told when a colleague publishes a question in it, a first version or a new one, as one entry per pool with a count. Never its readers, never the author. Off by e-mail and Teams by default. P2 S
F-NOTIF-11 The recipients of a teacher kind about a classroom are the course's staff seats, admins without a seat excluded. Nobody is notified of their own action. P2 M
F-NOTIF-12 An entry that folds events (F-NOTIF-04, -07, -08, -10, and F-NOTIF-13's two staff kinds project_deadline_applied and project_provision_failed, folded per project, 2026-10-04) is the unread one of the same kind and target. Once it is read, the next event starts a new entry. P2 S
F-NOTIF-13 Project kinds (D18), kinds of their own: F-NOTIF-04 to -07 stay the evaluations'. Student: project_published (a project of their classroom is published; for a recipient with no linked GitHub account when it is sent, a second text asks to link the account, then accept the project — the payload carries only that boolean, the text is frozen at sending, and no kind or preference is added; product owner, 2026-10-06), project_deadline_reminder (24 h before the deadline, once, to every claimed student seat of the classroom), project_repo_invited (an invitation to a repository awaits them on GitHub), project_grade_final (the scores of a project are released; no score in the entry). Staff of the course: project_deadline_applied (one entry per project, with the count of repositories locked), project_provision_failed (a student's repository could not be made), github_org_lost (the organization of a classroom was deleted, or Quiz's App uninstalled from it). E-mail and Teams are on by default for the must-not-miss kinds — project_deadline_reminder, project_repo_invited, project_grade_final, project_provision_failed, github_org_lost —, off for project_published and project_deadline_applied (ADR-030). An entry opens the project. P2 S
F-NOTIF-14 What's new. After an update, a user is shown once, in a dialog, what changed on the platform since their last acknowledgement: new, changed, moved, deprecated or removed, in their words, grouped by release (date and commit). A student sees the entries for students; a teacher and an admin also those for teachers. Closing the dialog acknowledges it. It opens only on the home and the list screens, never during an attempt, a live run, a projection, a preview or in the student view, and not for an impersonation or a Safe Exam Browser or kiosk session; a new account is shown nothing from before it. The coach marks wait for it. The account menu's What's new lists every entry. Each merged pull request adds one entry (possibly for nobody). ADR-087. P2 S
F-NOTIF-15 question_reported and question_report_resolved (F-POOL-11): the owner and the contributor and owner members of the pool (never the reporter, a reader, nor the staff of a course the pool is linked to, who read every report all the same: the same audience as pool_question_added) are told of a report, as one entry per question with a count (F-NOTIF-12); the reporter is told when a writer resolves it. The message and the reply are read in the question's Reports tab, never carried by the notification. question_reported is off by e-mail and Teams by default, question_report_resolved on. P2 S
F-NOTIF-16 pool_unpublished (F-POOL-13): each subscriber of a public pool and the owners of each course linked to it read-only are told when it leaves the catalogue, and the subscribers when it is deleted; one entry each, with the pool's name (and the course's) and no pool id. On by bell, e-mail and Teams by default. P2 S

F-STAT Question statistics

Id Requirement Phase Prio
F-STAT-01 For every question, all its versions pooled: the number of answers counted n and the mean success rate p (signed under negative marking), over exams only — never an exercise, done at home with help at hand —, shown from 10 answers on (ADR-038, amended 2026-09-30); a question never displayed is not counted. The time spent on it: median, P25–P75 and mean, over exams only, measured by the server, shown from 10 timed answers of its own — the mean is what F-STAT-04 adds up (ADR-039). Exams only, except the separate targeted-revision series of ADR-039 §6b: beside it, never merged into it, the time in targeted revision — per student, the time of their earliest answered first pass at the question in a round (F-DRILL-08), any correctness, a zero left out — median and P25–P75 over the students, no mean, shown from 10 distinct students, labelled as a first try on a question already met whose key the student could see, not an exam time. A question is listed when either series qualifies; with fewer than 10 exam answers its exam figures are null (product owner, 2026-10-16, open question 60). P2 S
F-STAT-02 Every statistic of a question counts exams only, except the separate targeted-revision series of ADR-039 §6b (F-STAT-01). Difficulty index p per question (ADR-038). Discrimination index: the corrected point-biserial (the item against the rest of the exam without it), per exam, over the attempts whose every item is validated, combined over the exams by Fisher's z; exams only, an exam counted from 5 other items and 10 attempts, shown to two decimals with its reading — weak below 0.2, fair, good from 0.3, inverse below zero (ADR-042). Distractor analysis for multiple choice: the whole-percent share of each option and of "no answer", the key marked, over the counted exam answers given to every version whose options are identical to the latest version's, shown from 10 of them (ADR-043). P2 S
F-STAT-03 The statistics are visible in the pool: an icon on the question's row or card opens them in a side panel. They also filter the pool's list: a success-rate range (signed, in whole percent) and a median-time range (seconds), applied in the page on the statistics already loaded; a question without the figure a bound reads is hidden unless the teacher includes the questions without statistics. The bounds read the exam figures only: the time in targeted revision (ADR-039 §6b) is shown in the panel, never filtered on nor a column. The MCP tool get_pool_question_stats reads the same figures (ADR-022). P2 S
F-STAT-04 "Generate a quiz": target duration, concepts (ADR-081), difficulty, number of questions, relying on the mean answer time — the exam's, never summed with the time in targeted revision (ADR-039 §6b). The result is an editable evaluation draft. P3 C
F-STAT-05 A contributor or an owner of the pool resets a question's statistics: only attempts started afterwards count, and only first passes of targeted revision served afterwards (ADR-039 §6b); the confirmation says both. Nothing is deleted, the reset is audited (ADR-038). P2 S
F-STAT-06 For the drill's questions of a classroom, its staff see per question the stated confidences aggregated over the class: right or wrong × sure (Sure, Certain) or unsure (No idea to Fairly sure), partial answers left out, and the share of confident errors among the wrong answers, highest first. Aggregated only, never one student's confidence; a question appears from 10 distinct students who stated a confidence with a right or wrong answer on it (the rows its cells count), a threshold applied by the server. Shown in the classroom's Drill tab, not in the pool's panel, whose readers span other classrooms (F-DRILL-04). ADR-085 §8. The distinction: a classroom's drill rows and per-classroom reads stay with its staff; only an anonymous per-question aggregate over every classroom, such as the time in targeted revision (F-STAT-01, ADR-039 §6b), sits in the pool's panel. P2 S

F-LLM LLM assistance

Id Requirement Phase Prio
F-LLM-01 One provider (Anthropic) and one institutional key, entered by an administrator and encrypted at rest, with a model per purpose (one chosen in the screen) and a daily spending cap. No per-teacher key (ADR-058). P2 M
F-LLM-02 In the editor: generate a variant of the question, propose the answer key, write the explanation, propose distractors. The result lands in the draft, never published automatically. P2 M
F-LLM-03 LLM grading is described in F-GRADE-02. No LLM call is made while an evaluation is running, except a brainstorm poll's AI assistance, which grades and releases nothing (ADR-072), and the teacher assistant of F-LLM-07, which grades and releases nothing: it may read a running evaluation's structure, and its results reader returns final (released) marks only, never a running or unreleased evaluation's partial scores (ADR-080 §3 and its P2 amendment). P2 M
F-LLM-04 Every piece of data sent is anonymised: no name, email nor student identifier. Exception (ADR-072): a brainstorm's ideas leave without any identifier but, for an anonymous guest, as typed — the names the platform knows are masked, and the poll page says so before anyone types. The calls are logged with the model, the number of tokens and the estimated cost, per teacher. Exception (ADR-080 §6): the teacher assistant's questions and answers are stored, 30 days an exchange, read by their owner and by an administrator with Super Powers (that read audited); a teacher's typed question leaves as typed, and the panel says an AI model reads it. P2 M
F-LLM-05 Without a configured key, the "Copy the prompt" action provides the full prompt to paste into an external client, and "Paste the answer" imports it. P2 S
F-LLM-06 MCP server exposing the reading and writing of question drafts, to author from an LLM client. P3 C
F-LLM-07 A help assistant on every teacher screen: a neutral round button at the bottom right opens a chat that answers questions about the platform and its interface from its documentation (the user guide and the per-screen help, built with the code), knowing the screen's route pattern, help topic and UI language — never its content. Since P2 (ADR-080 §8 and its P2 amendment of 2026-10-08) it also answers about the teacher's own data — courses, classrooms, pools, questions, evaluations, templates and the final results of their classrooms — through a closed list of read tools that act with the teacher's own seats, never Super Powers; the screen's context then carries the ids of its entities from a closed list of kinds (course, classroom, pool, question, evaluation, template), never a user's, an enrollment's, an attempt's or a student's, and the stored exchange keeps the pattern without them. Only links to the platform itself are clickable in an answer. Since P2b (ADR-080 P2b amendment of 2026-10-08) it also drives the interface: asked to show something, it opens one of the app's teacher screens from a closed catalogue derived from the router — searched or on a tab when asked —, with no confirmation (Back returns; the leave guard still asks before unsaved work is left), and runs the current screen's effect-free palette commands; a command that changes anything is never run before P3. The browser runs these actions; the server checks them and stores none. Since P3 (ADR-080 P3 amendment of 2026-10-08) it PROPOSES changes the teacher makes, one by one: in the question editor, a rewrite of the teacher's own texts (statement, choices' texts, explanation; never an id, a setting, the key, the scoring, the variables, a [[…]] expression nor an image), shown before and after and applied to the draft in one step with an Undo, never published; a closed list of writes — a draft question, a category, a template, published questions added to a template, a pool linked to a course (whose whole staff gains access) —, each a card the server renders from the frozen arguments and runs only on the teacher's Confirm, within ten minutes, audited as assistant; and the current screen's write commands (publish, start, pause, extend, close, grade, regrade, release), run in the browser on Confirm. It never publishes, deletes, nor changes an evaluation, a classroom, a course or a poll; a question changes only in its own editor. It declines anything else. Teacher and administrator portal sessions only: absent in the student view, under impersonation, on seb and kiosk sessions and on projected screens. Its own share of the daily cap (a quarter, refused first) and a per-minute limit; conversations kept 30 days. ADR-080. P2 S

F-DRILL Practice

Id Requirement Phase Prio
F-DRILL-01 A question a student meets in an evaluation becomes a drill card: at the release of the results of an exam, never before, and at the hand-in of an exercise; only when the evaluation allows drill (a setting, on by default for an exercise, off by default for an exam), the classroom has the drill enabled (F-DRILL-04), and the question is drillable (04 §4.1). A card belongs to the classroom and the evaluation where it was met first. Turning the setting off keeps the cards already created; the teacher removes them with an explicit action, "Remove these questions from the drill". The setting is editable until the release and says, next to its switch, that students will see the key of these questions after each review. No whole-pool drill in v1, and a poll creates no card (06, question 28). A question left unanswered becomes a card too. Enabling the drill for a classroom (the first time or again) creates the cards of its past evaluations that allow drill: exams with released results, exercises handed in. ADR-041 §1–3, §10, §13. P2 S
F-DRILL-02 Scheduling follows FSRS-5 with its default weights. The recall rating is computed, never asked, from the correctness of the automatic grading and the active time against the question's reference time on the same device class, recorded on each review. ADR-041 §4–5. P2 S
F-DRILL-03 The student has a drill tab with the session of the day: due cards first, then new cards capped per day, up to a time budget of ten minutes (one budget in v1; a setting by the teacher or the admin may come later, F-ADMIN-03); courses and concepts interleaved (by the question's first concept, ADR-081 third addendum §8). A card leaves the session when its classroom is archived. An empty day says "nothing to review today" and gives the next due date. Each review draws a new seed, shuffles the choices of an mcq, serves the latest published version of the question, and shows the key afterwards. A card is served only when the key may reach the student: an exam's once its results are released (a withdrawn release suspends its cards), an exercise's once its own feedback policy shows the key to that student (ADR-041 §13). No reminders and no streaks: a "today's drill is available" badge on the home and on the centre slot of the bottom bar. A card served in a round of targeted revision today (F-DRILL-08) is left out of today's session, and its daily serve and answer refused, until the next day. ADR-041 §6; ADR-115 §7. P2 S
F-DRILL-04 The teacher enables the drill per classroom; its students are then in by default and may opt out. The teacher sees each student's individual activity — questions seen, sessions — and a measure of their improvement over time, besides the aggregates per classroom and per concept: the mastery on the concepts the course lists (a question counts on every listed concept that covers one of its own; a listed concept nothing has exercised reads "No evidence yet"; what no listed concept covers is gathered "Outside the course's list"; a course listing none gets the flat view), over reviewed, non-suspended cards, with no student by concept matrix (ADR-081 sixth addendum §9, #599 step 9a). The course's staff (owners and assistants) also see when the drill is practised: a weekday × hour heatmap of the last 30 days, Europe/Zurich time, class hours (Monday to Friday 08:00–18:00) set apart and the share outside them stated — distinct students per slot for the classroom, reviews per slot for one student; read from the drill's reviews only, never from the platform's presence hours (ADR-041 §8 amendment of 2026-10-14). The drill tab tells the student that their teacher sees this activity, at what times of the week included. Only the staff of the classroom a card belongs to see its reviews. An opt-out hides the activity from then on; what was recorded before stays visible to the teacher, the teacher sees that and when the student opted out, and the opt-out confirmation says both. The staff also see who revised which concept in rounds of targeted revision (F-DRILL-08): per student the rounds, the last one, the concepts, the cards served and the cards right; per concept the students, the rounds and the last one; never a card, a question or an answer. The time a round's answers took feeds only an anonymous statistic, the question's time in targeted revision, read by the pool's readers from 10 distinct students, never a time per person (F-STAT-01, ADR-039 §6b). ADR-041 §8 and §10; ADR-115 §9. P2 S
F-DRILL-05 The student sees, on their drill page and per classroom, a mastery level for each concept their course declares (validated concepts only, rolled up through broader relations; labelled in their interface language): To work on, Getting there or Solid, bands of the mean FSRS retrievability now, or "No data yet" below 5 reviewed cards, with the number of cards it rests on. Only cards whose key the student may already see count (a withdrawn release suspends them). The levels are said to be as of today and to fade without review. No class average, comparison or ranking, no concept outside the course. A secondary "Revise a concept" opens targeted revision (F-DRILL-08), which moves no level. One student exit in the drill module's student view, which also names the concepts offered for revision, with its own leak test (05 §5.7). Product owner, 2026-10-10; ADR-098, satisfying ADR-081 §10; amended by ADR-115. P2 S
F-DRILL-06 A question edited after its card exists keeps the card's FSRS state, unless its answer key changed: then the card is reset. ADR-041 §7. P2 S
F-DRILL-07 On every drill card, under the answer and before the correction, the student may say how sure they are: No idea, Unsure, Fairly sure, Sure, Certain (0 to 4), optional, nothing preselected, keys 0–4. The statement is stored with the review and never read by the grading, the rating (F-DRILL-02), the FSRS state or a score. After the correction one line says it back; a wrong answer stated Sure or Certain is highlighted ("You were sure: look closely") and is rated Again like any wrong answer, and comes back the next day (product owner, 2026-10-08) — its due date is the earlier of FSRS's and the first instant of the next calendar day, Zurich time (the start of tomorrow's session); its stability, difficulty, reps and lapses stay what FSRS computed. A right answer stated No idea is counted as luck and never upgraded. Drill only in v1: never in an exam, a graded exercise or a poll. The student's drill page shows their own calibration — per level, the share of their stated answers that were right, with the count, "not enough answers yet" below 5 answers at a level; the teacher sees per question, in the classroom's Drill tab, the class's 2×2 (right/wrong × sure/unsure) and the confident errors among the wrong answers (F-STAT-06; 2026-10-08). ADR-085 §8. P2 S
F-DRILL-08 Targeted revision by concept: in a classroom whose drill is on, not archived, where they hold a student seat and have not opted out, the student picks one validated concept the course declares and revises it on purpose, in rounds of at most 10 of their own cards — the questions they met in an exercise or an exam that allows drill and whose key they may already see, new cards included, whose concepts the chosen one covers (itself or a narrower one); never a question they have not met, never a pool. Cards not practised today first, then the others; within each, the most nearly forgotten reviewed cards first, then the new ones. A card answered wrong comes back once, three cards later; after each answer the key is shown, with no timer, no rating and no confidence. A round is extra practice: it feeds neither FSRS nor the mastery level (F-DRILL-05) nor any statistic of the daily reviews, and says so; it is served only within the day it started. Each round is recorded — student, classroom, concept, start and end, cards answered and answered right, the time each answer took — and kept five years; the staff see it (F-DRILL-04), the student is told so and reads their own rounds. The time of each student's first try at a question feeds an anonymous statistic of that question, its time in targeted revision, shown from 10 distinct students apart from the exam time (F-STAT-01, ADR-039 §6b); the "Data and privacy" page says so. Entry: a secondary "Revise a concept" in the classroom's Concepts section of the drill page, its own screen with Start as the primary action. Product owner, 2026-10-15 (#897 step E); ADR-115. P2 M

F-GH GitHub

Brought by the merge of heig-classroom (ADR-035). The platform acts on GitHub only through its own GitHub App (D23), and only for a classroom a member of its staff connected. Without a configured App, nothing of this section shows and its routes answer 404; the rest of the platform is unaffected.

Id Requirement Phase Prio
F-GH-01 GitHub is optional per classroom. A classroom is connected to at most one GitHub organization (D02), by any member of its course's staff (or an admin); two classrooms of a course may use different organizations. Every connection and disconnection is audited. Amended 2026-10-11 (ADR-100): only to an organization granted to the course; the connect itself grants it when the member's linked GitHub account owns the organization, or when an admin with Super Powers connects; otherwise 403 org_not_allowed, audited. A grant holds for every classroom of the course, every year; an admin with Super Powers removes it, never while a classroom of the course is connected to it. P2 M
F-GH-02 The GitHub section of the classroom's Settings connects it: it lists the organizations where Quiz's App is installed, suggesting first the organization of the course's other classrooms, or sends the teacher to install the App on another one (installing needs the organization owner's rights and access to all repositories). The install is a same-tab round trip: GitHub returns to the classroom's Settings, which reopen the connect sheet with the new organization picked (2026-10-06), and whose status turns green without a reload. Amended 2026-10-11 (ADR-100): the list is the classroom's — the organizations granted to its course, and those the teacher's linked GitHub account owns (all of them for an admin with Super Powers); with none, the sheet says "Ask an organization owner or an administrator", and "Connect to GitHub" stays the section's one primary action. P2 M
F-GH-03 Once connected, the section shows the checks, each a line with its state and what to do about it: the App installed with access to every repository — the only blocking one; the organization's plan (on free, no rulesets and no organization secrets for private repositories: a warning). The state is re-checked when the classroom's Settings open (a missing installation resolved, a plan refreshed); a renamed organization is followed, and an uninstalled App or a deleted organization shows as such. Amended 2026-10-14 (product owner, M3-14f): the third check, the presence of the organization's ANTHROPIC_API_KEY secret for the CI's LLM review of projects, is dropped. Quiz reviews projects with its own LLM access (ADR-106); a key the organization's CI may still use no longer depends on Quiz, which neither checks it nor reads the organization's secrets. P2 M
F-GH-04 A teacher may disconnect the classroom. Nothing is deleted on GitHub, no repository in particular. Disconnecting, or connecting another organization, is refused (409) while the classroom has a journal in a GitHub repository: the journal is removed, or brought back into Quiz, first (F-JRN-04, F-JRN-16; 06, question 33). A journal in Quiz does not hold the link. P2 M
F-GH-05 A user links their GitHub account from their settings, through the App's own authorisation, and comes back to the page they started from, with a message saying linked, already linked to another account, or failed. Linking is never a sign-in and asks for no permission on the user's repositories. One GitHub account belongs to one user: linking one already linked elsewhere is refused. The link follows a renamed GitHub account by its immutable id. The user may unlink. Linking and unlinking are refused in an impersonation session and in a delegated session (ADR-034, ADR-027), and audited. The settings card shows only when it is relevant: to the staff of a connected classroom, to a student with a claimed seat in one, and to a user who has or had a project; no banner, redirect or other reminder elsewhere urges a student to link: besides the project's own "Link GitHub" action, only the text of the project_published notification addressed to a recipient without a linked account mentions it (F-NOTIF-13; product owner, 2026-10-06). The staff of a connected classroom see each linked student's GitHub login on its roster. Editing a journal in Quiz does not need a linked account (F-JRN-10); being invited as a collaborator to a repository does (F-JRN-02, F-JRN-03). P2 M

F-JRN Journal

A classroom's course documentation, read in the app (ADR-035, ADR-049, ADR-057, docs/merge/04-journal.md). Not an activity: no grade, no deadline, no tracking. A journal has a mode, chosen at its creation: In Quiz (the default), where the platform holds the content and the staff edit it in the browser; or In a GitHub repository, where the repository is the content, edited in the teacher's own tools, and the platform keeps a rendered read-only copy, so a page is read without calling GitHub. Both modes render and serve the students the same way.

Id Requirement Phase Prio
F-JRN-01 A classroom has at most one journal, with a mode (ADR-057). In Quiz needs no GitHub at all (no organisation, no App, no connection): the platform's database is the content. In a GitHub repository, the journal is one repository of the classroom's organization, read on one branch (by default the repository's default branch) under an optional root folder (D03); two classrooms linked to the same organization may use the same repository, each keeps its own copy, and a push reaches both. There is no separate "journal on" switch: the classroom has a journal once one is created, and its Journal tab exists exactly then. A mode is changed only by an explicit action (F-JRN-15, F-JRN-16), never by a toggle. P2 M
F-JRN-02 The Journal section of the classroom's Settings offers the mode as a segmented control, each option with a one-line explanation: "In Quiz" / "In a GitHub repository" (fr: "Dans Quiz" / "Dans un dépôt GitHub"; never "local"). In Quiz (the default) creates the journal at once, with an empty home page, whether or not the classroom is connected. In a GitHub repository is offered once the classroom is connected (F-GH-02): Create a journal creates a new private repository in the organization, with a seed README.md, under a suggested name the teacher may change. It never adopts an existing repository: on a name already taken it proposes a free one. The staff of the course with a linked GitHub account at that moment are invited as collaborators, with the push permission only, never admin nor maintain, each invitation audited (journal.*); a member who joins the staff or links an account later is not invited automatically (as in heig-classroom). The pages are read in at once. Audited, with the mode. P2 M
F-JRN-03 Use a repository chooses any repository of the classroom's organization, including one another classroom uses (D27), with an optional branch (the repository's default branch otherwise) and an optional root folder, both honoured. The staff with a linked GitHub account are invited as collaborators as for a creation (push only, each invitation audited). The accepted risk is that any member of the staff of a connected classroom makes Quiz's App read that repository, whatever their own rights on GitHub; every choice is therefore audited. The platform never writes into a chosen repository (ADR-057). Amended 2026-10-11 (ADR-100): the organization is one granted to the course, which bounds the accepted risk to that course's staff; a repository Quiz knows as a course the teacher does not teach (a project's or a journal's) is refused as a missing one, while one of their own courses (last year's journal) stays usable; and nobody is invited — the staff's invitations are for a repository Quiz creates (F-JRN-02). P2 M
F-JRN-04 Remove the journal drops it and its Journal tab, after a confirmation. In a GitHub repository: the classroom's copy (pages and assets) goes, the confirmation says the repository is kept, and the repository is never deleted nor changed. In Quiz: the pages, their revisions and the assets are deleted, the only copy; the confirmation says so with the number of pages, and when there are pages the teacher types the classroom's name. Deleting the classroom removes its journal the same way (F-ORG-09). Audited. P2 M
F-JRN-05 In a GitHub repository, the copy follows the repository: a push to it updates every classroom that uses it; the staff may Refresh it by hand, from the Settings and from the Journal tab. A renamed repository is followed; a deleted or unreachable one puts the journal in error, shown with its reason in the Settings, and the pages already copied stay readable. The Settings show the repository, the last synchronisation and its state. Open readers see a change without a reload. P2 M
F-JRN-06 Navigation. In a GitHub repository it is the repository's layout, as in MkDocs: pages sorted by file name, where numeric prefixes (in steps of ten) order them and are never shown; a README.md is the landing page of its folder, a folder without one is a heading that does not navigate. In Quiz a page's path is set at its creation and never changes; the order among siblings is a field of its own and nesting is a parent page, so moving a page breaks no link and no bookmark (F-JRN-17). In both modes a page's title is its front matter title, else its first # heading, else its file name made readable, and links between pages are relative, so the same markdown reads correctly on github.com (and after a Move to GitHub, F-JRN-15). P2 M
F-JRN-07 Reading: the Journal tab of the classroom (both roles, /classrooms/:id/journal/<path>) shows the navigation as a strip of pages above the page (the home, then one entry per top-level page or folder, a folder opening a menu of its pages; it scrolls sideways when it outgrows the width), the page, and its table of contents, beside the page on a wide screen and folded above it otherwise. A student has no primary action there. The staff also see the primary action inside a page — Edit in Quiz, Edit on GitHub in a GitHub repository (F-JRN-14), beside a secondary Refresh — a badge on a draft and on a page not yet visible (with its date), and the page's warnings. A path that is not a page, or not one the caller may read, is a not-found page. P2 M
F-JRN-08 Front matter: title, date, draft: true (the page is read by the staff only) and visible_from (the page is hidden from the students until then, and appears to them without a reload once it passes). Front matter that does not parse is a warning, never a failure: the page still renders. P2 M
F-JRN-09 Rendering happens on the server, once per synchronisation or save: GitHub-flavoured markdown, inline and block KaTeX (a formula that fails is shown as code, with a warning), code fences coloured as in question statements. Raw HTML is shown as text (D15, N-SEC-14). Images come from the journal only; an external image is dropped, its alt text kept, with a warning. A link to another page opens it in the app, a link to another file of the journal serves that file, an anchor stays an anchor, an external http(s) or mailto link opens in a new tab, and a link that resolves to nothing is plain text. Headings get unique anchors. Warnings are codes with parameters, translated in the interface. P2 M
F-JRN-10 Editing, by the staff, in Quiz only: a page opens in the platform's standard WYSIWYG markdown editor (the one of question statements, with its source mode); its normalisation of the markdown is accepted. The front matter is edited as fields beside the editor, never inside it; a preview renders with the same renderer as the students' view. Save writes the page against the version the editor opened: when the page moved meanwhile, the save is refused (409 conflict), nothing is merged, and the teacher's text stays in the editor. Each save is a revision (F-JRN-13). The staff may also add a page and delete one. Every write is audited. A journal in a GitHub repository is never written from the browser: its write routes refuse it, and its pages link to GitHub instead (F-JRN-14). D25's round trip is superseded (ADR-057). P2 M
F-JRN-11 Assets (in Quiz): an image dropped or pasted into the editor is stored under a relative path beside its page (images/…) and inserted with that relative path, never as a platform asset (asset:<id>), so it also renders on GitHub after a Move to GitHub. An upload is at most 5 MB and its content type must match its extension. The assets a page references are recomputed with every save and every deletion, in the same transaction, so an asset referenced only by a draft never reaches a student (N-SEC-13); assets are not versioned, and one no page references any more is collected. In a GitHub repository, only files that a page references are copied into the platform (D14). P2 M
F-JRN-12 Who reads what: the staff of the course (and an admin) read every page, drafts included, and the markdown; a student with a claimed seat in the classroom reads only the pages that are not drafts and whose visible_from has passed, rendered, and only the assets those pages reference; anyone else gets a 404 indistinguishable from a classroom that does not exist. The journal's routes serve portal sessions only (ADR-027): a seb session, opened to sit one exam, never reaches them. A teacher in the student view and an impersonation session get exactly what a student gets (N-SEC-12). P2 M
F-JRN-13 Revisions (in Quiz): every save of a page records a revision, its markdown with its front matter, without limit. The staff list a page's revisions (date, author) and restore one, which is a new save, audited. No student route ever reads a revision (N-SEC-12). P2 M
F-JRN-14 Edit on GitHub (in a GitHub repository): each page links to github.com's editor for its file on the journal's branch; it is the primary action of the staff bar in this mode. The platform offers no add, delete nor upload there: the repository is edited in the teacher's tools, and a push (or Refresh) updates the copy. P2 M
F-JRN-15 Move to GitHub: a journal in Quiz moves into a NEW repository of the classroom's organization, or an EMPTY one, never a repository with content (ADR-049, point 6). One initial commit holds every page and asset; the file names get numeric prefixes from the explicit order, and the relative links are rewritten to match. The journal is then in a GitHub repository, read-only in the platform. Writes are frozen while it runs. Audited. P2 S
F-JRN-16 Bring back into Quiz: a journal in a GitHub repository becomes a journal in Quiz from its copy; the repository is detached, never deleted nor changed. Before confirming, the teacher sees what is left behind (files of the repository the copy does not hold) and that the first save of a page will normalise its markdown. Writes are frozen while it runs. Audited. It is the way back for the journals created before ADR-057, which are in a GitHub repository. P2 S
F-JRN-17 Order and nesting (in Quiz): the staff rename a page (its title), move it among its siblings and under another parent page. The page's path never changes, so links and bookmarks stay valid. Audited. P2 S
F-JRN-18 Copy a journal from another classroom of the same course (the next semester): later, not in the first version of ADR-057. P3 C

F-PROJ Projects

Graded work in a GitHub repository, one per student or per group: a lab as well as a semester project (ADR-035, heig-classroom's assignment; docs/merge/03-github-projects.md). A project is an activity of a classroom, beside its evaluations. It needs the classroom connected to an organization (F-GH-01) and Quiz's App installed there; without the App, nothing of this section shows. Its score is computed by the repository's own CI on the CI runners (ADR-007), never by apps/runner — amended 2026-10-12 (ADR-106, product owner): except a project in the criteria grading mode (F-PROJ-23), whose tests the platform runs in apps/runner's project action and whose criteria a platform LLM pass proposes for the teacher to validate. Every member of a connected classroom's staff may create projects in Quiz, before the cutover too (D26 and its addendum of 2026-10-02); the import still brings heig-classroom's projects (F-PROJ-20). Until the project's student view (F-PROJ-15, merge task M3-09), the students see none of them.

Id Requirement Phase Prio
F-PROJ-01 A member of the course's staff creates a project in a connected classroom, from New ▾ › Project of the classroom's Activities: a name, a source repository of the classroom's organization (any other is refused, 422 source_not_found), the branches to hand out (by default the source's default branch; the first one chosen becomes the default branch of the students' repositories), a start and a deadline, and the advanced options with their defaults: source strategy squash (whole keeps the source's history), deadline strategy lock (commit leaves the repository open), grading auto (none: no score is shown and no review is dispatched; criteria: graded by its criteria on the platform, F-PROJ-23, chosen by the staff only, never by default — ADR-106 §1, product owner, 2026-10-12), a grace of 30 minutes, the protected files, group mode with its group set (F-PROJ-06) and review checkpoints (F-PROJ-11). The form pre-checks as protected the criteria.yml, README.md and .github/workflows/grading.yml the source holds; unchecking grading.yml warns that the student could then alter the grading. The project is born a draft. Its slug is unique in the classroom (a suffix -2 … -20 on reuse). Audited. P2 M
F-PROJ-02 At creation the platform builds the distribution repository, private, in the organization (<slug>-squashed, suffixed on a collision): with squash, one commit per branch holding the source's tree, after applying the hand-out overlay (student/ and .studentignore, safe against symlinks and paths that escape); with whole, the selected branches as they are. It is managed by the App alone, and its link is shown to the staff only. P2 M
F-PROJ-03 Lifecycle: draft → published → locked (at the deadline); a project may be archived and unarchived. A draft is invisible to the students and freely editable; a scheduled start must lie ahead when it is set — at creation, or by a patch of the mode or of the start (422 start_past); switching a draft back to manual resets its start to now, and is refused (422 deadline_past) when the deadline it keeps has passed (orchestrator, 2026-10-11, after the spec challenge of PR 2). Publish either now (the start becomes now, the deadline the date set or now plus a duration) or on a schedule (the ticker publishes it at its start). Once published, only the name, the deadline (never to a date already past, 422 deadline_past; moving it later after it passed reopens the project, F-PROJ-09), the deadline strategy (until the deadline, 409 strategy_frozen) and the protected files change; the publication mode and its duration freeze at publication (409 publish_mode_frozen), the start, the grace, the grading mode and scale, the group mode and its group set too (409 not_draft; the groups themselves keep following their set, F-PROJ-06). One exception (ADR-106 §1, product owner, 2026-10-12): the grading mode may be switched to or from criteria on a draft and on a locked project not yet released — never on a published one, never after the release —, and the switch to criteria is refused unless the distribution holds a valid criteria.yml (the answer carries the parser's errors) and the source's workflow needs no package the lab image lacks. The source repository, the branches and the source strategy are fixed at creation, as in heig-classroom, in every state — the distribution repository is built from them (F-PROJ-02, ADR-062); to change them, delete the draft and create it again (product owner, 2026-10-02). A new list of protected files applies to the next pushes, without a retroactive revert. A moved deadline reschedules everything that hangs on it (F-PROJ-09). Audited. P2 M
F-PROJ-04 A student sees the published projects of their classrooms in Activities and on the classroom's page (F-ORG-14, F-ORG-15): the name, the start, the deadline, a status — to accept, in progress, locked — and the link to their repository once it exists. A project whose start is still ahead is listed but cannot be accepted. A student without a linked GitHub account sees the projects and a card that leads to linking it (F-GH-05); only acceptance waits for it. Once their repository is ready, the card also says the state of their work, as heig-classroom's row did — the short hash and date of the commit the view stands on, the number of commits pushed to their repository (their group's in a group project) by their deadline (counted from the server's push receipts: never the App's nor a workflow's commits, never a push after the deadline), the CI status and the indicative score (none once the scores are released); what F-PROJ-15 shows, read the same way (N-SEC-20; product owner, 2026-10-06). Every activity card has one grammar — a small icon for the kind before the title, the status as a badge beside it, the facts as icon-and-text items (deadline, commit, CI badge only when a run exists, the indicative score marked by a lock once frozen and by the word "indicative"), and one button that wears the GitHub mark when it leads to GitHub; the accent belongs to the button alone. The student's project page shows the same header, under a breadcrumb (product owner, 2026-10-06). P2 M
F-PROJ-05 Accept gives the student their repository. Allowed to a claimed student seat of a classroom that is not archived (a teacher's staff seat, ADR-018, never counts for unassigned_students and accepts only an INDIVIDUAL project, for its holder's test repository, counted nowhere — on a group project it gets 409 no_group (ADR-077; product owner, 2026-10-06); an archived classroom takes no new work), on a published project whose start has passed and whose deadline has not, with a linked GitHub account (409 github_not_linked; 409 github_account_stale when the account was deleted or renamed away) and the App installed (409 app_not_installed). The platform creates the private repository <slug>-<login> in the organization, pushes the distribution repository's branches into it (so that every student repository shares an ancestor with it, F-PROJ-12), sets its default branch, protects the handed-out branches against force pushes and deletions (a ruleset; tolerated where the organization's plan has none, and retried by the daily reconciliation once the plan allows it, up to a day later, with no staff action — product owner, 2026-10-06), and invites the student with the push permission, never more. Up to the invitation sent, it takes under 60 seconds (N-PERF-07). Accepting is idempotent: a second click, or two members of a group at once, never makes two repositories (409 provision_in_progress while one is under way); a failure leaves a state the student retries, and tells the staff (F-NOTIF-13). Audited. P2 M
F-PROJ-06 Groups (ADR-070, amending ADR-048): a classroom holds any number of group sets (fr répartitions), each a named list of groups of its students (its roster lines, claimed or not, staff seats excepted); a student is in at most one group of a set. The staff create, rename, duplicate and delete a set (named "Groups of P2 S
F-PROJ-07 Invitations: the student and the staff see each repository's invitation state (pending, accepted) and may resend it. GitHub lets an invitation expire after seven days without telling anyone: the reconciliation (N-RES-08) re-invites a student who still has no access, at most once a day per repository. P2 M
F-PROJ-08 Protected files: a push by anyone but the App — a student, or a workflow's own commit (github-actions[bot]) — that touches a protected file of a handed-out branch is answered by a restore commit of the App, fast-forward only, that puts back the distribution repository's current version of each file on that branch (ADR-062 addendum, 2026-10-02) — the student's work is never rewritten, only covered. A push is answered once, however often GitHub delivers it. The student is told which files were restored. Past five restores in an hour on one repository, the platform stops restoring, marks the repository "protected files in conflict" for the staff and the student, and marks every score captured meanwhile "to verify"; the staff re-enable the protection by hand. P2 S
F-PROJ-09 Deadline, on the server's clock. (1) Each repository has an effective deadline: its own, when the staff gave it an individual deadline (D13 as amended: the individual extension, now, not later) — a date ahead of now, which they may also take back —, or else the project's; every rule of this requirement and of F-PROJ-11 reads it for that repository: its lock or commit, a late receipt, its provisional and definitive freeze (product owner, 2026-10-02). Within 60 seconds of the deadline the platform starts applying it: lock adds a ruleset that blocks pushes on every branch, which the App and the organization's administrators bypass; commit pushes one empty commit of the App per handed-out branch and leaves the repository open. (2) lock, the default, finishes for 100 repositories within 5 minutes (N-PERF-07); commit is best effort, at the pace GitHub's limits on writes allow, and the score never depends on the deadline commit (the receipt time decides, F-PROJ-11) (product owner, 2026-10-02). (3) Archiving the repository, shown as degraded, stands for the lock only where rulesets cannot exist — a repository provisioned without one, or GitHub refusing one for the organization's plan — never on GitHub failing (a 5xx, a rate limit: retried) (product owner, 2026-10-02). The staff may lock and unlock one repository by hand; (4) a repository its staff unlocked after its deadline is never locked again by a resumed deadline pass, until its effective deadline moves, and an unlock before the deadline leaves the deadline to lock it (product owner, 2026-10-02). The roster's extra time (F-ORG-07) never applies to a project's deadline (D13). The project is locked at its own deadline; a repository with a later deadline of its own stays open until it. Moving the deadline of a project already locked to a later date reopens it: the locks lifted (an archived repository un-archived), the freeze and the final review undone, the runs requalified against the new deadline, the reminder re-armed; the teacher scores and a release already made are kept, as in heig-classroom. Moving a repository's own deadline later once that repository is locked reopens it alone. A day before the deadline, the students are reminded (F-NOTIF-13). A repository deleted on GitHub is marked so for good and skipped. Audited. P2 M
F-PROJ-10 Score capture. A run of .github/workflows/grading.yml reports its score as one annotation ::notice title=GRADE::<points>/<max>; the platform reads it from the run's check annotations. A run counts only on a handed-out branch, on a head commit that is not a bot's (the App's restore, deadline commit or sync, a workflow's own commit) nor a commit whose protected files the App restored (its run used the student's copy of them: kept, marked "to verify", never the score — ADR-062 addendum), once per (repository, run, attempt). A run with several GRADE annotations, even equal, has no score and alerts the staff (a student's code could print one). A negative points value (-2/6) counts 0: the staff see that the run was clamped, the student sees only the score 0 (product owner, 2026-10-06). A malformed one — a maximum of 0 or below, or any other malformed message — has none, and the reason is kept. A repository without grading.yml gets a pass / fail CI status instead: a run of another workflow counts only while the repository has no grading.yml run at all. The current score is that of the latest counted run with a score, by GitHub's completion time, whose commit the platform received before the deadline. The staff may ask for a run now. The score is points out of a maximum, a score, never a grade (F-PROJ-14). Amended 2026-10-12: in the criteria mode the CI's score is still captured, indicative, and the staff's Regrade replaces "a run now" — see ADR-106 §4, §10. P2 M
F-PROJ-11 Freeze and review. At its effective deadline (F-PROJ-09) a repository's current score becomes its provisional frozen score, a freeze of its own (product owner, 2026-10-02); it becomes definitive at that deadline plus the grace (30 minutes by default), so that a run started on a commit received in time may still finish. What counts is the time the platform received the push (its own clock, written when the webhook arrives), never the commit's date; a commit whose receipt is unknown counts as late. A run on a late commit never changes the frozen score, and the staff alone see it, marked "after the deadline" (ADR-012, read literally). Once a repository's freeze is definitive, the platform dispatches its final review, once (grade-final, at the frozen commit, carrying its effective deadline): the repository's CI runs it — an LLM review with the organization's ANTHROPIC_API_KEY secret (D17) — and its score fills the review slot when it parsed and succeeded once the freeze is definitive. A dispatched run runs on the default branch's head, not on the frozen commit, which travels in its payload: the platform can check that the review was asked for the frozen commit, not that it read it (2026-10-02). The final review is dispatched at most once — claimed in the ledger before GitHub is called; one whose call was never confirmed (a crash, an answer that never came) is never sent again and shows to the staff as "not confirmed", a manual re-dispatch being a later option — and it is sent whatever the organization's secrets, which Quiz never checks (F-GH-03 as amended 2026-10-14); a repository without a frozen run gets none ("no review"), and a repository archived as its lock (F-PROJ-09) gets none either, shown as degraded, and is never un-archived for it (product owner, 2026-10-02). While a repository's protection is suspended its final review is not dispatched (degraded, audited): its teacher's score settles it (orchestrator, 2026-10-02). Only Quiz's review counts: the review slot takes a review run only when Quiz's App triggered it — a review the student dispatched, or re-ran, is a trace (product owner, 2026-10-02), and so is a review received before the freeze, a checkpoint's (2026-10-02); a review run to verify (F-PROJ-08: protection suspended, or a restored head) never fills the review slot, nor one started before the repository's freeze or before its final review was asked (orchestrator, 2026-10-02). Review checkpoints: a name and a date, absolute or relative to the deadline (J−3, moved with it), which must lie ahead (422 due_past) and before the project's deadline (422 due_after_deadline); a relative checkpoint counts calendar days in Europe/Zurich, at the deadline's local time (J−3 of 23:59 is 23:59 three days before, across a change of the clocks), by one rule (checkpointDueAt). At its date, a grade-milestone dispatch goes to every repository whose deadline has not yet come (one with a later own deadline included), on the last commit received before the date that no bot pushed; a checkpoint never counts for the score. A checkpoint left behind by a deadline moved earlier never fires (void, deletable); a checkpoint can no longer be deleted once a dispatch of it was claimed (409 checkpoint_dispatched) (product owner, 2026-10-02). The wire names stay heig-classroom's (I16). Amended 2026-10-12 (owner's decision 3): in the criteria mode nothing is dispatched to the repository; the platform runs the final pass at the definitive freeze and a milestone pass at each checkpoint, which locks its tagged criteria for the final pass (the teacher may always correct them); a deadline moved later undoes the final pass and the validations before the release only — see ADR-106 §5, §6. P2 S
F-PROJ-12 Synchronisation of the source. A push to the source's handed-out branches shows the staff that the source is ahead, with the number of commits, counted from the source's sha handed out, recorded at the build and at each sync (a project built before shows "ahead" with no number; ADR-073, product owner, 2026-10-05). Nothing reaches the students until a member of the staff syncs: the distribution repository is updated (a new commit on top with squash) — a draft syncs its distribution repository only (ADR-073) —, then for every unlocked repository (one whose effective deadline has passed is skipped, locked or not, like a locked, archived or deleted one; ADR-073) the App pushes the update to sync/<branch> and opens a pull request — or updates the one already open: never two, per handed-out branch (one pull request per branch per repository, the row showing the default branch's; ADR-073) —, which the student merges and whose conflicts they resolve; the platform never merges. A repository already up to date gets no pull request (up to date: GitHub's compare listing no file that differs, not a commit count; ADR-073). The staff get a summary: opened, up to date, failed, and also updated and skipped (ADR-073); the state of the pull requests is shown per repository, with its last outcome (ADR-073). The staff see the sync's sha of the distribution, never a student the source's (N-SEC-20; ADR-073). P2 S
F-PROJ-13 The project page (staff): one row per student, or per group — and a staff seat's test repository, a badged row of its own counted nowhere (ADR-077; product owner, 2026-10-06) —, with the repository, the acceptance and invitation state, the last commit (date, hash), the commit count, the CI status, the final score with its source — the current, frozen, review and teacher scores in the row's sheet (orchestrator, 2026-10-11, by delegation of the product owner; 06 no. 58) —, and the flags — locked, after the deadline, to verify, deleted, access to revoke (a group member's revocation GitHub refused, F-PROJ-06); for a group project, the set it follows, and that it has drifted from a copy that stopped following (Resync with the set is allowed after the deadline, its confirmation naming every frozen repository it touches and each student it places in a group without a repository while Accept is closed; a frozen group with a repository whose set group was deleted is kept, its members following the set; refused once the project is released, 409 released — ADR-070's fourth amendment; product owner, 2026-10-05); a row opens the history of its runs. The commit count is the student's own, by the rule and from the receipts of their card (F-PROJ-04, N-SEC-20) — a group repository's and a test repository's included, with no GitHub call — and GitHub's total, the App's initial commit included, is only its tooltip, said stale when the cache is (product owner, 2026-10-06). Its one primary action follows the state: Publish for a draft, Sync when the source is ahead, Release once the scores are final. The page updates without a reload; a page view never waits for GitHub: the live state of a repository is cached (one minute, served stale up to fifteen while GitHub is slow or rate-limited) and shown stale as such. Two faces by state: a draft is a configuration form (F-PROJ-01, F-PROJ-03); a published or locked project is a dashboard whose figures filter the table, its settings still open to change demoted under a folded panel (product owner, 2026-10-11, the dashboard and the demoted settings; orchestrator, 2026-10-11, the filtering figures); the layout is apps/web/DESIGN.md's. Amended 2026-10-12 (ADR-106 §7, §8): in the criteria mode the row's sheet also lists the criteria total, and Release is the primary action once every repository is validated. Amended 2026-10-13 (product owner): a published or locked project's page is tabbed — Summary (the figures, the lifecycle), Repositories, Grading (criteria mode only), Settings; a figure of the Summary opens Repositories filtered by it; the settings still open to change are a tab of their own, no longer demoted under a folded panel; a draft stays a configuration form. P2 M
F-PROJ-14 Teacher score and release (D05). After the definitive freeze (409 not_frozen before), the staff may set a repository's score by hand, with a comment. The final score is the teacher's, else the review's, else the frozen score. Amended 2026-10-12: a fourth source, criteria (the repository's validated criteria), ranks between the teacher's and the review's; the teacher's score and comment stay the override; in the criteria mode the release also waits for every repository's validation (F-PROJ-26) — see ADR-106 §7, §8. Release makes the final scores the students' and the gradebook's (F-GBOOK); it is refused while a repository's final score rests on a run to verify (409 to_verify, F-PROJ-08; the teacher's score settles it — orchestrator, 2026-10-04) and while a confirmed Resync of a group project's groups is not fully applied on GitHub (409 group_sync_pending; ADR-070's fourth amendment, product owner, 2026-10-05). Before it, a student's score is marked indicative. A project has a grading scale that turns a score into a Swiss grade: Quiz's linear scale (1 + 5 × points / max, capped at 6, rounded to the tenth, F-EVAL-10), the default, or the preset "the score is the grade" for a score out of 6 (any other maximum falls back to the linear scale, with a warning); every score converts with its own maximum (06 no. 49). A score changed after the release updates the grade and shows "changed after release", as for an evaluation (F-GRADE-09). Audited. P2 M
F-PROJ-15 The student's project shows their repository and its invitation, the CI status, the evaluated commit and its run, the current score marked indicative (nothing under grading none), after the deadline the frozen score, and after the release the final score, the grade and the teacher's comment. Never: the source repository (neither its link nor its existence), the distribution repository, another student's anything, the runs after the deadline, the staff's flags (N-SEC-20). It updates without a reload, through the student's own channel only. Amended 2026-10-12 (ADR-106 §7, §9): the CI score stays indicative in every mode; in the criteria mode the page also shows the criteria table (F-PROJ-24). P2 M
F-PROJ-16 Delete and archive (D19). Archiving takes a project out of the lists, reversibly — the gradebook included, for the staff and the students: its grades leave the table and the mean until it is restored, which its confirmation, a dangerous action, says (product owner, 2026-10-14; ADR-074 amendment of 2026-10-14). An archived project stays reachable from its classroom's Activities tab behind "Show archived", as the archived classrooms of a course are (F-ORG-03), each row opening the page where it is restored (product owner, 2026-10-14; #864). Deleting it, after a confirmation that names it, deletes its rows in the platform — repositories, runs, scores, groups — and never a repository on GitHub: the confirmation says that the students' repositories, the distribution repository and the source stay where they are. Deleting the classroom deletes its projects the same way (F-ORG-09). Audited. P2 M
F-PROJ-17 Leaving the roster: removing a student from a classroom first revokes their access to the repositories of its projects (a group's included), synchronously — the GitHub account Quiz invited, not the one linked today: a revocation GitHub refuses on a repository it can reach refuses the removal (502 revoke_failed), the roster unchanged. When GitHub cannot be asked — the App no longer on the organization, the repository deleted on GitHub, no account ever invited — there is nothing to revoke: the removal proceeds and the audit records the revocation as skipped, with its reason (product owner, 2026-10-05). A staff seat that accepted holds a repository, revoked the same way (ADR-077; product owner, 2026-10-06). The same revocation runs first when a seat is unclaimed, when an e-mail change detaches its account, and when a self-enroll turns it into a staff seat. The student then leaves every group set and every project's copy, stopped ones included; the repositories, the runs and the scores are kept. A seat whose account changes is a departure of the former account followed by an arrival (ADR-070). P2 M
F-PROJ-18 GitHub changes are followed: a renamed repository or organization keeps its rows; a repository deleted on GitHub is marked deleted; an organization deleted, or Quiz's App uninstalled from it, suspends the project's writes and tells the staff (F-NOTIF-13), and nothing is deleted. P2 M
F-PROJ-19 Work mode: a project has a work mode, free (the default: the student's own tools), online (the workspace in the browser, the student invited with pull: only the workspace writes) or online_seb (the same under Safe Exam Browser, nobody invited before grading; launched from M6-07 only) (ADR-047 as amended; product owner, 2026-10-07). The online workspace and the projects sat in Safe Exam Browser (ADR-047) are phase M6 of the merge, after the cutover (D09); heig-classroom's online projects, all in test classrooms, are not imported (D09); the workspace is rebuilt in Quiz and tried before any migration of it. The same workspace also serves supervised evaluations (SEB or kiosk) through the workspace question type (04 §4.15), never an exercise: one workspace per attempt, seeded from the teacher's template and visible cases only, no git channel and a closed network, frozen and collected by the server at the attempt's deadline + 3 s, destroyed afterwards, graded by the teacher's tests on the runner as a proposal the teacher validates (M6-08, M6-09; ADR-075, product owner, 2026-10-05). Only an owner of the course whose account the administrator granted the workspace, or an administrator (ADR-047, 2026-10-08), sets a mode other than free; the administrator also sets each grant's number of simultaneous workspaces, carried by the project's creator while they own the course, else by its oldest owner, and enforced by the portal. The mode freezes once a workspace was launched; a group project stays free. The student opens their workspace from the project page (Open workspace); the staff see the project's open workspaces, the last update the workspace received and a Resync. Without a configured workspace none of it exists. P2 M
F-PROJ-20 The import (D20, D26): at the cutover, heig-classroom's projects come over with their state, dates, repositories, groups, runs, receipts and scores, into the Quiz classrooms the correspondence table names (D22), which their teachers created and connected to the same organization beforehand (06 no. 47), with the GitHub account links heig-classroom holds (06 no. 45); the reconciliation then catches up from the repositories themselves. One evening heig-classroom's web service is shut down and its data dumped and imported; from then on only Quiz acts on the repositories (06 no. 46); until that moment the teachers change nothing in it. A project created in Quiz before the cutover (D26 addendum) is Quiz's own; the import does not touch it. P2 M
F-PROJ-21 Real-time notices on the project page and the student's project, never notifications, computed in the browser from the page's data before and after each re-read (the page's interval, the projects hint), never from a server frame or field; nothing on the first read, nothing on an unchanged one. Worded through t() from structured notices, never a server sentence. On the staff's page the notices are aggregated per re-read, one per kind with its count ("3 pushes received"), never one per repository: acceptances, pushes, scores captured, final reviews dispatched. The student's are theirs alone, from the student view (N-SEC-20): the invitation accepted, their deadline applied, a push and an indicative score while the project is open. The deadline applied, for the staff, and the release, for the students, are the toasts of the notification kinds project_deadline_applied and project_grade_final (F-NOTIF-13), never doubled by a notice. There is no "protected files restored" notice — no datum on the page; the row's flag and the bell cover the cap — nor "sync done" — the page's last-sync line says it. No per-kind preference in v1 (product owner, 2026-10-05). Amended 2026-10-12 (ADR-106 §10): a project in the criteria mode dispatches nothing, so it has no "final reviews dispatched" notice. P2 S
F-PROJ-22 Groups formed by the students (ADR-070, lot 2): the staff open a group set to the students until a date (the server's clock), with a maximum size, which then binds them. While it is open, and as long as no group of the set has a repository in any project, a student holding a claimed seat creates and names a group, joins a group below the maximum, leaves their group, or renames the group they are in; the staff keep every right. At the closing nothing happens by itself: the staff place the rest. A student reads, while the set is open, its groups' names, sizes and members' names, and once closed only their own group; never an e-mail, a GitHub login, another classroom's set. Not in an impersonation, seb or kiosk session. Audited. (S1) It happens in a Groups tab of the student's classroom page, /classrooms/:id/groups, drawn only while a set reaches the students (F-ORG-15); (S2) a student sees an OPEN set, or one a published project of the classroom that is not archived names — a closed set no such project names is invisible; (S3) an open set is a row "Form your group until …" (fr "Formez votre groupe jusqu'au …") in Open now of the student's Activities, on the classroom page and the home, leading to the tab, never the urgent accent, no notification; (S4) while it is open, the students also see the students in no group, by first and last name only (ADR-070's third amendment; product owner, 2026-10-05). Refusals: set_closed once open_until has passed (no grace), set_frozen once a group of the set has a repository in a project, group_full at the maximum or above; opening a set without a maximum size is max_size_required. P3 M
F-PROJ-23 Grading by criteria (ADR-106 §1–§6, product owner, 2026-10-12). A project's staff may choose the grading mode criteria (F-PROJ-01, F-PROJ-03), never automatically; it is refused without a valid criteria.yml in the distribution. The project then keeps a read-only snapshot of its criteria, reloadable from the source. Each criterion is graded automatically from the tests, proposed by an LLM, or left to the teacher; a criterion that judges an identity is never sent to the model. The platform collects the evidence itself — the repository at a commit it chooses from its push receipts, the distribution's protected files in place of the student's, built and tested in apps/runner with the network closed (ADR-106 §3) — and one LLM pass per repository, its input masked of every name, proposes points and a rationale per criterion; a repository without evidence gets 0-point proposals with their reason. Audited. P2 S
F-PROJ-24 The criteria table (ADR-106 §6, §9; owner's decision 2). In the criteria mode the student's project (F-PROJ-15) shows one table: criterion, expected (its description), maximum, obtained — empty until a pass graded it or the teacher wrote it, every row not yet validated and released marked indicative, a lock on a locked row. Its rationale column appears after the repository's definitive freeze, marked provisional, and is final after the release; before that freeze no rationale is in the payload at all, a milestone's included; a rationale is plain text, shown escaped. Never the source of a row, a row's reason (no_commit, archive_too_large, repo_missing, llm_*, grader_error), the model, a superseded row, an evidence file, nor anything of another repository (N-SEC-20). P2 S
F-PROJ-25 The grading screen (ADR-106 §8): /projects/:id/grading, after the grading table of evaluations — a rail of repositories (to validate, validated) with ‹ › and progress; the selected repository's criteria (criterion, expected, maximum, awarded editable, the source CI / AI / teacher, the rationale folded, the reason of a missing pass); Validate per repository, then Next. A teacher's edit is a teacher row; a locked row stays editable. Regrade of a repository or of the project (F-PROJ-10). Audited. Amended 2026-10-13 (product owner): the maximum and the awarded points are one control, the points over the maximum. P2 S
F-PROJ-26 The release gate (ADR-106 §8; owner's decision 4): a project in the criteria mode is released only when every live student or group repository (a staff test repository excepted) is validated — every criterion of the snapshot has a validated row —, else 409 not_validated; a bulk Validate the rest as proposed is offered. A teacher-only criterion is written by a teacher, 0 included, never by default: while one has no row, Validate and the bulk action answer 409 criteria_incomplete, naming the repositories and criteria. The released score of a repository is its final score (F-PROJ-14); the criteria source exists only for a validated repository, otherwise it is empty, never 0. P2 S

F-GBOOK Gradebook

A classroom's grades in one table: its released evaluations and projects, and a mean (D06; F-RES-05 promoted). The gradebook reads the results; it never grades.

Id Requirement Phase Prio
F-GBOOK-01 The teacher's classroom page has a Grades tab: one row per claimed student seat (a staff seat never appears, ADR-018 §3), one column per activity whose results are released — an evaluation (F-GRADE-09), a project (F-PROJ-14) —; the staff's table also lists the columns not released yet, where marks can be set, their grades not shown and out of the mean (ADR-074, product owner, 2026-10-05). Each cell is the Swiss grade to the tenth: for an evaluation what its results give. An absence is 1.0 shown with a sigil and a colour of its own (the school's a1.0), which counts as 1.0; it has two sources, both shown with the sigil — derived (a released exam not taken: a1.0, computed and never stored; an exercise not opened is an empty cell) and a stored mark the staff set (absent, or the teacher's own score out of a maximum, which also fills the cell of a student who never accepted a project, 06 no. 48) (ADR-074, product owner, 2026-10-05). A project not accepted is otherwise an empty cell, which does not count (06 no. 48). A mark wins over the grade beneath it, but replacing a real grade is deliberate (override: true, else 409 grade_exists); clearing a mark is explicit; every set and clear is audited with before and after; a mark is staff-only until its column is released, and a student never reads its comment (ADR-074, product owner, 2026-10-05). Each cell names its source: the evaluation's results, or the project's teacher, review or frozen score (I42) — and, since 2026-10-12, its criteria score (ADR-106 §7). An archived classroom's gradebook is read-only (409 classroom_archived; ADR-074, product owner, 2026-10-05). An archived project has no column, staff or student, its marks and settings kept for its restoration (product owner, 2026-10-14; ADR-074 amendment of 2026-10-14). P2 M
F-GBOOK-02 Which columns count: exams and projects by default, exercises when the teacher includes them, polls never. Each column has a weight, a whole percentage, 0 to 100, 100 % by default, shown in the column's header; weights are relative, not a budget — they need not sum to 100 (ADR-074 as amended; product owner, 2026-10-08). The mean of a student is the weighted mean of the columns that count and that have a grade for them, rounded to the tenth: Σ(wᵢ·gᵢ) / Σ(wᵢ) over the counted columns the student has a grade in (a column with no grade for them is skipped and the others renormalised; an absence counts as 1.0); 0 % keeps the column in view and out of the mean; all weights at 0 % give no mean (product owner, 2026-10-08). The staff's table also shows the class means: per column the mean of its released cells' grades (an absence counts as 1.0, as in a student's mean; an empty cell is left out; none before the release), and overall the mean of the students' means; students never see the class means (product owner, 2026-10-08). No ranking anywhere. P2 M
F-GBOOK-03 A column follows its activity: a grade changed after the release (a regrade, a teacher score) changes the cell. P2 M
F-GBOOK-04 CSV export of the table: e-mail, name, one column per activity, the mean; the format of F-RES-02; staff only, no audit (like F-RES-02). One row per claimed student seat, a grade at one decimal, the absence mark written a1.0, an empty cell empty, a column not released suffixed (unreleased) (2026-10-05); a weight line under the header (each counted column's weight in whole percent, empty for a column that does not count and under the mean) and a last class_mean line (each column's class mean, the overall class mean under the mean), both carrying their label in the e-mail field (2026-10-08). P2 M
F-GBOOK-05 The student's classroom page has a Grades tab (F-ORG-15): their own cells, under the rule of F-RES-04 for an evaluation and after the release for a project; the mean only when the teacher publishes it, a switch of the tab, off by default, and with it each column's weight. The student's global Grades page (F-ORG-14) shows the same rows, in the same two groups: what counts toward the mean ("Count toward your average"), then what does not ("Indicative"); each column tells the student whether it counts (the flag alone, never the weight, before the mean is published too; product owner, 2026-10-13, #803). P2 S
F-GBOOK-06 Settings of the gradebook (counted columns, weights, the published mean) are staff writes, audited, open to every member of the course's staff, except that a mark replacing a real grade of a released column (override: true) is an owner's (403 owner_required, ADR-068; ADR-074, 2026-10-05), as is a mark over the derived absence (a1.0) of a released column, which needs no override (amended 2026-10-10). "Counted columns" is a per-column flag (exams and projects count by default, exercises are opt-in one by one); a weight is a whole percentage, 0 to 100 (F-GBOOK-02, 2026-10-08); the mean is the weighted mean of the released, counted columns the student has a grade in, computed from the displayed tenth-rounded cell grades (ADR-074, 2026-10-05). P2 S

F-ADMIN Administration

Id Requirement Phase Prio
F-ADMIN-01 List of users, roles, last sign-in. Promotion to teacher. P1 M
F-ADMIN-02 Runner status: available languages, queue, mean time, errors. P1 M
F-ADMIN-03 Global settings: LLM providers, default drill duration, announcement message. P2 S
F-ADMIN-04 Audit log of sensitive actions: deletion, re-grading, role change, release of results. P1 S
F-ADMIN-05 Super Powers: without them an admin reaches what a teacher reaches (own seats, pools by pool role, 404 elsewhere). Switched on from the settings, they open every course, classroom, evaluation and pool for one fixed hour of the server's clock, in that browser session only — never through an API token or an assistant, never extended — and are the condition to create a link to act as a student. A red banner states them on every page with the time left, a countdown in the last five minutes and the way out. Switching on, off, expiry and sign-out are audited. ADR-054. P1 S
F-ADMIN-06 Scheduled tasks: the server's periodic housekeeping, each with its status, last run, duration and last message; the administrator pauses a task, changes its period and runs it now. The clock of live evaluations is not among them (D10, 05 §5.4, Clock). P1 S
F-ADMIN-07 System status: can an exam run now, and does anything need the operator? Each check (the live clock and its symptoms — attempts and evaluations left open past their end —, scheduled tasks, background jobs, runner, database, disk, last backup) is OK, to look at, failing or unknown, with its value, when it was checked and, when not OK, a one-sentence cause; then what is deployed. Counts and sizes only, never who, never a log. A check that fails on two runs of the health.checks task in a row notifies every admin (bell, and e-mail by default), once, and again when it recovers. N-OPS-03, ADR-055. P1 M
F-ADMIN-08 Concept curation (issue #599, step 5; ADR-081, fifth addendum). The administration has a Concepts tab, a queue of the concepts that are not merged, the proposed ones first, searchable and filtered by status. Each concept shows its labels, qualifiers and descriptions in both languages, who proposed it and when, and the instance-wide number of live questions that use it: a number only, which an administrator reads without Super Powers, while pool names and statements need them (ADR-054, amended). The administrator validates a concept (refused while a language has no label), edits it (rename, qualify, fill the other language, describe: the administrator is not bound by the stop list) and deletes it while nothing refers to it (a question, deleted or not, a course that lists it, or a merge). The administrator merges a concept into a validated one (never into a proposed or merged one, nor itself), from the concept's sheet: every question link of the merged concept moves to the target, soft-deleted questions included, a question linked to both keeps one link, the concepts merged earlier into it are re-pointed to the target, and the target keeps its labels and status, and the courses that listed the merged concept list the target instead (once); all in one transaction, after which no link to a merged concept remains. There is no undo button; the audit keeps the question ids moved. The merge dialog offers, off by default (the merged label is dropped, ADR-081 §6), to keep the merged concept's labels as aliases of the target; its own aliases always move to the target, and the audit lists those the target answered to already. The administrator also adds and removes aliases on the sheet: other names a concept answers to, with no language, which only the administrator curates. A typed word resolves by its key to a label or an alias; an alias equal to the concept's own label is refused, and one equal to another live concept's label or alias is refused with the colliding concepts named until the administrator confirms, because that word is then ambiguous for every teacher; symmetrically, a concept cannot be created or renamed onto a label another concept answers to as an alias. Audited: concept.validate, concept.edit, concept.delete, concept.merge, concept.alias_add, concept.alias_remove. The Duplicates filter lists the pairs of concepts that look alike, computed on read from labels, qualifiers and aliases without a model: a label of one equal to an alias of the other, the French label of one equal to the English label of the other, labels a few edits apart (the resolver's own rule), and the same label under different qualifiers (homonym candidates, never proposed for a merge); merging a pair opens the merge dialog with the target chosen. Under them, Ask the AI (secondary, on demand, on the daily cap of the LLM gateway, purpose concepts) asks a model for further pairs from the labels and qualifiers alone — no description, alias, question, pool or person is sent; the pairs come back labelled as AI suggestions, filed as close, translation, homonym or related with a short reason, validated by the server, kept in the page only and never written automatically; Merge is offered for close and translation pairs only, Edit for all. The administrator also links concepts on the sheet's Relations section (issue #599, step 7c; ADR-081 §4 and sixth addendum): a concept may have several broader concepts (a hierarchy, never a cycle: an edge that would close one is refused with the path named) and related concepts (symmetric; refused between two concepts of one broader chain, which a hierarchy already connects). Relations are the administrator's alone, saved at once and audited (concept.relation_add, concept.relation_remove); teachers read them with the concept, and the concept picker shows the broader path ("Memory > Pointer") under a concept. The curation queue shows that path under each concept's name and at the top of its sheet (with the count of its other broader concepts), and an Unplaced filter lists the concepts with no broader and no narrower concept, whatever their status, to be given a place from their sheet (#897). The sheet also lists, read-only, the concept's direct narrower concepts (each placed from its own sheet), each candidate of its broader and related searches shows its broader path, and each broader candidate names the narrower concepts it currently holds (#897). On All, To validate and Validated the queue can also be read as a tree (a view switch remembered per browser): each concept once, indented under the broader concept its path follows, its other broader concepts named, under a filter or a search the ancestors of a shown concept kept dimmed and without actions as its path. An AI "related" pair in the Duplicates view can be added as a relation in one click. A merge rewrites the loser's relations onto the target: the edges between the two are dropped and listed in the audit, a rewrite that would close a longer cycle is refused (409 concept_merge_cycle, the path named, nothing written). A selected concept covers its narrower ones in the pool filters, the course filter and the mastery (step 7d, sixth addendum §10). The Relations filter (ADR-114, #897 step A1) lists relations proposed without a model: a concept whose label contains the whole label of another, in the same language, under it (the most specific only; a qualified label only when its qualifier is there too), and two concepts on at least 3 live questions together, at least half of the less-used one's, as related; the graph's own refusals and the relations already there are left out, and the list holds only candidates that can all be accepted together. Counts only (no question, statement, pool or person), read without Super Powers. Nothing is checked; the administrator ticks rows and Accept the n selected writes them one by one through the relation route (broader first; each refusal shown on its row, the others go on; audited as any relation); Dismiss hides a row until reload, nothing is stored. Validate is not offered in this view. Below these, when the platform has a model, Propose relations with AI (secondary, on demand, on the daily cap and the per-minute budget of the duplicates pass, purpose concepts; #897 step A2) sends the labels and qualifiers of the live concepts (at most 1000) with the Unplaced ones marked, at most 40 of them a call, and nothing else; its broader rows (at most two per concept), related pairs with a reason and missing top concepts (a new concept in the administrator's language gathering at least two others; one the vocabulary already names becomes broader rows towards it) are validated by the server against the graph and shown apart, a row the pre-pass lists already shown once; Ask again goes on with the unplaced concepts not asked about yet in the page. While rows are ticked, a row that would conflict with them is disabled. Accepting a top's row first creates the top concept, proposed, through the concept creation route (an existing one of that name is reused), then writes the edges as above. On a concept's sheet, Suggest a broader concept (secondary, when the platform has a model; #897 step A3, ADR-114 §3) makes the same call for that one concept and shows only its broader rows, each with the broader concept's path, and a missing top concept that would gather it; each is added on its own through the relation route (Add as broader, or Create and add, which first creates the top concept, proposed), nothing being written before. P1 M
F-ADMIN-09 Platform activity (ADR-107). The administration has an Activity tab: how many people are active now (seen within 15 minutes), were active in the last 24 hours and in the last 7 days, each split into students and staff by their stored role (staff: teachers and administrators); and a grid of the last seven calendar days, today last, by hour of Europe/Zurich, each cell the exact number of distinct people active in that hour, shown for students, staff or everyone. Class hours are Monday to Friday, 08:00 to 18:00 Europe/Zurich: the cells outside are marked, and the screen gives the share of the students' active hours that fell outside, the window written beside it. Counts only: no screen shows a person's hours. An hour counts a person when a request of their own session (portal, SEB or kiosk) reached the server in it — never an impersonation, an API or OAuth token, background traffic (the event stream, a page idle or hidden) or an anonymous guest. The administrator's list of accounts (F-ADMIN-01) shows each account's last activity; no roster does. P2 S

F-UI Interface

Id Requirement Phase Prio
F-UI-01 Festive touches. On ten days of the year (Christmas, Easter, Pi Day, a computing pioneer's birthday…), the frame's logo wears an accessory that opens a short sheet about the day with its Wikipedia article, and an ambient animation of five seconds at most plays once a day on the home and the lists. Never in an attempt, a preview, a projection, a Safe Exam Browser or kiosk session, nor under reduced motion; switched off from the settings, per browser. ADR-093. P3 C