xrefkit 0.4.16__tar.gz → 0.5.0__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 (191) hide show
  1. {xrefkit-0.4.16/xrefkit.egg-info → xrefkit-0.5.0}/PKG-INFO +27 -12
  2. {xrefkit-0.4.16 → xrefkit-0.5.0}/README.md +26 -11
  3. {xrefkit-0.4.16 → xrefkit-0.5.0}/pyproject.toml +1 -1
  4. xrefkit-0.5.0/tests/test_artifact_selected_family.py +46 -0
  5. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_boundary_analysis.py +42 -1
  6. xrefkit-0.5.0/tests/test_brownfield_csharp_structure_definitions.py +44 -0
  7. xrefkit-0.5.0/tests/test_business_intake_conversation_definitions.py +46 -0
  8. xrefkit-0.5.0/tests/test_business_intake_definition.py +55 -0
  9. xrefkit-0.5.0/tests/test_catalog_preparation_definition.py +47 -0
  10. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_cli.py +9 -1
  11. xrefkit-0.5.0/tests/test_code_constraint_definition.py +47 -0
  12. xrefkit-0.5.0/tests/test_constraint_derivation_family_definition.py +68 -0
  13. xrefkit-0.5.0/tests/test_constraint_derivation_selected_family.py +44 -0
  14. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_contribution_returns.py +1 -0
  15. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_dashboard.py +68 -0
  16. xrefkit-0.5.0/tests/test_database_batch_definition_migration.py +90 -0
  17. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_decision_trace.py +28 -0
  18. xrefkit-0.5.0/tests/test_design_business_intake_selected_family.py +36 -0
  19. xrefkit-0.5.0/tests/test_design_planning_definition_migration.py +72 -0
  20. xrefkit-0.5.0/tests/test_dotnet_definition_candidate.py +103 -0
  21. xrefkit-0.5.0/tests/test_editorial_family_definition.py +58 -0
  22. xrefkit-0.5.0/tests/test_editorial_intake_definition.py +28 -0
  23. xrefkit-0.5.0/tests/test_editorial_selected_family.py +42 -0
  24. xrefkit-0.5.0/tests/test_execution_binding.py +198 -0
  25. xrefkit-0.5.0/tests/test_existing_skill_definition_batch.py +27 -0
  26. xrefkit-0.5.0/tests/test_final_skilldefinition_batch.py +54 -0
  27. xrefkit-0.5.0/tests/test_flow_selected_family.py +44 -0
  28. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_gateway.py +45 -0
  29. xrefkit-0.5.0/tests/test_gateway_mcp.py +236 -0
  30. xrefkit-0.5.0/tests/test_gateway_skill_adapter.py +342 -0
  31. xrefkit-0.5.0/tests/test_gateway_work_items.py +542 -0
  32. xrefkit-0.5.0/tests/test_implementation_flow_definition.py +75 -0
  33. xrefkit-0.5.0/tests/test_implementation_review_selected_family.py +36 -0
  34. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_instruction_workflow.py +5 -0
  35. xrefkit-0.5.0/tests/test_language_review_definition.py +58 -0
  36. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_mcp_package_routing.py +79 -0
  37. xrefkit-0.5.0/tests/test_mcp_subagent_startup.py +452 -0
  38. xrefkit-0.5.0/tests/test_mcp_subagent_startup_integration.py +170 -0
  39. xrefkit-0.5.0/tests/test_os_family_definition.py +65 -0
  40. xrefkit-0.5.0/tests/test_os_knowledge_family_definition.py +54 -0
  41. xrefkit-0.5.0/tests/test_os_selected_family.py +45 -0
  42. xrefkit-0.5.0/tests/test_qa_report_definition.py +51 -0
  43. xrefkit-0.5.0/tests/test_security_definition_migration.py +37 -0
  44. xrefkit-0.5.0/tests/test_skill_definition.py +157 -0
  45. xrefkit-0.5.0/tests/test_skill_definition_governance.py +121 -0
  46. xrefkit-0.5.0/tests/test_skill_definition_mcp.py +262 -0
  47. xrefkit-0.5.0/tests/test_skill_definition_runtime.py +193 -0
  48. xrefkit-0.5.0/tests/test_subagent_startup.py +154 -0
  49. xrefkit-0.5.0/tests/test_subagent_startup_boundaries.py +153 -0
  50. xrefkit-0.5.0/tests/test_workflow_family_definition.py +60 -0
  51. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_xrefkit_v2_pipeline.py +15 -6
  52. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/__init__.py +1 -1
  53. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/boundary_analysis.py +111 -60
  54. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/dashboard.py +57 -0
  55. xrefkit-0.5.0/xrefkit/execution_binding.py +205 -0
  56. xrefkit-0.5.0/xrefkit/execution_binding_cli.py +20 -0
  57. xrefkit-0.5.0/xrefkit/gateway.py +1733 -0
  58. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/catalog.py +382 -31
  59. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/cli.py +14 -0
  60. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/contracts.py +8 -0
  61. xrefkit-0.5.0/xrefkit/mcp/gateway.py +169 -0
  62. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/schemas.py +10 -6
  63. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/server.py +183 -23
  64. xrefkit-0.5.0/xrefkit/mcp/startup_contract_pack.py +120 -0
  65. xrefkit-0.5.0/xrefkit/mcp/subagent_startup.py +316 -0
  66. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/operations_cli.py +55 -1
  67. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/startup_sources/011_startup_xref_routing.md +5 -3
  68. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/startup_sources/017_base_and_xref_layering.md +3 -1
  69. xrefkit-0.5.0/xrefkit/skill_definition.py +260 -0
  70. xrefkit-0.5.0/xrefkit/skill_definition_catalog.py +91 -0
  71. xrefkit-0.5.0/xrefkit/skill_definition_governance.py +133 -0
  72. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/skillrun.py +105 -16
  73. xrefkit-0.5.0/xrefkit/subagent_startup.py +166 -0
  74. {xrefkit-0.4.16 → xrefkit-0.5.0/xrefkit.egg-info}/PKG-INFO +27 -12
  75. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit.egg-info/SOURCES.txt +45 -1
  76. xrefkit-0.4.16/tests/test_base_sync_ownership.py +0 -123
  77. xrefkit-0.4.16/tests/test_gateway_mcp.py +0 -116
  78. xrefkit-0.4.16/xrefkit/gateway.py +0 -450
  79. xrefkit-0.4.16/xrefkit/mcp/gateway.py +0 -98
  80. xrefkit-0.4.16/xrefkit/mcp/startup_contract_pack.py +0 -170
  81. {xrefkit-0.4.16 → xrefkit-0.5.0}/LICENSE +0 -0
  82. {xrefkit-0.4.16 → xrefkit-0.5.0}/setup.cfg +0 -0
  83. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_brownfield_file_editing_protocol.py +0 -0
  84. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_calibration_lint.py +0 -0
  85. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_check_skill_knowledge_xids.py +0 -0
  86. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_collect_analyzer_sarif.py +0 -0
  87. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_convert_to_xrefkit_skill.py +0 -0
  88. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_cs_scope_probe.py +0 -0
  89. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_csharp_commonality.py +0 -0
  90. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_csharp_naming_profile.py +0 -0
  91. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_ctx.py +0 -0
  92. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_cutover_readiness.py +0 -0
  93. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_error_policy_audit.py +0 -0
  94. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_error_policy_locator.py +0 -0
  95. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_fm_multiroot.py +0 -0
  96. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_gate.py +0 -0
  97. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_goal_desired_state.py +0 -0
  98. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_host_precheck.py +0 -0
  99. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_human_evaluation.py +0 -0
  100. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_inbound_uploads.py +0 -0
  101. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_knowledge_relations_validator.py +0 -0
  102. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_mcp_setup.py +0 -0
  103. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_ownership.py +0 -0
  104. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_packmeta.py +0 -0
  105. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_project_quality_baseline.py +0 -0
  106. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_resource_provider.py +0 -0
  107. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_runtime_contracts.py +0 -0
  108. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_sarif_to_locator.py +0 -0
  109. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_skill_edits.py +0 -0
  110. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_skill_maturity_return.py +0 -0
  111. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_skill_runtime_audit.py +0 -0
  112. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_skillmeta.py +0 -0
  113. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_skills_sync.py +0 -0
  114. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_structure_catalog.py +0 -0
  115. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_wbs.py +0 -0
  116. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_xref.py +0 -0
  117. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_xrefkit_instance.py +0 -0
  118. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_xrefkit_tools.py +0 -0
  119. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_xrefkit_v2_discovery.py +0 -0
  120. {xrefkit-0.4.16 → xrefkit-0.5.0}/tests/test_xrefkit_v2_models.py +0 -0
  121. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/__main__.py +0 -0
  122. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/catalog_cli.py +0 -0
  123. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/cli.py +0 -0
  124. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/contracts.py +0 -0
  125. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/ctx.py +0 -0
  126. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/decision_trace.py +0 -0
  127. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/discovery.py +0 -0
  128. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/gate.py +0 -0
  129. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/goalstate.py +0 -0
  130. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/hashing.py +0 -0
  131. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/host_precheck.py +0 -0
  132. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/import_skill.py +0 -0
  133. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/instance.py +0 -0
  134. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/loaders.py +0 -0
  135. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/__init__.py +0 -0
  136. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/audit.py +0 -0
  137. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/bootstrap.py +0 -0
  138. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/client_cache.py +0 -0
  139. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/client_flow.py +0 -0
  140. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/context_registry.py +0 -0
  141. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/context_token.py +0 -0
  142. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/contribution_adoption.py +0 -0
  143. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/contribution_returns.py +0 -0
  144. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/dist.py +0 -0
  145. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/inbound_uploads.py +0 -0
  146. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/knowledge_edits.py +0 -0
  147. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/ownership.py +0 -0
  148. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/repository.py +0 -0
  149. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/setup.py +0 -0
  150. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/skill_edits.py +0 -0
  151. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp/skill_maturity.py +0 -0
  152. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/mcp_tools.py +0 -0
  153. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/__init__.py +0 -0
  154. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/common.py +0 -0
  155. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/effective_bundle.py +0 -0
  156. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/human_evaluation.py +0 -0
  157. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/local_manifest.py +0 -0
  158. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/package_manifest.py +0 -0
  159. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/run_log.py +0 -0
  160. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/server_config.py +0 -0
  161. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/models/skill_definition.py +0 -0
  162. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/ownership.py +0 -0
  163. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/packmeta.py +0 -0
  164. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/registry.py +0 -0
  165. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resolver.py +0 -0
  166. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resource_provider.py +0 -0
  167. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/contracts.json +0 -0
  168. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/current.json +0 -0
  169. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
  170. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
  171. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
  172. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
  173. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/model_body.md +0 -0
  174. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/startup_sources/000_agent_entry.md +0 -0
  175. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/startup_sources/015_shared_memory_operations.md +0 -0
  176. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/startup_sources/016_uncertainty_protocol.md +0 -0
  177. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/resources/base/startup_sources/053_context_direction_security_guard.md +0 -0
  178. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/runlog.py +0 -0
  179. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/skillmeta.py +0 -0
  180. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/skills_sync.py +0 -0
  181. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/structure_catalog.py +0 -0
  182. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/tools/__init__.py +0 -0
  183. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/tools/__main__.py +0 -0
  184. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/v2_cli.py +0 -0
  185. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/wbs.py +0 -0
  186. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/workspace.py +0 -0
  187. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit/xref.py +0 -0
  188. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit.egg-info/dependency_links.txt +0 -0
  189. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit.egg-info/entry_points.txt +0 -0
  190. {xrefkit-0.4.16 → xrefkit-0.5.0}/xrefkit.egg-info/requires.txt +0 -0
  191. {xrefkit-0.4.16 → xrefkit-0.5.0}/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.4.16
3
+ Version: 0.5.0
4
4
  Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
5
5
  Author: synthaicode
6
6
  License: MIT License
@@ -42,8 +42,8 @@ Dynamic: license-file
42
42
  XRefKit is a framework for making AI-assisted work repeatable, reviewable, and
43
43
  handoff-ready.
44
44
 
45
- It helps teams turn domain procedures and knowledge into work that can be
46
- performed with explicit evidence, human judgment, and completion checks.
45
+ It helps teams structure domain procedures and knowledge so that work can
46
+ record evidence, preserve human judgment, and apply explicit completion checks.
47
47
 
48
48
  ## Why XRefKit?
49
49
 
@@ -61,24 +61,34 @@ XRefKit provides a repository and runtime model for addressing these problems.
61
61
 
62
62
  ## What it provides
63
63
 
64
- - **Skills** — reusable procedures for a defined kind of work
65
- - **Knowledge** — source-backed domain facts and local rules
66
- - **Workflow protocol** — recorded progress, deterministic verification, and
67
- closure checks
64
+ - **Skills** — reusable procedures expressed as SkillDefinitions
65
+ - **Knowledge** — source-backed domain facts and local rules resolved by XID
66
+ when a Skill run needs them
67
+ - **Workflow protocol** — recorded progress, deterministic workflow-record
68
+ verification, and closure checks
68
69
  - **Evidence and handoffs** — outputs, judgments, concerns, and decisions that
