Enforce task session binding for authoring apply
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-16 19:59:14 +03:00
parent f37d45066d
commit cca24d0e7e
2 changed files with 133 additions and 0 deletions
@@ -3260,6 +3260,68 @@ def _authoring_guard_checks(
]
def _authoring_task_session_check(
project_id: str,
task_id: str | None,
session_id: str | None,
) -> AuthoringGuardCheck:
if not task_id:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message="Task id is required for workspace apply",
)
task = _collaboration.tasks.get(task_id)
if task is None:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message=f"Task {task_id} was not found",
)
if task.project_id != project_id:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message=f"Task {task_id} belongs to project {task.project_id}",
)
if task.status.value in {"DONE", "CANCELED"}:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message=f"Task {task_id} is {task.status.value}",
)
if not session_id:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message="Session id is required for workspace apply",
)
session = _collaboration.sessions.get(session_id)
if session is None:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message=f"Session {session_id} was not found",
)
if session.task_id != task_id:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message=f"Session {session_id} belongs to task {session.task_id}",
)
if session.finished_at is not None:
return AuthoringGuardCheck(
name="task-session",
status="BLOCKED",
message=f"Session {session_id} is already finished",
)
return AuthoringGuardCheck(
name="task-session",
status="OK",
message=f"Task {task_id} and session {session_id} are active for project {project_id}",
)
def _authoring_target_node(snapshot: SirSnapshot, request: AuthoringSemanticDiffPreviewRequest):
if request.target_lineage_id:
found = next((node for node in snapshot.nodes if node.lineage_id == request.target_lineage_id), None)
@@ -3393,6 +3455,7 @@ def _authoring_semantic_diff_preview(
user_id=request.user_id,
),
)
checks.append(_authoring_task_session_check(project_id, request.task_id, request.session_id))
version_preview = _authoring_version_preview(target, request.proposed_text, request.task_id, request.session_id)
return AuthoringSemanticDiffPreviewResponse(
project_id=project_id,
@@ -5219,6 +5282,7 @@ def _authoring_metadata_object_preview(
AuthoringGuardCheck(name="preview", status="REQUIRED", message="Metadata draft must be reviewed before apply"),
AuthoringGuardCheck(name="workspace-history", status="READY", message="Draft can be saved to SFERA workspace history"),
AuthoringGuardCheck(name="production-1c", status="BLOCKED", message="Production 1C metadata write is disabled"),
_authoring_task_session_check(project_id, request.task_id, request.session_id),
]
return AuthoringMetadataObjectPreviewResponse(
project_id=project_id,
@@ -5706,6 +5770,12 @@ async def authoring_apply_metadata_object(
raise HTTPException(status_code=400, detail="No metadata draft to apply")
if preview.version_preview.next_version_id != request.expected_next_version_id:
raise HTTPException(status_code=409, detail="Expected version id does not match current metadata preview")
blocking_checks = [
check for check in preview.checks
if check.status == "BLOCKED" and check.name not in {"production-1c"}
]
if blocking_checks:
raise HTTPException(status_code=409, detail={"blocked_checks": [check.model_dump(mode="json") for check in blocking_checks]})
version, change_id, path = _persist_authoring_metadata_object(project_id, preview, request)
return AuthoringApplyMetadataObjectResponse(
project_id=project_id,
@@ -5748,6 +5818,9 @@ async def authoring_apply_rollback(
raise HTTPException(status_code=409, detail="Expected rollback version id does not match current preview")
if not preview.apply_available:
raise HTTPException(status_code=409, detail="Rollback apply is not available")
task_session_check = _authoring_task_session_check(project_id, request.task_id, request.session_id)
if task_session_check.status == "BLOCKED":
raise HTTPException(status_code=409, detail={"blocked_checks": [task_session_check.model_dump(mode="json")]})
version, path = _persist_authoring_rollback(project_id, change_payload, preview, request)
return AuthoringApplyRollbackResponse(
project_id=project_id,