agentic-forge 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,93 @@
1
+ # Rust
2
+ target/
3
+ *.swp
4
+ *.swo
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.egg-info/
11
+ *.egg
12
+ dist/
13
+ build/
14
+ .eggs/
15
+ *.whl
16
+ .venv/
17
+ venv/
18
+ env/
19
+
20
+ # IDE
21
+ .vscode/
22
+ .idea/
23
+ *.iml
24
+ .fleet/
25
+
26
+ # OS
27
+ .DS_Store
28
+ Thumbs.db
29
+ *.swp
30
+ *~
31
+
32
+ # Testing
33
+ .pytest_cache/
34
+ .coverage
35
+ htmlcov/
36
+ .mypy_cache/
37
+ .tox/
38
+
39
+ # LaTeX build artifacts
40
+ *.aux
41
+ *.bbl
42
+ *.blg
43
+ *.log
44
+ *.out
45
+ *.toc
46
+ *.fls
47
+ *.fdb_latexmk
48
+ *.synctex.gz
49
+
50
+ # Planning docs / AI prompts (internal only)
51
+ planning-docs/
52
+ CLAUDE-CODE-INSTRUCTIONS*.md
53
+ SPEC-*.md
54
+
55
+ # Internal vision / roadmap documents
56
+ docs/VISION-*.md
57
+
58
+ # MCP crate internal docs / specs / AI prompts
59
+ crates/agentic-forge-mcp/docs/
60
+ crates/agentic-forge-mcp/scripts/
61
+
62
+ # Internal / not for public repo
63
+ CLAUDE.md
64
+ sister.manifest.json
65
+
66
+ # Claude Code
67
+ .claude/
68
+ .agentra/
69
+ docs/REPO_HYGIENE.md
70
+
71
+ # Root-level paper files (canonical copies are in paper/)
72
+ /agenticforge-paper.*
73
+ /references.bib
74
+
75
+ # FFI (build artifacts)
76
+ /ffi/
77
+
78
+ # Scripts (internal tooling)
79
+ /agent/scripts/
80
+ /installer/scripts/
81
+
82
+ # Internal docs (not for public repo)
83
+ docs/internal/
84
+ ECOSYSTEM-CONVENTIONS.md
85
+
86
+ # Local goals (internal only)
87
+ goals/
88
+
89
+ # Environment
90
+ .env
91
+ .env.local
92
+ *.pem
93
+ *.key
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentic-forge
3
+ Version: 0.1.0
4
+ Summary: Blueprint engine for AI agents
5
+ Project-URL: Homepage, https://github.com/agentralabs/agentic-forge
6
+ Project-URL: Documentation, https://github.com/agentralabs/agentic-forge/tree/main/docs
7
+ Project-URL: Repository, https://github.com/agentralabs/agentic-forge
8
+ Author: Agentra Labs
9
+ License-Expression: MIT
10
+ Keywords: agents,ai,blueprint,forge,mcp
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.10
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
22
+ Requires-Dist: pytest>=8.0; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # AgenticForge Python SDK
26
+
27
+ Blueprint engine for AI agents -- project architecture generation and code scaffolding.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install agentic-forge
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```python
38
+ from agentic_forge import ForgeEngine
39
+
40
+ fe = ForgeEngine("project.forge")
41
+ fe.create_blueprint("auth-service", template="microservice")
42
+ fe.generate("auth-service", output_dir="./generated")
43
+ ```
44
+
45
+ Requires the `aforge` CLI binary on PATH. Install via:
46
+
47
+ ```bash
48
+ curl -fsSL https://agentralabs.tech/install/forge | bash
49
+ ```
@@ -0,0 +1,25 @@
1
+ # AgenticForge Python SDK
2
+
3
+ Blueprint engine for AI agents -- project architecture generation and code scaffolding.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install agentic-forge
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from agentic_forge import ForgeEngine
15
+
16
+ fe = ForgeEngine("project.forge")
17
+ fe.create_blueprint("auth-service", template="microservice")
18
+ fe.generate("auth-service", output_dir="./generated")
19
+ ```
20
+
21
+ Requires the `aforge` CLI binary on PATH. Install via:
22
+
23
+ ```bash
24
+ curl -fsSL https://agentralabs.tech/install/forge | bash
25
+ ```
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agentic-forge"
7
+ version = "0.1.0"
8
+ description = "Blueprint engine for AI agents"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Agentra Labs" },
14
+ ]
15
+ keywords = ["ai", "agents", "forge", "blueprint", "mcp"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
25
+ ]
26
+
27
+ dependencies = []
28
+
29
+ [project.optional-dependencies]
30
+ dev = [
31
+ "pytest>=8.0",
32
+ "pytest-cov>=5.0",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/agentralabs/agentic-forge"
37
+ Documentation = "https://github.com/agentralabs/agentic-forge/tree/main/docs"
38
+ Repository = "https://github.com/agentralabs/agentic-forge"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/agentic_forge"]
42
+
43
+ [tool.pytest.ini_options]
44
+ testpaths = ["tests"]
@@ -0,0 +1,133 @@
1
+ """AgenticForge — Blueprint engine for AI agents.
2
+
3
+ Pure-Python SDK that wraps the ``aforge`` CLI binary via subprocess.
4
+ Zero required dependencies; only stdlib: subprocess, json, pathlib, dataclasses.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import subprocess
12
+ from dataclasses import dataclass, field
13
+ from pathlib import Path
14
+ from typing import Any, Optional
15
+
16
+ __version__ = "0.1.0"
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ForgeError(Exception):
22
+ """Raised when an aforge CLI command fails."""
23
+
24
+
25
+ @dataclass
26
+ class ForgeEngine:
27
+ """Interface to a ``.forge`` blueprint file.
28
+
29
+ Parameters
30
+ ----------
31
+ path : str | Path
32
+ Path to the ``.forge`` file. Created automatically on first write
33
+ if it does not exist.
34
+ binary : str
35
+ Name or path of the ``aforge`` CLI binary.
36
+ """
37
+
38
+ path: str | Path
39
+ binary: str = "aforge"
40
+ _resolved_binary: Optional[str] = field(default=None, repr=False, init=False)
41
+
42
+ def __post_init__(self) -> None:
43
+ self.path = Path(self.path)
44
+
45
+ # ------------------------------------------------------------------
46
+ # Internal helpers
47
+ # ------------------------------------------------------------------
48
+
49
+ def _find_binary(self) -> str:
50
+ if self._resolved_binary is not None:
51
+ return self._resolved_binary
52
+
53
+ import shutil
54
+
55
+ found = shutil.which(self.binary)
56
+ if found is None:
57
+ raise ForgeError(
58
+ f"Cannot find '{self.binary}' on PATH. "
59
+ "Install AgenticForge: curl -fsSL https://agentralabs.tech/install/forge | bash"
60
+ )
61
+ self._resolved_binary = found
62
+ return found
63
+
64
+ def _run(self, *args: str, check: bool = True) -> str:
65
+ """Execute an aforge CLI command and return stdout."""
66
+ cmd = [self._find_binary(), "--file", str(self.path), *args]
67
+ logger.debug("Running: %s", " ".join(cmd))
68
+ result = subprocess.run(cmd, capture_output=True, text=True)
69
+ if check and result.returncode != 0:
70
+ raise ForgeError(
71
+ f"aforge command failed (exit {result.returncode}): {result.stderr.strip()}"
72
+ )
73
+ return result.stdout.strip()
74
+
75
+ def _run_json(self, *args: str) -> Any:
76
+ """Execute a command and parse JSON output."""
77
+ raw = self._run(*args, "--format", "json")
78
+ return json.loads(raw) if raw else {}
79
+
80
+ # ------------------------------------------------------------------
81
+ # Blueprint operations
82
+ # ------------------------------------------------------------------
83
+
84
+ def create_blueprint(
85
+ self,
86
+ name: str,
87
+ *,
88
+ template: Optional[str] = None,
89
+ ) -> str:
90
+ """Create a new blueprint. Returns the blueprint ID."""
91
+ args = ["blueprint", "create", name]
92
+ if template:
93
+ args.extend(["--template", template])
94
+ return self._run(*args)
95
+
96
+ def generate(
97
+ self,
98
+ name: str,
99
+ *,
100
+ output_dir: Optional[str] = None,
101
+ ) -> str:
102
+ """Generate code from a blueprint. Returns generation summary."""
103
+ args = ["generate", name]
104
+ if output_dir:
105
+ args.extend(["--output", output_dir])
106
+ return self._run(*args)
107
+
108
+ def list_blueprints(self) -> list[dict[str, Any]]:
109
+ """List all blueprints."""
110
+ raw = self._run("blueprint", "list", "--format", "json")
111
+ return json.loads(raw) if raw else []
112
+
113
+ # ------------------------------------------------------------------
114
+ # Stats
115
+ # ------------------------------------------------------------------
116
+
117
+ def stats(self) -> dict[str, Any]:
118
+ """Get forge engine statistics."""
119
+ raw = self._run("stats", "--format", "json")
120
+ return json.loads(raw) if raw else {}
121
+
122
+ # ------------------------------------------------------------------
123
+ # File operations
124
+ # ------------------------------------------------------------------
125
+
126
+ def save(self) -> None:
127
+ """Explicit save (most operations auto-save)."""
128
+ pass
129
+
130
+ @property
131
+ def exists(self) -> bool:
132
+ """Whether the .forge file exists on disk."""
133
+ return self.path.exists()
File without changes
@@ -0,0 +1,310 @@
1
+ """Comprehensive tests for AgenticForge Python SDK."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import subprocess
6
+ from pathlib import Path, PurePosixPath
7
+ from unittest.mock import patch, MagicMock
8
+
9
+ import pytest
10
+
11
+ from agentic_forge import ForgeEngine, ForgeError, __version__
12
+
13
+
14
+ # ---------------------------------------------------------------------------
15
+ # 1. Package Metadata
16
+ # ---------------------------------------------------------------------------
17
+
18
+
19
+ class TestPackageMetadata:
20
+ def test_version_exists(self) -> None:
21
+ assert __version__ is not None
22
+ assert isinstance(__version__, str)
23
+ assert len(__version__) > 0
24
+
25
+ def test_version_semver(self) -> None:
26
+ parts = __version__.split(".")
27
+ assert len(parts) == 3
28
+ assert all(p.isdigit() for p in parts)
29
+
30
+ def test_version_is_010(self) -> None:
31
+ assert __version__ == "0.1.0"
32
+
33
+ def test_import_main_class(self) -> None:
34
+ assert ForgeEngine is not None
35
+
36
+ def test_import_error_class(self) -> None:
37
+ assert ForgeError is not None
38
+ assert issubclass(ForgeError, Exception)
39
+
40
+ def test_main_class_has_docstring(self) -> None:
41
+ assert ForgeEngine.__doc__ is not None
42
+ assert len(ForgeEngine.__doc__) > 10
43
+
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # 2. Initialization
47
+ # ---------------------------------------------------------------------------
48
+
49
+
50
+ class TestInit:
51
+ def test_create_with_string_path(self, tmp_path: Path) -> None:
52
+ path = str(tmp_path / "test.forge")
53
+ obj = ForgeEngine(path)
54
+ assert str(obj.path) == path
55
+
56
+ def test_create_with_path_object(self, tmp_path: Path) -> None:
57
+ path = tmp_path / "test.forge"
58
+ obj = ForgeEngine(path)
59
+ assert obj.path == path
60
+
61
+ def test_create_with_pure_posix_path(self) -> None:
62
+ obj = ForgeEngine(PurePosixPath("/tmp/test.forge"))
63
+ assert "test.forge" in str(obj.path)
64
+
65
+ def test_path_converted_to_path_object(self, tmp_path: Path) -> None:
66
+ path = str(tmp_path / "test.forge")
67
+ obj = ForgeEngine(path)
68
+ assert isinstance(obj.path, Path)
69
+
70
+ def test_custom_binary_name(self, tmp_path: Path) -> None:
71
+ obj = ForgeEngine(str(tmp_path / "test.forge"), binary="custom-bin")
72
+ assert obj.binary == "custom-bin"
73
+
74
+ def test_default_binary_name(self, tmp_path: Path) -> None:
75
+ obj = ForgeEngine(str(tmp_path / "test.forge"))
76
+ assert obj.binary == "aforge"
77
+
78
+ def test_exists_false_for_new(self, tmp_path: Path) -> None:
79
+ obj = ForgeEngine(str(tmp_path / "nonexistent.forge"))
80
+ assert not obj.exists
81
+
82
+ def test_exists_true_when_file_present(self, tmp_path: Path) -> None:
83
+ path = tmp_path / "exists.forge"
84
+ path.touch()
85
+ obj = ForgeEngine(str(path))
86
+ assert obj.exists
87
+
88
+ def test_save_is_noop(self, tmp_path: Path) -> None:
89
+ obj = ForgeEngine(str(tmp_path / "test.forge"))
90
+ obj.save()
91
+
92
+ def test_repr_does_not_crash(self, tmp_path: Path) -> None:
93
+ obj = ForgeEngine(str(tmp_path / "test.forge"))
94
+ r = repr(obj)
95
+ assert isinstance(r, str)
96
+
97
+
98
+ # ---------------------------------------------------------------------------
99
+ # 3. Binary Resolution
100
+ # ---------------------------------------------------------------------------
101
+
102
+
103
+ class TestBinaryResolution:
104
+ def test_missing_binary_raises(self, tmp_path: Path) -> None:
105
+ obj = ForgeEngine(str(tmp_path / "t.forge"), binary="nonexistent-xyz-999")
106
+ with pytest.raises(ForgeError):
107
+ obj._find_binary()
108
+
109
+ def test_error_contains_binary_name(self, tmp_path: Path) -> None:
110
+ obj = ForgeEngine(str(tmp_path / "t.forge"), binary="nonexistent-xyz-999")
111
+ with pytest.raises(ForgeError, match="nonexistent-xyz-999"):
112
+ obj._find_binary()
113
+
114
+ def test_error_contains_install_hint(self, tmp_path: Path) -> None:
115
+ obj = ForgeEngine(str(tmp_path / "t.forge"), binary="nonexistent-xyz-999")
116
+ with pytest.raises(ForgeError, match="Install"):
117
+ obj._find_binary()
118
+
119
+ def test_caches_result(self, tmp_path: Path) -> None:
120
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
121
+ obj._resolved_binary = "/fake/path/aforge"
122
+ assert obj._find_binary() == "/fake/path/aforge"
123
+
124
+ def test_cache_persists_across_calls(self, tmp_path: Path) -> None:
125
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
126
+ obj._resolved_binary = "/cached/bin"
127
+ assert obj._find_binary() == "/cached/bin"
128
+ assert obj._find_binary() == "/cached/bin"
129
+
130
+
131
+ # ---------------------------------------------------------------------------
132
+ # 4. Subprocess Execution
133
+ # ---------------------------------------------------------------------------
134
+
135
+
136
+ class TestSubprocessExecution:
137
+ def test_run_calls_subprocess(self, tmp_path: Path) -> None:
138
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
139
+ obj._resolved_binary = "/usr/bin/echo"
140
+ with patch("subprocess.run") as mock_run:
141
+ mock_run.return_value = MagicMock(
142
+ returncode=0, stdout="ok\n", stderr=""
143
+ )
144
+ result = obj._run("arg1", "arg2")
145
+ assert mock_run.called
146
+ cmd = mock_run.call_args[0][0]
147
+ assert cmd[0] == "/usr/bin/echo"
148
+ assert "arg1" in cmd
149
+ assert "arg2" in cmd
150
+
151
+ def test_run_includes_file_flag(self, tmp_path: Path) -> None:
152
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
153
+ obj._resolved_binary = "/usr/bin/echo"
154
+ with patch("subprocess.run") as mock_run:
155
+ mock_run.return_value = MagicMock(
156
+ returncode=0, stdout="ok\n", stderr=""
157
+ )
158
+ obj._run("test")
159
+ cmd = mock_run.call_args[0][0]
160
+ assert "--file" in cmd
161
+ assert str(tmp_path / "t.forge") in cmd
162
+
163
+ def test_run_raises_on_nonzero_exit(self, tmp_path: Path) -> None:
164
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
165
+ obj._resolved_binary = "/bin/false"
166
+ with patch("subprocess.run") as mock_run:
167
+ mock_run.return_value = MagicMock(
168
+ returncode=1, stdout="", stderr="error happened"
169
+ )
170
+ with pytest.raises(ForgeError, match="error happened"):
171
+ obj._run("fail")
172
+
173
+ def test_run_returns_stripped_stdout(self, tmp_path: Path) -> None:
174
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
175
+ obj._resolved_binary = "/usr/bin/echo"
176
+ with patch("subprocess.run") as mock_run:
177
+ mock_run.return_value = MagicMock(
178
+ returncode=0, stdout=" hello world \n", stderr=""
179
+ )
180
+ result = obj._run("test")
181
+ assert result == "hello world"
182
+
183
+ def test_run_json_parses_output(self, tmp_path: Path) -> None:
184
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
185
+ obj._resolved_binary = "/usr/bin/echo"
186
+ with patch("subprocess.run") as mock_run:
187
+ mock_run.return_value = MagicMock(
188
+ returncode=0, stdout='{"key": "value"}\n', stderr=""
189
+ )
190
+ result = obj._run_json("test")
191
+ assert result == {"key": "value"}
192
+
193
+ def test_run_json_raises_on_invalid_json(self, tmp_path: Path) -> None:
194
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
195
+ obj._resolved_binary = "/usr/bin/echo"
196
+ with patch("subprocess.run") as mock_run:
197
+ mock_run.return_value = MagicMock(
198
+ returncode=0, stdout="not json at all", stderr=""
199
+ )
200
+ with pytest.raises((json.JSONDecodeError, ForgeError)):
201
+ obj._run_json("test")
202
+
203
+ def test_run_json_returns_empty_dict_on_empty_output(self, tmp_path: Path) -> None:
204
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
205
+ obj._resolved_binary = "/usr/bin/echo"
206
+ with patch("subprocess.run") as mock_run:
207
+ mock_run.return_value = MagicMock(
208
+ returncode=0, stdout="", stderr=""
209
+ )
210
+ result = obj._run_json("test")
211
+ assert result == {}
212
+
213
+
214
+ # ---------------------------------------------------------------------------
215
+ # 5. Edge Cases
216
+ # ---------------------------------------------------------------------------
217
+
218
+
219
+ class TestEdgeCases:
220
+ def test_empty_path(self) -> None:
221
+ obj = ForgeEngine("")
222
+ assert isinstance(obj.path, Path)
223
+
224
+ def test_path_with_spaces(self, tmp_path: Path) -> None:
225
+ path = tmp_path / "path with spaces" / "test.forge"
226
+ obj = ForgeEngine(str(path))
227
+ assert "spaces" in str(obj.path)
228
+
229
+ def test_path_with_unicode(self, tmp_path: Path) -> None:
230
+ path = tmp_path / "donnees" / "test.forge"
231
+ obj = ForgeEngine(str(path))
232
+ assert "donnees" in str(obj.path)
233
+
234
+ def test_very_long_path(self, tmp_path: Path) -> None:
235
+ long_name = "a" * 200
236
+ path = tmp_path / long_name / "test.forge"
237
+ obj = ForgeEngine(str(path))
238
+ assert len(str(obj.path)) > 200
239
+
240
+ def test_save_idempotent(self, tmp_path: Path) -> None:
241
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
242
+ obj.save()
243
+ obj.save()
244
+ obj.save()
245
+
246
+ def test_multiple_instances_independent(self, tmp_path: Path) -> None:
247
+ a = ForgeEngine(str(tmp_path / "a.forge"))
248
+ b = ForgeEngine(str(tmp_path / "b.forge"))
249
+ assert a.path != b.path
250
+ a._resolved_binary = "/path/a"
251
+ assert b._resolved_binary is None
252
+
253
+ def test_dot_in_directory_name(self, tmp_path: Path) -> None:
254
+ path = tmp_path / "v1.0.0" / "test.forge"
255
+ obj = ForgeEngine(str(path))
256
+ assert "v1.0.0" in str(obj.path)
257
+
258
+
259
+ # ---------------------------------------------------------------------------
260
+ # 6. Error Handling
261
+ # ---------------------------------------------------------------------------
262
+
263
+
264
+ class TestErrorHandling:
265
+ def test_error_is_exception(self) -> None:
266
+ assert issubclass(ForgeError, Exception)
267
+
268
+ def test_error_stores_message(self) -> None:
269
+ err = ForgeError("test message")
270
+ assert "test message" in str(err)
271
+
272
+ def test_error_caught_as_exception(self) -> None:
273
+ with pytest.raises(Exception):
274
+ raise ForgeError("boom")
275
+
276
+ def test_error_caught_specifically(self) -> None:
277
+ try:
278
+ raise ForgeError("specific")
279
+ except ForgeError as e:
280
+ assert "specific" in str(e)
281
+
282
+ def test_error_repr(self) -> None:
283
+ err = ForgeError("repr test")
284
+ assert repr(err) is not None
285
+
286
+
287
+ # ---------------------------------------------------------------------------
288
+ # 7. Stress Tests
289
+ # ---------------------------------------------------------------------------
290
+
291
+
292
+ class TestStress:
293
+ def test_create_1000_instances(self, tmp_path: Path) -> None:
294
+ instances = [
295
+ ForgeEngine(str(tmp_path / f"test_{i}.forge"))
296
+ for i in range(1000)
297
+ ]
298
+ assert len(instances) == 1000
299
+ assert instances[0].path != instances[999].path
300
+
301
+ def test_find_binary_1000_cached(self, tmp_path: Path) -> None:
302
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
303
+ obj._resolved_binary = "/cached/bin"
304
+ for _ in range(1000):
305
+ assert obj._find_binary() == "/cached/bin"
306
+
307
+ def test_save_100_times(self, tmp_path: Path) -> None:
308
+ obj = ForgeEngine(str(tmp_path / "t.forge"))
309
+ for _ in range(100):
310
+ obj.save()