Skip to content

fix: four-perspective audit remediation (student flow, security, UX, AI) - #24

Merged
veniplex merged 9 commits into
mainfrom
claude/learning-workflow-ai-support-nu8iho
Jul 19, 2026
Merged

veniplex merged 9 commits into
mainfrom
claude/learning-workflow-ai-support-nu8iho

Conversation

@veniplex

Copy link
Copy Markdown
Owner

Warum

Nach dem Merge des zielbasierten Lern-Workflows (v1.2.0) wurde die gesamte App aus vier Perspektiven auditiert (Student, Backend-, Frontend-, AI-Developer): sechs parallele Code-Audits, ~90 belegte Findings. Dieser PR behebt sie in 8 verifizierten Phasen — jede grün auf typecheck + Tests + Lint, Migrations-Phasen zusätzlich mit db:generate ohne Drift, plus erfolgreichem next build.

Was sich ändert

Student — der Lernplan erfüllt jetzt „vor der Prüfung wiederholen statt Neues lernen"

  • Konsolidierungsfenster (reviewDays): Lern-Tasks sind auf Prüfung − reviewDays fällig; im Fenster landen nur noch Wiederholung + Probeklausur + FSRS-/Fehler-Deck-Sessions, kein neuer Stoff; der Prüfungstag ist frei und blockiert.
  • Kein Wiederaufleben von Erledigtem: Session-Abhaken schließt die Tasks; Replan behält gepinnte/erledigte Sessions und holt verpasste nach.
  • Geführter Flow + Feedback: Setup-Checkliste, „Plan veraltet"/„N verpasste Sessions"-Banner mit Ein-Klick-Replan, Readiness-Ampel (Aufwand vs. Kapazität bis Prüfung), „diese Woche weniger Zeit"-Override, Prüfungstermin-Sync in den Kalender.

Backend — Sicherheit, Korrektheit, Performance

  • Geschlossene/Invite-Registrierung nicht mehr per Social/OIDC umgehbar; Planungshorizont gedeckelt (DoS-Fix); goalId-IDOR geschlossen; tus-Upload-Ownership; transaktionale get-or-create/createModule; CSP + HSTS; getSetting-Cache; DB-Pool-Limits; AI-Nutzungslimits auf Hintergrundpfaden; Storage-Cleanup bei Kaskadenlöschung; UTC/Lokalzeit-Fix.

