Implement trusted agent forge identity and guidance #520

Merged
didericis merged 2 commits from feat/trusted-agent-forge-identity into prd-forge-subroles 2026-07-26 22:04:04 -04:00
Collaborator

Implements PRD prd-new-trusted-agent-forge-identity (added in #480). Stacked on prd-forge-subroles — review/merge #480 first.

Moves author identity and named forge configurations onto the host-only trusted agent definition, and lets a bottle repo optionally associate a git-gate repo with one of the agent's forge aliases — giving the agent proxy-authenticated forge API access (token never enters the bottle) plus generated workflow guidance.

What's implemented (per PRD implementation chunks)

  • Home-only definitions. $CWD/.bot-bottle/agents no longer contributes, overrides, or is selectable — warned-and-ignored like cwd bottles. Enumeration, lazy load, require_agent, defaults, and the start selector are all home-only.
  • Manifest schema. New agent-only author (name/email) and forge-accounts (alias → canonical Gitea /api/v1 origin + host token_secret ref). git-gate.user removed from agents and bottles (fail-closed with a migration pointer to author); git-gate rejected entirely on agents. New optional bottle-only git-gate.repos.<name>.forge, resolved against the selected agent's forge-accounts at composition (fail-closed on unknown alias). Fail-closed Gitea API URL validation (https only; no userinfo/query/fragment; /api/v1 base; host lowercased; trailing slash normalized; origin dedup).
  • Proxy provisioning. One inspected, token-authenticated egress route synthesized 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.
  • Prompt generation. Non-secret, typed forge workflow guidance appended to the agent prompt for associated repos (API base URL, branch-backed PR flow, AGit-ref prohibition, mutation verification). Omitted when no selected repo declares a forge; contains neither the token value nor its token_secret name.
  • Docs & migration. README examples, PRD 0011 supersession note, manifest schema docstring, example agent, PRD status → Accepted.

Testing

  • New tests/unit/test_forge_identity.py (34 tests): author validation, Gitea URL canonicalization/fail-closed, repo→forge association, synthesized route shape + token-slot resolution + secret-absence in rendered routes, guidance rendering, and prompt append.
  • Legacy git-gate.user / cwd-agent tests updated to the agent-owned identity model.
  • Full unit suite green (one unrelated pre-existing orchestrator flake passes in isolation).

🤖 Generated with Claude Code

Implements PRD prd-new-trusted-agent-forge-identity (added in #480). **Stacked on `prd-forge-subroles`** — review/merge #480 first. Moves author identity and named forge configurations onto the host-only trusted agent definition, and lets a bottle repo optionally associate a git-gate repo with one of the agent's forge aliases — giving the agent proxy-authenticated forge API access (token never enters the bottle) plus generated workflow guidance. ## What's implemented (per PRD implementation chunks) - **Home-only definitions.** `$CWD/.bot-bottle/agents` no longer contributes, overrides, or is selectable — warned-and-ignored like cwd bottles. Enumeration, lazy load, `require_agent`, defaults, and the start selector are all home-only. - **Manifest schema.** New agent-only `author` (name/email) and `forge-accounts` (alias → canonical Gitea `/api/v1` origin + host `token_secret` ref). `git-gate.user` removed from agents *and* bottles (fail-closed with a migration pointer to `author`); `git-gate` rejected entirely on agents. New optional bottle-only `git-gate.repos.<name>.forge`, resolved against the selected agent's forge-accounts at composition (fail-closed on unknown alias). Fail-closed Gitea API URL validation (https only; no userinfo/query/fragment; `/api/v1` base; host lowercased; trailing slash normalized; origin dedup). - **Proxy provisioning.** One inspected, `token`-authenticated egress route synthesized 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. - **Prompt generation.** Non-secret, typed forge workflow guidance appended to the agent prompt for associated repos (API base URL, branch-backed PR flow, AGit-ref prohibition, mutation verification). Omitted when no selected repo declares a forge; contains neither the token value nor its `token_secret` name. - **Docs & migration.** README examples, PRD 0011 supersession note, manifest schema docstring, example agent, PRD status → Accepted. ## Testing - New `tests/unit/test_forge_identity.py` (34 tests): author validation, Gitea URL canonicalization/fail-closed, repo→forge association, synthesized route shape + token-slot resolution + secret-absence in rendered routes, guidance rendering, and prompt append. - Legacy `git-gate.user` / cwd-agent tests updated to the agent-owned identity model. - Full unit suite green (one unrelated pre-existing orchestrator flake passes in isolation). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
didericis force-pushed feat/trusted-agent-forge-identity from 46fd97356a to 2fafeef61c 2026-07-26 20:40:38 -04:00 Compare
didericis added the Kind/Feature label 2026-07-26 20:45:42 -04:00
didericis added 1 commit 2026-07-26 20:55:36 -04:00
feat(manifest): trusted agent forge identity and guidance
tracker-policy-pr / check-pr (pull_request) Successful in 6s
test / image-input-builds (pull_request) Successful in 42s
lint / lint (push) Successful in 1m1s
test / integration-docker (pull_request) Successful in 1m9s
test / unit (pull_request) Successful in 2m33s
test / coverage (pull_request) Successful in 17s
ba19012179
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>
didericis force-pushed feat/trusted-agent-forge-identity from 2fafeef61c to ba19012179 2026-07-26 20:55:36 -04:00 Compare
didericis-codex requested changes 2026-07-26 21:06:12 -04:00
didericis-codex left a comment
Collaborator

Requesting changes for one credential-routing issue:

[P1] Reject conflicting forge credentials on the same routed hostbot_bottle/egress/service.py:135-140

egress_forge_routes() deduplicates referenced accounts only by hostname and silently discards every account after the first. The manifest permits two aliases for the same host (and even the same origin) with different token_secret values. If repos reference both aliases, composition succeeds and guidance advertises both, but the generated proxy plan contains only the first credential; all API calls to that host are therefore authenticated as the first account. This breaks the per-agent/per-alias identity guarantee and can perform mutations as the wrong forge user. I reproduced it with two aliases on https://same.example/api/v1: associations contained FIRST_TOKEN and SECOND_TOKEN, while the resolved token map contained only FIRST_TOKEN. Please fail closed when referenced aliases sharing a routable host disagree on origin/auth/token, or change the routing model so the intended credential can be selected unambiguously. Add a regression test with two aliases/tokens on the same host (the current dedup test covers only two repos sharing one alias).

The focused manifest/forge/git-gate suite passes: 186 tests.

Requesting changes for one credential-routing issue: **[P1] Reject conflicting forge credentials on the same routed host** — `bot_bottle/egress/service.py:135-140` `egress_forge_routes()` deduplicates referenced accounts only by hostname and silently discards every account after the first. The manifest permits two aliases for the same host (and even the same origin) with different `token_secret` values. If repos reference both aliases, composition succeeds and guidance advertises both, but the generated proxy plan contains only the first credential; all API calls to that host are therefore authenticated as the first account. This breaks the per-agent/per-alias identity guarantee and can perform mutations as the wrong forge user. I reproduced it with two aliases on `https://same.example/api/v1`: associations contained `FIRST_TOKEN` and `SECOND_TOKEN`, while the resolved token map contained only `FIRST_TOKEN`. Please fail closed when referenced aliases sharing a routable host disagree on origin/auth/token, or change the routing model so the intended credential can be selected unambiguously. Add a regression test with two aliases/tokens on the same host (the current dedup test covers only two repos sharing one alias). The focused manifest/forge/git-gate suite passes: 186 tests.
didericis added 1 commit 2026-07-26 21:14:53 -04:00
fix(forge): fail closed on aliases colliding on a routable host
prd-number-check / require-numbered-prds (pull_request) Failing after 7s
tracker-policy-pr / check-pr (pull_request) Successful in 16s
test / integration-docker (pull_request) Successful in 49s
lint / lint (push) Successful in 55s
test / image-input-builds (pull_request) Successful in 55s
test / unit (pull_request) Successful in 2m19s
test / coverage (pull_request) Successful in 18s
e53e078451
Codex review (PR #520, P1): egress_forge_routes deduplicated referenced
forge accounts by hostname and silently dropped every account after the
first. Two aliases with different token_secret on the same host would let
all API calls to that host authenticate as whichever alias won the dedup —
acting as the wrong forge account and breaking the per-alias identity
guarantee.

Fail closed at composition (before the bottle is created): when two
referenced aliases resolve to the same host but disagree on
origin/auth/token_secret, resolve_forge_associations raises ManifestError.
Identical url/auth under two aliases still dedups to one route. The proxy
routes by host, so an ambiguous credential cannot be selected safely.

Regression tests cover both the conflicting-tokens (raise) and
identical-config (single route) cases; forge.py stays at 100% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
Collaborator

Thanks — fixed in e53e078.

[P1] Conflicting forge credentials on the same routed host — you're right, egress_forge_routes() deduped by hostname and silently kept only the first account. Rather than pick a credential at route-synthesis time, I now fail closed at composition (resolve_forge_associations, before the bottle is created): when two referenced aliases resolve to the same host but disagree on origin/auth/token_secret, it raises ManifestError ("... both resolve to host '' ... the intended credential would be ambiguous"). Identical url/auth under two aliases still collapses to a single route. The host-dedup in egress_forge_routes now only ever merges genuinely-identical credentials, and I noted that guarantee in its docstring.

Added regression tests for both branches: test_conflicting_aliases_same_host_fail_closed (two aliases / two tokens on https://same.example/api/v1 → raises) and test_matching_aliases_same_host_ok (identical config → one route, correct token). forge.py stays at 100% line+branch coverage; pyright strict clean; focused manifest/forge/egress suite green.

Thanks — fixed in e53e078. **[P1] Conflicting forge credentials on the same routed host** — you're right, `egress_forge_routes()` deduped by hostname and silently kept only the first account. Rather than pick a credential at route-synthesis time, I now **fail closed at composition** (`resolve_forge_associations`, before the bottle is created): when two referenced aliases resolve to the same host but disagree on `origin`/`auth`/`token_secret`, it raises `ManifestError` ("... both resolve to host '<host>' ... the intended credential would be ambiguous"). Identical `url`/`auth` under two aliases still collapses to a single route. The host-dedup in `egress_forge_routes` now only ever merges genuinely-identical credentials, and I noted that guarantee in its docstring. Added regression tests for both branches: `test_conflicting_aliases_same_host_fail_closed` (two aliases / two tokens on `https://same.example/api/v1` → raises) and `test_matching_aliases_same_host_ok` (identical config → one route, correct token). `forge.py` stays at 100% line+branch coverage; pyright strict clean; focused manifest/forge/egress suite green.
didericis-codex approved these changes 2026-07-26 21:17:36 -04:00
didericis-codex left a comment
Collaborator

Re-reviewed at e53e078. The prior credential-routing issue is resolved: referenced aliases sharing a proxy-routed host now fail closed when origin/auth/token_secret differ, while genuinely identical credentials remain safely deduplicated. The added regression tests cover both cases.

Focused manifest/forge/git-gate suite passes: 188 tests. No remaining findings.

Re-reviewed at `e53e078`. The prior credential-routing issue is resolved: referenced aliases sharing a proxy-routed host now fail closed when origin/auth/token_secret differ, while genuinely identical credentials remain safely deduplicated. The added regression tests cover both cases. Focused manifest/forge/git-gate suite passes: 188 tests. No remaining findings.
didericis-codex approved these changes 2026-07-26 21:18:03 -04:00
didericis-codex reviewed 2026-07-26 21:18:13 -04:00
didericis-codex left a comment
Collaborator

Re-reviewed at e53e078. The prior credential-routing issue is resolved: referenced aliases sharing a proxy-routed host now fail closed when origin/auth/token_secret differ, while genuinely identical credentials remain safely deduplicated. The added regression tests cover both cases.

Focused manifest/forge/git-gate suite passes: 188 tests. No remaining findings.

Re-reviewed at `e53e078`. The prior credential-routing issue is resolved: referenced aliases sharing a proxy-routed host now fail closed when origin/auth/token_secret differ, while genuinely identical credentials remain safely deduplicated. The added regression tests cover both cases. Focused manifest/forge/git-gate suite passes: 188 tests. No remaining findings.
didericis merged commit e53e078451 into prd-forge-subroles 2026-07-26 22:04:04 -04:00
didericis deleted branch feat/trusted-agent-forge-identity 2026-07-26 22:04:05 -04:00
Sign in to join this conversation.