@ -97,6 +97,7 @@ _REVIEW_QUEUE_STATUSES = (
ToolLifecycleStatus . APPROVED ,
ToolLifecycleStatus . FAILED ,
)
_HUMAN_DECISION_NOTES_MIN_LENGTH = 12
class ToolManagementService :
@ -356,6 +357,63 @@ class ToolManagementService:
" supported_statuses " : list ( _REVIEW_QUEUE_STATUSES ) ,
}
def build_review_detail_payload ( self , version_id : str ) - > dict :
if (
self . draft_repository is None
or self . version_repository is None
or self . metadata_repository is None
) :
raise RuntimeError (
" Fluxo de governanca de tools ainda nao esta completamente conectado ao armazenamento administrativo. "
)
normalized_version_id = str ( version_id or " " ) . strip ( ) . lower ( )
version = self . version_repository . get_by_version_id ( normalized_version_id )
if version is None :
raise LookupError ( " Versao administrativa nao encontrada. " )
draft = self . draft_repository . get_by_tool_name ( version . tool_name )
if draft is None :
raise RuntimeError ( " Draft raiz da tool nao encontrado para a versao governada. " )
metadata = self . metadata_repository . get_by_tool_version_id ( version . id )
if metadata is None :
raise RuntimeError ( " Metadados persistidos da versao nao encontrados para a governanca administrativa. " )
validation_payload = { }
if self . artifact_repository is not None :
validation_artifact = self . artifact_repository . get_by_tool_version_and_kind (
version . id ,
ToolArtifactKind . VALIDATION_REPORT ,
)
if validation_artifact is not None :
validation_payload = dict ( validation_artifact . payload_json or { } )
automated_validation = self . _extract_latest_automated_validation ( version . id )
generated_source_code = str ( validation_payload . get ( " generated_source_code " ) or " " ) . strip ( )
return {
" version_id " : version . version_id ,
" tool_name " : version . tool_name ,
" display_name " : metadata . display_name ,
" domain " : metadata . domain ,
" version_number " : version . version_number ,
" status " : version . status ,
" summary " : version . summary ,
" description " : metadata . description ,
" business_goal " : version . business_goal ,
" owner_name " : version . owner_display_name ,
" parameters " : self . _serialize_parameters_for_response ( metadata . parameters_json ) ,
" queue_entry " : self . _serialize_review_queue_entry ( version ) ,
" automated_validations " : list ( validation_payload . get ( " automated_checks " ) or [ ] ) ,
" automated_validation_summary " : automated_validation . get ( " summary " ) ,
" generated_module " : build_generated_tool_module_name ( version . tool_name ) ,
" generated_callable " : GENERATED_TOOL_ENTRYPOINT ,
" generated_source_code " : generated_source_code ,
" human_gate " : self . _build_human_review_gate ( version ) ,
" decision_history " : self . _list_governance_history_entries ( version . id ) ,
" next_steps " : self . _build_review_detail_next_steps ( version , bool ( generated_source_code ) ) ,
}
def build_publications_payload ( self ) - > dict :
publications_by_tool_name = {
publication [ " tool_name " ] : publication
@ -481,7 +539,7 @@ class ToolManagementService:
pipeline_snapshot = self . _build_pipeline_snapshot ( version . status )
if automated_validation_result and automated_validation_result [ " passed " ] :
message = (
" Pipeline de geracao executado com sucesso e as validacoes automaticas de contrato, assinatura e schema passaram. "
" Pipeline de geracao executado com sucesso e as validacoes automaticas de contrato, assinatura , importacao e smoke tests passaram. "
" A versao agora segue para a proxima etapa de validacao governada. "
)
next_steps = [
@ -490,11 +548,11 @@ class ToolManagementService:
]
else :
message = (
" Pipeline de geracao executado, mas alguma validacao automatica de contrato, assinatura ou schema falhou. "
" Pipeline de geracao executado, mas alguma validacao automatica de contrato, assinatura , importacao ou smoke test falhou. "
" A versao foi marcada como failed para ajuste e nova tentativa. "
)
next_steps = [
" Ajustar metadados, assinatura esperada e schema dos parametro s antes de rodar o pipeline novamente." ,
" Ajustar metadados, assinatura esperada , importacao do modulo e smoke test s antes de rodar o pipeline novamente." ,
" Enquanto alguma validacao automatica falhar, a versao nao pode seguir para aprovacao e ativacao. " ,
]
return {
@ -517,9 +575,27 @@ class ToolManagementService:
reviewer_staff_account_id : int ,
reviewer_name : str ,
reviewer_role : StaffRole | str ,
decision_notes : str ,
reviewed_generated_code : bool ,
) - > dict :
normalized_notes = self . _normalize_human_decision_notes ( decision_notes )
normalized_version_id = str ( version_id or " " ) . strip ( ) . lower ( )
version = (
self . version_repository . get_by_version_id ( normalized_version_id )
if self . version_repository is not None
else None
)
if version is not None and version . status == ToolLifecycleStatus . GENERATED :
if not reviewed_generated_code :
raise ValueError (
" A revisao humana exige confirmar que o codigo gerado foi analisado antes da validacao. "
)
if not self . _version_has_generated_source ( normalized_version_id ) :
raise ValueError (
" A revisao humana exige que a pipeline tenha registrado o codigo completo gerado para esta versao. "
)
return self . _transition_version_status (
version_id ,
normalized_ version_id,
target_status = ToolLifecycleStatus . VALIDATED ,
allowed_current_statuses = ( ToolLifecycleStatus . GENERATED , ) ,
actor_staff_account_id = reviewer_staff_account_id ,
@ -529,6 +605,8 @@ class ToolManagementService:
artifact_kind = ToolArtifactKind . DIRECTOR_REVIEW ,
artifact_summary = " Revisao inicial de diretor registrada para a versao governada. " ,
success_message = " Versao revisada por diretor com sucesso e pronta para aprovacao. " ,
decision_notes = normalized_notes ,
reviewed_generated_code = reviewed_generated_code ,
next_steps = [
" A diretoria ainda precisa aprovar formalmente a versao antes da publicacao. " ,
" Depois da aprovacao, a publicacao ativa a tool no catalogo governado do produto. " ,
@ -542,7 +620,9 @@ class ToolManagementService:
approver_staff_account_id : int ,
approver_name : str ,
approver_role : StaffRole | str ,
decision_notes : str ,
) - > dict :
normalized_notes = self . _normalize_human_decision_notes ( decision_notes )
return self . _transition_version_status (
version_id ,
target_status = ToolLifecycleStatus . APPROVED ,
@ -554,6 +634,7 @@ class ToolManagementService:
artifact_kind = ToolArtifactKind . DIRECTOR_APPROVAL ,
artifact_summary = " Aprovacao de diretor registrada para a versao governada. " ,
success_message = " Versao aprovada por diretor com sucesso e pronta para publicacao. " ,
decision_notes = normalized_notes ,
next_steps = [
" A publicacao administrativa ainda precisa ser executada antes da ativacao. " ,
" Enquanto a versao estiver apenas aprovada, ela permanece fora do catalogo ativo do produto. " ,
@ -585,6 +666,176 @@ class ToolManagementService:
] ,
)
def deactivate_version (
self ,
version_id : str ,
* ,
actor_staff_account_id : int ,
actor_name : str ,
actor_role : StaffRole | str ,
decision_notes : str ,
) - > dict :
payload = self . _transition_version_status (
version_id ,
target_status = ToolLifecycleStatus . ARCHIVED ,
allowed_current_statuses = ( ToolLifecycleStatus . ACTIVE , ) ,
actor_staff_account_id = actor_staff_account_id ,
actor_name = actor_name ,
actor_role = actor_role ,
required_permission = AdminPermission . PUBLISH_TOOLS ,
artifact_kind = ToolArtifactKind . PUBLICATION_DEACTIVATION ,
artifact_summary = " Publicacao ativa desativada pela diretoria. " ,
success_message = " Versao ativa desativada com sucesso e retirada do catalogo governado. " ,
decision_notes = self . _normalize_human_decision_notes ( decision_notes ) ,
next_steps = [
" A versao saiu do catalogo ativo e agora permanece apenas para historico e auditoria. " ,
" Se houver uma versao arquivada anterior da mesma tool, a diretoria pode executar rollback controlado quando necessario. " ,
] ,
)
payload [ " queue_entry " ] = None
return payload
def rollback_version (
self ,
version_id : str ,
* ,
actor_staff_account_id : int ,
actor_name : str ,
actor_role : StaffRole | str ,
decision_notes : str ,
) - > dict :
normalized_role = normalize_staff_role ( actor_role )
if not role_has_permission ( normalized_role , AdminPermission . PUBLISH_TOOLS ) :
raise PermissionError (
f " Papel ' { normalized_role . value } ' sem permissao administrativa ' { AdminPermission . PUBLISH_TOOLS . value } ' . "
)
if (
self . draft_repository is None
or self . version_repository is None
or self . metadata_repository is None
) :
raise RuntimeError (
" Fluxo de governanca de tools ainda nao esta completamente conectado ao armazenamento administrativo. "
)
normalized_version_id = str ( version_id or " " ) . strip ( ) . lower ( )
current_version = self . version_repository . get_by_version_id ( normalized_version_id )
if current_version is None :
raise LookupError ( " Versao administrativa nao encontrada. " )
if current_version . status != ToolLifecycleStatus . ACTIVE :
raise ValueError (
f " O rollback exige uma versao atualmente active, mas a versao esta em ' { current_version . status . value } ' . "
)
rollback_version = self . _find_latest_archived_version (
tool_name = current_version . tool_name ,
excluding_version_id = current_version . id ,
)
if rollback_version is None :
raise ValueError ( " Nenhuma versao arquivada disponivel para rollback controlado desta tool. " )
draft = self . draft_repository . get_by_tool_name ( current_version . tool_name )
if draft is None :
raise RuntimeError ( " Draft raiz da tool nao encontrado para o rollback governado. " )
current_metadata = self . metadata_repository . get_by_tool_version_id ( current_version . id )
rollback_metadata = self . metadata_repository . get_by_tool_version_id ( rollback_version . id )
if current_metadata is None or rollback_metadata is None :
raise RuntimeError ( " Metadados persistidos nao encontrados para executar o rollback governado. " )
normalized_notes = self . _normalize_human_decision_notes ( decision_notes )
repository_session = self . _resolve_repository_session ( )
atomic_write_options = { " commit " : False } if repository_session is not None else { }
artifact_commit = False if repository_session is not None else None
try :
self . _ensure_human_governance_ready_for_activation ( rollback_version . id )
self . version_repository . update_status (
current_version ,
status = ToolLifecycleStatus . ARCHIVED ,
* * atomic_write_options ,
)
self . metadata_repository . update_status (
current_metadata ,
status = ToolLifecycleStatus . ARCHIVED ,
* * atomic_write_options ,
)
self . version_repository . update_status (
rollback_version ,
status = ToolLifecycleStatus . ACTIVE ,
* * atomic_write_options ,
)
self . metadata_repository . update_status (
rollback_metadata ,
status = ToolLifecycleStatus . ACTIVE ,
* * atomic_write_options ,
)
self . draft_repository . update_status (
draft ,
status = ToolLifecycleStatus . ACTIVE ,
* * atomic_write_options ,
)
self . _persist_governance_artifact (
draft = draft ,
version = current_version ,
artifact_kind = ToolArtifactKind . PUBLICATION_DEACTIVATION ,
summary = " Versao ativa desativada para permitir rollback controlado. " ,
previous_status = ToolLifecycleStatus . ACTIVE ,
current_status = ToolLifecycleStatus . ARCHIVED ,
actor_staff_account_id = actor_staff_account_id ,
actor_name = actor_name ,
actor_role = normalized_role ,
decision_notes = normalized_notes ,
extra_payload = {
" deactivated_for_rollback " : True ,
" rollback_target_version_id " : rollback_version . version_id ,
" rollback_target_version_number " : rollback_version . version_number ,
} ,
commit = artifact_commit ,
)
self . _persist_governance_artifact (
draft = draft ,
version = rollback_version ,
artifact_kind = ToolArtifactKind . PUBLICATION_ROLLBACK ,
summary = " Rollback controlado executado para restaurar a versao arquivada no catalogo ativo. " ,
previous_status = ToolLifecycleStatus . ARCHIVED ,
current_status = ToolLifecycleStatus . ACTIVE ,
actor_staff_account_id = actor_staff_account_id ,
actor_name = actor_name ,
actor_role = normalized_role ,
decision_notes = normalized_notes ,
extra_payload = {
" rollback_from_version_id " : current_version . version_id ,
" rollback_from_version_number " : current_version . version_number ,
} ,
commit = artifact_commit ,
)
if repository_session is not None :
self . _commit_repository_session (
repository_session ,
draft = draft ,
version = rollback_version ,
)
except Exception :
if repository_session is not None :
repository_session . rollback ( )
raise
return {
" message " : (
" Rollback executado com sucesso e a versao arquivada voltou ao catalogo governado como ativa. "
) ,
" version_id " : rollback_version . version_id ,
" tool_name " : rollback_version . tool_name ,
" version_number " : rollback_version . version_number ,
" status " : rollback_version . status ,
" queue_entry " : None ,
" publication " : self . _serialize_metadata_publication ( rollback_metadata ) ,
" next_steps " : [
" A versao restaurada voltou ao catalogo ativo do produto sob governanca da diretoria. " ,
" A versao que estava ativa foi arquivada para manter trilha auditavel do rollback controlado. " ,
] ,
}
def _transition_version_status (
self ,
version_id : str ,
@ -598,6 +849,8 @@ class ToolManagementService:
artifact_kind : ToolArtifactKind ,
artifact_summary : str ,
success_message : str ,
decision_notes : str | None = None ,
reviewed_generated_code : bool | None = None ,
next_steps : list [ str ] ,
) - > dict :
normalized_role = normalize_staff_role ( actor_role )
@ -644,6 +897,7 @@ class ToolManagementService:
try :
if target_status == ToolLifecycleStatus . ACTIVE :
self . _ensure_human_governance_ready_for_activation ( version . id )
self . _archive_active_publications (
tool_name = version . tool_name ,
excluding_version_id = version . id ,
@ -675,6 +929,8 @@ class ToolManagementService:
actor_staff_account_id = actor_staff_account_id ,
actor_name = actor_name ,
actor_role = normalized_role ,
decision_notes = decision_notes ,
reviewed_generated_code = reviewed_generated_code ,
commit = artifact_commit ,
)
if repository_session is not None :
@ -995,6 +1251,9 @@ class ToolManagementService:
actor_staff_account_id : int ,
actor_name : str ,
actor_role : StaffRole ,
decision_notes : str | None = None ,
reviewed_generated_code : bool | None = None ,
extra_payload : dict | None = None ,
commit : bool | None = None ,
) - > None :
if self . artifact_repository is None :
@ -1018,6 +1277,9 @@ class ToolManagementService:
actor_staff_account_id = actor_staff_account_id ,
actor_name = actor_name ,
actor_role = actor_role ,
decision_notes = decision_notes ,
reviewed_generated_code = reviewed_generated_code ,
extra_payload = extra_payload ,
) ,
author_staff_account_id = actor_staff_account_id ,
author_display_name = actor_name ,
@ -1034,8 +1296,11 @@ class ToolManagementService:
actor_staff_account_id : int ,
actor_name : str ,
actor_role : StaffRole ,
decision_notes : str | None = None ,
reviewed_generated_code : bool | None = None ,
extra_payload : dict | None = None ,
) - > dict :
return {
payload = {
" source " : " director_governance " ,
" action " : artifact_kind . value ,
" tool_name " : version . tool_name ,
@ -1046,7 +1311,12 @@ class ToolManagementService:
" actor_staff_account_id " : actor_staff_account_id ,
" actor_display_name " : actor_name ,
" actor_role " : actor_role . value ,
" decision_notes " : str ( decision_notes or " " ) . strip ( ) or None ,
" reviewed_generated_code " : reviewed_generated_code ,
}
if extra_payload :
payload . update ( extra_payload )
return payload
def _persist_initial_version_artifacts (
self ,
@ -1341,6 +1611,14 @@ class ToolManagementService:
" signature_schema " : dict ( signature_schema_blueprint ) ,
" import_loading " : dict ( import_loading_result ) ,
" smoke_tests " : dict ( smoke_test_result ) ,
" generated_source_code " : (
str ( import_loading_result . get ( " rendered_source " ) or " " ) . strip ( )
or self . _render_generated_tool_module_source (
version = version ,
metadata = metadata ,
signature_schema_blueprint = signature_schema_blueprint ,
)
) ,
" publication_envelope " : publication_envelope ,
}
@ -1499,6 +1777,11 @@ class ToolManagementService:
" loaded_callable " : GENERATED_TOOL_ENTRYPOINT ,
" loaded_signature " : None ,
" sandbox_package_root " : None ,
" rendered_source " : self . _render_generated_tool_module_source (
version = version ,
metadata = metadata ,
signature_schema_blueprint = signature_schema_blueprint ,
) ,
" issues " : [
" generated import/loading validation skipped because the signature/schema blueprint is invalid. "
] ,
@ -1538,6 +1821,7 @@ class ToolManagementService:
" loaded_callable " : load_result [ " loaded_callable " ] ,
" loaded_signature " : loaded_signature ,
" sandbox_package_root " : load_result [ " sandbox_package_root " ] ,
" rendered_source " : load_result [ " rendered_source " ] ,
" issues " : issues ,
}
@ -1973,6 +2257,189 @@ class ToolManagementService:
" queued_at " : version . updated_at or version . created_at ,
}
def _version_has_generated_source ( self , version_id : str ) - > bool :
if self . version_repository is None or self . artifact_repository is None :
return False
normalized_version_id = str ( version_id or " " ) . strip ( ) . lower ( )
version = self . version_repository . get_by_version_id ( normalized_version_id )
if version is None :
return False
validation_artifact = self . artifact_repository . get_by_tool_version_and_kind (
version . id ,
ToolArtifactKind . VALIDATION_REPORT ,
)
if validation_artifact is None :
return False
generated_source_code = str ( ( validation_artifact . payload_json or { } ) . get ( " generated_source_code " ) or " " ) . strip ( )
return bool ( generated_source_code )
def _find_latest_archived_version (
self ,
* ,
tool_name : str ,
excluding_version_id : int | None = None ,
) - > ToolVersion | None :
if self . version_repository is None :
return None
for archived_version in self . version_repository . list_versions (
tool_name = tool_name ,
statuses = ( ToolLifecycleStatus . ARCHIVED , ) ,
) :
if excluding_version_id is not None and archived_version . id == excluding_version_id :
continue
return archived_version
return None
@staticmethod
def _normalize_human_decision_notes ( decision_notes : str ) - > str :
normalized_notes = str ( decision_notes or " " ) . strip ( )
if len ( normalized_notes ) < _HUMAN_DECISION_NOTES_MIN_LENGTH :
raise ValueError (
" A decisao humana precisa registrar um parecer com pelo menos "
f " { _HUMAN_DECISION_NOTES_MIN_LENGTH } caracteres. "
)
return normalized_notes
def _ensure_human_governance_ready_for_activation ( self , tool_version_id : int ) - > None :
if self . artifact_repository is None :
raise RuntimeError (
" A ativacao governada exige trilha de auditoria habilitada para validar a aprovacao humana. "
)
review_artifact = self . artifact_repository . get_by_tool_version_and_kind (
tool_version_id ,
ToolArtifactKind . DIRECTOR_REVIEW ,
)
approval_artifact = self . artifact_repository . get_by_tool_version_and_kind (
tool_version_id ,
ToolArtifactKind . DIRECTOR_APPROVAL ,
)
review_payload = dict ( review_artifact . payload_json or { } ) if review_artifact is not None else { }
approval_payload = dict ( approval_artifact . payload_json or { } ) if approval_artifact is not None else { }
if not review_payload . get ( " decision_notes " ) or not bool ( review_payload . get ( " reviewed_generated_code " ) ) :
raise ValueError (
" A ativacao exige uma revisao humana registrada com parecer e confirmacao de leitura do codigo gerado. "
)
if not approval_payload . get ( " decision_notes " ) :
raise ValueError (
" A ativacao exige uma aprovacao humana registrada com parecer explicito da diretoria. "
)
def _build_human_review_gate ( self , version : ToolVersion ) - > dict :
rollback_candidate = None
if version . status == ToolLifecycleStatus . ACTIVE :
rollback_candidate = self . _find_latest_archived_version (
tool_name = version . tool_name ,
excluding_version_id = version . id ,
)
return {
" current_gate " : ToolManagementService . _build_review_gate ( version . status ) ,
" review_action_available " : version . status == ToolLifecycleStatus . GENERATED ,
" approval_action_available " : version . status == ToolLifecycleStatus . VALIDATED ,
" publication_action_available " : version . status == ToolLifecycleStatus . APPROVED ,
" deactivation_action_available " : version . status == ToolLifecycleStatus . ACTIVE ,
" rollback_action_available " : version . status == ToolLifecycleStatus . ACTIVE and rollback_candidate is not None ,
" rollback_target_version_id " : rollback_candidate . version_id if rollback_candidate is not None else None ,
" rollback_target_version_number " : rollback_candidate . version_number if rollback_candidate is not None else None ,
" requires_decision_notes " : version . status in {
ToolLifecycleStatus . GENERATED ,
ToolLifecycleStatus . VALIDATED ,
ToolLifecycleStatus . ACTIVE ,
} ,
" requires_code_review_confirmation " : version . status == ToolLifecycleStatus . GENERATED ,
}
def _build_review_detail_next_steps (
self ,
version : ToolVersion ,
generated_source_available : bool ,
) - > list [ str ] :
status = version . status
next_steps_by_status = {
ToolLifecycleStatus . DRAFT : [
" Execute a pipeline de geracao para produzir o modulo governado antes da revisao humana. " ,
" Enquanto a versao estiver em draft, ela permanece fora da aprovacao e da ativacao. " ,
] ,
ToolLifecycleStatus . GENERATED : [
" Analise o codigo completo gerado, confirme a leitura manual e registre a revisao da diretoria. " ,
" Somente depois da revisao humana a versao pode seguir para aprovacao formal. " ,
] ,
ToolLifecycleStatus . VALIDATED : [
" Registre o parecer final de aprovacao da diretoria antes da publicacao. " ,
" A ativacao continua bloqueada ate existir aprovacao humana explicita. " ,
] ,
ToolLifecycleStatus . APPROVED : [
" A versao ja foi aprovada pela diretoria e agora pode seguir para publicacao controlada. " ,
" A ativacao vai validar novamente a trilha de revisao e aprovacao humana antes de entrar no catalogo. " ,
] ,
ToolLifecycleStatus . ACTIVE : [
" A versao esta ativa no catalogo governado e pode ser desativada com parecer explicito da diretoria. " ,
" Quando houver uma versao arquivada anterior, o rollback controlado pode restaurar rapidamente a publicacao anterior. " ,
] ,
ToolLifecycleStatus . ARCHIVED : [
" Esta versao foi retirada do catalogo ativo e permanece arquivada para historico e auditoria. " ,
" A diretoria pode restaurar uma versao arquivada por rollback controlado a partir da publicacao ativa correspondente. " ,
] ,
ToolLifecycleStatus . FAILED : [
" Corrija os bloqueios da pipeline e execute uma nova geracao antes de voltar para a revisao humana. " ,
" Enquanto a versao estiver em failed, a aprovacao e a ativacao permanecem indisponiveis. " ,
] ,
}
next_steps = list ( next_steps_by_status . get ( status , [ " Acompanhe a governanca da versao pela trilha administrativa. " ] ) )
if status == ToolLifecycleStatus . ACTIVE :
rollback_candidate = self . _find_latest_archived_version (
tool_name = version . tool_name ,
excluding_version_id = version . id ,
)
if rollback_candidate is not None :
next_steps . append (
f " Ha uma versao arquivada disponivel para rollback: v { rollback_candidate . version_number } . "
)
if not generated_source_available :
next_steps . append ( " O codigo completo aparece aqui assim que a pipeline gerar e registrar a funcao governada. " )
return next_steps
def _list_governance_history_entries ( self , tool_version_id : int ) - > list [ dict ] :
if self . artifact_repository is None :
return [ ]
history_entries = self . artifact_repository . list_artifacts (
tool_version_id = tool_version_id ,
artifact_stage = ToolArtifactStage . GOVERNANCE ,
)
return [
self . _serialize_governance_history_entry ( artifact )
for artifact in reversed ( history_entries )
]
@staticmethod
def _serialize_governance_history_entry ( artifact ) - > dict :
payload = dict ( artifact . payload_json or { } )
label_by_kind = {
ToolArtifactKind . DIRECTOR_REVIEW : " Revisao humana registrada " ,
ToolArtifactKind . DIRECTOR_APPROVAL : " Aprovacao humana registrada " ,
ToolArtifactKind . PUBLICATION_RELEASE : " Publicacao administrativa registrada " ,
ToolArtifactKind . PUBLICATION_DEACTIVATION : " Desativacao registrada " ,
ToolArtifactKind . PUBLICATION_ROLLBACK : " Rollback registrado " ,
}
return {
" action_key " : artifact . artifact_kind . value ,
" label " : label_by_kind . get ( artifact . artifact_kind , " Governanca registrada " ) ,
" summary " : artifact . summary ,
" previous_status " : payload . get ( " previous_status " ) ,
" current_status " : payload . get ( " current_status " ) ,
" actor_name " : payload . get ( " actor_display_name " ) ,
" actor_role " : payload . get ( " actor_role " ) ,
" decision_notes " : payload . get ( " decision_notes " ) ,
" reviewed_generated_code " : payload . get ( " reviewed_generated_code " ) ,
" recorded_at " : artifact . updated_at or artifact . created_at ,
}
@staticmethod
def _build_review_gate ( status : ToolLifecycleStatus ) - > str :
gate_by_status = {
@ -1980,6 +2447,8 @@ class ToolManagementService:
ToolLifecycleStatus . GENERATED : " validation_required " ,
ToolLifecycleStatus . VALIDATED : " director_approval_required " ,
ToolLifecycleStatus . APPROVED : " director_publication_required " ,
ToolLifecycleStatus . ACTIVE : " publication_active " ,
ToolLifecycleStatus . ARCHIVED : " archived_history " ,
ToolLifecycleStatus . FAILED : " pipeline_retry_required " ,
}
return gate_by_status . get ( status , " governance_required " )
@ -2035,6 +2504,18 @@ class ToolManagementService:
def _serialize_metadata_publication ( self , metadata : ToolMetadata ) - > dict :
parameters = self . _serialize_parameters_for_response ( metadata . parameters_json )
version_record = None
if self . version_repository is not None :
for candidate in self . version_repository . list_versions ( tool_name = metadata . tool_name ) :
if candidate . id == metadata . tool_version_id :
version_record = candidate
break
rollback_candidate = None
if metadata . status == ToolLifecycleStatus . ACTIVE :
rollback_candidate = self . _find_latest_archived_version (
tool_name = metadata . tool_name ,
excluding_version_id = metadata . tool_version_id ,
)
return {
" publication_id " : metadata . metadata_id ,
" tool_name " : metadata . tool_name ,
@ -2043,6 +2524,7 @@ class ToolManagementService:
" domain " : metadata . domain ,
" version " : metadata . version_number ,
" status " : metadata . status ,
" version_id " : version_record . version_id if version_record is not None else None ,
" parameter_count " : len ( parameters ) ,
" parameters " : parameters ,
" author_name " : metadata . author_display_name ,
@ -2050,6 +2532,10 @@ class ToolManagementService:
" implementation_callable " : GENERATED_TOOL_ENTRYPOINT ,
" published_by " : metadata . author_display_name ,
" published_at " : metadata . updated_at or metadata . created_at ,
" deactivation_action_available " : metadata . status == ToolLifecycleStatus . ACTIVE and version_record is not None ,
" rollback_action_available " : metadata . status == ToolLifecycleStatus . ACTIVE and rollback_candidate is not None ,
" rollback_target_version_id " : rollback_candidate . version_id if rollback_candidate is not None else None ,
" rollback_target_version_number " : rollback_candidate . version_number if rollback_candidate is not None else None ,
}
def _serialize_draft_summary ( self , draft : ToolDraft ) - > dict :