Skip to content

feat: Conditional Workflow Unlocking and End-Node Completion#176

Merged
mushrafmim merged 8 commits intoOpenNSW:mainfrom
sthanikan2000:174-conditional-workflow-unlock
Feb 25, 2026
Merged

feat: Conditional Workflow Unlocking and End-Node Completion#176
mushrafmim merged 8 commits intoOpenNSW:mainfrom
sthanikan2000:174-conditional-workflow-unlock

Conversation

@sthanikan2000
Copy link
Contributor

@sthanikan2000 sthanikan2000 commented Feb 23, 2026

Summary

This PR implements conditional workflow unlocking and end-node-based completion for Issue #174, and now supports generalized boolean unlock expressions.

Key capabilities:

  1. General unlock expressions

    • Adds recursive boolean expression support with nested AND/OR logic via expression.
    • Keeps existing anyOf/allOf DNF payloads fully supported for backward compatibility.
  2. Completion outcomes

    • Adds outcome on workflow nodes to represent completion sub-states (APPROVED, REJECTED, FAST_TRACKED, etc.) without expanding core state enums.
  3. End-node workflow completion

    • Adds EndNodeTemplateID support so workflows can finish when a configured end node completes (instead of requiring all nodes to complete).
  4. Full backward compatibility

    • Existing workflows without unlock configuration continue using legacy depends_on AND-all behavior.
    • Existing DNF unlock configuration continues to work unchanged.

Type of Change

  • New feature (non-breaking change which adds functionality)

Changes Made

Schema and Migrations

  • Added unlock_configuration JSONB to workflow_node_templates.
  • Added outcome and unlock_configuration to workflow_nodes.
  • Added end_node_template_id to workflow_templates.
  • Added end_node_id to consignments to track resolved end-node instance.

Unlock Configuration Model

  • Added generalized recursive expression support:
    • expression.anyOf for OR
    • expression.allOf for AND
    • leaf conditions with nodeTemplateId plus optional state and/or outcome
  • Kept legacy AnyOf/AllOf DNF support in place.
  • Validation now supports both modes and prevents ambiguous configs using both expression and legacy anyOf together.
  • ResolveToInstanceIDs and Evaluate now support recursive expressions.

Workflow/State Machine Behavior

  • Unlock evaluation now supports generalized boolean expressions.
  • Outcome is set on completion and used during unlock evaluation.
  • End-node completion is supported via WorkflowCompletionConfig.
  • Legacy behavior remains default when advanced config is absent.

Seed Data

Updated Fresh Coconut seed workflow in backend/internal/database/migrations/011_insert_unlock_config_seed.sql:

  • Node 1: unchanged (root, no unlock config)
  • Node 2–5: migrated from DNF anyOf/allOf blocks to expression-based boolean configs
  • Logic remains equivalent to previous behavior, but now demonstrates the generalized structure

Testing

  • Added/updated unit tests for:
    • legacy DNF validation and evaluation
    • recursive expression validation and evaluation
    • template-to-instance resolution for expression trees
    • JSON marshal/unmarshal for both legacy and expression formats
  • Workflow state machine tests continue to pass with unlock + completion behavior.

Backward Compatibility

  • New fields are optional/nullable.
  • Existing payloads and workflows continue to function:
    • no unlock config → legacy depends_on behavior
    • existing anyOf/allOf DNF config → still supported
    • new expression config → fully supported

Related Issue

Closes #174

Notes

  • This PR now documents and demonstrates both unlock configuration styles:
    • Legacy DNF: anyOf/allOf
    • Generalized expression: expression with nested anyOf/allOf
  • Recommended direction for new configs is expression, while preserving legacy compatibility for existing data.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @sthanikan2000, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the workflow management system by introducing advanced control mechanisms for node progression and workflow completion. It allows for more dynamic and flexible workflows through conditional unlocking logic based on DNF expressions and granular completion outcomes. Additionally, it provides the ability to define a specific 'end node' for a workflow, enabling path-based completion rather than strict adherence to all nodes finishing. These changes provide greater adaptability for complex business processes while maintaining full backward compatibility with existing implementations.

Highlights

  • Conditional Node Unlocking with DNF Expressions: Introduced UnlockConfiguration using Disjunctive Normal Form (DNF) boolean expressions, allowing for flexible and complex dependency logic for workflow nodes. Nodes can now unlock based on combinations of states and outcomes of other nodes, moving beyond simple 'all dependencies completed' logic.
  • Completion Outcomes for Nodes: Added an outcome field to workflow_nodes to capture sub-states (e.g., APPROVED, REJECTED, EXPEDITED) when a node transitions to COMPLETED. This provides more granular information about how a node was completed without expanding the core state enums.
  • End-Node Based Workflow Completion: Implemented an optional EndNodeTemplateID on workflow templates. If specified, the workflow is considered complete when this particular end node reaches a COMPLETED state, rather than requiring all nodes in the workflow to be completed.
  • Full Backward Compatibility: All new features are optional and backward compatible. Existing workflows without UnlockConfiguration or EndNodeTemplateID will continue to function using the legacy AND-all dependency logic and all-nodes-completed workflow completion.
Changelog
  • backend/internal/database/migrations/011_add_unlock_configuration.sql
    • Added unlock_configuration (JSONB) to workflow_node_templates for template-level conditional unlock rules.
    • Added outcome (VARCHAR) and unlock_configuration (JSONB) to workflow_nodes for instance-level outcome tracking and resolved conditions.
    • Added end_node_template_id (UUID) to workflow_templates with a foreign key constraint, allowing workflows to define a specific completion node.
  • backend/internal/database/migrations/011_add_unlock_configuration_down.sql
    • Added SQL statements to drop the newly added columns and foreign key constraint for rollback purposes.
  • backend/internal/database/migrations/011_insert_unlock_config_seed.sql
    • Inserted seed data for a 'Fresh Coconut Export' workflow template, demonstrating the usage of unlock_configuration with various DNF patterns and end_node_template_id.
  • backend/internal/database/migrations/run.sh
    • Updated the migration script to include the new 011_add_unlock_configuration.sql and 011_insert_unlock_config_seed.sql migration files.
  • backend/internal/task/manager/notification.go
    • Added an Outcome field to the WorkflowManagerNotification struct to track outcome sub-states for completed transitions.
  • backend/internal/workflow/manager.go
    • Modified the StartWorkflowNodeUpdateListener to pass the Outcome from the update request to the WorkflowManagerNotification.
  • backend/internal/workflow/model/unlock_config.go
    • Added a new UnlockCondition struct to define individual conditions based on node state and/or outcome.
    • Added a new UnlockGroup struct to represent an AND-group of UnlockConditions.
    • Added a new UnlockConfig struct to represent a DNF expression (OR of AND groups) for conditional unlocking.
    • Implemented Validate() method for UnlockConfig to ensure well-formedness.
    • Implemented ResolveToInstanceIDs() to convert template-level UnlockConfig to instance-level by resolving template IDs to node instance IDs.
    • Implemented Evaluate() method for UnlockConfig to check if conditions are satisfied based on current node states and outcomes.
    • Implemented MarshalJSON and UnmarshalJSON for UnlockConfig.
  • backend/internal/workflow/model/unlock_config_test.go
    • Added comprehensive unit tests for UnlockConfig validation, evaluation, instance ID resolution, and JSON serialization/deserialization.
  • backend/internal/workflow/model/workflow.go
    • Added EndNodeTemplateID (pointer to UUID) to the WorkflowTemplate struct, allowing definition of a specific node whose completion signifies workflow completion.
  • backend/internal/workflow/model/workflow_node.go
    • Added UnlockConfiguration (pointer to UnlockConfig) to WorkflowNodeTemplate for defining conditional unlock rules at the template level.
    • Added Outcome (pointer to string) to WorkflowNode to store completion sub-states.
    • Added UnlockConfiguration (pointer to UnlockConfig) to WorkflowNode for storing resolved instance-level unlock conditions.
    • Added Outcome to UpdateWorkflowNodeDTO and WorkflowNodeResponseDTO to support passing and displaying completion outcomes.
  • backend/internal/workflow/service/consignment_service.go
    • Updated buildConsignmentDetailDTO to include the new Outcome field when constructing WorkflowNodeResponseDTO.
  • backend/internal/workflow/service/pre_consignment_service.go
    • Modified updateWorkflowNodeStateAndPropagateChangesInTx to retrieve and pass a WorkflowCompletionConfig to the state machine's TransitionToCompleted method.
    • Added a new helper function getCompletionConfig to fetch the EndNodeTemplateID from the associated workflow template.
    • Updated buildPreConsignmentResponseDTO to include the new Outcome field when constructing WorkflowNodeResponseDTO.
  • backend/internal/workflow/service/state_machine.go
    • Added WorkflowCompletionConfig struct to encapsulate workflow completion logic, including EndNodeTemplateID.
    • Modified TransitionToCompleted to accept an optional WorkflowCompletionConfig argument.
    • Updated TransitionToCompleted to set the Outcome on the completed node.
    • Modified unlockDependentNodes to pass the outcome of the completed node to its dependencies.
    • Refactored areDependenciesMet to first check for UnlockConfiguration and evaluate DNF expressions, falling back to legacy DependsOn logic if no UnlockConfiguration is present.
    • Renamed areAllNodesCompleted to isWorkflowCompleted and updated its logic to use WorkflowCompletionConfig (checking EndNodeTemplateID if present) or fall back to checking all nodes.
    • Enhanced InitializeNodesFromTemplates to resolve template-level UnlockConfigurations to instance-level UnlockConfigurations with actual node IDs.
    • Modified InitializeNodesFromTemplates to correctly set initial READY state for nodes based on the presence of UnlockConfiguration.
  • backend/internal/workflow/service/state_machine_test.go
    • Added strPtr helper function for creating string pointers in tests.
    • Added tests for TransitionToCompletedWithOutcome to verify outcome handling.
    • Added tests for UnlockWithUnlockConfiguration to validate conditional unlocking logic with DNF expressions, including various outcome and state conditions.
    • Added tests for EndNodeWorkflowCompletion to verify workflow completion based on a specific end node or fallback to all nodes.
    • Added tests for InitializeNodesWithUnlockConfiguration to ensure correct resolution of template IDs to instance IDs in unlock configurations during initialization.
