xrefkit 0.7.6__tar.gz → 0.7.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. {xrefkit-0.7.6/xrefkit.egg-info → xrefkit-0.7.7}/PKG-INFO +1 -1
  2. {xrefkit-0.7.6 → xrefkit-0.7.7}/pyproject.toml +1 -1
  3. xrefkit-0.7.7/tests/test_governance_host.py +248 -0
  4. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/__init__.py +1 -1
  5. xrefkit-0.7.7/xrefkit/governance_host.py +378 -0
  6. {xrefkit-0.7.6 → xrefkit-0.7.7/xrefkit.egg-info}/PKG-INFO +1 -1
  7. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit.egg-info/SOURCES.txt +2 -0
  8. {xrefkit-0.7.6 → xrefkit-0.7.7}/LICENSE +0 -0
  9. {xrefkit-0.7.6 → xrefkit-0.7.7}/README.md +0 -0
  10. {xrefkit-0.7.6 → xrefkit-0.7.7}/setup.cfg +0 -0
  11. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_artifact_selected_family.py +0 -0
  12. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_attention_pet.py +0 -0
  13. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_attention_pet_client_protocol.py +0 -0
  14. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_attention_pet_codex_session.py +0 -0
  15. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_attention_pet_fit.py +0 -0
  16. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_azure_connection.py +0 -0
  17. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_azure_import.py +0 -0
  18. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_azure_recovery.py +0 -0
  19. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_azure_update_candidate.py +0 -0
  20. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_azure_writer.py +0 -0
  21. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_boundary_analysis.py +0 -0
  22. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_brownfield_csharp_structure_definitions.py +0 -0
  23. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_brownfield_file_editing_protocol.py +0 -0
  24. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_business_intake_conversation_definitions.py +0 -0
  25. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_business_intake_definition.py +0 -0
  26. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_calibration_lint.py +0 -0
  27. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_catalog_preparation_definition.py +0 -0
  28. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_check_skill_knowledge_xids.py +0 -0
  29. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_ci_workflow_invariants.py +0 -0
  30. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_cli.py +0 -0
  31. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_code_constraint_definition.py +0 -0
  32. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_collect_analyzer_sarif.py +0 -0
  33. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_compact_skill_logs.py +0 -0
  34. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_constraint_derivation_family_definition.py +0 -0
  35. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_constraint_derivation_selected_family.py +0 -0
  36. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_contribution_adoption_bytes.py +0 -0
  37. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_contribution_returns.py +0 -0
  38. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_convert_to_xrefkit_skill.py +0 -0
  39. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_cs_scope_probe.py +0 -0
  40. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_csharp_commonality.py +0 -0
  41. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_csharp_naming_profile.py +0 -0
  42. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_ctx.py +0 -0
  43. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_cutover_readiness.py +0 -0
  44. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_dashboard.py +0 -0
  45. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_database_batch_definition_migration.py +0 -0
  46. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_decision_trace.py +0 -0
  47. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_design_business_intake_selected_family.py +0 -0
  48. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_design_planning_definition_migration.py +0 -0
  49. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_dotnet_definition_candidate.py +0 -0
  50. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_editorial_family_definition.py +0 -0
  51. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_editorial_intake_definition.py +0 -0
  52. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_editorial_selected_family.py +0 -0
  53. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_embedded_startup_pack.py +0 -0
  54. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_error_policy_audit.py +0 -0
  55. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_error_policy_locator.py +0 -0
  56. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_execution_binding.py +0 -0
  57. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_existing_skill_definition_batch.py +0 -0
  58. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_final_skilldefinition_batch.py +0 -0
  59. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_flow_selected_family.py +0 -0
  60. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_fm_multiroot.py +0 -0
  61. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_gate.py +0 -0
  62. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_gateway.py +0 -0
  63. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_gateway_mcp.py +0 -0
  64. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_gateway_skill_adapter.py +0 -0
  65. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_gateway_work_items.py +0 -0
  66. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_goal_desired_state.py +0 -0
  67. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_governance_entry.py +0 -0
  68. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_host_precheck.py +0 -0
  69. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_human_evaluation.py +0 -0
  70. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_implementation_flow_definition.py +0 -0
  71. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_implementation_review_selected_family.py +0 -0
  72. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_inbound_uploads.py +0 -0
  73. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_instruction_workflow.py +0 -0
  74. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_knowledge_relations_validator.py +0 -0
  75. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_language_review_definition.py +0 -0
  76. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_legacy_guidance.py +0 -0
  77. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_legacy_migration.py +0 -0
  78. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_legacy_resources.py +0 -0
  79. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_mcp_package_routing.py +0 -0
  80. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_mcp_setup.py +0 -0
  81. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_mcp_subagent_startup.py +0 -0
  82. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_mcp_subagent_startup_integration.py +0 -0
  83. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_ontology_assessment_validator.py +0 -0
  84. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_os_family_definition.py +0 -0
  85. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_os_knowledge_family_definition.py +0 -0
  86. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_os_selected_family.py +0 -0
  87. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_ownership.py +0 -0
  88. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_package_skill_run.py +0 -0
  89. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_packaged_native_governance.py +0 -0
  90. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_packmeta.py +0 -0
  91. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_plan_markdown.py +0 -0
  92. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_plan_observation.py +0 -0
  93. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_project_management_materialization.py +0 -0
  94. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_project_quality_baseline.py +0 -0
  95. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_qa_report_definition.py +0 -0
  96. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_reporting_profiles.py +0 -0
  97. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_reporting_quality.py +0 -0
  98. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_repository_skill_adoption.py +0 -0
  99. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_repository_startup_pack.py +0 -0
  100. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_resource_provider.py +0 -0
  101. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_runtime_contracts.py +0 -0
  102. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_sarif_to_locator.py +0 -0
  103. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_security_definition_migration.py +0 -0
  104. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_site_release_metadata.py +0 -0
  105. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_definition.py +0 -0
  106. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_definition_governance.py +0 -0
  107. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_definition_mcp.py +0 -0
  108. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_definition_runtime.py +0 -0
  109. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_edits.py +0 -0
  110. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_maturity_return.py +0 -0
  111. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_release_compatibility.py +0 -0
  112. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skill_runtime_audit.py +0 -0
  113. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skillmeta.py +0 -0
  114. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_skills_sync.py +0 -0
  115. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_source_analysis_fragments.py +0 -0
  116. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_structure_catalog.py +0 -0
  117. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_subagent_startup.py +0 -0
  118. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_subagent_startup_boundaries.py +0 -0
  119. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_wbs.py +0 -0
  120. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_work_management.py +0 -0
  121. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_work_packages.py +0 -0
  122. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_workflow_family_definition.py +0 -0
  123. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_xref.py +0 -0
  124. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_xrefkit_instance.py +0 -0
  125. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_xrefkit_tools.py +0 -0
  126. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_xrefkit_v2_discovery.py +0 -0
  127. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_xrefkit_v2_models.py +0 -0
  128. {xrefkit-0.7.6 → xrefkit-0.7.7}/tests/test_xrefkit_v2_pipeline.py +0 -0
  129. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/__main__.py +0 -0
  130. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/__init__.py +0 -0
  131. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/__main__.py +0 -0
  132. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/client.py +0 -0
  133. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/client_protocol.py +0 -0
  134. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/codex_session.py +0 -0
  135. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/evaluator.py +0 -0
  136. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/model.py +0 -0
  137. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/presentation.py +0 -0
  138. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/profiles.py +0 -0
  139. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/scenarios.py +0 -0
  140. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/server.py +0 -0
  141. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/attention_pet/store.py +0 -0
  142. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/azure_connection.py +0 -0
  143. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/azure_import.py +0 -0
  144. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/azure_recovery.py +0 -0
  145. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/azure_update_candidate.py +0 -0
  146. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/azure_writer.py +0 -0
  147. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/boundary_analysis.py +0 -0
  148. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/catalog_cli.py +0 -0
  149. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/cli.py +0 -0
  150. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/contracts.py +0 -0
  151. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/ctx.py +0 -0
  152. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/dashboard.py +0 -0
  153. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/decision_trace.py +0 -0
  154. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/discovery.py +0 -0
  155. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/execution_binding.py +0 -0
  156. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/execution_binding_cli.py +0 -0
  157. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/gate.py +0 -0
  158. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/gateway.py +0 -0
  159. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/goalstate.py +0 -0
  160. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/governance_entry.py +0 -0
  161. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/governance_entry_cli.py +0 -0
  162. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/hashing.py +0 -0
  163. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/host_precheck.py +0 -0
  164. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/import_skill.py +0 -0
  165. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/instance.py +0 -0
  166. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/loaders.py +0 -0
  167. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/__init__.py +0 -0
  168. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/audit.py +0 -0
  169. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/bootstrap.py +0 -0
  170. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/catalog.py +0 -0
  171. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/cli.py +0 -0
  172. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/client_cache.py +0 -0
  173. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/client_flow.py +0 -0
  174. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/context_registry.py +0 -0
  175. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/context_token.py +0 -0
  176. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/contracts.py +0 -0
  177. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/contribution_adoption.py +0 -0
  178. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/contribution_returns.py +0 -0
  179. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/dist.py +0 -0
  180. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/gateway.py +0 -0
  181. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/governed_overlays.py +0 -0
  182. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/inbound_uploads.py +0 -0
  183. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/knowledge_edits.py +0 -0
  184. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/legacy_guidance.py +0 -0
  185. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/legacy_migration.py +0 -0
  186. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/legacy_resources.py +0 -0
  187. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/ownership.py +0 -0
  188. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/repository.py +0 -0
  189. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/schemas.py +0 -0
  190. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/server.py +0 -0
  191. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/setup.py +0 -0
  192. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/skill_edits.py +0 -0
  193. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/skill_maturity.py +0 -0
  194. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/startup_contract_pack.py +0 -0
  195. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/subagent_startup.py +0 -0
  196. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp/update_gate_intake.py +0 -0
  197. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/mcp_tools.py +0 -0
  198. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/__init__.py +0 -0
  199. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/common.py +0 -0
  200. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/effective_bundle.py +0 -0
  201. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/human_evaluation.py +0 -0
  202. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/local_manifest.py +0 -0
  203. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/package_manifest.py +0 -0
  204. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/run_log.py +0 -0
  205. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/server_config.py +0 -0
  206. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/models/skill_definition.py +0 -0
  207. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/operations_cli.py +0 -0
  208. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/ownership.py +0 -0
  209. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/packmeta.py +0 -0
  210. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/plan_markdown.py +0 -0
  211. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/plan_observation.py +0 -0
  212. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/registry.py +0 -0
  213. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/reporting.py +0 -0
  214. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/reporting_profiles.py +0 -0
  215. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/repository_skills.py +0 -0
  216. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resolver.py +0 -0
  217. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resource_provider.py +0 -0
  218. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/attention_pet/client-state.schema.json +0 -0
  219. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/attention_pet/pet.css +0 -0
  220. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/attention_pet/pet.html +0 -0
  221. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/attention_pet/pet.js +0 -0
  222. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/contracts.json +0 -0
  223. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/current.json +0 -0
  224. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
  225. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
  226. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
  227. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
  228. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/model_body.md +0 -0
  229. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/startup_sources/000_agent_entry.md +0 -0
  230. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/startup_sources/011_startup_xref_routing.md +0 -0
  231. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/startup_sources/015_shared_memory_operations.md +0 -0
  232. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/startup_sources/016_uncertainty_protocol.md +0 -0
  233. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/startup_sources/017_base_and_xref_layering.md +0 -0
  234. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/startup_sources/053_context_direction_security_guard.md +0 -0
  235. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/base/startup_sources/yomiyasu_LICENSE.txt +0 -0
  236. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/migration/docs/guides/062_legacy_flow_skill_migration_guide.md +0 -0
  237. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/migration/knowledge/operations/130_legacy_flow_skill_migration_rules.md +0 -0
  238. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/migration/knowledge/organization/160_context_direction_guard_rules.md +0 -0
  239. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/migration/skills/os/legacy_flow_skill_migration/SKILL.v1.md +0 -0
  240. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/migration/skills/os/legacy_flow_skill_migration/references/legacy_flow_skill_migration_template.md +0 -0
  241. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/migration/skills/repository_adoption.json +0 -0
  242. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/resources/migration/tools/migrate_legacy_flow_skill.py +0 -0
  243. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/runlog.py +0 -0
  244. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/skill_definition.py +0 -0
  245. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/skill_definition_catalog.py +0 -0
  246. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/skill_definition_governance.py +0 -0
  247. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/skillmeta.py +0 -0
  248. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/skillrun.py +0 -0
  249. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/skills_sync.py +0 -0
  250. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/structure_catalog.py +0 -0
  251. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/subagent_startup.py +0 -0
  252. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/tools/__init__.py +0 -0
  253. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/tools/__main__.py +0 -0
  254. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/v2_cli.py +0 -0
  255. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/wbs.py +0 -0
  256. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/work_management.py +0 -0
  257. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/work_management_view.py +0 -0
  258. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/workspace.py +0 -0
  259. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit/xref.py +0 -0
  260. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit.egg-info/dependency_links.txt +0 -0
  261. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit.egg-info/entry_points.txt +0 -0
  262. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit.egg-info/requires.txt +0 -0
  263. {xrefkit-0.7.6 → xrefkit-0.7.7}/xrefkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrefkit
