ssot-core 0.2.20.dev1__tar.gz → 0.2.21.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.
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/PKG-INFO +4 -4
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/pyproject.toml +4 -4
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_core.egg-info/PKG-INFO +4 -4
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_core.egg-info/SOURCES.txt +1 -0
- ssot_core-0.2.21.dev1/src/ssot_core.egg-info/requires.txt +7 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/__init__.py +10 -1
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/config.py +26 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/entity_ops.py +265 -1
- ssot_core-0.2.21.dev1/src/ssot_registry/api/proof_graph.py +420 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/release.py +39 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/upgrade.py +2 -1
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/service.py +89 -6
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/sqlite_store.py +52 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/publication.py +13 -3
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/registry_lock.py +26 -1
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/coverage.py +1 -6
- ssot_core-0.2.20.dev1/src/ssot_core.egg-info/requires.txt +0 -7
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/README.md +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/setup.cfg +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_core.egg-info/dependency_links.txt +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_core.egg-info/top_level.txt +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/__main__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/acl/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/acl/policy.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/acl/wrapper.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/boundary.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/claims.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/documents.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/evidence.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/graph.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/init.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/lifecycle.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/load.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/local_assurance.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/origin.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/packs.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/plan.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/profile_eval.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/profile_resolution.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/registry.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/save.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/status_sync.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/test_execution.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/api/validate.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/adr_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/boundary_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/claim_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/common.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/evidence_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/feature_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/graph_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/init_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/issue_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/main.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/profile_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/registry_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/release_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/risk_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/spec_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/test_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/upgrade_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/cli/validate_cmd.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/events.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/models.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/paths.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/scaffold.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/control/sse.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/graph/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/graph/export_dot.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/graph/export_json.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/certification.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/claim_closure.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/claim_tier_gates.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/completion.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/document_lifecycle.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/document_supersession.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/feature_claims.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/feature_requirements.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/lifecycle.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/profile_requirements.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/guards/promotion.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/maturation/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/maturation/selector.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/boundary.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/claim.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/document.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/enums.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/evidence.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/feature.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/ids.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/issue.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/profile.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/registry.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/release.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/risk.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/schema_version.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/model/test.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/reports/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/reports/certification_report.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/reports/summary.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/reports/validation_report.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/snapshots/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/snapshots/boundary_snapshot.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/snapshots/hashing.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/snapshots/published_snapshot.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/snapshots/release_snapshot.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/templates/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/templates/registry.full.json +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/templates/registry.minimal.json +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/document_io.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/errors.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/formatting.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/fs.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/jcs.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/jsonio.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/util/time.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/bidirectional.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/bounds.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/claim_lineage.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/documents.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/feature_parent_links.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/filesystem.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/identity.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/lifecycle.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/origin.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/promotion.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/references.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/reservations.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/structure.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/validators/tiers.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/version.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/watch/__init__.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.dev1}/src/ssot_registry/watch/git_status.py +0 -0
- {ssot_core-0.2.20.dev1 → ssot_core-0.2.21.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.
|
|
3
|
+
Version: 0.2.21.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.
|
|
40
|
-
Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.
|
|
41
|
-
Requires-Dist: ssot-views==0.2.
|
|
39
|
+
Requires-Dist: ssot-contracts==0.2.21.dev1
|
|
40
|
+
Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.22.dev1
|
|
41
|
+
Requires-Dist: ssot-views==0.2.21.dev1
|
|
42
42
|
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
|
|
43
43
|
|
|
44
44
|
<div align="center">
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ssot-core"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.21.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.
|
|
16
|
-
"ssot-pack-contracts>=0.2.
|
|
17
|
-
"ssot-views==0.2.
|
|
15
|
+
"ssot-contracts==0.2.21.dev1",
|
|
16
|
+
"ssot-pack-contracts>=0.2.22.dev1,<0.3.0",
|
|
17
|
+
"ssot-views==0.2.21.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.
|
|
3
|
+
Version: 0.2.21.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.
|
|
40
|
-
Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.
|
|
41
|
-
Requires-Dist: ssot-views==0.2.
|
|
39
|
+
Requires-Dist: ssot-contracts==0.2.21.dev1
|
|
40
|
+
Requires-Dist: ssot-pack-contracts<0.3.0,>=0.2.22.dev1
|
|
41
|
+
Requires-Dist: ssot-views==0.2.21.dev1
|
|
42
42
|
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
|
|
43
43
|
|
|
44
44
|
<div align="center">
|
|
@@ -28,6 +28,7 @@ src/ssot_registry/api/packs.py
|
|
|
28
28
|
src/ssot_registry/api/plan.py
|
|
29
29
|
src/ssot_registry/api/profile_eval.py
|
|
30
30
|
src/ssot_registry/api/profile_resolution.py
|
|
31
|
+
src/ssot_registry/api/proof_graph.py
|
|
31
32
|
src/ssot_registry/api/registry.py
|
|
32
33
|
src/ssot_registry/api/release.py
|
|
33
34
|
src/ssot_registry/api/save.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from .boundary import freeze_boundary
|
|
2
2
|
from .claims import evaluate_claims
|
|
3
|
-
from .config import ensure_repo_config, load_repo_config, run_repo_automation, validate_repo_config
|
|
3
|
+
from .config import ensure_repo_config, load_repo_config, resolve_feature_create_auto_scaffold, run_repo_automation, validate_repo_config
|
|
4
4
|
from .documents import (
|
|
5
5
|
create_document,
|
|
6
6
|
create_document_reservation,
|
|
@@ -24,7 +24,9 @@ from .entity_ops import (
|
|
|
24
24
|
add_release_boundaries,
|
|
25
25
|
add_release_claims,
|
|
26
26
|
add_release_evidence,
|
|
27
|
+
audit_feature_parent_links,
|
|
27
28
|
create_entity,
|
|
29
|
+
create_feature_with_scaffolded_proof_graph,
|
|
28
30
|
delete_entity,
|
|
29
31
|
get_entity,
|
|
30
32
|
link_entities,
|
|
@@ -36,6 +38,7 @@ from .entity_ops import (
|
|
|
36
38
|
remove_release_boundaries,
|
|
37
39
|
remove_release_claims,
|
|
38
40
|
remove_release_evidence,
|
|
41
|
+
migrate_feature_parent_audit_edge,
|
|
39
42
|
set_claim_status,
|
|
40
43
|
set_claim_tier,
|
|
41
44
|
set_feature_parents,
|
|
@@ -60,6 +63,7 @@ from .load import load_registry
|
|
|
60
63
|
from .plan import plan_features, plan_issues
|
|
61
64
|
from .packs import inspect_pack, preflight_pack, sync_pack
|
|
62
65
|
from .origin import sync_origin_assurance_rows
|
|
66
|
+
from .proof_graph import certify_feature_proof_graphs
|
|
63
67
|
from .registry import export_registry
|
|
64
68
|
from .release import certify_release, promote_release, publish_release, revoke_release
|
|
65
69
|
from .save import save_registry, save_registry_unchecked
|
|
@@ -77,6 +81,7 @@ __all__ = [
|
|
|
77
81
|
"ensure_repo_config",
|
|
78
82
|
"load_repo_config",
|
|
79
83
|
"validate_repo_config",
|
|
84
|
+
"resolve_feature_create_auto_scaffold",
|
|
80
85
|
"run_repo_automation",
|
|
81
86
|
"create_document",
|
|
82
87
|
"get_document",
|
|
@@ -93,6 +98,7 @@ __all__ = [
|
|
|
93
98
|
"create_document_reservation",
|
|
94
99
|
"list_document_reservations",
|
|
95
100
|
"create_entity",
|
|
101
|
+
"create_feature_with_scaffolded_proof_graph",
|
|
96
102
|
"get_entity",
|
|
97
103
|
"list_entities",
|
|
98
104
|
"update_entity",
|
|
@@ -111,6 +117,8 @@ __all__ = [
|
|
|
111
117
|
"add_feature_children",
|
|
112
118
|
"remove_feature_children",
|
|
113
119
|
"list_feature_children",
|
|
120
|
+
"audit_feature_parent_links",
|
|
121
|
+
"migrate_feature_parent_audit_edge",
|
|
114
122
|
"add_release_boundaries",
|
|
115
123
|
"remove_release_boundaries",
|
|
116
124
|
"add_release_claims",
|
|
@@ -123,6 +131,7 @@ __all__ = [
|
|
|
123
131
|
"preflight_pack",
|
|
124
132
|
"sync_pack",
|
|
125
133
|
"sync_origin_assurance_rows",
|
|
134
|
+
"certify_feature_proof_graphs",
|
|
126
135
|
"set_feature_lifecycle",
|
|
127
136
|
"build_artifact_manifest",
|
|
128
137
|
"build_local_evidence_bundle",
|
|
@@ -24,6 +24,13 @@ _DEFAULT_CONFIG: dict[str, Any] = {
|
|
|
24
24
|
"interactive": False,
|
|
25
25
|
"fail_closed": True,
|
|
26
26
|
},
|
|
27
|
+
"commands": {
|
|
28
|
+
"feature": {
|
|
29
|
+
"create": {
|
|
30
|
+
"auto_scaffold_proof_graph": True,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
27
34
|
"sync": {
|
|
28
35
|
"docs": "manual",
|
|
29
36
|
"templates": "manual",
|
|
@@ -51,6 +58,9 @@ _DEFAULT_TEMPLATE = """[policy]
|
|
|
51
58
|
interactive = false
|
|
52
59
|
fail_closed = true
|
|
53
60
|
|
|
61
|
+
[commands.feature.create]
|
|
62
|
+
auto_scaffold_proof_graph = true
|
|
63
|
+
|
|
54
64
|
[sync]
|
|
55
65
|
docs = "manual"
|
|
56
66
|
templates = "manual"
|
|
@@ -148,6 +158,11 @@ def validate_repo_config_payload(config: dict[str, Any]) -> dict[str, Any]:
|
|
|
148
158
|
_expect_bool(policy, "interactive", prefix="policy")
|
|
149
159
|
_expect_bool(policy, "fail_closed", prefix="policy")
|
|
150
160
|
|
|
161
|
+
commands = _expect_table(normalized, "commands")
|
|
162
|
+
feature_commands = _expect_table(commands, "feature")
|
|
163
|
+
feature_create = _expect_table(feature_commands, "create")
|
|
164
|
+
_expect_bool(feature_create, "auto_scaffold_proof_graph", prefix="commands.feature.create")
|
|
165
|
+
|
|
151
166
|
sync = _expect_table(normalized, "sync")
|
|
152
167
|
for field_name in ("docs", "templates", "upstream_packages"):
|
|
153
168
|
_expect_choice(sync, field_name, _AUTOMATION_MODE_CHOICES, prefix="sync")
|
|
@@ -226,6 +241,17 @@ def validate_repo_config(path: str | Path) -> dict[str, Any]:
|
|
|
226
241
|
}
|
|
227
242
|
|
|
228
243
|
|
|
244
|
+
def resolve_feature_create_auto_scaffold(path: str | Path, explicit: bool | None) -> bool:
|
|
245
|
+
if explicit is not None:
|
|
246
|
+
return explicit
|
|
247
|
+
try:
|
|
248
|
+
payload = load_repo_config(path)
|
|
249
|
+
except FileNotFoundError:
|
|
250
|
+
return bool(_DEFAULT_CONFIG["commands"]["feature"]["create"]["auto_scaffold_proof_graph"])
|
|
251
|
+
feature_create = payload["config"]["commands"]["feature"]["create"]
|
|
252
|
+
return bool(feature_create["auto_scaffold_proof_graph"])
|
|
253
|
+
|
|
254
|
+
|
|
229
255
|
def run_repo_automation(path: str | Path) -> dict[str, Any]:
|
|
230
256
|
global _AUTOMATION_DEPTH
|
|
231
257
|
|
|
@@ -4,9 +4,10 @@ from copy import deepcopy
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
-
from ssot_registry.model.enums import ASSURANCE_ENTITY_SECTIONS, ASSURANCE_ORIGINS, REF_FIELD_TARGETS
|
|
7
|
+
from ssot_registry.model.enums import ASSURANCE_ENTITY_SECTIONS, ASSURANCE_ORIGINS, CLAIM_TIER_RANK, REF_FIELD_TARGETS
|
|
8
8
|
from ssot_registry.model.registry import normalize_repo_kind
|
|
9
9
|
from ssot_registry.util.errors import ValidationError
|
|
10
|
+
from ssot_registry.util.jsonio import stable_json_dumps
|
|
10
11
|
|
|
11
12
|
from .load import load_registry
|
|
12
13
|
from .save import save_registry
|
|
@@ -52,6 +53,9 @@ LINKABLE_FIELDS = {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
SECTIONS = tuple(SECTION_LABELS)
|
|
56
|
+
PARENT_AUDIT_TERMS = ("requires", "require", "required", "depends", "dependency", "prerequisite", "blocks", "blocked", "before")
|
|
57
|
+
PARENT_AUDIT_TARGET_HORIZONS = {"current", "explicit"}
|
|
58
|
+
PARENT_AUDIT_INCOMPLETE_STATUSES = {"absent", "partial"}
|
|
55
59
|
|
|
56
60
|
|
|
57
61
|
def _row_lookup(registry: dict[str, Any], section: str) -> dict[str, dict[str, Any]]:
|
|
@@ -192,6 +196,160 @@ def _validate_and_save(registry_path: Path, repo_root: Path, registry: dict[str,
|
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
|
|
199
|
+
def _safe_slug(entity_id: str) -> str:
|
|
200
|
+
return entity_id.split(":", 1)[-1].replace("/", ".").replace(" ", "-").lower()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _tier_sequence(target_tier: str) -> list[str]:
|
|
204
|
+
if target_tier not in CLAIM_TIER_RANK:
|
|
205
|
+
raise ValueError(f"Unsupported claim tier: {target_tier}")
|
|
206
|
+
return [tier for tier, _rank in sorted(CLAIM_TIER_RANK.items(), key=lambda item: item[1]) if CLAIM_TIER_RANK[tier] <= CLAIM_TIER_RANK[target_tier]]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _ensure_scaffold_file(path: Path, content: str) -> None:
|
|
210
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
211
|
+
if not path.exists():
|
|
212
|
+
path.write_text(content, encoding="utf-8")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def create_feature_with_scaffolded_proof_graph(path: str | Path, row: dict[str, Any]) -> dict[str, Any]:
|
|
216
|
+
registry_path, repo_root, registry = load_registry(path)
|
|
217
|
+
entity_id = row.get("id")
|
|
218
|
+
if not isinstance(entity_id, str):
|
|
219
|
+
raise ValueError("feature row must include a string id")
|
|
220
|
+
if entity_id in _row_lookup(registry, "features"):
|
|
221
|
+
raise ValueError(f"Feature already exists: {entity_id}")
|
|
222
|
+
|
|
223
|
+
candidate = deepcopy(row)
|
|
224
|
+
_ensure_assurance_origin(registry, "features", candidate)
|
|
225
|
+
candidate.setdefault("parent_feature_ids", [])
|
|
226
|
+
_normalize_feature_parent_ids(candidate)
|
|
227
|
+
for field_name in LINKABLE_FIELDS["features"]:
|
|
228
|
+
if field_name in candidate and isinstance(candidate[field_name], list):
|
|
229
|
+
candidate[field_name] = _dedupe_preserve(candidate[field_name])
|
|
230
|
+
_validate_assurance_origin_mutation(registry, "features", candidate)
|
|
231
|
+
|
|
232
|
+
plan = candidate.get("plan") if isinstance(candidate.get("plan"), dict) else {}
|
|
233
|
+
target_tier = str(plan.get("target_claim_tier") or "T1")
|
|
234
|
+
tiers = _tier_sequence(target_tier)
|
|
235
|
+
slug = _safe_slug(entity_id)
|
|
236
|
+
evidence_id = f"evd:{target_tier.lower()}.{slug}.proof-graph"
|
|
237
|
+
evidence_path = f".ssot/evidence/{slug}/proof-graph-{target_tier.lower()}.json"
|
|
238
|
+
claim_ids = [f"clm:{slug}.{tier.lower()}" for tier in tiers]
|
|
239
|
+
test_ids = [f"tst:pytest.{slug}.{tier.lower()}.proof-graph" for tier in tiers]
|
|
240
|
+
test_path_by_tier = {
|
|
241
|
+
tier: f"tests/ssot_scaffold/test_{slug.replace('.', '_').replace('-', '_')}_{tier.lower()}_proof_graph.py"
|
|
242
|
+
for tier in tiers
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
claims: list[dict[str, Any]] = []
|
|
246
|
+
for index, tier in enumerate(tiers):
|
|
247
|
+
claim_ids_in_chain = claim_ids[: index + 1]
|
|
248
|
+
claim = {
|
|
249
|
+
"id": claim_ids[index],
|
|
250
|
+
"title": f"{candidate['title']} {tier} claim",
|
|
251
|
+
"status": "declared" if tier == "T0" else "proposed",
|
|
252
|
+
"tier": tier,
|
|
253
|
+
"kind": "runtime",
|
|
254
|
+
"description": f"{tier} scaffold claim for {entity_id}.",
|
|
255
|
+
"origin": candidate["origin"],
|
|
256
|
+
"feature_ids": [entity_id],
|
|
257
|
+
"test_ids": [test_ids[index]],
|
|
258
|
+
"evidence_ids": [evidence_id],
|
|
259
|
+
"depends_on_claim_ids": claim_ids_in_chain[:-1],
|
|
260
|
+
}
|
|
261
|
+
claims.append(claim)
|
|
262
|
+
|
|
263
|
+
test_rows: list[dict[str, Any]] = []
|
|
264
|
+
for index, tier in enumerate(tiers):
|
|
265
|
+
test_path = test_path_by_tier[tier]
|
|
266
|
+
test_rows.append(
|
|
267
|
+
{
|
|
268
|
+
"id": test_ids[index],
|
|
269
|
+
"title": f"{candidate['title']} {tier} proof-graph scaffold test",
|
|
270
|
+
"body": f"Planned scaffold test for {entity_id} {tier} support.",
|
|
271
|
+
"origin": candidate["origin"],
|
|
272
|
+
"status": "planned",
|
|
273
|
+
"kind": "pytest",
|
|
274
|
+
"path": test_path,
|
|
275
|
+
"feature_ids": [entity_id],
|
|
276
|
+
"claim_ids": [claim_ids[index]],
|
|
277
|
+
"evidence_ids": [evidence_id],
|
|
278
|
+
"execution": {
|
|
279
|
+
"argv": ["python", "-m", "pytest", test_path, "-q"],
|
|
280
|
+
"cwd": ".",
|
|
281
|
+
"env": {},
|
|
282
|
+
"mode": "command",
|
|
283
|
+
"success": {"expected": 0, "type": "exit_code"},
|
|
284
|
+
"timeout_seconds": 600,
|
|
285
|
+
},
|
|
286
|
+
}
|
|
287
|
+
)
|
|
288
|
+
evidence_row = {
|
|
289
|
+
"id": evidence_id,
|
|
290
|
+
"title": f"{candidate['title']} proof-graph scaffold evidence",
|
|
291
|
+
"status": "planned",
|
|
292
|
+
"kind": "scaffold",
|
|
293
|
+
"tier": target_tier,
|
|
294
|
+
"body": f"Planned scaffold evidence for {entity_id}.",
|
|
295
|
+
"origin": candidate["origin"],
|
|
296
|
+
"path": evidence_path,
|
|
297
|
+
"claim_ids": claim_ids,
|
|
298
|
+
"test_ids": test_ids,
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
candidate["claim_ids"] = _dedupe_preserve([*claim_ids, *_ensure_list_field(candidate, "claim_ids")])
|
|
302
|
+
candidate["test_ids"] = _dedupe_preserve([*test_ids, *_ensure_list_field(candidate, "test_ids")])
|
|
303
|
+
registry["features"].append(candidate)
|
|
304
|
+
registry.setdefault("claims", []).extend(claims)
|
|
305
|
+
registry.setdefault("tests", []).extend(test_rows)
|
|
306
|
+
registry.setdefault("evidence", []).append(evidence_row)
|
|
307
|
+
|
|
308
|
+
for created in claims:
|
|
309
|
+
_sync_reciprocals_for_row(registry, "claims", created)
|
|
310
|
+
for created in test_rows:
|
|
311
|
+
_sync_reciprocals_for_row(registry, "tests", created)
|
|
312
|
+
_sync_reciprocals_for_row(registry, "evidence", evidence_row)
|
|
313
|
+
_sync_reciprocals_for_row(registry, "features", candidate)
|
|
314
|
+
|
|
315
|
+
for test_path in test_path_by_tier.values():
|
|
316
|
+
_ensure_scaffold_file(
|
|
317
|
+
repo_root / test_path,
|
|
318
|
+
"def test_ssot_scaffold_placeholder():\n assert True\n",
|
|
319
|
+
)
|
|
320
|
+
_ensure_scaffold_file(
|
|
321
|
+
repo_root / evidence_path,
|
|
322
|
+
stable_json_dumps(
|
|
323
|
+
{
|
|
324
|
+
"schema_version": "ssot.evidence.scaffold.v1",
|
|
325
|
+
"feature_id": entity_id,
|
|
326
|
+
"claim_ids": claim_ids,
|
|
327
|
+
"test_ids": test_ids,
|
|
328
|
+
"target_tier": target_tier,
|
|
329
|
+
"status": "planned",
|
|
330
|
+
}
|
|
331
|
+
),
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
mutation = _validate_and_save(registry_path, repo_root, registry, f"creating feature {entity_id} with scaffolded proof graph")
|
|
335
|
+
return {
|
|
336
|
+
"passed": True,
|
|
337
|
+
"registry_path": registry_path.as_posix(),
|
|
338
|
+
"section": "features",
|
|
339
|
+
"entity": candidate,
|
|
340
|
+
"scaffolded": {
|
|
341
|
+
"claim_ids": claim_ids,
|
|
342
|
+
"test_id": test_ids[-1],
|
|
343
|
+
"test_ids": test_ids,
|
|
344
|
+
"evidence_id": evidence_id,
|
|
345
|
+
"test_path": test_path_by_tier[tiers[-1]],
|
|
346
|
+
"test_paths": [test_path_by_tier[tier] for tier in tiers],
|
|
347
|
+
"evidence_path": evidence_path,
|
|
348
|
+
},
|
|
349
|
+
**mutation,
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
195
353
|
def _rewrite_references_for_renamed_id(
|
|
196
354
|
registry: dict[str, Any],
|
|
197
355
|
*,
|
|
@@ -486,6 +644,112 @@ def list_feature_children(path: str | Path, parent_id: str) -> list[dict[str, An
|
|
|
486
644
|
return sorted(rows, key=lambda row: row["id"])
|
|
487
645
|
|
|
488
646
|
|
|
647
|
+
def _audit_text(row: dict[str, Any]) -> str:
|
|
648
|
+
return " ".join(str(row.get(field_name, "")) for field_name in ("id", "title", "description", "body")).lower()
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def audit_feature_parent_links(path: str | Path) -> dict[str, Any]:
|
|
652
|
+
registry_path, _repo_root, registry = load_registry(path)
|
|
653
|
+
feature_lookup = _row_lookup(registry, "features")
|
|
654
|
+
findings: list[dict[str, Any]] = []
|
|
655
|
+
|
|
656
|
+
for feature in sorted(feature_lookup.values(), key=lambda row: row["id"]):
|
|
657
|
+
feature_id = str(feature["id"])
|
|
658
|
+
parent_ids = feature.get("parent_feature_ids", [])
|
|
659
|
+
if not isinstance(parent_ids, list):
|
|
660
|
+
continue
|
|
661
|
+
for parent_id in sorted(dict.fromkeys(parent_ids)):
|
|
662
|
+
parent = feature_lookup.get(parent_id)
|
|
663
|
+
if parent is None:
|
|
664
|
+
continue
|
|
665
|
+
|
|
666
|
+
reasons: list[str] = []
|
|
667
|
+
feature_horizon = feature.get("plan", {}).get("horizon") if isinstance(feature.get("plan"), dict) else None
|
|
668
|
+
parent_status = parent.get("implementation_status")
|
|
669
|
+
if (feature_horizon in PARENT_AUDIT_TARGET_HORIZONS or feature.get("implementation_status") == "implemented") and (
|
|
670
|
+
parent_status in PARENT_AUDIT_INCOMPLETE_STATUSES
|
|
671
|
+
):
|
|
672
|
+
reasons.append("targeted_or_implemented_child_has_incomplete_parent")
|
|
673
|
+
|
|
674
|
+
combined_text = f"{_audit_text(feature)} {_audit_text(parent)}"
|
|
675
|
+
matched_terms = [term for term in PARENT_AUDIT_TERMS if term in combined_text]
|
|
676
|
+
if matched_terms:
|
|
677
|
+
reasons.append(f"dependency_language:{','.join(matched_terms)}")
|
|
678
|
+
|
|
679
|
+
if not reasons:
|
|
680
|
+
continue
|
|
681
|
+
|
|
682
|
+
confidence = "high" if len(reasons) > 1 else "medium"
|
|
683
|
+
if reasons == [reason for reason in reasons if reason.startswith("dependency_language:")]:
|
|
684
|
+
confidence = "low"
|
|
685
|
+
findings.append(
|
|
686
|
+
{
|
|
687
|
+
"feature_id": feature_id,
|
|
688
|
+
"parent_feature_id": parent_id,
|
|
689
|
+
"reason": reasons,
|
|
690
|
+
"suggested_requires_edge": {"feature_id": feature_id, "requires": parent_id},
|
|
691
|
+
"confidence": confidence,
|
|
692
|
+
}
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
"passed": True,
|
|
697
|
+
"registry_path": registry_path.as_posix(),
|
|
698
|
+
"findings": findings,
|
|
699
|
+
"summary": {
|
|
700
|
+
"finding_count": len(findings),
|
|
701
|
+
"high_confidence_count": sum(1 for finding in findings if finding["confidence"] == "high"),
|
|
702
|
+
"medium_confidence_count": sum(1 for finding in findings if finding["confidence"] == "medium"),
|
|
703
|
+
"low_confidence_count": sum(1 for finding in findings if finding["confidence"] == "low"),
|
|
704
|
+
},
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def migrate_feature_parent_audit_edge(
|
|
709
|
+
path: str | Path,
|
|
710
|
+
feature_id: str,
|
|
711
|
+
parent_id: str,
|
|
712
|
+
*,
|
|
713
|
+
remove_parent_link: bool = False,
|
|
714
|
+
) -> dict[str, Any]:
|
|
715
|
+
registry_path, repo_root, registry = load_registry(path)
|
|
716
|
+
feature_lookup = _row_lookup(registry, "features")
|
|
717
|
+
if feature_id not in feature_lookup:
|
|
718
|
+
raise ValueError(f"Unknown feature id: {feature_id}")
|
|
719
|
+
if parent_id not in feature_lookup:
|
|
720
|
+
raise ValueError(f"Unknown parent feature id: {parent_id}")
|
|
721
|
+
|
|
722
|
+
feature = feature_lookup[feature_id]
|
|
723
|
+
parent_ids = _ensure_list_field(feature, "parent_feature_ids")
|
|
724
|
+
if parent_id not in parent_ids:
|
|
725
|
+
raise ValueError(f"Feature {feature_id} does not have parent link {parent_id}")
|
|
726
|
+
|
|
727
|
+
requires = _ensure_list_field(feature, "requires")
|
|
728
|
+
added_requires = parent_id not in requires
|
|
729
|
+
if added_requires:
|
|
730
|
+
requires.append(parent_id)
|
|
731
|
+
feature["requires"] = _dedupe_preserve(requires)
|
|
732
|
+
|
|
733
|
+
removed_parent_link = False
|
|
734
|
+
if remove_parent_link:
|
|
735
|
+
feature["parent_feature_ids"] = _dedupe_sorted([value for value in parent_ids if value != parent_id])
|
|
736
|
+
removed_parent_link = True
|
|
737
|
+
else:
|
|
738
|
+
_normalize_feature_parent_ids(feature)
|
|
739
|
+
|
|
740
|
+
mutation = _validate_and_save(registry_path, repo_root, registry, "migrating feature parent audit edge")
|
|
741
|
+
return {
|
|
742
|
+
"passed": True,
|
|
743
|
+
"registry_path": registry_path.as_posix(),
|
|
744
|
+
"feature_id": feature_id,
|
|
745
|
+
"parent_feature_id": parent_id,
|
|
746
|
+
"added_requires": added_requires,
|
|
747
|
+
"removed_parent_link": removed_parent_link,
|
|
748
|
+
"entity": deepcopy(feature),
|
|
749
|
+
**mutation,
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
|
|
489
753
|
def set_claim_tier(path: str | Path, claim_id: str, tier: str) -> dict[str, Any]:
|
|
490
754
|
registry_path, repo_root, registry = load_registry(path)
|
|
491
755
|
row = _entity_row(registry, "claims", claim_id)
|