ssot-core 0.2.19.dev2__tar.gz → 0.2.20.dev1__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 (141) hide show
  1. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/PKG-INFO +7 -6
  2. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/README.md +3 -2
  3. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/pyproject.toml +4 -4
  4. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_core.egg-info/PKG-INFO +7 -6
  5. ssot_core-0.2.20.dev1/src/ssot_core.egg-info/requires.txt +7 -0
  6. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/__init__.py +2 -1
  7. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/init.py +2 -2
  8. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/profile_eval.py +9 -3
  9. ssot_core-0.2.20.dev1/src/ssot_registry/api/save.py +37 -0
  10. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/status_sync.py +4 -4
  11. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/scaffold.py +2 -2
  12. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/claim_closure.py +43 -3
  13. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/feature_claims.py +53 -1
  14. ssot_core-0.2.19.dev2/src/ssot_core.egg-info/requires.txt +0 -7
  15. ssot_core-0.2.19.dev2/src/ssot_registry/api/save.py +0 -9
  16. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/setup.cfg +0 -0
  17. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_core.egg-info/SOURCES.txt +0 -0
  18. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_core.egg-info/dependency_links.txt +0 -0
  19. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_core.egg-info/top_level.txt +0 -0
  20. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/__init__.py +0 -0
  21. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/__main__.py +0 -0
  22. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/acl/__init__.py +0 -0
  23. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/acl/policy.py +0 -0
  24. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/acl/wrapper.py +0 -0
  25. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/boundary.py +0 -0
  26. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/claims.py +0 -0
  27. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/config.py +0 -0
  28. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/documents.py +0 -0
  29. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/entity_ops.py +0 -0
  30. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/evidence.py +0 -0
  31. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/graph.py +0 -0
  32. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/lifecycle.py +0 -0
  33. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/load.py +0 -0
  34. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/local_assurance.py +0 -0
  35. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/origin.py +0 -0
  36. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/packs.py +0 -0
  37. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/plan.py +0 -0
  38. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/profile_resolution.py +0 -0
  39. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/registry.py +0 -0
  40. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/release.py +0 -0
  41. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/test_execution.py +0 -0
  42. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/upgrade.py +0 -0
  43. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/api/validate.py +0 -0
  44. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/__init__.py +0 -0
  45. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/adr_cmd.py +0 -0
  46. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/boundary_cmd.py +0 -0
  47. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/claim_cmd.py +0 -0
  48. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/common.py +0 -0
  49. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/evidence_cmd.py +0 -0
  50. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/feature_cmd.py +0 -0
  51. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/graph_cmd.py +0 -0
  52. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/init_cmd.py +0 -0
  53. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/issue_cmd.py +0 -0
  54. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/main.py +0 -0
  55. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/profile_cmd.py +0 -0
  56. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/registry_cmd.py +0 -0
  57. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/release_cmd.py +0 -0
  58. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/risk_cmd.py +0 -0
  59. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/spec_cmd.py +0 -0
  60. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/test_cmd.py +0 -0
  61. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/upgrade_cmd.py +0 -0
  62. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/cli/validate_cmd.py +0 -0
  63. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/__init__.py +0 -0
  64. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/events.py +0 -0
  65. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/models.py +0 -0
  66. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/paths.py +0 -0
  67. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/service.py +0 -0
  68. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/sqlite_store.py +0 -0
  69. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/control/sse.py +0 -0
  70. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/graph/__init__.py +0 -0
  71. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/graph/export_dot.py +0 -0
  72. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/graph/export_json.py +0 -0
  73. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/__init__.py +0 -0
  74. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/certification.py +0 -0
  75. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/claim_tier_gates.py +0 -0
  76. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/completion.py +0 -0
  77. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/document_lifecycle.py +0 -0
  78. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/document_supersession.py +0 -0
  79. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/feature_requirements.py +0 -0
  80. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/lifecycle.py +0 -0
  81. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/profile_requirements.py +0 -0
  82. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/promotion.py +0 -0
  83. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/guards/publication.py +0 -0
  84. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/maturation/__init__.py +0 -0
  85. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/maturation/selector.py +0 -0
  86. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/__init__.py +0 -0
  87. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/boundary.py +0 -0
  88. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/claim.py +0 -0
  89. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/document.py +0 -0
  90. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/enums.py +0 -0
  91. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/evidence.py +0 -0
  92. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/feature.py +0 -0
  93. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/ids.py +0 -0
  94. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/issue.py +0 -0
  95. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/profile.py +0 -0
  96. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/registry.py +0 -0
  97. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/release.py +0 -0
  98. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/risk.py +0 -0
  99. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/schema_version.py +0 -0
  100. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/model/test.py +0 -0
  101. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/reports/__init__.py +0 -0
  102. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/reports/certification_report.py +0 -0
  103. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/reports/summary.py +0 -0
  104. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/reports/validation_report.py +0 -0
  105. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/snapshots/__init__.py +0 -0
  106. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/snapshots/boundary_snapshot.py +0 -0
  107. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/snapshots/hashing.py +0 -0
  108. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/snapshots/published_snapshot.py +0 -0
  109. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/snapshots/release_snapshot.py +0 -0
  110. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/templates/__init__.py +0 -0
  111. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/templates/registry.full.json +0 -0
  112. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/templates/registry.minimal.json +0 -0
  113. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/__init__.py +0 -0
  114. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/document_io.py +0 -0
  115. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/errors.py +0 -0
  116. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/formatting.py +0 -0
  117. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/fs.py +0 -0
  118. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/jcs.py +0 -0
  119. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/jsonio.py +0 -0
  120. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/registry_lock.py +0 -0
  121. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/util/time.py +0 -0
  122. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/__init__.py +0 -0
  123. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/bidirectional.py +0 -0
  124. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/bounds.py +0 -0
  125. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/claim_lineage.py +0 -0
  126. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/coverage.py +0 -0
  127. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/documents.py +0 -0
  128. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/feature_parent_links.py +0 -0
  129. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/filesystem.py +0 -0
  130. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/identity.py +0 -0
  131. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/lifecycle.py +0 -0
  132. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/origin.py +0 -0
  133. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/promotion.py +0 -0
  134. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/references.py +0 -0
  135. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/reservations.py +0 -0
  136. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/structure.py +0 -0
  137. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/validators/tiers.py +0 -0
  138. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/version.py +0 -0
  139. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/watch/__init__.py +0 -0
  140. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/watch/git_status.py +0 -0
  141. {ssot_core-0.2.19.dev2 → ssot_core-0.2.20.dev1}/src/ssot_registry/watch/observer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssot-core
