django-logic 0.10.0
An overengineering sweep. ~1,900 lines removed across the engine, its tests and
its docs, four settings retired, and one addition: a system check for the
failure mode that motivated the audit. Every removal below was verified to have
no consumer — the two real consumers were searched under both import spellings
before anything was deleted.
Removed (breaking)
-
RedisStateandState.get_db_state().RedisStatewas 199 of
state.py's 326 lines and no consumer had ever setstate_classto it;
measured on a live deployment of 28 bound machines / 137 transitions, all 50
process classes resolved to the baseState. Useget_persisted_state()in
place ofget_db_state(). Two hardening issues turn out to have been spent on
dead code — #139's token-in-key wrapper and #151's Lua compare-and-set — while
the base-Stateownership token and compare-and-delete unlock stay.
Deliberately noRedisState = Statealias: the two differ in locking
semantics, so an alias would change behaviour silently where anImportError
says so plainly. -
LOG_KWARGS/LOG_KWARGS_REDACTOR. Scrub with alogging.Filteron the
django-logic.transitionlogger instead.redact_log_kwargskeeps its
shallow copy, which is a real fix, not a knob. -
PHASE2_STATE_GUARD = 'warn'. The phase-2 state guard now always
enforces; a stale'warn'is ignored. Its only purpose was restoring pre-0.4
behaviour. -
SENTRY_TRANSACTION_NAMING. Transactions are always named. -
PROCESS_CLASS_ALIASES— removed rather than fixed, because it never did
what it documented: the map was applied to the recorded bound class but
_find_background_transition_in_ownercomparedowning_process_class
verbatim, so renaming a nested owning process still yielded
[unrestorable]with a correct alias configured. Drain in-flight rows
before renaming a Process class — already the documented rule for the
siblingaction_namerefactor. -
django_logic.conditions(all_related_in,any_related_in). The one
consumer that wanted this pattern hand-wrote it four times with different
empty-set semantics.docs/recipes/nested-processes.mdnow shows the closures
inline. -
Process.get_transition_by_action_nameand the publicignore_state=
parameter ofget_available_transitions. Neither had a caller in the library,
its suite, or any consumer. To resolve one action, iterate
get_available_transitions(user=..., action_name='x')and take the single
match — that is what the removed method did. Gotcha: a stale call will not
raiseAttributeErrorpointing at this removal, becauseProcess.__getattr__
treats any non-underscore miss as an action name. Attribute access still
returns a callable, and calling it reports an argument problem with a
now-nonexistent action:TypeError: get_transition_by_action_name() accepts keyword arguments only (got 1 positional). Pass user and other valuesGrep for the name when upgrading rather than relying on the traceback.
-
ProcessScenario.snapshot_on_failureandformat_failure(snapshot=),
never enabled by anyone in the 14 months since they shipped.
snapshot()/from_snapshot()are unaffected. -
BaseTransition— folded intoTransition, along with the
conditions_class/failure_callbacks_class/failure_side_effects_class/
next_transition_classswap hooks that had no override anywhere.
Transition.is_background,side_effects_class,callbacks_class,
permissions_class,conditions_classandProcess.permissions_classall
stay: consumers use them. -
ExecutionTrackeris no longer re-exported fromdjango_logic.testing
(the class remains;track()is the supported path). -
process_nameis now required onlatest_message,message_forand
uncompleted_message. TheNonedefault made it possible to silently
reintroduce the #150 cross-process bleed by omitting an argument. -
JourneyStep.matches()— use==. -
coverage_reportno longer reads 0.8 or 0.9.0 log lines. Record a fresh
log per run, which was always the documented practice. -
The non-finite-float pre-scan in
serialize_kwargs. The
json.dumps(allow_nan=False)round-trip rejects a superset (it also catches a
non-finite float in a dict key, which the scan could not reach); the error
message no longer names the offending key path. -
_infer_field_name: aTransitionMessagewith nofield_namenow fails
closed as[unrestorable]instead of guessing'state', which could drive
the wrong machine on a multi-process model.
Added
django_logic.W002— underBACKGROUND_EXECUTION='celery', reports any
shipped safety-net task missing from the running app's beat schedule. This
existed to catch a real incident: the README andbeat_schedule()'s own
docstring recommendedapp.conf.beat_schedule = {...}, which Celery silently
ignores when the project also definesCELERY_BEAT_SCHEDULEin Django
settings, and a consumer consequently ran seven weeks with all five tasks
registered and never fired. Both now recommend
app.conf['CELERY_BEAT_SCHEDULE'].django_logic.W003— reports anyDJANGO_LOGICkey this release removed.
DJANGO_LOGIChas no unknown-key rejection, so every removal above fails
open and silently; the sharpest case is a deployment that set
LOG_KWARGS_REDACTORfor PII compliance, upgrades, and starts writing raw
kwargs to its logs with no signal anywhere. The warning names the replacement
for each key.
Fixed
TaskCrashRedeliveryConfigTestshardcoded five task objects and so never
coveredrecover_stranded_states; bothacks_lateand
reject_on_worker_lostcould be stripped from it with the suite green. The
list is now derived from the module.- The duplicated failure-side-effect savepoint in
background/runner.py: #138
had already moved that rule intocommands.FailureSideEffects.executewith a
stronger savepoint, and both call sites already ran inside the atomic block.
Docs
- Deleted five stale pre-implementation documents and the index that tiered
them (2,311 lines), whose rot was already realized —docs/PLAN.mdomitted
five shipped settings whileCLAUDE.mdtold engine-changers to read it first.
The two still-open items from the Heroku-validation notes moved toTODO.md. - De-duplicated the prose that
README.mdandCHANGELOG.mdalready owned, and
collapsed the README testing chapter's overlap withdocs/TESTING_GUIDE.md. CLAUDE.mdnow states the comment/docstring rule that reviewers had been
citing as though it existed.
Repo
- Removed the nightly "stress" CI step (its
STABILITY_STRESS_MODEwas read by
nothing, so it re-ran the identical tests), an unreferenced and broken
docker-compose.test.ymlservice, and.cursor/rules/(no audience, five
false statements). Thedevextra droppedpytest/pytest-django— there is
no pytest configuration, so collection could never have worked — and
djangorestframework, which no test imports. Thedrfextra stays. - Test settings: one
tests.dl_settings(**overrides)helper replaces 26
hand-copied dicts, and 13 no-op@override_settingsare gone.