xrefkit 0.7.6__tar.gz → 0.7.8__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 (264) hide show
  1. {xrefkit-0.7.6/xrefkit.egg-info → xrefkit-0.7.8}/PKG-INFO +1 -1
  2. {xrefkit-0.7.6 → xrefkit-0.7.8}/pyproject.toml +1 -1
  3. xrefkit-0.7.8/tests/test_governance_host.py +248 -0
  4. xrefkit-0.7.8/tests/test_package_knowledge_resolution.py +166 -0
  5. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_package_skill_run.py +29 -0
  6. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/__init__.py +1 -1
  7. xrefkit-0.7.8/xrefkit/governance_host.py +378 -0
  8. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/catalog.py +113 -7
  9. {xrefkit-0.7.6 → xrefkit-0.7.8/xrefkit.egg-info}/PKG-INFO +1 -1
  10. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit.egg-info/SOURCES.txt +3 -0
  11. {xrefkit-0.7.6 → xrefkit-0.7.8}/LICENSE +0 -0
  12. {xrefkit-0.7.6 → xrefkit-0.7.8}/README.md +0 -0
  13. {xrefkit-0.7.6 → xrefkit-0.7.8}/setup.cfg +0 -0
  14. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_artifact_selected_family.py +0 -0
  15. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_attention_pet.py +0 -0
  16. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_attention_pet_client_protocol.py +0 -0
  17. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_attention_pet_codex_session.py +0 -0
  18. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_attention_pet_fit.py +0 -0
  19. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_azure_connection.py +0 -0
  20. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_azure_import.py +0 -0
  21. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_azure_recovery.py +0 -0
  22. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_azure_update_candidate.py +0 -0
  23. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_azure_writer.py +0 -0
  24. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_boundary_analysis.py +0 -0
  25. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_brownfield_csharp_structure_definitions.py +0 -0
  26. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_brownfield_file_editing_protocol.py +0 -0
  27. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_business_intake_conversation_definitions.py +0 -0
  28. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_business_intake_definition.py +0 -0
  29. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_calibration_lint.py +0 -0
  30. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_catalog_preparation_definition.py +0 -0
  31. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_check_skill_knowledge_xids.py +0 -0
  32. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_ci_workflow_invariants.py +0 -0
  33. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_cli.py +0 -0
  34. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_code_constraint_definition.py +0 -0
  35. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_collect_analyzer_sarif.py +0 -0
  36. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_compact_skill_logs.py +0 -0
  37. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_constraint_derivation_family_definition.py +0 -0
  38. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_constraint_derivation_selected_family.py +0 -0
  39. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_contribution_adoption_bytes.py +0 -0
  40. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_contribution_returns.py +0 -0
  41. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_convert_to_xrefkit_skill.py +0 -0
  42. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_cs_scope_probe.py +0 -0
  43. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_csharp_commonality.py +0 -0
  44. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_csharp_naming_profile.py +0 -0
  45. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_ctx.py +0 -0
  46. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_cutover_readiness.py +0 -0
  47. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_dashboard.py +0 -0
  48. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_database_batch_definition_migration.py +0 -0
  49. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_decision_trace.py +0 -0
  50. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_design_business_intake_selected_family.py +0 -0
  51. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_design_planning_definition_migration.py +0 -0
  52. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_dotnet_definition_candidate.py +0 -0
  53. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_editorial_family_definition.py +0 -0
  54. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_editorial_intake_definition.py +0 -0
  55. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_editorial_selected_family.py +0 -0
  56. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_embedded_startup_pack.py +0 -0
  57. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_error_policy_audit.py +0 -0
  58. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_error_policy_locator.py +0 -0
  59. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_execution_binding.py +0 -0
  60. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_existing_skill_definition_batch.py +0 -0
  61. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_final_skilldefinition_batch.py +0 -0
  62. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_flow_selected_family.py +0 -0
  63. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_fm_multiroot.py +0 -0
  64. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_gate.py +0 -0
  65. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_gateway.py +0 -0
  66. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_gateway_mcp.py +0 -0
  67. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_gateway_skill_adapter.py +0 -0
  68. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_gateway_work_items.py +0 -0
  69. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_goal_desired_state.py +0 -0
  70. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_governance_entry.py +0 -0
  71. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_host_precheck.py +0 -0
  72. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_human_evaluation.py +0 -0
  73. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_implementation_flow_definition.py +0 -0
  74. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_implementation_review_selected_family.py +0 -0
  75. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_inbound_uploads.py +0 -0
  76. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_instruction_workflow.py +0 -0
  77. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_knowledge_relations_validator.py +0 -0
  78. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_language_review_definition.py +0 -0
  79. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_legacy_guidance.py +0 -0
  80. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_legacy_migration.py +0 -0
  81. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_legacy_resources.py +0 -0
  82. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_mcp_package_routing.py +0 -0
  83. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_mcp_setup.py +0 -0
  84. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_mcp_subagent_startup.py +0 -0
  85. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_mcp_subagent_startup_integration.py +0 -0
  86. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_ontology_assessment_validator.py +0 -0
  87. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_os_family_definition.py +0 -0
  88. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_os_knowledge_family_definition.py +0 -0
  89. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_os_selected_family.py +0 -0
  90. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_ownership.py +0 -0
  91. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_packaged_native_governance.py +0 -0
  92. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_packmeta.py +0 -0
  93. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_plan_markdown.py +0 -0
  94. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_plan_observation.py +0 -0
  95. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_project_management_materialization.py +0 -0
  96. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_project_quality_baseline.py +0 -0
  97. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_qa_report_definition.py +0 -0
  98. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_reporting_profiles.py +0 -0
  99. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_reporting_quality.py +0 -0
  100. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_repository_skill_adoption.py +0 -0
  101. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_repository_startup_pack.py +0 -0
  102. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_resource_provider.py +0 -0
  103. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_runtime_contracts.py +0 -0
  104. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_sarif_to_locator.py +0 -0
  105. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_security_definition_migration.py +0 -0
  106. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_site_release_metadata.py +0 -0
  107. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_definition.py +0 -0
  108. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_definition_governance.py +0 -0
  109. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_definition_mcp.py +0 -0
  110. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_definition_runtime.py +0 -0
  111. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_edits.py +0 -0
  112. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_maturity_return.py +0 -0
  113. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_release_compatibility.py +0 -0
  114. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skill_runtime_audit.py +0 -0
  115. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skillmeta.py +0 -0
  116. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_skills_sync.py +0 -0
  117. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_source_analysis_fragments.py +0 -0
  118. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_structure_catalog.py +0 -0
  119. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_subagent_startup.py +0 -0
  120. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_subagent_startup_boundaries.py +0 -0
  121. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_wbs.py +0 -0
  122. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_work_management.py +0 -0
  123. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_work_packages.py +0 -0
  124. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_workflow_family_definition.py +0 -0
  125. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_xref.py +0 -0
  126. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_xrefkit_instance.py +0 -0
  127. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_xrefkit_tools.py +0 -0
  128. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_xrefkit_v2_discovery.py +0 -0
  129. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_xrefkit_v2_models.py +0 -0
  130. {xrefkit-0.7.6 → xrefkit-0.7.8}/tests/test_xrefkit_v2_pipeline.py +0 -0
  131. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/__main__.py +0 -0
  132. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/__init__.py +0 -0
  133. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/__main__.py +0 -0
  134. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/client.py +0 -0
  135. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/client_protocol.py +0 -0
  136. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/codex_session.py +0 -0
  137. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/evaluator.py +0 -0
  138. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/model.py +0 -0
  139. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/presentation.py +0 -0
  140. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/profiles.py +0 -0
  141. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/scenarios.py +0 -0
  142. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/server.py +0 -0
  143. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/attention_pet/store.py +0 -0
  144. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/azure_connection.py +0 -0
  145. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/azure_import.py +0 -0
  146. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/azure_recovery.py +0 -0
  147. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/azure_update_candidate.py +0 -0
  148. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/azure_writer.py +0 -0
  149. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/boundary_analysis.py +0 -0
  150. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/catalog_cli.py +0 -0
  151. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/cli.py +0 -0
  152. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/contracts.py +0 -0
  153. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/ctx.py +0 -0
  154. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/dashboard.py +0 -0
  155. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/decision_trace.py +0 -0
  156. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/discovery.py +0 -0
  157. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/execution_binding.py +0 -0
  158. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/execution_binding_cli.py +0 -0
  159. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/gate.py +0 -0
  160. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/gateway.py +0 -0
  161. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/goalstate.py +0 -0
  162. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/governance_entry.py +0 -0
  163. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/governance_entry_cli.py +0 -0
  164. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/hashing.py +0 -0
  165. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/host_precheck.py +0 -0
  166. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/import_skill.py +0 -0
  167. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/instance.py +0 -0
  168. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/loaders.py +0 -0
  169. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/__init__.py +0 -0
  170. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/audit.py +0 -0
  171. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/bootstrap.py +0 -0
  172. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/cli.py +0 -0
  173. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/client_cache.py +0 -0
  174. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/client_flow.py +0 -0
  175. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/context_registry.py +0 -0
  176. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/context_token.py +0 -0
  177. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/contracts.py +0 -0
  178. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/contribution_adoption.py +0 -0
  179. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/contribution_returns.py +0 -0
  180. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/dist.py +0 -0
  181. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/gateway.py +0 -0
  182. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/governed_overlays.py +0 -0
  183. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/inbound_uploads.py +0 -0
  184. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/knowledge_edits.py +0 -0
  185. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/legacy_guidance.py +0 -0
  186. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/legacy_migration.py +0 -0
  187. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/legacy_resources.py +0 -0
  188. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/ownership.py +0 -0
  189. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/repository.py +0 -0
  190. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/schemas.py +0 -0
  191. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/server.py +0 -0
  192. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/setup.py +0 -0
  193. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/skill_edits.py +0 -0
  194. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/skill_maturity.py +0 -0
  195. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/startup_contract_pack.py +0 -0
  196. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/subagent_startup.py +0 -0
  197. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp/update_gate_intake.py +0 -0
  198. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/mcp_tools.py +0 -0
  199. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/__init__.py +0 -0
  200. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/common.py +0 -0
  201. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/effective_bundle.py +0 -0
  202. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/human_evaluation.py +0 -0
  203. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/local_manifest.py +0 -0
  204. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/package_manifest.py +0 -0
  205. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/run_log.py +0 -0
  206. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/server_config.py +0 -0
  207. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/models/skill_definition.py +0 -0
  208. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/operations_cli.py +0 -0
  209. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/ownership.py +0 -0
  210. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/packmeta.py +0 -0
  211. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/plan_markdown.py +0 -0
  212. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/plan_observation.py +0 -0
  213. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/registry.py +0 -0
  214. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/reporting.py +0 -0
  215. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/reporting_profiles.py +0 -0
  216. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/repository_skills.py +0 -0
  217. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resolver.py +0 -0
  218. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resource_provider.py +0 -0
  219. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/attention_pet/client-state.schema.json +0 -0
  220. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/attention_pet/pet.css +0 -0
  221. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/attention_pet/pet.html +0 -0
  222. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/attention_pet/pet.js +0 -0
  223. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/contracts.json +0 -0
  224. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/current.json +0 -0
  225. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
  226. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
  227. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
  228. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
  229. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/model_body.md +0 -0
  230. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/startup_sources/000_agent_entry.md +0 -0
  231. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/startup_sources/011_startup_xref_routing.md +0 -0
  232. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/startup_sources/015_shared_memory_operations.md +0 -0
  233. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/startup_sources/016_uncertainty_protocol.md +0 -0
  234. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/startup_sources/017_base_and_xref_layering.md +0 -0
  235. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/startup_sources/053_context_direction_security_guard.md +0 -0
  236. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/base/startup_sources/yomiyasu_LICENSE.txt +0 -0
  237. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/migration/docs/guides/062_legacy_flow_skill_migration_guide.md +0 -0
  238. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/migration/knowledge/operations/130_legacy_flow_skill_migration_rules.md +0 -0
  239. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/migration/knowledge/organization/160_context_direction_guard_rules.md +0 -0
  240. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/migration/skills/os/legacy_flow_skill_migration/SKILL.v1.md +0 -0
  241. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/migration/skills/os/legacy_flow_skill_migration/references/legacy_flow_skill_migration_template.md +0 -0
  242. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/migration/skills/repository_adoption.json +0 -0
  243. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/resources/migration/tools/migrate_legacy_flow_skill.py +0 -0
  244. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/runlog.py +0 -0
  245. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/skill_definition.py +0 -0
  246. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/skill_definition_catalog.py +0 -0
  247. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/skill_definition_governance.py +0 -0
  248. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/skillmeta.py +0 -0
  249. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/skillrun.py +0 -0
  250. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/skills_sync.py +0 -0
  251. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/structure_catalog.py +0 -0
  252. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/subagent_startup.py +0 -0
  253. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/tools/__init__.py +0 -0
  254. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/tools/__main__.py +0 -0
  255. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/v2_cli.py +0 -0
  256. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/wbs.py +0 -0
  257. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/work_management.py +0 -0
  258. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/work_management_view.py +0 -0
  259. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/workspace.py +0 -0
  260. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit/xref.py +0 -0
  261. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit.egg-info/dependency_links.txt +0 -0
  262. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit.egg-info/entry_points.txt +0 -0
  263. {xrefkit-0.7.6 → xrefkit-0.7.8}/xrefkit.egg-info/requires.txt +0 -0
  264. {xrefkit-0.7.6 → xrefkit-0.7.8}/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.8
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.8"
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
@@ -0,0 +1,166 @@
1
+ from dataclasses import replace
2
+
3
+ import pytest
4
+
5
+ from xrefkit.discovery import DiscoveredSkillPackage
6
+ from xrefkit.loaders import load_package_manifest
7
+ from xrefkit.mcp.catalog import XRefCatalog
8
+
9
+
10
+ def package_catalog(tmp_path, asset_path="knowledge/rules.md"):
11
+ import yaml
12
+ package_root = tmp_path / "package"
13
+ package_root.mkdir()
14
+ (package_root / "knowledge").mkdir()
15
+ (package_root / "knowledge/rules.md").write_text("# Package rules\n\nRequired rules.\n")
16
+ (package_root / "method.md").write_text("# Method\n")
17
+ manifest = package_root / "package_manifest.yaml"
18
+ manifest.write_text(yaml.safe_dump({
19
+ "package_id": "example.rules", "package_type": "skill_package", "version": "1.0.0",
20
+ "requires": {"xrefkit_core": ">=2.0.0 <3.0.0"},
21
+ "provides": {
22
+ "skills": [{"id": "example.review", "xid": "xid-skill-example", "path": "method.md"}],
23
+ "knowledge": [{"id": "rules", "xid": "xid-knowledge-rules", "path": asset_path}],
24
+ },
25
+ "contract": {},
26
+ }))
27
+ package = DiscoveredSkillPackage("example", package_root, manifest, load_package_manifest(manifest))
28
+ repo = tmp_path / "repo"
29
+ repo.mkdir()
30
+ return replace(XRefCatalog.build(repo), discovered_packages=(package,))
31
+
32
+
33
+ def test_manifest_identity_resolves_without_inline_xid(tmp_path):
34
+ catalog = package_catalog(tmp_path)
35
+ document = catalog.get_document_by_xid("xid-knowledge-rules")
36
+ assert document["content"] == catalog.expand_knowledge("xid-knowledge-rules")["content"]
37
+ assert document["xid"] == "xid-knowledge-rules"
38
+ assert catalog.search_knowledge_catalog("Required rules")[0]["xid"] == "xid-knowledge-rules"
39
+ cached = catalog.get_document_by_xid("xid-knowledge-rules", document["content_hash"])
40
+ assert cached["content_hash"] == document["content_hash"]
41
+ assert cached["content_omitted"] == document["cache_policy"]["cache_recommended"]
42
+
43
+
44
+ def test_duplicate_package_xid_refuses_selection(tmp_path):
45
+ catalog = package_catalog(tmp_path)
46
+ catalog = replace(catalog, discovered_packages=catalog.discovered_packages * 2)
47
+ assert catalog.get_document_by_xid("xid-knowledge-rules")["error"] == "xid_conflict"
48
+ with pytest.raises(ValueError, match="knowledge xid conflict"):
49
+ catalog.expand_knowledge("xid-knowledge-rules")
50
+
51
+
52
+ def test_package_path_cannot_escape_root(tmp_path):
53
+ catalog = package_catalog(tmp_path, "../outside.md")
54
+ with pytest.raises(ValueError, match="escapes root"):
55
+ catalog.get_document_by_xid("xid-knowledge-rules")
56
+
57
+
58
+ def installed_catalog(tmp_path, monkeypatch):
59
+ """Simulate entry-point installation, then use the real package discovery loader."""
60
+ import yaml
61
+ from types import SimpleNamespace
62
+ from xrefkit import discovery
63
+ catalog = package_catalog(tmp_path)
64
+ package = catalog.discovered_packages[0]
65
+ root = package.package_root
66
+ manifest = yaml.safe_load(package.manifest_path.read_text())
67
+ manifest["provides"]["skills"][0]["path"] = "method.skill.yaml"
68
+ manifest["provides"]["skills"][0]["required_knowledge"] = ["xid-knowledge-rules"]
69
+ manifest["provides"]["review_axes"] = [{"id": "axis", "xid": "xid-axis-rules", "path": "axis.yaml"}]
70
+ manifest["provides"]["schemas"] = [{"id": "schema", "xid": "xid-schema-rules", "path": "schema.json"}]
71
+ package.manifest_path.write_text(yaml.safe_dump(manifest))
72
+ (root / "method.skill.yaml").write_text(yaml.safe_dump({
73
+ "skill_id": "example.review", "xid": "xid-skill-example",
74
+ "entry": {"xid": "xid-entry-example", "path": "method.md", "load_policy": "required_inline"},
75
+ "required_outputs": ["report"], "required_knowledge": ["xid-knowledge-rules"],
76
+ "review_axes": ["xid-axis-rules"], "schemas": ["xid-schema-rules"],
77
+ "must_not": [], "extension_policy": {},
78
+ }))
79
+ (root / "axis.yaml").write_text("axis_id: example\nxid: xid-axis-rules\nchecks: []\n")
80
+ (root / "schema.json").write_text('{"type":"object"}\n')
81
+ ep = SimpleNamespace(name="example", load=lambda: lambda: root)
82
+ monkeypatch.setattr(discovery, "_select_entry_points", lambda group: [ep])
83
+ return XRefCatalog.build(catalog.repo_root, discover_packages=True)
84
+
85
+
86
+ def test_discovered_required_knowledge_is_resolved_and_all_declared_assets_are_fetchable(tmp_path, monkeypatch):
87
+ catalog = installed_catalog(tmp_path, monkeypatch)
88
+ skill = catalog.get_skill("example.review")
89
+ assert skill["required_knowledge"] == [{"xid": "xid-knowledge-rules", "required": True, "reason": "package declaration"}]
90
+ resolved = catalog.resolve_skill_knowledge("example.review")
91
+ assert not resolved["unsatisfied_required"]
92
+ assert resolved["slots"][0]["bind"] == "xid-knowledge-rules"
93
+ assert resolved["slots"][0]["required"] is True and resolved["slots"][0]["satisfied"] is True
94
+ for xid in ("xid-knowledge-rules", "xid-axis-rules", "xid-schema-rules", "xid-entry-example", "xid-skill-example"):
95
+ assert catalog.get_document_by_xid(xid)["content"]
96
+ assert catalog.expand_knowledge("xid-knowledge-rules")["content"].startswith("# Package rules")
97
+
98
+
99
+ def test_undeclared_file_is_not_exposed_and_absent_need_remains_unsatisfied(tmp_path, monkeypatch):
100
+ catalog = installed_catalog(tmp_path, monkeypatch)
101
+ package = catalog.discovered_packages[0]
102
+ (package.package_root / "secret.md").write_text("<!-- xid: ABCDEF123456 -->\n# Never declared")
103
+ with pytest.raises(KeyError):
104
+ catalog.get_document_by_xid("ABCDEF123456")
105
+ package.manifest.provides.knowledge.clear()
106
+ resolved = catalog.resolve_skill_knowledge("example.review")
107
+ assert resolved["unsatisfied_required"] == ["xid-knowledge-rules"]
108
+ assert resolved["slots"][0]["candidates"] == []
109
+ with pytest.raises(KeyError):
110
+ catalog.expand_knowledge("xid-knowledge-rules")
111
+
112
+
113
+ def test_live_asset_changes_invalidate_catalog_and_document_cache(tmp_path, monkeypatch):
114
+ catalog = installed_catalog(tmp_path, monkeypatch)
115
+ path = catalog.discovered_packages[0].package_root / "axis.yaml"
116
+ before = catalog.catalog_version
117
+ document = catalog.get_document_by_xid("xid-axis-rules")
118
+ path.write_text(path.read_text() + "summary: revised\n")
119
+ current = catalog.get_document_by_xid("xid-axis-rules", document["content_hash"])
120
+ assert not current["content_omitted"]
121
+ assert current["content_hash"] != document["content_hash"]
122
+ assert catalog.catalog_version != before
123
+
124
+
125
+ def test_duplicate_knowledge_cannot_silently_satisfy_required_slot(tmp_path, monkeypatch):
126
+ catalog = installed_catalog(tmp_path, monkeypatch)
127
+ (catalog.repo_root / "knowledge").mkdir()
128
+ (catalog.repo_root / "knowledge/local.md").write_text("<!-- xid: xid-knowledge-rules -->\n# Conflicting local rules")
129
+ with pytest.raises(ValueError, match="knowledge xid conflict"):
130
+ catalog.resolve_skill_knowledge("example.review")
131
+
132
+
133
+ @pytest.mark.parametrize("relative", ["../outside.md", "/absolute.md", "C:/absolute.md", "knowledge/../rules.md", "knowledge\\rules.md"])
134
+ def test_nonportable_or_escaping_declarations_refused(tmp_path, relative):
135
+ catalog = package_catalog(tmp_path, relative)
136
+ with pytest.raises(ValueError, match="package asset escapes root"):
137
+ catalog.get_document_by_xid("xid-knowledge-rules")
138
+
139
+
140
+ def test_symlink_escape_is_not_exposed(tmp_path):
141
+ catalog = package_catalog(tmp_path)
142
+ outside = tmp_path / "outside.md"
143
+ outside.write_text("private outside content")
144
+ inside = catalog.discovered_packages[0].package_root / "knowledge/rules.md"
145
+ inside.unlink()
146
+ try:
147
+ inside.symlink_to(outside)
148
+ except OSError:
149
+ pytest.skip("symlink creation not available")
150
+ with pytest.raises(ValueError, match="escapes root"):
151
+ catalog.get_document_by_xid("xid-knowledge-rules")
152
+
153
+
154
+ def test_package_cannot_shadow_embedded_startup_identity(tmp_path):
155
+ catalog = package_catalog(tmp_path)
156
+ catalog.discovered_packages[0].manifest.provides.knowledge[0].xid = "0B5C58B5E5B2"
157
+ with pytest.raises(ValueError, match="core startup XID"):
158
+ catalog.get_document_by_xid("0B5C58B5E5B2")
159
+
160
+
161
+ def test_inline_identity_mismatch_refused(tmp_path):
162
+ catalog = package_catalog(tmp_path)
163
+ path = catalog.discovered_packages[0].package_root / "knowledge/rules.md"
164
+ path.write_text("<!-- xid: ABCDEF123456 -->\n# Wrong identity")
165
+ with pytest.raises(ValueError, match="identity does not match"):
166
+ catalog.expand_knowledge("xid-knowledge-rules")
@@ -18,6 +18,13 @@ def test_installed_package_run_binds_over_mcp(tmp_path, module, reference, xid):
18
18
  from pathlib import Path
