fix(#1198): create internal/knowledge as public, patch existing private repos

This commit is contained in:
damocles 2026-06-03 21:29:37 +02:00 committed by mara
commit cd1f77b640

View file

@ -55,10 +55,10 @@ const SHARED_ORG: &str = "internal";
/// The shared docs repo inside `SHARED_ORG`. Cloneable by every agent
/// at `{FORGE_HTTP}/internal/docs.git`.
const SHARED_DOCS_REPO: &str = "docs";
/// The hive-wide knowledge repo inside `SHARED_ORG`. Bind-mounted
/// read-only into every container at `/knowledge`. Agents
/// contribute by forking + opening PRs; direct writes to main are
/// not granted. See `hive-c0re/src/knowledge.rs`.
/// The hive-wide knowledge repo inside `SHARED_ORG`. Public — agents
/// can fork it and open PRs without explicit collaborator grants.
/// Bind-mounted read-only into every container at `/knowledge`.
/// See `hive-c0re/src/knowledge.rs`.
const KNOWLEDGE_REPO: &str = crate::knowledge::REPO;
/// Forgejo orgs hive-c0re ensures on startup. The meta repo lives at
/// `core/meta` (the `core` user's own namespace — no org needed).
@ -470,6 +470,37 @@ fn repo_body(name: &str) -> String {
format!(r#"{{"name":"{name}","auto_init":false,"private":true,"default_branch":"main"}}"#)
}
/// JSON body for a public, empty repo defaulting to `main`.
fn repo_body_public(name: &str) -> String {
format!(r#"{{"name":"{name}","auto_init":false,"private":false,"default_branch":"main"}}"#)
}
/// Set an existing repo to public visibility. No-op if the repo is
/// already public. Used for `internal/knowledge` which may have been
/// created as private on an older deployment.
async fn set_repo_public(owner: &str, repo: &str, token: &str) -> Result<()> {
let url = format!("{FORGE_HTTP}/api/v1/repos/{owner}/{repo}");
let status = forge_http(reqwest::Method::PATCH, &url, token, r#"{"private":false}"#).await?;
match status.as_u16() {
200 => {
tracing::debug!(%owner, %repo, "forge: repo set to public");
Ok(())
}
other => anyhow::bail!("PATCH {owner}/{repo} (set public) returned HTTP {other}"),
}
}
/// Create `name` inside org `org` as a public repo. Idempotent.
async fn ensure_org_repo_public(org: &str, name: &str, token: &str) -> Result<()> {
create_repo(
&format!("{FORGE_HTTP}/api/v1/orgs/{org}/repos"),
&repo_body_public(name),
token,
&format!("{org}/{name}"),
)
.await
}
/// POST a repo-creation request to `url` and fold "already exists"
/// (HTTP 409 / 422) into success. `label` is `<owner>/<name>` — purely
/// for log + error context.
@ -611,32 +642,17 @@ pub async fn shared_docs_access(name: &str, core_token: &str) -> Result<()> {
}
}
/// Ensure the `internal/knowledge` repo exists. Called once at
/// startup after `ensure_org(SHARED_ORG)`. Idempotent — `ensure_org_repo`
/// treats 409 as success.
/// Ensure the `internal/knowledge` repo exists and is public.
/// Called once at startup after `ensure_org(SHARED_ORG)`. Idempotent.
///
/// The repo is created as public so any agent with a forge account can
/// fork it and open PRs to contribute. Existing deployments that ended
/// up with a private repo are patched to public on the next hive-c0re
/// startup via `set_repo_public`.
pub async fn ensure_knowledge_repo(core_token: &str) -> Result<()> {
ensure_org_repo(SHARED_ORG, KNOWLEDGE_REPO, core_token).await
}
/// Grant agent `name` read-only collaborator access to
/// `internal/knowledge`. Agents read documents from the bind-mounted
/// clone and contribute by forking + opening PRs — no write to main.
/// Idempotent: HTTP 204 (already a collaborator) is treated as success.
pub async fn knowledge_access(name: &str, core_token: &str) -> Result<()> {
let url = format!(
"{FORGE_HTTP}/api/v1/repos/{SHARED_ORG}/{KNOWLEDGE_REPO}/collaborators/{name}"
);
let body = r#"{"permission":"read"}"#;
let status = forge_http(reqwest::Method::PUT, &url, core_token, body).await?;
match status.as_u16() {
204 => {
tracing::info!(%name, "forge: granted knowledge read access");
Ok(())
}
other => anyhow::bail!(
"PUT {SHARED_ORG}/{KNOWLEDGE_REPO}/collaborators/{name} returned HTTP {other}"
),
}
ensure_org_repo_public(SHARED_ORG, KNOWLEDGE_REPO, core_token).await?;
// Ensure public even if the repo already existed as private (older deployment).
set_repo_public(SHARED_ORG, KNOWLEDGE_REPO, core_token).await
}
/// Grant agent `name` read-only collaborator access to `core/meta` on
@ -818,13 +834,7 @@ pub async fn sync_agent(name: &str, core_token: Option<&str>) {
{
tracing::warn!(%name, error = ?e, "forge: shared_docs_access failed");
}
// Grant read-only access to internal/knowledge so the agent can
// read the bind-mounted knowledge repo and fork it to open PRs.
if let Some(token) = core_token
&& let Err(e) = knowledge_access(name, token).await
{
tracing::warn!(%name, error = ?e, "forge: knowledge_access failed");
}
// internal/knowledge is public — no per-agent collaborator grant needed.
}
/// Sweep every existing container (manager + sub-agents) and ensure