This is a pre-release. djust 1.0.0 has shipped since: read the djust 1.0.0 release notes.
Before you upgrade, read the upgrade guide.
Tests
- Theming register/get roundtrip meta-invariant (#1597) — structural prevention for the #1595 bug class. PR #1596 fixed
presets.get_preset()to consult the runtime registry first (the #1595 bug); this PR adds the meta-invariant test that catches the bug class — any futureregister_X()API indjust.theming.registrywhose matching module-levelget_X()ignores the registry. New filepython/djust/tests/test_theming_register_get_roundtrip_invariant.pycarries an authoritativeROUNDTRIP_PAIRStable covering the 3 current pairs (register_preset → presets.get_preset,register_design_system → theme_packs.get_design_system,register_theme_pack → theme_packs.get_theme_pack); a parametrized test asserts the roundtrip works for each (register sentinel → call module-level getter → assert identity); a secondtest_no_unaudited_register_apis_1597AUDIT test grep-walksdjust.theming.registry's public surface and fails loud if anyregister_*function exists without a row inROUNDTRIP_PAIRS— so a new register API can't ship without either coverage or an explicit decision to opt it out. Failure messages name the specific module/function that broke the contract and pointer-reference the canonical fix pattern (theme_packs.get_theme_pack()atpython/djust/theming/theme_packs.py:1216). Gate-the-fix-off self-test (Action #1200/#1468) passes: revertingget_presetto its pre-#1596 broken shape, exactly 1 of the 4 new test cases fails (theregister_preset→get_presetcase) — the other 3 parametrized cases + the audit case continue to pass, confirming no tautology and that each parametrized case is independently load-bearing. Generalizes PR #1565's "read/write gate symmetry" rule (action-tracker candidate) to the lookup-API-pair class for registry-aware subsystems.
Fixed
- Theming registry/static-dict divergence — runtime-registered presets now reach the CSS generator (#1595).
register_preset()adds to a runtimeRegistry._presetsdict that the theme manager, theme switcher, and introspection APIs all consult; butpresets.get_preset()— the function the CSS generator path ultimately calls to render--primaryetc. into:root— read only from the staticTHEME_PRESETSmodule dict, blind to runtime registration. Result: any consumer following the documentedregister_preset()API inAppConfig.ready()got their custom palette silently replaced with the default slate-blackTHEME_PRESETS["default"]in the actual rendered CSS, while the manager/switcher/gh-pr-checks-style introspection correctly reported the registered preset as active — exactly the kind of API-says-X-but-renderer-uses-Y divergence that costs an hour of debugging. Fix mirrors the registry-first-OR-static-fallback dispatch already established intheme_packs.get_theme_pack()(python/djust/theming/theme_packs.py:1216-1222):get_preset()now consultsget_registry().get_preset(name)first, then falls back toTHEME_PRESETS.get(name, DEFAULT_THEME). Same shape, sameimport .registryinside the function to avoid the circular import that bites if registry imports back from presets. Covered by 3 new regression tests inpython/djust/tests/test_theming_presets.py(test_get_preset_consults_runtime_registry_first_1595+ 2 companions locking in the second half of the contract: static-dict fallback for built-in names +DEFAULT_THEMEfallback for unknown names). Gate-the-fix-off self-test passes (Action #1200/#1468): with the fix reverted, exactly 1 of the 3 new tests fails — the registry-first regression case — and the other 2 pass, confirming no tautology. Removes the need for the documented workaround (_presets.THEME_PRESETS[name] = preset) — consumers can now use the publicregister_preset()API alone.