@@ -60,6 +60,7 @@ class Task:
6060 lineage : list [str ] = field (default_factory = list )
6161 is_leaf : bool = True
6262 hierarchical_id : Optional [str ] = None
63+ requirement_ids : list [str ] = field (default_factory = list )
6364
6465
6566def create (
@@ -77,6 +78,7 @@ def create(
7778 lineage : Optional [list [str ]] = None ,
7879 is_leaf : bool = True ,
7980 hierarchical_id : Optional [str ] = None ,
81+ requirement_ids : Optional [list [str ]] = None ,
8082) -> Task :
8183 """Create a new task.
8284
@@ -95,6 +97,7 @@ def create(
9597 lineage: Optional list of ancestor descriptions
9698 is_leaf: Whether this is a leaf/executable task (default True)
9799 hierarchical_id: Optional display ID like "1.2.3"
100+ requirement_ids: Optional list of PROOF9 requirement IDs this task implements
98101
99102 Returns:
100103 Created Task
@@ -103,16 +106,17 @@ def create(
103106 now = _utc_now ().isoformat ()
104107 depends_on_list = depends_on or []
105108 lineage_list = lineage or []
109+ requirement_ids_list = requirement_ids or []
106110
107111 conn = get_db_connection (workspace )
108112 try :
109113 cursor = conn .cursor ()
110114 cursor .execute (
111115 """
112- INSERT INTO tasks (id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, parent_id, lineage, is_leaf, hierarchical_id, created_at, updated_at)
113- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
116+ INSERT INTO tasks (id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, parent_id, lineage, is_leaf, hierarchical_id, created_at, updated_at, requirement_ids )
117+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
114118 """ ,
115- (task_id , workspace .id , prd_id , title , description , status .value , priority , json .dumps (depends_on_list ), estimated_hours , complexity_score , uncertainty_level , parent_id , json .dumps (lineage_list ), 1 if is_leaf else 0 , hierarchical_id , now , now ),
119+ (task_id , workspace .id , prd_id , title , description , status .value , priority , json .dumps (depends_on_list ), estimated_hours , complexity_score , uncertainty_level , parent_id , json .dumps (lineage_list ), 1 if is_leaf else 0 , hierarchical_id , now , now , json . dumps ( requirement_ids_list ) ),
116120 )
117121 conn .commit ()
118122 finally :
@@ -134,6 +138,7 @@ def create(
134138 lineage = lineage_list ,
135139 is_leaf = is_leaf ,
136140 hierarchical_id = hierarchical_id ,
141+ requirement_ids = requirement_ids_list ,
137142 created_at = datetime .fromisoformat (now ),
138143 updated_at = datetime .fromisoformat (now ),
139144 )
@@ -154,7 +159,7 @@ def get(workspace: Workspace, task_id: str) -> Optional[Task]:
154159
155160 cursor .execute (
156161 """
157- SELECT id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, created_at, updated_at, github_issue_number, parent_id, lineage, is_leaf, hierarchical_id
162+ SELECT id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, created_at, updated_at, github_issue_number, parent_id, lineage, is_leaf, hierarchical_id, requirement_ids
158163 FROM tasks
159164 WHERE workspace_id = ? AND id = ?
160165 """ ,
@@ -190,7 +195,7 @@ def list_tasks(
190195 if status :
191196 cursor .execute (
192197 """
193- SELECT id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, created_at, updated_at, github_issue_number, parent_id, lineage, is_leaf, hierarchical_id
198+ SELECT id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, created_at, updated_at, github_issue_number, parent_id, lineage, is_leaf, hierarchical_id, requirement_ids
194199 FROM tasks
195200 WHERE workspace_id = ? AND status = ?
196201 ORDER BY priority ASC, created_at ASC
@@ -201,7 +206,7 @@ def list_tasks(
201206 else :
202207 cursor .execute (
203208 """
204- SELECT id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, created_at, updated_at, github_issue_number, parent_id, lineage, is_leaf, hierarchical_id
209+ SELECT id, workspace_id, prd_id, title, description, status, priority, depends_on, estimated_hours, complexity_score, uncertainty_level, created_at, updated_at, github_issue_number, parent_id, lineage, is_leaf, hierarchical_id, requirement_ids
205210 FROM tasks
206211 WHERE workspace_id = ?
207212 ORDER BY priority ASC, created_at ASC
@@ -415,6 +420,49 @@ def update_depends_on(
415420 return task
416421
417422
423+ def update_requirement_ids (
424+ workspace : Workspace ,
425+ task_id : str ,
426+ requirement_ids : list [str ],
427+ ) -> Task :
428+ """Update a task's linked PROOF9 requirement IDs.
429+
430+ Args:
431+ workspace: Target workspace
432+ task_id: Task to update
433+ requirement_ids: List of PROOF9 requirement IDs this task implements
434+
435+ Returns:
436+ Updated Task
437+
438+ Raises:
439+ ValueError: If task not found
440+ """
441+ task = get (workspace , task_id )
442+ if not task :
443+ raise ValueError (f"Task not found: { task_id } " )
444+
445+ now = _utc_now ().isoformat ()
446+
447+ conn = get_db_connection (workspace )
448+ cursor = conn .cursor ()
449+ cursor .execute (
450+ """
451+ UPDATE tasks
452+ SET requirement_ids = ?, updated_at = ?
453+ WHERE workspace_id = ? AND id = ?
454+ """ ,
455+ (json .dumps (requirement_ids ), now , workspace .id , task_id ),
456+ )
457+ conn .commit ()
458+ conn .close ()
459+
460+ task .requirement_ids = requirement_ids
461+ task .updated_at = datetime .fromisoformat (now )
462+
463+ return task
464+
465+
418466def get_dependents (workspace : Workspace , task_id : str ) -> list [Task ]:
419467 """Get all tasks that depend on the given task.
420468
@@ -705,7 +753,7 @@ def _row_to_task(row: tuple) -> Task:
705753 Row columns: id, workspace_id, prd_id, title, description, status, priority,
706754 depends_on, estimated_hours, complexity_score, uncertainty_level,
707755 created_at, updated_at, github_issue_number, parent_id, lineage,
708- is_leaf, hierarchical_id
756+ is_leaf, hierarchical_id, requirement_ids
709757 """
710758 # Parse depends_on from JSON string (default to empty list if null)
711759 depends_on_raw = row [7 ]
@@ -719,6 +767,10 @@ def _row_to_task(row: tuple) -> Task:
719767 is_leaf_raw = row [16 ] if len (row ) > 16 else 1
720768 is_leaf = bool (is_leaf_raw ) if is_leaf_raw is not None else True
721769
770+ # Parse requirement_ids from JSON string (default to empty list if null)
771+ requirement_ids_raw = row [18 ] if len (row ) > 18 else None
772+ requirement_ids = json .loads (requirement_ids_raw ) if requirement_ids_raw else []
773+
722774 return Task (
723775 id = row [0 ],
724776 workspace_id = row [1 ],
@@ -738,4 +790,5 @@ def _row_to_task(row: tuple) -> Task:
738790 lineage = lineage ,
739791 is_leaf = is_leaf ,
740792 hierarchical_id = row [17 ] if len (row ) > 17 else None ,
793+ requirement_ids = requirement_ids ,
741794 )
0 commit comments