Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,10 @@
## 2026-08-04 - Status Semantics and Numeric Alignment for Reports
**Learning:** Explicit status semantics can make conditionally rendered empty states easier to discover with assistive technology, while tabular numerals improve visual comparison of metric columns. Focus-reveal behavior must not depend only on `:focus-visible`, and hover styling must not reduce the contrast of unrelated rows.
**Action:** Use `role="status"` for genuine conditionally rendered status messages, apply `font-variant-numeric: tabular-nums` to numeric report tables, reveal skip links on `:focus`, retain a visible `:focus-visible` indicator, and avoid opacity-based dimming of non-hovered content.
## 2026-08-05 - Fail-Safe Pointer Focus Outline Suppression
**Learning:** Globally suppressing focus outlines for mouse users with `:focus { outline: none; }` destroys keyboard accessibility on user agents that do not support `:focus-visible`. A fail-safe selector approach uses `:focus:not(:focus-visible)` to explicitly target non-keyboard interaction, preserving the user agent default when `:focus-visible` is unsupported. Pointer-only ring suppression is a visual polish feature, not a complete solution for whole-product WCAG conformance; pointer users may still benefit from focus indication.
**Action:** When suppressing pointer focus rings, always use `:focus:not(:focus-visible) { outline: none; }`.
References:
- W3C. (2025). *Understanding Success Criterion 2.4.7: Focus Visible*.
- W3C. (2025). *C45: Using CSS :focus-visible to provide keyboard focus indication*.
- W3C. (2025). *Selectors Level 4*.
47 changes: 26 additions & 21 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,38 @@
<!-- BEGIN AUTHORITATIVE CHANGELOG FRAGMENTS -->
### Changed

#### Release cut 0.8.0

- Project version is bumped to 0.8.0 in `pyproject.toml`, `crates/mlsirm-core`,
and `crates/fast-mlsirm-py`. The accumulated `Unreleased` notes now form the
`[0.8.0] - 2026-08-17` release section: governed contract additions
(multilevel/multiple-membership/longitudinal design, structural
model-relation and leakage-safe model-validation units, post-pilot
item-bank lifecycle, RAG scoring/perturbation-anchor/facets-calibration
adapters, enterprise issue-intelligence observation/calibration/reporting
contracts, essay facets/score/validation HTML reports, paired rating-range
and essay-facets synthetic recovery evidence), a broad Rust-ownership
hardening sweep across dozens of public entry points (CAT, ATA, DIF,
equating, scaling, reliability, multilevel, response-time, fit-statistics,
inference, linking, LLM-judge orchestration, parallel-analysis, plausible
values, Rasch-CML, model-comparison, and rotation/loader concurrency) that
reject hostile Python callback/conversion-protocol inputs before native
dispatch, and accessibility/documentation polish (exact-value tooltips,
tabular numerals, print styles, row headers, architecture baseline,
Python 3.14 CI).
- Released authoritative fragments are removed from `docs/changelog.d`; the
directory again holds only genuinely unreleased notes.
#### Harden remaining equating controls before native discovery

- Validate circle-arc method/point/scalar controls, nominal-weights score ceilings and synthetic-population weight, and the composite-linking exponent before compiled-core discovery.
- Reject caller-defined scalar/container subclasses and arbitrary conversion providers without executing their conversion, comparison, representation, hashing, or iteration callbacks.
- Preserve exact built-in and genuine NumPy scalar compatibility while keeping circle-arc geometry, nominal-weights moments, composite-linking weight arithmetic, and all result-affecting equating mathematics in Rust.

### Fixed

#### Fail closed on unsafe multilevel contextual effects

- Multilevel contextual-effect evaluation now fails closed when any referenced context random-effect value is NaN or infinite and when finite inputs overflow the weighted sum, preventing non-finite predictor results from escaping the Rust boundary while leaving unreferenced table capacity outside sparse validation work.
- Python context-effect marshalling snapshots each required mapping value once without caller-defined membership probes and normalizes hostile lookup callbacks to non-reflective package errors before native dispatch.

#### Workflow-registry audit transport retry hardening

- Expanded the read-only Actions-registry audit transport's bounded retry classifier to cover transient HTTP 403, 404, 429, and all 5xx responses, while preserving fail-closed exhaustion and immediate failure for non-transient authentication errors such as HTTP 401.
- Added direct transport regression coverage so incident audits do not misclassify one transient GitHub control-plane response as a completed inventory failure.

### Security

#### Parallel-analysis control trust hardening