Frontend — Konsistenz, Mobile, Performance

  • Chat- und Kalender-Stack dynamisch geladen (raus aus dem Shell-Bundle); einheitliche Dialoge/Empty-States; Mobile-Layouts (Tab-Leiste mit fixem „+", gestapelte Prefs, listWeek-Agenda, gestapelte Versuchstabelle); optimistische Toggles.
  • Lokalisierte Fehler: stabile ERR:<code>-Fehler statt roher deutsch/englisch gemischter Toasts; Reminder folgen der User-Sprache; de-DE-Formatierung/Jargon entfernt.

AI — nativ, kostenbewusst, transparent

  • Generierungs-Aufrufe mit Output-Budget (keine Truncation); BYOK-Sackgasse mit „API-Key in Einstellungen"-Hinweis; /ai & Palette bei fehlender AI ausgeblendet; Confirm-Cards zeigen Modulnamen statt UUIDs; AI-Herkunft im Kalender markiert.

Lern-Kern

  • Material-Verarbeitungsstatus & Fehler sichtbar, ask-document erst bei „ready"; Fehler-Deck sprachunabhängig + erklärt; paralleles Quiz-Grading mit Fortschritt, „nicht bewertet" statt 0 %, Retry-wrong nur bei Fehlern, Prev/Skip; FSRS-Intervallvorschau; tsvector-Volltextsuche inkl. Karteikarten/Fragen.

Migrationen (additiv)

  • 0041 — Konsolidierung: plan_session.kind, semester_plan.week_overrides/stale_at/last_warnings, event.goal_id, GoalConfig.reviewDays.
  • 0042 — Suche & Locale: material.text_content_tsv (GIN), user_prefs.locale, event.skip_dates.

Verifikation

  • npm run typecheck → 0 · npm run test222 bestanden (u. a. erweiterte Scheduler-Property-Tests fürs Konsolidierungsfenster/Wochen-Override/Horizont-Cap, replan-, Readiness-, Suche-, Recurrence-Tests) · npm run lint → 0 · npm run build → erfolgreich (CSP verifiziert) · beide Migrationen db:generate ohne Drift, additiv.
  • Mangels DB in dieser Umgebung sind die Migrationen nicht zur Laufzeit ausgeführt; verifiziert über Snapshot-Konsistenz und Review.

Note

Dev-Umgebung: bewusste additive Schema-Änderungen. Ein Teil von Phase R7 (goal-card-Dialog-Migration + Mobile/Optimistik) wurde nach einem Agent-Abbruch manuell fertiggestellt und ist grün.

🤖 Generated with Claude Code

https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7


Generated by Claude Code

claude added 9 commits July 19, 2026 12:44
- plan scheduler: cap schedule horizon at today+365d and clamp task dueDate
  to <=today+3y (DoS via far-future deadline); degenerate session-config guard
  + slot-loop backstop; expandAbsences clamps range to <=400 days
- plan tasks: validate client goalId belongs to the module (IDOR)
- availability: strict HH:mm regex + from<to refine
- auth: disableImplicitSignUp on social/OIDC when registration is closed/invite
  (bypass fix); explicit session expiry, rate limit, min password length
- /api/health: default GET no longer writes storage; full probe gated behind
  ?full=1 + HEALTH_TOKEN, 30s cache
- tus uploads: PATCH/HEAD/DELETE verify the stamped userId owns the upload
- audit undo: scope create/update-undo deletes by userId for user-owned tables

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
The study plan now enforces "learn early, consolidate before the exam":
- migration 0041 (additive): plan_session.kind (study|review|cards),
  semester_plan.week_overrides/stale_at/last_warnings, event.goal_id, and
  GoalConfig.reviewDays
- tasks: learning tasks are due exam-reviewDays; a spaced review series +
  companion FSRS/mistakes "cards" tasks + a mock fill the window
- scheduler: no learn task inside the consolidation window (assignment due
  in-window excepted), only review/cards there, no session on exam day,
  per-week capacity caps, horizon_clipped warning, earliestDue hoisted
- recompute uses collectReplannableTasks (keeps pinned/done future sessions,
  catches up past-undone tasks); session kind persisted; warnings persisted
- toggleSession completes/uncompletes its tasks; staleness flag on every plan
  mutation; auto-replan only on availability save, exam-date change, session done
- exam goals sync an all-day exam calendar event (blocks the exam day)
- task regeneration reconciles stale/removed generated tasks
- reminders use local today; calendar bounds its plan-session query

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
- setup-checklist: derives the exam-date -> availability -> tasks -> compute
  sequence from data; shown on module plan tab, strategy board first run,
  onboarding CTA
- plan banners: "plan is stale" one-click replan + "N missed sessions - catch
  up"; recompute shows a summary toast with humanized warnings
- readiness (pure, tested): exam goal card shows on-track/at-risk/unreachable
  from remaining task minutes vs capacity-to-exam, plus persisted warnings
- week capacity override ("less time this week") in the availability editor
- shared module-plan-prefs component (save-on-change) used by both the strategy
  board and the module plan tab
- deterministic actions drop the Sparkles icon; AiBadge on AI-generated tasks;
  bonus 0-1 fraction + weight helper text; outline nudge
- i18n plan.* additions (de/en parity)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
- action-errors: typed ERR:<code> layer + toastActionError/useActionErrorToast
  client mapping (mirrors the AI_ERROR pattern); user-facing throws across
  plan/goal/writing/thesis/materials/usage actions now emit stable codes,
  localized client-side (de/en), unknown -> GENERIC
- reminders: drop hardcoded de-DE; translatable notification templates via a
  job-safe createTranslator + reminderLocale() seam (R5 wires per-user locale)
- formatters: grade-goal + mini-calendar use the active locale (lib/grades
  keeps the fixed German grade-scale convention, documented)
- wording: drop "BYOK"/"Token-Verbrauch" jargon and the "Embedding-Modell" hint
  from user-facing strings
- mistakes deck: language-independent identity via deck.kind, localized label +
  "Auto" badge/tooltip; reviewCard now revalidates (comment fixed)
- errors coverage test (every code has de+en keys)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
…udit R5)

