orch

package
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

Documentation

Overview

Package orch is the PR-AF review orchestrator — the Go port of src/pr_af/orchestrator.py. It coordinates the multi-phase review pipeline: intake → anatomy → meta-selectors → review+layer (streaming) → coverage ‖ consistency → synthesis → merge-gate → output. It owns the HITL revision loop, the streaming producer/consumer channel, the order-preserving fan-outs, the (inert) wall-clock budget gate, and the byte-exact Markdown output builders.

Concurrency parity: Python's asyncio is cooperative single-threaded, so its shared orchestrator state needs no locking. Go runs the fan-outs on real goroutines, so every field mutated from a parallel closure (agentInvocations, totalCostUSD, costBreakdown, the adversary/cross-ref counters, budgetExhausted) is guarded by o.mu.

Index

Constants

This section is empty.

Variables

View Source
var ErrBadInput = errors.New("bad input")

ErrBadInput is the sentinel wrapping every ValueError-class failure Python's review() maps to HTTP 400: the "One of pr_url, diff_text, or repo_path is required" guard and the _compute_repo_diff failure. The node layer (T4.2) maps errors.Is(err, ErrBadInput) to 400 and everything else to 500 with the "review execution failed: " prefix.

Functions

func ExtractPRNumber

func ExtractPRNumber(prURL string) (int, bool)

ExtractPRNumber ports app.py::_extract_pr_number: the integer following "/pull/" in a github.com URL, or (0, false) when absent/unparsable.

func IsBudgetExhausted

func IsBudgetExhausted(err error) bool

IsBudgetExhausted reports whether err is the pipeline stopping at its cost or wall-clock cap before a phase could start. The node maps it to a 500 like any other failure; a caller that words endings by cause (the delegate) reads it as "a ceiling was reached", not "the program broke".

func PartialReviewPayload

func PartialReviewPayload(review schemas.GitHubReview, body string) schemas.GitHubReview

PartialReviewPayload keeps findings available without a clean merge claim. Its body is built from completed work by the host that knows what stopped.

func PostReview

func PostReview(ctx context.Context, gh github.Client, pr schemas.GitHubPRData, review schemas.GitHubReview) error

PostReview posts review on the pull request pr names, against the commit that was reviewed (pr.HeadSHA), downgrading a 422 "own pull request" REQUEST_CHANGES to a COMMENT and retrying once (orchestrator.py's httpx handling). internal/praf's post command calls it with a saved review, on the person's yes. It answers the event that was posted.

func PostReviewEvent

func PostReviewEvent(ctx context.Context, gh github.Client, pr schemas.GitHubPRData, review schemas.GitHubReview) (string, error)

PostReviewEvent is PostReview, answering the event GitHub took: the review's own, or COMMENT when GitHub refused a request for changes on the author's own pull request.

func ResolveRepo

func ResolveRepo(ctx context.Context, access Access, repoPath, prURL string) (string, error)

ResolveRepo ports app.py::_resolve_repo. It resolves repoPath / prURL to a local directory: an existing dir is returned as-is (resolved absolute), and an http(s)/git@ URL is (shallow) cloned into access.Workdir, with the PR branch checked out when a PR number is known. Anything else is refused: pr-af fell back to $PR_AF_REPO_PATH or its working directory.

The workspace is keyed by repo AND PR number when one is known (<repoName>-pr<N>): the shared per-repo dir re-pointed its pr-review branch on every review, so two concurrent reviews of different PRs of the same repo silently reviewed the wrong checkout. Plain <repoName> is kept when no PR number is known (repo_path / diff_text flows), preserving the old layout.

Empty strings stand in for Python's None. Errors mirror Python's ValueError ("git clone failed: …", plus the checkout strings via checkoutPRBranch).

Types

type Access

type Access struct {
	Workdir string
	Token   string
}

Access is what a review needs to reach a repository: the folder its checkouts go in, and the GitHub token for a private one (empty for none).

type App

type App = appx.App

App is the agent capability surface the orchestrator depends on (appx.App), the single seam every sub-package's narrower interface is fed from: harnessx.HarnessCaller (Harness), reasoners.AICaller / gates.AICaller (AI), and the progress notes (Note). pr-af's served node also paused here for a person's approval before posting; inside codeaf a review never posts from the pipeline (internal/praf's post command does, on the person's yes), so that verb is gone.

type Deps

type Deps struct {
	App    App
	GH     github.Client
	NodeID string

	// Local, when non-nil, routes every phase invocation through
	// LocalCaller.CallLocal so the control plane records one child execution
	// per phase (the pipeline DAG). nil falls back to plain in-process
	// function calls — the pre-DAG behavior unit tests and stub harnesses
	// rely on. Production (node/register.go) always wires the live agent.
	Local LocalCaller

	// SpentUSD, when non-nil, reports what the run has really spent so far,
	// metered by the caller from the harness and AI results. The reasoner
	// returns never carry cost_usd, so without it the cost cap is inert (the
	// node's long-standing behavior, kept when this is nil); with it the cost
	// cap trips on real spend and the review degrades the way the wall-clock
	// cap already makes it, keeping what it has. The one-shot delegate wires it.
	SpentUSD func() float64
}

Deps carries the injected capabilities. Divergence from design §C.6: the hax client is NOT a Deps field — Python builds it inside run() via build_hax_client_from_env (gated on pr_url && !dry_run), so the orchestrator mirrors that through the buildHaxClient seam. GH is the GitHub client interface (Python constructs GitHubClient() inline; Go injects it so tests can stub fetch/post).

type LocalCaller

type LocalCaller interface {
	CallLocal(ctx context.Context, reasonerName string, input map[string]any) (any, error)
}

LocalCaller is the SDK surface for same-process reasoner invocation with workflow tracking (Agent.CallLocal): each call builds a child execution context from ctx and emits running/succeeded/failed events to the control plane, so every pipeline phase renders as its own node in the run's DAG — the same orchestration graph the Python port produces via its tracked router-reasoner calls. Python's @router.reasoner() wrapper routes direct in-process calls through workflow instrumentation; CallLocal is the Go SDK's equivalent, and routing the phase seams through it is what keeps the Go node from collapsing into a single opaque `review` execution.

type Orchestrator

type Orchestrator struct {
	// contains filtered or unexported fields
}

Orchestrator holds one review's state and seams. New wires the default (production) seams; tests override individual fields to stub phases.

func New

New constructs an Orchestrator with production seams. startedAt is set now so the wall-clock budget gate measures from construction (parity with Python's time.monotonic() in __init__).

func (*Orchestrator) PRData

func (o *Orchestrator) PRData() *schemas.GitHubPRData

PRData is the pull request the review fetched, nil before intake or for a review of a diff with no pull request. internal/praf keeps it beside the saved review so a later post lands on the commit that was reviewed.

func (*Orchestrator) Run

Run executes the pipeline once (orchestrator.py run(), without its HITL revision loop). ErrBadInput-wrapped errors mark a request the review could not take; anything else is an internal failure.

THE REVIEW POSTS ONLY WHEN IT IS NOT A DRY RUN AND NAMES A PULL REQUEST. pr-af's served node put a person's approval between the review and the post; inside codeaf every review runs dry and the post is its own step on the person's yes (internal/praf's post command), so the loop that waited for that approval is gone.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL