Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
586a5dd
docs: Add CR-011 for unified lifecycle status management
itercharles Jan 1, 2026
c9bbcbe
feat: Add global lifecycle models to configuration schema
itercharles Jan 1, 2026
f6352c5
feat: Add global lifecycle configuration to project config
itercharles Jan 1, 2026
0fbf631
refactor: Move from_state to per-doc-type configuration
itercharles Jan 1, 2026
b92361e
refactor: Merge states and actions, simplify per-doc-type config
itercharles Jan 1, 2026
586390f
refactor: Remove status property from UC doc type
itercharles Jan 1, 2026
7d437f6
feat: Add transition criteria support to lifecycle config
itercharles Jan 1, 2026
951234f
refactor: Simplify transitions to from_states -> to_state pattern
itercharles Jan 1, 2026
10a6067
feat: Add initial_state to per-doc-type lifecycle config
itercharles Jan 1, 2026
2a5ac14
refactor: Remove redundant initial_state field
itercharles Jan 1, 2026
d496cfc
feat: Add lifecycle management methods to CompliantFlowCore
itercharles Jan 1, 2026
bc23881
feat: Auto-set initial status when creating items
itercharles Jan 1, 2026
fa25e2f
revert: Remove redundant initial status setting
itercharles Jan 1, 2026
b1cfe5f
feat: Move status initialization to backend
itercharles Jan 1, 2026
ed90976
refactor: Always set initial status, don't allow override
itercharles Jan 1, 2026
b69a9d9
refactor: Remove defensive check for initial_state
itercharles Jan 1, 2026
fa585ea
feat: Update workflow engine for new lifecycle format
itercharles Jan 1, 2026
d1e5cf0
wip: Update UI to use core.get_available_transitions()
itercharles Jan 1, 2026
e474507
feat: Update render_transition_workflow to use core.execute_transition()
itercharles Jan 1, 2026
8322490
fix: Update Traceability page for new lifecycle system
itercharles Jan 1, 2026
0a112a6
fix: Update render_metrics to use global_lifecycle
itercharles Jan 1, 2026
f91083f
refactor: Remove status_history recording
itercharles Jan 1, 2026
8cc3809
feat: Migrate all doc types to new lifecycle format
itercharles Jan 1, 2026
a565e72
fix: Use global_lifecycle for status filter
itercharles Jan 1, 2026
1a6db2c
fix: Get status filter states from doc type transitions
itercharles Jan 1, 2026
adf93e3
refactor: Make get_state_info public
itercharles Jan 1, 2026
3bf5228
refactor: Remove DynamicWorkflowEngine from UI
itercharles Jan 1, 2026
b20af3a
refactor: Remove obsolete methods from DynamicWorkflowEngine
itercharles Jan 1, 2026
9a2fb6b
fix: Update import to use get_state_info instead of _get_state_info
itercharles Jan 1, 2026
c50eb2a
fix: Remove remaining DynamicWorkflowEngine type hints
itercharles Jan 1, 2026
3b1adf8
refactor: Remove workflow_engine.py entirely
itercharles Jan 1, 2026
95ad062
fix: Remove remaining DynamicWorkflowEngine imports
itercharles Jan 1, 2026
7b10034
fix: Replace remaining DynamicWorkflowEngine usage with core methods
itercharles Jan 1, 2026
59970d6
test: Update tests to use core methods instead of DynamicWorkflowEngine
itercharles Jan 1, 2026
67d2655
test: Update test data to use new lifecycle format
itercharles Jan 1, 2026
222a0b0
fix: Correct syntax error in test_data.py
itercharles Jan 1, 2026
fa9c102
test: Fix test_srs_015_lifecycle to use core methods
itercharles Jan 1, 2026
2971c0f
test: Fix test_srs_008_workflow to use core methods
itercharles Jan 1, 2026
50da493
test: Fix all workflow variable references in tests
itercharles Jan 1, 2026
f4d962c
fix: Remove last DynamicWorkflowEngine reference in update_item
itercharles Jan 1, 2026
08180dd
test: Fix remaining test failures
itercharles Jan 1, 2026
4e9e1fe
test: Convert CR lifecycle to new format in test data
itercharles Jan 1, 2026
a70f37e
fix: Replace workflow.get_initial_state() with self.get_initial_state()
itercharles Jan 1, 2026
7941fd7
test: Remove redundant code setting SYS-001 to approved
itercharles Jan 1, 2026
2eb50b5
fix: Only auto-initialize status if not provided
itercharles Jan 1, 2026
f87495d
fix: Properly fix create_item status auto-initialization
itercharles Jan 1, 2026
98dfff6
Revert "fix: Properly fix create_item status auto-initialization"
itercharles Jan 1, 2026
fb4527a
refactor: centralize workflow logic in core and simplify DHF root con…
itercharles Jan 1, 2026
04f43b4
Auto-update CR-011 affected_items
github-actions[bot] Jan 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
454 changes: 208 additions & 246 deletions DHF/config/project_config.yaml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions DHF/items/02_req_sys/SYS-TRANS-001.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
id: SYS-TRANS-001
title: Transition Test
content: Testing transitions
category: Functional
status: approved
file_path: /Users/chenwenliang/code/CompliantFlow/DHF/items/02_req_sys/SYS-TRANS-001.yaml
approved_by: tester
approved_date: '2025-01-01'
29 changes: 29 additions & 0 deletions DHF/items/09_cr/CR-011.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
id: CR-011
title: refactor the status management of object
type: CR
status: submitted
description: "Current status definition is too complex and should be simplified. \n\
\nThere should be a collection of all the lifecycle status and action in the configuration\
\ - \naction: create -> status: draft\naction: submit for review -> status: in review\
\ \naction: approve -> status: approved \naction: start implementation -> status:\
\ implementing \naction: complete -> status: completed\naction: start verification\
\ -> status: verified\naction: start validation -> status validated\naction: acceptance\
\ assessment -> status accept\naction: retire -> status: retired\naction: close\
\ -> status: closed\n\nThen, for each object, the property can be configured:\n\
name: lifecycle_status\ncurrent_status: none\nsupport_actions:\n- create\ncurrent_status:\
\ draft\nsupport_actions:\n- approve\n- retire\ncurrent_status: approved\nsupport_actions:\n\
- retire\n\nAnd, every action shall recorded as atom data, including who and when\
\ this change is performed. This is not necessary to be configured, it's a forced\
\ policy."
justification: unify the status management.
priority: High
file_path: /Users/chenwenliang/code/CompliantFlow/DHF/items/09_cr/CR-011.yaml
affected_items:
- SYS-TRANS-001
- TC-SRS-006
- TC-SRS-008
- TC-SRS-015
implementation_prs:
- pr_number: 12
pr_url: https://github.com/itercharles/CompliantFlow/pull/12
title: Feature/cr 011 unified lifecycle
17 changes: 0 additions & 17 deletions app_config.yaml

This file was deleted.

1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[pytest]
addopts = -p no:anyio
markers =
browser: marks tests as browser tests (deselect with '-m "not browser"')
54 changes: 14 additions & 40 deletions src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,59 +24,33 @@
from utils.ui_helpers import check_and_show_item_detail


def get_dhf_root(config_file: str = None) -> Path:
def get_dhf_root() -> Path:
"""
Get DHF root directory from configuration.

Args:
config_file: Path to app_config.yaml (optional)

Returns:
Path to DHF directory
"""
# Check for environment variable override (used by tests)
import os
env_dhf = os.environ.get("COMPLIANTFLOW_DHF_ROOT")
if env_dhf:
print(f"[APP] Using DHF from environment: {env_dhf}")
return Path(env_dhf)

# Default to production DHF
default_dhf = Path(__file__).resolve().parent.parent / "DHF"

# If no config file specified, use default location
if config_file is None:
config_file = Path(__file__).resolve().parent.parent / "app_config.yaml"
else:
config_file = Path(config_file)

# If config file doesn't exist, use default
if not config_file.exists():
return default_dhf

# Read config file
try:
with open(config_file, 'r') as f:
config_data = yaml.safe_load(f) or {}

if 'dhf_root' in config_data:
dhf_path = config_data['dhf_root']
# Resolve relative paths from project root
if not Path(dhf_path).is_absolute():
project_root = Path(__file__).resolve().parent.parent
return project_root / dhf_path
return Path(dhf_path)
except Exception as e:
st.warning(f"Failed to load config file {config_file}: {e}. Using default DHF.")

return default_dhf
return Path(__file__).resolve().parent.parent / "DHF"


# Initialize Core
@st.cache_resource
def get_core(_config_file: str = None):
def get_core():
"""
Initialize CompliantFlowCore with DHF from configuration.

Args:
_config_file: Path to app_config.yaml (optional, prefixed with _ for st.cache)
Initialize CompliantFlowCore.

Note: dhf_root is read from config and used as implicit cache key
Note: DHF root is resolved dynamically.
"""
dhf_root = get_dhf_root(_config_file)
dhf_root = get_dhf_root()
return CompliantFlowCore(dhf_root)


Expand Down Expand Up @@ -161,7 +135,7 @@ def home_page():
# === CORE INITIALIZATION ===
try:
# Get config file path (use default location)
st.session_state.core = get_core(None) # None will use default app_config.yaml location
st.session_state.core = get_core()
except Exception as e:
st.error(f"Failed to initialize CompliantFlow Core: {e}")
st.stop()
Expand Down
30 changes: 2 additions & 28 deletions src/pages/02_Traceability.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,34 +88,8 @@ def get_shape_by_type(item_id: str) -> str:

def should_show_warning(item: dict, core) -> bool:
"""Check if item should show warning icon based on lifecycle state."""
item_status = item.get('status', 'draft')
item_id = item['id']

# Get doc type for this item
doc_type_code = get_doc_type_code(item_id)
doc_type_config = core.config.get_doc_type(doc_type_code)

if not doc_type_config:
raise ValueError(f"Document type configuration not found for: {doc_type_code}")

if not doc_type_config.lifecycle:
raise ValueError(f"Lifecycle configuration missing for document type: {doc_type_code}")

# Get lifecycle states
lifecycle = doc_type_config.lifecycle
states = lifecycle.get('states', [])

if not states:
raise ValueError(f"No states defined in lifecycle for document type: {doc_type_code}")

# Find stable states (states with is_stable: true)
stable_states = [s.get('id') for s in states if s.get('is_stable', False)]

if not stable_states:
raise ValueError(f"No stable states configured for document type: {doc_type_code}. Add 'is_stable: true' to appropriate states.")

# Show warning if not in a stable state
return item_status not in stable_states
# Use core.is_item_editable() which checks global lifecycle
return core.is_item_editable(item)

def build_matrix_table(all_items: List[dict], path: List[str], core) -> pd.DataFrame:
"""Build traceability table for a specific matrix configuration."""
Expand Down
16 changes: 8 additions & 8 deletions src/pages/ui_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def render_item_card(
item: Dict[str, Any],
item_type: str,
core: Any,
workflow_engine: Any = None,

show_actions: bool = True
) -> None:
"""
Expand All @@ -183,7 +183,7 @@ def render_item_card(
item: Item dictionary
item_type: Type prefix (CRS, SYS, SDS, etc.)
core: CompliantFlowCore instance
workflow_engine: DynamicWorkflowEngine instance for state info

show_actions: Whether to show action buttons
"""
with st.expander(f"▼ {item['id']} - {item.get('title', 'N/A')}", expanded=False):
Expand All @@ -192,8 +192,8 @@ def render_item_card(
with col1:
current_status = item.get('status', 'unknown')
state_config = None
if workflow_engine:
state_config = workflow_engine.get_state_info(current_status)
if True: # Always use core
state_config = core.get_state_info(current_status)

render_status_badge(
current_status,
Expand Down Expand Up @@ -225,8 +225,8 @@ def render_item_card(
link_status = linked_item.get('status', 'unknown')
# Get state config for linked item to show appropriate icon
link_icon = '📄'
if workflow_engine:
link_state = workflow_engine.get_state_info(link_status)
if True: # Always use core
link_state = core.get_state_info(link_status)
link_icon = link_state.get('icon', '📄')
st.write(f"{link_icon} → {link_id}: {linked_item.get('title', 'N/A')}")
with col2:
Expand All @@ -236,8 +236,8 @@ def render_item_card(

# Actions - show available transitions
if show_actions and workflow_engine:
current_status = item.get('status', workflow_engine.get_initial_state())
available_transitions = workflow_engine.get_available_transitions(current_status)
current_status = item.get('status', core.get_initial_state(item["id"].split("-")[0]))
available_transitions = core.get_available_transitions(item)

if available_transitions:
st.markdown("---")
Expand Down
Loading