Preskočiť na obsah

Changelog

Kompletná história zmien projektu GitPulse - od prvého commitu po najnovšie úpravy. Formát: Keep a Changelog.


[2026-05-17] - Progressive skeleton table loading 2026.5.17.post3

Zmenené

  • Zdieľané dashboard table skeletony teraz používajú postupné načítanie: najprv sa zobrazia tri riadky a až pri pomalšom lazy requeste sa otvorí page-specific počet riadkov.
  • Dokumentácia quality gates dopĺňa frontend loading contract: skeleton pre sekcie, ktoré sa postupne naplnia obsahom, spinner pre akcie a background prácu, bez miešania skeletonu a spinneru pre rovnaký región.
  • Scale load-test konzolový status používa ASCII [PASS] / [FAIL] značky, aby kontroly neboli závislé od Unicode podpory vo Windows termináli.
  • Aplikačná aj dokumentačná metadata teraz používajú 2026.5.17.post3.
  • Service-worker cache verzia je teraz gitpulse-v27.

[2026-05-17] - Avatar loading a health version display 2026.5.17.post2

Opravené

  • Avatary na dashboarde a v topbare sa teraz načítavajú eager s vysokou prioritou a používajú neutrálny image background. Pomalá odpoveď GitLab avatar URL už počas vstupu na stránku neukáže fialový initials fallback.
  • Health karta v Admin System zobrazuje runtime verziu presne tak, ako ju vracia /health (2026.5.17.post2), bez pridania prefixu v. Forma v... je vyhradená pre názvy git tagov.
  • Service-worker cache verzia je teraz gitpulse-v26.

[2026-05-17] - CSP inline cleanup a release metadata 2026.5.17.post1

Zmenené

  • Aplikačná aj dokumentačná metadata teraz používajú 2026.5.17.post1. Existujúci tag v2026.5.17 zostáva nezmenený, preto tento release používa .post1 namiesto prepisovania histórie tagov.
  • Dashboard shell už nevkladá inline script pre registráciu service workera. Registráciu rieši zdieľaný bundle core.js.
  • Warning toasty na stránke Admin Events sú riadené cez JSON gp-page-config a renderuje ich admin-events.js; stránka už negeneruje state-specific inline scripty.
  • DLQ loading spinner používa zdieľanú CSS utilitu .gl-spinner-inline namiesto inline style atribútu.
  • Service-worker cache verzia je teraz gitpulse-v25.

Overené

  • Konzervatívny dead-code scan nenašiel bezpečných kandidátov na odstránenie.
  • Typography cleanup scan nenašiel žiadne project-owned súbory na zmenu.

[2026-05-16] - Semester scope a docs hygiene 2026.5.16.post2

Pridané

  • Formulár nastavení kurzu zobrazuje viditeľnú info-poznámku "Kaskáda rozsahu", ktorá vždy upozorňuje, že zmena semestrálneho okna prepočíta rozsah pre všetky dashboardy, synchronizácie, hodnotenie zhody a reporty.
  • Pred uložením zmeny semestrálnych dátumov sa zobrazí potvrdzovací dialóg s presným zoznamom dôsledkov (čo bude skryté, čo bude opäť pridané).
  • Prázdne stavy v profile študenta uvádzajú aktuálne semestrálne okno ("Showing current semester window: ..."), aby bolo jasné, prečo neexistujú žiadne komity / MR / issues.
  • Skript na čistenie typografie teraz pokrýva JSON, web manifesty, extensionless deployment súbory a voliteľne i18n katalógy; normalizuje smart punctuation, dekoratívne šípky a box-drawing oddeľovače v komentároch.

Zmenené

  • Aplikačná aj dokumentačná metadata používajú 2026.5.16.post2. Omylom future-dated metadata boli opravené, pretože dátum releasu je 16. máj 2026.
  • Sweep, enrichment a LLM joby (enrich_issue, enrich_mr, llm_job, _enrich_all_async) explicitne filtrujú entity vytvorené pred course.settings.semester_start_date. Staršie komity, MR, issues a pipeliny zostávajú uložené, ale nie sú znovu spracované ani započítané do current-semester metrík.
  • Dashboardy admina, hlavnej stránky, kurzov a tímov používajú spoločnú helper-funkciu semester_floor_utc, takže read-path a worker-path zdieľajú presne tú istú dolnú hranicu.
  • Spodná hranica pre HTML5 validáciu dátumov sa automaticky posúva každý rok (semester_date_min = January 1 of today.year - 2) namiesto fixného 2020-01-01 - tým sa zabraňuje vstupu zastaraných dátumov, ktoré by "spôsobili nulový rozsah" a tichú stratu komitov.
  • Reporty a exporty používajú zdieľaný mobile-safe blob download helper.
  • Service-worker cache verzia je teraz gitpulse-v24.

Opravené

  • Generovanie reportov zachováva full name učiteľov, počíta distinct student improvements, uvádza compliance delta ako percentuálne body a spoľahlivo sťahuje PDF aj v mobilných prehliadačoch.
  • Vrchný progress bar sa už nespúšťa skôr, než AJAX form handlery stihnú zastaviť native submit, takže report formuláre nemajú falošný progress flash.

[2026-05-16] - Health version follow-up 2026.5.16.post1

Opravené

  • Aplikačná verzia je teraz 2026.5.16.post1.
  • Runtime kontajnery teraz čítajú project.version z pyproject.toml, keď nie je dostupná installed package metadata, takže /health a metrics už po source-checkout deploymente nehlásia version="unknown".

[2026-05-16] - Course-scoped dashboard release 2026.5.16

Zmenené

  • Aplikačná verzia je teraz 2026.5.16.
  • Aktivita v detaile študenta je pri nastavenom začiatku semestra obmedzená na aktívne časové okno kurzu. Importovaná história repozitára zo starších akademických rokov sa skryje z tabuliek commitov a riadkových súčtov namiesto toho, aby nafukovala aktuálny kurz.
  • Weekly compliance výpočet aj historický backfill ignorujú issues, merge requesty, review aktivitu, pipelines a commity pred začiatkom semestra.
  • Tabuľky členov už neduplikujú globálne GitLab access role vedľa mena člena, keď ich už zobrazuje samostatný stĺpec Role.

Opravené

  • Staré commity z rokov 2021/2022 už nemôžu v kurze 2026 vytvárať zavádzajúcu commit quality ani contribution totals.
  • Dashboard latest-changes feed už neobsahuje future-dated záznam.

[2026-05-15] - Declarative client actions release 2026.5.15.post2

Zmenené

  • Aplikačná verzia je teraz 2026.5.15.post2.
  • Reload ovládacie prvky generované JavaScriptom teraz používajú spoločný kontrakt data-action="reload-keep-scroll" namiesto inline onclick handlerov.
  • Read-only hint v detaile support ticketu používa CSS triedu na spacing namiesto inline štýlu v template.
  • Service-worker cache namespace zvýšený gitpulse-v6 -> gitpulse-v7 a generované bundle sú obnovené.

Opravené

  • Offline fallback page už neemituje inline click handler; retry používa obyčajný GET formulár.
  • Pridané statické frontend testy, ktoré chránia source JS a service worker pred návratom inline onclick= handlerov.

[2026-05-15] - Login alert polish release 2026.5.15.post1

Zmenené

  • Aplikačná verzia je teraz 2026.5.15.post1.
  • Chyby autentifikácie na login stránke používajú reusable GitLab-style alert: tmavý surface, červený horný border, alert ikonu, dismiss akciu a žiadne inline štýly.
  • Client-side login failures používajú rovnaký alert DOM/CSS kontrakt ako server-rendered chyby.
  • Service-worker cache namespace zvýšený gitpulse-v5 -> gitpulse-v6 a generated bundles sú obnovené.

Opravené

  • Odstránený starý login-specific error block a neplatný red fallback v login stylesheet.
  • Pridané statické testy pre login alert component contract.

[2026-05-15] - Version baseline a obnovenie cache

Zmenené

  • Aplikačná verzia je teraz CalVer 2026.5.15.
  • Dôvod: repozitár začal ako full-codebase initial commit a pre-release audit pred týmto normalizačným commitom našiel 1875 commitov, 47 Alembic migračných súborov a žiadne historické release tagy. Spätne zrekonštruovaný SemVer by bol arbitrárny; CalVer čestne zapisuje dátum produkčného releasu.
  • Service-worker cache namespace zvýšený gitpulse-v4 -> gitpulse-v5.
  • Documentation status, deployment príklady a dashboard latest changes sú obnovené pre release 2026-05-15.

Opravené

  • dead-code-report.txt je ignorovaný ako generovaný lokálny/CI artefakt.
  • Overené, že aktuálne static/dead-code guard skripty sú shell-independent a nespoliehajú sa na git ls-files.

[2026-05-14] - Production-safe cleanup release 0.1.1

Zmenené

  • Stabilizované lokálne a CI gate-y: normalizácia test env, shell-independent pre-push unit runner, Ruff pin na 0.15.4, bundle drift check, static artifact guard a non-blocking dead-code report.
  • Dashboard scheduler registry teraz zodpovedá produkčnému scheduleru: 26 registrovaných jobov vrátane email log retention, cleanup expirovaných previous webhook secretov, team discovery a semester auto-archive.
  • OpenAPI hygiena: /dashboard/login GET má stabilný operation id a HEAD je skrytý zo schémy, aby nevznikali duplicate operation IDs.
  • Test suite je warning-clean v default lokálnom gate: 2521 passed, 19 deselected.

Opravené

  • Odstránený náhodne trackovaný root artefakt -w.
  • Bundle/vendor skripty používajú ASCII status markery, takže Windows cp1252 terminály nepadajú.
  • Previous webhook secret sa akceptuje len do webhook_secret_previous_expires_at, potom ho čistí scheduler.
  • CSV roster import validuje všetky riadky pred zápisom, takže nevzniká partial commit.
  • Rubric save odmieta nulový súčet category weights.
  • Redis session-store fallback inkrementuje session_store_fallback_total a readiness robí probe iba keď je store zapnutý.

[2026-05-14 - root-cause patch] - Opätovné prihlasovanie na každej návšteve opravené

Príčina

Defaulty v app/config.py boli správne (persistent_sessions=True, 30 dní idle, 90 dní absolútne), ale docker-compose.yml riadky 11-12 hardkódovali zlé defaulty:

YAML
PERSISTENT_SESSIONS: ${PERSISTENT_SESSIONS:-false}
SESSION_MAX_AGE_SECONDS: ${SESSION_MAX_AGE_SECONDS:-28800}   # 8 hodín

Produkcia mala v .env.production PERSISTENT_SESSIONS=true, avšak SESSION_MAX_AGE_SECONDS=28800 (8 h) znamenalo, že idle okno podpísanej cookie vypršalo po 8 hodinách - kto sa vrátil raz za deň, musel prejsť celým GitLab OAuth flow.

Opravené

  • docker-compose.yml defaulty prepnuté: PERSISTENT_SESSIONS:-true, SESSION_MAX_AGE_SECONDS:-2592000 (30 dní) a nový SESSION_ABSOLUTE_MAX_AGE_SECONDS:-7776000 (90 dní).
  • redeploy.py injektuje všetky tri hodnoty do .env.production pri každom deploye (idempotentný sed/echo vzor ako pri GIT_SHA). Auditovateľné, odolné voči driftu.
  • .env aj .env.production aktualizované na nové 30 d / 90 d hodnoty ako baseline pre ďalší deploy.

Odstránené (dead code)

  • session_auth.verify_password() a session_auth.hash_password() - produkcia beží výhradne cez GitLab OAuth; tieto pomocné funkcie nemali žiadneho volajúceho mimo import smoke testu. Odstránené spolu s už nevyužívaným bcrypt importom v session_auth.py (samotný bcrypt package zostáva v pyproject.toml, využíva ho webhook autentifikácia).
  • Príslušné riadky v tests/unit/test_module_imports.py odstránené.

Overené ako používané (ponechané, NIE dead code)

Audit potvrdil, že gitlab_oauth.refresh_access_token() JE používaná - volaná z dependencies.get_fresh_gitlab_token() (riadok 626) s 5-minútovou rezervou pred expiráciou tokenu. Tichý OAuth refresh pri telemetry/sync volaniach už funguje.


[2026-05-14] - Trvalé relácie a celosystémová multi-tab synchronizácia

Zmenené (autentifikácia)

  • persistent_sessions prepnuté na True (default) - relácie prežijú reštart servera aj redeploy. Používateľ sa už nemusí opätovne prihlasovať pri každom nasadení nového buildu.
  • Idle TTL 8 h -> 30 dní, absolútna TTL 24 h -> 90 dní (session_max_age_seconds, session_absolute_max_age_seconds v app/config.py). Obranné mechanizmy ostávajú: podpísaná cookie + Redis opaque sid + Secure/HttpOnly/SameSite=Lax/__Host- prefix + UA fingerprint (voliteľný) + explicitné zneplatnenie pri logoute.

Pridané (multi-tab synchronizácia - celosystémovo)

  • Locale (gp_lang) - locale-sync.js počúva storage event a po zmene jazyka v ktorejkoľvek karte aktualizuje cookie a reloadne ostatné karty.
  • Kolaps sidebaru - desktopové zbalenie/rozbalenie sa zrkadlí v reálnom čase medzi všetkými otvorenými kartami cez sidebar-collapsed v localStorage + nový storage listener.
  • Banner pre cookies - BroadcastChannel('gp_cookie_consent_sync') skryje banner vo všetkých sesterských kartách v okamihu, keď ho ktorákoľvek karta akceptuje; dismiss() je teraz idempotentný a po dokončení animácie sa odstráni cookie-banner-hiding trieda.

Overené (už synchronizované, bezo zmeny)

  • Dokončenie tour (tour-engine.js), globálny job bar (global-job-bar.js), cross-toast broadcast (core.js), dismiss PWA install karty (pwa-install.js).

[2026-05-13 - round 3] - PWA install card: multi-tab sync, prevencia konfliktov, životný cyklus

Pridané

  • Cross-tab synchronizácia - BroadcastChannel('gp_pwa_install') plus záloha cez localStorage storage event. Dismiss alebo inštalácia v jednej karte sa okamžite prepáli do všetkých ostatných.
  • Prevencia konfliktov - _conflictPresent() odloží zobrazenie karty, kým je viditeľný #cookieConsent, .gp-tour-welcome, .driver-active/.driver-popover, .modal.show/.gl-modal-overlay alebo .gp-app-loader.is-visible. 800 ms re-poll loop kartu znova ukáže hneď, ako prekážajúci element zmizne.
  • Page-synced životný cyklus - visibilitychange zastaví plánovanie pri skrytej karte a po návrate ho obnoví (žiadna animácia na nečinnej karte).
  • Event-hooky - počúva gp:cookie-consent, gp:tour:welcome:dismissed, gp:modal:close a okamžite skúsi zobraziť. tour-engine.dismissWelcomeCard() teraz dispatchuje gp:tour:welcome:dismissed po skončení 320 ms exit animácie.
  • UX detaily - Escape zatvára bez perzistencie; BroadcastChannel sa čisto zatvára pri pagehide.

