agent surface: create_repo through hive-c0re (#1787)

Closes the #1787 loop — the sanctioned create path now that agents
can't create repos directly. Adds:

- wire: Request::CreateRepo{repo} + Response::RepoCreated{full_name,
  clone_url} (hive-sh4re).
- agent_server: dispatch_shared arm + handle_create_repo — validates the
  repo name, then forge::create_agent_repo (org-owned repo, agent=write
  collaborator, operator-team branch protection). Returns the full name
  + clone url so the agent can git clone immediately.
- MCP: create_repo tool + CreateRepoArgs in the harness.
- a new opt-in ToolGroup::Forge (=[create_repo]) so the operator
  controls which agents can spin up repos (least privilege).

Workspace clippy -D warnings, cargo test, nix fmt all green.
This commit is contained in:
atlas 2026-06-19 12:57:01 +02:00 committed by mara
commit f1d54ce12c
4 changed files with 115 additions and 3 deletions

View file

@ -316,7 +316,7 @@ async fn ensure_repo_creation_disabled(name: &str) {
tracing::warn!(%name, %status, "forge: PATCH max_repo_creation returned non-success");
}
Err(e) => {
tracing::warn!(%name, error = %e, "forge: PATCH max_repo_creation transport error")
tracing::warn!(%name, error = %e, "forge: PATCH max_repo_creation transport error");
}
}
}
@ -984,12 +984,12 @@ async fn apply_operator_branch_protection(repo: &str, token: &str) -> Result<()>
/// bypass branch protection), and the default branch gets the operator
/// merge gate. This is the sanctioned create path now that agents can't
/// create repos directly (`max_repo_creation = 0`). Idempotent.
pub async fn create_agent_repo(agent: &str, repo: &str, core_token: &str) -> Result<()> {
pub async fn create_agent_repo(agent: &str, repo: &str, core_token: &str) -> Result<String> {
ensure_org_repo(AGENTS_ORG, repo, core_token).await?;
add_collaborator(AGENTS_ORG, repo, agent, "write", core_token).await?;
apply_operator_branch_protection(repo, core_token).await?;
tracing::info!(%agent, %repo, "forge: created agent repo in {AGENTS_ORG} with operator merge gate");
Ok(())
Ok(format!("{AGENTS_ORG}/{repo}"))
}
/// Per-agent forge sync: ensure the agent has a forgejo user + token,