19
19
 
20
20
  pytest.importorskip(module)
21
+ from xrefkit.discovery import discover_skill_packages
22
+ package_id = reference.split("::")[0]
23
+ package = next(item for item in discover_skill_packages() if item.package_id == package_id)
24
+ package_knowledge_xids = [asset.xid for asset in package.manifest.provides.knowledge]
25
+ package_reference_xids = [asset.xid for asset in (
26
+ *package.manifest.provides.review_axes, *package.manifest.provides.schemas
27
+ )]
21
28
  anyio = pytest.importorskip("anyio")
22
29
  pytest.importorskip("mcp")
23
30
  from mcp.client.session import ClientSession
@@ -94,8 +101,30 @@ def test_installed_package_run_binds_over_mcp(tmp_path, module, reference, xid):
94
101
  )
95
102
  assert not requirements.isError, requirements.content
96
103
  assert not requirements.structuredContent["missing"]
104
+ for xid in package_knowledge_xids:
105
+ document = await session.call_tool("get_document_by_xid", {"xid": xid})
106
+ assert not document.isError, document.content
107
+ expanded = await session.call_tool("expand_knowledge", {"xid": xid})
108
+ assert not expanded.isError, expanded.content
109
+ assert expanded.structuredContent["content"]
110
+
97
111
  skill = await session.call_tool("get_skill", {"skill_id": run["skill_id"]})
98
112
  assert not skill.isError, skill.content
99
113
  assert skill.structuredContent["skill_id"] == run["skill_id"]
114
+ for asset_xid in package_reference_xids:
115
+ document = await session.call_tool("get_document_by_xid", {"xid": asset_xid})
116
+ assert not document.isError, document.content
117
+ assert document.structuredContent["content"]
118
+ resolved = await session.call_tool(
119
+ "resolve_skill_knowledge", {"skill_id": run["skill_id"]}
120
+ )
121
+ assert not resolved.isError, resolved.content
122
+ required_xids = {need["xid"] for need in skill.structuredContent["required_knowledge"]}
123
+ assert required_xids <= {slot["bind"] for slot in resolved.structuredContent["slots"]}
124
+ for slot in resolved.structuredContent["slots"]:
125
+ if slot["bind"] in package_knowledge_xids:
126
+ assert slot["satisfied"]
127
+ if not slot["satisfied"]:
128
+ assert slot["slot"] in resolved.structuredContent["unsatisfied_required"]
100
129
 
101
130
  anyio.run(scenario)
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.7.6"
5
+ __version__ = "0.7.8"