3
- Version: 0.2.19.dev2
3
+ Version: 0.2.20.dev1
4
4
  Summary: Core Python runtime, registry model, validation, and release workflow APIs for SSOT.
5
5
  Author-email: Jacob Stewart <jacob@swarmauri.com>
6
6
  License-Expression: Apache-2.0
@@ -36,9 +36,9 @@ Classifier: Topic :: Utilities
36
36
  Requires-Python: <3.15,>=3.10
37
37
  Description-Content-Type: text/markdown
38
38
  Requires-Dist: orjson<4.0,>=3.10
39
- Requires-Dist: ssot-contracts==0.2.19.dev2
40
- Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.20.dev2
41
- Requires-Dist: ssot-views==0.2.19.dev2
39
+ Requires-Dist: ssot-contracts==0.2.20.dev1
40
+ Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.21.dev1
41
+ Requires-Dist: ssot-views==0.2.20.dev1
42
42
  Requires-Dist: tomli>=2.0.1; python_version < "3.11"
43
43
 
44
44
  <div align="center">
@@ -59,7 +59,7 @@ Requires-Dist: tomli>=2.0.1; python_version < "3.11"
59
59
 
60
60
  `ssot-core` is the core Python runtime package for SSOT.
61
61
 
62
- It owns the canonical registry model, core APIs for loading and mutating registries, validation and guard logic, planning and release workflows, and the domain operations that [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), and [ssot-tui](https://pypi.org/project/ssot-tui/) build on top of.
62
+ It owns the canonical registry model, core APIs for loading and mutating registries, validation and guard logic, planning and release workflows, and the domain operations that [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-mcp](https://pypi.org/project/ssot-mcp/), and [ssot-tui](https://pypi.org/project/ssot-tui/) build on top of.
63
63
 
64
64
  - GitHub: https://github.com/groupsum/ssot-registry
65
65
 
@@ -103,6 +103,7 @@ ADR and SPEC companion documents are canonically authored as JSON under `.ssot/a
103
103
  python -m pip install ssot-core # core library/runtime only
104
104
  python -m pip install ssot-cli # primary CLI distribution + ssot-core + ssot-conformance
105
105
  python -m pip install ssot-registry # umbrella bundle: ssot-core + ssot-cli + ssot-conformance
106
+ python -m pip install "ssot-registry[mcp]" # optional MCP server bundle
106
107
  python -m pip install "ssot-registry[tui]" # optional TUI bundle
107
108
  ```
108
109
 
@@ -941,6 +942,6 @@ ssot-registry registry export . --format toml --output .ssot/exports/registry.to
941
942
 
942
943
  - Package type: core runtime/library package
943
944
  - Depends on: [ssot-contracts](https://pypi.org/project/ssot-contracts/), [ssot-views](https://pypi.org/project/ssot-views/)
944
- - Consumed by: [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-tui](https://pypi.org/project/ssot-tui/), direct Python integrations, and automation
945
+ - Consumed by: [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-mcp](https://pypi.org/project/ssot-mcp/), [ssot-tui](https://pypi.org/project/ssot-tui/), direct Python integrations, and automation
945
946
 
946
947
  If you are embedding SSOT behavior inside Python code, this is the package to import. If you need the primary CLI distribution, install [ssot-cli](https://pypi.org/project/ssot-cli/) alongside it.
@@ -16,7 +16,7 @@
16
16
 
17
17
  `ssot-core` is the core Python runtime package for SSOT.
18
18
 
19
- It owns the canonical registry model, core APIs for loading and mutating registries, validation and guard logic, planning and release workflows, and the domain operations that [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), and [ssot-tui](https://pypi.org/project/ssot-tui/) build on top of.
19
+ It owns the canonical registry model, core APIs for loading and mutating registries, validation and guard logic, planning and release workflows, and the domain operations that [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-mcp](https://pypi.org/project/ssot-mcp/), and [ssot-tui](https://pypi.org/project/ssot-tui/) build on top of.
20
20
 
21
21
  - GitHub: https://github.com/groupsum/ssot-registry
22
22
 
@@ -60,6 +60,7 @@ ADR and SPEC companion documents are canonically authored as JSON under `.ssot/a
60
60
  python -m pip install ssot-core # core library/runtime only
61
61
  python -m pip install ssot-cli # primary CLI distribution + ssot-core + ssot-conformance
62
62
  python -m pip install ssot-registry # umbrella bundle: ssot-core + ssot-cli + ssot-conformance
63
+ python -m pip install "ssot-registry[mcp]" # optional MCP server bundle
63
64
  python -m pip install "ssot-registry[tui]" # optional TUI bundle
64
65
  ```
65
66
 
@@ -898,6 +899,6 @@ ssot-registry registry export . --format toml --output .ssot/exports/registry.to
898
899
 
899
900
  - Package type: core runtime/library package
900
901
  - Depends on: [ssot-contracts](https://pypi.org/project/ssot-contracts/), [ssot-views](https://pypi.org/project/ssot-views/)
901
- - Consumed by: [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-tui](https://pypi.org/project/ssot-tui/), direct Python integrations, and automation
902
+ - Consumed by: [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-mcp](https://pypi.org/project/ssot-mcp/), [ssot-tui](https://pypi.org/project/ssot-tui/), direct Python integrations, and automation
902
903
 
903
904
  If you are embedding SSOT behavior inside Python code, this is the package to import. If you need the primary CLI distribution, install [ssot-cli](https://pypi.org/project/ssot-cli/) alongside it.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ssot-core"
7
- version = "0.2.19.dev2"
7
+ version = "0.2.20.dev1"
8
8
  description = "Core Python runtime, registry model, validation, and release workflow APIs for SSOT."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10,<3.15"
@@ -12,9 +12,9 @@ license = "Apache-2.0"
12
12
  authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
13
13
  dependencies = [
14
14
  "orjson>=3.10,<4.0",
15
- "ssot-contracts==0.2.19.dev2",
16
- "ssot-pack-contracts>=0.2.20.dev2,<0.3.0",
17
- "ssot-views==0.2.19.dev2",
15
+ "ssot-contracts==0.2.20.dev1",
16
+ "ssot-pack-contracts>=0.2.21.dev1,<0.3.0",
17
+ "ssot-views==0.2.20.dev1",
18
18
  "tomli>=2.0.1; python_version < '3.11'",
19
19
  ]
20
20
  keywords = ["ssot", "core", "registry", "validation", "release-management", "governance", "compliance"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssot-core
3
- Version: 0.2.19.dev2
3
+ Version: 0.2.20.dev1
4
4
  Summary: Core Python runtime, registry model, validation, and release workflow APIs for SSOT.
5
5
  Author-email: Jacob Stewart <jacob@swarmauri.com>
6
6
  License-Expression: Apache-2.0
@@ -36,9 +36,9 @@ Classifier: Topic :: Utilities
36
36
  Requires-Python: <3.15,>=3.10
37
37
  Description-Content-Type: text/markdown
38
38
  Requires-Dist: orjson<4.0,>=3.10
39
- Requires-Dist: ssot-contracts==0.2.19.dev2
40
- Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.20.dev2
41
- Requires-Dist: ssot-views==0.2.19.dev2
39
+ Requires-Dist: ssot-contracts==0.2.20.dev1
40
+ Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.21.dev1
41
+ Requires-Dist: ssot-views==0.2.20.dev1
42
42
  Requires-Dist: tomli>=2.0.1; python_version < "3.11"
43
43
 
44
44
  <div align="center">
@@ -59,7 +59,7 @@ Requires-Dist: tomli>=2.0.1; python_version < "3.11"
59
59
 
60
60
  `ssot-core` is the core Python runtime package for SSOT.
61
61
 
62
- It owns the canonical registry model, core APIs for loading and mutating registries, validation and guard logic, planning and release workflows, and the domain operations that [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), and [ssot-tui](https://pypi.org/project/ssot-tui/) build on top of.
62
+ It owns the canonical registry model, core APIs for loading and mutating registries, validation and guard logic, planning and release workflows, and the domain operations that [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-mcp](https://pypi.org/project/ssot-mcp/), and [ssot-tui](https://pypi.org/project/ssot-tui/) build on top of.
63
63
 
64
64
  - GitHub: https://github.com/groupsum/ssot-registry
65
65
 
@@ -103,6 +103,7 @@ ADR and SPEC companion documents are canonically authored as JSON under `.ssot/a
103
103
  python -m pip install ssot-core # core library/runtime only
104
104
  python -m pip install ssot-cli # primary CLI distribution + ssot-core + ssot-conformance
105
105
  python -m pip install ssot-registry # umbrella bundle: ssot-core + ssot-cli + ssot-conformance
106
+ python -m pip install "ssot-registry[mcp]" # optional MCP server bundle
106
107
  python -m pip install "ssot-registry[tui]" # optional TUI bundle
107
108
  ```
108
109
 
@@ -941,6 +942,6 @@ ssot-registry registry export . --format toml --output .ssot/exports/registry.to
941
942
 
942
943
  - Package type: core runtime/library package
943
944
  - Depends on: [ssot-contracts](https://pypi.org/project/ssot-contracts/), [ssot-views](https://pypi.org/project/ssot-views/)
944
- - Consumed by: [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-tui](https://pypi.org/project/ssot-tui/), direct Python integrations, and automation
945
+ - Consumed by: [ssot-cli](https://pypi.org/project/ssot-cli/), [ssot-conformance](https://pypi.org/project/ssot-conformance/), [ssot-mcp](https://pypi.org/project/ssot-mcp/), [ssot-tui](https://pypi.org/project/ssot-tui/), direct Python integrations, and automation
945
946
 
946
947
  If you are embedding SSOT behavior inside Python code, this is the package to import. If you need the primary CLI distribution, install [ssot-cli](https://pypi.org/project/ssot-cli/) alongside it.
@@ -0,0 +1,7 @@
1
+ orjson<4.0,>=3.10
2
+ ssot-contracts==0.2.20.dev1
3
+ ssot-pack-contracts<0.3.0,>=0.2.21.dev1
4
+ ssot-views==0.2.20.dev1
5
+
6
+ [:python_version < "3.11"]
7
+ tomli>=2.0.1
@@ -62,7 +62,7 @@ from .packs import inspect_pack, preflight_pack, sync_pack
62
62
  from .origin import sync_origin_assurance_rows
63
63
  from .registry import export_registry
64
64
  from .release import certify_release, promote_release, publish_release, revoke_release
65
- from .save import save_registry
65
+ from .save import save_registry, save_registry_unchecked
66
66
  from .status_sync import sync_automated_statuses
67
67
  from .test_execution import run_boundary_tests, run_resolved_test_rows, run_spec_tests, run_tests
68
68
  from .upgrade import upgrade_registry
@@ -72,6 +72,7 @@ __all__ = [
72
72
  "initialize_repo",
73
73
  "load_registry",
74
74
  "save_registry",
75
+ "save_registry_unchecked",
75
76
  "validate_registry",
76
77
  "ensure_repo_config",
77
78
  "load_repo_config",
@@ -7,7 +7,7 @@ from ssot_registry.api.documents import sync_all_documents
7
7
  from ssot_registry.model.registry import build_minimal_registry, default_paths
8
8
  from ssot_registry.util.errors import RegistryError
9
9
  from .config import ensure_repo_config
10
- from .save import save_registry
10
+ from .save import save_registry_unchecked
11
11
  from .validate import validate_registry
12
12
 
13
13
 
@@ -37,7 +37,7 @@ def initialize_repo(
37
37
  (repo_root / relative_path).mkdir(parents=True, exist_ok=True)
38
38
 
39
39
  registry = build_minimal_registry(repo_id, repo_name, version)
40
- save_registry(registry_path, registry)
40
+ save_registry_unchecked(registry_path, registry)
41
41
 
42
42
  _copy_schema_tree(repo_root / paths["schema_root"])
43
43
  config_result = ensure_repo_config(repo_root)
@@ -46,23 +46,29 @@ def evaluate_feature_passing(
46
46
 
47
47
  checked_claim_ids: list[str] = []
48
48
  satisfying_claim_ids: list[str] = []
49
+ failed_claim_ids: list[str] = []
49
50
  active_claims = active_required_feature_claims(feature, index)
50
51
  for claim in active_claims:
51
52
  claim_id = str(claim["id"])
52
53
  checked_claim_ids.append(claim_id)
53
54
  guard = evaluate_claim_guard(claim, index, guard_policies)
54
55
  if not guard.get("passed"):
56
+ failed_claim_ids.append(claim_id)
55
57
  continue
56
58
  if claim.get("tier") != "T0" and (required_tier is None or CLAIM_TIER_RANK[claim["tier"]] >= CLAIM_TIER_RANK[required_tier]):
57
59
  satisfying_claim_ids.append(claim_id)
58
60
 
59
- checks["claim_target_met"] = bool(active_claims) and len(satisfying_claim_ids) == len(active_claims)
61
+ checks["claim_target_met"] = bool(satisfying_claim_ids) and not failed_claim_ids
60
62
  if not checks["claim_target_met"]:
61
63
  if required_tier is None:
62
64
  failures.append(f"Feature {feature_id} has no effective required claim tier")
63
65
  else:
64
- failures.append(f"Feature {feature_id} does not have all active required claims satisfying tier {required_tier}")
65
- failures.extend(failure.removeprefix(f"features.{feature_id} ") for failure in feature_claim_ceiling_failures(feature, index))
66
+ failures.append(f"Feature {feature_id} does not have an active evidenced claim satisfying tier {required_tier}")
67
+ failures.extend(f"Claim {claim_id} does not pass claim guard" for claim_id in failed_claim_ids)
68
+ failures.extend(
69
+ failure.removeprefix(f"features.{feature_id} ")
70
+ for failure in feature_claim_ceiling_failures(feature, index, require_evidenced_status=False)
71
+ )
66
72
 
67
73
  return {
68
74
  "feature_id": feature_id,
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from ssot_registry.util.errors import ValidationError
6
+ from ssot_registry.util.registry_lock import save_registry_json_locked
7
+
8
+
9
+ def _repo_root_for_registry_path(registry_path: str | Path) -> Path:
10
+ path = Path(registry_path)
11
+ if path.name == "registry.json" and path.parent.name == ".ssot":
12
+ return path.parent.parent
13
+ if path.parent.name == ".ssot":
14
+ return path.parent.parent
15
+ return path.parent
16
+
17
+
18
+ def save_registry_unchecked(registry_path: str | Path, registry: dict[str, object]) -> None:
19
+ save_registry_json_locked(registry_path, registry)
20
+
21
+
22
+ def save_registry(
23
+ registry_path: str | Path,
24
+ registry: dict[str, object],
25
+ *,
26
+ repo_root: str | Path | None = None,
27
+ action: str = "saving registry",
28
+ ) -> dict[str, object]:
29
+ from .validate import validate_registry_document
30
+
31
+ resolved_repo_root = Path(repo_root) if repo_root is not None else _repo_root_for_registry_path(registry_path)
32
+ report = validate_registry_document(registry, registry_path, resolved_repo_root)
33
+ if not report["passed"]:
34
+ detail = "; ".join(report["failures"])
35
+ raise ValidationError(f"Registry validation failed before {action}: {detail}")
36
+ save_registry_unchecked(registry_path, registry)
37
+ return report
@@ -234,22 +234,22 @@ def _sync_features_once(registry: dict[str, Any]) -> list[dict[str, object]]:
234
234
  required_features = [index["features"][required_id] for required_id in feature.get("requires", []) if required_id in index["features"]]
235
235
  required_tier = feature.get("plan", {}).get("target_claim_tier")
236
236
  tests_pass = bool(linked_tests) and all(test.get("status") == "passing" for test in linked_tests)
237
- claims_pass = bool(active_claims) and all(_claim_satisfies_feature(claim, required_tier) for claim in active_claims)
237
+ claims_pass = bool(active_claims) and any(_claim_satisfies_feature(claim, required_tier) for claim in active_claims)
238
238
  requirements_pass = all(required.get("implementation_status") == "implemented" for required in required_features)
239
+ ceiling_failures = feature_claim_ceiling_failures(feature, index)
239
240
  only_planned_support = (
240
241
  bool(linked_tests or linked_claims)
241
242
  and all(test.get("status") == "planned" for test in linked_tests)
242
243
  and all(CLAIM_STATUS_RANK.get(claim.get("status"), -999) <= CLAIM_STATUS_RANK["proposed"] for claim in active_claims)
243
244
  )
244
- if tests_pass and claims_pass and requirements_pass:
245
+ if tests_pass and claims_pass and requirements_pass and not ceiling_failures:
245
246
  status = "implemented"
246
- reason = "feature has passing tests, all active required claims satisfy implementation, and implemented requirements"
247
+ reason = "feature has passing tests, an active required claim satisfies implementation, and implemented requirements"
247
248
  elif only_planned_support:
248
249
  status = "absent"
249
250
  reason = "feature has only planned verification support"
250
251
  elif linked_tests or linked_claims or required_features:
251
252
  status = "partial"
252
- ceiling_failures = feature_claim_ceiling_failures(feature, index)
253
253
  reason = "; ".join(ceiling_failures) if ceiling_failures else "feature has linked support but does not yet satisfy implementation criteria"
254
254
  else:
255
255
  status = "absent"
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from typing import Any
6
6
 
7
7
  from ssot_registry.api.load import load_registry
8
- from ssot_registry.api.save import save_registry
8
+ from ssot_registry.api.save import save_registry_unchecked
9
9
  from ssot_registry.api.validate import validate_registry_document
10
10
  from ssot_registry.maturation.selector import build_registry_index
11
11
  from ssot_registry.util.jsonio import stable_json_dumps
@@ -148,7 +148,7 @@ def scaffold_target_claim_wiring(repo_root: str | Path, feature_id: str, target_
148
148
  "new_validation_failures": new_failures,
149
149
  }
150
150
 
151
- save_registry(registry_path, working)
151
+ save_registry_unchecked(registry_path, working)
152
152
  return {
153
153
  "passed": True,
154
154
  "validation_clean": bool(report.get("passed")),
@@ -4,6 +4,45 @@ from ssot_registry.guards.feature_requirements import evaluate_required_feature_
4
4
  from ssot_registry.model.enums import CLAIM_STATUS_RANK, CLAIM_TIER_RANK
5
5
 
6
6
 
7
+ def _dependency_closure_contains(
8
+ claim: dict[str, object],
9
+ target_claim_id: str,
10
+ index: dict[str, dict[str, dict[str, object]]],
11
+ *,
12
+ seen: set[str] | None = None,
13
+ ) -> bool:
14
+ seen = seen or set()
15
+ for dependency_id in claim.get("depends_on_claim_ids", []):
16
+ if dependency_id == target_claim_id:
17
+ return True
18
+ if dependency_id in seen or dependency_id not in index["claims"]:
19
+ continue
20
+ seen.add(str(dependency_id))
21
+ if _dependency_closure_contains(index["claims"][dependency_id], target_claim_id, index, seen=seen):
22
+ return True
23
+ return False
24
+
25
+
26
+ def _has_target_tier_successor(
27
+ claim: dict[str, object],
28
+ feature: dict[str, object],
29
+ feature_target_tier: str,
30
+ index: dict[str, dict[str, dict[str, object]]],
31
+ ) -> bool:
32
+ claim_id = str(claim["id"])
33
+ for candidate_id in feature.get("claim_ids", []):
34
+ candidate = index["claims"].get(candidate_id)
35
+ if candidate is None or candidate.get("status") == "retired":
36
+ continue
37
+ if CLAIM_TIER_RANK[candidate["tier"]] < CLAIM_TIER_RANK[feature_target_tier]:
38
+ continue
39
+ if CLAIM_STATUS_RANK.get(candidate.get("status"), -999) < CLAIM_STATUS_RANK["evidenced"]:
40
+ continue
41
+ if _dependency_closure_contains(candidate, claim_id, index):
42
+ return True
43
+ return False
44
+
45
+
7
46
  def evaluate_claim_guard(
8
47
  claim: dict[str, object],
9
48
  index: dict[str, dict[str, dict[str, object]]],
@@ -64,9 +103,10 @@ def evaluate_claim_guard(
64
103
  for feature in linked_features:
65
104
  feature_target_tier = feature.get("plan", {}).get("target_claim_tier")
66
105
  if feature_target_tier is not None and CLAIM_TIER_RANK[claim["tier"]] < CLAIM_TIER_RANK[feature_target_tier]:
67
- feature_target_failures.append(
68
- f"Claim {claim['id']} tier {claim['tier']} is below feature target tier {feature_target_tier} on {feature['id']}"
69
- )
106
+ if not _has_target_tier_successor(claim, feature, feature_target_tier, index):
107
+ feature_target_failures.append(
108
+ f"Claim {claim['id']} tier {claim['tier']} is below feature target tier {feature_target_tier} on {feature['id']}"
109
+ )
70
110
  requirement_failures.extend(
71
111
  f"Claim {claim['id']} linked feature requirement failure: {failure}"
72
112
  for failure in evaluate_required_feature_failures(feature["id"], index)
@@ -29,6 +29,34 @@ def claim_satisfies_feature_implementation(
29
29
  return CLAIM_STATUS_RANK.get(claim.get("status"), -999) >= CLAIM_STATUS_RANK["evidenced"]
30
30
 
31
31
 
32
+ def _dependency_closure_contains(
33
+ claim: dict[str, object],
34
+ target_claim_id: str,
35
+ index: dict[str, dict[str, dict[str, object]]],
36
+ *,
37
+ seen: set[str] | None = None,
38
+ ) -> bool:
39
+ seen = seen or set()
40
+ for dependency_id in claim.get("depends_on_claim_ids", []):
41
+ if dependency_id == target_claim_id:
42
+ return True
43
+ if dependency_id in seen or dependency_id not in index["claims"]:
44
+ continue
45
+ seen.add(str(dependency_id))
46
+ if _dependency_closure_contains(index["claims"][dependency_id], target_claim_id, index, seen=seen):
47
+ return True
48
+ return False
49
+
50
+
51
+ def _has_satisfying_successor(
52
+ claim: dict[str, object],
53
+ satisfying_claims: list[dict[str, object]],
54
+ index: dict[str, dict[str, dict[str, object]]],
55
+ ) -> bool:
56
+ claim_id = str(claim["id"])
57
+ return any(_dependency_closure_contains(candidate, claim_id, index) for candidate in satisfying_claims)
58
+
59
+
32
60
  def feature_claim_ceiling_failures(
33
61
  feature: dict[str, Any],
34
62
  index: dict[str, dict[str, dict[str, object]]],
@@ -37,8 +65,32 @@ def feature_claim_ceiling_failures(
37
65
  ) -> list[str]:
38
66
  feature_id = str(feature["id"])
39
67
  required_tier = feature.get("plan", {}).get("target_claim_tier")
68
+ active_claims = active_required_feature_claims(feature, index)
69
+ satisfying_claims = [
70
+ claim
71
+ for claim in active_claims
72
+ if claim.get("tier") != "T0"
73
+ and (required_tier is None or CLAIM_TIER_RANK[claim["tier"]] >= CLAIM_TIER_RANK[required_tier])
74
+ and (
75
+ not require_evidenced_status
76
+ or CLAIM_STATUS_RANK.get(claim.get("status"), -999) >= CLAIM_STATUS_RANK["evidenced"]
77
+ )
78
+ ]
79
+ if satisfying_claims:
80
+ failures: list[str] = []
81
+ for claim in active_claims:
82
+ claim_tier = str(claim["tier"])
83
+ if claim in satisfying_claims:
84
+ continue
85
+ if required_tier is None or CLAIM_TIER_RANK[claim_tier] < CLAIM_TIER_RANK[required_tier]:
86
+ if not _has_satisfying_successor(claim, satisfying_claims, index):
87
+ failures.append(
88
+ f"features.{feature_id} is capped below implemented by claim {claim['id']} outside satisfying claim lineage"
89
+ )
90
+ return failures
91
+
40
92
  failures: list[str] = []
41
- for claim in active_required_feature_claims(feature, index):
93
+ for claim in active_claims:
42
94
  claim_id = str(claim["id"])
43
95
  claim_tier = str(claim["tier"])
44
96
  claim_status = str(claim.get("status"))
@@ -1,7 +0,0 @@
1
- orjson<4.0,>=3.10
2
- ssot-contracts==0.2.19.dev2
3
- ssot-pack-contracts<0.3.0,>=0.2.20.dev2
4
- ssot-views==0.2.19.dev2
5
-
6
- [:python_version < "3.11"]
7
- tomli>=2.0.1
@@ -1,9 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
-
5
- from ssot_registry.util.registry_lock import save_registry_json_locked
6
-
7
-
8
- def save_registry(registry_path: str | Path, registry: dict[str, object]) -> None:
9
- save_registry_json_locked(registry_path, registry)