Zmenené (PWA copy)

  • Popis karty už neklame o "offline support" (dashboard vyžaduje autentifikáciu a DB, offline fungovať nemôže). Nový text: "Install this app on your device for a faster, full-screen experience with quick access from your home screen."

[2026-05-13 - round 2] - Modernizácia JS a oprava chýb v PWA prompte

Zmenené

  • pwa-install.js prepísané do moderného ES6+: const/let, arrow funkcie, async/await, optional chaining, nullish coalescing.
  • core.js Alpine $mdShowPreview magic - 6 var deklarácií (stack, i, node, refEl, rendered, rm) nahradených const/let.

Opravené

  • Race condition v hide-timeri: _hide() setTimeout sa nedal zrušiť pri opätovnom zobrazení. Pridaný _hideTimer referenčný cyklus na úrovni modulu.
  • Deprecated userChoice API: _triggerInstall prepísaný cez result?.outcome ?? (await prompt.userChoice?.catch(()=>null))?.outcome - podporuje moderné (Chrome 97+) aj staršie dvojkrokové API.
  • clearTimeout(t) vo vnútri fired callbacku: presunuté pred plánovanie, aby sa timer skutočne dal zrušiť.

[2026-05-13] - PWA install prompt (štýl GitLab Pajamas)

Pridané

  • Vlastná PWA install karta (pwa-install.js + pwa-install.css) - zachytí beforeinstallprompt a vykreslí elegantnú kartu v pravom dolnom rohu s ikonou, titulkom, popisom, akciami Install a Not now a tlačidlom ×.
  • 90-dňová pamäť dismissu (gp_pwa_install_dismissed_until v localStorage).
  • Rešpektuje reduced-motion (prefers-reduced-motion: reduce zníži tranzíciu na čistý fade).
  • Mobil - prilepené k spodku viewportu, šírka cez celú obrazovku.
  • Public API - window.GP.installApp.{ show, dismiss, isAvailable }.
  • i18n háčiky - pwa.install.title, pwa.install.desc, pwa.install.cta, pwa.install.cancel, pwa.install.close (s fallback reťazcami).

[2026-05-12] - Deduplikácia toastov a zoradenie z-index ladderu

Opravené

  • Duplicitné sync-completion toasty - pri dokončení sync jobu sa toast občas publikoval dvakrát (jedenkrát z SSE eventu, druhý raz z terminal pollu). Pridaný sessionStorage kľúč gp_job_completion_dedup_v1 na potlačenie duplicít v rámci session; auto-poll je preskočený pre terminálne joby.
  • Z-index konflikty - toast container sa občas renderoval pod app loaderom / cookie bannerom. Token ladder je teraz: --z-cookie-banner: 1040, --z-tour-welcome: 1045, --z-app-loader: 1050, PWA install karta hardkódovaná na 1055, --z-toast: 1060.

[2026-05-11] - Oprava dismissu sprievodcu a responzívnosť na mobile

Opravené

  • Sprievodca - dismiss pri kliknutí do sidebaru na deskope/tablete: na desktopoch a tabletoch je sidebar permanentná súčasť layoutu - kliknutie na navigačný odkaz, prepínač jazyka alebo tlačidlo Collapse už welcome card ani aktívne turné nerušia. Guard pridaný do click listenera: if (!_isMobile() && t.closest('#appSidebar, .gl-sidebar')) return;.
  • Sprievodca - príliš broad summary selektor: click handler používal holý selektor summary, ktorý zachytil každý <summary> element na stránke vrátane collapsible sekcií (napr. code-reference panely v nastaveniach kurzu). Selektor zúžený na details.gp-menu, details.dropdown, details.user-menu - .closest() sa cez DOM strom sám dostane k <summary> deťom, takže žiadna funkčnosť nevypadla.
  • Tour popover - Android 3-tlačidlový nav bar skrýval footer: pri aktívnom viewport-fit=cover sa viewport roztiahne za systémový panel, ale env(safe-area-inset-bottom) môže byť na Android 3-button nav nulové. Dvojfázová oprava: transform bottom-sheetu odrátava env(safe-area-inset-bottom, 0px); prehliadače s podporou dvh dostanú @supports (height: 100dvh) blok s 100dvh, ktorý vylúči systémový panel spoľahlivo. Rovnaká logika platí pre tablet portrait a landscape breakpointy.
  • Tour popover footer - minimálna dotyková plocha: padding-bottom: max(env(safe-area-inset-bottom, 0px), 12px) garantuje aspoň 12 px nad systémovým panelom aj tam, kde env() vracia 0. Pridané min-height: 44px (WCAG 2.5.5).
  • Welcome card - poloha nad systémovým panelom: bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px)) posúva kartu nahor o výšku systémového panela.
  • Tour popover - max-height dvh: max-height: 70dvh (s fallbackom 70vh) používa dynamickú jednotku, ktorá vylučuje adresný riadok prehliadača aj systémovú lištu.

[2026-05-10 - round 3] - Attribution chips: kto skutočne robil prácu na MR a Issues

Pridané

  • Attribution chips v tabuľke MR (detail študenta): keď MR neobsahuje žiadne commity od zobrazeného študenta, zobrazí sa oranžový badge "Žiadne commity študenta" a mená skutočných autorov (modrý = učiteľ, sivý = spolužiak). Ak študent commitoval spolu s inými, zobrazí sa riadok "Tiež od:" s menami.
  • Stĺpec "Assigned to" v tabuľke Authored Issues: kto má issue pridelenú - modrý badge pre učiteľa, sivý pre spolužiaka, pomlčka ak nikto.
  • Stĺpec "Opened by" v tabuľke Assigned Issues: kto issue otvoril - rovnaká farebná logika.
  • Preklady (sk.json): 6 nových kľúčov - Also by:, No student commits, tooltip pre "Žiadne commity študenta", Opened by, Assigned to, Peer student.

Implementácia

  • src/app/api/dashboard/students.py: batch SQL query cez tuple_(Commit.project_id, Commit.ref).in_(branch_pairs) - žiadne N+1; teacher_ids (UUID set) + teacher_gitlab_ids (int set) pre správnu detekciu učiteľov aj bez author_id; aliased(StudentModel) / aliased(TeamMember) pre dva outerjoin na Issues.
  • src/app/templates/partials/student/_merge_requests.html: attribution row sekcia s podmienkami not _student_commits / _other_commits.
  • src/app/templates/partials/student/_detail_content.html: nové stĺpce "Assigned to" a "Opened by".

[2026-05-10 - round 2] - Badge "Vetva zmazaná" a synchronizácia commitov cez MR API

Pridané

  • Badge "Vetva zmazaná" v tabuľke MR (detail študenta): keď bola zdrojová vetva MR zmazaná po zlúčení (GitLab force_remove_source_branch alebo ručné zmazanie), na príslušnom riadku sa zobrazí sivý badge s tooltipom. Vyžaduje nový DB stĺpec merge_requests.source_branch_deleted (migrácia 044).
  • Primárna detekcia zmazania vetvy (mr_processor.py): GitLab posiela force_remove_source_branch=true v REST aj webhook payloade - hodnota sa ukladá do stĺpca pri každom spracovaní MR.
  • Sekundárna detekcia (sync_job.py): sync-job sleduje počty commitov per-vetva; ak MR fallback loop obnoví > 0 commitov, ale branch-ref vrátil 0 a MR je merged, systém automaticky nastaví source_branch_deleted = True - zachytí aj manuálne zmazania bez GitLab príznaku.
  • Obnovenie commitov cez MR API (sync_job.py): po slučke branch-ref nasleduje fallback slučka, ktorá pre každý MR zavolá GET /projects/:id/merge_requests/:iid/commits - tento endpoint funguje aj po zmazaní zdrojovej vetvy.
  • Fallback priradenia commitov podľa mena autora (students.py): dotaz pre commity na stránke detailu študenta pridáva klauzulu author_name = student.full_name - zachytí prípady, keď sa email nezhodoval, ale git display name áno.
  • Preklady (sk.json): "Branch Deleted": "Vetva zmazaná" + slovenský preklad tooltip textu.

Opravené

  • CI - mypy (students.py): holá anotácia : list na _commit_clauses spôsobovala chybu type-arg v strict móde -> odstránená.
  • CI - ruff-format (students.py): viacriadkové .append(func.lower(...)) volania zmestia na jeden riadok -> jednoradková forma.

[2026-05-10] - Oprava priradenia commitov (email fallback, noreply prefix)

Opravené

  • Fallback priradenia commitov podľa emailu (students.py): dotaz na commity na stránke detailu študenta obsahuje klauzulu author_email = student.email - commity bez author_id sa zobrazia, ak email sedí.
  • Odstraňovanie prefixu <čísla>+ z GitLab noreply emailov (commit_processor.py): GitLab generuje noreply emaily vo formáte 123456+username@users.noreply.gitlab.com; prefix sa pred porovnaním orezal - sync správne zaradí commity k správnemu študentovi.

[2026-05-09 - round 2] - Druhý sprint na sprievodcu, DLQ a tabuľky

Opravené

  • Sprievodca v2 (gp_tour_v1 -> gp_tour_v2 storage prefix): jednorazový čistý reset zastaralých :completed kľúčov, ktoré pod v1 vznikali pri každom dokončení turné a navždy potlačili welcome card na všetkých etapách multistage stránky /dashboard/support (course_id -> category -> formulár). Pod v2 sa bare kľúč zapisuje iba pri explicitnom zavretí welcome card (× alebo "Maybe later") - dokončenie konkrétnej etapy si pamätáme len pod fingerprintovým kľúčom, takže ďalšia etapa stránky znova ponúkne sprievodcu.
  • Sprievodca - synchronizácia medzi tabmi: nový storage event listener počúva zápisy do gp_tour_v2: v iných tabách. Akonáhle používateľ dokončí alebo zatvorí sprievodcu v jednom tabe, ostatné taby okamžite skryjú welcome card a (ak práve beží) zatvoria aj aktívny Driver.js overlay. Kým predtým sa dva taby pretekali o to, ktorý prepíše localStorage neskôr.
  • Admin / Event Pipeline - DLQ loader: krúžiaci spinner v prázdnej tabuľke DLQ Events bol vizuálne statický. SVG transform-origin defaultne býva 0 0, takže ikona sa "točila" okolo svojho ľavého horného rohu a v 20×20 px bunke vyzerala zamrznutá. Pridaný transform-origin: 50% 50%; transform-box: fill-box; na .spin rotuje ikonu okolo jej skutočného stredu - naprieč všetkými stránkami, ktoré túto utility class používajú.
  • Admin / Users + DLQ tabuľky - orezávanie textu: Role (badge "Student") a Status (badge "Active") boli orezané na trojbodku alebo zalomené uprostred slova, lebo td { overflow: hidden; text-overflow: ellipsis } platilo na celú tabuľku vrátane buniek s pill badgemi. Truncation teraz cieli iba textové stĺpce (Username, GitLab ID, Full name, Email, Created); Role / Status / Actions majú white-space: nowrap; overflow: visible, takže sa zmestia kompletne. Upravené šírky stĺpcov: Actions 8 % -> 12 %, Email 22 % -> 18 %.
  • Admin / Users + DLQ tabuľky - responzívnosť: pod 900 px (Users) a 1024 px (DLQ) tabuľky prepínajú na table-layout: auto s min-width v reálnych pixeloch, takže sa skryjú za horizontálny scroll svojho .table-responsive wrappera namiesto skreslenia hlavičiek typu "GitLab User I..." alebo orezania pravostranných tlačidiel.

[2026-05-09] - Oprava OAuth slučky a UI ladenia

Opravené

  • OAuth prihlasovanie: koniec nekonečnej slučky "Session expired" po návrate z GitLabu. Callback teraz robí explicitný HTTP 303 See Other s Cache-Control: no-store, predĺžili sme STATE_TTL z 600 s na 900 s a opravili sme atribúty cookie gp_returning, takže prehliadač spoľahlivo prenesie session-id na cieľovú stránku.
  • select_field formulár: koniec doslovného textu u2014 v dropdown poliach. Filter tojson_attr teraz serializuje JSON s ensure_ascii=False, takže em-dash (-) ide do HTML atribútu ako natívny UTF-8 znak a nestráca sa cestou backslash escape.
  • Sprievodca - popover na mobile: zmiernené pravidlo auto-prepnutia na vrchný panel. Predtým striktné podmienky (≥ 40 % prekrytia zdola a ≤ 10 % zhora) blokovali legitímne preklopenia pre polia v dolnej polovici obrazovky (napr. "Priorita" v /dashboard/support), takže popover fyzicky zakrýval pole, na ktoré ukazoval.
  • Sprievodca - viacstupňové stránky: každá etapa /dashboard/support (výber kurzu -> kategória -> formulár) má teraz vlastný welcome card. Predtým dokončenie jednej etapy zapísalo aj "globálny" :completed kľúč, čím navždy potlačilo welcome card na ostatných etapách. Globálny kľúč sa teraz zapisuje len pri explicitnom zavretí (× alebo "Maybe later").

[2026-05-07] - Sprievodca a UI vylepšenia

Pridané

  • Sprievodca používateľa (Tour): nová časť o "Servisných účtoch" v administrácii (admin_users tour), ktorá vysvetľuje botov a analytickú kartu.

Opravené

  • Nastavenia kurzu: fix renderovania Unicode znakov (Em-dash vizuálny glitch ako \u2014) nahradením spojovníkom v "Select a preset" poliach.
  • Sprievodca (Tour engine): oprava logiky pre cache (lokálne verzus re-prompt po refreshi, kedy sa nesprávne ukladalo skipped). Po skutočnom dokončení turné sa označí ako completed a zbytočne sa neopakuje.
  • Zamedzenie layout shiftov: nasadenie spinnerov pre nastavenia kurzu miesto ťažkých skeleton tabuliek pri načítavaní kontrol s HTMX.

[2026-05-06] - Servisné účty (boti) ako prvotriedna entita

Pridané

  • Stĺpec users.is_bot + parciálny index (Alembic 042_user_is_bot): nová BOOLEAN NOT NULL DEFAULT FALSE na users tabuľke s parciálnym indexom ix_users_is_bot_false WHERE is_bot=false. Migrácia urobí backfill cez vzory zhodné s is_bot_user() (project_*, group_*, *_bot, *-bot, ghost, alert-bot, support-bot). Predošlé runtime-regex filtrovanie sa nahradilo jediným indexovaným predikátom humans_only_clause().
  • Nastavenie is_bot pri každom vytvorení používateľa: 9 miest, kde aplikácia vytvára User/UserModel riadok (group import, OAuth first login, dev-mode auto-create, admin manuál sync, student create, projects/teams auto-create z inherited members) teraz volá is_bot_user(username, user_type=...) a ukladá výsledok do stĺpca. Servisné účty sú tým označené raz pri vstupe, nie sa heuristicky filtrujú pri každom dotaze.
  • Stat karta "Service Accounts" na admin System Settings + samostatné bots číslo v /dashboard/api/v1/admin/system/stats. User Role Breakdown (chart) teraz počíta iba ľudí - botov treba čítať z dedikovanej karty, nemiešajú sa s rolami.
  • Service Accounts collapsible panel pod hlavnou tabuľkou User Management. Botov sa nedá editovať/deaktivovať z UI; ostáva len Info tlačidlo, ktoré otvára rovnaký User Info modal s Service Account badge vedľa rolovej značky a tooltipom "GitLab service account / bot - not a human user". Skeleton (sk_admin_users) zarovnaný s reálnym layoutom: 4 filter pily (All/Admin/Teacher/Student) + náznak servisného panelu pod tabuľkou.
  • Vylúčenie botov z agregovaných počtov: dashboard_stats/home._build_stat_cards (Active Users / Students), member_sync_service.get_teachers_for_course (učiteľský whitelist), users.update_user last-admin guard, support assignee dropdown - všetky teraz ignorujú is_bot=true.

