Release Notes — v0.2.21 (September 19, 2026)
This release consolidates the work completed since v0.2.19: the new state-based workflow engine, the workflow hooks extension point, and the full-page navigation fix (recorded in the changelog under 0.2.20 but never given its own release note).
1. State-Based Workflow Engine (workflow_kind='state_based')
Status: Included in v0.2.21
A second workflow mode for enezaworkflowengine. States and transitions are defined by the
developer and drawn by an administrator in the designer, including parallel Fork/Join branches.
The existing approval workflow is unchanged: 'approval' remains the default, and a template is
state-based only when WorkflowTemplate.workflow_kind = 'state_based'.
| Approval workflow | State-based workflow | |
|---|---|---|
| Node types | start, approval, condition, end | start, state, fork, join, end |
| Routing | Computed (conditions / next_step) |
Actor chooses an allowed transition |
| Parallel paths | Not supported | Fork / Join branches |
| Document field | Approvalstatus |
workflowstate |
| Runtime unit | One current step | One or more active branches (tokens) |
Models & Fields
| Model / Field | Purpose |
|---|---|
WorkflowTemplate.workflow_kind |
approval (default) or state_based. |
WorkflowTemplate.state_choices_path |
Optional per-template override pointing at a TextChoices class. |
StepTemplate.state_code |
State code for state steps; validated against the state vocabulary. |
StepTemplate.is_initial / is_terminal |
Marks start/end states. |
StepTemplate.matched_join |
Links a Fork to its matching Join. |
StateTransition |
Labelled, ordered edge (source_state → target_state). |
WorkflowBranch |
One live path (token) through the workflow; siblings share a fork_scope UUID. |
Runtime (enezaworkflowengine/services/state_workflow.py)
start_state_workflow, branch move, and completion services.- Fork fires all outgoing transitions, spawning one branch per target sharing a
fork_scope. - Join waits until every sibling branch has been performed, then auto-continues; it has no assignees and is never shown by name.
- A submitted document is pinned to the template version active at submission.
workflowstatemirrors the oldest active state branch;WorkflowBranchrows are authoritative.
State Vocabulary (services/state_choices.py)
- Developer-owned via a
workflowstatefield and aWORKFLOW_STATE_CHOICESregistry mapping workflow name →TextChoicespath. - Resolution precedence: registry → per-template
state_choices_path→ auto-discovery from a documentworkflowstatefield that declares choices. - Diagnostic command:
python manage.py check_workflow_state_choices --workflowname <name>.
Validation, Designer & Activation
validate_state_workflow_templateenforces rules S1–S16 (single START, valid transitions, state codes in vocabulary, matched joins, at least one effective assignee per state, …).- The workflow designer palette adds State, Fork and Join nodes with vertical ports and labelled transitions.
- Saving always clones the loaded version into a new inactive version; activation runs the validator and refuses an invalid graph. Assignees only take effect on the active version.
Management Commands
assign_state_users,check_workflow_state_choices,create_sample_state_workflow,delete_workflow_template,workflow_instance_status,workflow_task_status.
Migrations
0004_state_based_workflow.py,0005_state_choices_and_start_end.py.
Documentation
state-based-workflow.md,state-based-workflow-architecture.md,state-based-workflow-reference.md,state-based-workflow-troubleshooting.md, and updates to the Workflow developer index.
2. Workflow Hooks System
Status: Included in v0.2.21
A registry-based extension point that runs developer callables on workflow engine events, for both approval and state-based workflows.
Events
| Event | Kind | Fires when |
|---|---|---|
submitted |
both | A document is submitted and the instance starts. |
step_entered |
approval | An approval step is entered and approvers assigned. |
approved |
approval | An approval step reaches its threshold. |
rejected |
approval | A step is rejected (terminal). |
changes_requested |
approval | Changes are requested on a step. |
state_entered |
state | A branch arrives at a business state. |
state_changed |
state | A user successfully moves a branch. |
completed |
both | The workflow finishes with no active work left. |
cancelled |
both | The workflow is cancelled. |
Registration
- New
WorkflowTypeConfig.WORKFLOW_HOOKSregistry (workflowconfig/hooks.py), following the same pattern asWORKFLOW_TYPE_REGISTRYandWORKFLOW_STATE_CHOICES. - Entries are dotted callable paths or
{'handler': path, ...filters}dicts with optional exact-match filters (from_state,to_state,step_name,document_model); the'*'key matches all workflows. - Handlers receive a context carrying
document_model,document_id,get_document_id()andget_document().
Execution Semantics (services/workflow_hooks.py)
- Hooks are scheduled with
transaction.on_commit, so they can never roll back a transition. - Failures are caught, logged, and recorded as a
hookWorkflowTransactionentry; remaining hooks still run and the document still moves. WORKFLOW_HOOKS_ASYNC = Trueenqueues hooks throughdjango_tasks(db_worker); enqueue failure falls back to inline. AWORKFLOW_HOOKS_MAX_DEPTH(default 10) guard stops runaway recursion.- Diagnostic command:
python manage.py check_workflow_hooks --json.
Config Package Restructure
workflowconfig.pybecame a package:workflowconfig/__init__.py,hooks.py,state_choices.py,type_registry.py. The project template and this repository were migrated to the package form.
Samples & Migration
frameworkapp/workflow_hooks.pyships active samples forIncidentWorkflow(state-based) andCreditCardWorkflow(approval).0006_alter_workflowtransaction_action_type.pyadds thehookhistory action type.- New docs:
workflow-hooks.mdandworkflow-hooks-reference.md.
3. Fix: Full-page Navigations Rendered HTMX Partials
Status: Shipped in the 0.2.20 line, included in v0.2.21
Duplicating a tab, pressing Back/Forward, or refreshing a list URL could return the HTMX list fragment instead of the list page, rendering a layout-less document.
- New
HtmxVaryMiddleware(enezaframework/middleware/htmx_vary.py) appendsHX-Requestto every response'sVaryheader and setsCache-Control: no-storeon HTML responses in DEBUG. - List shells now request their partial from a distinct URL (
?partial=1); list views acceptHX-Requestorpartial=1. - New
FullPageFallbackMixinrenderscore/full_page_template.htmlfor non-HTMX requests to detail/parameter/inline views, and native POST now follows POST/Redirect/GET. - Layouts enable htmx
getCacheBusterParamso GETs never share a cache slot with a page navigation.
See the [0.2.20] changelog entry for the complete file-level detail.
4. Version Bump & License
Version
setup.py: Version bumped from0.2.20→0.2.21.docs/Release note/CHANGELOG.md: Added a 0.2.21 entry.
License
- License changed from MIT to the Enezatech Commercial License.
setup.pynow declareslicense="Enezatech Commercial License"and uses theLicense :: Other/Proprietary Licenseclassifier instead ofLicense :: OSI Approved :: MIT License.- New root
LICENSEfile: proprietary and confidential, Copyright (c) 2026 Enezatech Digital Solutions, all rights reserved; use requires a written commercial license agreement. MANIFEST.innow includesLICENSE.
Summary
| Area | Description | Scope |
|---|---|---|
| State-Based Workflow | Developer-defined states/transitions, Fork/Join parallel branches, S1–S16 validator, designer, commands | enezaworkflowengine |
| State Vocabulary | WORKFLOW_STATE_CHOICES registry + auto-discovery, diagnostic command |
enezaworkflowengine, framework/workflowconfig |
| Workflow Hooks | WORKFLOW_HOOKS registry, 9 events, on_commit execution, async mode, depth guard, hook history type |
enezaworkflowengine, framework/workflowconfig |
| Config Packaging | workflowconfig.py → workflowconfig/ package (hooks, state_choices, type_registry) |
framework, project template |
| Navigation Fix | Full-page fallback, HtmxVaryMiddleware, PRG on native POST, cache buster |
enezaframework, templates |
| Docs | 6 new workflow guides, hooks/reference guides, index updates | docs/Documentions/Workflow |
| Migrations | 0004, 0005, 0006 |
enezaworkflowengine |
| Versioning | setup.py → 0.2.21, CHANGELOG + this note |
root, docs |
Note: The developer or agent does not run or claim test results. The user is solely responsible for testing and validating all outputs before committing or deploying.