Forge native integration: PRD + forge library layer #318

Open
didericis-claude wants to merge 9 commits from forge-native-integration into main
5 changed files with 45 additions and 31 deletions
Showing only changes of commit f211ece6bf - Show all commits
+3 -2
View File
@@ -8,6 +8,7 @@ core (`assume_yes` + `headless_prompt_text`) as `start --headless`.
from __future__ import annotations
import unittest
from typing import Any
from unittest.mock import MagicMock, patch
import bot_bottle.cli.resume as resume_mod
@@ -40,9 +41,9 @@ class ResumeHeadlessTest(unittest.TestCase):
).start()
self.addCleanup(patch.stopall)
def _launch_kwargs(self) -> dict:
def _launch_kwargs(self) -> dict[str, Any]:
self._launch.assert_called_once()
return self._launch.call_args.kwargs
return dict(self._launch.call_args.kwargs)
def test_headless_passes_assume_yes_and_prompt(self):
rc = resume_mod.cmd_resume(
+1 -1
View File
@@ -88,7 +88,7 @@ class TestScopedForgeWrites(unittest.TestCase):
def test_scope_error_is_permission_error(self):
# Sidecars can catch the stdlib base type.
self.assertTrue(issubclass(ForgeScopeError, PermissionError))
self.assertIn(PermissionError, ForgeScopeError.__mro__)
if __name__ == "__main__":
+2 -2
View File
@@ -20,11 +20,11 @@ def _client() -> GiteaClient:
)
def _resp(body, status: int = 200) -> MagicMock:
def _resp(body: object, status: int = 200) -> MagicMock:
resp = MagicMock()
resp.read.return_value = json.dumps(body).encode() if body is not None else b""
resp.status = status
resp.__enter__ = lambda s: s
resp.__enter__ = lambda s: s # type: ignore
resp.__exit__ = MagicMock(return_value=False)
return resp
+16 -16
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import tempfile
import unittest
from dataclasses import replace
from pathlib import Path
from unittest.mock import patch
@@ -15,22 +16,21 @@ from bot_bottle.contrib.gitea.forge_state import (
)
def _state(**over) -> ForgeState:
base = {
"owner": "didericis",
"repo": "bot-bottle",
"issue_number": 17,
"slug": "implementer-abc12",
"agent_name": "implementer",
"bottle_names": ["claude"],
"backend_name": "docker",
"agent_git_user": "didericis-claude",
"pr_number": 42,
"status": STATUS_FROZEN,
"last_checkin_at": "2026-06-29T12:04:12-04:00",
}
base.update(over)
return ForgeState(**base)
def _state(**over: object) -> ForgeState:
base = ForgeState(
owner="didericis",
repo="bot-bottle",
issue_number=17,
slug="implementer-abc12",
agent_name="implementer",
bottle_names=["claude"],
backend_name="docker",
agent_git_user="didericis-claude",
pr_number=42,
status=STATUS_FROZEN,
last_checkin_at="2026-06-29T12:04:12-04:00",
)
return replace(base, **over)
class ForgeStateTest(unittest.TestCase):
+23 -10
View File
@@ -7,16 +7,29 @@ import unittest
from bot_bottle.contrib.gitea.provenance import build_provenance_footer
def _footer(slug: str = "implementer-abc12", **over) -> str:
base = {
"agent_name": "implementer",
"bottle_names": ("claude",),
"started_at": "2026-06-29T12:00:00-04:00",
"finished_at": "2026-06-29T12:04:12-04:00",
"exit_code": 0,
}
base.update(over)
return build_provenance_footer(slug, **base)
def _footer(
slug: str = "implementer-abc12",
*,
agent_name: str = "implementer",
bottle_names: tuple[str, ...] = ("claude",),
started_at: str = "2026-06-29T12:00:00-04:00",
finished_at: str = "2026-06-29T12:04:12-04:00",
exit_code: int = 0,
watchdog_fired: bool = False,
gitleaks_clean: bool | None = None,
egress_routes: list[str] | None = None,
) -> str:
return build_provenance_footer(
slug,
agent_name=agent_name,
bottle_names=bottle_names,
started_at=started_at,
finished_at=finished_at,
exit_code=exit_code,
watchdog_fired=watchdog_fired,
gitleaks_clean=gitleaks_clean,
egress_routes=egress_routes,
)
class ProvenanceTest(unittest.TestCase):