- migration 0042 (additive): material.text_content_tsv (GIN) for full-text
  search, user_prefs.locale, event.skip_dates
- materials: processing/error banner on detail page, ask-document gated on
  ready, chunk N/M progress, zip-unpack failure surfaced as a failed material
- decks: log partial minutes on leave, FSRS next-interval preview on ratings
- quizzes: parallel free-text grading with progress, "N cards to mistakes deck"
  link, honest "not graded" instead of 0%, retry-wrong only when wrong exists,
  prev/skip navigation, estimated progress for large single-shot generation
- dashboard: NextExam scoped to active program, TodayFocus fallback CTA
- calendar: delete single recurring occurrence via skip_dates, palette deep-link
  to a focused event, consistent server-local datetime handling
- search: tsvector (GIN) material match + flashcard/question union
- notifications: category push toggles reconciled with subscription state
- reminders now follow the persisted user locale
- tests: recurrence skip-dates, search query builder, FSRS interval preview

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
- writing project + retryThesis + createModule: transactional / onConflict so
  concurrent get-or-create can't violate the partial unique indexes or leave
  goalless modules
- first-admin promotion moved to a post-commit hook under a pg advisory lock
- material file route returns 416 for unsatisfiable ranges
- cascade delete of program/semester/module enqueues a sweep-orphan-files job
  to remove the underlying storage objects (crash-safe, chunked)
- cron schedules honor CRON_TZ; db pool gets explicit max/idle/connect timeouts
  and prepare:false for a transaction pooler
- background AI paths respect the usage cap (embedding/OCR skip, search/title
  degrade gracefully)
- getSetting gets a 30s in-process TTL cache (invalidated on write)
- enforcing Content-Security-Policy (KaTeX/pdf.js/AI-safe) + HSTS
- chat route rejects oversized bodies (413) before buffering
- new ttl-cache + storage-paths helpers with unit tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
…dit R7 part 1)

- chat stack (ai-sdk, markdown, katex) and FullCalendar are now dynamically
  imported (ssr:false), out of the shared app-shell first-load bundle
- FormDialog gains a scrollable/contentClassName prop; goal-card attempt dialog
  migrated to it; goal-card delete uses ConfirmDeleteDialog (no native confirm)
- goal-card attempts render as a stacked layout instead of a wide table
- shared EmptyState component
- calendar adds a listWeek agenda view, defaulted on phones (E21)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
…udit R7 part 2)

- module tabs: scrollable tab strip with a pinned, non-scrolling "more tools"
  button so it stays reachable on phones (D5)
- module-plan prefs row stacks its fields under sm (D4)
- session dialog + today-plan card disable toggles while a mutation is in
  flight (useTransition) to prevent double-fire; localized errors (D13)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
- generation calls (writing outline/milestones/sources, deck cards, thesis
  brainstorm) now pass GEN_PARAMS + an explicit output budget (no mid-JSON
  truncation); added maxTokensForText helper
- BYOK dead-end closed at request time: detect usable key presence; chat and
  generation surface an actionable "add your API key in Settings" message/link
  instead of a generic auth error
- /ai short-circuits to a friendly "not configured" state and the command
  palette hides the AI entry when no model is available
- generation-job failures show a friendly message (raw error kept as tooltip)
- write-tool confirmation cards whitelist + localize fields and resolve
  moduleId to the module name (no raw uuids/ids)
- AI-generated calendar events get a provenance marker in every view
- usage feature keys are localized (cost estimate intentionally skipped)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Nrz9bERddhwVXWuZyVBn7
@veniplex veniplex added the release-candidate Creates a new release and deployment of docker image, once merged. label Jul 19, 2026
@veniplex
veniplex merged commit 6d089cf into main Jul 19, 2026
1 of 2 checks passed
@veniplex
veniplex deleted the claude/learning-workflow-ai-support-nu8iho branch July 19, 2026 17:16
github-actions Bot added a commit that referenced this pull request Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-candidate Creates a new release and deployment of docker image, once merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants