Skip to content

Agent PR Submission Pipeline

FractalOps는 PR 제출을 템플릿 범위의 권능(capability) 으로 다루지 — 일반 에이전트 특권으로 다루지 않습니다. 정책은 access/application/studio/pr_submission_policy.py에 코드로 고정되어 있고, 전송(transport)은 CodexGate GitHub App을 통해 봇으로 발행합니다.

상수는 pr_submission_policy.py에 그대로 있습니다:

  • allowed_write_policy: report-only (REPORT_ONLY_WRITE_POLICY) — 에이전트는 issue와 증거를 보고할 수 있지만 branch나 PR을 claim할 수 없습니다. normalize_write_policy는 빈 값, read-only, report-only를 모두 여기로 모읍니다.
  • allowed_write_policy: agent-branch-pr-only (AGENT_BRANCH_PR_ONLY_POLICY) — 에이전트는 배정된 branch prefix와 PR 파이프라인을 통해서만 제출할 수 있습니다. github-pr-only / pr-only도 여기로 정규화됩니다.
  • pr_submission_policy: selective-agent-pr (PR_SUBMISSION_POLICY_SELECTIVE) — 에이전트 id가 pr_submitter_agent_ids에 들어 있지 않으면 파이프라인을 fail-closed로 둡니다.
  • pr_submission_pipeline은 durable transport의 이름입니다. Studio는 codexgate-github-app-pr(PR_SUBMISSION_PIPELINE 상수)을 씁니다. CodexGate 리포지토리 정책은 github-app-draft-pr을 씁니다.

판정 함수는 pr_submission_allowed(...)입니다:

def pr_submission_allowed(
*,
agent_id: object,
allowed_write_policy: object,
pr_submitter_agent_ids: object,
explicit_allowed: object = None,
) -> bool:
if explicit_allowed is not None:
return bool(explicit_allowed)
normalized_agent = text(agent_id).strip()
return (
normalize_write_policy(allowed_write_policy) == AGENT_BRANCH_PR_ONLY_POLICY
and normalized_agent in set(normalized_agent_ids(pr_submitter_agent_ids))
)

즉 PR 제출이 허용되려면 (1) write policy가 agent-branch-pr-only이고 (2) 에이전트 id가 submitter 목록에 있어야 합니다(또는 세션 메타데이터의 pr_submission_allowed로 명시 허용). 이 게이트는 assert_report_pr_submission_allowed에서 강제됩니다 — 보고된 attributes에 branch_name 또는 pr_url(즉 PR delivery claim)이 있는데 허용되지 않으면 agent_pr_submission_not_allowed를 raise합니다.

flowchart TD
  report["에이전트 report\n(branch_name? / pr_url?)"]
  claim{"has_pr_delivery_claim?\n(branch_name 또는 pr_url)"}
  allow{"pr_submission_allowed?\n(agent-branch-pr-only\n+ submitter 목록)"}
  prefix{"branch가 허용 prefix\n또는 agent/<lane>/ 와 일치?"}
  ok["report 수락\n-> codexgate-github-app-pr 전송"]
  rejclaim["claim 없음:\n그냥 보고로 수락"]
  rejallow["reject:\nagent_pr_submission_not_allowed"]
  rejprefix["reject:\nagent_pr_branch_prefix_violation"]

  report --> claim
  claim -->|no| rejclaim
  claim -->|yes| allow
  allow -->|no| rejallow
  allow -->|yes| prefix
  prefix -->|no| rejprefix
  prefix -->|yes| ok
  • Studio 템플릿은 submitter 목록과 에이전트별 pr_submission_allowed 플래그를 발행합니다.
  • Studio 세션 메타데이터는 활성 에이전트에 대해 그 정책을 미러링합니다.
  • HUD report 인제스션은 세션이 PR 제출을 부여하지 않으면 branch_name 또는 pr_url을 거부합니다.
  • CodexGate 발행은 선택된 에이전트가 리포지토리 정책에 의해 허용되지 않으면 publish=True를 거부합니다.
  • branch prefix 검사(_branch_matches_allowed_submission_target)는 branch가 allowed_write_prefix로 시작하거나, 세션의 정식 branch 이름과 같거나, agent/<lane>/...(lane = agent_id 또는 role_key) 형태일 때만 통과시킵니다. 어긋나면 agent_pr_branch_prefix_violation.

AgentSquad는 기본적으로 report-only입니다. forge가 유일한 기본 Studio PR submitter입니다. CodexGate는 crux-portalsable-runtime을 허용하고, mira-evidence는 report-only로 남습니다.

전송은 raw org PAT가 아니라 CodexGate GitHub App(fractalops-codexgate[bot])으로 발행합니다. AgentSquad의 PR과 issue는 App 설치(installation)가 작성합니다:

  • App id 3249317, installation 120794317, yamonco org(all-repo).
  • CodexGateGitHubAppService._installation_token이 git 인증과 GitHub REST/GraphQL 호출용으로 실제 ghs_… installation 토큰을 mint합니다. repository_materialization이 이미 git 인증을 이 경로로 라우팅하고 있어서, App이 온라인이 됐을 때 app 코드는 바뀌지 않았습니다.
  • codexgate_github_bot_actor는 실제 App slug와 맞추기 위해 fractalops-codexgate[bot]입니다.

App은 이전에 두 가지 이유로 무력(inert)했고, 지금은 둘 다 고쳐졌습니다: 크리덴셜 (app_id / installation_id / private_key)이 프로비저닝된 적이 없었고, 남아 있던 FRACTALOPS_GITHUB_APP_TOKEN override env(org PAT)가 _installation_token 안에서 App 크리덴셜을 이겼습니다(메서드가 override를 먼저 반환). override env는 모든 곳에서 제거됐고, App 크리덴셜은 k8s secret fractalops-codexgate-github-app에서 공급됩니다. API, worker, Studio worker가 envFrom으로 소비하며 provider workspace에는 private key를 전달하지 않습니다.

이 secret은 오늘날 수동으로 프로비저닝됩니다 — org-PAT fractalops-github-runtime secret과 같은 패턴입니다. 이를 OpenBao + ExternalSecret으로 옮기는 것은 pending follow-up입니다.

delivery reference(PR/issue)가 있다고 lane이 닫히는 것은 아닙니다. committer는 연결된 PR과 tester 증명을 검토한 뒤 merge lifecycle decision을 닫아야 합니다. 정식 런타임 blocker는 merge_lifecycle_decision_required이고, 닫는 방법은 둘 중 하나입니다: handoff_state=merge_requested + merge_gate_status=qa_passed, 또는 handoff_state=merged. committer_merge_state_required는 retired wire field일 뿐입니다. (AgentSquad Delivery Language 참고.)