feat(manifest): trusted agent forge identity and guidance
Implements PRD prd-new-trusted-agent-forge-identity. Moves author identity and named forge configurations onto the trusted, host-only agent definition, and lets a bottle repository optionally associate a git-gate repo with one of the agent's forge aliases. - Agent-owned identity: new `author` (name/email) and `forge-accounts` (alias -> canonical Gitea /api/v1 origin + host `token_secret` ref) on the agent manifest. `author` populates the bottle's git user.name/user.email. - Remove `git-gate.user` from both agents and bottles; fail with a migration pointer to `author`. `git-gate` is no longer accepted on an agent. - Bottle git-gate repos gain optional `forge: <alias>`, resolved against the selected agent's forge-accounts at composition (fail-closed on unknown). - Fail-closed Gitea API URL validation (https, no userinfo/query/fragment, /api/v1 base, host lowercased, trailing slash normalized, host dedup). - Proxy-held credential: synthesize one inspected, token-authenticated egress route per referenced forge alias, scoped to the origin + API prefix. The token is resolved from the host env at launch into the egress proxy only — never the bottle env, prompt, gitconfig, or workspace. - Generated, non-secret forge workflow guidance appended to the agent prompt for associated repos (API base, branch-backed PR flow, AGit-ref prohibition, mutation verification); omitted when no repo declares a forge. - Agents become home-only: cwd `.bot-bottle/agents` no longer contributes, overrides, or is selectable; warned-and-ignored like cwd bottles. - Docs: README examples, PRD 0011 supersession note, manifest schema docstring. - Tests: new test_forge_identity suite; legacy git-gate.user/cwd tests updated to the agent-owned identity model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -212,13 +212,21 @@ class TestAgentValidation(unittest.TestCase):
|
||||
with self.assertRaises(ManifestError):
|
||||
ManifestAgent.from_dict("a", {"prompt": 5}, set())
|
||||
|
||||
def test_git_gate_repos_rejected_at_agent_level(self) -> None:
|
||||
def test_git_gate_rejected_at_agent_level(self) -> None:
|
||||
# `git-gate` (user or repos) is no longer accepted on an agent;
|
||||
# identity moved to `author`, repos stays bottle-only.
|
||||
with self.assertRaises(ManifestError):
|
||||
ManifestAgent.from_dict("a", {"git-gate": {"repos": {}}}, set())
|
||||
with self.assertRaises(ManifestError):
|
||||
ManifestAgent.from_dict(
|
||||
"a", {"git-gate": {"user": {"name": "x"}}}, set()
|
||||
)
|
||||
|
||||
def test_git_gate_empty_is_allowed(self) -> None:
|
||||
agent = ManifestAgent.from_dict("a", {"git-gate": {}}, set())
|
||||
self.assertTrue(agent.git_user.is_empty())
|
||||
def test_bottle_empty_git_gate_is_allowed(self) -> None:
|
||||
# An empty `git-gate: {}` on a bottle is still allowed (only the
|
||||
# optional `repos` subkey exists now); it contributes no git repos.
|
||||
bottle = ManifestBottle.from_dict("b", {"git-gate": {}})
|
||||
self.assertEqual((), bottle.git)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -228,9 +236,12 @@ class TestAgentValidation(unittest.TestCase):
|
||||
|
||||
class TestEagerIndexLookups(unittest.TestCase):
|
||||
def _idx(self) -> ManifestIndex:
|
||||
# Identity lives on the agent's `author` block now; at composition
|
||||
# it populates the effective bottle's git_user.
|
||||
return _idx({
|
||||
"bottles": {"b": {"git-gate": {"user": {"name": "Bot", "email": "b@x"}}}},
|
||||
"agents": {"a": {"bottle": "b"}},
|
||||
"bottles": {"b": {}},
|
||||
"agents": {"a": {"bottle": "b",
|
||||
"author": {"name": "Bot", "email": "b@x"}}},
|
||||
})
|
||||
|
||||
def test_unknown_bottle_section_is_empty(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user