Opravené

  • Predošlý regex-based filter not_bot_user_clause() bol band-aid: tá istá logika sa duplikovala v Pythone (is_bot_user) aj v SQL (ilike-OR strom). Existuje stále ako alias, ale nový kód má používať humans_only_clause() / bots_only_clause() z app.services.user_filters.
  • sk_admin_users skeleton mal len 3 filter pily namiesto 4 - pri lazy load sa layout vizuálne posúval, keď doľahla skutočná hlavička.

Zmenené

  • Preklady (sk.json): pridané Service Account, Service Accounts, Bot, View service account details, GitLab bots - read-only, GitLab service account / bot - not a human user.

[2026-05-05] - Mobile-friendly tours + sync access control

Pridané

  • Sprievodcovia plne responzívni na mobile (static/css/tour.css): pridaný @media (max-width: 600px) blok, ktorý zmení popover na bottom-sheet zaberajúci celú šírku obrazovky, max 70 vh, s tlačidlami Späť/Ďalej v zalamovacom päte. Welcome karta sa rozťahuje na celú šírku mínus 0.75 rem margins. Žiadne horizontálne scrollovanie, žiadne odrezané popover-y.
  • Tour engine otvorí sidebar automaticky (static/js/tour-engine.js): keď krok cieli element vnútri #appSidebar / .gl-sidebar a používateľ je na mobile (≤ 768 px), engine pred meraním rectu cieľa pridá mobile-open class na sidebar a visible na overlay. V onDeselected (a onDestroyed) sa sidebar zatvorí, aby ďalší krok mimo sidebaru nebol prekrytý drawerom. Kľúčový fix: doteraz mali sidebar prvky kladný bounding rect aj v zatvorenom stave (translateX(-100%)), takže Driver.js kreslil cut-out mimo viewport - používateľ videl len stmavnutý overlay s ničím vyznačeným.

Opravené

  • Sync progress bar - viditeľnosť pre študentov bez kurzov: /api/sync/active vracal per_course slovník so všetkými prebiehajúcimi syncmi systému; študenti, ktorí nie sú zapísaní do žiadneho kurzu, videli aktivitu cudzích kurzov. Endpoint teraz volá _filter_courses_for_user (rovnaká logika ako dashboard widgets), takže ne-admin používateľ vidí iba kurzy, do ktorých má prístup. Bearer-token volania a admini obchádzajú filter.
  • Multi-job count badge - prekrývanie v Opere: badge bol absolútne polohovaný v pravom hornom rohu #globalJobBar, čo v Opere s úzkym viewport-om prekrývalo titulok jobu. Badge sa presunul do tenkého header pásu nad listom úloh (bar.insertBefore(countBadge, bar.firstChild)) a má teraz prirodzený vlastný riadok.

Zmenené

  • Dokumentácia (docs-site): pridané záznamy v changelog.md (SK) a changelog.en.md (EN). Dashboard "Latest changes" widget zoradený rovnakým poradím cez _CHANGELOG_ENTRIES v src/app/api/dashboard/home.py.

[2026-05-04] - Sprievodca aplikáciou (Guided Tours)

Pridané

  • Driver.js v1.3.6 vendored v static/vendor/driver.js/ (MIT, ~21 KB JS + 4 KB CSS, žiadny CDN). Globálny entry point window.driver.js.driver({...}) je predvolene súčasťou každej autentifikovanej stránky.
  • Tour engine (static/js/tour-engine.js): role-aware (študent / učiteľ / admin), multi-language (en + sk), per-page detekcia podľa location.pathname, perzistencia stavu v localStorage (gp_tour_v1:<slug>@<role>:{completed,skipped,prompted}). Kľúče sú scope-nuté podľa role, takže po promócii študenta na učiteľa sa nové sprievodci ponúknu znovu.
  • Tour content registry (static/js/tour-content.js): 20 sprievodcov pokrývajúcich Dashboard, Course Detail / Settings / Roster / Teachers / Analytics / Activity, Team Detail, Project Detail, Student Profile, Admin Users / Courses / System, Rubric Editor a Support - každý plne preložený.
  • First-visit welcome card: nenápadná floating karta vpravo dole s názvom sprievodca, počtom krokov, odhadom času a CTA "Take the tour / Maybe later". Auto-zatváranie po 25 s, jednorázová ponuka per role.
  • Help launcher v hlavičke: položky "Take a tour" a "Replay tours" v existujúcom user dropdowne, klikom na Replay sa vyčistí história sprievodcov.
  • Themed Driver.js popover (static/css/tour.css): plne zhodný s design tokenmi (--surface-default, --primary, --shadow-lg), animovaný stage pulse cez .gp-tour-pulse, redukovaný režim animácií (prefers-reduced-motion).
  • Hard role guard: run(slug) odmietne spustiť sprievodcu, ktorého roles neobsahujú aktuálnu rolu používateľa - bezpečnostná poistka pre prípad, že content registry omylom registruje teacher-only sprievodcu na študentskej stránke.