3
- Version: 0.7.6
3
+ Version: 0.7.7
4
4
  Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
5
5
  Author: synthaicode
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xrefkit"
7
- version = "0.7.6"
7
+ version = "0.7.7"
8
8
  description = "Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,248 @@
1
+ """Local host integration checks use simulated subprocess/UI, never real approval."""
2
+ import copy
3
+ import json
4
+ from pathlib import Path
5
+ import subprocess
6
+ from types import SimpleNamespace
7
+
8
+ import pytest
9
+
10
+ from test_governance_entry import case, host_receipt, HOST_KEY
11
+ from test_subagent_startup import startup
12
+ from xrefkit.governance_entry import digest, validate_result
13
+ from xrefkit.mcp.contribution_adoption import HmacHumanApprovalVerifier
14
+ from xrefkit import governance_host as host
15
+
16
+
17
+ @pytest.fixture
18
+ def local(case, monkeypatch):
19
+ root, packet, _ = case
20
+ packet_file = root / "work/packet.json"
21
+ packet_file.write_text(json.dumps(packet))
22
+ receipt = host_receipt(root, packet)
23
+ receipt_file = root / "work/receipt.json"
24
+ receipt_file.write_text(json.dumps(receipt))
25
+ state_file = root / "authority.dpapi"
26
+ state = {"schema": "xrefkit.local_host_authority/v1", "root": str(root),
27
+ "packet_hash": packet["packet_hash"], "receipt_hash": digest(receipt),
28
+ "host_secret": HOST_KEY, "applied": False}
29
+ monkeypatch.setattr(host, "_load_state", lambda path: copy.deepcopy(state))
30
+ monkeypatch.setattr(host, "_save_state", lambda path, value, **kwargs: state.update(value))
31
+ return root, packet, packet_file, receipt_file, state_file, state
32
+
33
+
34
+ def test_native_decline_never_applies_or_issues_human_secret(local, monkeypatch):
35
+ root, _, packet, receipt, state_file, state = local
36
+ monkeypatch.setattr(host, "_review_dialog", lambda *args: False)
37
+ result = host.apply_local(root, packet, receipt, state_file, root / "work/reflection.json")
38
+ assert result["status"] == "human_declined_no_application"
39
+ assert not (root / "knowledge/item.md").exists()
40
+ assert "human_secret" not in state and not state["applied"]
41
+
42
+
43
+ def test_native_action_applies_with_separate_keys_and_stale_replay_refuses(local, monkeypatch):
44
+ root, _, packet, receipt, state_file, state = local
45
+ observed = []
46
+ monkeypatch.setattr(host, "_review_dialog", lambda *args: observed.append(args) or True)
47
+ result = host.apply_local(root, packet, receipt, state_file, root / "work/reflection.json")
48
+ assert result["runtime_activation"] == "not_performed"
49
+ assert observed[0][0] == "knowledge/item.md" and "+# New" in observed[0][2]
50
+ assert state["human_secret"] != state["host_secret"]
51
+ assert (root / "knowledge/item.md").read_bytes() == (root / "work/candidate.md").read_bytes()
52
+ with pytest.raises(ValueError, match="unapplied dispatch"):
53
+ host.apply_local(root, packet, receipt, state_file, root / "work/replay.json")
54
+
55
+
56
+ @pytest.mark.parametrize("mutation", ["candidate", "target"])
57
+ def test_change_during_dialog_refuses_even_after_ui_accepts(local, monkeypatch, mutation):
58
+ root, _, packet, receipt, state_file, state = local
59
+ def change(*args):
60
+ path = root / ("work/candidate.md" if mutation == "candidate" else "knowledge/item.md")
61
+ path.parent.mkdir(exist_ok=True)
62
+ path.write_text("external change")
63
+ return True
64
+ monkeypatch.setattr(host, "_review_dialog", change)
65
+ with pytest.raises(ValueError, match="changed"):
66
+ host.apply_local(root, packet, receipt, state_file, root / "work/reflection.json")
67
+ assert not state["applied"]
68
+ if mutation == "target":
69
+ assert (root / "knowledge/item.md").read_text() == "external change"
70
+
71
+
72
+ @pytest.mark.parametrize("field", ["packet_hash", "receipt_hash", "root"])
73
+ def test_wrong_dispatch_state_rejected_before_dialog(local, monkeypatch, field):
74
+ root, _, packet, receipt, state_file, state = local
75
+ state[field] = "wrong"
76
+ monkeypatch.setattr(host, "_review_dialog", lambda *args: pytest.fail("UI must not open"))
77
+ with pytest.raises(ValueError, match="does not match"):
78
+ host.apply_local(root, packet, receipt, state_file, root / "work/reflection.json")
79
+
80
+
81
+ def test_failed_gate_cannot_be_human_approved(local, monkeypatch):
82
+ root, packet, packet_file, receipt_file, state_file, state = local
83
+ receipt = host_receipt(root, packet, failed=True)
84
+ receipt_file.write_text(json.dumps(receipt))
85
+ state["receipt_hash"] = digest(receipt)
86
+ monkeypatch.setattr(host, "_review_dialog", lambda *args: pytest.fail("UI must not open"))
87
+ with pytest.raises(ValueError, match="failed or unknown"):
88
+ host.apply_local(root, packet_file, receipt_file, state_file, root / "work/reflection.json")
89
+
90
+
91
+ def test_tampered_signature_refused_even_with_matching_receipt_digest(local, monkeypatch):
92
+ root, _, packet, receipt_file, state_file, state = local
93
+ receipt = json.loads(receipt_file.read_text())
94
+ receipt["signature"] = "wrong"
95
+ receipt_file.write_text(json.dumps(receipt))
96
+ state["receipt_hash"] = digest(receipt)
97
+ monkeypatch.setattr(host, "_review_dialog", lambda *args: pytest.fail("UI must not open"))
98
+ with pytest.raises(ValueError, match="signature"):
99
+ host.apply_local(root, packet, receipt_file, state_file, root / "work/reflection.json")
100
+
101
+
102
+ def test_evidence_destination_cannot_overwrite_packet(local, monkeypatch):
103
+ root, _, packet, receipt, state_file, _ = local
104
+ monkeypatch.setattr(host, "_review_dialog", lambda *args: pytest.fail("UI must not open"))
105
+ with pytest.raises(ValueError, match="overwrite"):
106
+ host.apply_local(root, packet, receipt, state_file, packet)
107
+
108
+
109
+ @pytest.mark.parametrize("events", [[], [{"type": "thread.started", "thread_id": "x"}],
110
+ [{"type": "thread.started", "thread_id": "x"}, {"type": "turn.failed"}],
111
+ [{"type": "thread.started", "thread_id": "x"}, {"type": "turn.completed"}, {"type": "turn.completed"}]])
112
+ def test_missing_or_failed_terminal_events_are_not_execution(tmp_path, events):
113
+ path = tmp_path / "events.jsonl"
114
+ path.write_text("\n".join(json.dumps(event) for event in events))
115
+ with pytest.raises(ValueError):
116
+ host._completed_session(path)
117
+
118
+
119
+ def test_valid_single_completion(tmp_path):
120
+ path = tmp_path / "events.jsonl"
121
+ path.write_text('{"type":"thread.started","thread_id":"actual-session"}\n{"type":"turn.completed"}\n')
122
+ assert host._completed_session(path) == "actual-session"
123
+
124
+
125
+ def test_authority_never_inherited_by_child(monkeypatch):
126
+ monkeypatch.setenv("XREFKIT_GOVERNANCE_HOST_SECRET", "private")
127
+ monkeypatch.setenv("XREFKIT_GOVERNANCE_HUMAN_SECRET", "private")
128
+ monkeypatch.setenv("XREFKIT_CONTRIBUTION_APPROVAL_SECRET", "private")
129
+ env = host._child_environment()
130
+ assert not any(key.startswith("XREFKIT_GOVERNANCE_") for key in env)
131
+ assert "XREFKIT_CONTRIBUTION_APPROVAL_SECRET" not in env
132
+
133
+
134
+ def test_child_open_refusal_never_dispatches(case, monkeypatch):
135
+ root, packet, _ = case
136
+ source = root / "work/packet.json"
137
+ source.write_text(json.dumps(packet))
138
+ binary = root / "codex.exe"
139
+ binary.write_bytes(b"test fixture, never executable")
140
+ monkeypatch.setattr(host.subprocess, "run", lambda *args, **kwargs:
141
+ SimpleNamespace(returncode=2, stdout='{"ok":false}'))
142
+ monkeypatch.setattr(host.subprocess, "Popen", lambda *args, **kwargs: pytest.fail("must not dispatch"))
143
+ with pytest.raises(ValueError, match="Skill run refused"):
144
+ host.dispatch_local(root, source, binary, root / "work/dispatch")
145
+
146
+
147
+ def test_process_failure_never_generates_host_authority(case, monkeypatch):
148
+ root, packet, _ = case
149
+ source = root / "work/packet.json"
150
+ source.write_text(json.dumps(packet))
151
+ binary = root / "codex.exe"
152
+ binary.write_bytes(b"test fixture, never executable")
153
+ monkeypatch.setattr(host.subprocess, "run", lambda *args, **kwargs:
154
+ SimpleNamespace(returncode=0, stdout='{"ok":true,"run_id":"opened"}'))
155
+ process = SimpleNamespace(returncode=7, pid=123, communicate=lambda *args, **kwargs: None)
156
+ monkeypatch.setattr(host.subprocess, "Popen", lambda *args, **kwargs: process)
157
+ monkeypatch.setattr(host.secrets, "token_hex", lambda *args: pytest.fail("key must not be generated"))
158
+ with pytest.raises(ValueError, match="process failed"):
159
+ host.dispatch_local(root, source, binary, root / "work/dispatch")
160
+
161
+
162
+ def test_no_cli_automatic_approval_option():
163
+ with pytest.raises(SystemExit) as exc:
164
+ host.main(["--root", ".", "apply", "--packet", "a", "--receipt", "b",
165
+ "--authority-state", "c", "--out", "d", "--yes"])
166
+ assert exc.value.code == 2
167
+
168
+
169
+ def test_applied_but_authority_save_failure_carries_reflection(local, monkeypatch):
170
+ root, _, packet, receipt, state_file, _ = local
171
+ monkeypatch.setattr(host, "_review_dialog", lambda *args: True)
172
+ def fail_save(*args, **kwargs):
173
+ raise OSError("simulated authority disk failure")
174
+ monkeypatch.setattr(host, "_save_state", fail_save)
175
+ with pytest.raises(host.AppliedEvidenceError) as exc:
176
+ host.apply_local(root, packet, receipt, state_file, root / "work/reflection.json")
177
+ assert (root / "knowledge/item.md").read_bytes() == (root / "work/candidate.md").read_bytes()
178
+ assert exc.value.reflection["target"]["path"] == "knowledge/item.md"
179
+
180
+
181
+ def test_cli_distinguishes_applied_evidence_failure(monkeypatch, capsys):
182
+ reflection = {"target": {"path": "skills/example/SKILL.v1.md", "sha256": "observed-hash"}}
183
+ def failed(*args):
184
+ raise host.AppliedEvidenceError(reflection)
185
+ monkeypatch.setattr(host, "apply_local", failed)
186
+ code = host.main(["--root", ".", "apply", "--packet", "a", "--receipt", "b",
187
+ "--authority-state", "c", "--out", "d"])
188
+ assert code == 3
189
+ assert "observed-hash" in capsys.readouterr().out
190
+
191
+
192
+ @pytest.mark.parametrize("invalid_run", [None, "identity", "escalated"])
193
+ def test_dispatch_attests_only_actual_opened_run_and_completed_output(case, monkeypatch, invalid_run):
194
+ root, packet, _ = case
195
+ source = root / "work/packet.json"
196
+ source.write_text(json.dumps(packet))
197
+ binary = root / "codex.exe"
198
+ binary.write_bytes(b"test fixture, never executable")
199
+ host_receipt(root, packet) # Test-only prepared body/closed log.
200
+ out = root / "work/dispatch"
201
+ state = {}
202
+ child_path = None
203
+ def open_run(argv, **kwargs):
204
+ nonlocal child_path
205
+ child_path = Path(argv[argv.index("--out") + 1])
206
+ assert child_path.name.endswith("_skill_run_shared_asset_update_gate.md")
207
+ text = (root / "work/child.md").read_text().replace("work/analysis.md", "work/dispatch/analysis.json")
208
+ child_path.write_text(text)
209
+ return SimpleNamespace(returncode=0, stdout='{"ok":true,"run_id":"child-test-run"}')
210
+ def launch(argv, **kwargs):
211
+ def communicate(prompt, **options):
212
+ assert b"Do not open another run" in prompt
213
+ (out / "analysis.json").write_bytes((root / "work/analysis.md").read_bytes())
214
+ Path(argv[argv.index("--output-last-message") + 1]).write_text("Analysis complete")
215
+ kwargs["stdout"].write(b'{"type":"thread.started","thread_id":"simulated-session"}\n{"type":"turn.completed"}\n')
216
+ if invalid_run == "identity":
217
+ child_path.write_text(child_path.read_text().replace("child-test-run", "replaced-run"))
218
+ elif invalid_run == "escalated":
219
+ child_path.write_text(child_path.read_text().replace(
220
+ "## Closure Gate\n- status: `done`", "## Closure Gate\n- status: `escalated`"))
221
+ return SimpleNamespace(returncode=0, pid=123, communicate=communicate)
222
+ monkeypatch.setattr(host.subprocess, "run", open_run)
223
+ monkeypatch.setattr(host.subprocess, "Popen", launch)
224
+ monkeypatch.setattr(host, "_state_path", lambda execution_id: root / "authority.dpapi")
225
+ monkeypatch.setattr(host, "_save_state", lambda path, value, **kwargs: state.update(value))
226
+ if invalid_run:
227
+ expected = "host-opened child identity" if invalid_run == "identity" else "completed child Skill run"
228
+ with pytest.raises(ValueError, match=expected):
229
+ host.dispatch_local(root, source, binary, out)
230
+ assert not state and not (out / "receipt.json").exists()
231
+ else:
232
+ result = host.dispatch_local(root, source, binary, out)
233
+ receipt = json.loads(Path(result["receipt"]).read_text())
234
+ verifier = HmacHumanApprovalVerifier(state["host_secret"])
235
+ assert validate_result(root, packet, receipt, verifier)["packet_hash"] == packet["packet_hash"]
236
+ provenance = json.loads((out / "dispatch.json").read_text())
237
+ verifier.verify_event(provenance["event"], provenance["signature"])
238
+ assert provenance["event"]["thread_id"] == "simulated-session"
239
+ assert state["host_secret"] not in (out / "receipt.json").read_text()
240
+ assert not (root / "knowledge/item.md").exists()
241
+
242
+
243
+ @pytest.mark.skipif(host.os.name != "nt", reason="Windows operator adapter")
244
+ def test_windows_dpapi_roundtrip():
245
+ disposable = b"non-secret disposable integration test"
246
+ ciphertext = host._protect(disposable)
247
+ assert ciphertext != disposable
248
+ assert host._protect(ciphertext, decrypt=True) == disposable
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.7.6"
5
+ __version__ = "0.7.7"
@@ -0,0 +1,378 @@
1
+ """Local Windows host bridge; analysis, host attestation and human action stay separate.
2
+
3
+ This is an operator-controlled adapter, not protection against another process
4
+ running as the same OS user. It never accepts an imported analyst receipt or an
5
+ automatic human-approval flag. Existing governance validators own acceptance.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import ctypes
11
+ import difflib
12
+ from datetime import date
13
+ import hashlib
14
+ import json
15
+ import os
16
+ from pathlib import Path
17
+ import secrets
18
+ import subprocess
19
+ import sys
20
+ import tempfile
21
+ import time
22
+ import uuid
23
+
24
+ from .governance_entry import apply_update, digest, revalidate, snapshot, validate_result
25
+ from .governance_entry_cli import _read
26
+ from .mcp.contribution_adoption import HmacHumanApprovalVerifier, issue_hmac_approval_assertion
27
+ from .skillrun import _log_field
28
+
29
+
30
+ class AppliedEvidenceError(RuntimeError):
31
+ """The target changed successfully; retry must inspect reflection first."""
32
+
33
+ def __init__(self, reflection: dict):
34
+ super().__init__("canonical application completed but reflection evidence save failed")
35
+ self.reflection = reflection
36
+
37
+
38
+ def _write_new(path: Path, value: dict) -> None:
39
+ path.parent.mkdir(parents=True, exist_ok=True)
40
+ with path.open("x", encoding="utf-8") as stream:
41
+ json.dump(value, stream, ensure_ascii=False, indent=2)
42
+ stream.write("\n")
43
+
44
+
45
+ def _work_path(root: Path, path: Path) -> Path:
46
+ path = path.resolve()
47
+ if not path.is_relative_to((root / "work").resolve()):
48
+ raise ValueError("host evidence must remain under repository work/")
49
+ return path
50
+
51
+
52
+ def _relative(root: Path, path: Path) -> str:
53
+ return path.resolve().relative_to(root.resolve()).as_posix()
54
+
55
+
56
+ def _child_environment() -> dict[str, str]:
57
+ # Do not pass any existing operator authority to the analyst. New host keys
58
+ # are generated only AFTER the analyst exits, not inherited or put on disk.
59
+ return {key: value for key, value in os.environ.items()
60
+ if not key.upper().startswith("XREFKIT_GOVERNANCE_")
61
+ and key.upper() != "XREFKIT_CONTRIBUTION_APPROVAL_SECRET"}
62
+
63
+
64
+ def _protect(raw: bytes, *, decrypt: bool = False) -> bytes:
65
+ if os.name != "nt":
66
+ raise RuntimeError("this operator adapter requires Windows DPAPI")
67
+
68
+ class Blob(ctypes.Structure):
69
+ _fields_ = [("size", ctypes.c_ulong), ("data", ctypes.POINTER(ctypes.c_ubyte))]
70
+
71
+ buffer = ctypes.create_string_buffer(raw)
72
+ source = Blob(len(raw), ctypes.cast(buffer, ctypes.POINTER(ctypes.c_ubyte)))
73
+ output = Blob()
74
+ crypt = ctypes.WinDLL("crypt32", use_last_error=True)
75
+ kernel = ctypes.WinDLL("kernel32", use_last_error=True)
76
+ kernel.LocalFree.argtypes = [ctypes.c_void_p]
77
+ kernel.LocalFree.restype = ctypes.c_void_p
78
+ if decrypt:
79
+ success = crypt.CryptUnprotectData(ctypes.byref(source), None, None, None, None,
80
+ 1, ctypes.byref(output))
81
+ else:
82
+ success = crypt.CryptProtectData(ctypes.byref(source), "XRefKit governance host",
83
+ None, None, None, 1, ctypes.byref(output))
84
+ if not success:
85
+ raise OSError("Windows authority storage failed")
86
+ try:
87
+ return ctypes.string_at(output.data, output.size)
88
+ finally:
89
+ kernel.LocalFree(output.data)
90
+
91
+
92
+ def _state_path(execution_id: str) -> Path:
93
+ location = os.environ.get("LOCALAPPDATA")
94
+ if not location:
95
+ raise RuntimeError("LOCALAPPDATA is required for operator authority storage")
96
+ return Path(location) / "XRefKit" / "governance-host" / (execution_id + ".dpapi")
97
+
98
+
99
+ def _save_state(path: Path, state: dict, *, create: bool = True) -> None:
100
+ raw = _protect(json.dumps(state, sort_keys=True).encode("utf-8"))
101
+ path.parent.mkdir(parents=True, exist_ok=True)
102
+ if create:
103
+ with path.open("xb") as stream:
104
+ stream.write(raw)
105
+ return
106
+ descriptor, temporary = tempfile.mkstemp(prefix=".authority-", dir=path.parent)
107
+ try:
108
+ with os.fdopen(descriptor, "wb") as stream:
109
+ stream.write(raw)
110
+ stream.flush()
111
+ os.fsync(stream.fileno())
112
+ os.replace(temporary, path)
113
+ finally:
114
+ if os.path.exists(temporary):
115
+ os.unlink(temporary)
116
+
117
+
118
+ def _load_state(path: Path) -> dict:
119
+ state = json.loads(_protect(path.read_bytes(), decrypt=True))
120
+ if not isinstance(state, dict) or state.get("schema") != "xrefkit.local_host_authority/v1":
121
+ raise ValueError("invalid local host authority")
122
+ return state
123
+
124
+
125
+ def _completed_session(path: Path) -> str:
126
+ started = []
127
+ completed = 0
128
+ for line in path.read_text(encoding="utf-8").splitlines():
129
+ event = json.loads(line)
130
+ if event.get("type") in {"error", "turn.failed"}:
131
+ raise ValueError("analyst stream reported failure")
132
+ if event.get("type") == "thread.started":
133
+ started.append(event.get("thread_id"))
134
+ if event.get("type") == "turn.completed":
135
+ completed += 1
136
+ if len(started) != 1 or not isinstance(started[0], str) or not started[0] or completed != 1:
137
+ raise ValueError("one actual completed Codex thread is required")
138
+ return started[0]
139
+
140
+
141
+ def _prompt(packet_path: Path, child_log: Path, result: Path) -> str:
142
+ return f"""Act only as the isolated shared_asset_update_gate analyst.
143
+ The host has already opened your correlated Skill run. Do not open another run.
144
+ Read the repository startup sources, then the selected method
145
+ skills/os/shared_asset_update_gate/SKILL.v1.md and the immutable packet:
146
+ {packet_path}
147
+ Your opened run log: {child_log}
148
+ Save the strict JSON result body (without output pointer) to: {result}
149
+ Consume only packet materials and required startup/method contracts. Do not edit
150
+ the packet, candidate, canonical target, specialist evidence, parent log or code.
151
+ Use the existing skill workitem/artifact/concern/phase/verify/close commands to
152
+ record and close your own run. Never advance check yourself; use deterministic
153
+ verify. Never sign a receipt or approve/apply a target. Record the designated
154
+ result as a done output artifact using its repository-relative path; record
155
+ evidence, explicit handoff and all concerns. Report failed/unknown findings
156
+ honestly. Successful process exit is not evidence that checks pass.
157
+ The later host attestation, separate human action and canonical application are
158
+ normal downstream responsibilities, outside your analysis scope. Record them
159
+ as handoff steps; their being future steps alone is not an unresolved analysis
160
+ risk. If you identify a concrete unresolved risk or uncertainty, retain it and
161
+ its required disposition even when that prevents host attestation. The host
162
+ requires actual Closure Gate done; it will not reinterpret escalated closure as
163
+ success, and you must not resolve a genuine concern merely to obtain a receipt.
164
+ Return only a short completion message after the output and run are saved.
165
+ """
166
+
167
+
168
+ def dispatch_local(root: Path, packet_path: Path, codex: Path, out_dir: Path,
169
+ *, timeout: int = 1800) -> dict:
170
+ """Run the actual CLI child, then attest its validated, closed result."""
171
+ root = root.resolve()
172
+ packet_path = packet_path.resolve()
173
+ packet = _read(str(packet_path))
174
+ revalidate(root, packet)
175
+ if packet["kind"] != "asset_update" or "endpoint" in packet["payload"]:
176
+ raise ValueError("local host requires a canonical single-target asset_update packet")
177
+ codex = codex.resolve(strict=True)
178
+ if not codex.is_file() or codex.suffix.lower() != ".exe":
179
+ raise ValueError("select the trusted native Codex executable, not a shell wrapper")
180
+ out_dir = _work_path(root, out_dir)
181
+ out_dir.mkdir(parents=True, exist_ok=False)
182
+ child = out_dir / f"{date.today().isoformat()}_skill_run_shared_asset_update_gate.md"
183
+ output = out_dir / "analysis.json"
184
+ final = out_dir / "last-message.txt"
185
+ events = out_dir / "events.jsonl"
186
+ stderr = out_dir / "stderr.log"
187
+ binding = packet["binding"]
188
+ command = [sys.executable, "-m", "xrefkit", "skill", "run", "--root", str(root),
189
+ "--definition", "skills/os/shared_asset_update_gate/SKILL.v1.md",
190
+ "--task", "Analyze frozen canonical governance packet " + packet["packet_hash"],
191
+ "--capability", "governance analysis", "--tuning", "shared asset management",
192
+ "--responsibility", "Analyze only the frozen common management checks without adoption",
193
+ "--execution-mode", "subagent_required", "--parent-run-id", binding["run_id"],
194
+ "--root-run-id", binding["run_snapshot"]["fields"]["root_run_id"],
195
+ "--work-item-id", binding["work_item_id"], "--out", str(child), "--json"]
196
+ opened = subprocess.run(command, cwd=root, env=_child_environment(), capture_output=True,
197
+ text=True, encoding="utf-8", timeout=60, check=False)
198
+ (out_dir / "open-run.json").write_text(opened.stdout, encoding="utf-8")
199
+ opened_record = json.loads(opened.stdout)
200
+ if opened.returncode or not opened_record.get("ok") or not opened_record.get("run_id"):
201
+ raise ValueError("analyst Skill run refused; inspect open-run.json")
202
+ execution_id = str(uuid.uuid4())
203
+ argv = [str(codex), "exec", "--json", "--color", "never", "--sandbox", "workspace-write",
204
+ "--cd", str(root), "--output-last-message", str(final), "-"]
205
+ before = time.time()
206
+ with events.open("xb") as stdout, stderr.open("xb") as error:
207
+ process = subprocess.Popen(argv, cwd=root, env=_child_environment(), stdin=subprocess.PIPE,
208
+ stdout=stdout, stderr=error)
209
+ try:
210
+ process.communicate(_prompt(packet_path, child, output).encode("utf-8"), timeout=timeout)
211
+ except subprocess.TimeoutExpired:
212
+ if os.name == "nt":
213
+ subprocess.run(["taskkill.exe", "/PID", str(process.pid), "/T", "/F"],
214
+ capture_output=True, timeout=30, check=False)
215
+ process.kill()
216
+ process.wait()
217
+ raise ValueError("analyst timed out; no attestation or approval created") from None
218
+ if process.returncode:
219
+ raise ValueError("analyst process failed; inspect stderr.log")
220
+ session = _completed_session(events)
221
+ if not final.is_file() or not final.read_text(encoding="utf-8").strip():
222
+ raise ValueError("analyst final message missing")
223
+ revalidate(root, packet)
224
+ if _log_field(child.read_text(encoding="utf-8"), "run_id") != opened_record["run_id"]:
225
+ raise ValueError("analyst replaced the host-opened child identity")
226
+ body = _read(str(output))
227
+ if "output" in body:
228
+ raise ValueError("analyst must not supply a host output pointer")
229
+ result = {**body, "output": snapshot(root, _relative(root, output))}
230
+ event = {"schema": "xrefkit.governance_execution/v1", "packet_hash": packet["packet_hash"],
231
+ "execution_id": execution_id, "child_log": _relative(root, child),
232
+ "child_log_hash": snapshot(root, _relative(root, child))["sha256"],
233
+ "skill_id": "shared_asset_update_gate", "result": result, "result_hash": digest(result)}
234
+ host_secret = secrets.token_hex(32)
235
+ verifier = HmacHumanApprovalVerifier(host_secret)
236
+ receipt = {"event": event, "signature": verifier.seal_event(event)}
237
+ validate_result(root, packet, receipt, verifier)
238
+ state_path = _state_path(execution_id)
239
+ provenance = {"schema": "xrefkit.local_host_dispatch/v1", "execution_id": execution_id,
240
+ "packet_hash": packet["packet_hash"], "thread_id": session, "pid": process.pid,
241
+ "started_at": before, "completed_at": time.time(), "exit_code": process.returncode,
242
+ "executable": str(codex), "executable_sha256": hashlib.sha256(codex.read_bytes()).hexdigest(),
243
+ "events": snapshot(root, _relative(root, events)), "receipt_hash": digest(receipt)}
244
+ _save_state(state_path, {"schema": "xrefkit.local_host_authority/v1", "root": str(root),
245
+ "packet_hash": packet["packet_hash"], "receipt_hash": digest(receipt),
246
+ "host_secret": host_secret, "applied": False})
247
+ _write_new(out_dir / "receipt.json", receipt)
248
+ _write_new(out_dir / "dispatch.json", {"event": provenance, "signature": verifier.seal_event(provenance)})
249
+ handoff = {"receipt": str(out_dir / "receipt.json"), "authority_state": str(state_path),
250
+ "status": "dispatched_not_approved", "human_action": "run apply and review the native dialog"}
251
+ _write_new(out_dir / "handoff.json", handoff)
252
+ return handoff
253
+
254
+
255
+ def _review_dialog(target: str, candidate_hash: str, difference: str) -> bool:
256
+ # No command-line/STDIN yes path. The operator must act in this native UI.
257
+ import tkinter as tk
258
+ from tkinter.scrolledtext import ScrolledText
259
+
260
+ window = tk.Tk()
261
+ window.title("XRefKit — 正式Skillソースへの反映")
262
+ window.geometry("1000x750")
263
+ accepted = False
264
+ tk.Label(window, text="次の差分を正式ソースへ反映します。成熟度・採用・配布版は変更しません。",
265
+ anchor="w").pack(fill="x", padx=12, pady=8)
266
+ tk.Label(window, text=f"対象: {target}\n候補 SHA-256: {candidate_hash}",
267
+ anchor="w", justify="left", wraplength=970).pack(fill="x", padx=12)
268
+ text = ScrolledText(window, wrap="none")
269
+ text.insert("1.0", difference)
270
+ text.configure(state="disabled")
271
+ text.pack(fill="both", expand=True, padx=12, pady=12)
272
+
273
+ def approve() -> None:
274
+ nonlocal accepted
275
+ accepted = True
276
+ window.destroy()
277
+
278
+ buttons = tk.Frame(window)
279
+ buttons.pack(fill="x", padx=12, pady=10)
280
+ tk.Button(buttons, text="反映せず終了", command=window.destroy).pack(side="right", padx=8)
281
+ tk.Button(buttons, text="この差分を正式ソースへ反映", command=approve).pack(side="right", padx=8)
282
+ window.mainloop()
283
+ return accepted
284
+
285
+
286
+ def apply_local(root: Path, packet_path: Path, receipt_path: Path, state_path: Path,
287
+ output: Path) -> dict:
288
+ root = root.resolve()
289
+ packet = _read(str(packet_path))
290
+ receipt = _read(str(receipt_path))
291
+ state = _load_state(state_path)
292
+ if (state.get("root") != str(root) or state.get("packet_hash") != packet["packet_hash"]
293
+ or state.get("receipt_hash") != digest(receipt) or state.get("applied")):
294
+ raise ValueError("authority state does not match this unapplied dispatch")
295
+ verifier = HmacHumanApprovalVerifier(state["host_secret"])
296
+ result = validate_result(root, packet, receipt, verifier)
297
+ if result["unknowns"] or any(row["result"] not in {"pass", "not_applicable"} for row in result["findings"]):
298
+ raise ValueError("failed or unknown analysis cannot be approved")
299
+ output = _work_path(root, output)
300
+ protected = {packet_path.resolve(), receipt_path.resolve(), state_path.resolve(),
301
+ (root / packet["log"]).resolve(), (root / packet["payload"]["target"]).resolve(),
302
+ (root / receipt["event"]["child_log"]).resolve(),
303
+ *((root / row["path"]).resolve() for row in packet["materials"]),
304
+ (root / result["output"]["path"]).resolve()}
305
+ if output in protected:
306
+ raise ValueError("reflection output would overwrite governance input")
307
+ _write_new(output, {"status": "awaiting_native_human_action"})
308
+ target = packet["payload"]["target"]
309
+ old_bytes = (root / target).read_bytes() if (root / target).exists() else None
310
+ candidate = packet["payload"]["candidate"]
311
+ new_bytes = (root / candidate).read_bytes()
312
+ candidate_hash = next(row["sha256"] for row in packet["materials"] if row["path"] == candidate)
313
+ if (hashlib.sha256(new_bytes).hexdigest() != candidate_hash
314
+ or (hashlib.sha256(old_bytes).hexdigest() if old_bytes is not None else None)
315
+ != packet["payload"]["expected_target_hash"]):
316
+ raise ValueError("review display differs from frozen candidate or target")
317
+ revalidate(root, packet)
318
+ old = old_bytes.decode("utf-8") if old_bytes is not None else ""
319
+ new = new_bytes.decode("utf-8")
320
+ difference = "".join(difflib.unified_diff(old.splitlines(True), new.splitlines(True),
321
+ fromfile=target, tofile=candidate))
322
+ if not _review_dialog(target, candidate_hash, difference):
323
+ output.write_text('{"status":"human_declined_no_application"}\n', encoding="utf-8")
324
+ return {"status": "human_declined_no_application"}
325
+ # Authority is issued only after the native action, in the operator process.
326
+ # Revalidation inside apply_update rejects changes made while the UI was open.
327
+ human_secret = secrets.token_hex(32)
328
+ assertion = issue_hmac_approval_assertion(human_secret, {
329
+ "action": "apply_governed_asset", "packet_hash": packet["packet_hash"],
330
+ "target": target, "result_hash": digest(result)})
331
+ reflected = apply_update(root, packet, receipt, host_verifier=verifier,
332
+ approval_verifier=HmacHumanApprovalVerifier(human_secret),
333
+ approval_assertion=assertion)
334
+ state.update({"human_secret": human_secret, "approval_assertion": assertion, "applied": True,
335
+ "approved_at": time.time(), "reflected_target": reflected["target"]})
336
+ try:
337
+ _save_state(state_path, state, create=False)
338
+ output.write_text(json.dumps(reflected, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
339
+ except (OSError, RuntimeError) as exc:
340
+ raise AppliedEvidenceError(reflected) from exc
341
+ return reflected
342
+
343
+
344
+ def main(argv=None) -> int:
345
+ parser = argparse.ArgumentParser(prog="python -m xrefkit.governance_host")
346
+ parser.add_argument("--root", required=True, type=Path)
347
+ commands = parser.add_subparsers(dest="command", required=True)
348
+ start = commands.add_parser("dispatch")
349
+ start.add_argument("--packet", required=True, type=Path)
350
+ start.add_argument("--codex", type=Path, default=os.environ.get("CODEX_APP_RUNTIME_CLI_PATH"))
351
+ start.add_argument("--out-dir", required=True, type=Path)
352
+ start.add_argument("--timeout", type=int, default=1800)
353
+ apply = commands.add_parser("apply")
354
+ apply.add_argument("--packet", required=True, type=Path)
355
+ apply.add_argument("--receipt", required=True, type=Path)
356
+ apply.add_argument("--authority-state", required=True, type=Path)
357
+ apply.add_argument("--out", required=True, type=Path)
358
+ args = parser.parse_args(argv)
359
+ try:
360
+ if args.command == "dispatch":
361
+ if args.codex is None or args.timeout <= 0:
362
+ raise ValueError("native Codex executable and positive timeout required")
363
+ result = dispatch_local(args.root, args.packet, args.codex, args.out_dir, timeout=args.timeout)
364
+ else:
365
+ result = apply_local(args.root, args.packet, args.receipt, args.authority_state, args.out)
366
+ print(json.dumps(result, ensure_ascii=False))
367
+ return 0
368
+ except AppliedEvidenceError as exc:
369
+ print(str(exc))
370
+ print(json.dumps(exc.reflection, ensure_ascii=False))
371
+ return 3
372
+ except (ValueError, OSError, RuntimeError, KeyError, TypeError, subprocess.SubprocessError) as exc:
373
+ print(f"local governance host refused: {exc}")
374
+ return 2
375
+
376
+
377
+ if __name__ == "__main__":
378
+ raise SystemExit(main())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrefkit
3
- Version: 0.7.6
3
+ Version: 0.7.7
4
4
  Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
5
5
  Author: synthaicode
6
6
  License: MIT License