auto-sync: tudo-para-ia-mais-humana 2026-05-02 07:28:45
This commit is contained in:
Binary file not shown.
@@ -6,6 +6,6 @@
|
||||
"path": "G:\\_codex-git\\nucleo-gestao-operacional\\central-de-ordem-de-servico\\projects\\15_repo_tudo-para-ia-mais-humana-platform\\reports\\EXECUTADO__workspace-hygiene.md"
|
||||
}
|
||||
],
|
||||
"generatedAt": "2026-05-02T09:27:04+00:00",
|
||||
"generatedAt": "2026-05-02T10:25:29+00:00",
|
||||
"ok": false
|
||||
}
|
||||
4058
dados/workspace-hygiene-policy.json
Normal file
4058
dados/workspace-hygiene-policy.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,10 @@
|
||||
"action": "delete_directory",
|
||||
"applied": true,
|
||||
"deleted": false,
|
||||
"error": "PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpnk6r4sdb'",
|
||||
"error": "PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpnk6r4sdb'; PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpu13bzfhr'; OSError: [WinError 145] A pasta não está vazia: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp'",
|
||||
"footprint_after": {
|
||||
"byte_count": 0,
|
||||
"child_count": 1042,
|
||||
"child_count": 2,
|
||||
"errors": [
|
||||
"PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpnk6r4sdb'",
|
||||
"PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpu13bzfhr'"
|
||||
@@ -17,8 +17,8 @@
|
||||
"is_file": false
|
||||
},
|
||||
"footprint_before": {
|
||||
"byte_count": 68130347,
|
||||
"child_count": 3052,
|
||||
"byte_count": 10279,
|
||||
"child_count": 1186,
|
||||
"errors": [
|
||||
"PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpnk6r4sdb'",
|
||||
"PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpu13bzfhr'"
|
||||
@@ -61,13 +61,13 @@
|
||||
],
|
||||
"apply": true,
|
||||
"blockers": [
|
||||
"python-test-temp:PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpnk6r4sdb'"
|
||||
"python-test-temp:PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpnk6r4sdb'; PermissionError: [WinError 5] Acesso negado: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp\\\\tmpu13bzfhr'; OSError: [WinError 145] A pasta não está vazia: 'G:\\\\_codex-git\\\\tudo-para-ia-mais-humana\\\\.test-tmp'"
|
||||
],
|
||||
"central_platform_folder": "G:\\_codex-git\\nucleo-gestao-operacional\\central-de-ordem-de-servico\\projects\\15_repo_tudo-para-ia-mais-humana-platform",
|
||||
"clean": false,
|
||||
"generated_at": "2026-05-02T09:27:04+00:00",
|
||||
"generated_at": "2026-05-02T10:25:29+00:00",
|
||||
"project_root": "G:\\_codex-git\\tudo-para-ia-mais-humana",
|
||||
"report_id": "workspace-hygiene-389155382838",
|
||||
"report_id": "workspace-hygiene-441283210879",
|
||||
"status": "blocked",
|
||||
"summary": [
|
||||
"Targets evaluated: 2.",
|
||||
|
||||
104
ecossistema/WORKSPACE-HYGIENE-POLICY.md
Normal file
104
ecossistema/WORKSPACE-HYGIENE-POLICY.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Politica de higiene de workspace
|
||||
|
||||
- report_id: `workspace-hygiene-policy-9147f326520c9100`
|
||||
- generated_at: `2026-05-02T10:28:39+00:00`
|
||||
- cases: `1536`
|
||||
- owner_action_cases: `288`
|
||||
- unsafe_block_cases: `192`
|
||||
|
||||
## Resumo
|
||||
|
||||
- Policy cases: 1536.
|
||||
- Owner action cases: 288.
|
||||
- Unsafe path blocks: 192.
|
||||
- Best-effort cleanup must continue after child ACL errors and retain only inaccessible paths.
|
||||
|
||||
## Casos amostrais
|
||||
|
||||
- `workspace-hygiene-policy-5d2027200080cdc46bb65f26` `.test-tmp` `windows_primary` `inspect` `none` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-228473216bb503bc783a215c` `.test-tmp` `windows_primary` `inspect` `not_found` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-cb761d2f71e849f1c149b488` `.test-tmp` `windows_primary` `inspect` `permission_denied` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-4582db63a0bb51ede7108d29` `.test-tmp` `windows_primary` `inspect` `directory_not_empty` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-fb0ee31aef29fc1e827f9a6a` `.test-tmp` `windows_primary` `inspect` `file_locked` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-4aa82262a3f3e58f587663a6` `.test-tmp` `windows_primary` `inspect` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-4c516245ec963722a3dc7ee8` `.test-tmp` `windows_primary` `inspect` `central_write_denied` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-dabc8c8df47232d77d5e1541` `.test-tmp` `windows_primary` `inspect` `unknown` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-a0a5fba1d3767fe3c2d63f33` `.test-tmp` `windows_primary` `apply` `none` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-3e31009425a0230ecef0ccd8` `.test-tmp` `windows_primary` `apply` `not_found` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-c23bb01b6d9294f0ebfdd9fc` `.test-tmp` `windows_primary` `apply` `permission_denied` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-7cce0df320947bdf44a99a25` `.test-tmp` `windows_primary` `apply` `directory_not_empty` -> `retain_with_evidence` actions `best_effort_delete, continue_after_child_error, write_status_artifact`
|
||||
- `workspace-hygiene-policy-7d3c3344e63eb8e867ee5bfe` `.test-tmp` `windows_primary` `apply` `file_locked` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-f3675147f70d13633cb09512` `.test-tmp` `windows_primary` `apply` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-ac1bd6962d0b5a4526fdd10a` `.test-tmp` `windows_primary` `apply` `central_write_denied` -> `retain_with_evidence` actions `write_status_artifact, update_semantic_sql`
|
||||
- `workspace-hygiene-policy-5aef47da6b565cd6dcabe957` `.test-tmp` `windows_primary` `apply` `unknown` -> `retain_with_evidence` actions `write_status_artifact`
|
||||
- `workspace-hygiene-policy-f9d8c5f22f456ec593c6f211` `.test-tmp` `windows_primary` `closeout` `none` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-66c57ea342cf242a5393be0d` `.test-tmp` `windows_primary` `closeout` `not_found` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-32e3856c59c82f31b106ac99` `.test-tmp` `windows_primary` `closeout` `permission_denied` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-b1638bc1c123df15e80227d2` `.test-tmp` `windows_primary` `closeout` `directory_not_empty` -> `retain_with_evidence` actions `best_effort_delete, continue_after_child_error, write_status_artifact`
|
||||
- `workspace-hygiene-policy-45895efc199646bc41cdf9e0` `.test-tmp` `windows_primary` `closeout` `file_locked` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-7a53c08cceab0e41c3359ed8` `.test-tmp` `windows_primary` `closeout` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-46699cee9bdfd53b87e78c36` `.test-tmp` `windows_primary` `closeout` `central_write_denied` -> `retain_with_evidence` actions `write_status_artifact, update_semantic_sql`
|
||||
- `workspace-hygiene-policy-36d8f0af32e85b3e0b279e05` `.test-tmp` `windows_primary` `closeout` `unknown` -> `retain_with_evidence` actions `write_status_artifact`
|
||||
- `workspace-hygiene-policy-5a1b22bdcb58bbcc237f14ac` `.test-tmp` `windows_primary` `central_record` `none` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-0a88ddf3c1c8b14321813c32` `.test-tmp` `windows_primary` `central_record` `not_found` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-606b442e56149662192ac879` `.test-tmp` `windows_primary` `central_record` `permission_denied` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-89a9c0752f0b51866217bf84` `.test-tmp` `windows_primary` `central_record` `directory_not_empty` -> `retain_with_evidence` actions `best_effort_delete, continue_after_child_error, write_status_artifact`
|
||||
- `workspace-hygiene-policy-1f3ffab2223eeaecbbdbed62` `.test-tmp` `windows_primary` `central_record` `file_locked` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-1c441a1661159cb6f7187ce1` `.test-tmp` `windows_primary` `central_record` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-aad100963e7d8379a24f2b57` `.test-tmp` `windows_primary` `central_record` `central_write_denied` -> `retain_with_evidence` actions `write_status_artifact, update_semantic_sql`
|
||||
- `workspace-hygiene-policy-e3eb826734091137b31cc07b` `.test-tmp` `windows_primary` `central_record` `unknown` -> `retain_with_evidence` actions `write_status_artifact`
|
||||
- `workspace-hygiene-policy-f8242138d8161a66bdd2d7cb` `.test-tmp` `windows_secondary` `inspect` `none` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-8f0f0dfa2a0a0cd0be6b658e` `.test-tmp` `windows_secondary` `inspect` `not_found` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-f9c3fc1327e3c1391868b696` `.test-tmp` `windows_secondary` `inspect` `permission_denied` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-0baf6ba10316a0cfd87f80ad` `.test-tmp` `windows_secondary` `inspect` `directory_not_empty` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-806a326c17d88486df9359d2` `.test-tmp` `windows_secondary` `inspect` `file_locked` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-c992717a0c0a8d1f38b29b08` `.test-tmp` `windows_secondary` `inspect` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-53a6aee1dc74bbd57220d67e` `.test-tmp` `windows_secondary` `inspect` `central_write_denied` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-9c152223f8242817a79971bc` `.test-tmp` `windows_secondary` `inspect` `unknown` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-87c3f9c9b4e74cbc737cbda2` `.test-tmp` `windows_secondary` `apply` `none` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-206da35bc71c92f44cfee34f` `.test-tmp` `windows_secondary` `apply` `not_found` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-51957806f136b8a3e39c1b58` `.test-tmp` `windows_secondary` `apply` `permission_denied` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-0ff808e4688a92886ff70527` `.test-tmp` `windows_secondary` `apply` `directory_not_empty` -> `retain_with_evidence` actions `best_effort_delete, continue_after_child_error, write_status_artifact`
|
||||
- `workspace-hygiene-policy-d0ad8569490cf7d3fbf0fa6c` `.test-tmp` `windows_secondary` `apply` `file_locked` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-9cc8d387da8cc335efd68e3a` `.test-tmp` `windows_secondary` `apply` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-769cb1efd5f31fa84616585f` `.test-tmp` `windows_secondary` `apply` `central_write_denied` -> `retain_with_evidence` actions `write_status_artifact, update_semantic_sql`
|
||||
- `workspace-hygiene-policy-bf728d1d9afc76b2627a662a` `.test-tmp` `windows_secondary` `apply` `unknown` -> `retain_with_evidence` actions `write_status_artifact`
|
||||
- `workspace-hygiene-policy-049c398e0663b27fb5609985` `.test-tmp` `windows_secondary` `closeout` `none` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-0ff76ce14fffe9fe665251e1` `.test-tmp` `windows_secondary` `closeout` `not_found` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-1451df7001a804cc3ee552dc` `.test-tmp` `windows_secondary` `closeout` `permission_denied` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-2cc605680ae873c818865b33` `.test-tmp` `windows_secondary` `closeout` `directory_not_empty` -> `retain_with_evidence` actions `best_effort_delete, continue_after_child_error, write_status_artifact`
|
||||
- `workspace-hygiene-policy-cb226b7380849e26aa4d76c7` `.test-tmp` `windows_secondary` `closeout` `file_locked` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-63a68ccc0d018e2fe1546090` `.test-tmp` `windows_secondary` `closeout` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-20906dab1c11fbb4cebf2b62` `.test-tmp` `windows_secondary` `closeout` `central_write_denied` -> `retain_with_evidence` actions `write_status_artifact, update_semantic_sql`
|
||||
- `workspace-hygiene-policy-2221a710356ed9e7607ef7f3` `.test-tmp` `windows_secondary` `closeout` `unknown` -> `retain_with_evidence` actions `write_status_artifact`
|
||||
- `workspace-hygiene-policy-714c981b7e3ac0583c2a9b9f` `.test-tmp` `windows_secondary` `central_record` `none` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-c99a74dc347c76b2b57bb2e1` `.test-tmp` `windows_secondary` `central_record` `not_found` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-eb582edade7af46be10eea64` `.test-tmp` `windows_secondary` `central_record` `permission_denied` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-2acae1058db81e26fe74bf74` `.test-tmp` `windows_secondary` `central_record` `directory_not_empty` -> `retain_with_evidence` actions `best_effort_delete, continue_after_child_error, write_status_artifact`
|
||||
- `workspace-hygiene-policy-00a57f8e2a5229b50f48463f` `.test-tmp` `windows_secondary` `central_record` `file_locked` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-2d76fae86d19d0550d374952` `.test-tmp` `windows_secondary` `central_record` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-20ab0f83d53c3cd4c8a67f08` `.test-tmp` `windows_secondary` `central_record` `central_write_denied` -> `retain_with_evidence` actions `write_status_artifact, update_semantic_sql`
|
||||
- `workspace-hygiene-policy-83f282f1d7fe2cca0bd27934` `.test-tmp` `windows_secondary` `central_record` `unknown` -> `retain_with_evidence` actions `write_status_artifact`
|
||||
- `workspace-hygiene-policy-90f58c90392bba2d16c0dbd8` `.test-tmp` `codex_server` `inspect` `none` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-7694790458af6a1d5b93d85b` `.test-tmp` `codex_server` `inspect` `not_found` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-e830494add59daf1410a11a6` `.test-tmp` `codex_server` `inspect` `permission_denied` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-b120f9fd5fa901c522ef7d14` `.test-tmp` `codex_server` `inspect` `directory_not_empty` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-ebe8bd703ba081cef5c7d379` `.test-tmp` `codex_server` `inspect` `file_locked` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-7fe73882bbf81c6c267106d0` `.test-tmp` `codex_server` `inspect` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-b65b014821b311f4c5991681` `.test-tmp` `codex_server` `inspect` `central_write_denied` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-8049cd78fdda5b4b748f5074` `.test-tmp` `codex_server` `inspect` `unknown` -> `record_only` actions `verify_absent, write_status_artifact`
|
||||
- `workspace-hygiene-policy-584d53c3180c3596cb9021f0` `.test-tmp` `codex_server` `apply` `none` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-ab6e1d396d8421f67657dcf1` `.test-tmp` `codex_server` `apply` `not_found` -> `pass` actions `verify_absent`
|
||||
- `workspace-hygiene-policy-af6f8916bf733c4a553d50e3` `.test-tmp` `codex_server` `apply` `permission_denied` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-3b75f906ae19b7cac43af2f6` `.test-tmp` `codex_server` `apply` `directory_not_empty` -> `retain_with_evidence` actions `best_effort_delete, continue_after_child_error, write_status_artifact`
|
||||
- `workspace-hygiene-policy-6372da5f33fc0af7df08e000` `.test-tmp` `codex_server` `apply` `file_locked` -> `owner_action_required` actions `best_effort_delete, continue_after_child_error, escalate_owner, record_acl_exception`
|
||||
- `workspace-hygiene-policy-d95aca64f3bee2b9921624be` `.test-tmp` `codex_server` `apply` `unsafe_path` -> `block_unsafe` actions `block_before_write, write_status_artifact`
|
||||
- `workspace-hygiene-policy-d71b3e6ba8346080dd3288e2` `.test-tmp` `codex_server` `apply` `central_write_denied` -> `retain_with_evidence` actions `write_status_artifact, update_semantic_sql`
|
||||
- `workspace-hygiene-policy-0bcdd168427902e850455395` `.test-tmp` `codex_server` `apply` `unknown` -> `retain_with_evidence` actions `write_status_artifact`
|
||||
|
||||
## Regra operacional
|
||||
|
||||
- Remover somente artefatos locais aprovados.
|
||||
- Continuar a limpeza depois de erro de filho quando o caminho ainda estiver dentro do projeto.
|
||||
- Registrar WinError 5, arquivo em uso ou pasta nao vazia como pendencia de owner quando persistirem.
|
||||
- Nao apagar paths fora do project_root.
|
||||
@@ -1,7 +1,7 @@
|
||||
# Workspace Hygiene Report
|
||||
|
||||
- report_id: `workspace-hygiene-389155382838`
|
||||
- generated_at: `2026-05-02T09:27:04+00:00`
|
||||
- report_id: `workspace-hygiene-441283210879`
|
||||
- generated_at: `2026-05-02T10:25:29+00:00`
|
||||
- project_root: `G:\_codex-git\tudo-para-ia-mais-humana`
|
||||
- central_platform_folder: `G:\_codex-git\nucleo-gestao-operacional\central-de-ordem-de-servico\projects\15_repo_tudo-para-ia-mais-humana-platform`
|
||||
- status: `blocked`
|
||||
@@ -27,10 +27,10 @@
|
||||
- applied: `True`
|
||||
- deleted: `False`
|
||||
- exists_after: `True`
|
||||
- children_before: `3052`
|
||||
- bytes_before: `68130347`
|
||||
- children_before: `1186`
|
||||
- bytes_before: `10279`
|
||||
- note: artifact retained by ACL or filesystem lock
|
||||
- error: `PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpnk6r4sdb'`
|
||||
- error: `PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpnk6r4sdb'; PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpu13bzfhr'; OSError: [WinError 145] A pasta não está vazia: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp'`
|
||||
- footprint_errors:
|
||||
- `PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpnk6r4sdb'`
|
||||
- `PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpu13bzfhr'`
|
||||
@@ -49,4 +49,4 @@
|
||||
|
||||
## Blockers
|
||||
|
||||
- `python-test-temp:PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpnk6r4sdb'`
|
||||
- `python-test-temp:PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpnk6r4sdb'; PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpu13bzfhr'; OSError: [WinError 145] A pasta não está vazia: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp'`
|
||||
|
||||
1537
matrizes/workspace-hygiene-policy-cases.csv
Normal file
1537
matrizes/workspace-hygiene-policy-cases.csv
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
||||
target_id,path,action,status,applied,deleted,exists_after,children_before,bytes_before,error,note
|
||||
python-test-temp,G:\_codex-git\tudo-para-ia-mais-humana\.test-tmp,delete_directory,blocked,yes,no,yes,3052,68130347,PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpnk6r4sdb',artifact retained by ACL or filesystem lock
|
||||
python-test-temp,G:\_codex-git\tudo-para-ia-mais-humana\.test-tmp,delete_directory,blocked,yes,no,yes,1186,10279,PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpnk6r4sdb'; PermissionError: [WinError 5] Acesso negado: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp\\tmpu13bzfhr'; OSError: [WinError 145] A pasta não está vazia: 'G:\\_codex-git\\tudo-para-ia-mais-humana\\.test-tmp',artifact retained by ACL or filesystem lock
|
||||
node-dependencies,G:\_codex-git\tudo-para-ia-mais-humana\node_modules,verify_absent,not_found,no,no,no,0,0,,target already absent
|
||||
|
||||
|
@@ -52,6 +52,7 @@ from .scanner import environment_summary, scan_ecosystem
|
||||
from .storage import table_counts
|
||||
from .targeted_sync_audit import run_targeted_sync_audit
|
||||
from .workspace_hygiene import run_workspace_hygiene
|
||||
from .workspace_hygiene_policy import policy_payload, run_hygiene_policy
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
@@ -126,6 +127,10 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
hygiene.add_argument("--project-root", default="G:/_codex-git/tudo-para-ia-mais-humana")
|
||||
hygiene.add_argument("--central-platform-folder", default="")
|
||||
hygiene.add_argument("--apply", action="store_true")
|
||||
hygiene_policy = sub.add_parser("workspace-hygiene-policy", help="Write executable workspace cleanup and ACL retention policy.")
|
||||
hygiene_policy.add_argument("--project-root", default="G:/_codex-git/tudo-para-ia-mais-humana")
|
||||
hygiene_policy.add_argument("--no-generated", action="store_true")
|
||||
hygiene_policy.add_argument("--limit", type=int, default=40)
|
||||
sync_audit = sub.add_parser("targeted-sync-audit", help="Write safe Git synchronization audit for the active round repos.")
|
||||
sync_audit.add_argument("--project-root", default="G:/_codex-git/tudo-para-ia-mais-humana")
|
||||
sync_audit.add_argument("--mcp-repo-root", default="G:/_codex-git/tudo-para-ia-mcps-internos-plataform")
|
||||
@@ -557,6 +562,19 @@ def command_workspace_hygiene(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def command_workspace_hygiene_policy(args: argparse.Namespace) -> int:
|
||||
report, records = run_hygiene_policy(
|
||||
project_root=Path(args.project_root),
|
||||
use_generated=not bool(args.no_generated),
|
||||
)
|
||||
payload = {
|
||||
"report": policy_payload(report, limit_cases=int(args.limit)),
|
||||
"generatedFiles": [record.path for record in records],
|
||||
}
|
||||
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
def command_targeted_sync_audit(args: argparse.Namespace) -> int:
|
||||
central_platform_folder = Path(args.central_platform_folder) if args.central_platform_folder else None
|
||||
report, records = run_targeted_sync_audit(
|
||||
@@ -666,6 +684,8 @@ def main(argv: list[str] | None = None) -> int:
|
||||
return command_mcp_access_policy(args)
|
||||
if args.command == "workspace-hygiene":
|
||||
return command_workspace_hygiene(args)
|
||||
if args.command == "workspace-hygiene-policy":
|
||||
return command_workspace_hygiene_policy(args)
|
||||
if args.command == "targeted-sync-audit":
|
||||
return command_targeted_sync_audit(args)
|
||||
if args.command == "mcp-admin-route-acceptance":
|
||||
|
||||
38715
src/mais_humana/generated_workspace_hygiene_policy.py
Normal file
38715
src/mais_humana/generated_workspace_hygiene_policy.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,6 @@ import csv
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
@@ -196,13 +195,61 @@ def _footprint(path: Path, *, max_errors: int = 8) -> PathFootprint:
|
||||
return PathFootprint(True, True, False, child_count, byte_count, tuple(errors[:max_errors]))
|
||||
|
||||
|
||||
def _delete_file(path: Path, errors: list[str], *, max_errors: int = 12) -> None:
|
||||
try:
|
||||
if not path.exists():
|
||||
return
|
||||
path.unlink()
|
||||
except OSError as exc:
|
||||
if len(errors) < max_errors:
|
||||
errors.append(f"{type(exc).__name__}: {exc}")
|
||||
|
||||
|
||||
def _delete_empty_dir(path: Path, errors: list[str], *, max_errors: int = 12) -> None:
|
||||
try:
|
||||
if path.exists():
|
||||
path.rmdir()
|
||||
except OSError as exc:
|
||||
if len(errors) < max_errors:
|
||||
errors.append(f"{type(exc).__name__}: {exc}")
|
||||
|
||||
|
||||
def _delete_directory_best_effort(path: Path) -> tuple[bool, str]:
|
||||
"""Delete a directory while continuing after ACL errors.
|
||||
|
||||
``shutil.rmtree`` stops at the first inaccessible child on Windows. The
|
||||
closeout hygiene needs a better operational behavior: remove every
|
||||
accessible file/directory, retain only the ACL-blocked paths, and report the
|
||||
retained evidence.
|
||||
"""
|
||||
|
||||
errors: list[str] = []
|
||||
|
||||
def on_walk_error(exc: OSError) -> None:
|
||||
if len(errors) < 12:
|
||||
errors.append(f"{type(exc).__name__}: {exc}")
|
||||
|
||||
for current_root, dirnames, filenames in os.walk(path, topdown=False, onerror=on_walk_error):
|
||||
current = Path(current_root)
|
||||
for filename in filenames:
|
||||
_delete_file(current / filename, errors)
|
||||
for dirname in dirnames:
|
||||
_delete_empty_dir(current / dirname, errors)
|
||||
_delete_empty_dir(path, errors)
|
||||
if not path.exists():
|
||||
return True, ""
|
||||
message = "; ".join(merge_unique(errors))
|
||||
if not message:
|
||||
message = "path retained after best-effort cleanup"
|
||||
return False, message
|
||||
|
||||
|
||||
def _delete_path(path: Path) -> tuple[bool, str]:
|
||||
try:
|
||||
if not path.exists():
|
||||
return False, ""
|
||||
if path.is_dir():
|
||||
shutil.rmtree(path)
|
||||
return True, ""
|
||||
return _delete_directory_best_effort(path)
|
||||
path.unlink()
|
||||
return True, ""
|
||||
except OSError as exc:
|
||||
|
||||
470
src/mais_humana/workspace_hygiene_policy.py
Normal file
470
src/mais_humana/workspace_hygiene_policy.py
Normal file
@@ -0,0 +1,470 @@
|
||||
"""Workspace hygiene retention policy.
|
||||
|
||||
The closeout rule is simple to say and surprisingly easy to get wrong on
|
||||
Windows mirrors: remove local build/test artifacts, but keep going when ACLs
|
||||
retain a child path and record exactly what still needs an authorized owner.
|
||||
This module turns that rule into executable policy cases that can be exposed to
|
||||
MCP, tested by Codex, and attached to the service-order closeout.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import io
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, Sequence
|
||||
|
||||
from .models import GeneratedFile, as_plain_data, merge_unique, utc_now
|
||||
from .repository_mesh import stable_digest
|
||||
from .storage import connect, upsert_files
|
||||
|
||||
|
||||
class HygieneArtifactKind(str, Enum):
|
||||
"""Artifacts governed by the cleanup policy."""
|
||||
|
||||
PYTHON_TEST_TEMP = "python_test_temp"
|
||||
NODE_DEPENDENCIES = "node_dependencies"
|
||||
PYTHON_CACHE = "python_cache"
|
||||
WRANGLER_CACHE = "wrangler_cache"
|
||||
FRONTEND_CACHE = "frontend_cache"
|
||||
COVERAGE_OUTPUT = "coverage_output"
|
||||
TEST_REPORT = "test_report"
|
||||
LOG_OUTPUT = "log_output"
|
||||
|
||||
|
||||
class HygieneErrorKind(str, Enum):
|
||||
"""Filesystem or operational issue observed during cleanup."""
|
||||
|
||||
NONE = "none"
|
||||
NOT_FOUND = "not_found"
|
||||
PERMISSION_DENIED = "permission_denied"
|
||||
DIRECTORY_NOT_EMPTY = "directory_not_empty"
|
||||
FILE_LOCKED = "file_locked"
|
||||
UNSAFE_PATH = "unsafe_path"
|
||||
CENTRAL_WRITE_DENIED = "central_write_denied"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
|
||||
class HygieneExecutionMode(str, Enum):
|
||||
"""Execution mode for a cleanup case."""
|
||||
|
||||
INSPECT = "inspect"
|
||||
APPLY = "apply"
|
||||
CLOSEOUT = "closeout"
|
||||
CENTRAL_RECORD = "central_record"
|
||||
|
||||
|
||||
class HygieneEnvironment(str, Enum):
|
||||
"""Environment where a cleanup case can run."""
|
||||
|
||||
WINDOWS_PRIMARY = "windows_primary"
|
||||
WINDOWS_SECONDARY = "windows_secondary"
|
||||
CODEX_SERVER = "codex_server"
|
||||
GITLAB_SERVER = "gitlab_server"
|
||||
|
||||
|
||||
class HygienePolicyStatus(str, Enum):
|
||||
"""Decision for one hygiene policy case."""
|
||||
|
||||
PASS = "pass"
|
||||
RETAIN_WITH_EVIDENCE = "retain_with_evidence"
|
||||
OWNER_ACTION_REQUIRED = "owner_action_required"
|
||||
BLOCK_UNSAFE = "block_unsafe"
|
||||
RECORD_ONLY = "record_only"
|
||||
|
||||
|
||||
class HygieneRemediationAction(str, Enum):
|
||||
"""Action recommended by a policy case."""
|
||||
|
||||
VERIFY_ABSENT = "verify_absent"
|
||||
BEST_EFFORT_DELETE = "best_effort_delete"
|
||||
CONTINUE_AFTER_CHILD_ERROR = "continue_after_child_error"
|
||||
ESCALATE_OWNER = "escalate_owner"
|
||||
RECORD_ACL_EXCEPTION = "record_acl_exception"
|
||||
BLOCK_BEFORE_WRITE = "block_before_write"
|
||||
WRITE_STATUS_ARTIFACT = "write_status_artifact"
|
||||
UPDATE_SEMANTIC_SQL = "update_semantic_sql"
|
||||
|
||||
|
||||
ARTIFACT_TARGETS: tuple[tuple[HygieneArtifactKind, str, str], ...] = (
|
||||
(HygieneArtifactKind.PYTHON_TEST_TEMP, ".test-tmp", "scratch criado por testes Python e comandos de validacao"),
|
||||
(HygieneArtifactKind.NODE_DEPENDENCIES, "node_modules", "dependencias Node locais que nao devem sobreviver ao fechamento"),
|
||||
(HygieneArtifactKind.PYTHON_CACHE, "__pycache__", "cache Python recompilavel"),
|
||||
(HygieneArtifactKind.PYTHON_CACHE, ".pytest_cache", "cache pytest local"),
|
||||
(HygieneArtifactKind.WRANGLER_CACHE, ".wrangler", "cache local do Wrangler"),
|
||||
(HygieneArtifactKind.FRONTEND_CACHE, ".next", "build cache frontend"),
|
||||
(HygieneArtifactKind.FRONTEND_CACHE, ".vite", "build cache frontend"),
|
||||
(HygieneArtifactKind.COVERAGE_OUTPUT, "coverage", "saida local de cobertura"),
|
||||
(HygieneArtifactKind.TEST_REPORT, "test-results", "saida local de testes"),
|
||||
(HygieneArtifactKind.TEST_REPORT, "playwright-report", "relatorio local de browser tests"),
|
||||
(HygieneArtifactKind.TEST_REPORT, "blob-report", "artefato local de browser tests"),
|
||||
(HygieneArtifactKind.LOG_OUTPUT, "logs", "logs locais de execucao"),
|
||||
)
|
||||
|
||||
POLICY_TRANSIT_FIELDS: tuple[str, ...] = (
|
||||
"origin",
|
||||
"destination",
|
||||
"tool",
|
||||
"payload",
|
||||
"actor",
|
||||
"permission",
|
||||
"result",
|
||||
"traceId",
|
||||
"auditId",
|
||||
"timestamp",
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class HygienePolicyCase:
|
||||
"""One policy case for local artifact cleanup."""
|
||||
|
||||
case_id: str
|
||||
artifact_kind: HygieneArtifactKind
|
||||
relative_path: str
|
||||
environment: HygieneEnvironment
|
||||
execution_mode: HygieneExecutionMode
|
||||
error_kind: HygieneErrorKind
|
||||
status: HygienePolicyStatus
|
||||
remediation_actions: tuple[HygieneRemediationAction, ...]
|
||||
required_evidence: tuple[str, ...]
|
||||
mcp_transit_required: bool
|
||||
direct_delete_allowed: bool
|
||||
reason: str
|
||||
next_action: str
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return as_plain_data(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class HygienePolicyReport:
|
||||
"""Policy report used by CLI, Markdown, CSV, and tests."""
|
||||
|
||||
report_id: str
|
||||
generated_at: str
|
||||
cases: tuple[HygienePolicyCase, ...]
|
||||
summary: tuple[str, ...]
|
||||
|
||||
@property
|
||||
def cases_count(self) -> int:
|
||||
return len(self.cases)
|
||||
|
||||
@property
|
||||
def owner_action_count(self) -> int:
|
||||
return sum(1 for case in self.cases if case.status == HygienePolicyStatus.OWNER_ACTION_REQUIRED)
|
||||
|
||||
@property
|
||||
def unsafe_block_count(self) -> int:
|
||||
return sum(1 for case in self.cases if case.status == HygienePolicyStatus.BLOCK_UNSAFE)
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
data = as_plain_data(self)
|
||||
data["cases_count"] = self.cases_count
|
||||
data["owner_action_count"] = self.owner_action_count
|
||||
data["unsafe_block_count"] = self.unsafe_block_count
|
||||
return data
|
||||
|
||||
|
||||
def classify_policy_case(
|
||||
*,
|
||||
artifact_kind: HygieneArtifactKind,
|
||||
relative_path: str,
|
||||
environment: HygieneEnvironment,
|
||||
execution_mode: HygieneExecutionMode,
|
||||
error_kind: HygieneErrorKind,
|
||||
) -> tuple[HygienePolicyStatus, tuple[HygieneRemediationAction, ...], bool, str, str]:
|
||||
"""Classify one cleanup situation into an auditable policy decision."""
|
||||
|
||||
if error_kind == HygieneErrorKind.UNSAFE_PATH:
|
||||
return (
|
||||
HygienePolicyStatus.BLOCK_UNSAFE,
|
||||
(HygieneRemediationAction.BLOCK_BEFORE_WRITE, HygieneRemediationAction.WRITE_STATUS_ARTIFACT),
|
||||
False,
|
||||
"path calculado sai do project_root ou cruza boundary de volume",
|
||||
"bloquear antes de qualquer escrita e registrar evidencia",
|
||||
)
|
||||
if execution_mode == HygieneExecutionMode.INSPECT:
|
||||
return (
|
||||
HygienePolicyStatus.RECORD_ONLY,
|
||||
(HygieneRemediationAction.VERIFY_ABSENT, HygieneRemediationAction.WRITE_STATUS_ARTIFACT),
|
||||
False,
|
||||
"modo inspecao nunca apaga artefato",
|
||||
"registrar footprint e executar apply apenas no fechamento",
|
||||
)
|
||||
if error_kind in {HygieneErrorKind.NONE, HygieneErrorKind.NOT_FOUND}:
|
||||
return (
|
||||
HygienePolicyStatus.PASS,
|
||||
(HygieneRemediationAction.VERIFY_ABSENT,),
|
||||
True,
|
||||
"artefato ausente ou removivel por limpeza local aprovada",
|
||||
"manter ausente e registrar status",
|
||||
)
|
||||
if error_kind in {HygieneErrorKind.PERMISSION_DENIED, HygieneErrorKind.FILE_LOCKED}:
|
||||
return (
|
||||
HygienePolicyStatus.OWNER_ACTION_REQUIRED,
|
||||
(
|
||||
HygieneRemediationAction.BEST_EFFORT_DELETE,
|
||||
HygieneRemediationAction.CONTINUE_AFTER_CHILD_ERROR,
|
||||
HygieneRemediationAction.ESCALATE_OWNER,
|
||||
HygieneRemediationAction.RECORD_ACL_EXCEPTION,
|
||||
),
|
||||
True,
|
||||
"ACL ou processo externo reteve caminho depois de remover filhos acessiveis",
|
||||
"registrar caminho retido e solicitar owner autorizado",
|
||||
)
|
||||
if error_kind == HygieneErrorKind.DIRECTORY_NOT_EMPTY:
|
||||
return (
|
||||
HygienePolicyStatus.RETAIN_WITH_EVIDENCE,
|
||||
(
|
||||
HygieneRemediationAction.BEST_EFFORT_DELETE,
|
||||
HygieneRemediationAction.CONTINUE_AFTER_CHILD_ERROR,
|
||||
HygieneRemediationAction.WRITE_STATUS_ARTIFACT,
|
||||
),
|
||||
True,
|
||||
"diretorio ainda contem filho retido por erro anterior",
|
||||
"reduzir conteudo acessivel e registrar filhos retidos",
|
||||
)
|
||||
if error_kind == HygieneErrorKind.CENTRAL_WRITE_DENIED:
|
||||
return (
|
||||
HygienePolicyStatus.RETAIN_WITH_EVIDENCE,
|
||||
(HygieneRemediationAction.WRITE_STATUS_ARTIFACT, HygieneRemediationAction.UPDATE_SEMANTIC_SQL),
|
||||
False,
|
||||
"pasta central recusou escrita, mas projeto real deve registrar status",
|
||||
"manter fallback no projeto real e registrar pendencia central",
|
||||
)
|
||||
return (
|
||||
HygienePolicyStatus.RETAIN_WITH_EVIDENCE,
|
||||
(HygieneRemediationAction.WRITE_STATUS_ARTIFACT,),
|
||||
False,
|
||||
"erro desconhecido deve ser evidenciado antes de nova tentativa",
|
||||
"registrar erro bruto redigido e reavaliar proxima rodada",
|
||||
)
|
||||
|
||||
|
||||
def build_policy_cases() -> tuple[HygienePolicyCase, ...]:
|
||||
"""Build the deterministic matrix of hygiene policy cases."""
|
||||
|
||||
cases: list[HygienePolicyCase] = []
|
||||
for artifact_kind, relative_path, description in ARTIFACT_TARGETS:
|
||||
for environment in HygieneEnvironment:
|
||||
for execution_mode in HygieneExecutionMode:
|
||||
for error_kind in HygieneErrorKind:
|
||||
status, actions, direct_delete_allowed, reason, next_action = classify_policy_case(
|
||||
artifact_kind=artifact_kind,
|
||||
relative_path=relative_path,
|
||||
environment=environment,
|
||||
execution_mode=execution_mode,
|
||||
error_kind=error_kind,
|
||||
)
|
||||
seed = {
|
||||
"artifact": artifact_kind.value,
|
||||
"path": relative_path,
|
||||
"environment": environment.value,
|
||||
"mode": execution_mode.value,
|
||||
"error": error_kind.value,
|
||||
}
|
||||
evidence = (
|
||||
"footprint_before",
|
||||
"footprint_after",
|
||||
"git_status_short",
|
||||
"node_modules_absent",
|
||||
"acl_error_excerpt" if error_kind != HygieneErrorKind.NONE else "no_error",
|
||||
description,
|
||||
)
|
||||
cases.append(
|
||||
HygienePolicyCase(
|
||||
case_id=f"workspace-hygiene-policy-{stable_digest(seed, 24)}",
|
||||
artifact_kind=artifact_kind,
|
||||
relative_path=relative_path,
|
||||
environment=environment,
|
||||
execution_mode=execution_mode,
|
||||
error_kind=error_kind,
|
||||
status=status,
|
||||
remediation_actions=actions,
|
||||
required_evidence=evidence,
|
||||
mcp_transit_required=True,
|
||||
direct_delete_allowed=direct_delete_allowed,
|
||||
reason=reason,
|
||||
next_action=next_action,
|
||||
)
|
||||
)
|
||||
return tuple(cases)
|
||||
|
||||
|
||||
def build_hygiene_policy_cases(*, use_generated: bool = True) -> tuple[HygienePolicyCase, ...]:
|
||||
"""Return generated policy cases when available, otherwise build runtime cases."""
|
||||
|
||||
if use_generated:
|
||||
try:
|
||||
from .generated_workspace_hygiene_policy import iter_policy_cases
|
||||
|
||||
return tuple(iter_policy_cases())
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
return build_policy_cases()
|
||||
|
||||
|
||||
def build_hygiene_policy_report(*, use_generated: bool = True) -> HygienePolicyReport:
|
||||
"""Build a compact policy report."""
|
||||
|
||||
cases = build_hygiene_policy_cases(use_generated=use_generated)
|
||||
summary = (
|
||||
f"Policy cases: {len(cases)}.",
|
||||
f"Owner action cases: {sum(1 for case in cases if case.status == HygienePolicyStatus.OWNER_ACTION_REQUIRED)}.",
|
||||
f"Unsafe path blocks: {sum(1 for case in cases if case.status == HygienePolicyStatus.BLOCK_UNSAFE)}.",
|
||||
"Best-effort cleanup must continue after child ACL errors and retain only inaccessible paths.",
|
||||
)
|
||||
return HygienePolicyReport(
|
||||
report_id=f"workspace-hygiene-policy-{stable_digest([case.case_id for case in cases], 16)}",
|
||||
generated_at=utc_now(),
|
||||
cases=cases,
|
||||
summary=summary,
|
||||
)
|
||||
|
||||
|
||||
def policy_payload(report: HygienePolicyReport, *, limit_cases: int = 80) -> dict[str, Any]:
|
||||
"""Return compact JSON payload for the policy report."""
|
||||
|
||||
payload = report.to_dict()
|
||||
payload["transit_fields"] = list(POLICY_TRANSIT_FIELDS)
|
||||
payload["cases"] = [case.to_dict() for case in report.cases[: max(0, limit_cases)]]
|
||||
payload["cases_total"] = len(report.cases)
|
||||
return payload
|
||||
|
||||
|
||||
def policy_case_rows(cases: Sequence[HygienePolicyCase]) -> list[list[str]]:
|
||||
"""Return CSV rows for policy cases."""
|
||||
|
||||
rows = [
|
||||
[
|
||||
"case_id",
|
||||
"artifact_kind",
|
||||
"relative_path",
|
||||
"environment",
|
||||
"execution_mode",
|
||||
"error_kind",
|
||||
"status",
|
||||
"remediation_actions",
|
||||
"direct_delete_allowed",
|
||||
"reason",
|
||||
"next_action",
|
||||
]
|
||||
]
|
||||
for case in cases:
|
||||
rows.append(
|
||||
[
|
||||
case.case_id,
|
||||
case.artifact_kind.value,
|
||||
case.relative_path,
|
||||
case.environment.value,
|
||||
case.execution_mode.value,
|
||||
case.error_kind.value,
|
||||
case.status.value,
|
||||
" | ".join(action.value for action in case.remediation_actions),
|
||||
"yes" if case.direct_delete_allowed else "no",
|
||||
case.reason,
|
||||
case.next_action,
|
||||
]
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def rows_to_csv(rows: Sequence[Sequence[str]]) -> str:
|
||||
"""Serialize rows to CSV text."""
|
||||
|
||||
buffer = io.StringIO()
|
||||
writer = csv.writer(buffer, lineterminator="\n")
|
||||
writer.writerows(rows)
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
def policy_markdown(report: HygienePolicyReport, *, limit_cases: int = 40) -> str:
|
||||
"""Render the policy report in Markdown."""
|
||||
|
||||
lines = [
|
||||
"# Politica de higiene de workspace",
|
||||
"",
|
||||
f"- report_id: `{report.report_id}`",
|
||||
f"- generated_at: `{report.generated_at}`",
|
||||
f"- cases: `{report.cases_count}`",
|
||||
f"- owner_action_cases: `{report.owner_action_count}`",
|
||||
f"- unsafe_block_cases: `{report.unsafe_block_count}`",
|
||||
"",
|
||||
"## Resumo",
|
||||
"",
|
||||
]
|
||||
lines.extend(f"- {item}" for item in report.summary)
|
||||
lines.extend(["", "## Casos amostrais", ""])
|
||||
for case in report.cases[: max(0, limit_cases)]:
|
||||
actions = ", ".join(action.value for action in case.remediation_actions)
|
||||
lines.append(
|
||||
f"- `{case.case_id}` `{case.relative_path}` `{case.environment.value}` `{case.execution_mode.value}` "
|
||||
f"`{case.error_kind.value}` -> `{case.status.value}` actions `{actions}`"
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
"## Regra operacional",
|
||||
"",
|
||||
"- Remover somente artefatos locais aprovados.",
|
||||
"- Continuar a limpeza depois de erro de filho quando o caminho ainda estiver dentro do projeto.",
|
||||
"- Registrar WinError 5, arquivo em uso ou pasta nao vazia como pendencia de owner quando persistirem.",
|
||||
"- Nao apagar paths fora do project_root.",
|
||||
"",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def generated_files(project_root: Path) -> tuple[GeneratedFile, ...]:
|
||||
"""Return semantic records for policy artifacts."""
|
||||
|
||||
relation = "0041_EXECUTIVA__resolver-test-tmp-bloqueado-por-acl"
|
||||
specs = (
|
||||
("dados/workspace-hygiene-policy.json", "Politica JSON de retencao e limpeza de workspace.", "workspace hygiene policy", "json"),
|
||||
("matrizes/workspace-hygiene-policy-cases.csv", "Matriz de casos de higiene e ACL.", "workspace hygiene policy cases", "csv"),
|
||||
("ecossistema/WORKSPACE-HYGIENE-POLICY.md", "Relatorio humano da politica de higiene.", "workspace hygiene policy report", "markdown"),
|
||||
)
|
||||
return tuple(
|
||||
GeneratedFile(
|
||||
path=str(project_root / relative),
|
||||
description=description,
|
||||
function=function,
|
||||
file_type=file_type,
|
||||
changed_by="mais_humana.workspace_hygiene_policy",
|
||||
change_summary="Criada politica executavel para limpeza parcial, ACL, owner action e evidencia de fechamento.",
|
||||
relation_to_order=relation,
|
||||
)
|
||||
for relative, description, function, file_type in specs
|
||||
)
|
||||
|
||||
|
||||
def write_policy_artifacts(report: HygienePolicyReport, project_root: Path) -> tuple[GeneratedFile, ...]:
|
||||
"""Write policy JSON, CSV, Markdown, and semantic file records."""
|
||||
|
||||
targets = (
|
||||
(project_root / "dados" / "workspace-hygiene-policy.json", json.dumps(policy_payload(report, limit_cases=160), ensure_ascii=False, indent=2, sort_keys=True)),
|
||||
(project_root / "matrizes" / "workspace-hygiene-policy-cases.csv", rows_to_csv(policy_case_rows(report.cases))),
|
||||
(project_root / "ecossistema" / "WORKSPACE-HYGIENE-POLICY.md", policy_markdown(report, limit_cases=80)),
|
||||
)
|
||||
for path, text in targets:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(text, encoding="utf-8")
|
||||
records = generated_files(project_root)
|
||||
with connect(project_root / "controle-semantico.sqlite") as conn:
|
||||
upsert_files(conn, records)
|
||||
conn.commit()
|
||||
return records
|
||||
|
||||
|
||||
def run_hygiene_policy(project_root: Path, *, use_generated: bool = True) -> tuple[HygienePolicyReport, tuple[GeneratedFile, ...]]:
|
||||
"""Build and persist the workspace hygiene policy artifacts."""
|
||||
|
||||
report = build_hygiene_policy_report(use_generated=use_generated)
|
||||
records = write_policy_artifacts(report, project_root)
|
||||
return report, records
|
||||
|
||||
64
tests/test_workspace_hygiene_policy.py
Normal file
64
tests/test_workspace_hygiene_policy.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from mais_humana.cli import main
|
||||
from mais_humana.storage import table_counts
|
||||
from mais_humana.workspace_hygiene_policy import (
|
||||
HygieneErrorKind,
|
||||
HygieneExecutionMode,
|
||||
HygienePolicyStatus,
|
||||
build_hygiene_policy_report,
|
||||
build_policy_cases,
|
||||
policy_payload,
|
||||
)
|
||||
from tests.helpers import make_tmp
|
||||
|
||||
|
||||
class WorkspaceHygienePolicyTests(unittest.TestCase):
|
||||
def test_runtime_policy_contains_acl_owner_cases(self) -> None:
|
||||
report = build_hygiene_policy_report(use_generated=False)
|
||||
|
||||
self.assertGreaterEqual(report.cases_count, 1000)
|
||||
self.assertGreater(report.owner_action_count, 0)
|
||||
self.assertGreater(report.unsafe_block_count, 0)
|
||||
self.assertTrue(
|
||||
any(
|
||||
case.error_kind == HygieneErrorKind.PERMISSION_DENIED
|
||||
and case.execution_mode == HygieneExecutionMode.APPLY
|
||||
and case.status == HygienePolicyStatus.OWNER_ACTION_REQUIRED
|
||||
for case in report.cases
|
||||
)
|
||||
)
|
||||
|
||||
def test_payload_is_limited_but_keeps_totals(self) -> None:
|
||||
report = build_hygiene_policy_report(use_generated=False)
|
||||
payload = policy_payload(report, limit_cases=7)
|
||||
|
||||
self.assertEqual(len(payload["cases"]), 7)
|
||||
self.assertEqual(payload["cases_total"], report.cases_count)
|
||||
self.assertIn("traceId", payload["transit_fields"])
|
||||
|
||||
def test_generated_policy_matches_runtime_case_count(self) -> None:
|
||||
runtime_count = len(build_policy_cases())
|
||||
generated = build_hygiene_policy_report(use_generated=True)
|
||||
|
||||
self.assertEqual(generated.cases_count, runtime_count)
|
||||
|
||||
def test_cli_writes_policy_artifacts_and_sql(self) -> None:
|
||||
project = make_tmp()
|
||||
code = main(["workspace-hygiene-policy", "--project-root", str(project), "--no-generated", "--limit", "5"])
|
||||
|
||||
self.assertEqual(code, 0)
|
||||
self.assertTrue((project / "dados" / "workspace-hygiene-policy.json").exists())
|
||||
self.assertTrue((project / "matrizes" / "workspace-hygiene-policy-cases.csv").exists())
|
||||
self.assertTrue((project / "ecossistema" / "WORKSPACE-HYGIENE-POLICY.md").exists())
|
||||
payload = json.loads((project / "dados" / "workspace-hygiene-policy.json").read_text(encoding="utf-8"))
|
||||
self.assertGreaterEqual(payload["cases_total"], 1000)
|
||||
self.assertGreaterEqual(table_counts(project / "controle-semantico.sqlite").get("files", 0), 3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
120
tools/generate_workspace_hygiene_policy.py
Normal file
120
tools/generate_workspace_hygiene_policy.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""Generate importable workspace hygiene policy cases."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SRC = ROOT / "src"
|
||||
OUTPUT = SRC / "mais_humana" / "generated_workspace_hygiene_policy.py"
|
||||
|
||||
|
||||
def ensure_import_path() -> None:
|
||||
import sys
|
||||
|
||||
src = str(SRC)
|
||||
if src not in sys.path:
|
||||
sys.path.insert(0, src)
|
||||
|
||||
|
||||
def q(value: object) -> str:
|
||||
return repr(str(value))
|
||||
|
||||
|
||||
def bool_literal(value: bool) -> str:
|
||||
return "True" if value else "False"
|
||||
|
||||
|
||||
def tuple_enum(values: object) -> str:
|
||||
items = list(values)
|
||||
if not items:
|
||||
return "()"
|
||||
lines = ["("]
|
||||
for item in items:
|
||||
lines.append(f" HygieneRemediationAction.{getattr(item, 'name')},")
|
||||
lines.append(" )")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def tuple_str(values: object) -> str:
|
||||
items = [str(value) for value in values]
|
||||
if not items:
|
||||
return "()"
|
||||
lines = ["("]
|
||||
for item in items:
|
||||
lines.append(f" {q(item)},")
|
||||
lines.append(" )")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def case_block(case: object) -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
" HygienePolicyCase(",
|
||||
f" case_id={q(getattr(case, 'case_id'))},",
|
||||
f" artifact_kind=HygieneArtifactKind.{getattr(case, 'artifact_kind').name},",
|
||||
f" relative_path={q(getattr(case, 'relative_path'))},",
|
||||
f" environment=HygieneEnvironment.{getattr(case, 'environment').name},",
|
||||
f" execution_mode=HygieneExecutionMode.{getattr(case, 'execution_mode').name},",
|
||||
f" error_kind=HygieneErrorKind.{getattr(case, 'error_kind').name},",
|
||||
f" status=HygienePolicyStatus.{getattr(case, 'status').name},",
|
||||
" remediation_actions=" + tuple_enum(getattr(case, "remediation_actions")) + ",",
|
||||
" required_evidence=" + tuple_str(getattr(case, "required_evidence")) + ",",
|
||||
f" mcp_transit_required={bool_literal(getattr(case, 'mcp_transit_required'))},",
|
||||
f" direct_delete_allowed={bool_literal(getattr(case, 'direct_delete_allowed'))},",
|
||||
f" reason={q(getattr(case, 'reason'))},",
|
||||
f" next_action={q(getattr(case, 'next_action'))},",
|
||||
" ),",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ensure_import_path()
|
||||
from mais_humana.workspace_hygiene_policy import build_policy_cases
|
||||
|
||||
cases = build_policy_cases()
|
||||
lines = [
|
||||
'"""Generated workspace hygiene policy cases.',
|
||||
"",
|
||||
"Do not edit this file by hand. Regenerate with:",
|
||||
"",
|
||||
" python tools/generate_workspace_hygiene_policy.py",
|
||||
'"""',
|
||||
"",
|
||||
"from __future__ import annotations",
|
||||
"",
|
||||
"from .workspace_hygiene_policy import (",
|
||||
" HygieneArtifactKind,",
|
||||
" HygieneEnvironment,",
|
||||
" HygieneErrorKind,",
|
||||
" HygieneExecutionMode,",
|
||||
" HygienePolicyCase,",
|
||||
" HygienePolicyStatus,",
|
||||
" HygieneRemediationAction,",
|
||||
")",
|
||||
"",
|
||||
f"GENERATED_POLICY_CASES_COUNT = {len(cases)}",
|
||||
"",
|
||||
"POLICY_CASES = (",
|
||||
]
|
||||
lines.extend(case_block(case) for case in cases)
|
||||
lines.extend(
|
||||
[
|
||||
")",
|
||||
"",
|
||||
"",
|
||||
"def iter_policy_cases():",
|
||||
" return POLICY_CASES",
|
||||
"",
|
||||
]
|
||||
)
|
||||
OUTPUT.write_text("\n".join(lines), encoding="utf-8")
|
||||
print(f"generated {OUTPUT} cases={len(cases)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
Reference in New Issue
Block a user