Opravené

  • Run Compliance Checks modal - biely pruh dole: rcm-footer mal background: var(--card-bg, var(--surface, #fff)), ale ani jeden token nie je v aplikácii definovaný (existujú len --surface-default, --surface-strong). Fallback #fff vykreslil biely sticky footer. Footer používa --surface-strong a vystupuje cez --gl-spacing-5 modal padding, takže lícuje s okrajmi modálu. Rovnako rcm-group a rcm-item boli prepojené na reálne tokeny - checkbox riadky majú teraz povrch, hover state, focus ring a inset border-stripe pri zaškrtnutí.

Zmenené

  • Tour CSS používa real SVG ikony / unicode dots namiesto emoji (žiadne 🚀 / 👋 / 💡 / 📍 / ⏱). Welcome card meta riadok má dva pillové badge s farebnou bodkou (primary pre "steps", success pre čas).
  • Build pipeline: tour-engine.js a tour-content.js pridané do JS_MODULES v scripts/build_bundle.py; tour.css pridaný do static/styles.css manifestu - minifikované .min siblingy a gzip-precompressed bundle sa generujú automaticky.

[2026-05-03] - Webhooks v2

Pridané

  • Multi-scope konfigurácie webhookov: Každý kurz môže mať ľubovoľný počet webhook tajomstiev, každé priradené ku GitLab Group, Sub-group, zoznamu projektov alebo celému kurzu. Tajomstvá sa generujú na serveri, zobrazia sa práve raz a rotácia má 24h grace okno. Audit log zachytáva všetky create / rotate / revoke události.
  • Auto-detekcia GitLab group path: Pole gitlab_group_path na úrovni kurzu sa už nezadáva ručne - systém počíta najdlhší spoločný prefix z gitlab_group_path existujúcich tímov pri uložení. Pole odstránené z UI kvôli duplicite s per-config scope.
  • Archive-safe webhooky: Pri archivácii kurzu sa všetky jeho WebhookConfiguration riadky preklopia na is_active=False, takže auth lookup ďalšie doručenia odmietne s 401. Riadky zostanú zachované (audit + rotation grace). Pri reaktivácii kurzu zostanú konfigurácie deaktivované - operator manuálne rozhodne, ktoré scope sú ešte relevantné.

Opravené

  • Webhook config modal sa neotvára: Vnútorný .modal div mal class='d-none', čo prepisalo .active triedu na backdrope. Backdrop sa zobrazil, ale obsah zÿstal skrytý. Odstránené d-none zo všetkých 3 webhook modálov.
  • "Heads up" upozornenie prerobé aby ladilo s ostatnými .alert.alert-info v modáloch (jednoriadkový callout, bez bold-prologu).

Zmenené

  • Odstránené legacy api/group_webhooks.py, schemas/group_webhook.py, services/group_webhook.py a tests/unit/test_group_webhook_service.py (nahradené modulom webhook_configurations). Pomocná funkcia na označovanie posledného použitia legacy course-level tajomstva bola priamo vložená do api/webhooks.py; ROTATION_GRACE teraz žije v services/webhook_configurations.py.

[2026-05-03] - Late

Zmenené

  • Backend refactor (-60 LOC, -73 net riadkov): Hoistovanie 60+ duplicitných inúlínovaných importov v admin.py, home.py, evaluation_report.py, pilot_telemetry.py na úroveň modúlu. Odstránené mrtve cesty, opravené PERF401/PERF403 list/dict comprehensions a S110 "best-effort swallow" bloky majú explicitný noqa komentár. Žiadna zmena správania - ruff + mypy 1.11 ostali čisté.
  • Analýza stránky kurzu - rýchla cesta: EvaluationReportService má novú metódu _collect_preview_data(), ktorá preskčí 3 drahé agregáty (compliance trends, support summary, weekly aggregates) pre načítanie analytickej stránky. Generovanie PDF zostalo nezmenené.

Opravené

  • Prístupnosť (a11y) - <label> bez asociovaného poľa:
  • V _analytics_content.html (formulár generovania PDF reportu) štyri "header" labely (Course Name, Semester, Teacher Name, Report Period) prekonvertované z <label> na <span class="form-label">, lebo neopisujú žiaden vstupný prvok - označujú readonly zobrazenia.
  • V member_filters.html, _run_checks_modal.html a forms.html (toggle, bare checkbox, file-upload) doplnený explicitný for= atribút na obalľvé <label> prvky a generované id k checkbox vstupom v _run_checks_modal.html. Implicitné zabalenie aj explicitné for zahája oba kontrolné mechanizmy DevTools/axe.

[2026-05-03]

Zmenené

  • Detaily študentov (Collapsibles): Prvá sekcia typu <details> sa teraz automaticky otvára aj v prípade prázdnych dát. Pokiaľ študent nemá priradené Issues alebo MR Diskusie, prázdny stav sa vizualizuje s inštruktážnym textom (namiesto potichu zbaleného zoznamu).
  • Vizuálne úpravy komponentov:
  • Pridaná automaticky rotujúca šípka (chevron) pre element .summary-toggle (používaný v prehľadoch detailov študenta).
  • Component Raw Check Details (JSON) prestajlovaný ako štandardný rozbaľovací element s pre-formátovaním a lepšie ladí so zvyškom dashboardu.
  • Okraje v elemente pagination-controls sa po vložení do details neprekrývajú cez content (odstránený tzv. CSS notch).

Opravené

  • Sprievodný text pod hlásením GitLab Health: Notifikáčný banner je teraz vertikálne stohovaný s globálnym task barom, aby sa elementy neprekrývali. Hranica pre notifikáciu (GL_FAIL_THRESHOLD) bola zvýšená na 3 nadväzujúce výpadky.
  • Skeleton tabuľky (.gl-skeleton-table): Vizuálne loaderi teraz lepšie využívajú flexbox a šírky sú rôznorodejšie (nth-child pattern) pre odstránenie monotónnych "blokov", no animácia a kompozícia zostáva konzistentná po celý čas načítania bez zablokovania vlákna DOM.
  • Oprava CSP pre <script> a <link> inliny: Priamy atribút onload prepísaný, nonce pridaný na všetky inline script značky pre fungovanie striktného Strict CSP bez varovaní vo všetkých dashboard template moduloch.
  • Markdown úniky: Znak spätného lomítka \ pred Markdown kľúčovými znakmi sa už vo výslednom parsovaní renderuje bezpečne namiesto jeho tichého odseknutia.

[2026-05-02]

Bezpečnosť

  • Strict CSP - @alpinejs/csp build povýšený na 3.15.12 a defaultne zapnutý. Pôvodne vendorovaný 3.14.9 mal očesaný evaluator výrazov, ktorý odmietal ||, ===, !==, !, && a reťazcové argumenty metód - po zapnutí flagu padali všetky stránky používajúce select_field(), markdown editor taby, dropdowny pre vyhľadanie assignee, role-toggle dropdowny a sidebar taby (Alpine Error: Alpine is unable to interpret the following expression... v konzole). Alpine 3.15.3 (PR #4711, "Improve evaluator") obnovil plnú podporu inline výrazov; vendorujeme 3.15.12 (vendor/alpine-csp-3.15.12.min.js, SHA-256 566167134bb2...), takže šablóny zostávajú idiomatické. Middleware posiela enforced policy (s unsafe-eval/unsafe-inline pre spätnú kompatibilitu) aj strict Report-Only policy (nonce-{nonce} + strict-dynamic); povýšenie na enforce čaká na prázdny violation report.

Zmenené

  • Markdown editor extrahovaný do jedného komponentu (components/markdown_editor.html). ~30-riadkový GitHub-style toolbar (Heading / Bold / Italic / Quote / Code / Link / Bulleted list + Write / Preview taby) bol duplikovaný v troch templatoch (support formulár, detail tiketu, odpoveď v tikete). Jedno makro md_editor() teraz poháňa všetky tri miesta. Tlačidlá toolbaru prešli z title= na data-tippy-content= pre konzistentné tooltipy, dostali aria-label, a wrapper má role="toolbar" + aria-label="Markdown formatting". Taby dostali role="tab" + aria-selected.

Opravené

  • Tlačidlo Code v markdown toolbar opravené (regresia z CSP migrácie). Predošlý overlapping regex cez tri support templaty dvojito predprifixoval volanie $mdInsert na $$mdInsert, ktoré ticho zlyhávalo. Opravené a konsolidované cez nové makro, takže sa to už per-template nemôže opakovať.
  • Edit User - loader na Save changes je vycentrovaný v tlačidle (inset:0; margin:auto).
  • Dashboard - duplicitné riadky soft-deleted projektov sú skryté; projekty s rovnakým názvom v rôznych tímoch sú v course tree disambiguované.

Bezpečnosť

  • Absolútny TTL session-y vynucovaný aj na stateless (cookie-only) session-ách (AUTH-06b): predošlý AUTH-06 fix pridal session_absolute_max_age_seconds iba do Redis session store-u. Stateless cookies (fallback pri nedostupnom Redise) naďalej záviseli výlučne od sliding max_age - aktívny používateľ (alebo ukradnutý cookie) si mohol TTL obnovovať donekonečna. get_current_session() teraz validuje claim ts (dátum vytvorenia) oproti session_absolute_max_age (default 24 h) pri každom načítaní session, nezávisle od storage backendu. Malformovaný ts = expired (fail-closed).
  • Rate-limit fail-closed vo VŠETKÝCH prostrediach (AUTH-05b): predošlý AUTH-05 fix aplikoval fail-closed iba na APP_ENV=production; dev a test boli stále fail-open. Ak by dev .env omylom unikol na produkciu, rate limiting by sa tichto degradoval na no-op. Zjednotené na HTTP 503 + Retry-After: 30 všade.
  • UA fingerprint sa ukladá vždy - aj pri prázdnom User-Agent (AUTH-08): predtým pri chýbajúcom alebo prázdnom User-Agent headri cookie obsahoval ua="", čo obišlo kontrolu fingerprintu. Útočník s ukradnutým cookie ho mohol replayovať z ľubovoľného klienta jednoduchým vynechaním headra. Session-y teraz vždy nesú fingerprint; prázdny UA produkuje hodnotu "no_ua|no_ua", takže mismatch check sa spúšťa aj proti replay útokom, ktoré stripujú headre.

Zmenené

  • Sweep enrich limitovaný na 2 000 jobov za periodický beh (SCALE-01): periodic_enrich_all predtým zaraďoval enrich joby pre každý issue a MR naprieč všetkými aktívnymi projektami bez limitu. Pri 567 študentoch (~200-500 projektov × 15-30 entít) to mohlo zaplaviť frontu 5 000-15 000 jobmi naraz, vyčerpať kapacitu workerov a oneskoriť spracovanie webhook-ov. Nový config sweep_enrich_max_jobs_per_run (default 2 000) limituje celkový počet; zvyšné entity sa spracujú v ďalšom 24h cykle.

Opravené

  • Mŕtvy kód odstránený v sweep_stale_projects: duplikátny return 0 (nedosiahnuteľný kód za prvým return) odstránený.

[2026-05-07]

Zmenené

  • Compliance engine - audit-driven kalibrácia kontrol R02/R04/R05/R06/R07/R08/R10/R11/R12: komplexný logický audit 13-check systému odhalil sériu prípadov, kedy default semantika checkov nezodpovedala duchu zadania ZSI a buď nespravodlivo penalizovala disciplinovaných študentov, alebo naopak prepúšťala nezhody. Týmto release-om sa kalibrácia zarovnáva s textom "doing & communicating" (4-5 členné tímy, separátna feature branch per ticket, MR linkovaný na issue, dvaja reviewer-i, commit-ref-citing odpovede, Approve pred merge, MR + ticket sa zatvárajú spolu) a engine od teraz robí to, čo by skúsený učiteľ urobil pri ručnej kontrole.
    • R02 (Branch naming) - fail-detail vysvetľuje aj protected-branch prípad (predtým len "doesn't follow naming convention"). Nový text: "Branch '' is on a protected name or does not match the configured naming convention" - okamžite jasné, či ide o naming-pattern miss alebo o priame mergeovanie do master/main/develop. Práznin vzor branch_pattern už nezhadzuje check (vetva sa proste preskočí).
    • R04 (MR linked to issue) - branch-name fallback + presnejší bare-#N regex. Worker enrich_mr.py aj mr_processor.py extrahuje linked_issue_iid aj z názvu vetvy (issue-7-foo, 7-add-login, feature/issue-12), ak popis MR neobsahuje Closes #N. Kombinuje sa s textovým parserom - preferuje sa explicitné Closes #N, branch-IID je fallback. Bare-reference pattern #N je sprísnený na word-boundary aj na konci ((?<![\w-])#(\d+)(?!\d)), takže nezachytí v#10 alebo #10x. FK MergeRequest.linked_issue_id sa zaresolvuje cez gitlab_iid lookup -> R11 vie spárovať MR s tiketom.
    • R05 (MR description quality) - ratio počítania slov ignoruje markdown štruktúru. Predtým mohol MR prejsť 10-word threshold s úplne prázdnym obsahom (sekcie ## Co, ## Ako, ## Prečo zaberali ~9 slov bez toho, aby skutočne niečo opisovali). Helper _word_count teraz odstrihne markdown headers, list markers, link syntax [text](url)->text, italic/bold sigil-y a filtruje tokeny <2 znaky bez \w znaku. "Prázdne nadpisy bez obsahu" už neprejdú.
    • R06 (Review Received) - počíta len reviewer-ov s zmysluplným feedback-om. Predtým stačilo, aby reviewer napísal "+1" alebo "ok" a R06 ho započítal. Engine teraz agreguje všetky komentáre per reviewer (notes_by_reviewer: dict[author_id, list[notes]]), spojí ich a prejde cez is_meaningful_comment(joined, min_words=cfg.min_word_count). Nový cfg.require_meaningful (default True) sa dá v UI vypnúť pre kurzy s lightweight review kultúrou. Detail uvádza počet reviewer-ov so "short comments only" zvlášť, takže teacher vidí, prečo R06 fail-ne aj pri 2 prítomných reviewer-och.
    • R07 (Code Review Given) - default min_words 30 -> 15 (model course_settings.min_review_word_count, helper is_meaningful_comment, migrácia 039). 30 slov bolo v praxi privysoké pre typické per-line review komentáre ("Tu by si mohol použiť dict comprehension namiesto for-loopu" má 9 slov a je to legitímna review). Zarovnáva sa s zvyškom kódu (kde min_review_word_count bol fallback na 15) a s ZSI textom, ktorý žiadny kvantitatívny prah neuvádza. Migrácia upraví iba kurzy stále na default-e 30; učiteľské customizácie sa nedotkne.
    • R08 (Review Response) - počíta aj standalone reviewer notes + softer defaults. Predtým engine bral ako "review thread" iba inline diff diskusie (notes s discussion_id); standalone reviewer komentáre na úrovni MR boli ignorované, takže študent mohol nikdy neodpovedať na "Tento PR vyzerá dobre, ale chcel by som vidieť testy." a R08 prešiel. Teraz total_threads = inline_total + standalone_reviewer_notes a answered = inline_answered + min(standalone_author_notes, standalone_reviewer_notes). cfg.require_commit_ref zmenený True -> False default (ZSI commit-ref-citing odporúča, ale neuvádza ako must), cfg.pass_if_no_threads False -> True (študent nemôže odpovedať na feedback, ktorý neexistuje). Keď commit-ref nie je vyžadovaný, partial scoring je čistá response_rate (nie 50/50 weighting).
    • R10 (Merged by author) - akceptuje aj merger-tímakolega. Predtým allow_self_merge=True znamenalo "autor MUSÍ merge-núť", čo je sémanticky proti ZSI textu "assignment owner (or designated assignee) executed the merge". Default semantika je teraz permisívna: prejde, keď autor ALEBO ďalší resolved tímakolega (merged_by_id != None) merge-uje; fail-ne iba pri unknown-user merger-i (admin, bot, externý GitLab user). Nové explicitné cfg: require_self_merge (default False - autor MUSÍ kliknúť Merge) a forbid_self_merge (default False - reviewer-gates-merge model: niekto INÝ ako autor musí merge-núť). Detail strings: "Merged by author", "Merged by a team member after approval", "Merged by an unknown user (not a team member)".
    • R11 (MR + Issue closed) - strict pairing cez linked_issue_iid. Predtým bol check decoupled: stačilo, aby existoval ľubovoľný merged MR + ľubovoľný closed issue, čo prešlo aj keď študent merge-ol niečo úplne iné než tiket #7, ktorý zostal otvorený. Engine teraz buduje issues_by_iid = {i.gitlab_iid: i for i in ctx.issues_assigned}, pre každý merged MR pozre mr.linked_issue_iid a check passne iba, ak spárovaný issue je closed. Fallback: keď žiadny merged MR nemá linked_issue_iid (študent zabudol napísať Closes #N aj pomenovať vetvu), engine spadne späť na legacy any-issue-closed semantiku, aby unlinked-but-closed pair nebol prísnejšie penalizovaný než predtým. Detail strings: "Merged MR's linked issue is closed", "Merged MR references an issue that is still open", "MR merged and assigned issue closed (no explicit link)", "MR merged but no assigned issue is closed".
    • R12 (Pipeline Health) - širšia detekcia test job-ov. Substring "test" zachytával len "test", "pytest", "junit" (junit obsahuje test po zámene jednej slovo... nie, neobsahuje), takže pipeline s job-om "spec", "verify", "coverage", "jest", "mocha", "rspec", alebo "phpunit" sa hlásil ako "nemá test job" a R12 fail-ol. Detektor v pipeline_processor.py teraz prechádza tuple ("test", "spec", "pytest", "junit", "jest", "mocha", "rspec", "phpunit", "verify", "coverage") proti name aj stage, takže tipické naming conventions cross-language sú pokryté.

Bezpečnosť

  • Absolútny strop životnosti session-y 24 h (AUTH-06): doteraz mal session store iba sliding TTL - aktívny používateľ (alebo ukradnutý long-lived cookie) si mohol TTL refreshovať donekonečna. Pridaný session_absolute_max_age_seconds (default 24 h); store stamp-uje created_at pri create() a v load() odmieta session-y staršie než limit nezávisle od sliding TTL. Ohraničuje blast-radius ukradnutého cookie.
  • Webhook side-effecty po commit-e fail-best-effort (EVT-01): event processor-uje doteraz commitol DB transakciu a potom spúšťal cache-invalidation, immediate enrichment, metrics enqueue a SSE notify. Ak ktorýkoľvek z nich raisol, outer except zavolal rollback - ale commit už bol persistovaný, takže event.processed=True zostalo a side-effecty sa nikdy nezopakovali. Teraz každý side-effect ide cez _safe(coro, name) ktorý chytá výnimky, inkrementuje Prometheus counter webhook_processed_total{status="sideeffect_failed:<name>"} a loguje WARNING namiesto bublania.
  • Redis vyžaduje heslo aj na internej Docker sieti (SEC-03/SEC-04): doteraz Redis počúval bez autentifikácie a REDIS_URL mohlo byť redis://redis:6379/0 bez hesla. Útočník s prístupom do Docker bridge-u (alebo so zlým rebind-om porto-mappingu) by si vedel prečítať session blob-y a queues. Validátor v app.config teraz v APP_ENV=production odmieta naštartovať, ak REDIS_URL neobsahuje password segment (redis://:PASSWORD@...). Compose template plnohodnotne zapína --requirepass keď je REDIS_PASSWORD set. Na produkcii vygenerované 40-char heslo, všetky 4 worker-y + scheduler + api re-pripojené.
  • FEATURE_RBAC=false v produkcii hard-faily (AUTH-04): predtým _check_required_roles len logovalo WARNING a požiadavku prepúšťalo bez kontroly rolí. Človek omylom vypnutím flagu by ticho otvoril všetky teacher-only endpointy. Validátor teraz v produkcii vyhodí ValueError pri štartu, čím deployment fail-ne fail-fast namiesto fail-open behu.
  • DEBUG=true v produkcii hard-faily (§7b): debug mód odhaľuje stack trace-y a môže auto-zakladať admin používateľov. Validátor odmieta beh.
  • Rate-limit fail-closed v produkcii (AUTH-05): keď bol Redis nedostupný, dependencies.check_rate_limit skončilo v except Exception a povolilo request (graceful degradation). To znamenalo, že útočník dokáže obísť rate limit kompletným zhotenín Redisu (alebo brute-force-om kým Redis flapne). Produkcia teraz vracia HTTP 503 s Retry-After: 30 a logom rate_limit_unavailable_blocking. Vývoj a testy zostávajú permissívne.
  • SESSION_STRICT_UA_FINGERPRINT opt-in (AUTH-02): UA-fingerprint je k dispozícii ako defence-in-depth proti XSS / cookie-theft replay útokom z iného OS / browser-a, ale ako default zostáva false. Pri prepínaní mobil <-> desktop alebo browser update-och nie je hash UA stabilný a flag default-true v praxi vyhadzoval reálnych používateľov 401-kou. Sady "prísnych" CSRF + IP-binding + absolútneho session ceiling-u (AUTH-04/05/06) poskytujú ekvivalent post-stolen-cookie ochrany bez tejto false-positive miery.

Pridané

  • Healthcheck pre Caddy reverzný proxy (INFRA-02): Compose definícia teraz robí caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile (interval 30 s, retry 3, start_period 10 s). Keď preklep v Caddyfile zhodí konfiguráciu, kontajner sa označí unhealthy namiesto tichého servovania starej cache.
  • Json-file log rotation 20 MB × 5 (INFRA-04): postgres, redis, api, worker, scheduler, caddy majú default-logging cez x-default-logging YAML anchor. Predtým sa /var/lib/docker/containers/*/*.log mohol rozrásť na desiatky GB pri verbose worker-och a zhodiť disk.
  • Scheduler healthcheck (/proc/1/cmdline): pôvodný pgrep -f testy zlyhávali, lebo slim worker image nemá procps. Použitý tr '\0' ' ' < /proc/1/cmdline | grep -q app.workers.scheduler - funguje v každom Linux kontajneri bez extra závislosti.
  • Auto-spustené integration-tests na feature branchoch (CI-05): predtým when: manual + allow_failure: true znamenalo, že integration regression sa zachytil iba pri otvorení MR alebo merge-i do main. Teraz beží automaticky pri každom push-i na feature branch.

Vylepšené

  • API memory limit 768 MB -> 1024 MB (INFRA-03): PDF render pipeline + súčasné LLM streamy občas trafili OOM kill. Worker bumpnutý 512 -> 768 MB.
  • Sweep cleanup robí batched delete-y (DB-04): _cleanup_events_async, _cleanup_snapshots_async a _cleanup_audit_async mazali WHERE timestamp < cutoff jedným statement-om. Pri 1 M+ riadkoch to malo dlhú transakciu, blokovalo replikačné slot-y a vacuum mohlo zaostávať. Helper _delete_in_batches mazaní vo farbenných 5 K-row dávkach s commit-om medzi nimi a hard cap 200 dávok / spustenie (~1 M riadkov / sweep). Subsekvenčné spustenia plynulo dorobia zvyšok.
  • SBOM generovanie nemá allow_failure (CI-02): SBOM je supply-chain compliance artefakt. Tichý fail by znamenal, že release ide bez auditovateľnej depencency listingu.
  • Globálne APP_ENV: "test" v .gitlab-ci.yml: unit-tests job nemal explicitné APP_ENV, takže s pridanými production validátormi by Settings() v testoch hádzalo ValueError. Globálna premenná zaisťuje, že iba production-targeted joby behajú v production režime.

Opravené

  • Globálny job-bar - ETA "2 min left" sa zasekol pri 5+ min reálne uplynulých: _computeEta sa volalo iba pri novej progress-vzorke, takže medzi dávkami 1 Hz timer iba inkrementoval elapsed clock, ale štítok time-left zostal na poslednej vypočítanej hodnote. Teraz ETA kotvená na wall-clock expectedFinishAt; tick re-rendruje [data-job-eta] cez _displayEta(job), ktoré odpočítava k 0 a po vypršaní pôvodnej predikcie spočíta novú zo cumulative rate (totalElapsed / pct * (100-pct)). Žiadne flickeringy, ETA monotónne klesá.
  • Toasty pre dokončenie sync-u sa nezobrazovali pri minimalizovanej karte: sidebar.js zatváral dashboardový SSE pri visibilitychange->hidden. Server pushe pre member_sync_complete teda počas minimalizácie nedorazili - _queuedToasts nemal čo flush-núť pri návrate. Teraz SSE zostáva otvorený (browseri throttle-ujú, ale doručia), zastavujeme iba 60 s polling timer.
  • Course Analytics - Alpine Expression Error: reportFormController is not defined: report-form Alpine controller žil v pages/course-analytics.js, ktorý sa loadoval cez <script defer> vo vnútri HTMX-swapnutého partial-u. HTMX vytvára nové <script> elementy s defer, ale dynamicky inserted scripts nerešpektujú defer semantiku - bežia async. Alpine medzitým prejde nový subtree a pokúsi sa reportFormController() evaluovať skôr, než sa skript stiahol -> cascade startDate / endDate / durationText / dateError is not defined. Controller inline-nutý ako x-data="{...}" literál priamo na <form> - žiadna load-order závislosť.
  • Test test_tampered_signature_returns_none flaky: itsdangerous URLSafeSerializer pre SHA-1 emit-uje 27-char base64 podpis (162 bit-ov, z toho posledné 2 sú padding bits). Flipping len posledného char-u medzi "A" a "B" neménil dekódované HMAC byty (rozdiel iba v padding bit-och). Test teda v ~25% pípeline-ov reportoval, že tamper neporušil signature - pravdivo, ale to znamenalo, že test merá nesprávnu vec. Teraz mení interior char.

[2026-04-29]

Pridané

  • Notifikácia personálu kurzu pri novom support tikete: doteraz dostával e-mail iba odosielateľ tiketu (potvrdenie). Učitelia/owneri kurzu sa o nových tiketoch dozvedeli iba tým, že manuálne otvorili dashboard. Nový template notify_ticket_new_to_staff (subject [GitPulse] New ticket #{id} ({course_code}) - {title}) sa rozosiela všetkým aktívnym owner + teacher daného kurzu (s vylúčením samotného odosielateľa) v oboch vetvách submit_support_ticket (dynamic-form aj fixed-column). Idempotency cez ticket_new_staff kľúč, retry s exponential backoff, sweep každé 2 min ako fallback (api/telemetry.py, services/email_notifications.py)

Opravené

  • Únik súkromia: interné poznámky lekli odosielateľovi tiketu: notify_ticket_comment posielalo e-mail submitterovi aj keď is_internal=true (interná poznámka určená len pre personál - napr. "tento študent klame, ignoruj"). Pridaný explicitný guard not payload.is_internal pred notifikáciou submitter-a. Internal poznámky teraz dostane výhradne pridelený teacher/staff
  • Avatary nezostávajú písmenkami po prebudení notebooku zo spánku: keď notebook zaspí alebo Wi-Fi krátko vypadne, prehliadač vyhodí error event na každom rozpracovanom <img> aváteri. Predošlý handler okamžite skryl <img> a zobrazil písmenkový fallback - bez akejkoľvek logiky obnovy. Po jednom uspánkovaní teda VŠETKY avatary v aplikácii (header, dashboard greeting, profil študenta, support tikety, course teachers, search) tichto degradovali na písmenká až do hard-reloadu. Fix v core.js: uložiť pôvodný src pri prvej chybe; ak !navigator.onLine, odložiť rozhodnutie o fallbacku (mark data-avatar-pending="1") namiesto natvrdo zobrazenia písmena; nový retry-all listener na online + pageshow (BFCache) + visibilitychange->visible s 250 ms defer (čas pre DNS/Wi-Fi po wake) a cache-busterom obnoví všetky pending/fallback avatary jediným re-fetchom. Centralizované - funguje na celej aplikácii, nie iba na jednej stránke
  • Reálny double-submit race v formulároch - niektoré akcie posielali 2 POST naraz: globálny intersepter formulárov v core.js bežal na capture fáze a volal preventDefault() + fetch(), zatiaľ čo page-level handler na bubble fáze tiež volal preventDefault() + fetch() na rovnakom formulári. Výsledok: dva HTTP POST za jeden klik. Postihnuté: dismiss LLM error (project detail), login, Alpine @submit formulár pridelenia učiteľa kurzu. Fix: rozšírený zoznam pravidiel preskočenia - globálny handler teraz nechá tak formuláre s data-submit-action, @submit / @submit.prevent (Alpine), data-no-boundary="1" aj HTMX atribútmi. login.html označený data-no-boundary="1"
  • Email scheduler zaseknutý 25+ hodín - koniec záhady "nedošlo žiadne email": scheduler kontajner po dlhom uptime prestal registrovať periodické joby; pri starte ukazoval iba "Scheduler registered 5 periodic jobs" namiesto plnej sady. sweep_email_queue sa nezavolal nikdy -> email_log riadky zostávali pending navždy. Po redeploy.py (ktorý recreate-uje kontajnery) scheduler správne registruje všetky joby a smoke-test e-mail prešiel pipeline za 1.6 s

Vylepšené

  • Caddy konfigurácia - vyčistené Caddy 2.x deprecation warnings: basicauth direktíva zmenená na basic_auth pre /grafana, /prometheus, /jaeger (×3); odstránené redundantné header_up X-Forwarded-Proto {scheme} (×2 - Caddy reverse_proxy ho posiela automaticky). Po prebuilde žiadne deprecation/style warnings v caddy logu, basic_auth naďalej vynucuje 401 bez kreditov

[2026-04-28]

Vylepšené

  • Profil študenta - MR a issue diskusie rendrujú markdown: komentariale review (partials/student/_comments.html) sa predým zobrazovali len s linkifikovanými #/!/@ referenciami, zvyšok bol surový text. Teraz prechádzajú cez filter render_gl_description, ktorý bol rozšírený o fenced code blocks (```c ...```) - obsah je escapovaný, zabalený do <pre class="gl-desc-pre"><code class="language-X">...</code></pre> a má vlastný CSS štýl. Mark down ako ## nadpis, **bold**, _italic_, zoznamy (*/-/1.), inline `code` a fenced bloky fungujú rovnako ako vo GitLab webe
  • Render markdown popis - žiadne veľké medzery medzi sekciami: newliny sa teraz strihajú okolo OPEN aj CLOSE block tag-ov (<h6> ... </h6>, <pre>, <ul>, <ol>), takže prázdny riadok medzi nadpisom a odsekom už neprodukuje viditeľný dvojitý <br> gap. Bezpečnostný cap: 3+ po sebe idúce <br> sa zberú na 2
  • Note list (components/lists.html) - markdown render: sekundárny note-list-item komponent (legacy cesta) používa rovnaký filter ako student detail, aby žiadna výpisová plocha s GitLab komentármi nezostala s holou escapovanou hodnotou
  • Globally unified course-sync banner format: päť rôznych miest produkovalo štyri rôzne formáty toho istého Syncing... textu (SSE detail / SSE fallback / legacy poll / registerServerSyncs / _reconcileCourseJobs). Jeden helper _courseSyncMsg(name, running, queued, synced, total) v global-job-bar.js produkuje jednu formu vsade: "NAME: Syncing X/Y groups done (R active, Q queued)", X/Y a zátvorky odpadajú pri neznámych dátach. Helper je vystavený cez window._courseSyncMsg, dashboard a course-detail page-skripty volajú ten istý kód - koniec flickeringu medzi formátmi pri každom SSE ticku alebo htmx settle

[2026-04-27]

Opravené

  • CI dependency-scan job - koniec falošného "Job failed": druhý beh pip-audit (oproti celému inštalovanému prostrediu, nie len requirements-lock.txt) zachytával zraniteľnosti v CI nástrojoch - pip samotný (CVE-2026-3219, fix zatiaľ nie je vydaný), prípadne cyclonedx-bom/pip-audit tranzitívne závislosti - ktoré nie sú našou aplikačnou závislosťou. Pridaný || true na informatívny scan + komentár; requirements-lock.txt audit (skutočné runtime deps) zostáva striktný a generuje SBOM artefakt
  • Dashboard - falošný toast "Process timed out - check results manually" počas dlhého synku: dva navzájom sa zosilňujúce buggy. (1) _jobMaxAge pre course-sync joby (UUID-like alebo numeric ID) bol 10 minút, reálny sync 8-skupinového kurzu beží 15-25 min -> staleness-guard hlásil timeout aj keď server stále aktívne synkoval. Zvýšené na 45 minút. (2) Predošlá oprava "passive deferGlobalJob neprepisuje živú správu" early-return-ovala bez showGlobalJob -> _autoStartPolling sa nikdy nezavolal, lastUpdate zostávalo zamrznuté, staleness-guard po 10 min vypálil aj na zdravý job. Passive vetva teraz spúšťa polling, ak ešte žiadny nebeží (global-job-bar.js)

[2026-04-25]

Vylepšené

  • Per-team Status pill na stránke kurzu - okamžite po kliknutí Sync All: optimistický update v triggerSyncAll() zapíše QUEUED badge do cells[4] všetkých riadkov tímov a "Sync queued..." do Last Updated stĺpca pred odpoveďou servera. _refreshCourseData() neprepíše SYNCING/QUEUED badge počas SSE race-u; updateTeamRow(team) rieši všetky stavové prechody (course-detail.js)
  • /sync-all odpoveď do ~100 ms (predtým ~20 s): discovery tímov (volania /projects/:id/users) presunuté do background tasku _discover_course_teams_async - endpoint okamžite vráti queued, frontend nemusí čakať na rate-limit od GitLabu (api/sync.py)
  • Pilot Telemetry Service lint - cast(date, ...) cleanup: zjednotené datetime importy, premenované shadowing premenné v cykloch, ruff/mypy strict pass (services/pilot_telemetry.py)
  • Bez flickeringu progresu na dashboard kurzových kartách: stabilizované Last Updated / progress text počas SSE updates

[2026-04-24]

Opravené

  • Compliance report PDF - žiadne orphanované nadpisy ani orezané riadky: odstránili sme page-break-after: avoid na <h2> a break-inside: avoid na veľkých naratívnych/attention blokoch v @media print. V A4 landscape to spôsobovalo, že WeasyPrint posúval nadpis sekcie na prázdnu stranu, ak sa obsah už nezmestil do aktuálnej. Ponechané je len break-inside: avoid na úrovni riadkov tabuliek a titulkov - individuálne riadky sa už nerežú uprostred (src/app/workers/report_job.py)
  • Pilot Evaluation - reálne počty: namiesto surového počtu riadkov MetricSnapshot (rástol s každým webhookom) sa teraz počítajú distinct trojice (team_id, student_id, week_bucket) joinnuté cez TeamMember s filtrami is_active + excluded_from_scoring=False - teacherov, peer-mentorov a vylúčených členov report už nezapočíta. Odstránili sme aj or-fallback, ktorý v prípade nulových rpt_course_count použil globálne číslo. Týždeň pokrytia má teraz fallback na len(compliance_trends.weeks), aby graf a štatistika neukazovali 0 pri reálnych 5 týždňoch. Násobič klikov znížený na 2×checks + 4×flags (src/app/services/evaluation_report.py)

Vylepšené

  • Detail projektu - rovnaká veľkosť tlačidiel ako na skupine/kurze: akčné tlačidlá v hlavičke stránky projektu (Open in GitLab, Rotate Secret, Unlink, Run Checks) už nepoužívajú size='sm' - zarovnávajú sa s tlačidlami Link Project / Sync / Run Checks / Export CSV / Export JSON / Generate Report / Delete na stránke skupiny. Vizuálna konzistencia naprieč úrovňami (Kurz -> Skupina -> Projekt)
  • @mention v popisoch MR na stránke študenta sa rendruje ako meno: filter linkify_gl_refs prijíma voliteľný username_map (gitlab_username -> full_name) a pre mentiony spárované s členom tímu vypíše čitateľné meno (napr. Reviewers: Andrii Betsa namiesto Reviewers: @sm361rw). Neznáme @mentiony sa zobrazujú ako plain handle. Dotknuté šablóny: partials/student/_merge_requests.html (opis MR), partials/student/_comments.html (MR notes)
  • Compliance report - prepis do štýlu diplomovej práce: číslované sekcie 1-9, titulky Table 1..N, pätka so stranou, empty-state paragraphs pre každú sekciu, kompaktný "Group Overview" namiesto 4-card gridu, odstránená duplicita v exekutívnom zhrnutí a recommended-actions

[2026-04-23]

Pridané

  • Per-kurz prepínač skip_auto_archive (migrácia 036) - nové boolean pole v course_settings chráni manuálne reaktivované kurzy pred opätovnou archiváciou pri ďalšom behu sweep-u. course_service.activate_course() ho automaticky nastaví na True, aby kurz reaktivovaný po skončení semestra nezmizol znova na ďalší deň. Učiteľ ho môže manuálne prepnúť v UI Settings -> sekcia "Dátumy semestra"
  • Presety integrované do modálok "Vytvoriť kurz" a "Klonovať kurz": selektor Rýchla predvoľba teraz jedným kliknutím vyplní academic_year, semester (zimný/letný) aj dátumy semester_start_date a semester_end_date - učiteľ už nemusí otvárať Settings po vytvorení kurzu, aby sa kurz archivoval automaticky. Endpoint POST /api/v1/courses/ prijíma voliteľné polia semester_start_date / semester_end_date priamo pri vytváraní
  • Pole semester rozšírené v SemesterPreset: každý preset teraz nesie aj kanonickú hodnotu winter / summer, čo odstránilo potrebu ručne synchronizovať preset s Course.semester výberom
  • Plný cyklus exportu/importu teraz prenáša aj skip_auto_archive v JSON konfigurácii - config-import.js validuje a vypisuje "Skip auto-archive: yes" do import summary
  • Automatické vypnutie automatizácie po skončení semestra: nový denný plánovaný job sweep_auto_archive_ended_courses (queue low, interval 24 h) prepne courses.is_active=False pre kurzy, ktorých semester_end_date + semester_grace_days už uplynul. Pretože všetkých 23 existujúcich periodických sweep-ov už filtruje na Course.is_active.is_(True), jediné preklopenie flagu okamžite zastaví: periodic_enrich_all, periodic_membership_sync, periodic_metrics_all, periodic_llm_analysis, periodic_telemetry_snapshot, periodic_verify_webhooks, periodic_docker_reverify, periodic_enrich_users, sweep_stale_projects, sweep_dead_projects, sweep_discover_new_projects, sweep_discover_new_teams, plus všetky cleanup sweepy. Výsledok: nulové GitLab API volania pre dokončené semestre (migrácia 035_semester_end_date.py)
  • Nové pole course_settings.semester_end_date (DATE NULL): explicitné "posledný deň výučby/skúšok". Pre kurzy bez vyplneného semester_end_date sa odvodí z semester_start_date + semester_default_length_weeks (default 14), čo chráni pred zabudnutými kurzami bežiacimi donekonečna
  • Rýchle predvoľby semestra pre TUKE FEI KPI - Informatika (všetky ročníky): nový modul app.utils.semester_presets a endpoint GET /dashboard/semester-presets ponúka 4 preset-y: Zimný 2025/2026, Letný 2025/2026, Zimný 2026/2027, Letný 2026/2027. V UI kurzových nastavení dropdown "Rýchla predvoľba" jedným kliknutím vyplní oba dátumy (Monday of week 1 + posledný deň skúšok); učiteľ ich môže ďalej upraviť. Kalendár identický pre 1., 2., aj 3. ročník - TUKE FEI používa jeden akademický kalendár pre celý študijný program Informatika
  • Nové nastavenia v app.config: semester_auto_archive_enabled (feature flag, default True), semester_grace_days (default 14 dní - buffer pre uzatvorenie známok, neskoré MR-ky, export záverečných reportov), semester_default_length_weeks (default 14, fallback pre kurzy bez semester_end_date)
  • Helper modul app.utils.semester: dve čisté funkcie - get_effective_semester_end(cs, default_length_weeks) a is_semester_over(cs, grace_days, default_length_weeks, today=None). Explicitné semester_end_date má prednosť; fallback semester_start_date + N weeks. Open-ended kurzy (žiaden start ani end) sa nikdy neoznačia ako skončené - archivujú sa len manuálne cez admin UI
  • Plný cyklus exportu/importu konfigurácie teraz prenáša aj semester_end_date v JSON /settings/full-config (export + import vetva), aby sa presety dali prenášať medzi inštanciami a klonovanými kurzami
  • 14 nových unit testov v tests/unit/test_semester.py pokrývajúcich: prázdne settings, open-ended kurz, within-grace, past-grace, fallback-to-start+N-weeks, preset uniqueness, bilingual labels, JSON shape
  • SK preklady: Dátum konca semestra, Koniec semestra, Dátumy semestra, Rýchla predvoľba, - Vyberte predvoľbu -, Končí, Posledný deň výučby / skúšok..., TUKE FEI - Zimný/Letný semester 2025/2026 a 2026/2027 (Informatika, všetky ročníky) a ďalšie

Opravené

  • Týždenný graf skóre a Weekly Breakdown - falošné W1-W7 riadky pre tímy aktívne od polovice semestra: reťaz piatich prepojených chýb, ktorá spôsobila, že Score Trend ukazoval falošnú čiaru W1=100 % -> W7=0 % -> W8+ reálne, Skill Profile používal zastaralý radar z W7 (0 %) a Weekly Breakdown mal sedem prázdnych červených riadkov vyvolávajúcich paniku študenta a učiteľa:
    • (a) pilot_telemetry.get_student_compliance_history primárna vetva (keď existovali StudentComplianceSnapshot riadky) nikdy nevracala zozbierané dáta - return filled chýbalo - a kód padol do fallback MetricSnapshot vetvy, ktorá videla len najnovšie computed_at (W10). Fix: pridané return filled (commit bd189ee8)
    • (b) compliance.engine.backfill_compliance_history fabrikoval StudentComplianceSnapshot riadky pre týždne 1..N aj pre tímy, ktoré GitLab projekt ešte nemali (-> W1=100 % "default ok" + W2..W7=0 %). Fix: detekcia prvého aktívneho týždňa z MIN(Commit.committed_at), preskočenie predchádzajúcich (commit 3e6fe1b1)
    • © heterogénna n-tica for col, model in ((Commit.committed_at, Commit), (MergeRequest.created_at, MergeRequest), ...) zlyhala na CI strict-mypy kvôli union-type inference. Fix: rozdelené na tri explicitné select(func.min(...)) dotazy (commit 52611892)
    • (d) Result[Any].rowcount nie je v SQLAlchemy async stuboch -> CI mypy attr-defined. Fix: getattr(deleted, "rowcount", 0) or 0 (commit c289c5b1)
    • (e) skrytý root cause: GitLab pri vytvorení projektu automaticky vytvorí počiatočný README commit s committed_at ≈ čas vytvorenia projektu, takže MIN(Commit.committed_at) vrátil W1 pre každý tím, a celá anti-fabrikačná logika z (b) zostala no-op. Fix: first_activity sa teraz určuje z MIN(MergeRequest.created_at) a MIN(Issue.created_at) (skutočné úmyselné akcie študenta); commity sa berú iba ako fallback a len tie, ktorých author_id zodpovedá TeamMember.student_id (ignoruje bootstrap bot/system commity) (commit 56a9387f)
    • (f) oprava post-deploy auditu: aj po DELETE pre-activity StudentComplianceSnapshot riadky API stále syntetizovalo W1..(first_activity-1) ako null stub riadky kvôli for wk in range(1, max_week+1) - Weekly Breakdown ich renderoval ako prázdne em-dash riadky. Fix: rozsah začína od min(populated_weeks), takže chart/heatmap/tabuľka zobrazujú len reálne týždne - žiadne fabrikované dáta, žiadne zavádzajúce prázdne riadky
  • Dôsledok: pre tím, ktorý zahájil prácu v W8 a má aktuálnu aktivitu W10, UI teraz pravdivo zobrazuje iba W8, W9, W10 - bez zavádzajúcich W1=100 % (falošné zelené) ani W2-W7=0 % (falošné červené)

[2026-04-22]

Pridané

  • Job bar - stav "Dokončuje sa...": globálna lišta postupu už nezamrzá na "0 beží, 0 v rade" počas 15-sekundového post-batch grace okna na serveri. Keď klient vidí running==0 && queued==0 ale server ešte hlási has_active_sync=true, lišta prepne do stavu finishing so správou t('Finishing...') mésto ínak šrtnutého počítadla. Týka sa SSE aj legacy poll vetvy v global-job-bar.js
  • Preklad - pokryté stavy kariet a job bar fallbacky: všetky hardcoded English fallbacky v global-job-bar.js ('Analyzing...', 'Generating report...', 'Running compliance checks...', 'Importing roster...', 'Exporting data...', 'Processing...') zabalené do t(...) a pridané zodpovedajúce klúče do sk.json. Audit skript scripts/find_missing_translations.py teraz hlási 0 chýbajúcich klúčov

Pridané

  • Issues to Address -> interaktívny graf: statický zoznam porušených kontrol nahradený horizontálnym stĺpcovým grafom (Chart.js) - os X je % zasiahnutej skupiny, stĺpce sú farebne odlíšené podľa závažnosti (červená High / oranžová Medium / modrá Low), animované entrance (900 ms, staggered). Hover tooltip zobrazuje count/weight/severity, klik na stĺpec rozbalí detail-panel so zoznamom zasiahnutých študentov v grid-e kariet s click-throughom do riadku tabuľky členov. Zachovaný IntersectionObserver scroll-reveal pattern a <noscript> fallback. Chart.js sa načíta iba na project-detail stránke cez extra_head
  • Detail-panel Issues to Address: nová kartička s farebným ľavým okrajom podľa severity, uppercase severity-pill, close-tlačidlo (×), tabular-nums štatistiky (affected/total · share · weight) a zoznam študentov ako hover-highlighted karty v responzívnom grid-e. Legenda ako pill-chips (namiesto kurzívy) pre konzistentnosť s ostatnými komponentmi
  • Quick Access - informatívne položky: každá položka v bočnom paneli teraz zobrazuje traffic-light bodku poslednej compliance metriky, skóre v % a počet členov z TeamMetricSnapshot namiesto iba názvu (sidebar je teraz informatívny, nie dekoratívny)
  • Slovenský preklad UI - kompletné pokrytie: doplnených 99 chýbajúcich prekladov do sk.json (chybové hlášky synchronizácie, dialógy potvrdení, toast správy, editor rubriky, stránka admin-users, user-info modal, reaktivácia projektu, CI scenáre). Nový audit skript scripts/find_missing_translations.py skenuje všetky t(...) volania v JS a šablónach a hlási chýbajúce kľúče - zaradený do CI checklistu
  • Vlastné výnimky pre GitLab klienta: nová trieda GitLabPaginationTruncatedError (podtrieda GitLabAPIError) - pagination-truncate už nevracia čiastočné dáta ticho, ale vyvolá výnimku, takže per-project try/except označí len tento jeden projekt ako zlyhaný a sweep ho neskôr zopakuje

Opravené

  • Mobilná responzívnosť - tabuľky a profilová karta: (1) profile header na úzčku .profile-identity nemal min-width:0 a odkaz "Zobraziť na GitLabe" pretáčal cez okraj karty na mobile - pridané flex:1+min-width:0, overflow-wrap:anywhere a align-self:flex-start+nowrap na link. (2) Tabuľka členov projektu mala na mene <td class="text-nowrap">, sticky koľonka sa preto roztáhla a prekrývala R01-R13 check-bunky - na mobile sa text-nowrap prepíše na white-space:normal + max-width:14rem + wrap badges. (3) Teams tabuľka kurzu (7 stĺpcov) skrýva tree-toggle a Last Updated stĺpec na mobile, aby Avg Score / Status / Actions boli viditeľné bez horizontálneho skrolu. (4) Check-bunky .check-pass/.check-fail/.check-na prepnuté na vertical-align:middle - ikony už neleznia navrchu viacriadkovej bunky s menom
  • Klik na meno študenta v Issues to Address už neposkúva stránku na vrch: scrollIntoView skroloval aj externe ch-containery (viewport) prečo sa fixný topbar vizuálne presunúl. Teraz skrol cieli výhradne na .gl-page-content a dopočíta scrollTop + boundingRect posun, takže cieľový riadok presne centruje v scrolľovateľnej zone a okolité elementy (topbar, breadcrumb, sidebar) zostanú nehybné
  • Týždenný compliance heatmap - historické týždne už nie sú prázdne: dve previazané chyby mazali minulé týždne pri synku. (a) compliance.engine v backfill_compliance_history ignoroval parameter as_of_week a vždy písal aktuálne číslo týždňa do StudentComplianceSnapshot.week_number. (b) pilot_telemetry.capture_team_snapshots posielal ON CONFLICT DO UPDATE so zbaveným raw_metrics (bez checks_detail), čím prepísal engine'om naplných 13 checkov na prázdne. Fix: engine rešpektuje as_of_week, telemetry sweep teraz odvodí kategorické skóre z _derive_category_scores(checks_detail) a do raw_metrics píše superset. 170 unit testov prehľto

  • Issues to Address - kohorta priamych členov projektu: bola zoradená podľa latest_computed s limitom 20, ale tých 20 riadkov boli tie isté tímy, čo už boli v karte Groups (team:project je ~1:1), takže po Python dedupe ostalo 0 projektov a karta vždy ukazovala prázdny stav. Teraz sa team_id NOT IN tlačí do SQL WHERE, takže 5-row limit sa minie na tímy, ktoré NIE SÚ už v Groups; ak Groups pokrýva všetky tímy (≤5), fallback ukáže top-5 projektov bez ohľadu na duplicity

  • Course Analytics - automatické skrolovanie stránky pri načítaní: prvotné selectWeek(currentWeek) volalo pill.scrollIntoView({block:'nearest'}), čo prehliadače interpretovali ako vertikálne skrolovanie celého dokumentu (keď cieľ bol pod prvou obrazovkou). Teraz initial select preskakuje scroll (scrollPill:false), a user-driven klik používa weekPills.scrollBy({left, behavior:'smooth'}) scoped iba na horizontálny pásik pill-ov
  • LLM modal - guard proti volaniu endpointov bez API kľúča: všetky LLM modály teraz kontrolujú feature flag aj prítomnosť API kľúča pred zobrazením a vracajú priateľskú chybu namiesto 500 z upstream volania
  • GitLab 429 (rate limit) - tichá strata dát: po vyčerpaní retry-ov klient vracal None, čo volajúci kód interpretoval ako "žiadne dáta" (404) - sync job sa označil ako completed s chýbajúcimi issue/MR/pipeline záznamami, dashboard zobrazoval nesprávne metriky. Teraz vyvolá GitLabAPIError(429) s Retry-After v správe
  • GitLab pagination (10 000+ položiek) - tichá strata dát: pri dosiahnutí stropu max_pages=100 s plnou poslednou stránkou (čo znamená, že GitLab má viac dát) klient vracal čiastočný zoznam a len zalogoval warning. Dashboard po synchronizácii zobrazoval "completed" s podhodnotenými číslami. Teraz vyvolá GitLabPaginationTruncatedError so štruktúrovanými poliami (path, max_pages, items_fetched) pre Grafana alert
  • Webhook rate-limit IP spoofing (DoS): orphan-webhook rate limiter čítal X-Forwarded-For priamo z hlavičiek a obchádzal trust-boundary ProxyHeadersMiddleware - útočník mohol rotovať spoofed IP pre obídenie limitu 10/min a zaplavenie orphan_webhook_events. Teraz sa používa request.client.host, ktorý middleware už validoval voči trusted_hosts
  • Webhook backpressure - tiché zlyhanie kontroly: except: enqueue anyway ticho skrývalo poruchy backpressure modulu / Redis outage. Fallback je zachovaný (best-effort enqueue), ale teraz sa loguje logger.warning s exc_info - operátori vidia degradovaný režim namiesto falošnej istoty
  • Globálny job bar - únik intervalu pri zatvorení karty: _syncWatchdogId (5-sekundový polling /sync/active) sa nečistil v beforeunload handleri - interval žil až do garbage collection po zatvorení karty alebo odhlásení. Teraz clearInterval spolu s _elapsedTimerId

[2026-04-21]

Zmenené

  • Sync UI - jeden zdroj pravdy: odstránený redundantný inline info-card "Sync in progress" z overview, roster aj teachers záložiek - perzistentný globálny progress bar je teraz jediný indikátor synku, čím sa odstránili duplicitné UI flash-e a double-animation počas prechodov medzi stránkami (-312 riadkov mŕtveho UI kódu vrátane komponentu SyncStatusBanner a jeho pollerov)
  • Sync freshness guard (up-to-date kontrola): POST /api/v1/courses/{id}/sync-all a POST /api/v1/teams/{id}/sync teraz preskočia tímy, ktorých posledný úspešný sync skončil v 5-minútovom okne - odpoveď obsahuje already_fresh: N, single-team vracia status: "skipped_fresh"; ?force=true obídete guard. Zabraňuje zahlteniu GitLab API pri opakovanom kliknutí na Sync All alebo pri paralelnom sledovaní kurzu viacerými učiteľmi. Frontend ponúkne force re-sync, ak sú všetky tímy čerstvé, namiesto tichého no-op-u

Opravené

  • Globálny progress bar - objavenie aktívnych synkov na všetkých stránkach: nový endpoint GET /api/v1/sync/active vráti prebiehajúce kurzové synky, ktoré globálny bar volá pri restoreGlobalJobBar() - bar sa teraz zobrazí aj na Admin -> Users / System / Dashboard, keď sync beží v inom tabe, zariadení alebo bol spustený plánovačom
  • Globálny progress bar - race conditions: odstránený tichý removal bar-u počas 2-sekundového restore okna, keď /sync-status dočasne vrátil has_active_sync=false počas prechodu stavu; job má teraz flag serverConfirmedRunning, a silent-remove guard sa spúšťa len pre skutočné redirect-flash prípady
  • Issues to Address - kohorta priamych členov projektu: karta iterovala cez celý tímový zoznam (napr. 37/37) namiesto len priamych členov GitLab projektu (napr. 5/5) - live-data endpoint teraz volá get_project_members(include_inherited=False) rovnako ako full-page render
  • Issues to Address - dynamická aktualizácia: karta sa teraz prerendrovuje pri každej zmene (vylúčenie, zmena roly, nový check run) cez server-side Jinja partial
  • Issues to Address - syntetickí/zdedení členovia: učitelia, mentori, zdedení a syntetickí GitLab-only členovia sú vylúčení zo scorer kohorty
  • CSS bleed systémové riešenie: globálne details summary::before pravidlá presunuté pod opt-in triedu .details-accordion; odstránená duplikátna unicode šípka ▶ z pages/course.css

[2026-04-20]

Pridané

  • Comprehensive mobile responsiveness overhaul
  • GitHub-style zebra tables + changelog sidebar widget
  • Kompletný changelog zo všetkých 1347 commitov od vytvorenia repozitára

Opravené

  • Auth resilience - login page for new users, next-URL preservation, crash protection
  • Redesign OG image - clean centered layout for Google previews
  • Prevent skeleton flash + lighten heavy table skeletons
  • Rebuild bundles + ruff format auth.py (fix CI lint)
  • Prevent zebra-stripe flash when toggling member filters
  • Clean empty CSS ruleset + add restripeTable to no-paginator filter path
  • Fully suppress transition on clickable rows during restripe
  • Zebra tabuľky bez animácií - odstránenie transition na riadkoch, okamžitý hover (GitHub štýl)
  • Propagate zebra/hover backgrounds to <td> for overflow:hidden tables
  • Use solid opaque colors for table zebra stripes
  • Remove opacity from row-page-enter animation to prevent color mixing during filter/pagination

Zmenené

  • GitLab-style skeleton loaders - lighter, mixed with spinners

Infraštruktúra

  • Code quality audit - var->let/const, dead code removal, cache fixes

[2026-04-13 - 2026-04-17]

Pridané

  • Periodic team discovery sweep + lint fixes
  • Email-based teacher/mentor detection + early sync progress
  • Improve OG image + add skeleton loader to User Management
  • Add peer mentor detection and filter
  • Production-grade email notification system with retry queue

Opravené

  • Email enrichment propagates to User + filter inherited teachers from contributors
  • Whitelist contributor filter - show only team student commits
  • Whitelist contributor counts on course/team dashboard too
  • Remove deprecated code, fix labels, inherited members modal, subtle flash
  • Resolve mypy arg-type errors in _stats.py narrowing
  • Lower coverage threshold to 30, fix arena email caching and timeouts
  • Replace asyncio.TimeoutError with builtin TimeoutError (ruff UP041)
  • Add arena email fallback to single-user enrich, suppress global bar flash on redirect
  • Auto-create User records for inherited members, remove teacher/mentor opacity
  • Batch enrich Arena fallback, course count from groups, inherited member import
  • Inherited member import, toast visibility, course cards in modal
  • Course display with code/year/semester for membership-derived courses, add academic_year+semester to membership API, fix ruff-format
  • Arena enrichment for User records during import, role badge for derived courses, elevate Arena logs to warning
  • Mypy User.gitlab_username type + strip course name from card sync badge
  • Stale sync after redeploy + smooth global bar entrance
  • Create User records during member sync (not only on initial import)
  • Ensure User record for existing active members during sync
  • Limit User creation during sync to teachers only
  • Enrich User emails from Student records and Arena during sync
  • Sync_all discovers new teams + Arena enrichment shared httpx client
  • Arena enrichment 30s per-request timeout, concurrency 2
  • Update scheduler test counts for job #23 + cross-team project dup guard
  • Pass course_id to sync worker for early progress toasts
  • Add toasts to enrich button + welcome-back toast after login
  • Reduce lazy skeleton reveal delay from 400ms to 150ms
  • Rewrite admin users skeleton to match page structure + ruff format
  • Remove corrupted duplicate in sk_admin_users skeleton
  • Defer admin-users paginator/filter init for lazy-shell loading
  • Skeleton pagination to match 3-column layout (info | buttons | per-page)
  • Update all skeleton pagination to 3-column layout (info|buttons|per-page)
  • Skeleton search width, pagination inside card-body, mobile dropdown clamping
  • Dropdown portal right-aligned, support tickets skeleton structure
  • Dropdown portal conflict with global handler - use data-gl-portal marker
  • Keep per-page dropdown visible when pageSize=all so user can switch back
  • Skeleton card structure, pagination DRY, pageSize localStorage cache
  • Add per-page dropdown to admin/system Job History pagination
  • Mobile responsiveness, optimize scheduler & telemetry
  • Stop spinner reset and button color flash on mobile
  • Pull-to-refresh false trigger on table scroll + toast reliability + lint
  • Completion toast suppressed when global bar visible
  • Ruff E501 in scheduler.py (line too long)
  • Member table, enrichment gate, contributor exclusion
  • Global bar dedup, contributor exclusion in project detail
  • Complete mentor support across all views and scoring paths
  • Exclude mentors from all student count queries
  • Preserve is_mentor and excluded_from_scoring on team reassignment
  • Increase worker heartbeat freshness window to match RQ TTL (420s)
  • Enrich PRIVATE emails during sync and periodic sweep
  • Member table bugs - filter empty state, badge count, sorting, inherited visibility, commit-based email enrichment
  • Arena email enrichment, mentor badge, blue focus, progress bar course name, student count labels
  • Dashboard Students stat now counts User accounts (matches admin breakdown)
  • Deduplicate role badges, global bar flash, blue row selection
  • Prevent global bar flash during skeleton loader phase
  • Ruff-check unused import + ruff-format (fixes CI pipeline)
  • Dashboard JS created duplicate at-risk badge using old class names
  • Show more description text in course card (3-line clamp, no Jinja truncate)
  • Global job bar hidden on team page when course sync is running
  • Sorting/filter conflict + welcome email on first login
  • Rebuild JS bundle for sorting/filter changes
  • Correct CTA links in welcome emails (404 paths)
  • Remove all emoji characters from email notifications
  • Replace deforming loader icon with smooth circle spinner
  • Show text status badges instead of dots in group activity table
  • Prevent page-size dropdown from being clipped by pagination container
  • Replace custom page-size dropdown with native select
  • Restore custom page-size dropdown with fixed positioning
  • Portal page-size dropdown to document.body to escape overflow
  • Smart dropdown direction - open downward when space allows
  • Remove unused Request import (ruff F401)
  • Tabs overflow, footer theme, remove icons, accuracy fixes
  • Tabs scroll + footer light theme with explicit colors
  • Shorten tab names, remove tab icons, light footer, full-width tabs
  • Footer light mode with !important to override Material theme
  • Footer text/link colors visible in light mode
  • Stronger no-store cache headers to prevent stale proxy/browser cache
  • Job history table responsive headers + zebra striping on filter/sort
  • Replace broken Sync Status modal with toast notifications
  • Update tests for queue-based email system + fix ruff lint

Zmenené

  • Replace var with const/Set/Map in JS, use begin_nested savepoints instead of rollback, add logger.debug for inherited member fetches, consolidate imports, fix line lengths
  • Reuse ClientPaginator for admin Job History (server-side mode)
  • Revert: remove commit-based email enrichment - Arena API already handles this
  • Privacy: comprehensive GitLab-quality privacy statement with TOC, tables, GDPR rights, retention schedule, cookie details, supervisory authority

Bezpečnosť

  • Fix header issues from security scan
  • Add privacy policy page, CSP upgrade-insecure-requests, sitemap update

UI / UX

  • Redesign: course card - clean layout like top companies

Infraštruktúra

  • Config: pass EXCLUDED_CONTRIBUTOR_NAMES through docker-compose
  • Config: remove unused ARENA_EMAIL_API_TOKEN from compose (endpoint is public)

Dokumentácia

  • Comprehensive audit - fix counts, intervals, add 19 missing models
  • Full documentation audit and actualization
  • Move legal pages to docs-site, add student guide, open docs access

[2026-04-09 - 2026-04-12]

Pridané

  • Smart stale data notifications with webhook activity detection
  • Webhook attention items + fix activity feed showing team actions
  • Premium Week Analytics with interactive Chart.js charts
  • I18n: add 17 missing Slovak translations for Week Analytics
  • Auto-expand current week in Week Analytics on page load
  • I18n: add 87 missing Slovak translations
  • Add Week Analytics skeleton loader to analytics page
  • Redesign report generation form with component-based UI
  • Simplify sub-project rows to links only, remove per-project metrics
  • Detect template repos, unify team page contributor count
  • Stale data warning banner for course and team pages
  • Comprehensive i18n, job history improvements, LLM config diagnostics
  • Expose Grafana/Prometheus/Jaeger via Caddy with basic auth
  • Enrich Configuration Preview with detailed settings, LLM status, course metadata
  • Install OpenTelemetry packages in Docker image and init tracing in workers
  • GitLab-style course sub-navigation + update course configs

Opravené

  • Enrich activity feed with background_jobs + fix audit commit in sync endpoints
  • Ruff lint + format errors (line length, unused import, alias)
  • Remove broken effective-start detection, replace weekly summary with week navigator
  • Analytics & activity quality audit - responsive, empty states, data consistency
  • Show empty state message in week bar chart when activity is 0
  • Pill focus outline follows rounded shape
  • Fully suppress rectangular focus outline on week pills
  • Remove focus ring from week pills entirely
  • Remove blue glow shadow from active week pill
  • Ruff-format compliance + suppress stale-data toast on report generation
  • Widen GitLab ID column, add webhook setup info to Event Pipeline page
  • Smart webhook detection on events page, rebuild CSS bundle
  • Use toast instead of alert banner for webhook warning on events page
  • Code quality improvements across admin module
  • CI pipeline failures - lint script, admin early-return in _session_caller
  • Mobile responsiveness - activity tables scroll, instructors layout, danger zone overlap
  • Redesign instructors mobile layout - CSS Grid 2-row compact (GitHub/GitLab style)
  • Mobile responsive - students table scroll, sidebar overflow, compact action buttons
  • Sidebar panel overflow:hidden, action buttons 32px matching btn base height
  • Distinct icons for team actions - code for JSON, bar-chart-2 for Report
  • Stretch action buttons evenly across full width on mobile
  • Uniform 36px square buttons in mobile action toolbar
  • Quick Access projects tab overflow + skeleton loader matches action toolbar
  • Quick Access ellipsis - move overflow:hidden to list level, not item
  • Quick Access ellipsis - remove negative margins, add overflow:hidden to .gl-qa-text
  • Truncate long project paths server-side (45 chars) + display:block on qa spans
  • Quick Access: filter empty/template teams, fix CSS text-overflow
  • Use student_id instead of id in TeamMember count
  • Add display:block + flex:1 for CSS text-overflow ellipsis in Quick Access
  • Use width:0+flex-grow:1 for reliable text-overflow ellipsis, cap sidebar on mobile
  • Commits table + heatmap mobile responsive, badge inline positioning
  • Pin trivy to 0.62.1 (latest tag removed from Docker Hub), guard publish upload
  • Team contributors count uses distinct commit authors for consistency with sub-projects
  • Live-data and SSE endpoints also use distinct commit authors for contributor counts
  • Show dash instead of 0% for projects with no resolved authors
  • Resolve ruff lint errors - unused import and per-file-ignores for utility scripts
  • Sub-projects inherit team avg score and traffic light when no commit authors resolved
  • Hide stale-data banner when sync is already in progress
  • Template detection now also checks author_gitlab_id against team students
  • Exclude teachers from contributor counts + robust compliance chart loading
  • Contributor count excludes teacher by name + compliance data_attrs as dict
  • Mypy types, dedup teacher imports, improve group messages, quick access, compliance visuals
  • Mypy list[Any] type, never-synced count uses sync status not metrics
  • MR empty state msg, improve semester settings, remove review requirements card
  • Align course configs with assignment requirements + fix sk.json broken quote
  • Remove C01/C02 custom checks, fix weights to sum 1.00
  • Resolve mypy and ruff lint errors in LLM status endpoint
  • Monitoring proxy + code quality cleanup
  • Rebuild CSS bundle (config-preview-details styles were missing)
  • Config preview sub-items layout (flex-basis + baseline align)
  • Hide AI Analysis Configuration card when LLM feature is disabled
  • Support form dropdown clipped by overflow:hidden on .gh-category-fields
  • Full monitoring stack - Grafana Failed to fetch, Jaeger DS, enhanced dashboard
  • Remove orphaned duplicate alerts in alerts.yml
  • Add basic_auth for Grafana metrics scraping by Prometheus
  • Correct rq_failed_jobs metric name in dashboard and alerts
  • Align DevOps config with ZSI, remove sergej.chodarev from teachers
  • Rebuild CSS bundle from sources + fix ZSI description
  • Update DevOps description with test (10b) + team project (10b)
  • Align course descriptions with official kurzy.kpi pages
  • Unify Refresh buttons across all course pages
  • Unify dashboard header buttons - remove size='sm' to match course pages
  • Break SSE read loop on done event to prevent spurious network error toast
  • Remove extra closing brace that broke JS bundle parsing
  • Break outer while loop in readImportSSEStream after done event
  • Concurrent email enrichment + activity feed labels/dedup
  • Remove commit sync date filter + fix CI lint + show 0 for empty sub-projects
  • R08 and R13 no longer auto-pass with zero student activity
  • Update R13 test to expect fail with zero commits
  • Progress timer ticks every 1s + threshold labels positioned at actual percentages
  • Send SSE done event before sync-enqueue to prevent network error toast
  • Default member filter shows Direct + Inherited to match team contributor count
  • Exclude inherited members from team student count on course overview

Zmenené

  • Deduplicate components, consolidate JS utils, update docs
  • Consolidate _lock alias into GP destructuring across 14 page JS files
  • Deduplicate code patterns, validate thresholds, clean translations
  • Unify contributor counts via shared _stats helper, remove fake per-project score fallback
  • Lift tab nav to shell pages - instant tabs, ARIA a11y, keyboard nav, body-only skeletons

UI / UX

  • Fix ruff-format for teams.py and reset_db_remote.py

Testy

  • Force-show all 3 stale data scenarios for visual review

[2026-03-30 - 2026-04-04]

Pridané

  • Search panel - mobile close btn, GitLab avatars, user->admin nav with auto-search
  • PWA support + rewrite login animations (no conflicts)
  • SSR-direct page entrance animation - staggered contentEnter for pages without skeleton loaders
  • Edge-to-edge mobile cards + pull-to-refresh
  • Comprehensive SEO - Google Search Console, OG, sitemap, robots.txt
  • Comprehensive email notifications + refactor to shared template
  • Non-anonymous tickets + auth-only submission + SMTP setup
  • Add proper error handling for 401/session expired
  • Wire support form skeleton + sys audit cleanup
  • Add 346 missing Slovak translations for support, fix form overflow CSS, improve skeleton
  • Support ticket system with comments, filters, and management\n\n- Add migration 031: ticket lifecycle columns (status, priority, category,\n assigned_to, resolved_at, updated_at) + support_comments table\n- Add SupportComment model with author, content, is_internal fields\n- Add ticket management API: PUT ticket, GET/POST comments\n- Add filtering by status/priority/category/search in ticket list\n- Rewrite support_tickets.html: filter bar, expandable detail rows,\n inline status/priority/category management, comments section\n- Replace all Unicode arrows with SVG icon components\n- Replace emojis with icon() macro calls throughout\n- Rename FEATURE_FEEDBACK_FORMS to FEATURE_SUPPORT_FORMS in docs\n- Update architecture doc: feedback.css -> support.css\n- Add btn_link_icon for back navigation and pagination controls\n- All linters pass (ruff, mypy), 2382 tests pass"
  • GitHub-style support system redesign
  • SSE real-time updates, email notifications, GitHub-identical UI
  • Redesign support filters with animated selects + category-specific form fields
  • Add alternating row backgrounds for visual consistency
  • Ticket detail - component-based layout, assignee user search
  • Dashboard integration - attention items, activity feed, sidebar badges, error page links
  • Pass SMTP env vars through docker-compose to containers
  • Component-based Subgroups & Projects table + Groups child rows
  • Auto-enrich user profiles from GitLab after config import
  • Persistent feedback links with CRUD, form picker, expiry, and link management
  • Move Feedback Links to Feedback Forms page + gl-dropdown form picker + context default forms
  • Add expand/collapse arrows to groups table with member preview
  • Expandable tree view for team projects + UI fixes
  • Arena email API fallback + filter bots from instructor lists
  • Security hardening - secret rotation, per-user tokens, gaming detectors, CSRF UA binding

Opravené

  • Add PNG favicon for Google Search + shorten meta description
  • Redirect /favicon.ico to PNG instead of SVG
  • Slovak translations (podpora), mobile responsive improvements, email cleanup
  • Login page mobile - keep desktop look (background, centered card)
  • Ruff format + remove deploy job from CI pipeline
  • Login language switcher 404 - use /dashboard/login path
  • Login mobile - keep card contained like Keycloak (remove max-width:100%)
  • Login mobile - full-screen card like Keycloak (edge-to-edge white, no dark bg)
  • Login mobile - center content, visible lang selector on white bg
  • SW CORS error on OAuth redirect - skip navigate requests
  • Remove /dashboard/ from SW pre-cache - redirects to OAuth when unauthenticated
  • Remove SSR entrance guard - caused blank page on slow connections
  • Prevent skeleton flash on fast HTMX loads
  • Mobile card styling + PTR indicator hidden by default
  • Rewrite PTR to match GitLab blue circle design
  • Rewrite PTR and mobile card styling (manual)
  • Clean PTR rewrite - fix cancelable touchmove + Material spinner
  • Instant mobile logout + suppress native PTR on login page
  • Regenerate OG image with proper GitPulse branding
  • Clean OG image rewrite - minimalist pixel-perfect layout
  • Render login page for search-engine crawlers instead of OAuth redirect
  • Detect Lighthouse/PageSpeed bot in crawler regex
  • Ruff lint + accessibility (main landmark, color contrast)
  • Allow HEAD method on SEO routes (robots.txt, sitemap.xml, verification)
  • Restructure robots.txt - unblock /dashboard/login for Google
  • Simplify robots.txt - remove dashboard disallow entirely
  • Support HEAD on /dashboard/login + detect Google-InspectionTool
  • CI mypy errors, remove sidebar badge, fix login lang auto-login
  • Wire comprehensive email enrichment with GitLab API + Arena fallback
  • Support form double toast, markdown preview, asterisk notice, email notification
  • Proper double-toast fix (data-no-boundary), XSS prevention in markdown, hide info-card on success
  • Rebuild JS bundle, fix mypy dict type in gitlab_group_import
  • Remove temp SMTP scripts causing ruff lint failures
  • Add Arena API token auth + Arena fallback in student enrichment
  • Improve student enrichment with username search fallback
  • Replace anonymous message with logged-in user info on support form
  • Rebuild JS bundle and use proper 401 SVG from GitLab design
  • Rewrite renderMarkdown() in support-form & ticket-detail
  • Ticket comments 500, emails, comment permissions
  • Duplicate comments, missing icons, email avatar, notifications, responsiveness
  • Admin-system crash, duplicate emails, 401 UA mismatch
  • SSE real-time timeline update, touch updated_at on comment, skeleton loaders
  • Perfect 1:1 skeleton loaders for support pages, update email v4 with arena/SMTP/SSE changes
  • Restrict textarea.form-input to vertical resize only
  • Render info_card HTML correctly in support form (use call block)
  • Complete feedback->support rename, fix .env config bug and data.feedback attribute bug
  • CI lint failures + SSE security + dashboard auth guards + docs
  • Support page layout to match standard dashboard structure
  • Remove sidebar duplication, drop support/manage page, fix filter styling
  • Full-width form, move info card, expand category-specific fields
  • Improve support form - reorder notices, add padding, expand category fields
  • Dropdown z-index/transparency bug - replace opacity transition with class-based enter/leave
  • Add id=description to textarea and for=description to label (a11y)
  • Correct RateLimitDep usage and add empty-string UUID validator for SupportCreate
  • Tickets table matching admin pattern + dropdown transparency
  • Rewrite support tickets page with proper table/filter components
  • Cast UUID to string for tojson serialization
  • Improve toolbar dropdown hover with button-like bg highlight
  • Move filters to card header with search (User Management pattern)
  • Use subtle rgba hover for data-table rows across all pages
  • Ticket detail - proper two-column layout, fix sidebar overflow, self-contained assignee dropdown
  • Alpine expression error, form corners, sidebar heading colors, assignee reload, responsive
  • RenderMarkdown via vanilla JS init instead of Alpine expressions - prevents quote escaping errors
  • Correct SSE endpoint URL - remove stale /sse/ prefix
  • Enable assignee unassign + form accessibility (id/label)
  • Change timeline labels to spans (no associated form field)
  • Deep-link URLs, email notifications, assigned-by info, 403 page
  • Harden teacher membership sync - upgrade student memberships, elevate User.role, enrich avatars
  • Resolve ruff lint/format failures in teacher membership sync
  • Course Teachers count includes admins, filter-pill descender clipping, config preview spacing
  • Create User records for teacher_usernames during config import
  • Ruff lint errors in reset_prod_db.py, tighter config preview spacing
  • Section count toast mismatch + dashboard config preview spacing
  • Add error handlers for unexpected scenarios + fix mismatches
  • Active column width, unify config-import component
  • Remove stale _fullConfig ref, add comprehensive config validations
  • Pass disabledFeatures to config-import in course-settings
  • Prevent SSE task cancellation after import commit + add keepalive pings
  • Filter bot users from teacher discovery + clear config on import success
  • Fast config import, modal close, admin-teacher filter
  • Correct cell indices in course-detail.js (group names overwritten by member count)
  • Student privacy - hide metrics, restrict profile access, fix tree layout
  • Student profiles, LLM error display, table layout, global job bar coverage
  • Clean LLM error display, role-appropriate suggestions, proper icon
  • Error card dynamic injection + CI test fix
  • Skip job history for zero-work scheduled jobs + fix mypy
  • Groups child-row dashes, member name overflow, LLM error card bugs
  • Restore Groups Status column + LLM error card delayed swap
  • Build LLM error card client-side, show project last_synced_at, fix worker MissingGreenlet
  • Per-project contributors, inline SVG icons, toast-delayed error card, dismiss empty state
  • Error card delay 6s, per-project avg score, remove Active badge, single-MR retry
  • Real per-project avg score, error card without toast, better error classification
  • Role-based error messages + classify MissingGreenlet as transient
  • MissingGreenlet root cause + role-based messages + MR links in analysis
  • LLM error classifier order, global progress bar deferral, per-project status badges
  • Replace en dash with hyphen in llm_job comment (ruff RUF003)
  • Use forceCloseModal after successful import to avoid spurious Discard confirm
  • Lint errors (ruff/mypy) + UI consistency for feedback links
  • Cast UUID to text in feedback-links join to fix 503 error
  • Add for attribute to form picker label (a11y)
  • Fix Form picker dropdown not opening
  • Align tokens and modals 1:1 with GitLab Pajamas
  • Weekly deadlines table uses full width, no wasted space
  • Align table th, toggle token, pagination padding to GitLab Pajamas
  • Pixel-perfect GitLab Pajamas 1:1 alignment (round 5)
  • Align backgrounds and borders with GitLab dark theme
  • Remove max-width cap on main-container - content fills full page width
  • Pixel-perfect GitLab Pajamas component alignment
  • Align status dots, pagination radius, table padding, card/modal footer to GitLab
  • Project links to internal pages, arena JSON API with identities
  • Even column distribution across all tables (colgroups + table-layout:fixed)
  • Ruff lint/format errors in arena_email (UP037, E501, N806)
  • Show empty-state message when filter pills yield 0 results
  • Hide pagination when filter yields 0 rows, clean empty-state styling
  • Add !important to .d-none utility to override .pagination-controls display:flex
  • Prevent scrollbar flash on table pages by deferring page-initialised with double-rAF and hiding pagination-controls pre-init
  • Suppress row animation and lock container height during filter/sort to prevent scrollbar flash
  • Add missing column width classes for compliance checks table and fix weekly deadlines layout
  • Improve weekly deadlines table layout and prevent modal scrollbar flash
  • Unify scrollbar styling across modals, drawers, tables to match main content
  • Switch checksTable to auto layout on mobile to prevent column overflow
  • Hide Active/Code columns on small mobile screens, constrain Actions column width
  • Wire per-user token auth, fix ruff format, enable event pipeline
  • Resolve ruff lint/format failures, rename remaining instructor refs in i18n, fixtures and docs
  • Move Resurrect button right, fix duplicate toasts, let pagination stretch
  • Move Resurrect Selected to card header top-right, footer only pagination
  • Move pagination inside card body, hide in empty state
  • Add table-empty-row class to all empty/loading/error rows, hide pagination in empty state
  • Use table-empty-state with icon for empty DLQ, matching courses/users pages
  • Map event.replay/resurrect/processed/failed in activity feed with icons, labels, details, URLs
  • Align Configuration Preview icon, expose currentUsername to Alpine, fix modal close animation
  • Configuration Preview spacing, add fg855ni to ZSI config, fix DLQ Type column wrapping
  • Sync teacher_usernames from config import to CourseMembership

Zmenené

  • Unified premium animation system - staggered entrance, no conflicts
  • Zero-lag PTR - all passive listeners + rAF batching
  • Audit fixes - deduplicate afterSwap handler, fix deploy script PS5.1 compat
  • Comprehensive best-practice audit
  • Audit: full system refactor - security, a11y, i18n, dedup, dead code
  • Deep rename feedback->support across entire codebase
  • Redesign: pixel-perfect GitHub Support UI with markdown editor, thread view, expanded categories
  • GitHub-identical support UI - reply on top, reverse chronological comments, remove thread connectors
  • Unify table styles and replace inline styles with CSS classes
  • Ticket detail page - use page_header, remove custom action bar
  • Simplify forms, fix dropdown, sortable table
  • Rename Feedback subsystem to Support/Ticketing system
  • Revert: undo UI changes
  • Revert: restore to d03fd4f (align tokens and modals 1:1 with GitLab Pajamas)
  • Unify role system (instructor->teacher), drop instructor_usernames column, R13 push enqueue
  • Eliminate all 'instructor' terminology - rename to 'teacher' everywhere

UI / UX

  • Align UI system to GitLab Pajamas design tokens
  • Align remaining components to GitLab Pajamas
  • Revert: restore main-container max-width layout
  • Pixel-perfect GitLab Pajamas alignment - focus ring 1px/3px, form padding 8px/12px, line-height 1rem, triple-ring focus, card radius 8px, modal radius 12px, table cell 12px/16px

Infraštruktúra

  • Remove test support script
  • Remove temp verify_fix script
  • Remove junk files and harden .gitignore
  • Remove debug scripts
  • Remove utility scripts with hardcoded credentials

Dokumentácia

  • Comprehensive update - R13, 30 migrations, 2382 tests, CourseMembership

Testy

  • Add unit tests for email notifications, api utils, constants, and type modules

[2026-03-23 - 2026-03-29]

Pridané

  • Clickable member rows + context-aware back navigation
  • Group members table navigates to project context
  • Role separation - force logout on change, hide docs from students, fix attention items
  • Suggest full sync after course settings save
  • Redesign instructors page (GitLab-style invite bar + member rows)
  • Real GitLab avatars + wider layout
  • Clickable instructor rows with User Info modal, Owner badge
  • GitLab-style Source & Role columns in member tables
  • GitLab reference linkification + reduce member-api-note spacing
  • Role-based sidebar + thesis feedback card
  • Add R13 Commit Quality check - web editor detection
  • Simplify: remove intervention list, risk groups, all groups; disable event pipeline/roster; add instructors lazy loading
  • Redesign dashboard cards, header, timeline grid, profile menu
  • Quick Access sidebar, health for teacher, scheduler feature-flag gating
  • Single-user enrich button in User Info modal
  • Comprehensive UX improvements from teacher review
  • Add 295 missing Slovak translations
  • Overhaul global search - students scope, role badges, direct links, docs
  • Drastically reduce GitLab API polling - webhook-first approach
  • Extend activity-window filter to ALL remaining GitLab polling jobs
  • Webhook edge-case hardening - orphan projects, backpressure, immediate enrichment, delivery health
  • System-wide resilience - sweep retry, pagination limit, Prometheus enrichment metrics
  • Add feature_auto_sync toggle to gate all GitLab-polling sweep jobs

Opravené

  • Member profile - dynamic role title, no-activity guard, table scroll
  • Member profile UX - back nav to group, instructor-aware empty states
  • Prevent infinite login loop when allowlist user changes role
  • Dashboard: fix status dots, reporter role warning, pipeline health overflow
  • Fix CI lint + Groups 0: match Student by gitlab_user_id
  • Fix OAuth: GitLabUser.id not gitlab_user_id
  • Elevate dashboard role for course owners/instructors, remove redundant stat tooltip
  • Rewrite modal CSS to match GitLab Pajamas exactly
  • Silence editCheck console warning and aria-hidden focus conflict
  • Quick Access shows real teams, System health enriched
  • Quick Access Groups/Subgroups tabs, interactive timeline weeks, skeleton blue bar fix
  • Lint errors (ruff/mypy), timeline semester_weeks order, project name display
  • Sidebar layout, project names, timeline nav, telemetry gating
  • Round avatars, attention link to course, instructor layout
  • Avatar backgrounds hardcoded, groups sort by RED, instructor panels stretch
  • Revert email, muted avatar colors 24px, restore email docs
  • Language switch revert, new analytics favicon, DTA refs cleanup
  • Cache-bust extra.js/css/favicon, reduce nginx cache, remove dead locale persistence
  • Fix language revert, favicon flash, and canonical URLs
  • Restore missing