Documentation
¶
Overview ¶
Package parallel ships Harbor's runtime parallel-call executor — the consumer of planner.CallParallel Decisions (RFC §6.2, Phase 47, D-056). The Planner emits CallParallel; this package dispatches the branches concurrently with three settled invariants:
**Atomic setup validation.** Every branch's args is validated via the resolved tools.ToolDescriptor.Validate BEFORE any branch dispatches. A single invalid-args branch fails the whole call with wrapped planner.ErrParallelBranchInvalidArgs — no branch has executed; no side-effect tool has fired; the planner's next step sees a clean failure observation.
**System cap on branch count.** Any CallParallel with `len(Branches) > planner.AbsoluteMaxParallel` (=50) fails the whole call with planner.ErrParallelCapExceeded — defence in depth against a runaway emission.
**Parallel-pause atomicity.** No branch starts side-effecting tools, or all reach checkpointed observation before pause commits (RFC §6.2). The unified pause/resume primitive lands at Phase 50; for Phase 47 a mid-execution pause request fails loud with planner.ErrParallelPauseUnsupported. Phase 50 upgrades this path to a checkpointed atomic pause.
Three join shapes are supported (D-056):
- planner.JoinAll: wait for every branch to terminate; return the result slice in branch-index order. Default.
- planner.JoinFirstSuccess: return the first successful branch's result; cancel the remainder mid-flight. Failures do NOT cancel until all remaining branches terminate.
- planner.JoinN: wait until N branches succeed, then cancel the remainder. JoinSpec.N carries the threshold; 0 < N ≤ len(Branches) is validated at setup time.
**Import-graph contract (§13).** This package lives in `internal/runtime/parallel`, OUTSIDE the planner subtree, so it MAY import `internal/planner`. The reverse (planner → runtime) is FORBIDDEN by the Phase 42 conformance lint; the executor is the one-way dispatch site that consumes the typed planner shape.
**Identity (§6 rule 9).** Every dispatch reads the run's identity quadruple from ctx; missing identity returns wrapped tools.ErrIdentityRequired. Branches inherit the parent ctx so identity propagates unchanged to every tool invocation.
**Concurrent reuse (D-025).** The Executor struct is immutable after construction; per-call state lives on the stack and in ctx. `concurrent_test.go` pins N≥128 concurrent invocations against one shared instance under `-race`.
**Deterministic merge keys.** Each Result entry carries the branch's input index AND its tool name. JoinAll returns results in branch-index order; JoinFirstSuccess returns a single-entry slice keyed on the successful branch's index; JoinN returns the first N successful branches in completion order (the per-branch index + tool name is the deterministic key for downstream observation rendering).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ExecuteOption ¶ added in v1.2.0
type ExecuteOption func(*executeOptions)
ExecuteOption configures a single Executor.Execute call. Options are per-call (NOT stored on the immutable Executor — D-025), so a shared executor instance can serve atomic programmatic callers and non-atomic native callers concurrently without cross-talk.
func WithNonAtomicSetup ¶ added in v1.2.0
func WithNonAtomicSetup() ExecuteOption
WithNonAtomicSetup selects non-atomic setup validation (Phase 107d — D-169). In non-atomic mode a branch whose tool fails to resolve, or whose args fail the descriptor's Validate, surfaces as that branch's Result.Err and is skipped at dispatch; the remaining valid branches still fan out. The result slice carries exactly one Result per branch, so every provider `tool_call_id` can be answered — the wire-contract requirement the native React path depends on.
The atomic default (a single invalid-args branch fails the whole call with planner.ErrParallelBranchInvalidArgs before ANY branch dispatches) is unchanged for existing callers. The branch-count cap (planner.AbsoluteMaxParallel) and the missing-identity reject (identity.ErrIdentityMissing) stay fail-loud whole-call aborts in BOTH modes — non-atomic relaxes only per-branch setup, never the system-level guards.
Non-atomic setup is honoured only on the planner.JoinAll path (the native React surface); the other join kinds are programmatic-planner surface and keep the atomic posture.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor dispatches planner.CallParallel Decisions. Constructed once; safe for N concurrent invocations against a shared instance (D-025).
The executor is intentionally minimal — it does NOT apply tools.ToolPolicy retry / timeout (that is the per-tool dispatch shell's job; the executor just invokes the descriptor's Invoke once per branch). Future runtime phases that wire the per-tool policy shell at the dispatcher should compose the shell INSIDE the per-branch goroutine.
func New ¶
New constructs an Executor backed by the supplied Resolver. Nil resolver panics — composition error caught at boot.
func (*Executor) Execute ¶
func (e *Executor) Execute(ctx context.Context, call planner.CallParallel, opts ...ExecuteOption) ([]Result, error)
Execute dispatches the planner.CallParallel Decision per the JoinSpec semantics and returns the per-branch results in deterministic order.
Step 1 (atomic setup validation):
- Branch count vs. planner.AbsoluteMaxParallel (=50). Exceeded → planner.ErrParallelCapExceeded.
- JoinSpec shape (JoinKind known; JoinN threshold in range). Malformed → planner.ErrParallelInvalidJoin.
- Every branch's tool resolves via Resolver.Resolve. Missing → tools.ErrToolNotFound wrapped with the branch index.
- Every branch's args validates via the descriptor's Validate. Failed → planner.ErrParallelBranchInvalidArgs wrapped with the branch index + upstream error.
If any setup step fails, NO branch dispatches. The error is the only return; results is nil. This is the load-bearing "atomicity-contract" surface RFC §6.2 names.
Step 2 (dispatch):
Each surviving branch fires in its own goroutine with the supplied ctx (cancellation, identity, deadline propagate). Per the JoinSpec:
- JoinAll: wait for every branch; return all results in branch-index order.
- JoinFirstSuccess: return the first successful branch; cancel the remainder via a derived ctx; the returned slice is single-entry. If every branch fails, the slice is empty + the error is a joined error of every branch's failure.
- JoinN: wait until N branches succeed; cancel the remainder. The returned slice carries the N successes in COMPLETION order (each Result still carries its original branch Index for deterministic merge-key consumption downstream).
Returns (results, error). The error path is reserved for setup-validation failures and JoinFirstSuccess/JoinN exhaustion (no branch met the threshold). Per-branch failures land on Result.Err — the caller (planner step adapter) decides how to surface mixed-success-and-failure observations.
type Resolver ¶
type Resolver interface {
// Resolve returns the descriptor for name. found=false on miss.
Resolve(name string) (tools.ToolDescriptor, bool)
}
Resolver is the narrow descriptor-lookup view the executor depends on. The full tools.ToolCatalog surface (Register / List) is not needed at dispatch — only Resolve. This narrowing lets tests inject a tiny stub resolver without standing up the production catalog.
type Result ¶
type Result struct {
// Index is the branch's position in the input [planner.CallParallel.Branches]
// slice. Stable for the lifetime of the call.
Index int
// Tool is the branch's tool name. Same as Branches[Index].Tool.
Tool string
// Result is the [tools.ToolResult] on success. Nil on failure /
// cancellation.
Result *tools.ToolResult
// Err is the upstream error on failure. Nil on success.
Err error
}
Result is the per-branch outcome the executor produces. Each entry carries the branch's input index + tool name (the deterministic merge key), the tools.ToolResult on success, and the error on failure.
Either Result is populated (success) or Err is populated (failure) — never both. Cancelled branches surface Err = context.Canceled.