djust 0.9.5rc2

Pre-releaseReleased

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 via cls.__dict__.get (avoids attribute inheritance). Refactored the existing snapshot-truncation warning in python/djust/websocket.py to 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.json Stage 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.json Stage 7: mandatory checklist item requiring disconfirming citations during self-review — bias toward active falsification rather than passive confirmation (#1386).
    • CLAUDE.md Bug-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_attrs snapshot-order invariant note (#1393).
    • CLAUDE.md Process 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.py now verifies Makefile and pyproject.toml testpaths 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 left python/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.md and docs/audits/decorator-contract-2026-05.md now 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_baseline already runs in production at python/djust/websocket.py:2145. /djust-dev audit-status will 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.rs migrated from manually-constructed Node::* ASTs to parse(tokenize(source)). Previously, manual construction bypassed parser invariants like parse_filter_specs's outer-quote preservation, so contract violations silently passed (the original PR #1086 / #1081 case). Conversion uncovered a previously-masked bug in nodes_to_template_string for {% 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_attribute and _class_defines_method accept an optional class_index parameter and walk the same-module MRO via static analysis when supplied. The X008 IDOR-shape checker uses this so views inheriting permission_required from a base mixin (or inheriting has_object_permission / check_permissions overrides) 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_static prevents recursion on class A(B): ...; class B(A): ....
    • _mount_assigns_url_kwarg_id now recognizes three additional RHS shapes beyond bare self.x = x: self.kwargs["x"] (Subscript), int(x) / str(x) / uuid(x) / UUID(x) (whitelisted casts; literal arguments like int(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 TestX008IDORShapeNeedsObjectPermission cover 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's owner_request is None (the parent failed to stamp request because mixins/sticky.py:212-218's read-only-child constraint raised AttributeError), _validate_event_security now FAILS CLOSED if the child opted into the object-permission lifecycle: sends a permission_denied error frame and logs a WARNING instead of returning the handler. Views that did NOT override get_object are unchanged (no security check is active for them, so silent fall-through is correct). Companion change: mixins/sticky.py:215 logger.debuglogger.warning on the read-only-child path so the upstream gap is observable in production logs at its source.

All releases · Atom feed