atomicguard 0.1.0__py3-none-any.whl → 1.1.0__py3-none-any.whl

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 (36) hide show
  1. atomicguard/__init__.py +8 -3
  2. atomicguard/application/action_pair.py +7 -1
  3. atomicguard/application/agent.py +46 -6
  4. atomicguard/application/workflow.py +494 -11
  5. atomicguard/domain/__init__.py +4 -1
  6. atomicguard/domain/exceptions.py +19 -0
  7. atomicguard/domain/interfaces.py +137 -6
  8. atomicguard/domain/models.py +120 -6
  9. atomicguard/guards/__init__.py +16 -5
  10. atomicguard/guards/composite/__init__.py +11 -0
  11. atomicguard/guards/dynamic/__init__.py +13 -0
  12. atomicguard/guards/dynamic/test_runner.py +207 -0
  13. atomicguard/guards/interactive/__init__.py +11 -0
  14. atomicguard/guards/static/__init__.py +13 -0
  15. atomicguard/guards/static/imports.py +177 -0
  16. atomicguard/infrastructure/__init__.py +4 -1
  17. atomicguard/infrastructure/llm/__init__.py +1 -1
  18. atomicguard/infrastructure/llm/mock.py +32 -6
  19. atomicguard/infrastructure/llm/ollama.py +40 -17
  20. atomicguard/infrastructure/persistence/__init__.py +7 -1
  21. atomicguard/infrastructure/persistence/checkpoint.py +361 -0
  22. atomicguard/infrastructure/persistence/filesystem.py +69 -5
  23. atomicguard/infrastructure/persistence/memory.py +25 -3
  24. atomicguard/infrastructure/registry.py +126 -0
  25. atomicguard/schemas/__init__.py +142 -0
  26. {atomicguard-0.1.0.dist-info → atomicguard-1.1.0.dist-info}/METADATA +13 -13
  27. atomicguard-1.1.0.dist-info/RECORD +36 -0
  28. atomicguard-1.1.0.dist-info/entry_points.txt +3 -0
  29. atomicguard/guards/test_runner.py +0 -176
  30. atomicguard-0.1.0.dist-info/RECORD +0 -27
  31. /atomicguard/guards/{base.py → composite/base.py} +0 -0
  32. /atomicguard/guards/{human.py → interactive/human.py} +0 -0
  33. /atomicguard/guards/{syntax.py → static/syntax.py} +0 -0
  34. {atomicguard-0.1.0.dist-info → atomicguard-1.1.0.dist-info}/WHEEL +0 -0
  35. {atomicguard-0.1.0.dist-info → atomicguard-1.1.0.dist-info}/licenses/LICENSE +0 -0
  36. {atomicguard-0.1.0.dist-info → atomicguard-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,142 @@
1
+ """AtomicGuard JSON Schema definitions and validation utilities.
2
+
3
+ This module provides JSON Schema definitions for AtomicGuard configuration files,
4
+ aligned with the formal framework defined in the paper.
5
+
6
+ Schemas:
7
+ - workflow.schema.json: Workflow definition (action pairs, guards, preconditions)
8
+ - prompts.schema.json: Prompt templates for generators
9
+ - artifact.schema.json: Artifact storage format in DAG
10
+
11
+ Usage:
12
+ from atomicguard.schemas import validate_workflow, validate_prompts
13
+
14
+ # Validate a workflow configuration
15
+ with open("workflow.json") as f:
16
+ data = json.load(f)
17
+ validate_workflow(data) # Raises jsonschema.ValidationError if invalid
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ from importlib.resources import files
24
+ from typing import TYPE_CHECKING, Any
25
+
26
+ if TYPE_CHECKING:
27
+ import types
28
+
29
+ try:
30
+ import jsonschema as _jsonschema
31
+
32
+ jsonschema: types.ModuleType | None = _jsonschema
33
+ except ImportError:
34
+ jsonschema = None
35
+
36
+
37
+ def _load_schema(name: str) -> dict[str, Any]:
38
+ """Load a JSON schema from the schemas package.
39
+
40
+ Args:
41
+ name: Schema filename (e.g., 'workflow.schema.json')
42
+
43
+ Returns:
44
+ Parsed JSON schema as a dictionary
45
+ """
46
+ schema_text = files("atomicguard.schemas").joinpath(name).read_text()
47
+ result: dict[str, Any] = json.loads(schema_text)
48
+ return result
49
+
50
+
51
+ def get_workflow_schema() -> dict[str, Any]:
52
+ """Get the workflow.json schema.
53
+
54
+ Returns:
55
+ JSON Schema for workflow configuration
56
+ """
57
+ return _load_schema("workflow.schema.json")
58
+
59
+
60
+ def get_prompts_schema() -> dict[str, Any]:
61
+ """Get the prompts.json schema.
62
+
63
+ Returns:
64
+ JSON Schema for prompt templates
65
+ """
66
+ return _load_schema("prompts.schema.json")
67
+
68
+
69
+ def get_artifact_schema() -> dict[str, Any]:
70
+ """Get the artifact.json schema.
71
+
72
+ Returns:
73
+ JSON Schema for artifact storage
74
+ """
75
+ return _load_schema("artifact.schema.json")
76
+
77
+
78
+ def validate_workflow(data: dict[str, Any]) -> None:
79
+ """Validate a workflow configuration against the schema.
80
+
81
+ Args:
82
+ data: Workflow configuration dictionary
83
+
84
+ Raises:
85
+ jsonschema.ValidationError: If validation fails
86
+ ImportError: If jsonschema is not installed
87
+ """
88
+ if jsonschema is None:
89
+ raise ImportError(
90
+ "jsonschema is required for validation. "
91
+ "Install it with: pip install jsonschema"
92
+ )
93
+ schema = get_workflow_schema()
94
+ jsonschema.validate(data, schema)
95
+
96
+
97
+ def validate_prompts(data: dict[str, Any]) -> None:
98
+ """Validate prompt templates against the schema.
99
+
100
+ Args:
101
+ data: Prompts configuration dictionary
102
+
103
+ Raises:
104
+ jsonschema.ValidationError: If validation fails
105
+ ImportError: If jsonschema is not installed
106
+ """
107
+ if jsonschema is None:
108
+ raise ImportError(
109
+ "jsonschema is required for validation. "
110
+ "Install it with: pip install jsonschema"
111
+ )
112
+ schema = get_prompts_schema()
113
+ jsonschema.validate(data, schema)
114
+
115
+
116
+ def validate_artifact(data: dict[str, Any]) -> None:
117
+ """Validate an artifact against the schema.
118
+
119
+ Args:
120
+ data: Artifact dictionary
121
+
122
+ Raises:
123
+ jsonschema.ValidationError: If validation fails
124
+ ImportError: If jsonschema is not installed
125
+ """
126
+ if jsonschema is None:
127
+ raise ImportError(
128
+ "jsonschema is required for validation. "
129
+ "Install it with: pip install jsonschema"
130
+ )
131
+ schema = get_artifact_schema()
132
+ jsonschema.validate(data, schema)
133
+
134
+
135
+ __all__ = [
136
+ "get_workflow_schema",
137
+ "get_prompts_schema",
138
+ "get_artifact_schema",
139
+ "validate_workflow",
140
+ "validate_prompts",
141
+ "validate_artifact",
142
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atomicguard
3
- Version: 0.1.0
3
+ Version: 1.1.0
4
4
  Summary: A Dual-State Agent Framework for reliable LLM code generation with guard-validated loops
5
5
  Author-email: Matthew Thompson <thompsonson@gmail.com>
6
6
  Maintainer-email: Matthew Thompson <thompsonson@gmail.com>
@@ -26,17 +26,10 @@ Classifier: Typing :: Typed
26
26
  Requires-Python: >=3.12
27
27
  Description-Content-Type: text/markdown
28
28
  License-File: LICENSE
29
- Requires-Dist: click>=8.3.1
30
29
  Requires-Dist: matplotlib>=3.10.0
31
- Requires-Dist: openai>=2.12.0
32
- Requires-Dist: rich>=14.0.0
33
- Provides-Extra: dev
34
- Requires-Dist: mypy>=1.13.0; extra == "dev"
35
- Requires-Dist: pre-commit>=4.5.0; extra == "dev"
36
- Requires-Dist: ruff>=0.14.0; extra == "dev"
37
- Provides-Extra: test
38
- Requires-Dist: pytest>=8.0.0; extra == "test"
39
- Requires-Dist: pytest-cov>=6.0.0; extra == "test"
30
+ Requires-Dist: openhands-ai>=0.27.0
31
+ Requires-Dist: pydantic-ai>=1.0.0
32
+ Requires-Dist: pytestarch>=4.0.1
40
33
  Dynamic: license-file
41
34
 
42
35
  # AtomicGuard
@@ -124,13 +117,20 @@ atomicguard/
124
117
 
125
118
  ## Citation
126
119
 
120
+ If you use this framework in your research, please cite the paper:
121
+
122
+ > Thompson, M. (2025). Managing the Stochastic: Foundations of Learning in Neuro-Symbolic Systems for Software Engineering. arXiv preprint arXiv:2512.20660.
123
+
127
124
  ```bibtex
128
125
  @article{thompson2025managing,
129
126
  title={Managing the Stochastic: Foundations of Learning in Neuro-Symbolic Systems for Software Engineering},
130
127
  author={Thompson, Matthew},
131
- year={2025}
128
+ journal={arXiv preprint arXiv:2512.20660},
129
+ year={2025},
130
+ url={[https://arxiv.org/abs/2512.20660](https://arxiv.org/abs/2512.20660)}
132
131
  }
133
- ```
132
+
133
+ ```
134
134
 
135
135
  ## License
136
136
 
@@ -0,0 +1,36 @@
1
+ atomicguard/__init__.py,sha256=baopCXBSWqGIeKbKuKurOUsxgDNbtHZMzyB1MmXHrNU,3051
2
+ atomicguard/application/__init__.py,sha256=-myJt7VuC1tMjO424NhjY6iZZuUdHGnFCJKFapFCAhw,409
3
+ atomicguard/application/action_pair.py,sha256=JfQH88gTZCMl5qa5frqLJnhebn-KgO_oGAol6sD58ys,2214
4
+ atomicguard/application/agent.py,sha256=ND2_pnRGwcWM5MovM8dnratrMph2gz9UmwXEgTeA59s,5907
5
+ atomicguard/application/workflow.py,sha256=mfWz2pdBqQsGC5rBZ5S1x_jaNe2aLvqoG20TvWyAsnM,23309
6
+ atomicguard/domain/__init__.py,sha256=BRmYKYFU-kLG1wPpRrXKxZ2jb4QgMea83r0gbDX_Da0,1149
7
+ atomicguard/domain/exceptions.py,sha256=Zj1iz31AiksyWnyKR8KfnNLRGhZdetrYhCIwFOapBtY,1376
8
+ atomicguard/domain/interfaces.py,sha256=73vRiPzTLkVAiBc2srn-EUNFSQfleooQNx6r3odIQFM,6574
9
+ atomicguard/domain/models.py,sha256=sVuHzuP98pgHefTz1mk1bYptgoeAySekRyBG2zMz7TE,8496
10
+ atomicguard/domain/prompts.py,sha256=SMGcP0kYc9azCbGku5EG0JZZs83D80WkJO-DWiuFV58,2785
11
+ atomicguard/guards/__init__.py,sha256=OzpOjwmyHGpTQSDVSDeGEuXUJNOyiNpZxZVnTVMIOyg,956
12
+ atomicguard/guards/composite/__init__.py,sha256=AedlMBxFw4uTFExVWL6RJRR7vRwMOK3sgjhutkXz838,217
13
+ atomicguard/guards/composite/base.py,sha256=VGydlOD4gmXdiDHvWXF5jSppXcJAoJYSCyDj7Lrbng0,1213
14
+ atomicguard/guards/dynamic/__init__.py,sha256=pjBBeD_yYavvn-I7s5I3nTDOlZtPTq1Hqmim_RgW5cE,321
15
+ atomicguard/guards/dynamic/test_runner.py,sha256=d5cPb5cCM-UnfZjTYGOHn4XoS-N66nr5EBHqPoDGwNM,7196
16
+ atomicguard/guards/interactive/__init__.py,sha256=PlP5LNB4CMZ4fJ7Xr8gjjpn8VwLnB3zs5M2Em9xyVCc,226
17
+ atomicguard/guards/interactive/human.py,sha256=G8SstAEdcMpHR2b8ugyG9drk9aBjw4_pE6E4UrTtcUo,2960
18
+ atomicguard/guards/static/__init__.py,sha256=KANeEg_yhrKGzSmA0yf2SewOE9qkUwnJly4mWkBzsm4,305
19
+ atomicguard/guards/static/imports.py,sha256=nWS3FNNqle7YWU5_tgRgYMRAXwEcWlxee8Q4MdomN5w,6292
20
+ atomicguard/guards/static/syntax.py,sha256=mPVgGDY3pzwtXuulmmuEwYAQG7rNG0fgSGB1puYRI6Y,919
21
+ atomicguard/infrastructure/__init__.py,sha256=Y3Ot6sRgTind-8s5kCnNtRYJGN3uq84QmbAUESx8Pio,581
22
+ atomicguard/infrastructure/registry.py,sha256=XilDY2sUedwm5z1Xrp39HfQ9e3paixyJxL_hwGaVbqs,3662
23
+ atomicguard/infrastructure/llm/__init__.py,sha256=HVOeCf_wVx84h1EVh_OYvajcyn_5lWNIejTDccMJak0,234
24
+ atomicguard/infrastructure/llm/mock.py,sha256=883Y7e2Q1yWapvp3rXn03bHEvuRnFwDe0CZsTZziG_E,2639
25
+ atomicguard/infrastructure/llm/ollama.py,sha256=pLSZX_X5U1aPvIxCsF_wsQ_AHsTspqU3xkYHWLbgJQ0,4832
26
+ atomicguard/infrastructure/persistence/__init__.py,sha256=KS95wmO_qVF6It63_884lWDoa-IB0WvRma_nXS2uTq4,483
27
+ atomicguard/infrastructure/persistence/checkpoint.py,sha256=MS4qpJ_jcl6Ibw7h3KncWgDqz7CEIlFJdrpFcu2vEgs,13966
28
+ atomicguard/infrastructure/persistence/filesystem.py,sha256=4upoycWCOlWRoSlVm4Ux_Kj8r3hX3Nm8N80x-IfBSLw,11440
29
+ atomicguard/infrastructure/persistence/memory.py,sha256=JTnTwXSKYYDnQEYbjwAFVpklZVAlQlCDIt9zxcQjlz0,2042
30
+ atomicguard/schemas/__init__.py,sha256=9z3gWBHsq8FHD2qE_TPy31wkTa_qXPfVgJrNdIu8p4g,3735
31
+ atomicguard-1.1.0.dist-info/licenses/LICENSE,sha256=ROMMFVruZ18U24pKTNte9AK_YIqoMfXnMzoxqBNmKS4,1073
32
+ atomicguard-1.1.0.dist-info/METADATA,sha256=XFS0IYTNDX_anzgWxUjyv5ha8YE_mdVtHW-GJM5SRJM,5013
33
+ atomicguard-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ atomicguard-1.1.0.dist-info/entry_points.txt,sha256=wjCTsvKRBtbyiAQiGNNytVB3lbZU03Y0NtGvuPTvFLo,151
35
+ atomicguard-1.1.0.dist-info/top_level.txt,sha256=J_6ENELjnacSYJ5N3FGwomp-sVeAJQohPkJBh6pu6iY,12
36
+ atomicguard-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [atomicguard.generators]
2
+ MockGenerator = atomicguard.infrastructure.llm:MockGenerator
3
+ OllamaGenerator = atomicguard.infrastructure.llm:OllamaGenerator
@@ -1,176 +0,0 @@
1
- """
2
- Test execution guards.
3
-
4
- Guards that validate artifacts by running tests against them.
5
- """
6
-
7
- import multiprocessing
8
- import sys
9
- import types
10
- from typing import Any
11
-
12
- from atomicguard.domain.interfaces import GuardInterface
13
- from atomicguard.domain.models import Artifact, GuardResult
14
-
15
-
16
- class TestGuard(GuardInterface):
17
- """
18
- Validates artifact via test execution in the same process.
19
-
20
- Simple guard that executes test code against artifact content.
21
- For isolation, use DynamicTestGuard instead.
22
- """
23
-
24
- def __init__(self, test_code: str | None = None):
25
- """
26
- Args:
27
- test_code: Static test code to run (if not using dependencies)
28
- """
29
- self._static_test_code = test_code
30
-
31
- def validate(self, artifact: Artifact, **deps: Any) -> GuardResult:
32
- """
33
- Execute test code against artifact.
34
-
35
- Args:
36
- artifact: The implementation artifact to test
37
- **deps: May include 'test' artifact with test code
38
-
39
- Returns:
40
- GuardResult with test outcome
41
- """
42
- test_artifact = deps.get("test")
43
- test_code = test_artifact.content if test_artifact else self._static_test_code
44
-
45
- if not test_code:
46
- return GuardResult(passed=False, feedback="No test code provided")
47
-
48
- namespace: dict[str, Any] = {}
49
- try:
50
- exec(artifact.content, namespace)
51
- exec(test_code, namespace)
52
- return GuardResult(passed=True)
53
- except AssertionError as e:
54
- return GuardResult(passed=False, feedback=f"Test failed: {e}")
55
- except Exception as e:
56
- return GuardResult(passed=False, feedback=f"{type(e).__name__}: {e}")
57
-
58
-
59
- class DynamicTestGuard(GuardInterface):
60
- """
61
- Runs test code against implementation in isolated subprocess.
62
-
63
- Expects 'test' dependency containing the test artifact.
64
- Executes tests and returns pass/fail with detailed feedback.
65
-
66
- Uses multiprocessing for isolation to prevent test code from
67
- affecting the parent process.
68
- """
69
-
70
- def __init__(self, timeout: float = 60.0):
71
- """
72
- Args:
73
- timeout: Maximum time in seconds to wait for test execution
74
- """
75
- self.timeout = timeout
76
-
77
- def validate(self, artifact: Artifact, **deps: Any) -> GuardResult:
78
- """
79
- Run tests in isolated subprocess.
80
-
81
- Args:
82
- artifact: The implementation artifact to test
83
- **deps: Must include 'test' artifact with test code
84
-
85
- Returns:
86
- GuardResult with test outcome
87
- """
88
- test_artifact = deps.get("test")
89
- if not test_artifact:
90
- return GuardResult(
91
- passed=False, feedback="No test artifact in dependencies"
92
- )
93
-
94
- q: multiprocessing.Queue = multiprocessing.Queue()
95
- p = multiprocessing.Process(
96
- target=self._run_tests, args=(artifact, test_artifact, q)
97
- )
98
- p.start()
99
- p.join(self.timeout)
100
-
101
- if p.is_alive():
102
- p.terminate()
103
- p.join()
104
- return GuardResult(
105
- passed=False,
106
- feedback=f"Timeout: Test execution exceeded {self.timeout}s",
107
- )
108
-
109
- if not q.empty():
110
- passed, msg = q.get()
111
- return GuardResult(passed=passed, feedback=msg)
112
- return GuardResult(passed=False, feedback="Test execution crashed")
113
-
114
- def _run_tests(
115
- self, impl_artifact: Artifact, test_artifact: Artifact, q: Any
116
- ) -> None:
117
- """
118
- Execute tests in subprocess.
119
-
120
- This method runs in a forked process for isolation.
121
- """
122
- try:
123
- impl_code = impl_artifact.content
124
- test_code = test_artifact.content
125
-
126
- if not impl_code:
127
- q.put((False, "No implementation code"))
128
- return
129
-
130
- if not test_code:
131
- q.put((False, "No test code"))
132
- return
133
-
134
- # Create mock 'implementation' module
135
- impl_module = types.ModuleType("implementation")
136
- exec(impl_code, impl_module.__dict__)
137
- sys.modules["implementation"] = impl_module
138
-
139
- # Execute test code (pytest already in sys.modules from parent)
140
- import pytest
141
-
142
- test_scope = {"__builtins__": __builtins__, "pytest": pytest}
143
- exec(test_code, test_scope)
144
-
145
- # Find and run test functions
146
- test_funcs = [
147
- v
148
- for k, v in test_scope.items()
149
- if k.startswith("test_") and callable(v)
150
- ]
151
-
152
- if not test_funcs:
153
- q.put((False, "No test functions found"))
154
- return
155
-
156
- failures = []
157
- for func in test_funcs:
158
- try:
159
- func()
160
- except AssertionError as e:
161
- failures.append(f"{func.__name__}: AssertionError - {e}")
162
- except Exception as e:
163
- failures.append(f"{func.__name__}: {type(e).__name__} - {e}")
164
-
165
- if failures:
166
- q.put((False, "Test failures:\n" + "\n".join(failures)))
167
- else:
168
- q.put((True, f"All {len(test_funcs)} tests passed"))
169
-
170
- except SyntaxError as e:
171
- q.put((False, f"Syntax error: {e}"))
172
- except Exception as e:
173
- q.put((False, f"Execution error: {e}"))
174
- finally:
175
- if "implementation" in sys.modules:
176
- del sys.modules["implementation"]
@@ -1,27 +0,0 @@
1
- atomicguard/__init__.py,sha256=kiNxqjbVSlSf6Z0U7wpus3o_eMTB7M5-Dj41qgcgUwI,2927
2
- atomicguard/application/__init__.py,sha256=-myJt7VuC1tMjO424NhjY6iZZuUdHGnFCJKFapFCAhw,409
3
- atomicguard/application/action_pair.py,sha256=pa9gw4cTb8HaZLT_VqjQaKVMGGllo0BcDf6YzqsJlnA,1959
4
- atomicguard/application/agent.py,sha256=uzwhrn43JkrK1xxuw8ETvjL4IAiVZ1_Jeik4tMY_2ZE,4254
5
- atomicguard/application/workflow.py,sha256=CPnLVaten4hN4fiC_gGMvN9-BCJIVVwYsQb3HtRTlJg,4902
6
- atomicguard/domain/__init__.py,sha256=z7CWqBs21XsZPLeuqLD1fNQVtl9VxIs3zkrK_1n4uD0,1061
7
- atomicguard/domain/exceptions.py,sha256=3L1acckIcf0XewBy8QJpzgCRBaKyYdossbIAikt1Xkw,770
8
- atomicguard/domain/interfaces.py,sha256=3kPXRof0a-4c225d1DgZ_66_5A6P8H1XZK-gTp9YkCI,2935
9
- atomicguard/domain/models.py,sha256=p9FqB9MdriV7djaGUkREGEgkcWyhGvpYmHYrLq2VX-w,4483
10
- atomicguard/domain/prompts.py,sha256=SMGcP0kYc9azCbGku5EG0JZZs83D80WkJO-DWiuFV58,2785
11
- atomicguard/guards/__init__.py,sha256=pd4H3sfnrN76vKBX3KrXWsoe27V5vNacSEUKW9uIbmM,548
12
- atomicguard/guards/base.py,sha256=VGydlOD4gmXdiDHvWXF5jSppXcJAoJYSCyDj7Lrbng0,1213
13
- atomicguard/guards/human.py,sha256=G8SstAEdcMpHR2b8ugyG9drk9aBjw4_pE6E4UrTtcUo,2960
14
- atomicguard/guards/syntax.py,sha256=mPVgGDY3pzwtXuulmmuEwYAQG7rNG0fgSGB1puYRI6Y,919
15
- atomicguard/guards/test_runner.py,sha256=dIFtyOXxu_dxKDlrsBl_G7kCR5oy5gLQESQtYGslKO4,5538
16
- atomicguard/infrastructure/__init__.py,sha256=98eYKEgEIzJ05Z5r82M7tB0S0QGD2PrfPR_8wapzfHA,465
17
- atomicguard/infrastructure/llm/__init__.py,sha256=lmr-cYVeaiAsMi_DWcoM7G1wEs6Y6GS02RZeOs4GI-U,234
18
- atomicguard/infrastructure/llm/mock.py,sha256=OVumDkWYe9xU8KQHdt79evpHzIJIRRxXIqeC9fgI-hQ,1840
19
- atomicguard/infrastructure/llm/ollama.py,sha256=jtE3SXfMSsYUDocuKXDH3H4NrR8cMwjvCpSFkn99Xlg,4019
20
- atomicguard/infrastructure/persistence/__init__.py,sha256=DdoD0V-uCetJDo_RJRPiCfjae3xAX5yofnQWbalBrJw,285
21
- atomicguard/infrastructure/persistence/filesystem.py,sha256=LpdExc9FAwB2Od8eM5zSEMx4hC4Lx2AaztJdba8_MD0,8638
22
- atomicguard/infrastructure/persistence/memory.py,sha256=gZo2hcpi5VOu60nscxPnyQBDg8P87MI9ZXBdaW0eebQ,1339
23
- atomicguard-0.1.0.dist-info/licenses/LICENSE,sha256=ROMMFVruZ18U24pKTNte9AK_YIqoMfXnMzoxqBNmKS4,1073
24
- atomicguard-0.1.0.dist-info/METADATA,sha256=yJlnBW1n_-4ORhWkIjTOykpD9vf6N9OHzO4mJIh39lc,4919
25
- atomicguard-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- atomicguard-0.1.0.dist-info/top_level.txt,sha256=J_6ENELjnacSYJ5N3FGwomp-sVeAJQohPkJBh6pu6iY,12
27
- atomicguard-0.1.0.dist-info/RECORD,,
File without changes
File without changes
File without changes