feat(forge): forge library layer for native integration (PRD chunks 1-3, 5)
Implements the bot-bottle side of the forge-native PRD that is self-contained in this repo (the forge sidecar and orchestrate command belong to the separate bot-bottle-orchestrator, a PRD non-goal): - contrib/forge/base.py: Forge ABC + ScopedForge enforcing the read-anywhere / write-scoped model (writes rejected outside the assigned issue/PRs via ForgeScopeError). - contrib/gitea/client.py: GiteaClient (stdlib-only HTTP, mirrors the deploy-key provisioner) + GiteaForge. Token held by the caller (the sidecar), not injected by cred-proxy. - contrib/gitea/forge_state.py: ForgeState dataclass + atomic read/write/delete/all under ~/.bot-bottle/forge/<owner>/<repo>/. - contrib/gitea/provenance.py: build_provenance_footer — collapsed markdown audit footer; watchdog/gitleaks/egress rendering. - cli/resume.py: `resume --headless --prompt` reusing the shipped assume_yes + headless_prompt launch core (the new half of chunk 1). 47 new unit tests; pylint 9.98/10, pyright clean. Forge sidecar (chunk 4), orchestrate command (chunk 6), and forge_env plumbing are deferred: their only consumer is the separate orchestrator and they are untestable in isolation here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WL77TgFxKbs3cidGMG9dz7
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Unit: provenance footer (PRD forge-native-integration)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class ProvenanceTest(unittest.TestCase):
|
||||
def test_required_fields_present(self):
|
||||
out = _footer()
|
||||
for token in ("Run provenance", "`implementer`", "`claude`",
|
||||
"`implementer-abc12`", "| exit | 0 ✓ |"):
|
||||
self.assertIn(token, out)
|
||||
|
||||
def test_collapsed_details_block(self):
|
||||
out = _footer()
|
||||
self.assertTrue(out.startswith("<details>"))
|
||||
self.assertIn("</details>", out)
|
||||
|
||||
def test_duration_minutes_seconds(self):
|
||||
self.assertIn("| duration | 4m 12s |", _footer())
|
||||
|
||||
def test_duration_under_a_minute(self):
|
||||
out = _footer(finished_at="2026-06-29T12:00:30-04:00")
|
||||
self.assertIn("| duration | 30s |", out)
|
||||
|
||||
def test_duration_unknown_on_bad_timestamp(self):
|
||||
out = _footer(finished_at="not-a-time")
|
||||
self.assertIn("| duration | unknown |", out)
|
||||
|
||||
def test_nonzero_exit_marked(self):
|
||||
self.assertIn("| exit | 1 ✗ |", _footer(exit_code=1))
|
||||
|
||||
def test_watchdog_changes_done_signal_row(self):
|
||||
normal = _footer()
|
||||
self.assertIn("sidecar `signal_done`", normal)
|
||||
fired = _footer(watchdog_fired=True)
|
||||
self.assertIn("watchdog — agent did not signal", fired)
|
||||
self.assertNotIn("sidecar `signal_done`", fired)
|
||||
|
||||
def test_gitleaks_states(self):
|
||||
self.assertIn("not run", _footer())
|
||||
self.assertIn("✓ no secrets detected", _footer(gitleaks_clean=True))
|
||||
self.assertIn("✗ secrets detected", _footer(gitleaks_clean=False))
|
||||
|
||||
def test_egress_omitted_when_absent(self):
|
||||
self.assertNotIn("**Egress**", _footer())
|
||||
|
||||
def test_egress_rendered_when_present(self):
|
||||
out = _footer(egress_routes=[
|
||||
"`api.anthropic.com` — Bearer auth",
|
||||
"`pypi.org` — unauthenticated",
|
||||
])
|
||||
self.assertIn("**Egress** (deny-by-default; 2 routes allowed)", out)
|
||||
self.assertIn("- `api.anthropic.com` — Bearer auth", out)
|
||||
|
||||
def test_egress_singular_route(self):
|
||||
out = _footer(egress_routes=["`api.anthropic.com` — Bearer auth"])
|
||||
self.assertIn("1 route allowed", out)
|
||||
|
||||
def test_multiple_bottles_listed(self):
|
||||
out = _footer(bottle_names=("claude", "dev"))
|
||||
self.assertIn("`claude`, `dev`", out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user