- Validate `n_iterations`, `centile`, and `seed` before native-core discovery, accepting only exact built-in integers and supported concrete NumPy integer scalars while rejecting booleans, `np.bool_`, caller-defined subclasses, and conversion providers without executing their callbacks. Workspace and `u64` seed limits fail at the same pre-discovery boundary.
- Normalize nonnumeric `data` conversion failures to a package-owned `ValueError` before native-core discovery while preserving dimensionality and workspace validation for successfully converted arrays.
- Preserve the existing positive-iteration, centile `0..99`, Rust `u64` seed, and 128 MiB random-benchmark workspace limits without changing Rust-owned Horn/Glorfeld factor-retention arithmetic.
<!-- END AUTHORITATIVE CHANGELOG FRAGMENTS -->
## [0.8.0] - 2026-08-17


### Fixed

- 단독 실행 HTML 리포트의 focus 표시기에서 마우스 클릭 시 기본 브라우저 윤곽선이 나타나는 문제를 방지하기 위해, fail-safe CSS 선택자 `:focus:not(:focus-visible)`를 적용했습니다.
- Public spatial/marginal MMLE now wraps a version-matched Rust `fit_marginal` keyword `TypeError` as the package-owned ABI `RuntimeError`, so a stale native signature cannot leak past `MARGINAL_CAPABILITY_VERSION = 1` or fall back to NumPy production arithmetic.
- Public multigroup M2 fails closed without the compiled Rust core and delegates target/null projected quadratic forms to native `projected_m2`.
- Public conditional-Rasch M2 fails closed without the compiled Rust core and delegates every result field to the native `m2_cmle_rasch_stat` entrypoint.
Expand Down
23 changes: 0 additions & 23 deletions docs/changelog.d/release-0.8.0-cut.md

This file was deleted.

9 changes: 7 additions & 2 deletions python/fast_mlsirm/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,7 @@ def _css() -> str:
border-radius: 8px;
}

.table-wrap:focus {
.table-wrap:focus:not(:focus-visible) {
outline: none;
}

Expand Down Expand Up @@ -925,6 +925,11 @@ def _css() -> str:
color: var(--teal);
}

.exact-values > summary:focus:not(:focus-visible),
.export-block > summary:focus:not(:focus-visible) {
outline: none;
}

.exact-values > summary:focus-visible,
.export-block > summary:focus-visible {
outline: 3px solid var(--teal);
Expand All @@ -951,7 +956,7 @@ def _css() -> str:
white-space: pre;
}

.export-block pre:focus {
.export-block pre:focus:not(:focus-visible) {
outline: none;
}

Expand Down
2 changes: 1 addition & 1 deletion tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def test_render_table_region_has_keyboard_focus_style(tmp_path):
assert 'aria-label="Candidate Comparison diagnostics table"' in html
assert 'tabindex="0"' in html
assert ".table-wrap:focus-visible" in html
assert ".table-wrap:focus {" in html
assert ".table-wrap:focus:not(:focus-visible) {" in html
assert "tbody tr:hover" in html
assert '<div class="bar-chart" aria-hidden="true">' in html
assert '<div class="bar-track" aria-hidden="true">' in html
19 changes: 16 additions & 3 deletions tests/test_report_focus_contrast.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,20 @@ def test_hover_does_not_dim_unrelated_chart_or_table_content(tmp_path: Path) ->


def test_focus_containers_suppress_mouse_click_outlines(tmp_path: Path) -> None:
"""Semantic focus containers must suppress mouse click outlines."""
"""Semantic focus containers must suppress mouse click outlines without breaking fallbacks."""
html = _render_report(tmp_path)
assert ".table-wrap:focus {\n outline: none;\n}" in html
assert ".export-block pre:focus {\n outline: none;\n}" in html

# Reject bare :focus + outline: none
assert ".table-wrap:focus {" not in html
assert "pre:focus {" not in html
assert "summary:focus {" not in html

# Require explicit :focus:not(:focus-visible) fail-safe suppression
assert ".table-wrap:focus:not(:focus-visible) {\n outline: none;\n}" in html
assert ".export-block pre:focus:not(:focus-visible) {\n outline: none;\n}" in html
assert ".exact-values > summary:focus:not(:focus-visible),\n.export-block > summary:focus:not(:focus-visible) {\n outline: none;\n}" in html

# Require focus-visible 3px indicators
assert ".table-wrap:focus-visible {\n outline: 3px solid var(--teal);\n outline-offset: 3px;\n}" in html
assert ".export-block pre:focus-visible {\n outline: 3px solid var(--teal);\n outline-offset: -2px;\n}" in html
assert ".exact-values > summary:focus-visible,\n.export-block > summary:focus-visible {\n outline: 3px solid var(--teal);\n outline-offset: 2px;\n}" in html
Loading