Activity
  • The author has performed local testing of the changes.
  • Unit tests have been added for the new functionality, ensuring coverage for UnlockConfig and state machine enhancements.
  • Edge cases have been tested, particularly for conditional unlocking and workflow completion scenarios.
  • All existing tests continue to pass, indicating no regressions.
  • The author has performed a self-review, commented code, and verified backward compatibility and database migrations.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces conditional workflow unlocking using DNF expressions and path-based completion via end nodes. While the implementation is solid and includes comprehensive tests, there are a few areas for improvement, specifically regarding consistency between Consignment and Pre-Consignment services and potential runtime safety issues like nil pointer dereferences.

@sthanikan2000 sthanikan2000 self-assigned this Feb 23, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant new features for workflow management, including conditional node unlocking using DNF expressions, outcome sub-states for completed nodes, and end-node-based workflow completion. The changes are well-structured, with new models, service enhancements, and comprehensive tests. The backward compatibility considerations are clearly articulated, which is excellent. The database migrations and seed data also align with the new functionality. Overall, the implementation appears robust and addresses the requirements effectively.

@sthanikan2000 sthanikan2000 force-pushed the 174-conditional-workflow-unlock branch from 8aedfeb to 4f9c209 Compare February 24, 2026 18:54
Copy link
Contributor

@ginaxu1 ginaxu1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for addressing comments. See 2 nits and 1 comment below

… node instance IDs and auto-completion of end nodes
Copy link
Contributor

@ginaxu1 ginaxu1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm!

@mushrafmim mushrafmim merged commit 8330f8c into OpenNSW:main Feb 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BE][WM] Conditional Workflow Unlocking and End-Node Completion

3 participants