Commit graph

2 commits

Author SHA1 Message Date
Daniel
532d613393 feat: question folders, per-section notes, and two feedback paths
Four things that share a spine, so they arrive together.

**Folders.** A hand-picked set of questions, and the fourth thing a grant can
name beside exam, discipline and category. Deliberately not `user_collections`
with a sharing flag: a library is a consequence of access — you save what you
can already see — while a folder is a source of it, and one table holding
thousands of private lists beside a handful that confer permission is one
mistake away from a leak. Built from the question manager, granted on /access.
Membership stays with the owner and moderators so a grantee cannot widen their
own reach, and deleting a folder takes its grants with it.

Two live constraints had to be rewritten to accept it: `ck_grant_has_a_dimension`
and `uq_grant_dimensions` both predate `folder_id`, so a folder-only grant
failed the check and two folder grants collided on the unique index.

**Per-question feedback.** The learner's half already existed. What was wrong
was who could read it: any grant at all let an educator list and delete reports
about the whole bank. Reports are now scoped by `question_scope_predicate`, the
same predicate that decides which questions that educator can see, and a reply
thread makes the report a conversation the learner can follow rather than a
form that swallows what they said.

**Per-section notes and article feedback.** Two tables on purpose:
`article_section_notes` is private to whoever wrote it, `article_feedback` goes
to whoever maintains the article. Both point at the section id inside
`articles.sections` rather than at `article_section_index`, whose rows are
dropped on unpublish — a cascade from there would delete a learner's writing
because an educator took an article down for an afternoon. A rename keeps a
note attached; a deleted section leaves it marked orphaned under the heading it
was written on, for its writer alone to remove.

The header's feedback badge covers both, because questions and reading are the
same job to whoever is doing it.

Migration i9f0a1b2c3d4. 556 backend and 572 frontend tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-12 18:37:43 +02:00
Daniel
392a2cc483 feat: feedback replaces comments; Qbank is a landing page; one question page
Comments are gone. A thread under every question was a discussion nobody
moderated, and what it was used for was telling an educator something was
wrong. That is now feedback: a private report, carrying the question id,
that someone is expected to act on.

  * Give feedback sits in the question bar's new "more" menu, beside Save
    and Share — occasional actions, folded away rather than each taking a
    slot in a bar read on every question.
  * An educator gets a badge of what is outstanding. Each row names the
    question and opens its editor, where the report sits beside the field
    it is about; reply, resolve, reopen or delete from there.
  * Resolving keeps the report. A question with a history of the same
    complaint should visibly have one; deleting is for the ones that were
    never about the question.
  * A granted educator sees only their own branch. The badge answers
    quietly with zero for someone with no access, so the header can ask
    without first working out who is asking.

The question bank is now the Qbank: create a session, and the last three
with Resume. Its facets, tag tree and create-a-quiz were a second copy of
the custom-session page; marking and folders belong in the player while
you are sitting a question. Import and export moved to the question
manager, which is the one place questions are managed, and which now has
a Preview that opens over the list instead of a page you have to come
back from.

Fixed while there: a session in progress analysed as 0/0 with an empty
table, because the analysis read attempt_answers — written on submit —
while the session list counted the saved progress. They read the same
thing now. The category trail is gone from the player: it named the
answer's own topic and led out of a session part-way through. An option's
reasoning opens on click and closes on the next one.

Backend 253/253, frontend 323/323.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-11 18:50:57 +02:00