69
70
  remain reviewable after the work is done
70
- - **Skill Run Dashboard** — inspect Skill execution status, referenced XIDs,
71
- evidence, judgments, concerns, and handoffs to improve the auditability of
72
- results
71
+ - **Skill Run Dashboard** — inspect recorded Skill execution state, definition
72
+ identity, runtime binding, referenced XIDs, evidence, judgments, concerns,
73
+ and handoffs
73
74
  - **XIDs** — stable references for connecting procedures, knowledge, and
74
75
  supporting documents
75
76
 
76
77
  The package includes the resolver, Skill runtime, workflow controls, client
77
78
  tools, and an optional MCP adapter.
78
79
 
80
+ Current Skill authoring targets the one-document `skill_definition_v1` format.
81
+ Existing `legacy_split_v1` Skills and unversioned historical run logs remain
82
+ readable during migration. The [Workflow Runtime Binding](docs/core/contracts/111_workflow_runtime_binding.md#xid-8D50A972BA9F)
83
+ defines how instruction-derived `capability`, `tuning`, `responsibility`, and
84
+ `execution_mode` are captured; these are not fixed SkillDefinition properties.
85
+ Knowledge used by a Skill remains separate and is resolved from the XID catalog
86
+ when needed.
87
+
79
88
  The [Skill Run Dashboard](docs/guides/086_skill_run_observation_dashboard_usage.md#xid-4A4763A2DE63)
80
- helps people trace how a Skill run used its referenced XIDs and recorded its
81
- evidence before accepting or handing off the result.
89
+ helps people inspect recorded XID retrieval and use together with the run's
90
+ evidence before a human accepts or hands off the result. The Dashboard reports
91
+ recorded state and missing observations; it does not accept output quality.
82
92
 
83
93
  ## Quick start
84
94
 
@@ -104,12 +114,17 @@ python -m pip install "xrefkit[mcp]"
104
114
  xrefkit mcp serve --repo . --transport stdio
105
115
  ```
106
116
 
117
+ This command starts the optional catalog and runtime MCP adapter. This checkout
118
+ does not implement management upload, staging, sealing, review, or adoption of
119
+ uploaded SkillDefinition bytes into the active catalog.
120
+
107
121
  ## Where to go next
108
122
 
109
123
  - [Install XRefKit and register Skill Packages](docs/guides/089_xrefkit_package_first_registration.md#xid-4F8C2A7D1E90)
110
124
  - [Understand the workflow protocol](docs/guides/087_workflow_protocol_sequence_for_humans.md#xid-E8B4D2F19A63)
111
125
  - [Use an instruction-backed workflow](docs/guides/088_instruction_workflow_protocol.md#xid-9F4C2A7D1B60)
112
126
  - [Understand Skills and Knowledge](docs/core/models/052_flow_capability_skill_knowledge_model.md#xid-91C4B7E2D5A8)
127
+ - [Read the SkillDefinition v1 contract](docs/core/contracts/096_skill_definition_contract.md#xid-E6A19D4B72C3)
113
128
  - [Author a Skill with xref](docs/guides/013_skill_authoring_with_xref.md#xid-3DB05A0F5F5B)
114
129
  - [Browse the complete documentation index](docs/000_index.md#xid-56DD6EB68343)
115
130
 
@@ -3,8 +3,8 @@
3
3
  XRefKit is a framework for making AI-assisted work repeatable, reviewable, and
4
4
  handoff-ready.
5
5
 
6
- It helps teams turn domain procedures and knowledge into work that can be
7
- performed with explicit evidence, human judgment, and completion checks.
6
+ It helps teams structure domain procedures and knowledge so that work can
7
+ record evidence, preserve human judgment, and apply explicit completion checks.
8
8
 
9
9
  ## Why XRefKit?
10
10
 
@@ -22,24 +22,34 @@ XRefKit provides a repository and runtime model for addressing these problems.
22
22
 
23
23
  ## What it provides
24
24
 
25
- - **Skills** — reusable procedures for a defined kind of work
26
- - **Knowledge** — source-backed domain facts and local rules
27
- - **Workflow protocol** — recorded progress, deterministic verification, and
28
- closure checks
25
+ - **Skills** — reusable procedures expressed as SkillDefinitions
26
+ - **Knowledge** — source-backed domain facts and local rules resolved by XID
27
+ when a Skill run needs them
28
+ - **Workflow protocol** — recorded progress, deterministic workflow-record
29
+ verification, and closure checks
29
30
  - **Evidence and handoffs** — outputs, judgments, concerns, and decisions that
30
31
  remain reviewable after the work is done
31
- - **Skill Run Dashboard** — inspect Skill execution status, referenced XIDs,
32
- evidence, judgments, concerns, and handoffs to improve the auditability of
33
- results
32
+ - **Skill Run Dashboard** — inspect recorded Skill execution state, definition
33
+ identity, runtime binding, referenced XIDs, evidence, judgments, concerns,
34
+ and handoffs
34
35
  - **XIDs** — stable references for connecting procedures, knowledge, and
35
36
  supporting documents
36
37
 
37
38
  The package includes the resolver, Skill runtime, workflow controls, client
38
39
  tools, and an optional MCP adapter.
39
40
 
41
+ Current Skill authoring targets the one-document `skill_definition_v1` format.
42
+ Existing `legacy_split_v1` Skills and unversioned historical run logs remain
43
+ readable during migration. The [Workflow Runtime Binding](docs/core/contracts/111_workflow_runtime_binding.md#xid-8D50A972BA9F)
44
+ defines how instruction-derived `capability`, `tuning`, `responsibility`, and
45
+ `execution_mode` are captured; these are not fixed SkillDefinition properties.
46
+ Knowledge used by a Skill remains separate and is resolved from the XID catalog
47
+ when needed.
48
+
40
49
  The [Skill Run Dashboard](docs/guides/086_skill_run_observation_dashboard_usage.md#xid-4A4763A2DE63)
41
- helps people trace how a Skill run used its referenced XIDs and recorded its
42
- evidence before accepting or handing off the result.
50
+ helps people inspect recorded XID retrieval and use together with the run's
51
+ evidence before a human accepts or hands off the result. The Dashboard reports
52
+ recorded state and missing observations; it does not accept output quality.
43
53
 
44
54
  ## Quick start
45
55
 
@@ -65,12 +75,17 @@ python -m pip install "xrefkit[mcp]"
65
75
  xrefkit mcp serve --repo . --transport stdio
66
76
  ```
67
77
 
78
+ This command starts the optional catalog and runtime MCP adapter. This checkout
79
+ does not implement management upload, staging, sealing, review, or adoption of
80
+ uploaded SkillDefinition bytes into the active catalog.
81
+
68
82
  ## Where to go next
69
83
 
70
84
  - [Install XRefKit and register Skill Packages](docs/guides/089_xrefkit_package_first_registration.md#xid-4F8C2A7D1E90)
71
85
  - [Understand the workflow protocol](docs/guides/087_workflow_protocol_sequence_for_humans.md#xid-E8B4D2F19A63)
72
86
  - [Use an instruction-backed workflow](docs/guides/088_instruction_workflow_protocol.md#xid-9F4C2A7D1B60)
73
87
  - [Understand Skills and Knowledge](docs/core/models/052_flow_capability_skill_knowledge_model.md#xid-91C4B7E2D5A8)
88
+ - [Read the SkillDefinition v1 contract](docs/core/contracts/096_skill_definition_contract.md#xid-E6A19D4B72C3)
74
89
  - [Author a Skill with xref](docs/guides/013_skill_authoring_with_xref.md#xid-3DB05A0F5F5B)
75
90
  - [Browse the complete documentation index](docs/000_index.md#xid-56DD6EB68343)
76
91
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xrefkit"
7
- version = "0.4.16"
7
+ version = "0.5.0"
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,46 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from xrefkit.mcp.catalog import XRefCatalog
6
+ from xrefkit.skill_definition import load_skill_definition
7
+
8
+
9
+ CASES = [
10
+ ("xlsx_spec_traceability", "xlsx_spec_traceability", "A7C3E9D2F610", "2D1B6A9E7C40", "5A7C2D4E9130"),
11
+ ("marketing-explainer-video", "marketing_explainer_video", "B8F1A6C4D720", "5E147B19D33D", "A6B923E41178"),
12
+ ("marketing_slide_png", "marketing_slide_png", "C2E7B9F5A130", "91B67D4AC2F3", "5E2C4A90D711"),
13
+ ("import_skill", "import_skill", "D5A8C1E6B740", "7C2A492D2B72", "1DF4555E1B02"),
14
+ ]
15
+
16
+
17
+ @pytest.mark.parametrize("directory,skill_id,xid,body_xid,meta_xid", CASES)
18
+ def test_artifact_v1_parse_concrete_metadata_and_legacy(directory, skill_id, xid, body_xid, meta_xid):
19
+ repo = Path(__file__).resolve().parents[1]
20
+ relative = Path(f"skills/{directory}/SKILL.v1.md")
21
+ definition = load_skill_definition(repo / relative)
22
+ metadata = definition["metadata"]
23
+ assert metadata["skill_id"] == skill_id
24
+ assert metadata["xid"] == xid
25
+ assert set(metadata["aliases"]) == {body_xid, meta_xid}
26
+ assert metadata["inputs"] and metadata["outputs"]
27
+ assert metadata["control_refs"] == []
28
+ assert "CAP-" not in definition["method"]
29
+ assert "## Reporting Contract" not in definition["method"]
30
+ assert "skill_doc: `./SKILL.md`" in (repo / relative.parent / "meta.md").read_text()
31
+
32
+
33
+ def test_artifact_v1_explicit_batch_selection_and_knowledge_resolution():
34
+ repo = Path(__file__).resolve().parents[1]
35
+ paths = [Path(f"skills/{directory}/SKILL.v1.md") for directory, *_ in CASES]
36
+ catalog = XRefCatalog.build(repo, skill_definition_paths=paths)
37
+ ids = {case[1] for case in CASES}
38
+ entries = {entry.skill_id: entry for entry in catalog.skills if entry.skill_id in ids}
39
+ assert set(entries) == ids
40
+ assert all(entry.definition_format == "skill_definition_v1" for entry in entries.values())
41
+ for directory, skill_id, *_ in CASES:
42
+ definition = load_skill_definition(repo / f"skills/{directory}/SKILL.v1.md")
43
+ need_ids = [need["id"] for need in definition["metadata"]["knowledge_needs"]]
44
+ result = catalog.resolve_skill_knowledge(skill_id, need_ids)
45
+ assert result["unresolved_activation"] == []
46
+ assert result["unsatisfied_required"] == []
@@ -17,8 +17,9 @@ class BoundaryAnalysisTests(unittest.TestCase):
17
17
  xids: list[str],
18
18
  *,
19
19
  feedback: list[dict[str, str]] | None = None,
20
+ **context: str,
20
21
  ) -> dict[str, object]:
21
- return {
22
+ run: dict[str, object] = {
22
23
  "path": path,
23
24
  "name": Path(path).name,
24
25
  "skill_id": skill_id,
@@ -41,6 +42,8 @@ class BoundaryAnalysisTests(unittest.TestCase):
41
42
  "observation_events": feedback or [],
42
43
  "mcp_events": [],
43
44
  }
45
+ run.update(context)
46
+ return run
44
47
 
45
48
  def _payload(self) -> dict[str, object]:
46
49
  xid_a = "XID-A-001"
@@ -92,6 +95,44 @@ class BoundaryAnalysisTests(unittest.TestCase):
92
95
  self.assertEqual(4, xid_row["run_count"])
93
96
  self.assertEqual(4, xid_row["used_count"])
94
97
 
98
+ def test_analysis_does_not_combine_distinct_definition_bindings(self) -> None:
99
+ feedback = [{"event": "human.feedback", "status": "corrected", "target": "OUT-001"}]
100
+ runs = [
101
+ self._run(
102
+ "alpha", f"a-{index}.md", ["XID-A"], feedback=feedback if index == 1 else [],
103
+ definition_format="skill_definition_v1", definition_xid="DEF-A", definition_sha256="sha-a",
104
+ capability="analysis", tuning="bounded", responsibility="report", execution_mode="subagent_required",
105
+ )
106
+ for index in (1, 2)
107
+ ] + [
108
+ self._run(
109
+ "alpha", f"b-{index}.md", ["XID-B"], feedback=feedback if index == 1 else [],
110
+ definition_format="skill_definition_v1", definition_xid="DEF-A", definition_sha256="sha-b",
111
+ capability="analysis", tuning="deep", responsibility="report", execution_mode="subagent_required",
112
+ )
113
+ for index in (1, 2)
114
+ ]
115
+
116
+ report = analyze_dashboard_payload({"runs": runs}, min_samples=2)
117
+ categories = {item["category"] for item in report["proposals"]}
118
+
119
+ self.assertNotIn("split", categories)
120
+ self.assertNotIn("skill_correction", categories)
121
+
122
+ def test_merge_requires_samples_under_the_same_runtime_binding(self) -> None:
123
+ runs = [
124
+ self._run(
125
+ skill, f"{skill}-{index}.md", ["XID-A"],
126
+ capability="analysis", tuning=tuning, responsibility="report", execution_mode="subagent_required",
127
+ )
128
+ for skill, tuning in (("alpha", "bounded"), ("beta", "deep"))
129
+ for index in (1, 2)
130
+ ]
131
+
132
+ report = analyze_dashboard_payload({"runs": runs}, min_samples=2)
133
+
134
+ self.assertNotIn("merge", {item["category"] for item in report["proposals"]})
135
+
95
136
  def test_markdown_explains_proposal_only_boundary(self) -> None:
96
137
  markdown = render_markdown(analyze_dashboard_payload(self._payload(), source_hash="source-001"))
97
138
 
@@ -0,0 +1,44 @@
1
+ from pathlib import Path
2
+
3
+ from xrefkit.mcp.catalog import XRefCatalog
4
+ from xrefkit.skill_definition import load_skill_definition
5
+
6
+
7
+ CASES = {
8
+ "brownfield-workflow": ("brownfield_workflow", "E1A7C4D9B260", "A17C4E8B2D91", "9B3D7A1C4E20"),
9
+ "csharp_error_policy_extraction": ("csharp_error_policy_extraction", "F2B8D5A1C370", "FE342FB520D0", "B150A2A54169"),
10
+ "source_structure_overview": ("source_structure_overview", "A6E3C9F2B470", "423C8B8F8AD0", "907B100F9F9D"),
11
+ }
12
+
13
+
14
+ def test_tracked_trio_parse_aliases_metadata_and_legacy_readability():
15
+ repo = Path(__file__).resolve().parents[1]
16
+ for directory, (skill_id, xid, body_xid, meta_xid) in CASES.items():
17
+ path = repo / "skills" / directory / "SKILL.v1.md"
18
+ definition = load_skill_definition(path)
19
+ metadata = definition["metadata"]
20
+ assert metadata["skill_id"] == skill_id
21
+ assert metadata["xid"] == xid
22
+ assert set(metadata["aliases"]) == {body_xid, meta_xid}
23
+ assert metadata["control_refs"] == []
24
+ for forbidden in ("capability", "tuning", "responsibility", "execution_mode", "model", "maturity"):
25
+ assert forbidden not in metadata
26
+ for common in ("## Reporting Contract", "## Logging", "## Context Direction Guard"):
27
+ assert common not in definition["method"]
28
+ assert f"<!-- xid: {body_xid} -->" in (repo / "skills" / directory / "SKILL.md").read_text(encoding="utf-8")
29
+ assert f"<!-- xid: {meta_xid} -->" in (repo / "skills" / directory / "meta.md").read_text(encoding="utf-8")
30
+
31
+
32
+ def test_tracked_trio_combined_catalog_and_knowledge_resolution():
33
+ repo = Path(__file__).resolve().parents[1]
34
+ paths = [repo / "skills" / directory / "SKILL.v1.md" for directory in CASES]
35
+ catalog = XRefCatalog.build(repo, skill_definition_paths=paths)
36
+ entries = {entry.skill_id: entry for entry in catalog.skills if entry.skill_id in {case[0] for case in CASES.values()}}
37
+ assert set(entries) == {case[0] for case in CASES.values()}
38
+ assert all(entry.definition_format == "skill_definition_v1" for entry in entries.values())
39
+ for directory, (skill_id, *_rest) in CASES.items():
40
+ definition = load_skill_definition(repo / "skills" / directory / "SKILL.v1.md")
41
+ need_ids = [need["id"] for need in definition["metadata"]["knowledge_needs"]]
42
+ result = catalog.resolve_skill_knowledge(skill_id, need_ids)
43
+ assert result["unresolved_activation"] == []
44
+ assert result["unsatisfied_required"] == []
@@ -0,0 +1,46 @@
1
+ from pathlib import Path
2
+
3
+ from xrefkit.mcp.catalog import XRefCatalog
4
+ from xrefkit.skill_definition import load_skill_definition
5
+
6
+
7
+ CASES = [
8
+ ("conversation_topic_branch_mapping", "9D6A4F2B1C80", "2E755FFA0A86", "59777C84933D", []),
9
+ ("decision_topology_analysis", "B7E3C1A9D640", "502BB91D25FA", "2C927E868B25", ["7B3E5D1A6102", "7B3E5D1A6103"]),
10
+ ]
11
+
12
+
13
+ def test_business_intake_v1_definitions_parse_aliases_and_preserve_legacy():
14
+ repo = Path(__file__).resolve().parents[1]
15
+ for skill_id, xid, body_xid, meta_xid, _ in CASES:
16
+ relative = Path("skills/packs/business-intake") / skill_id / "SKILL.v1.md"
17
+ definition = load_skill_definition(repo / relative)
18
+ metadata = definition["metadata"]
19
+ assert metadata["xid"] == xid
20
+ assert set(metadata["aliases"]) == {body_xid, meta_xid}
21
+ assert metadata["control_refs"] == []
22
+ for forbidden in ("capability", "tuning", "responsibility", "execution_mode", "model", "maturity"):
23
+ assert forbidden not in metadata
24
+ assert "## Context Direction Guard" not in definition["method"]
25
+ assert "## Reporting Contract" not in definition["method"]
26
+ assert f"<!-- xid: {body_xid} -->" in (repo / relative.parent / "SKILL.md").read_text(encoding="utf-8")
27
+ assert f"skill_doc: `./SKILL.md`" in (repo / relative.parent / "meta.md").read_text(encoding="utf-8")
28
+
29
+
30
+ def test_business_intake_v1_explicit_catalog_selection_and_knowledge_resolution():
31
+ repo = Path(__file__).resolve().parents[1]
32
+ paths = [Path("skills/packs/business-intake") / skill_id / "SKILL.v1.md" for skill_id, *_ in CASES]
33
+ catalog = XRefCatalog.build(repo, skill_definition_paths=paths)
34
+ entries = [entry for entry in catalog.skills if entry.skill_id in {case[0] for case in CASES}]
35
+ assert len(entries) == 2
36
+ assert all(entry.definition_format == "skill_definition_v1" for entry in entries)
37
+ assert {entry.definition_xid for entry in entries} == {case[1] for case in CASES}
38
+ for skill_id, _, _, _, seed_xids in CASES:
39
+ definition = load_skill_definition(repo / (Path("skills/packs/business-intake") / skill_id / "SKILL.v1.md"))
40
+ need_ids = [need["id"] for need in definition["metadata"]["knowledge_needs"]]
41
+ if need_ids:
42
+ resolved = catalog.resolve_skill_knowledge(skill_id, need_ids)
43
+ assert resolved["unresolved_activation"] == []
44
+ assert resolved["unsatisfied_required"] == []
45
+ assert {candidate["xid"] for need in resolved["needs"] for candidate in need["candidates"]} >= set(seed_xids)
46
+
@@ -0,0 +1,55 @@
1
+ from pathlib import Path
2
+
3
+ from xrefkit.mcp.catalog import XRefCatalog
4
+ from xrefkit.skill_definition import load_skill_definition
5
+
6
+
7
+ CASES = (
8
+ ("business_learning_interview", "B6C4E9A2D781", "4D8E1A7C5B92", "2B6D4F18A3C1", "7B3E5D1A6103"),
9
+ ("business_intake_scoping", "B6C4E9A2D782", "6F2A9C41E8B3", "4E9B2C18D740", "7B3E5D1A6102"),
10
+ )
11
+
12
+
13
+ def test_business_intake_v1_definitions_parse_select_resolve_and_preserve_legacy():
14
+ repo = Path(__file__).resolve().parents[1]
15
+ paths = []
16
+ for skill_id, xid, body_xid, meta_xid, rule_xid in CASES:
17
+ directory = repo / "skills/packs/business-intake" / skill_id
18
+ definition_path = directory / "SKILL.v1.md"
19
+ paths.append(definition_path.relative_to(repo))
20
+ definition = load_skill_definition(definition_path)
21
+ metadata = definition["metadata"]
22
+ assert metadata["schema_version"] == 1
23
+ assert metadata["skill_id"] == skill_id
24
+ assert metadata["xid"] == xid
25
+ assert set(metadata["aliases"]) == {body_xid, meta_xid}
26
+ assert metadata["xid"] not in metadata["aliases"]
27
+ assert len(metadata["aliases"]) == len(set(metadata["aliases"]))
28
+ assert not {
29
+ "capability", "tuning", "responsibility", "execution_mode",
30
+ "model", "model_tier", "maturity",
31
+ } & set(metadata)
32
+ assert metadata["control_refs"] == []
33
+ assert metadata["knowledge_needs"][0]["seed_xids"] == [rule_xid]
34
+ assert "docs/packs/business-intake/" in definition["method"]
35
+
36
+ catalog = XRefCatalog.build(repo, skill_definition_paths=[definition_path.relative_to(repo)])
37
+ entries = [entry for entry in catalog.skills if entry.skill_id == skill_id]
38
+ assert len(entries) == 1
39
+ assert entries[0].definition_format == "skill_definition_v1"
40
+ assert entries[0].definition_xid == xid
41
+ resolved = catalog.resolve_skill_knowledge(skill_id, ["business_" + ("learning_interview" if skill_id == "business_learning_interview" else "intake_scoping") + "_rules"])
42
+ assert resolved["unresolved_activation"] == []
43
+ assert resolved["unsatisfied_required"] == []
44
+ assert resolved["needs"][0]["satisfied"] is True
45
+
46
+ legacy = (directory / "SKILL.md").read_text(encoding="utf-8")
47
+ meta = (directory / "meta.md").read_text(encoding="utf-8")
48
+ assert f"<!-- xid: {body_xid} -->" in legacy
49
+ assert f"<!-- xid: {meta_xid} -->" in meta
50
+ assert "skill_doc: `./SKILL.md`" in meta
51
+
52
+ catalog = XRefCatalog.build(repo, skill_definition_paths=paths)
53
+ assert {entry.skill_id for entry in catalog.skills if entry.definition_format == "skill_definition_v1"} == {
54
+ "business_learning_interview", "business_intake_scoping"
55
+ }
@@ -0,0 +1,47 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from xrefkit.mcp.catalog import XRefCatalog
6
+ from xrefkit.skill_definition import load_skill_definition
7
+
8
+
9
+ CASES = [
10
+ ("domain_knowledge_catalog_preparation", "skills/os/domain_knowledge_catalog_preparation", "A7C9E2D4F610", "EE95165681E8", "A409BACC6918"),
11
+ ("skill_flow_authoring", "skills/os/skill_flow_authoring", "B8D1A4C7E260", "C1B7A42D8E53", "D37E2B5C8A41"),
12
+ ("test_tool_catalog_preparation", "skills/test_tool_catalog_preparation", "C9E2B5D8F370", "DE815228B04C", "F2A36B52C0EB"),
13
+ ]
14
+
15
+
16
+ @pytest.mark.parametrize("skill_id,folder,xid,body_xid,meta_xid", CASES)
17
+ def test_v1_definitions_parse_with_aliases_and_concrete_metadata(skill_id, folder, xid, body_xid, meta_xid):
18
+ repo = Path(__file__).resolve().parents[1]
19
+ definition_path = repo / folder / "SKILL.v1.md"
20
+ definition = load_skill_definition(definition_path)
21
+ metadata = definition["metadata"]
22
+ assert metadata["skill_id"] == skill_id
23
+ assert metadata["xid"] == xid
24
+ assert set(metadata["aliases"]) == {body_xid, meta_xid}
25
+ assert metadata["inputs"] and metadata["outputs"]
26
+ assert metadata["control_refs"] == []
27
+ assert not any(key in metadata for key in ("capability", "tuning", "responsibility", "execution_mode", "model", "model_tier", "maturity"))
28
+ assert "CAP-" not in definition["method"]
29
+ assert "## Reporting Contract" not in definition["method"]
30
+ assert "## Required Knowledge (XID)" not in definition["method"]
31
+ assert (repo / folder / "SKILL.md").read_text(encoding="utf-8").startswith("<!-- xid: ")
32
+ assert (repo / folder / "meta.md").read_text(encoding="utf-8").startswith("<!-- xid: ")
33
+
34
+
35
+ def test_explicit_catalog_and_knowledge_resolution_cover_the_trio():
36
+ repo = Path(__file__).resolve().parents[1]
37
+ paths = [repo / folder / "SKILL.v1.md" for _, folder, *_ in CASES]
38
+ catalog = XRefCatalog.build(repo, skill_definition_paths=paths)
39
+ entries = {entry.skill_id: entry for entry in catalog.skills if entry.skill_id in {case[0] for case in CASES}}
40
+ assert set(entries) == {case[0] for case in CASES}
41
+ assert all(entry.definition_format == "skill_definition_v1" for entry in entries.values())
42
+ for skill_id, folder, *_ in CASES:
43
+ definition = load_skill_definition(repo / folder / "SKILL.v1.md")
44
+ need_ids = [need["id"] for need in definition["metadata"]["knowledge_needs"]]
45
+ result = catalog.resolve_skill_knowledge(skill_id, need_ids)
46
+ assert result["unresolved_activation"] == []
47
+ assert result["unsatisfied_required"] == []
@@ -307,7 +307,15 @@ class CliTests(unittest.TestCase):
307
307
  text = out.read_text(encoding="utf-8")
308
308
  self.assertIn("## Skill Load Gate", text)
309
309
  self.assertIn("## Runtime Role Assignment", text)
310
- self.assertIn("## Capability Layering", text)
310
+ self.assertIn("## Workflow Runtime Binding", text)
311
+ runtime_role_assignment = text.split("## Runtime Role Assignment", 1)[1].split("## Role Responsibilities", 1)[0]
312
+ self.assertNotIn("- capability: ", runtime_role_assignment)
313
+ self.assertNotIn("- tuning: ", runtime_role_assignment)
314
+ self.assertNotIn("- responsibility: ", runtime_role_assignment)
315
+ self.assertIn("- owner: `workflow_protocol`", text)
316
+ self.assertIn("- contract_xid: `8D50A972BA9F`", text)
317
+ self.assertIn("- source: `legacy_meta_compatibility`", text)
318
+ self.assertIn("- instruction_basis: `run.task`", text)
311
319
  self.assertIn("- capability_layering: `required`", text)
312
320
  self.assertIn("- workflow_protocol: `required`", text)
313
321
  self.assertIn("- tuning: `sample specialization`", text)
@@ -0,0 +1,47 @@
1
+ from pathlib import Path
2
+
3
+ from xrefkit.mcp.catalog import XRefCatalog
4
+ from xrefkit.skill_definition import load_skill_definition
5
+
6
+
7
+ def test_code_constraint_v1_parses_with_unique_legacy_aliases_and_catalog_selection():
8
+ repo = Path(__file__).resolve().parents[1]
9
+ relative = Path("skills/packs/constraint-derivation/code_constraint_derivation/SKILL.v1.md")
10
+ definition = load_skill_definition(repo / relative)
11
+ metadata = definition["metadata"]
12
+
13
+ assert metadata["schema_version"] == 1
14
+ assert metadata["xid"] == "7C4E9A1B2D60"
15
+ assert set(metadata["aliases"]) == {"D4701BFC6EA4", "D4701BFC6EA5"}
16
+ assert metadata["xid"] not in metadata["aliases"]
17
+ assert len(metadata["aliases"]) == len(set(metadata["aliases"]))
18
+ assert not {"capability", "tuning", "responsibility", "execution_mode", "model_tier", "maturity"} & set(metadata)
19
+
20
+ catalog = XRefCatalog.build(repo, skill_definition_paths=[relative])
21
+ entries = [entry for entry in catalog.skills if entry.skill_id == "code_constraint_derivation"]
22
+ assert len(entries) == 1
23
+ assert entries[0].definition_format == "skill_definition_v1"
24
+ assert entries[0].definition_xid == metadata["xid"]
25
+
26
+
27
+ def test_code_constraint_v1_resolves_selected_knowledge_and_legacy_files_are_unchanged():
28
+ repo = Path(__file__).resolve().parents[1]
29
+ relative = Path("skills/packs/constraint-derivation/code_constraint_derivation/SKILL.v1.md")
30
+ definition = load_skill_definition(repo / relative)
31
+ need_ids = [need["id"] for need in definition["metadata"]["knowledge_needs"]]
32
+
33
+ catalog = XRefCatalog.build(repo, skill_definition_paths=[relative])
34
+ result = catalog.resolve_skill_knowledge("code_constraint_derivation", need_ids)
35
+ assert result["unresolved_activation"] == []
36
+ assert result["unsatisfied_required"] == []
37
+ assert [need["id"] for need in result["needs"]] == need_ids
38
+ assert all(need["activation_state"] == "active" for need in result["needs"])
39
+ assert all(need["satisfied"] is True for need in result["needs"])
40
+
41
+ meta = (repo / "skills/packs/constraint-derivation/code_constraint_derivation/meta.md").read_text(encoding="utf-8")
42
+ legacy = (repo / "skills/packs/constraint-derivation/code_constraint_derivation/SKILL.md").read_text(encoding="utf-8")
43
+ assert "skill_doc: `./SKILL.md`" in meta
44
+ assert "<!-- xid: D4701BFC6EA4 -->" in meta
45
+ assert "<!-- xid: D4701BFC6EA5 -->" in legacy
46
+ assert "## Reporting Contract" not in definition["method"]
47
+ assert "111D282CA0EA" in definition["metadata"]["control_refs"]
@@ -0,0 +1,68 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from xrefkit.mcp.catalog import XRefCatalog
6
+ from xrefkit.skill_definition import load_skill_definition
7
+
8
+
9
+ CASES = [
10
+ ("integration_constraint_derivation", "9A1C4E7D2B60", "E547F90B1235", "E547F90B1234", ["81A6C4E2B190", "6F0D7C1A2E44"]),
11
+ ("integration_scenario_derivation", "5F8B2D6A1C90", "F6923D1E80C9", "F6923D1E80C8", ["81A6C4E2B190", "C3F60AEB5D93"]),
12
+ ("logic_constraint_derivation", "3D7A9E2B4C60", "D436E8FA0124", "D436E8FA0123", ["81A6C4E2B190", "4E5B8923C912"]),
13
+ ("ui_constraint_derivation", "6B1F8D3A5E20", "C325D7E9F123", "C325D7E9F122", ["81A6C4E2B190", "31C5A06B7E22"]),
14
+ ("constraint_derivation_index", "A4C9E2B7D160", "A103B5C7D901", "A103B5C7D900", ["81A6C4E2B190"]),
15
+ ]
16
+
17
+
18
+ @pytest.mark.parametrize("skill_id,xid,body_xid,meta_xid,seed_xids", CASES)
19
+ def test_constraint_derivation_v1_migration(skill_id, xid, body_xid, meta_xid, seed_xids):
20
+ repo = Path(__file__).resolve().parents[1]
21
+ relative = Path("skills/packs/constraint-derivation") / skill_id / "SKILL.v1.md"
22
+ definition = load_skill_definition(repo / relative)
23
+ metadata = definition["metadata"]
24
+ assert metadata["xid"] == xid
25
+ assert set(metadata["aliases"]) == {body_xid, meta_xid}
26
+ assert metadata["control_refs"] == ["111D282CA0EA"]
27
+ assert "## Reporting Contract" not in definition["method"]
28
+ assert "## Required Knowledge (XID)" not in definition["method"]
29
+ assert "Context direction guard" not in definition["method"]
30
+
31
+ legacy = (repo / relative.parent / "SKILL.md").read_bytes()
32
+ assert f"<!-- xid: {body_xid} -->".encode() in legacy
33
+ assert f"skill_doc: `./SKILL.md`".encode() in (repo / relative.parent / "meta.md").read_bytes()
34
+
35
+
36
+ def test_constraint_derivation_v1_explicit_selection_and_knowledge_resolution():
37
+ repo = Path(__file__).resolve().parents[1]
38
+ paths = [Path("skills/packs/constraint-derivation") / skill / "SKILL.v1.md" for skill, *_ in CASES]
39
+ catalog = XRefCatalog.build(repo, skill_definition_paths=paths)
40
+ entries = [entry for entry in catalog.skills if entry.skill_id in {case[0] for case in CASES}]
41
+ assert len(entries) == len(CASES)
42
+ assert all(entry.definition_format == "skill_definition_v1" for entry in entries)
43
+ assert {entry.definition_xid for entry in entries} == {case[1] for case in CASES}
44
+
45
+ for skill_id, _, _, _, seed_xids in CASES:
46
+ relative = Path("skills/packs/constraint-derivation") / skill_id / "SKILL.v1.md"
47
+ definition = load_skill_definition(repo / relative)
48
+ result = catalog.resolve_skill_knowledge(skill_id, [need["id"] for need in definition["metadata"]["knowledge_needs"]])
49
+ assert result["unresolved_activation"] == []
50
+ assert result["unsatisfied_required"] == []
51
+ for expected, need in zip(seed_xids, result["needs"], strict=True):
52
+ assert expected in [candidate["xid"] for candidate in need["candidates"]]
53
+
54
+
55
+ def test_complete_constraint_derivation_family_selects_without_identity_collisions():
56
+ repo = Path(__file__).resolve().parents[1]
57
+ family_root = Path("skills/packs/constraint-derivation")
58
+ paths = sorted(family_root.glob("*/SKILL.v1.md"))
59
+ relative_paths = [path.relative_to(repo) if path.is_absolute() else path for path in paths]
60
+
61
+ assert len(relative_paths) == 11
62
+ catalog = XRefCatalog.build(repo, skill_definition_paths=relative_paths)
63
+ family_ids = {path.parent.name for path in relative_paths}
64
+ entries = [entry for entry in catalog.skills if entry.skill_id in family_ids]
65
+
66
+ assert {entry.skill_id for entry in entries} == family_ids
67
+ assert len({entry.definition_xid for entry in entries}) == 11
68
+ assert all(entry.definition_format == "skill_definition_v1" for entry in entries)