This is a pre-release. djust 0.9.5 has shipped since: read the djust 0.9.5 release notes.
Added
- New framework helper:
djust.utils.emit_one_shot_class_warning(cls, key, message, *args)(#1392). Reusable pattern for "framework can't help mechanically; tell the developer loudly." Sets a class-level sentinel attr_djust_warned_<key>so subsequent instances of the same class don't repeat the warning. Subclasses get their own sentinel viacls.__dict__.get(avoids attribute inheritance). Refactored the existing snapshot-truncation warning inpython/djust/websocket.pyto use it. Pattern from PR #1326, canonicalized via Retro v0.9.3-2 finding #4.
Changed
Process canon batch: 8 retro-filed items into CLAUDE.md, pipeline templates, and
docs/website/guides/authorization.md(#1345, #1377, #1385, #1386, #1389, #1391, #1393)..pipeline-templates/bugfix-state.jsonStage 4: mandatory checklist item to verify cited cause for retro-filed issues before locking the fix scope (#1345)..pipeline-templates/feature-state.json+bugfix-state.jsonStage 7: mandatory checklist item requiring disconfirming citations during self-review — bias toward active falsification rather than passive confirmation (#1386).CLAUDE.mdBug-report triage section: rule that multi-reopen issues require a bit-exact runnable reproducer against the reporter's environment before "root cause confirmed" (#1389);_framework_attrssnapshot-order invariant note (#1393).CLAUDE.mdProcess Canon: filter-migration grep canon (when changing a filter convention, grep all call sites for the OLD pattern) (#1391); split-foundation soak-time guidance for solo-author case (no external consumers → soak optional) (#1385).python/djust/live_view.py: comment block on_framework_attrs = frozenset(self.__dict__.keys())documenting the BEFORE-snapshot vs AFTER-snapshot semantics (#1393).docs/website/guides/authorization.md: WS-communicator test pattern section showing how to test the per-event object-permission re-execution path (#1377).
scripts/check-test-coverage.pynow verifies Makefile andpyproject.tomltestpaths bidirectionally (#1346, defense-in-depth on #1339). The original one-directional check caught the case where the Makefile missed a path that pyproject.toml declared (#1339, the bug that leftpython/djust/tests/uncollected for months). The reverse direction — a path added to the Makefile but missing from pyproject.toml, or removed from pyproject and still in Makefile — would have gone unflagged. Now fails loud with a clear set diff in either direction.Refreshed stale
(file as new issue)placeholders in May 2026 audit docs (#1342).docs/audits/lifecycle-2026-05.mdanddocs/audits/decorator-contract-2026-05.mdnow cite real issue numbers for the 9 follow-ups (#1283-#1291), all of which are closed. The lifecycle §3 #7 row (mount() pre/post snapshot) gets a closure annotation noting_capture_dirty_baselinealready runs in production atpython/djust/websocket.py:2145./djust-dev audit-statuswill now report accurate state to app authors.Round-trip identity tests for AST-shape contracts now drive input from parser output (#1388, Action Tracker #158). 12 tests in
crates/djust_templates/src/inheritance.rsmigrated from manually-constructedNode::*ASTs toparse(tokenize(source)). Previously, manual construction bypassed parser invariants likeparse_filter_specs's outer-quote preservation, so contract violations silently passed (the original PR #1086 / #1081 case). Conversion uncovered a previously-masked bug innodes_to_template_stringfor{% include %}(parser stores the path with surrounding quotes; emitter wraps again, producing{% include ""path.html"" %}on round-trip). Out of scope per #1079 broader-sweep canon — filed as #1396 with the affected test marked#[ignore].X008 audit heuristic now walks same-module MRO and recognizes broader URL-kwarg-binding shapes (#1382, #1383, deferred from PR #1381 Stage 11). Two improvements to
python/djust/audit_ast.py:_class_has_attributeand_class_defines_methodaccept an optionalclass_indexparameter and walk the same-module MRO via static analysis when supplied. The X008 IDOR-shape checker uses this so views inheritingpermission_requiredfrom a base mixin (or inheritinghas_object_permission/check_permissionsoverrides) are correctly classified. Cross-module bases are silently skipped — by design, the static analysis is module-local. Cycle guard via visited-set in_walk_mro_staticprevents recursion onclass A(B): ...; class B(A): ...._mount_assigns_url_kwarg_idnow recognizes three additional RHS shapes beyond bareself.x = x:self.kwargs["x"](Subscript),int(x)/str(x)/uuid(x)/UUID(x)(whitelisted casts; literal arguments likeint(42)correctly do NOT match), and(self.)kwargs.get("x"[, default]). Reduces false-negatives from views using mixins or coercion.
10 new test cases in
TestX008IDORShapeNeedsObjectPermissioncover the new branches plus the X001-non-co-fire invariant.
Fixed
- Sticky-child views with overridden
get_object()no longer silently skip per-event object-permission checks (#1380, deferred from PR #1378 Stage 11 🟡 #2). When a sticky/embedded child view'sowner_requestisNone(the parent failed to stamprequestbecausemixins/sticky.py:212-218's read-only-child constraint raisedAttributeError),_validate_event_securitynow FAILS CLOSED if the child opted into the object-permission lifecycle: sends apermission_deniederror frame and logs aWARNINGinstead of returning the handler. Views that did NOT overrideget_objectare unchanged (no security check is active for them, so silent fall-through is correct). Companion change:mixins/sticky.py:215logger.debug→logger.warningon the read-only-child path so the upstream gap is observable in production logs at its source.