Naprawy WCAG 2.2 AA stwierdzone lekturą kodu (1.1.1, 3.1.1, 3.1.2) - #732
Open
mpasternak wants to merge 22 commits into
Open
Naprawy WCAG 2.2 AA stwierdzone lekturą kodu (1.1.1, 3.1.1, 3.1.2)#732mpasternak wants to merge 22 commits into
mpasternak wants to merge 22 commits into
Conversation
Wydziela z programu WCAG 2.2 AA (spec z 2026-08-05) te naprawy, które nie zależą od skanu szerokiego, audytu ani infrastruktury testowej: 1.1.1 (obraz bez alt, martwy szablon) oraz 3.1.2 (atrybuty lang na tytułach obcojęzycznych, oba wektory). Dokument koryguje dwa ustalenia poprzednika: - wektor 2 (opis_bibliograficzny_cache) NIE wymaga liveops per wdrożenie — nocny denorm_rebuild z Ofelii przelicza całą bazę niezależnie od tej zmiany, a szablon opisu wrócił na dysk migracją 0473. Rzeczywistym warunkiem koniecznym jest rozszerzenie allowlisty nh3 o span/lang, którego poprzednik nie wymieniał; - user_navigation_autocomplete.html jest martwy (widok autocomplete zwraca JSON, listę rysuje Select2) — usuwamy plik zamiast łatać alt. Bramka CI, baseline, audyt ręczny i raport zgodności pozostają poza zakresem do czasu pojawienia się odbiorcy raportu. 2.1.4 (skrót /) i 2.5.7 (graf powiązań) odroczone świadomie, z wpisem w wykazie niezgodności. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
Recenzja podważyła cztery ustalenia pierwszej wersji; wszystkie zweryfikowane w kodzie i przyjęte. - browse/praca_tabela.html NIE jest stroną szczegółów, tylko alternatywnym szablonem opisu bibliograficznego (instalowany przez migrację 0295 obok opis_bibliograficzny.html; praca.html:54 włącza wyłącznie wariant _mono). Przechodzi więc ścieżką wektora 2, nie 1. Pierwsza wersja popełniła dokładnie ten błąd, który wytykała poprzednikowi: uznała plik za żywy widok bez sprawdzenia, kto go renderuje. - Argument, że założenie o semantyce jezyk_alt jest "bezpieczne", był fałszywy: przy WYPEŁNIONYM polu o innym znaczeniu tytuł dostałby BŁĘDNY lang, co dokument sam nazywa gorszym niż brak. Zawężamy do tytułu oryginalnego (jezyk); przekład trafia do wykazu jako niezgodność częściowa. Fakt osłabiający hipotezę: eksport CERIF nie używa jezyk_alt do oznaczania tytułów. - Tabela konsumentów opisu miała 5 pozycji zamiast ~15 i błędnie etykietowała eksport oświadczeń jako PDF/weasyprint (to XLSX z surowym HTML w komórce). Uzupełniona i podzielona wg mechanizmu. - Wyszukiwanie po podłańcuchu w surowym HTML występuje w czterech miejscach, nie jednym — w tym w PUBLICZNEJ wyszukiwarce rekordów powiązanych (praca_tabela_mono.html:676,914), czyli w zakresie audytu. Gałąź w list_views.py to elif, aktywny tylko dla rekordów bez tytul_oryginalny — test musi ten warunek odtworzyć. Drobne: denorm nie zależy od bpp.Jezyk (uzupełnienie kod_bcp47 nie brudzi cache), zastrzeżenie dla wdrożeń bez harmonogramu Ofelii, wpis w wykazie dla instalacji z własnym szablonem opisu, testy breadcrumbu ograniczone do przypadków faktycznie tam występujących. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
Autor modelu potwierdza: jezyk_alt to odwzorowanie atrybutu z API PBN (pbn.nauka.gov.pl/api/v1/), skopiowane "na zapas", oznaczajace drugi jezyk PRACY (publikacja dwujezyczna) — nie jezyk tytulu przelozonego. Potwierdzenia w kodzie: adapter PBN wysyla mainLanguage z jezyk i originalLanguage z jezyk_orig, a jezyk_alt mimo pochodzenia z PBN nie jest tam eksportowane; zaden importer go nie wypelnia (wchodzi wylacznie recznie z admina); CERIF oznacza tytuly przez jezyk i dodatkowe_tytuly.jezyk. Decyzja bez zmian (oznaczamy tylko tytul_oryginalny), ale uzasadnienie sie zmienia: to nie ostroznosc wobec niepewnej semantyki, tylko brak danych. Zmienia to tez charakter wpisu w wykazie — domkniecie 3.1.2 dla przekladu wymaga NOWEGO pola w modelu (migracja + uzupelnianie danych przez uczelnie), a nie wykorzystania istniejacego. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
Dziewiec zadan w kolejnosci zaleznosci: filtr oznacz_jezyk → allowlista nh3
→ wektor 1 (strony) → wektor 2 (generator opisu) → naprawa podswietlania →
testy substring-search → 1.1.1 → korekty spec i newsfragmenty → weryfikacja.
Dwa ustalenia dopisane do specyfikacji przy rozpisywaniu planu:
- Podswietlanie w wyszukiwarce rekordow powiazanych
(praca_tabela_mono.html:1140-1200) wymaga NAPRAWY, nie tylko testu.
Regex podswietla na surowym HTML-u; dzis blob ma tylko <b>/<i>, wiec
problem jest utajony, ale <span lang="en"> wnosi podciagi "en", "an",
"lang" — pospolite w tytulach. Wpisanie "en" wstawia <mark> w srodek
atrybutu i psuje markup przy .html(). Logika podswietlania zostaje
wyekstrahowana do modulu JS (testowalnego vitestem) i poprawiona tak,
by omijac zawartosc znacznikow.
- 504.html:2 deklaruje <html lang="en"> przy polskiej tresci — WCAG 3.1.1
(poziom A), czytnik odczytuje CALA strone angielska fonetyka. Poprawka
jednowyrazowa, w pliku i tak edytowanym w tej iteracji. Jedyny szablon
z wlasnym <html lang>; reszta dziedziczy po base.html.
Plan zweryfikowany wobec kodu: fixture jezyki juz ustawia kod_bcp47
(pl/en), _get_record_title czyta opis w gałęzi elif, praca_tabela_mono.html
nie ma bloku extra_js ani {% load static %}, style .praca-mono__related-*
siedza w praca_detail.scss:1001.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
Pre-flight scan przed wykonaniem planu: parametryzowany test asertowal 'fraza in OPIS_ZE_ZNACZNIKIEM.lower()', czyli sprawdzal wlasna stala, nie kod BPP. Recenzent slusznie zglosilby to jako test bez wartosci. Zastapione dwoma testami wykonujacymi realny kod: _get_record_title w obu galeziach (tytul / opis) oraz przebieg widoku listy kolejki PBN z parametrem wyszukiwania na rekordzie bez tytulu oryginalnego. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Migracja 0295 zainstalowala browse/praca_tabela.html do dbtemplates jako drugi wariant generatora opisu. Migracja 0473 sprzatala dbtemplates tylko dla nazw wskazywanych przez SzablonDlaOpisuBibliograficznego, a ta nie byla wskazywana — wiersz przetrwal jako sierota. Loader dbtemplates stoi przed plikowym, wiec dopoki wiersz istnieje, kazda edycja tego szablonu na dysku (w tym znacznik lang z WCAG 3.1.2) nie ma zadnego efektu na instalacji, ktora przelaczy sie na ten wariant. Blad jest cichy: bez wyjatku, bez ostrzezenia, ze stara trescia sprzed lat. Kasowanie przez usun_dbtemplate_i_przebuduj — ten sam mechanizm co 0473, z guardem dysk-existence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ata-records Recenzja Task 5 wykazala dwa braki: 1. Regex segmentujacy rozpoznawal wylacznie znaczniki <tag>, wiec sekwencje encji HTML (np. podwojnie zescape'owany '&lt;' powstajacy z _escape_bare_angle_brackets + escapowania ampersandu w data-records) byly traktowane jak zwykly tekst. Fraza 'amp' albo 'lt' (pospolita w tekstach medycznych) rozbijala encje identycznie jak wczesniej span[lang]. Regex rozszerzony o trzecia alternatywe dla lancuchow encji (&(?:[#a-zA-Z0-9]+;)+), sprawdzana przed galezia tekstowa. 2. Test Pythona asertowal 'lang=' na calej stronie, co przechodzilo nawet gdyby znacznik trafil tylko do widocznego linku, a nie do data-records. Test teraz parsuje HTML przez lxml, wyciaga wartosc atrybutu data-records i dekoduje ja jak json.loads (dokladnie tak, jak robi to JS przez JSON.parse po stronie klienta, odwracajac escapowanie znakow przez filtr escapejs), zeby dowiesc, ze znacznik jezyka dotarl do konkretnego atrybutu, na ktorym operuje wyszukiwarka. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rozszerzono o korektę sekcji "Rollout wektora 2" w spec 08-06: wyjątek dla osieroconego dbtemplate browse/praca_tabela.html (migracja 0488). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Recenzja Task 8 wykazala, ze wstawione korekty (wektor 2 wykonany, martwy szablon usuniety) stoja obok oryginalnych, juz nieaktualnych twierdzen w tym samym dokumencie - klasyczna sprzecznosc faktograficzna. - Otwarte decyzje / Zakres 3.1.2: dopisano ze rekomendacja odroczenia wektora 2 nie zostala przyjeta. - Ryzyka / 3.1.2 moze nie zamknac sie w tej iteracji: dopisano ze ryzyko sie nie zmaterializowalo. - 3.1.2 Language of Parts: dopisano trzecia korekte przy zdaniu ktore wciaz przedstawialo domkniecie wektora 2 jako otwarte. - Kolejnosc prac i zaleznosci, krok 3: dopisano ze krok objal tez wektor 2, a 2.1.4/2.5.7 zostaly odroczone, nie naprawione. - Punkt wyjscia: dopisano ze user_navigation_autocomplete.html byl martwy i zostal usuniety - w zakresie zostaje jeden obraz, nie dwa. - Poprawiono bledny numer linii browse/praca.html:54 -> :55 w obu dokumentach (08-05 i zrodlowym 08-06). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ione)
Re-recenzja Task 8 znalazla te sama klase sprzecznosci jak poprzednio,
tym razem dla 2.1.4 (skrot /) i 2.5.7 (graf powiazan) - oba swiadomie
odroczone w tej iteracji, ale trzy miejsca dokumentu 08-05 nadal
przedstawialy je jako naprawione albo zmierzajace do naprawy.
- Tabela "Przypisanie 55 kryteriow": nota pod tabela Funkcjonalnosc
przy wierszach 2.1.4 i 2.5.7 ("naprawa stwierdzona" bylo faszywe);
wiersz 3.1.2 zostawiony bez zmian - tam naprawa faktycznie zaszla.
- Naglowek sekcji "Naruszenia stwierdzone": korekta rozgraniczajaca
naprawione (1.1.1, 3.1.2) od odroczonych (2.1.4, 2.5.7), plus dopiski
przy samych podsekcjach 2.1.4 i 2.5.7 (jedyne bez adnotacji).
- "Otwarte decyzje" / Skrot / (2.1.4): korekta ze rekomendacja
zawezenia do focusa nie zostala przyjeta, skrot dziala bez zmian.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naprawiliśmy PODŚWIETLANIE w wyszukiwarce rekordów powiązanych, ale FILTROWANIE zostało nietknięte — record.text.indexOf(fraza) na surowym HTML dopasowywał KAŻDY rekord z <span lang="…"> do frazy "span"/"lang", bez żadnego podświetlenia na liście (regresja tej gałęzi, publiczna strona szczegółów). - related-records-highlight.js: drugi eksport bppStripTags(html), ten sam tokenizer co podświetlanie, zwraca tekst bez znaczników z zdekodowanymi encjami (& < > " ' + numeryczne). - praca_tabela_mono.html: filtr dopasowuje teraz bppStripTags(record.text), nie surowy record.text. - Efekt uboczny na plus: znika też zastany przypadek frazy przeciętej znacznikiem (dawniej niedopasowywanej przez indexOf na HTML-u). - tests/js: 26 testów (było 17) — bppStripTags, filtr end-to-end, test kontrastowy dla frazy przeciętej znacznikiem. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-M6, M10) Reszta znalezisk z całościowej recenzji gałęzi (poza I1, osobny commit): I2 — testy okruszka nawigacyjnego celowały w cały res.content, nie w węzeł okruszka; przechodziły niezależnie od tego, czy filtr lang tam w ogóle działał. Teraz lxml.html + xpath na //li[@Class="current"]. I3 — dopisano wpis do wykazu odroczonych niezgodności (oba dokumenty spec) o override'ach opis_bibliograficzny.html w dbtemplates: loader dbtemplates stoi przed plikowym, admin zachęca do edycji tego konkretnego szablonu, a migracja 0488 czyści wyłącznie browse/praca_tabela.html. Plus zdanie w newsfragmencie. M1 — usunięto martwe (post-0488) drop_dbtemplate() i nieprawdziwy komentarz w test_lang_opis.py. M2 — util.py: dopisano .replace(".</span></b>[", ".</span></b> [") obok istniejącego wariantu bez <span>; poprawiono komentarz testu, który opisywał nieistniejące twierdzenie. M3 — usunięto martwy lokalny import format_html w autor_nazwa() (templatetags/prace.py) — modułowy import już to pokrywa. M4 — dopisano test na action_views._get_ids_matching_title (ten sam wzorzec fraza-w-HTML co list_views, dotąd bez testu). M5 — dopisano asercję, że related-records-highlight.js jest faktycznie podpięty do strony (script src). M6 — test_mono_bez_atrybutu_gdy_kod_pusty: dodano asercję wykluczającą gołe <span> bez atrybutu lang. M10 — dopisano w spec 2026-08-05 wzmiankę o poprawce lang="en" -> lang="pl" w 504.html przy korekcie 1.1.1 (kryterium 3.1.1, poza pierwotną inwentaryzacją). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment on lines
+65
to
+76
| return html.replace( | ||
| TAG_LUB_ENCJA_LUB_TEKST, | ||
| function (_, znacznik, encja, tekst) { | ||
| if (znacznik || encja) { | ||
| return znacznik || encja; | ||
| } | ||
| return tekst.replace( | ||
| regex, | ||
| '<mark class="bpp-highlight">$1</mark>' | ||
| ); | ||
| } | ||
| ); |
Comment on lines
+135
to
+145
| html.replace(TAG_LUB_ENCJA_LUB_TEKST, function (_, znacznik, encja, tekst) { | ||
| if (znacznik) { | ||
| return ""; | ||
| } | ||
| if (encja) { | ||
| segmenty.push(decodeEncje(encja)); | ||
| return ""; | ||
| } | ||
| segmenty.push(tekst); | ||
| return ""; | ||
| }); |
…ds-highlight.js Alerty CodeQL js/incomplete-multi-character-sanitization (155, 156) w bppHighlightOutsideTags i bppStripTags przeanalizowane i uznane za falszywie dodatnie — pelne uzasadnienie w .superpowers/sdd/2026-08-06-wcag-naprawy-stwierdzone/codeql-155-156-report.md (nietrackowane, .gitignore). - bppHighlightOutsideTags: jedyne nowe '<' w wyniku to staly literal <mark>/</mark>; znaczniki/encje wejscia przechodza bez zmian, wiec <script> nie moze powstac inaczej niz z wejscia (a wejscie jest juz przepuszczone przez nh3.clean po stronie serwera). - bppStripTags: dekodowanie MOZE odtworzyc tekst '<script>' z podwojnie zescape'owanej encji (test to potwierdza), ale wynik ma jedyne miejsce uzycia w calym repo (praca_tabela_mono.html:1185, .indexOf() w filtrze) — nigdy nie trafia do DOM. Dopisano komentarze przy obu funkcjach + 5 testow vitest dokumentujacych oba zalozenia bezpieczenstwa. Logika funkcji bez zmian. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Naprawy naruszeń WCAG 2.2 AA, które specyfikacja z 2026-08-05 stwierdziła na podstawie lektury kodu — bez czekania na audyt, skan szeroki ani infrastrukturę testową. To krok 3 z jej sekcji „Kolejność prac i zależności", wykonany jako samodzielna iteracja.
Specyfikacja tej iteracji:
2026-08-06-wcag-naprawy-stwierdzone-design.mdCo naprawia
langna tytułach oryginalnych publikacji — w szablonach stron szczegółów oraz w generatorze opisu bibliograficznegoalt=""na ikonie dekoracyjnej w504.html; usunięcie martwego szablonuuser_navigation_autocomplete.html<html lang="pl">zamiastlang="en"w504.html— treść strony jest polska, więc czytnik odczytywał ją całą angielską fonetykąBez
langczytnik ekranu odczytuje angielski tytuł polską fonetyką. Dane były już w modelu (Jezyk.kod_bcp47, dodane pod eksport CERIF) — brakowało wyłącznie wykorzystania ich w warstwie prezentacji.Dwie warstwy, dwa czasy działania
Szablony stron szczegółów — efekt natychmiast po wdrożeniu, bo renderują się z pól modelu przy każdym żądaniu.
Generator opisu bibliograficznego — opis jest cache'owany w bazie jako gotowy HTML i używany w 31 miejscach (listy, wyniki multiseek, wydawnictwa powiązane, REST API). Znacznik musi trafić do środka tego blobu, więc wstawia go generator. Istniejące wiersze złapią go przy najbliższym nocnym
denorm_rebuild(22:00 z harmonogramu Ofelii) — bez ręcznej interwencji i bez okna serwisowego ponad to, które już istnieje.Trzy rzeczy, które wyszły w trakcie
Sanityzator wyciąłby znacznik.
safe_opis_bibliograficzny_htmlprzepuszcza opis przez nh3 z wąską allowlistą, w którejspannie był dozwolony, a atrybuty miał wyłącznie<a>. Bez rozszerzenia listy ospan[lang]poprawka generatora cicho by nie zadziałała — testy szablonu przechodziłyby, a opis w bazie byłby bez atrybutu. Rozszerzenie jest wąskie (langnie wykonuje kodu,spanbezstyle/classnie pozwala nadpisać wyglądu) i ma osobny test regresji bezpieczeństwa.Osierocony wiersz
dbtemplates(migracja0488). Migracja 0295 zainstalowałabrowse/praca_tabela.htmldo bazy jako drugi wariant generatora opisu; migracja 0473 sprzątała dbtemplates, ale tylko dla nazw wskazywanych przezSzablonDlaOpisuBibliograficznego.nazwa_szablonu— ta nie była wskazywana, więc wiersz przetrwał. Loader dbtemplates stoi przed plikowym, więc edycja tego szablonu na dysku nie miałaby efektu na wdrożeniu, które by się na niego przełączyło. Błąd byłby cichy: bez wyjątku, ze starą treścią sprzed lat.Regresja w wyszukiwarce rekordów powiązanych. Filtr i podświetlanie działały na surowym HTML. Po dodaniu
<span lang="en">frazaspan/langdopasowywała każdy rekord z tytułem obcojęzycznym, a podświetlanie wstawiało<mark>w środek atrybutu. Logika została wydzielona do modułurelated-records-highlight.js(tokenizer dzielący na znaczniki/encje/tekst), przetestowana jednostkowo i naprawiona w obie strony.Świadomie odroczone
Wpisy z uzasadnieniem i datą w sekcji „Odroczone niezgodności" obu specyfikacji:
/wiązany nadocument. Wszystkie trzy dopuszczone przez kryterium wyjścia mają koszt produktowy; brak nacisku regulacyjnego i odbiorcy raportu.jezyk_altto odwzorowanie atrybutu z API PBN oznaczające drugi język pracy, nie język przekładu; użycie go dałoby oznaczenie błędne, a to gorsze niż brak.kod_bcp47w słowniku języków, własny szablon opisu, overrideOPIS_BIBLIOGRAFICZNY_ALLOWED_TAGS, oraz wierszopis_bibliograficzny.htmlzapisany wdbtemplates(admin wprost zachęca do jego edycji, a loader bazy ma pierwszeństwo — wtedy znacznik nie dotrze mimo poprawnego kodu).Weryfikacja
Lokalnie, na finalnym drzewie:
make tests-without-playwright→ 9350 passed, 4 skipped, 1 xfailedmake js-tests→ 107 passedmake tests-only-playwright→ 157 passed, 1 skippedpre-commit(bez argumentów) → wszystkie hooki PassedDryf zgłaszany przez
makemigrations --checkdotyczy trzech pakietów zewnętrznych (favicon, flexible_reports, siteblog) — identycznie na czystymdev, więc zastany, nie wprowadzony tą gałęzią.Znane drobiazgi do follow-upu
<i>): rekord trafia na listę bez podświetlenia. Rzadkie; nie odtwarza naprawionej regresji.test_znacznik_przezywa_post_processingnie ćwiczy gałęzi.replace(".</span></b>[", …)— scenariusz nie ma autora z typem odpowiedzialności. Poprawka zweryfikowana ręcznie, brak pokrycia regresyjnego.Uwaga przy scalaniu
baseline-sql/baseline.sqlbył regenerowany na Apple Silicon (postgres_versionzmieniax86_64→aarch64) i dociągnął niezwiązany dryfformdefaults(15 wierszy, dane domyślne). Zgodnie z regułą zCLAUDE.mdwarto odświeżyć baseline raz, przy scalaniu, najlepiej przez workflowrefresh-baseline.yml.🤖 Generated with Claude Code