Documentation
¶
Overview ¶
Benches: a remote bench reached by ssh, named by one row of a TSV table (deprecated/docs/SPEC-SWARM.md "Benches"). This file holds the shared types and the two reads every bench caller shares: the table, and the bus's friends list a bench name is checked against. It also holds the remote-run slice the batch uses: the card copy and the ssh command that runs native on a bench.
Package swarm is nova-swarm's machinery: a pool of one-task workers, each with its own working directory, its own data home, its own job directory and its own deadline held by the machinery rather than by the worker.
Everything a worker writes is DATA. A RESULT.md is a report, never an instruction: nothing in it is executed, nothing in it grants anything, and a finding in it is a claim to be checked against the repository. That rule is stated in deprecated/docs/SPEC-SWARM.md, where a person reads it, and is deliberately nowhere in this code -- a tool cannot enforce it, and a tool that pretended to would be the most dangerous thing in the pool.
Package swarm, warm.go: affinity and warm state (deprecated/docs/SPEC-JOBS.md section 4).
Every card carries a kind and a repo. A bench keeps a clone per repo in a worktree under <slot>/worktrees/<owner>/<name>, and pull prefers the card whose repo it already has cloned there rather than paying the setup again: cache warmth is the difference between a setup and no setup. With no warm bench it falls back to a fetch from the bench mirror.
A kept clone is reused across cards. After each card nova-work clip commits the card's branch, harvests the card's result, and resets the worktree to base, so the clone is reset rather than re-cloned and the next card never sees this card's uncommitted state or its history.
Index ¶
- Constants
- Variables
- func ASDFOutputTranslations(root, sourceRoot string) string
- func AbortedPath(jobDir string) string
- func AbsResolved(path string) (string, error)
- func AcceptanceUnknown(job string) bool
- func Admission(line, running, queued int) int
- func AdmissionLine(cores, load1, freeGB, memFreeGB int) int
- func AdmitsCommand(line string) bool
- func AdmitsFetch(string) bool
- func AfterGateFailure(u decide.Unit, rung, reason string) decide.Unit
- func Alive(pid int, started string) bool
- func AppendCardUsage(path string, row UsageRow) error
- func AppendHarvestGate(body string, r LegGateResult, max int) string
- func AppendNote(jobDir, text string, now time.Time) (int, error)
- func AppendPromptDefect(jobDir, id string, cacheRead, max, turns int) error
- func ApplyProviderReadDeadline(raw []byte, provider string) []byte
- func Asked(run AskedRun) (string, bool)
- func AskedEnd(jobDir string, rc int) (string, bool)
- func AskedReport(path string) bool
- func Backoff(retry int, base time.Duration) time.Duration
- func Batch(in BatchInput) int
- func BudgetWord(unmetered bool, tokens, spent int, observed, partial bool) string
- func BuildContainerArgs(engine, image, workDir, model, label, cardContent, secretKey string) []string
- func CacheEnv(root string, sourceRoot ...string) []string
- func CacheRoot(root string) string
- func CapacityLine(cores int, load1, freeGB, memFreeGB float64) int
- func CardCacheRead(u ProviderUsage) (int, bool)
- func CardCloneRepos(text string) []string
- func CardHeaderChecks() []string
- func CardIdleLine(task string, e IdleEnd) string
- func CardKindFromText(text string) string
- func CardNamesRepo(card []byte) bool
- func CardParentPaths(lines []string) []int
- func CardPrompt(card []byte) string
- func CardReadPaths(card []byte) []string
- func CardRepoURL(value string) string
- func CardStageBranch(card []byte) string
- func CardUnit(label, contract, text string) (decide.Unit, bool)
- func CheckKillPoint(point string)
- func CheckPausePoint(point string)
- func CheckPublicCard(w Worker, cardText, root string) (string, bool)
- func CheckStagedTree(jobRoot string) error
- func ClampTimeout(d time.Duration) time.Duration
- func ClampTimeoutWithDefault(d, defaultTimeout time.Duration) time.Duration
- func ContextualReadGlobs(paths []string, testLine string) []string
- func CoresList(cores string) ([]int, error)
- func Cost(p *Pool, since string, max int, by string, summaryOnly bool, ...) int
- func CountCardTurns(jobDir string, u ProviderUsage) int
- func CountCardTurnsIn(logPath string, u ProviderUsage) int
- func CountRefusals(path string) int
- func DeniedPathRoots(denied string) []string
- func EffectiveWidth(b Bench) int
- func EnsureCacheDirs(root string) error
- func ExitAttestHash(secret string) string
- func ExitAttestOK(secret, hash string) bool
- func ExitPath(jobDir string) string
- func FenceJobPatterns(jobDir string) []string
- func FencePermission(jobDir string, reads []string) map[string]any
- func FenceReadPatterns(reads []string) []string
- func FenceRejection(raw []byte) (string, bool)
- func FinalizeByHand(p *Pool, id string, now time.Time) (int, string)
- func FindBenchMirror(benchHome, baseRepo string) string
- func FindCardResult(job string) (string, bool)
- func FriendNames(busDir string) ([]string, error)
- func FullestBench(benches []string) (name string, queued int, ok bool, err error)
- func GoBuildCacheDir(root string) string
- func GoModCacheDir(root string) string
- func GroupAlive(pgid int, started string) bool
- func GroupMembers(pgid, self int) (int, bool)
- func HarnessSHA256(harness string) string
- func HarnessTail(jobDir string) string
- func HashBytes(data []byte) string
- func Headroom(cores int, load float64) int
- func HoldsRepo(slot, repo string) bool
- func InputLimitEnd(jobDir, end string, rc int, recorded string, extra []string) (string, string)
- func InputLimitSignalLine(sig InputLimitSignal) string
- func InputLimited(log []byte, extra []string) (string, bool)
- func IsCardContractLine(line string) bool
- func IsCardTemplate(name string) bool
- func IsNoCard(err error) bool
- func IsPowerOfTwo(n int) bool
- func IsPublicRefusal(why string) bool
- func IsPulseTemplate(name string) bool
- func JobASDFOutputTranslations(sourceRoot string) string
- func JobLispCacheDir(sourceRoot string) string
- func JobName(worker, card string) string
- func KeyFileMode(path string) (os.FileMode, bool)
- func KillGroup(pgid int, started string)
- func KnownStamp(started string) bool
- func LaunchArgv(provider, goos string) ([]string, error)
- func LaunchArgvFor(provider, goos string, req LaunchRequest) ([]string, error)
- func LaunchGrace(w Worker) time.Duration
- func LaunchRecordHash(r LaunchRecord) (string, error)
- func LaunchRecordPath(evidenceRoot, jobID, nonce string) string
- func LaunchRow(provider string) string
- func LegGateLine(r LegGateResult) string
- func LispCacheDir(root string) string
- func LoadHeadroom(cores int, load1 float64) float64
- func LookSandbox(path string) (string, error)
- func LostResponse(tail []byte) bool
- func MakeSlotLease(store, id, owner string, pid int, label string, until time.Time) error
- func MeasureWidthFromRounds(rounds []SizeRound) int
- func MergeFencePermission(raw []byte, jobDir string, reads []string) ([]byte, bool)
- func MirrorCloneArgs(mirror, target string, noCheckout bool) []string
- func MirrorDue(lastSteal, now time.Time) bool
- func NPMCacheDir(root string) string
- func NativeBudgetSourceRefusal(source string, tokens int, unmetered bool, worker *Worker) string
- func NativeUsageIntervalRefusal(interval, deadline time.Duration) string
- func NativeUsageSource(worker *Worker) string
- func NewExitAttest() (string, error)
- func NewID(now time.Time, label string) string
- func NextRoute(routes []string, failed string) string
- func Nonce() (string, error)
- func NormalizePath(path string) string
- func NotePath(jobDir string) string
- func OpenCodeStoreLocations(dataHome string) []string
- func OverMaxInput(sc Sidecar, prompt int) (string, bool)
- func OwedMatch(f Finding, owed []string) bool
- func OwnedCards(benchDir, worker string) ([]string, error)
- func ParseCardBase(card []byte) (baseRepo, baseSha string, ok bool)
- func ParseRouteList(s string) []string
- func ParseSlotRange(s string) (lo, hi int, err error)
- func PickRoute(rows []RouteRow, kind string, rounded int, private float64, def string) string
- func PidPath(jobDir string) string
- func PlanBenchLaunch(cores int, load float64, width, queued int) int
- func PointProviderAtProxy(raw []byte, provider, proxyURL string) ([]byte, bool)
- func PrepareLispJobCache(root, sourceRoot string) error
- func ProfileJobs(pattern string, stdout, stderr io.Writer) int
- func Prompt(in PromptInput) []byte
- func PromptDefectLine(id string, cacheRead, max, turns int) string
- func PromptSize(jobDir string) (int, bool)
- func ProviderBaseURL(raw []byte, provider string) string
- func ProviderLaunchFailure(raw []byte) (ref string, ok bool)
- func ProviderProxyEligible(raw string) bool
- func ProviderRetryAfter(log []byte) (time.Duration, bool)
- func PublicRefusalLine(repo, workerName string) string
- func PublicRefusalWhy(repo, workerName string) string
- func PublishEvidence(evidenceDir, jobID string, task, prompt, profile []byte, preimage string) error
- func PublishLaunchRecord(root, jobID, nonce string, r LaunchRecord) error
- func PullBatch(in PullInput) int
- func PullLanes(in PullLanesInput) int
- func PullQueue(queueDir, takenDir, worker string, admit int) ([]string, error)
- func QuarantineCard(queueDir, reason, cardID string, cardContent []byte, meta QuarantineRecord) (string, error)
- func QuarantineCardDir(queueDir, reason, cardID string) string
- func QuarantineDir(queueDir string) string
- func QuarantineReasonDir(queueDir, reason string) string
- func QueueCards(benchDir string) ([]string, error)
- func QueueDir(benchDir string) string
- func QueuedCards(store string) ([]string, error)
- func RateLimited(log []byte) bool
- func ReadBenchTable(path string) (map[string]Bench, error)
- func ReadJSON(path string, v any) error
- func ReadKey(path, varName string) (string, error)
- func ReadKeyOrSecret(w Worker) (string, error)
- func ReadOwed(path string) ([]string, error)
- func ReadRegular(path string) ([]byte, error)
- func Reap(pgid int, started string, grace time.Duration) (survived bool)
- func ReapAtTaskEnd(root string, w Worker, now func() time.Time) (int, int64)
- func ReapExpiredLeases(store string, now time.Time) ([]string, error)
- func ReapSlots(in ReapInput) (int, int64, error)
- func RecordBenchWidth(path, name string, width int, measured, version string) (cores, rows int, err error)
- func RecordStartupCost(root, harnessSHA256 string, cacheRead, turns int) error
- func RecordStartupCostIfAbsent(root, harnessSHA256 string, cacheRead, turns int) error
- func RefuseFriend(name string, friends []string) string
- func ReleaseQuarantined(queueDir, reason, cardID string) (string, error)
- func ReleaseSlotLeases(store, owner, label string, all bool) (released, held int, err error)
- func ReleaseSlotLeasesByID(store string, ids []string, pid int) (released int, err error)
- func ReleaseSlotLeasesForcing(store, owner, label string, all, force bool) (released, held, live int, err error)
- func RenewSlotLease(store, owner, label string, dur time.Duration, now time.Time) (renewed int, until time.Time, err error)
- func ResultByID(p *Pool, id string, stdout, stderr io.Writer) int
- func ResultPath(jobDir string) string
- func RetriableRateLimit(log []byte, extra []string) bool
- func RouteKey(model, authProfile string) string
- func RouteQuestions() map[string]decide.Question
- func Run(in RunInput) int
- func RunPullWorker(ctx context.Context, opts PullWorkerOptions) int
- func SQLiteOnPath() bool
- func SandboxGate(sandboxPath, poolDir, secret string, notes io.Writer) (reason, text string)
- func SandboxProbeDir(poolDir string) string
- func SchedulerAdmits(freeGB, memFreeGB int) int
- func ScopeGlobs(paths []string, testLine string) []string
- func SecretFromEnv(name string) (string, error)
- func ShareFromMemory(totalMemBytes, perCardBytes int64) int
- func ShellDenialReason(label, jobDir, wall string, rc int, d ShellDenial) string
- func Short(hash string) string
- func SizeRoundHolds(r SizeRound) bool
- func SlotAdmissionWeight(kind string) int
- func SlotHoldings(store, owner string, now time.Time) (held, share int, err error)
- func SlotUtilisation(store string, now time.Time) (capacity, reserve, held, free int, heldBy map[string]int, ...)
- func Slug(label string) string
- func StageCloneIdentity(repoDir string, id StagingIdentity) error
- func StageJob(poolDir, jobRoot, repoDir string) error
- func StageJobTree(source, dest string, card []byte) error
- func StagingGitEnv(id StagingIdentity) []string
- func Stamp(t time.Time) string
- func StartJobLease(jobDir, label string) (release func(), err error)
- func StartSlotLease(slotDir, label string) (release func(), err error)
- func StartStamp(pid int) string
- func Steal(victimDir, worker string, capacity int) ([]string, error)
- func StealCount(queued, capacity int) int
- func Supervise(in SuperviseInput) int
- func TakeCard(benchDir, worker string) (string, bool, error)
- func TakeSlotLeases(store, owner string, k int, dur time.Duration, label string, now time.Time, ...) (ids []string, held, share, free int, holders string, ok bool, err error)
- func TakeSlotLeasesKind(store, owner string, k int, kind string, dur time.Duration, label string, ...) (ids []string, held, share, free int, holders string, ok bool, err error)
- func TakenDir(benchDir string) string
- func TaskkillArgs(pid int, force bool) []string
- func Template(name string) (string, error)
- func TemplateNames() []string
- func TerminateGroup(pgid int, started string)
- func ThisOS() string
- func TooShortForAPhrase(phrase string) (string, bool)
- func ToolchainRootNames(goos string) []string
- func ToolchainRootOSes() []string
- func Triage(in TriageInput) int
- func TriageQuarantineSummary(queueDir string) string
- func TwoPhaseReap(pgid int, started string) (survived bool)
- func TwoPhaseReapWithGrace(pgid int, started string, grace time.Duration) (survived bool)
- func UnsandboxedLine(id string, slot int) string
- func UsageRefusalLine(id string, err error) string
- func ValidWorker(worker string) error
- func ValidateLaunchRecord(r LaunchRecord) error
- func ValidateUsageDestination(path string) error
- func VerifyEvidence(evidenceDir, jobID string) error
- func Version8(v string) string
- func WallCommits(repoDir string) (branch string, commits int, ok bool)
- func WallDeath(jobDir, task string) (string, bool)
- func WallKind(line string) string
- func WallLine(task string, w WallRefusal, branch string, commits int) string
- func WallRefusedLine(task, kind, path, step string) string
- func WallStep(log []byte) string
- func WatchIdle(w IdleWatch, stop <-chan struct{}) <-chan IdleEnd
- func WidthNeedsRemeasure(row Bench, currentVersion string) bool
- func WorktreePath(slot, repo string) string
- func WrapSecretsExec(seat string, cmdArgs []string) []string
- func WrapTemplate(name string, files int, text []byte) ([]byte, error)
- func WriteAskedResult(jobDir, task, question string) (string, bool, error)
- func WriteBlockedResult(jobDir, task, kind, path, step, reason string) (string, bool, error)
- func WriteCardUsage(path string, row UsageRow) error
- func WriteJSON(path string, v any) error
- func WriteMeasuredTable(path, name string, rounds []SizeRound, now time.Time) error
- func WriteReceipt(path string, o Outcome, c Contract) error
- func WriteStageTimeoutResult(jobDir, bench string, secs int) (string, error)
- func WriteTimeline(path string, rows []TimelineRow) error
- type AbortedRecord
- type AskedRun
- type BaseCheck
- type BatchInput
- type Bench
- type Card
- type CardBase
- type CardHandOff
- type CardHeaderFinding
- type CardRoute
- type CardWallTerms
- type ClipRequest
- type ClipResult
- type Contract
- type CredentialsProfile
- type DecideFunc
- type Decision
- type Ending
- type EvidenceManifest
- type EvidenceSnapshot
- type ExitRecord
- type FailureClassification
- type FailureKind
- type Finalized
- type Finding
- type FleetLegs
- type Gate
- type GateCommand
- type GateRun
- type GateRunner
- type Handback
- type IdleEnd
- type IdleWatch
- type InputLimitClass
- type InputLimitSignal
- type Item
- type JobLease
- type JobLeaseHeldError
- type JobProfile
- type KindP95
- type KubeJob
- type LaunchRecord
- type LaunchRecordContext
- type LaunchRecordControl
- type LaunchRecordLauncher
- type LaunchRecordRealization
- type LaunchRecordRefusal
- type LaunchRequest
- type LeaseRefusal
- type LegGateInput
- type LegGateResult
- type Lineup
- type Outcome
- type PidRecord
- type Pool
- func (p *Pool) Add(text []byte, sc Sidecar) error
- func (p *Pool) Claim(id, from, to string) error
- func (p *Pool) ClaimNext() (Sidecar, []byte, bool, error)
- func (p *Pool) Decide(n int) Decision
- func (p *Pool) Finalize(e Ending) (Finalized, error)
- func (p *Pool) FindAnywhere(id string) (Sidecar, bool)
- func (p *Pool) Free(n int) error
- func (p *Pool) FreeIf(n int, nonce string) error
- func (p *Pool) Identify(n int, nonce string, id SlotFile) error
- func (p *Pool) Jobs() ([]Sidecar, error)
- func (p *Pool) List(state string) ([]Sidecar, error)
- func (p *Pool) Orphan(n int, nonce string) (adopted bool, sf SlotFile, err error)
- func (p *Pool) Path(parts ...string) string
- func (p *Pool) ReadRev(id string) (attempt int, hash string, err error)
- func (p *Pool) ReadSidecar(state, id string) (Sidecar, error)
- func (p *Pool) ReadSlot(n int) (SlotFile, error)
- func (p *Pool) ReadSlotBy(n int, budget time.Time) (SlotFile, error)
- func (p *Pool) ReadUsage() ([]UsageRow, error)
- func (p *Pool) Reclaim(id, jobDir string) (freed int64, usagePath string, err error)
- func (p *Pool) ReportBytes(sc Sidecar) ([]byte, string, error)
- func (p *Pool) ReportsDir(id string) string
- func (p *Pool) RequeueOnce(id string, now time.Time) (Sidecar, bool)
- func (p *Pool) Reserve(n int, job, jobDir, nonce string, runnerPid int, now time.Time) error
- func (p *Pool) RetainedReport(id string) (raw []byte, kind string, err error)
- func (p *Pool) SlotNumbers() ([]int, []string, error)
- func (p *Pool) Stopped() bool
- func (p *Pool) TakeLock(name string, wait time.Duration) (func(), error)
- func (p *Pool) Text(state, id string) ([]byte, error)
- func (p *Pool) UpdateSlot(n int, nonce string, mutate func(SlotFile) SlotFile) error
- func (p *Pool) UsagePath(id string) string
- func (p *Pool) Where(id string) (string, bool)
- func (p *Pool) WriteSidecar(state string, sc Sidecar) error
- func (p *Pool) WriteUsage(id string, row UsageRow) (string, bool, error)
- type PrewarmCommand
- type PrewarmInput
- type PrewarmResult
- type Profile
- type ProfileRefusal
- type PromptInput
- type PromptProfile
- type ProviderExit
- type ProviderProxy
- func (p *ProviderProxy) Close() error
- func (p *ProviderProxy) HarnessURL() string
- func (p *ProviderProxy) HeaderWait() time.Duration
- func (p *ProviderProxy) HeaderWall() time.Duration
- func (p *ProviderProxy) Lost() bool
- func (p *ProviderProxy) Requests() int64
- func (p *ProviderProxy) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (p *ProviderProxy) Silence() time.Duration
- func (p *ProviderProxy) SilenceWall() time.Duration
- func (p *ProviderProxy) Stalled() <-chan struct{}
- type ProviderProxyConfig
- type ProviderUsage
- func ReadCardUsage(dataHome string, started, ended time.Time) (ProviderUsage, string, string, string)
- func ReadCardUsageAfter(dataHome string, started, ended, notBefore time.Time) (ProviderUsage, string, string, string)
- func ReadJobUsageLive(dataHome string) (ProviderUsage, error)
- func ReadProviderUsage(source, dataHome string) (ProviderUsage, error)
- type Pull
- type PullBench
- type PullCard
- type PullInput
- type PullLanesInput
- type PullResult
- type PullSubmitInput
- type PullSubmitResult
- type PullWorkerOptions
- type QuarantineRecord
- type ReapInput
- type ReconcileRecord
- type Report
- type ResultLint
- type ResultLintFinding
- type RouteError
- type RouteInput
- type RouteProfile
- type RouteResult
- type RouteRow
- type RunInput
- type SandboxJob
- type ShellDenial
- type ShellDenialReader
- type Sidecar
- type SizeRound
- type SlotFile
- type SlotLease
- type SlotWait
- type StageOptions
- type StageResult
- type StagingIdentity
- type StartupCost
- type SuperviseInput
- type TaskDecision
- type Timeline
- type TimelineRow
- type TimeoutHierarchy
- func (h TimeoutHierarchy) CardContext(parent context.Context) (context.Context, context.CancelFunc)
- func (h TimeoutHierarchy) GitBudget(remainingStep time.Duration) time.Duration
- func (h TimeoutHierarchy) GitContext(parent context.Context, remainingStep time.Duration) (context.Context, context.CancelFunc)
- func (h TimeoutHierarchy) StepBudget(remainingCard time.Duration) time.Duration
- func (h TimeoutHierarchy) StepContext(parent context.Context, remainingCard time.Duration) (context.Context, context.CancelFunc)
- func (h TimeoutHierarchy) Validate() error
- type ToolchainRoot
- type TriageAction
- type TriageInput
- type TriageRouteResult
- type TriageState
- type TrustState
- type UsageRow
- type Verdict
- type WallReader
- type WallRefusal
- type WindowsKillStrategy
- type Worker
- func (w Worker) BudgetRefusal(s StartupCost) string
- func (w Worker) DataHome(slot int, id string) string
- func (w Worker) DefaultDeadline() time.Duration
- func (w Worker) HarnessConfig() []byte
- func (w Worker) HasCardBudget() bool
- func (w Worker) IsPublic() bool
- func (w Worker) JobDir(slot int, id string) string
- func (w Worker) OverCardBudget(u ProviderUsage, turns int) (over bool, cacheRead, max int)
- func (w Worker) RefreshSlot(slot int) error
- func (w Worker) SlotDir(slot int) string
- func (w Worker) WriteHarnessConfig(slot int) (string, error)
- type WorkerProfile
Constants ¶
const ( // DefaultBackoff is the wait before the first retry when the provider names none. DefaultBackoff = 30 * time.Second // MaxBackoff is the cap the doubling never passes: the machinery never waits forever. MaxBackoff = 5 * time.Minute )
RATE LIMITS ARE THE DISPATCHER'S BUSINESS (SPEC-SWARM, "cost per task, and rate limits").
A provider's 429 is not a failed task: the dispatcher holds the slot, waits the interval the provider names, or this tool's own --backoff, and retries the SAME task once. A second 429 fails it with rc=429 in its sidecar and its usage row, so `triage` can see that a batch's silence was a limit rather than a set of bad tasks.
const ( // DecisionsFile is the per-task decision log beside the pool's outcome. DecisionsFile = "decisions.log" // DefaultDecideFloor is the confidence floor triage --decide starts at. DefaultDecideFloor = 0.9 )
const ( // FenceAllow, FenceAsk and FenceDeny are the harness's own permission actions. `ask` in // a non-interactive `run` is auto-rejected and the model STOPS -- the whole run ends and // the card's commits are stranded. `deny` is a TOOL ERROR returned to the model, which // notes it, works inside the job instead, and continues (issue #918). So the fence's // fallback is deny, and `ask` is never written: there is no terminal to answer it. FenceAllow = "allow" FenceAsk = "ask" FenceDeny = "deny" // FenceExternalDirectory is the permission key the harness asks under for a path // outside the cwd; FenceWebfetch is the network tool. Neither is left to prompt. FenceExternalDirectory = "external_directory" FenceWebfetch = "webfetch" )
THE HARNESS'S OWN FENCE (issue #644). OpenCode asks before a tool touches a path it calls EXTERNAL, and a `run` with no terminal answers every such question by rejecting it:
permission requested: external_directory (<dir>/*); auto-rejecting Error: The user rejected permission to use this specific tool call.
The model stops there, publishes nothing, and the batch scored the card `no-result` -- the model's own doing -- when the truth is that the machinery fenced off a path the CARD was told to use. Eight of thirty cards on Rowan's bench died this way on 2026-09-16.
TWO THINGS ARE WRONG AND BOTH ARE THIS FILE'S BUSINESS. The fence is configured by nothing -- the job runs on the harness's defaults, which call everything outside the harness's own cwd external -- and a rejection it makes is invisible to the reader, who sees only the absence of a result.
WHAT IS NAMED AND WHAT IS NOT. The job directory and everything under it, and nothing else -- not the job's `jobs` parent. An earlier draft of this fix named the parent too, on the belief that the fence resolves a card's `../scratch` against the HARNESS's cwd rather than the shell's cwd after `cd repo`; a real run of both binaries against the harness showed it resolving after the `cd`, with no rejection either way, so the parent rule bought nothing and widened a job's fence to every SIBLING job in the same slot on a --no-wall bench. The WALL, not the fence, is what keeps a card inside its job (SPEC-SANDBOX rule 1), and a fence rule is no place to hand one card another's work.
BOTH WILDCARD SPELLINGS ARE WRITTEN, and not because `*` stops at a separator -- it does not: the harness's own matcher turns `*` into `.*`, which crosses `/` freely, so `<job>/*` already admits `<job>/scratch/*`. `<job>/**` is written beside it because it is the spelling a person reads as "everything under here", and a matcher that ever tightened `*` to one segment would leave the rule meaning what it says.
const ( MarkerMalformed = "MALFORMED" MarkerNoResult = "NO-RESULT" MarkerRev = "REV" CopiedResult = "RESULT.md" )
The three names under <pool>/reports/<job>/.
const ( MarkWords = 2 MarkBareWords = 1 )
MarkWords is how many tokens may come BEFORE the mark and leave it a label.
A SUBSTRING SEARCH IS NOT A LABEL (the swarm delta eye on #150). `strings.Contains(lower, mark)` accepted the word anywhere on the line, so a RESULT.md bullet, a finding row or a heading that QUOTED the provider's sentence beside the word `error` was marked and classed -- the very line the mark rule was added to exclude, and a job classed this way is never retried, so the false mark takes a real 429's retry away.
A harness's label is a stamp, a component name and the mark: `Error: …`, `opencode: error: …`, `2026-09-12T12:24:31Z error: …`, `API Error: 400 …`, `openai.BadRequestError: Error code: 400 …` -- never more than two tokens before it. Prose has more (`- the provider printed error: …` has four), and a quote character before the mark means the line is quoting rather than reporting.
AND AT MOST ONE OF THOSE TOKENS IS A BARE WORD. Two alone is not enough: `the harness error is …` and `| 3 | red | error | …` are a sentence and a table row, and both put a mark two tokens in. A label token is a STAMP (it carries a digit) or a PREFIX (it ends in `:`, `]`, `|` or `>`); anything else is a bare word, and a harness writes at most one of them before its mark -- the `API` of `API Error:`, the `provider` of `provider error:`. A second bare word is prose.
const ( AskedResultPrefix = "RESULT: ASKED " AskedWrittenBy = "written-by: nova-swarm native (the card published no report of its own)" )
AskedResultPrefix opens line 1 of that report, and AskedWrittenBy is the line that says the machinery wrote it. Both are constants because two readers already depend on them: the one that writes the report and the one that refuses to count it as the card's own.
const ( Pending = "pending" Running = "running" Done = "done" Failed = "failed" // Aborted is where a task goes when a PERSON replaces it: `requeue` writes the new task // and moves the old one here, so a pool cannot wedge (the new-user audit, F2 and F7: // `requeue` added and never removed, pending went 2 to 4, and the only way out of the // refusal's own remedy was `rm`). Aborted = "aborted" // RoutedOut is where a task goes when the ladder rules it judgment work owed to a // child or a friend (issue #1486): a card the dispatcher routes to ask-child or // ask-bus is NOT dispatched to a model, it is parked here with its ROUTE line, and // the coordinator (or a friend, over the bus) takes it from here. RoutedOut = "routed-out" )
The four states a task's files sit in. A task is in exactly one of them, and the move between two of them is a rename, which is the claim: two dispatchers both try it and the kernel picks one.
const ( Reports = "reports" Scratch = "scratch" Slots = "slots" Usage = "usage" )
The pool's other directories. reports/ holds the pages and one directory per finalized job; usage/ holds one row per job and is OUTSIDE everything reclaim removes, which is the whole of rule 12.
const ( RunLock = "run.lock" SlotsLock = "slots.lock" StopFile = "stop" )
RunLock excludes dispatchers and nothing else; SlotsLock guards one slot-file transition and is never held across a wait (rule 17).
const ( WorkerClassPublic = "public" WorkerClassPaid = "paid" )
Public class words a worker description may carry. Empty means paid: no description written before this gate changes meaning by being re-read.
const ( JobCPURequest = "1" JobMemoryRequest = "2Gi" JobEphemeralRequest = "2Gi" JobMemoryLimit = "2Gi" // KubeReservedFloorGiB is the floor kube-reserved/system-reserved holds back. KubeReservedFloorGiB = 25 )
The Job's requests and limits: the capacity line as scheduler facts.
const ( DefaultWorkerLeaseDur = 30 * time.Minute DefaultWorkerImage = "nova-card:latest" DefaultWorkerModel = "deepseek/deepseek-chat" )
Default lease duration and container image.
const ( // QuarantineDirName is the directory under queue holding all quarantined cards. QuarantineDirName = "quarantine" // QuarantineMetadataFile is the metadata descriptor inside each quarantined card directory. QuarantineMetadataFile = "quarantine.json" // ProviderFailedExt is the legacy suffix that caused cards to sit in limbo. ProviderFailedExt = ".provider-failed" )
const ( // CardExt is the suffix that makes a file in queue/ a card. CardExt = ".card" // QueueName is the per-bench directory of waiting cards. QueueName = "queue" // TakenName is the per-bench directory of cards a worker owns, one // <worker>-<name>.card each. TakenName = "taken" // MirrorTimer is the bench mirror's fetch-only cadence: an idle worker steals // from the fullest bench once per five minutes, not once per look. MirrorTimer = 5 * time.Minute )
The per-bench queue's names and cadence, as section 2 prints them.
const ( // CacheDirName is the one shared cache directory under a swarm root. CacheDirName = "cache" // DefaultReapOlder is --older's default: a slot whose newest harness log is older than // an hour, with no RESULT.md, is a slot no live job is writing. DefaultReapOlder = time.Hour )
SHARED PER-BENCH CACHES (issue #1048). A native job downloads the Go toolchain and every module into its own sandboxed data home -- up to 5 GB per slot -- and 120 cards filled hulk and vision to 100%. The toolchain and the module cache are the same for every job under one swarm root, so they live once under <root>/cache and every job's child is pointed at them (GOMODCACHE, GOCACHE, NPM_CONFIG_CACHE). The root is a permitted write root beside the job directory (docs/SPEC-SANDBOX.md), never the job's own data home.
const ( ClassOK = "ok" ClassClean = "clean" ClassPlanOnly = "plan-only" ClassNoResult = "no-result" ClassMalformed = "malformed" )
The classes.
const ( RouteFallbackNoKey = "no-key" RouteFallbackBelow = "below-floor" RouteFallbackNoModel = "rung-is-asked-not-run" RouteFallbackRefused = "refused" RouteFallbackNoLadder = "no-ladder" // RouteFallbackNoAccount is the refusal to ask at all: a call nobody can // account for is not made, so the rules answer and today's model stands. RouteFallbackNoAccount = "no-accounting" )
The enumerated reasons a route fell back. They are tokens and not prose, so the log can be counted by them.
const ( SlotReserved = "reserved" SlotLaunched = "launched" SlotOrphaned = "orphaned" )
The states a slot file passes through.
const ( DecideAdopt = "adopt" DecideReclaim = "reclaim" DecideUnknown = "unknown" DecideUnlaunched = "unlaunched" DecideQuarantine = "quarantine" )
The decision words.
const ( SlotWeightRead = 1 SlotWeightHeavy = 4 )
const ( ModelPlaceholder = "{model}" PromptPlaceholder = "{prompt}" BaseURLPlaceholder = "{base_url}" )
The placeholders a worker description may write into harness_args.
const ( // MinExecutionTimeout is the absolute lower bound on execution timeout (5s). MinExecutionTimeout = 5 * time.Second // MaxExecutionTimeout is the absolute upper bound on execution timeout (2h). MaxExecutionTimeout = 2 * time.Hour // DefaultCardTimeout is the canonical default timeout for an unconfigured card (30m). DefaultCardTimeout = 30 * time.Minute // DefaultStepTimeout is the canonical default timeout for an individual step (5m). DefaultStepTimeout = 5 * time.Minute // DefaultGitTimeout is the default budget for a git/VCS operation (1m). DefaultGitTimeout = 1 * time.Minute // MaxGitTimeout is the maximum budget allowed for a single git operation (1m). MaxGitTimeout = 1 * time.Minute // MinGitTimeout is the minimum budget floor for a git operation when time allows (5s). MinGitTimeout = 5 * time.Second // DefaultGracePeriod is the grace period granted to a process group between SIGTERM // and SIGKILL, matching TerminateGrace (3s). DefaultGracePeriod = TerminateGrace )
const ( EndDone = "done" EndKilled = "killed" EndBudget = "budget" EndUnverifiable = "budget-unverifiable" EndViolation = "violation" EndFailed = "failed" EndUnknown = "unknown" EndLaunchFailed = "launch-failed" EndInputLimit = "input-limit" // EndProvider is a launch that did not take: the harness died inside the launch // grace with a provider server error in its tail. It is retried with backoff and, // after the third fast failure, is filed with the provider's own ref (issue #900). EndProvider = "provider" // EndWall is a job the harness's own fence stopped at a path outside it, with no // RESULT.md: the death names the path and the commits ./repo kept (issue #918). It is // also a card the harness's own fence or the OS wall stopped before it could publish: // the machinery ended the task, not the model (wall.go, issue #644's follow-up). EndWall = "wall" )
The ways a job ends, as the `end` column spells them.
const ( // SandboxRefusedMark is the sandbox's own refusal prefix (cmd/nova-sandbox/main.go). SandboxRefusedMark = "SANDBOX REFUSED" // OperationNotPermittedMark is the kernel's refusal, which on a path outside the write // set is the wall talking (internal/sandbox/gpu.go, the Darwin probe). OperationNotPermittedMark = "Operation not permitted" // WallStepMark is how a card numbers its steps; the LAST one in the log is where the // wall stopped it. WallStepMark = "STEP " )
The wall's own words, kept here beside the classifier that reads them.
const ( UsageOpenCode = "opencode" UsageNone = "none" )
The usage sources a description may declare (rule 13). There are two.
A SOURCE IS NAMED FOR WHAT IT IS. `opencode` promised that OpenCode's own accounting is read, and did not read it: on 2026-09-11 two real jobs burned 61,875 and 85,308 tokens against `--tokens 20000` and both reported `budget=-/20000`, because OpenCode writes `opencode.db` and this tool read a tab-separated file instead. The name is true now -- `opencode` reads that database through `sqlite3`, read-only, in the data home this tool exported for the job -- and the tab-separated file is not a name a description may carry: a caller names a SOURCE, never a file some harness might write.
const AskedResultName = BlockedResultName
AskedResultName is the report a run writes FOR a card that ended by asking. It is `RESULT.md` for the same reason the blocked report is: that is the one file every gather already looks for, and a card that ended by asking published nothing there.
const AskedTailBytes = 64 << 10
AskedTailBytes bounds what the question test reads of a capture. A card's capture is a whole transcript and can be megabytes; the question is the LAST thing in it, and no reader needs a megabyte read to answer a yes/no.
const BlockedResultName = "RESULT.md"
BlockedResultName is the report a run writes FOR a card that never published one, so the coordinator's harvest reads a named end instead of an absence. It is `RESULT.md` because that is the one file every gather already looks for, and its first line carries no findings head, so it can be scored `plan-only` and NEVER `ok` or `clean`: a report the machinery wrote can never be counted as work a worker did.
const CardContractWanted = "" /* 266-byte string literal not displayed */
CardContractWanted is what `result-first` wants, in ONE form, for the remedy line and for any tool that has to print the rule rather than apply it. It names the stopgap in the same breath so a writer reading a card `cut` wrote is not told it is wrong, and so that nobody has to read two documents to learn which of the two to type.
const CardDependsRemedy = "DEPENDS-ON: <card-id>[, ...] or DEPENDS-ON: -"
CardDependsRemedy is what the `depends-on` drift names. One remedy for the token, the two forms the key is allowed to take.
const CardOutEnv = "NOVA_CARD_OUT"
CardOutEnv names the card wrapper's out directory in a harness's environment.
const CardParentPathWanted = "" /* 385-byte string literal not displayed */
CardParentPathWanted is what `no-parent-path` wants, in one line, for the remedy and for any tool that prints the rule rather than applies it.
const CardReadPathsMarker = "READ:"
CardReadPathsMarker is the card line this reads. A card that needs a read-only path outside its job says so in one line -- `READ: /sys/kernel/security/lsm` -- and that line, and nothing inferred from the card's prose, is what opens the fence for it.
const CardTestGateRemedy = "" /* 276-byte string literal not displayed */
CardTestGateRemedy is what the `test-gate` token wants, in one line, in the same table shape as `depends-on` (CardDependsRemedy). It is not merged into the typed-header remedies map on purpose -- that map's size is pinned by the `--rules` listing and the cardLintChecks count in deprecated/cmd/nova-swarm/lint.go, which is the wiring this card's PATHS does not reach.
const Dash = "-"
Dash is the absence this file writes, and never a zero.
const DefaultContractLines = 20
DefaultContractLines is the bound on evidence lines when none is named.
const DefaultLaunchGrace = 15 * time.Second
DefaultLaunchGrace is how long a harness may run before its exit stops counting as a launch failure. A worker description's `launch_grace` overrides it.
const DefaultLaunchRow = "*"
DefaultLaunchRow names the providers-table row a route launches with when its provider has no row of its own. It is declared in the table like every other row, so a route the table does not name still launches through the one launcher and its one argv shape -- never through a guessed argv or a per-provider script.
const DefaultLaunchTimeout = 10 * time.Second
DefaultLaunchTimeout is how long the runner waits for a supervisor to identify itself. A tool property like a timeout, not a fact about anybody's pool.
const DefaultNativeIdle = 300 * time.Second
DefaultNativeIdle is the window a native run gives a card that is saying nothing and spending no CPU. It is 300 seconds, which is `batch --idle`'s own default: one number for the two verbs, so a card does not mean two different things on two paths. It is not a provider read deadline. A response whose headers arrived and then sent no body bytes is UNKNOWN, and that deadline is the provider proxy's body timer, not this window. Whole-card silence is not that evidence: a quiet think looks the same, and it stays CARD IDLE.
const DefaultPullFloor = 0.9
DefaultPullFloor is the confidence floor behind which the scorer's order is a suggestion, never an authorization.
const DefaultRouteFloor = 0.9
DefaultRouteFloor is the confidence floor the dispatch route starts at. It is the adoption default of SPEC-DECIDE rule 5 and it is stated HERE, beside the call site, because a floor belongs to the call it gates.
const DefaultStageTimeout = 120 * time.Second
DefaultStageTimeout is the hard timeout for card staging (120 s).
const DefaultUsageInterval = 5 * time.Second
DefaultUsageInterval is how often a live sample reads the source. It is a tool property, like a timeout, and never a fact about anybody's job. Rule 13 names it for `run` and rule 13d gives `native` the same flag and the same default.
const (
FenceRejectionMark = "permission requested: "
)
FenceRejectionMark and fenceRejectionTail are the harness's own words, printed by its `run` command for every permission it answers with a reject:
! permission requested: external_directory (/x/y/*); auto-rejecting
The line carries the harness's colour codes around it, so it is found by its two marks and never by an exact match on a whole line.
const GateOutBytes = 400
GateOutBytes bounds what one command's verbatim output contributes to the gate section. `go test` can print screens on a red package; the reader needs the verdict line and the first of the hurt, and the body has a budget like any other.
const GateSectionBytes = 4096
GateSectionBytes is the PR-body budget the whole gate section is bounded to when the caller names none: the same 4096 the harvest bounds a body to.
const GateTimeout = 10 * time.Minute
GateTimeout bounds one gate command. The gate runs the touched packages only, and the per-package time budget answers in two minutes at most; ten is the outer bound that keeps a hung bench from hanging the harvest with it.
const GroupDrainWait = 300 * time.Millisecond
GroupDrainWait is how long the check above gives a job's group to empty. It is a tool property, not a fact about anybody's job: long enough for a wrapper's exit to land after the work's, short enough that it is invisible beside a launch.
const HarvestGateHeading = "## Harvest gate"
HarvestGateHeading is the fixed PR-body heading the harvest's gate line is written under, the one heading a reader may trust. Everything under it is the harvest's own: the section is stripped and rewritten on every append, so a card-typed copy of the heading never survives a harvest pass (issue #2386: "a card's self-reported gate is data, never a gate").
const JobLeaseHeartbeat = 30 * time.Second
JobLeaseHeartbeat is how often a held lease's mtime is bumped. It is far under the ten minutes the reaper allows a heartbeat to age, so a bench under load that misses a tick or two still reads as live.
const JobLeaseName = ".lease"
JobLeaseName is the lease file's name inside the job directory. The reaper knows it by this name and by nothing else.
const JobLeaseStale = 10 * time.Minute
JobLeaseStale is how old a heartbeat may be before a lease whose OWNER CANNOT BE ASKED ABOUT stops counting as held. It is the reaper's own window (scripts/bench-hygiene.sh, HYGIENE_LEASE_STALE_MIN=10), read the same way and for the same reason: it is the only word there is about a record naming no pid this kernel can answer for. A lease whose pid this host CAN be asked about never reaches it.
const JobRepo = "repo"
JobRepo is the clone under a job directory: <job>/repo, the same name gather and harvest already look for.
const LaunchSchema = "nova.swarm.launch/1"
const LiveSampleLimit = 5 * time.Second
LiveSampleLimit is what ONE live sample is given, whatever the interval (SPEC-SWARM rule 13d): "a read is given 5 seconds whatever the interval, no sample starts while one is unanswered, a read still unanswered at its limit is abandoned and counted as a failed read". It is a fixed number rather than a function of the interval because at a short interval three slow reads would end an honest card `budget-unverifiable`.
const MaxLaunchRecordBytes = 65536
const MaxProviderAttempts = 3
MaxProviderAttempts is the whole number of launches one task gets: the first, then the two retries the grace earns.
const MaxRegularRecord = 16 * 1024 * 1024
MaxRegularRecord is the maximum size of a worker-written record read whole into memory by the supervisor. Normal records (RESULT.md, harness.log, sidecars, task files, retry state) range from tens of bytes to a few MiB for verbose test runs; 16 MiB provides generous margin while bounding supervisor memory consumption against unbounded reads or rogue worker files (security#30, finding 4 residue, issue #234).
const NoBatchTokensRefusal = "" /* 294-byte string literal not displayed */
NoBatchTokensRefusal is the ONE line a batch of cards prints when it was given no budget word (SPEC-SWARM rule 13d, issue #1545). Like NoSlotsStoreRefusal beside it, it is a constant so that the batch's refusal and `native`'s own say the same thing and a caller who greps for one finds the other.
IT NAMES BOTH SPELLINGS, because a caller running a local model has no accounting to read and `unmetered` is the only honest thing they can say; and it says whose budget the word is, because the first question a reader asks of a batch-wide flag is whether ten cards share it.
const NoIdleWindow = time.Duration(0)
NoIdleWindow is the window `--idle 0` reaches WatchIdle as: no watch at all, which is the behaviour every native run had before this file existed. It is named because a bare zero beside a duration reads like an oversight, and this one is a choice a caller can type.
const NoSlotsStoreRefusal = "" /* 163-byte string literal not displayed */
NoSlotsStoreRefusal is the ONE line printed when a native launch was asked for without a bench slot store or without an owner (nova-tools#1546). It lives here, beside the lease code, because FOUR places must print the same sentence -- `nova-swarm native` itself, the batch that refuses before any card runs, and the two paths that build a native argv -- and a remedy that drifts between them is a remedy a reader stops trusting.
It names the store, the owner AND the exact command that makes a one-seat store, because "pass --slots-store <dir>" on a bench that has never had one is not a remedy, it is a second question.
const ONoFollow = oNoFollow
ONoFollow is oNoFollow for callers outside this package: `native` opens a log inside the JOB, which is the worker's own writable directory, and a symlink planted there by a card would carry that write out of the wall (security#30's class, issue #608).
const OpenCodeDB = "opencode/opencode.db"
OpenCodeDB is the database's path inside the job's data home (XDG_DATA_HOME).
const PermissionDeniedMark = "Permission denied"
PermissionDeniedMark is the kernel's refusal on linux. landlock denies a write outside the write set with EACCES, and the C library spells that `Permission denied` -- so the marks this package had (`SANDBOX REFUSED`, `Operation not permitted`) named NO linux refusal at all. Measured inside the swarm's own wall on hulk: `sh: 1: cannot create /tmp/nova-wall-probe-swarm: Permission denied`. Like OperationNotPermittedMark it counts only ON A PATH: `Permission denied` with no path in the line is some other permission, and a card's own test output is full of sentences.
const PhraseFloor = 12
PhraseFloor is the shortest a provider's sentence may be, in characters, and it is a FLOOR because a phrase is a knife: a job classed `input-limit` is a job that is never retried.
const PoolIdentityRemedy = "remedy: make -C fleet converge (rowan-tools) writes the pool identity.tsv"
PoolIdentityRemedy is the one remedy every pool identity refusal names (nova-tools #3193). The pool identity is bench configuration: the fleet converge in rowan-tools writes <pool>/identity.tsv (its pool_identity task), so a bench refusing for it is an unconverged bench, never a hand-written file. The card wrapper carries the refusal line, this remedy included, onto the card record as its why.
const PrewarmReceiptDir = "prewarm"
const ProfileCatalogMax = 262144
ProfileCatalogMax is the catalog read ceiling from SPEC-SWARM-PROFILES: at most 262144 bytes, plus the overflow-detection byte a reader uses to prove it is not truncating.
const ProfileExitCode = 2
ProfileExitCode is the exit status a refused profile load carries: admission refusals are exit 2 before any worker starts (SPEC-SWARM-PROFILES, refusals and admission bounds).
const ProviderBeatName = ".provider-beat"
ProviderBeatName is the file the supervisor touches each time it observes provider bytes. The lease heartbeat only renews when this file has moved, so a hung socket lets the lease expire rather than keeping it alive on a timer.
const ProviderBodySilence = 45 * time.Second
ProviderBodySilence is how long the response body may sit with no bytes after headers have arrived. The proxy enforces it. A stream that keeps sending is not cut off. Silence past this is UNKNOWN and the card is not launched again. It is under the 90s a black-holed attempt may cost.
const ProviderChunkTimeout = 45 * time.Second
ProviderChunkTimeout is written into the job config as chunkTimeout, in milliseconds. Same limit as the header option, and the same non-claim: setting it was measured not to end the stall.
const ProviderClass5xx = "PROVIDER-5XX"
ProviderClass5xx is the class word the hand-back line carries in place of INCOMPLETE.
const ProviderHeaderTimeout = 45 * time.Second
ProviderHeaderTimeout is written into the job config as headerTimeout, in milliseconds. It is not the body-read deadline. It stays under 90s so a harness that does honor it cannot outlive the body-read budget.
const RoutesEnv = "NOVA_SWARM_ROUTES"
RoutesEnv names the ordered route list (provider/model, comma or space separated) the launcher hands the run, so the run can name the route after the one that failed.
const SQLiteBinary = "sqlite3"
SQLiteBinary is the one program a usage source needs on PATH.
const SandboxBinary = "nova-sandbox"
SandboxBinary is the name this tool looks for on PATH when --sandbox names no path. It is the tool's OWN name and not a path: a default PATH lookup of a named command is what this tool already does for the harness, and no directory is guessed.
const SandboxNoneByFlag = "none-by-flag"
SandboxNoneByFlag is the name the OK line carries for a run the caller unwalled with --no-wall: a run that had no sandbox, told apart here from one that had a real one, so no refusal attributes a denial to a wall that was not there.
ShareMemoryHeadroom is how many times the measured per-card RSS a card is planned at before the divide. A card measured at ~600 MB is budgeted ~1.2 GB, so a spike in one card does not spill into its neighbour and the OS keeps a cushion of its own rather than the whole box being priced at the measured minimum.
const SizeHeadroomFactor = 1.5
SizeHeadroomFactor is the launch headroom per tick: cores x 1.5 - load.
const SizeLoadFactor = 1.25
SizeLoadFactor is rule (a): the one-minute load holds at most 1.25 x cores.
const SizeScaleFactor = 1.5
SizeScaleFactor is rule (b): cards per minute at W holds at least 1.5 x cards per minute at W/2.
const SlotLeaseName = JobLeaseName
SlotLeaseName is where the slot lease lives: <slot>/.lease, beside jobs/ and data/.
const SlotStoreLockName = "slots.lock"
SlotStoreLockName is the bench store's one lock file, beside shares.tsv and the slots/ directory. It is NOT part of the store's format in the sense that matters: shares.tsv keeps every byte of its shape, `slots list` still reads directories, and a store made by an older `slots init` grows this file the first time a take runs against it.
const SlotStoreWait = 10 * time.Second
SlotStoreWait is how long a take or a release waits for the store lock. Every holder does a bounded reap, a count and at most `--n` mkdirs and then releases; a wait longer than this is a holder that is stuck, not a bench that is busy.
const SlotsWait = 10 * time.Second
SlotsWait is how long a slot transition waits for the lock. It is short because every holder of it does one read, one compare and one rename and then releases: a wait longer than this is a holder that is waiting on something, which this lock forbids.
const StartupCostFile = "startup-cost.tsv"
StartupCostFile is the measurement's name under a pool's or a native run's root.
const SteadyWindow = 2 * time.Second
SteadyWindow is how long a read or a rename waits out somebody else at the same path. It is a property of the FILESYSTEM's replace window -- the microseconds a delete is pending -- and not of anybody's job, so it is short, and long enough that a scheduler that parks this goroutine for a quantum still lands inside it.
const TerminateGrace = 3 * time.Second
TerminateGrace is how long a group is given to stop after the terminate and before the kill. Long enough that a harness can flush what it was writing, short enough that a reap is a reap.
const TimelineFileName = "timeline.tsv"
TimelineFileName is the per-turn timeline a native run writes beside a card's RESULT.md and usage.tsv. It is one header line and one row per model turn and per tool call.
const TriageStateFile = "triage.json"
TriageStateFile is the consumed set's file name.
const UsageIntervalFloor = time.Second
UsageIntervalFloor is the shortest interval `native` accepts. Rule 13d: an interval under one second "could end an honest card on three quick reads" -- three failed reads in a row end a card `budget-unverifiable`, and at a tenth of a second that is a third of a second of bad luck rather than a source that has really stopped answering.
const WorkerCap = 64
WorkerCap is the ceiling on --workers (Glenn, 2026-09-10). A request above it is a REFUSAL and not a silent clamp: the prototype prints a note and clamps, and a caller who asked for 200 workers has a belief about throughput that a note at the top of a log does not correct.
Variables ¶
var ApprovedCommands = []string{
"cat", "diff", "git", "go", "gofmt", "grep", "head", "ls", "make", "rg", "wc",
}
ApprovedCommands is the closed harness-prompt allowlist. A first-token not in it is denied without prompting, so a card does not spend a turn on a permission refusal. This is not command confinement and not the OS wall: git, go and make can still invoke other programs. curl, wget, ssh, nova-sandbox, nova-secrets and a login shell are not in it. No new permissions.
var BudgetColumns = []string{"tokens_in", "tokens_out", "reasoning"}
BudgetColumns are the columns rule 13's budget counts: what the job SENT, what it GENERATED, and what it REASONED. Cache reads are the provider re-reading context it already holds, and cache writes are that context being laid down once; neither is work this job asked for, and counting them ended a real four-million-token job at 4.18M in under three minutes with zero findings (dogfood D6, 2026-09-11). The usage ROW keeps all five columns -- `cost` reads what it always read -- the BUDGET counts these three.
var CardBaseRemedies = map[string]string{
"paths-at-base": "every PATHS entry names a file, directory or glob that exists at the card's base-sha (or is a new `_test` file); cut the card from the tree at that sha, not from the issue's words, and hand the lint a repository holding the sha with `--repo <dir>` (failed-cards-2026-09-22 class 9, nx-f19)",
"no-push-steps": "a card ends at a local commit: no STEP runs `git push` or `gh`, because the wall holds no credential and the harvest pushes and comments; say `no gh, no push` in RULES, never as a STEP command (failed-cards-2026-09-22 class 8, holdfix and nx-r repair cards)",
"leg-in-fleet": "LEG: (or each LEGS: entry) is a leg the fleet's leg table carries -- `sbcl`, not `lisp` -- and the table is handed over with `--legs <file>` (failed-cards-2026-09-22 LEG row, #2728)",
"donewhen-test-name": "DONE-WHEN: names the runner and a literal test that does not exist at the card's base-sha -- `go test ./<pkg> -run <TestName>`, `pytest <file>::<test_name>` (or `-k <test_name>`), `cargo test <name>` -- so the test can be red on base and green at head; an English outcome (\"applied cleanly\", \"make preflight\") or a runner with no test named is not a control, and the repository holding base-sha is handed over with `--repo <dir>` (#3083, the-control-is-the-sentence)",
"deadline-p95": "DEADLINE: is at or above the measured p95 wall of the card's KIND, in seconds (or `finish within <n> minutes`), and the p95 table is handed over with `--p95 <file>`; a kind with no row needs a `*` row or a measurement first (failed-cards-2026-09-22 class 11)",
}
CardBaseRemedies is what each base token wants, in the table shape of CardHeaderRemedies, so `nova-swarm lint --rules` prints them beside the rest.
var CardContractPrefixes = []string{"RESULT: ", "RESULT "}
THE CONTRACT LINE HAS ONE FORM, AND THIS IS THE ONE PLACE THAT SAYS SO (issue #1741, SPEC-TOOLWORK.md §5 rule 7).
Line 1 of a card is its contract: the swarm hashes everything below it, records the line at admission, and `gather` refuses a `RESULT.md` whose line 1 differs (deprecated/docs/spec-pulse/10-the-card-as-cut-writes-it.md:12-14). Three readers touch it and two of them disagreed on one character:
- `cut` writes and accepts `RESULT <label> sha=<sha12>` -- no colon (internal/pulse/cut.go, and the example at deprecated/docs/spec-pulse/10-the-card-as-cut-writes-it.md:4);
- `lint --card`'s `result-first` wanted `RESULT: ` -- with one (deprecated/docs/WORKER-CARDS.md practice 1, and deprecated/docs/SPEC-SWARM.md:969,976);
- `gather` compares line 1 to line 1 and imposes no prefix of its own, so it follows whichever the other two settle on (internal/pulse/harvest.go, classifyResult).
The cost of the disagreement: every card `cut` writes drew a `result-first` drift, and six cards written by hand on 2026-09-19 each drew one for a colon.
RULED: THE COLON FORM WINS (Rowan, 2026-09-19, on the cold read of PR #1759; docs/SPEC-TOOLWORK.md §5 rule 7). It is SPEC-SWARM's own law and it is what the majority of writers already write -- `cut --kind` (internal/pulse/cutkind.go), `internal/pulse/manager.go`, `internal/worklang/expand.go`. `RESULT: <label> sha=<sha12>` is the form to WRITE.
THE NO-COLON FORM IS ACCEPTED AS A STOPGAP, NOT AS A SECOND RULE. The one renderer still on it is the plain `cut` template path (`internal/pulse/cut.go`), and the follow-up card named in rule 7 rewrites it and adds the class test `every-writer-and-reader-agrees-on-the-result-line`. Until that card lands, refusing the no-colon form would refuse cards a tool on dev writes today, so both are read -- and it is that class test, never a judgement here, that retires the second entry below. internal/swarm/lintcontract_test.go goes red the day any of the three readers changes form.
The colon form is FIRST in this slice, and it is the only form any message names as what to write.
var CardHeaderRemedies = map[string]string{
"kind-declared": "the card carries `KIND: <kind>` as the first typed line under the contract line, and the kind is one `nova-pulse accept --kinds` names; `cut` writes it from the pool row and a model never does (SPEC-TOOLWORK.md §5 rules 1, 3)",
"paths-declared": "the card carries `PATHS: <glob>[, <glob>...]`, repository-relative, every glob holding at least one literal segment and none of them climbing with `..`; a card that changes nothing says `PATHS: none` (SPEC-TOOLWORK.md §5 rules 1, 2)",
"test-named": "the card carries `TEST: [-tags <tags>] <package> <TestName>` -- the package repository-relative and the name a Go test name -- or `TEST: none <why>` where the kind declares no gate (SPEC-TOOLWORK.md §5 rule 1; the grammar is cardhdr.ParseTest's)",
"paused": "the coordinator paused this kind, so `cut` cuts no card of it and a card launched before the pause is `ACCEPT ABSTAIN reason=paused` at harvest; the remedy is not a rerun but `nova-pulse trust --set trial --queue <dir> --kind <kind> --who <name> --reason <text>` (SPEC-TOOLWORK.md §5 rule 1, eligibility rule 3, §1's abstain list)",
}
CardHeaderRemedies is what each of the four tokens wants, in one line, in the same table shape the twelve older tokens use: a check without a remedy costs a card writer a guess per drift (#1464), and `nova-swarm lint --rules` prints these beside them.
var CardUsageColumns = []string{
"job", "attempt", "started", "ended", "end", "rc", "provider", "model",
"tokens_in", "tokens_out", "cache_write", "cache_read", "reasoning", "usd",
}
CardUsageColumns are the fourteen columns of one card's usage.tsv, in this order. The order is the contract between the native run that writes it and the batch that sums it.
`end` JOINED THEM WITH SPEC-SWARM RULE 13d (issue #1545). The word was being computed by every native launch and thrown away: `writeNativeUsage` has set `row["end"]` since issue #644's follow-up, and there was no column to put it in, so `end=wall` for a card the wall stopped -- and now `end=budget` for one a budget stopped -- reached no reader at all. Rule 13d requires it by name: "The launch a budget ended has `end=budget` in its row", and demanded test 13d reads it there.
IT IS INSERTED WHERE RULE 12 PUTS IT, after `ended` and before `rc`, so that a person who knows the pool's sixteen-column file reads this one without relearning it. THAT IS SAFE FOR OLD READS, and it was checked before it was done: every reader of this file in this repo maps its columns BY HEADER NAME and says so in terms -- `cmd/nova-tokens`'s `readCardFile` ("mapping its columns by the header so the reader never depends on a fixed index"), `internal/pulse`'s `parseProgressUsage` and `status`'s own index. A file written before this change has thirteen columns and no `end`, and every one of those readers answers the empty string for it, which is what an absence is.
var ErrInvalidTimeout = errors.New("timeout outside valid bounds [5s, 2h]")
ErrInvalidTimeout indicates a duration violates the clamping bounds.
var ErrNoCard = errors.New("queue is empty")
ErrNoCard is what a pull returns when the bench store's queue/ holds nothing to take. It is not a refusal: an empty queue is an idle puller.
var ErrNoSQLite = errors.New("USAGE REFUSED reason=no_sqlite")
ErrNoSQLite is the class of a usage source whose one program is not on PATH. It carries the literal refusal line so every caller that surfaces the error says the same thing and a missing reader is never a dash nobody explained.
var ErrStageTimeout = errors.New("stage-timeout")
ErrStageTimeout is returned when card staging exceeds the hard timeout.
var InputLimitPhrases = []string{
"input token limit exceeded",
"prompt is too long",
"maximum context length",
"context length exceeded",
"reduce the length of the messages",
"input length and `max_tokens` exceed",
"request too large",
}
InputLimitPhrases is the table this detection reads, and it is a TABLE because every provider says this in its own words and the tool meets a new provider every time a worker description changes. A description may name more (`input_limit_phrases`), and what it names is ADDED to these rather than substituted for them: a caller teaching the tool one provider's sentence cannot silently un-teach it another's.
EVERY PHRASE IS REFUSAL-SHAPED, in a provider's own words. The lesson is `refusalMarks` (worker.go, dogfood D13): a harness log is a TRANSCRIPT -- the diff the worker read, the spec it quoted, its own prose -- and the bare words `input limit` or `context window` appear in a clean read of any spec that discusses them. A diagnosis that fires on the word for the thing is noise in the one field a reader was told to trust.
var ProviderErrorMarks = []string{"error", "err", "fatal", "exception", "rejected"}
ProviderErrorMarks are how a harness says THIS IS THE PROVIDER TALKING, and a phrase counts only on a line whose own LABEL is one of them, or directly under such a line.
THE PHRASE ALONE IS NOT ENOUGH, because a harness log is a TRANSCRIPT and the sentences in the table above now live in this repository's own source, README and spec: a worker reading nova-tools quotes `input token limit exceeded` in its RESULT.md, and if it then died of a real 429 the whole transcript search would call that death an input limit and refuse it the retry a 429 has earned (Fable's read of #150, finding 3). It is the D13 rule one file over: a diagnosis that fires on the word for the thing, wherever it appears, is noise in the one field a reader was told to trust. Every real specimen carries a mark -- OpenCode's `Error: `, Anthropic's `API Error: 400 {"type":"error"...}`, OpenAI's `Error code: 400 - {'error': ...}` -- and a harness that puts the mark on its own line above the message is why the line ABOVE counts too.
var ProviderRetryDelay = func(failed int) time.Duration { if v := strings.TrimSpace(os.Getenv("NOVA_SWARM_PROVIDER_BACKOFF")); v != "" { if d, err := time.ParseDuration(v); err == nil && d >= 0 { return d } } lo, hi := 5, 20 if failed >= 2 { lo, hi = 30, 60 } return time.Duration(lo+randBelow(hi-lo+1)) * time.Second }
ProviderRetryDelay is the wait before the retry of a launch that just failed: 5-20s jittered after the first fast failure, 30-60s after the second. The jitter keeps a fleet that died on one provider wobble from retrying in lockstep. NOVA_SWARM_PROVIDER_BACKOFF pins the delay for a test.
var ResultLintRemedies = map[string]string{
"line2-grammar": "line 2 is exactly one of `DONE`, `ABSTAIN <why>`, `BLOCKED <why>`, `RED <line>`, `GAP <why>` or `CONFORMS <where>`, at column 0, on its own line",
"template-copied": "line 2 holds the template's `<-`: write the ONE disposition you reached, not the template line that lists them; a copied `DONE <- or: ...` is never counted as DONE",
"blocked-head-is-base": "`BLOCKED head=<base-sha>` says only that head is the base, which is the expected state of a card that has not committed yet; do the card's work, or name the actual obstacle after BLOCKED",
}
ResultLintRemedies is every check this lint makes, with what the writer should have written. One table: a check without a remedy is caught by the test.
var TimelineColumns = []string{
"t_start", "t_end", "tool", "wall_ms", "input_tokens", "output_tokens",
}
TimelineColumns are the six columns of one card's timeline.tsv, in this order: the span's two ends, what ran, its wall in milliseconds, and the turn's token counts (empty where the harness reported none).
var TokenColumns = []string{"tokens_in", "tokens_out", "cache_write", "cache_read", "reasoning"}
TokenColumns are nova-tokens's five types, which are the five this tool sums.
var UsageColumns = []string{
"job", "attempt", "from", "started", "ended", "end", "rc", "provider", "model", "repo",
"tokens_in", "tokens_out", "cache_write", "cache_read", "reasoning", "usd",
}
UsageColumns are the sixteen columns, in this order. The order is the contract.
Functions ¶
func ASDFOutputTranslations ¶
ASDFOutputTranslations points compiled Lisp output at the bench-shared cache without sharing the harness's whole XDG cache or its per-card HOME.
func AbortedPath ¶
func AbsResolved ¶
AbsResolved is a path made absolute and then symlink-resolved: the one spelling admission records for a slot or a root, so every later compare -- the wall's argv, a containment check, a test's expectation -- is against the same name the filesystem itself uses.
Absolute alone is not enough. On darwin `t.TempDir()` and `$TMPDIR` are handed out under `/var`, which IS a symlink to `/private/var`, so `filepath.Abs` of a relative spelling resolves it (the cwd `os.Getwd` returns is already resolved) while an absolute spelling passes through unresolved -- one directory under two names, and a run's own paths disagreeing with each other depending on how the caller typed them (issue #578).
A path that does not exist YET -- the slot a run is about to create -- cannot be resolved whole, so its deepest existing ancestor is resolved and the rest of the spelling is kept. That is the same answer the path will have once it is made, short of a symlink introduced later inside it.
func AcceptanceUnknown ¶
AcceptanceUnknown reports that this job's provider read may have been accepted and then lost. The marker file is the record. A marker that cannot be read is the same hold: a missing record must not become an ordinary retry. The runner's own verdict line is the second record, for the run whose marker could not be written.
func Admission ¶
Admission is how many workers a bench with this capacity line and this many already on it may still start, never more than the cards waiting and never negative. A full bench admits nothing.
func AdmissionLine ¶
AdmissionLine is section 7's capacity line, in whole workers and whole numbers. It is section 2's CapacityLine (queue.go) read on integer probe numbers rather than measured floats, and it is named apart so neither section borrows the other's rounding: the number of workers nova-swarm pull may start on a bench, min(cores*1.5 - load1, (free_gb-25)/2, memfree_gb/2), floored at zero so a loaded, full or memory-starved bench admits nothing rather than a negative count.
func AdmitsCommand ¶
AdmitsCommand reports whether a harness bash line is on the harness-prompt allowlist. It is not command confinement and not the OS wall: compound lines (`;`, `&&`, `||`, `|`, backtick, `$()`, newline) are refused, and a first token containing `/` is refused, so `git status; curl` and `/usr/bin/git` do not pass as `git`. Background `&` and process substitutions further show this is not a shell parser; the hint is explicitly non-authoritative.
func AdmitsFetch ¶
AdmitsFetch reports whether a webfetch is in the wall. It is never in the wall: S7 is no web. The argument is the URL a test names.
func AfterGateFailure ¶
AfterGateFailure is the escalation rule (Glenn, 2026-09-18: "if we fail then we automatically promote up the intelligence ladder"). A card that failed its gate re-enters the SAME decision carrying that failure as CONFIRMED evidence, and the ladder does the rest: the rung that failed is out of the eligible set and so is its lineage at that height, so the answer is another lineage on the same rung where there is one -- sideways before up -- and the rung above where there is not. It is never a retry on the rung that failed.
The reason travels in the unit and never to a provider: the public projection carries an attempt COUNT and nothing else.
func Alive ¶
Alive reports whether a pid names a live process. Signal 0 asks the kernel and sends nothing; EPERM is a live process this user may not signal, which is still alive.
`started` is the identity the caller recorded for that pid. It is WINDOWS's business, where a pid is re-issued the instant its holder ends; here rule 17's own comparison (slot.go) is the place a start stamp is read, and this call ignores it.
func AppendCardUsage ¶
AppendCardUsage appends one attempt's usage row to a card's usage.tsv, writing the header first when the file is new (issue #900). A native run that retried a launch writes one row per attempt -- attempt=1,2,3 for one card -- so the file holds the header and one row per launch, and a reader folds them. A field the provider did not report stays a dash, never a zero, exactly as in the single-row writer.
func AppendHarvestGate ¶
func AppendHarvestGate(body string, r LegGateResult, max int) string
AppendHarvestGate composes the PR body the harvest publishes: the card's own body first, VERBATIM and untouched -- a card's self-reported gate is data, and it stays in the body as the card wrote it -- then the harvest's gate section under HarvestGateHeading, bounded to max bytes (GateSectionBytes when max is 0 or negative).
THE HEADING IS THE HARVEST'S. A `## Harvest gate` section already in the body -- one the card typed into its RESULT, or one an earlier harvest wrote when the PR was updated in place -- is stripped before the harvest's is written, so the section is rewritten and never stacked, and a card-written gate line never survives as the harvest's.
func AppendNote ¶
A JOB HAS A NOTE FILE, AND ITS REPORT COUNTS THE NOTES IT READ (rule 10).
On 2026-09-11 a running child could not be redirected: there was no path a message could take, and the only thing that could be done to a child doing the wrong thing was to kill it. So every job directory holds <job>/note, created empty before the worker starts and named in the prompt; the coordinator appends to it, one line per note, stamped by the tool; nothing else writes it.
The COUNT is mandatory, because a job that ignored a note cannot be told apart from one that got none, and a coordinator who cannot tell the difference cannot redirect anything.
A NOTE IS DATA TO THE WORKER AND NEVER AN INSTRUCTION TO THIS TOOL: nothing here reads a note back, and nothing in this package branches on what one says.
func AppendPromptDefect ¶
AppendPromptDefect writes the PROMPT-DEFECT line into the task's report, creating the report where the worker published none. A line for this task is written once: a report that already carries one is left alone.
func ApplyProviderReadDeadline ¶
ApplyProviderReadDeadline returns the job config with the model's provider carrying headerTimeout and chunkTimeout, in milliseconds. Those options are not the body-read deadline; see ProviderBodySilence. A body that is not a JSON object is returned unchanged. An absent provider entry is created with only those two options, so a built-in provider still receives them. An apiKey already in the options is left as it was.
func Asked ¶
Asked reports the question a run ended on, and false when it ended any other way.
FOUR CONDITIONS, ALL OF THEM NECESSARY, and each one is a different card this must not claim:
- the last thing the card said is a question. A card that ended on a sentence did not ask, whatever else it did.
- the child exited 0. A crash is a crash: a non-zero exit is already its own end (`rc=<n>`), and a model that asked and then fell over is not waiting for an answer.
- no `RESULT.md` was found where the gather looks for one. A card that published owns its report, and a question in its last turn is prose in a finished card.
- `./repo` holds no commit past its base. A model that asked rhetorically and then went on and committed the work did the work; the question was narration, not a stop.
func AskedEnd ¶
AskedEnd asks Asked's question of a finished job on disk: the capture's own tail, the gather's own result lookup, and ./repo's own commit count. The rc is the caller's, because the run holds it and the job directory does not.
func AskedReport ¶
AskedReport reports whether the `RESULT.md` at path is the one this file wrote for a card that ended by asking, rather than a report a card published.
IT ASKS FOR BOTH MARKS -- the `RESULT: ASKED ` opening and the `written-by:` line -- so the answer rests on the whole shape this file writes and not on an opening a card could type. It exists so that writing this report does not silently promote a card's verdict: the absence of a result is what `no-result` is, and a report the machinery wrote for a card that asked is still an absence of the card's own.
func Backoff ¶
Backoff is the wait before retry n, where 1 is the first retry: the base doubled n-1 times and capped at five minutes. A base of zero or less takes the 30-second default, so a caller that names no backoff still gets the spec's own.
func Batch ¶
func Batch(in BatchInput) int
Batch runs one batch through scatter, wait and gather and returns the process exit code: 0 only when every card was done and none held, 1 otherwise, 2 when the admission could not even be read.
func BudgetWord ¶
BudgetWord is rule 13's ceiling and what was observed under it, in the four spellings the document names, and it is the ONE place either route renders them:
unmetered the caller said this provider has no live accounting
-/<n> nothing was observed, so the run is never reported as under budget
<s>+/<n> a PARTIAL observation: some columns were dashes, so it can show that the
budget was reached and can never show that the job stayed under it
<s>/<n> a whole observation
THE POOL'S `RUN DONE`/`RUN KILLED` AND `native`'s `NATIVE OK` BOTH COME THROUGH HERE (rule 13d: "Everything rule 13 says about the word, the sum, the stop, the record and a source that cannot be read holds for a native card"). Two renderings of one field is how one of them drifts, and a reader who learned the field on one line would misread it on the other.
func BuildContainerArgs ¶
func BuildContainerArgs(engine, image, workDir, model, label, cardContent, secretKey string) []string
BuildContainerArgs constructs the exact podman/docker run command per infra/image/README.md.
func CacheEnv ¶
CacheEnv is the cache environment the harness child carries: one cache root for every job under one swarm root, so N workers do not each download the same toolchain and modules. sourceRoot optionally names the job checkout ASDF maps into its private Lisp overlay.
func CapacityLine ¶
CapacityLine is a bench's capacity line: min(cores*1.5 - load1, (free_gb-25)/2, memfree_gb/2), floored to a whole number of workers and never negative (deprecated/docs/SPEC-JOBS.md section 2). A non-positive cores count is a bench with no pinning and no bound on the load term, so the line is the smaller memory term.
func CardCacheRead ¶
func CardCacheRead(u ProviderUsage) (int, bool)
CardCacheRead is the observed cache_read as a number, and whether it was one.
func CardCloneRepos ¶
CardCloneRepos returns the distinct owner/repo pairs a card's text names as clone URLs, in order of appearance: every `git clone <url>` operand that names github, plus every bare `github.com/<owner>/<repo>` mention.
func CardHeaderChecks ¶
func CardHeaderChecks() []string
CardHeaderChecks is every token this file draws, in one byte-stable order.
func CardIdleLine ¶
CardIdleLine is the typed line a card ended for idleness is reported on when NO refusal stopped it -- a provider turn that never answered, a tool call that never returned:
CARD IDLE task=<id> step=<n> idle=<n>s
It is deliberately not a WALL line. `js-under-20-bytes` died in a provider stall and was reported `WALL task=... path=/var/db/xcode_select_link`, and a shift went looking at the wall for it.
func CardKindFromText ¶
CardKindFromText reads a card's kind from its typed KIND: header or a :kind pull field. An untyped card is "".
func CardNamesRepo ¶
CardNamesRepo reports whether the card names a repository at all, readable or not: such a card that ends with nothing staged is a staging failure (STAGE FAIL reason=no-repo-staged).
func CardParentPaths ¶
CardParentPaths returns the 1-based numbers of the card lines that reach above the job, in line order, at most one per line. It reads the lines it is handed and nothing else.
func CardPrompt ¶
CardPrompt is the message a native run hands its harness for a card: the card text byte for byte and, for a typed card (a KIND: one of typedrec.Kinds), the RESULT-FORMAT paragraph typedrec.ResultFormat renders. Since nova-tools#3689 that is the two-line contract: line 1 verbatim, line 2 DONE, ABSTAIN or BLOCKED, an optional note (and, for a kind that needs them, the fields only the model can know); the card wrapper writes every other field (typedrec.Synthesize). A card of any other kind is its own prompt, unchanged.
func CardReadPaths ¶
CardReadPaths is every absolute path a card named on a `READ:` line, in the order the card names them, deduped. Only absolute paths are taken: a relative one is inside the job (which is already internal) or is a word in a sentence, and neither is a fence rule.
func CardRepoURL ¶
CardRepoURL is the clone URL a REPO: value names: a URL or a local path as written, an owner/name as the forge's https URL ending .git (the shape base-repo: lines and the URL fallback carry, so FindBenchMirror resolves all three to ~/nova-bench/mirror/<name>.git). "" when the value is none of those.
func CardStageBranch ¶
CardStageBranch is the branch the staged checkout is on, so the card commits on a named branch rather than a detached HEAD: rowan/<label> from line 1 (RESULT: <label> sha=...), rowan/card when line 1 names no label.
func CardUnit ¶
CardUnit reads one card's typed evidence. ok is false where the card names no kind and its contract line reads as none of them: a card nobody can type is a card nobody routes, and today's model stands.
func CheckKillPoint ¶
func CheckKillPoint(point string)
CheckKillPoint is a no-op in the release build: it never reads the environment and never kills. The kill injection point exists only in the swarmtest build.
func CheckPausePoint ¶
func CheckPausePoint(point string)
CheckPausePoint is a no-op in the release build: it never reads the environment and never stops. The pause injection point exists only in the swarmtest build.
func CheckPublicCard ¶
CheckPublicCard applies the gate: a non-public worker admits everything. A public worker admits a card only when every clone URL's owner/repo is listed in <root>/public-repos.txt. It returns the offending repo and true when the card is refused; a missing allowlist refuses every card (the first repo where the card names one, "-" where it names none).
func CheckStagedTree ¶
CheckStagedTree refuses a staged tree that holds a way out of itself: an absolute symlink, or a symlink resolving outside the job root. The error names the link, because the launcher refuses by path.
func ClampTimeout ¶
ClampTimeout clamps any raw duration to the bounded interval [MinExecutionTimeout, MaxExecutionTimeout] (5s to 2h). Durations below 5s are clamped to 5s; durations above 2h are clamped to 2h.
func ClampTimeoutWithDefault ¶
ClampTimeoutWithDefault clamps d if positive, or returns defaultTimeout clamped to [5s, 2h].
func ContextualReadGlobs ¶
ContextualReadGlobs is the dispatcher-approved read set that is not a write: `docs/SPEC-*.md`, same-package siblings of a PATHS glob, `testdata/` under the same directory, and the TEST: package's `*_test.go` files.
func CoresList ¶
CoresList parses the cores column into the sorted cores it pins, or nil for "-". A list that does not parse (an empty item, a non-number, a reversed range) is an error, which is how the table refuses it.
func Cost ¶
func Cost(p *Pool, since string, max int, by string, summaryOnly bool, stdout, stderr io.Writer) int
COST READS <pool>/usage/ AND NOTHING ELSE, which is why it answers after a reclaim.
A fleet can burn a session: one fleet ran about 9M tokens and three parallel workflows hit the limit in 20 minutes (Glenn, 2026-09-10). A pool whose cost is invisible is a pool that is discovered to be expensive by being cut off.
A ZERO IS A MEASUREMENT AND A DASH IS AN ABSENCE. COST OK carries `dashes=` so a total with an absence in it is never read as complete.
func CountCardTurns ¶
func CountCardTurns(jobDir string, u ProviderUsage) int
CountCardTurns counts a job's assistant turns: the harness output log's assistant turns, counted the way usage already counts them (assistant rows/lines), or the usage row count where the log has fewer. A missing log answers the usage row count; both missing is zero turns.
func CountCardTurnsIn ¶
func CountCardTurnsIn(logPath string, u ProviderUsage) int
CountCardTurnsIn is the same count against a NAMED log, because the two routes keep the harness's words in two different files and rule 13d says which is which: "Turns are counted as rule 13b counts them, with `<job>/harness-output.log` as the log, since `native` never writes `harness.log`."
THE NAMES ARE NOT INTERCHANGEABLE. `harness.log` has two owners on the native route already -- a batch pins its runner's stdout to it, which is where the NATIVE OK line lands -- so counting "assistant" in it would count the machinery's own lines and not the model's turns. `harness-output.log` is the capture with one writer (issue #608).
func CountRefusals ¶
CountRefusals reads a harness log and counts its own refusal lines. It reads the file in one pass and never holds more than a line.
func DeniedPathRoots ¶
DeniedPathRoots is the read set a coordinator would have to open IF the denied path was one the child had to read or execute -- a candidate, never a diagnosis. A path reached through a symlink needs TWO roots, because the kernel checks the grant against the resolved target and a coordinator who names one and not the other loses the card again. A launcher in a `bin` directory belongs to the tree above it; anything else is named by its own directory.
Nothing here is specific to Go, to a bench or to a user: every path is read off the denied path itself.
func EffectiveWidth ¶
EffectiveWidth is what a batch fills a bench to: its measured width, or, for a row without one, its core count as today, or -1 for unbounded.
func EnsureCacheDirs ¶
EnsureCacheDirs makes the three shared cache directories, so the child's first cache write lands in a directory that exists inside the write set.
func ExitAttestHash ¶ added in v0.14.0
ExitAttestHash is sha256(secret) in hex, the only form of the attestation that is written into the slot file (a file in neither the read set nor the write set).
func ExitAttestOK ¶ added in v0.14.0
ExitAttestOK is whether a reader holds the secret that produced the slot file's hash. A nonce match without this is a record a worker could have written, and is never reclaim.
func FenceJobPatterns ¶
FenceJobPatterns is every pattern that makes ONE JOB internal to the harness's fence: the job directory and everything under it, in both wildcard spellings, and NOTHING ABOVE IT. A card's own `../scratch` and `../wt-<n>` live under the job directory, which is where the card was told to put them; a sibling job's directory does not, and is not this card's.
func FencePermission ¶
FencePermission is the harness `permission` block one job runs under: everything external is DENIED WITHOUT PROMPTING -- a tool error the model routes around, never the auto-reject that ends the run (issue #918) -- and the job's own paths, plus the read-only paths the card named, are allowed. webfetch is denied as well, for the same reason: a run with no terminal has nobody to answer a prompt, and a prompt here is a dead card. The set is deduped so the file this writes is byte-stable for one job.
func FenceReadPatterns ¶
FenceReadPatterns is every pattern that admits ONE read-only path a card named on its `READ:` line. The harness asks about the path's PARENT with a `/*` on it -- `read` of /sys/kernel/security/lsm asks about `/sys/kernel/security/*` -- so the parent is named beside the path itself, and both wildcard spellings are given for a path that is a directory. A relative path, and the root itself, are named by nobody and dropped.
func FenceRejection ¶
FenceRejection reports the FIRST path the harness's fence rejected in one capture, and whether it rejected anything at all. The first is the one that matters: the model stops at it, and every later line is a consequence of the same missing rule.
func FinalizeByHand ¶
FinalizeByHand is `finalize --task <id>`: the verb for a person, for one ended job whose runner died before doing it. It is REFUSED while the job's process group is alive, and it is a no-op with FINALIZE OK when the file already exists.
func FindBenchMirror ¶
FindBenchMirror finds the path to the bench's local mirror for baseRepo. Candidate locations: - baseRepo itself, if it is a directory on disk that exists - $HOME/nova-bench/mirror/<repo>.git - $HOME/nova-bench/mirror/<repo> - /tmp/<repo>-mirror.git - /tmp/<repo>.git
func FindCardResult ¶
FindCardResult is THE ONE PLACE a card's published result is looked for: the job root first (ResultPath, the name the legacy runner's records use), then `repo/` and one directory below it, exactly as far as `liftResult` copies from (issue #594). A path that is not a regular file is not a result: a planted symlink is not followed and a FIFO is not a published report (issue #233). It is exported because `native` asks the same question before the batch ever gathers -- whether the harness published anything at all (issue #591) -- and a second, shallower lookup there would call a card that published under `repo/` silent about a run that worked. One lookup, one answer, both sides.
func FriendNames ¶
FriendNames returns every name, alias and group the bus roster knows, the list a bench name is refused against: a friend is never a bench.
func FullestBench ¶
FullestBench returns the bench with the most cards in its queue/ among the named bench directories, its queued count, and whether any of them holds a card. Ties go to the lexicographically first name so the choice is deterministic.
func GoBuildCacheDir ¶
GoBuildCacheDir is the shared build cache (GOCACHE) under a swarm root.
func GoModCacheDir ¶
GoModCacheDir is the shared module cache (GOMODCACHE) under a swarm root.
func GroupAlive ¶
GroupAlive reports whether ANY process remains in a group.
func GroupMembers ¶
GroupMembers counts the live processes in a process group other than self, by reading /proc -- the kernel's own table of pid and process group, never a command line.
func HarnessSHA256 ¶
HarnessSHA256 is the lowercase hex sha256 of the harness binary, resolved on PATH when the description names a bare command. An unreadable harness answers the empty string, and the measurement is still written.
func HarnessTail ¶
HarnessTail is the LAST thing the harness said, bounded: the one diagnosis of a failed job, which lived in <job>/harness.log, was printed by no verb, and was then deleted with the job directory by `reclaim`. A line whose key is wrong had no printed route to the word `unauthorized` (the new-user audit, F5, 2026-09-11).
func HashBytes ¶
HashBytes is a revision's identity: the SHA-256 of its bytes. There is no mtime anywhere in this tool -- two revisions with one mtime are two hashes, and an mtime a filesystem rounds is not an identity (rule 16).
func Headroom ¶
Headroom is how many cards one tick may launch: cores x 1.5 minus the one-minute load, never more than cores in one tick, floored at 0. A non-positive cores count is no pinning and no cap: -1, unbounded.
func InputLimitEnd ¶ added in v0.14.0
InputLimitEnd is the ONE place a job's end becomes `input-limit`, so that the dispatcher's own `finish`, the start-up pass that recovers a dead dispatcher's job (rule 17) and the supervisor all name it the same way. It returns the end and the provider's words.
WHICH ENDS IT MAY REWRITE is the D5 question asked of the input limit (finish.go, rateLimitedOutcome): a limit the harness met, backed off from and then answered past is HISTORY -- the completion evidence the supervisor wrote is the outcome -- and a reap's own end (the deadline, the budget ceiling, an unreadable usage source) is the supervisor's verdict from inside the job and is never written over. AND THE SENTENCE HAS TWO SOURCES, IN THIS ORDER: the harness log, and the supervisor's own `exit.json` record (`recorded`) when the log gives nothing. The supervisor classified this job from inside it and wrote the words down; a log that cannot be read at finish -- a Windows replace window, a directory already reclaimed, a disk that moved -- left the class standing with `sc.Limit` empty and `TRIAGE INPUT-LIMIT … : -`, which is the one line this class exists to print (Fable's read of #150, finding 2).
func InputLimitSignalLine ¶
func InputLimitSignalLine(sig InputLimitSignal) string
InputLimitSignalLine is the structured line the supervisor emits from a signal it read: the one line the next reader trusts, `class`, the measured `value` and the `limit` it hit.
func InputLimited ¶ added in v0.14.0
InputLimited reports the provider's own words when the harness log says the request did not fit, and whether it said so at all. A phrase counts only on a line whose own label is a provider error mark, or on the line directly under one (ProviderErrorMarks, MarkWords): the table's sentences appear in transcripts that merely QUOTE them, this repository's own docs and a worker's own RESULT.md included.
The quote is the LINE the phrase was found on, with the terminal paint stripped: OpenCode wrote `\x1b[91m\x1b[1mError: \x1b[0mRate limit reached: ...`, and a line that carries the escapes through reads as `\x1b[91m` on the one line a person is meant to read. The quote is bounded to a tail (oneline.TailBytes), because a harness log is 31KB and this is a field on a one-line event.
func IsCardContractLine ¶
IsCardContractLine says whether line 1 of a card is a contract line in either form. The prefix is anchored at column 0: an indented or quoted RESULT is prose.
func IsCardTemplate ¶
IsCardTemplate reports whether name is one of the task templates a card is built from (read-pr, probe-row, fix-card). The other names Template answers to -- result, worker, setup, capacity, and the pulse card templates of SPEC-PULSE rule 4 -- are not cards: result is the report's shape, worker is a JSON worker description, setup and capacity are forms, and the pulse names are the cards nova-pulse `cut` renders, all printed verbatim for their own purpose.
func IsPowerOfTwo ¶
IsPowerOfTwo reports whether n is 1, 2, 4, 8, ...: the only widths a bench is ever measured at.
func IsPublicRefusal ¶
IsPublicRefusal reports whether an admission reason came from this gate.
func IsPulseTemplate ¶
IsPulseTemplate reports whether name is one of the pulse card templates or the cost table nova-pulse `cut` reads (SPEC-PULSE rule 4). They are cards cut renders, never task templates to wrap.
func JobASDFOutputTranslations ¶
JobASDFOutputTranslations sends a card's writes to its own overlay. The overlay is seeded from the exact-tip prewarm cache before launch; cards never write that seed or another card's compiled output.
func JobLispCacheDir ¶
JobLispCacheDir is the private writable overlay for one card. It lives beside the card's checkout, inside the already-owned job directory, and is reaped with that job.
func JobName ¶
JobName is a DNS-1123 label for the card's Job: lower case, [a-z0-9-], at most 63 characters, starting and ending with an alphanumeric.
func KeyFileMode ¶
KeyFileMode reports whether the key file is readable by anybody but its owner. It is a NOTE rather than a refusal: a mode this tool refused over would be this tool deciding how somebody else's machine is administered, and a mode nobody mentions is how a key ends up world-readable.
func KnownStamp ¶
KnownStamp reports whether a start timestamp is valid and populated (not empty and not Dash).
func LaunchArgv ¶
LaunchArgv returns the argv for launching a provider on the given OS. The argv is the harness binary followed by the expanded harness arguments: {model} is replaced with the provider's model from the table, {title} with the provider name, and {prompt} with the literal "PROMPT.md". Unknown providers return an error rather than a guessed argv -- a launcher that guesses is a bespoke launcher with extra steps.
func LaunchArgvFor ¶
func LaunchArgvFor(provider, goos string, req LaunchRequest) ([]string, error)
LaunchArgvFor is LaunchArgv for one run: the table's row for provider gives the argv shape, and the request's non-empty fields fill it. This is the one launcher: `nova-swarm native` builds every harness argv here (deprecated/cmd/nova-swarm nativeLaunchArgv), so the table, not a per-provider script and not a literal in the caller, decides the shape a provider is launched with. The table declares OS-specific columns (linuxHarness, darwinHarness) and only those differ between the two OSes.
func LaunchGrace ¶
LaunchGrace is the worker description's own, or the default where it names none.
func LaunchRecordHash ¶
func LaunchRecordHash(r LaunchRecord) (string, error)
func LaunchRecordPath ¶
func LaunchRow ¶
LaunchRow is the table row a provider launches with: its own row when the table names it, and DefaultLaunchRow otherwise. A table that cannot be read answers the provider itself, so the caller's LaunchArgvFor reports the read error.
func LegGateLine ¶
func LegGateLine(r LegGateResult) string
LegGateLine is the gate's one line: the harvest, the bench it ran on, the sha it ran at, the verdict, and the counts. It is the line the PR body carries under HarvestGateHeading and the line a reader may trust; a card's own gate claims never appear on it.
func LispCacheDir ¶
LispCacheDir is the shared ASDF output directory under a swarm root.
func LoadHeadroom ¶
LoadHeadroom is the one term of the capacity line the puller enforces itself: cores*1.5 - load1. At or below zero the puller declines to submit.
func LookSandbox ¶ added in v0.12.0
LookSandbox resolves the wall's binary. A path the caller typed is used as typed; an empty one is the tool's own name on PATH. A miss is a REFUSAL naming the remedy, never a silent run without a wall: rule 11's --no-sandbox is the one workaround and it is typed by a person, never inferred by this tool.
func LostResponse ¶
LostResponse reports that this attempt's output is a provider read that ended after the request may have been accepted. The caller must not launch the card again on that signal.
func MakeSlotLease ¶
MakeSlotLease writes one lease directory by publish (atomic) for tests and for fixtures: the pid and until are the caller's, not the taker's.
func MeasureWidthFromRounds ¶
MeasureWidthFromRounds is the doubling loop over already-run rounds: the width is the last W that held, and the doubling ends at the first round that breaks a rule. No round held is width 0.
func MergeFencePermission ¶
MergeFencePermission returns the config bytes the job's harness reads: the provider config the caller carried (raw, which may be empty when `--config` named none) with this job's permission block merged into it. A config this side cannot parse is returned unchanged and ok=false -- the copy is not this function's to refuse (a provider config is carried verbatim, #465) -- and the caller says on stderr that the fence went unconfigured.
A permission block already in the carried config is KEPT and added to: a person's own `read` or `bash` rules are theirs, and only the external_directory patterns this job needs are written over.
func MirrorCloneArgs ¶
MirrorCloneArgs is the git argv of the one staging convention, for cards (StageCard) and for ci run (internal/nsprint/ci): a local clone of the bench mirror that borrows its objects and dissociates, so staging never reads the network and a later gc of the mirror cannot take objects from under the clone. noCheckout leaves the worktree empty for a caller that checks out an exact sha next.
func MirrorDue ¶
MirrorDue reports whether an idle worker's steal is due: the bench mirror fetches on a five-minute timer, so a worker steals at most once per MirrorTimer. A zero lastSteal is a worker that has never stolen and is due.
func NPMCacheDir ¶
NPMCacheDir is the shared npm cache (NPM_CONFIG_CACHE) under a swarm root.
func NativeBudgetSourceRefusal ¶
NativeBudgetSourceRefusal is the reason a native launch is refused for a budget nothing can observe, or "" when the launch may proceed. The caller prints it as a NATIVE REFUSED line and exits 2, BEFORE any directory is made.
`tokens` and `unmetered` are the budget word as read; `cardBudget` is whether a worker description set `max_turns` or `max_cache_read`, and `cardBudgetNames` names which, so the refusal tells a caller which field of their description put them here rather than making them guess.
func NativeUsageIntervalRefusal ¶
NativeUsageIntervalRefusal is the reason `native` refuses an interval, or "" when it takes it. The ceiling is the deadline and it is EXCLUSIVE: an interval equal to the deadline would let no sample run at all, because the deadline ends the card at the instant the first sample would have been due.
func NativeUsageSource ¶
NativeUsageSource is the source a native launch reads its budget from: the worker description's `usage` when `--worker` names one, and `opencode` when there is none. It is one function because "and `opencode` when there is no `--worker`" is a sentence of rule 13d's and not a default anybody should retype.
func NewExitAttest ¶ added in v0.14.0
NewExitAttest mints the per-launch attestation secret, drawn from the OS random source in the supervisor's own memory and never written anywhere a worker can read. Only its hash (ExitAttestHash) reaches the slot file before the launch, and the secret itself reaches <job>/exit.json only inside endWith, after the job's process group is dead.
func NewID ¶
NewID is the task id: a UTC stamp, the label, and a random half, so that two adds in one second cannot collide -- nova-bus's id lesson, which cost a lost note.
func NextRoute ¶
NextRoute is the route after failed in list order, wrapping at the end, never failed itself; a failed route not on the list hands back to the list's first other route. "" when the list has nowhere else to go.
func Nonce ¶
Nonce is twelve hex characters from the OS random source, drawn once per launch and never reused. It is what makes a stale supervisor's identify fail against a reservation that has moved on.
func NormalizePath ¶
NormalizePath is rule 15's normalization, in one place.
func OpenCodeStoreLocations ¶
OpenCodeStoreLocations are the paths OpenCode may keep its database at inside ONE job's data home, in the order the reader tries them: the XDG_DATA_HOME spelling this tool exports, then the HOME/.local/share spelling OpenCode derives from HOME on Linux -- the bench carries its auth.json there. Both are joined to the job's own data home, so both are this job's database and never another job's.
func OverMaxInput ¶ added in v0.14.0
OverMaxInput is the task budget checked BEFORE the launch: `max_input` is the ceiling a task names on the prompt this tool hands the harness, and the refusal carries the MEASURED size beside it.
IT IS A COUNT OF BYTES, not of tokens, and it is named for what it is: this tool has no tokenizer, and a token count it guessed would be a guess about somebody else's provider (SPEC.md, no guessing). It bounds the one thing the dispatcher can measure -- the PROMPT, which carries the task text -- and not the files the worker then opens; those are `--files`, and the class above is what happens when they do not fit anyway.
func OwedMatch ¶
OwedMatch reports whether a finding matches an item on a pull request's owed list. The match is the owed item's own text, compared without case and without punctuation, because a worker retyping an owed line is not expected to retype its commas.
func OwnedCards ¶
OwnedCards lists the cards a worker already holds on a bench: the taken/<worker>-<name>.card ownership records. A worker drains its own taken/ before it reaches for another bench.
func ParseCardBase ¶
ParseCardBase extracts the base repo and base sha from the card text (ReadCardBase). ok is false when the card names no repo that can be staged.
func ParseRouteList ¶
ParseRouteList reads RoutesEnv's value: routes separated by commas or white space, empty entries dropped, first occurrence winning.
func ParseSlotRange ¶
ParseSlotRange parses --slots: "" is no range at all (allocate from 1, no ceiling), "<n>" is 1-<n>, and "<lo>-<hi>" is itself. lo is where allocation starts and hi is the last slot it may take; hi 0 means no ceiling.
func PickRoute ¶
PickRoute chooses the worker description path for (kind, rounded complexity): rows of another kind never match, public-class rows are skipped when touches_private >= 0.5, an exact complexity wins, otherwise the nearest lower complexity for that kind, otherwise the default.
func PlanBenchLaunch ¶
PlanBenchLaunch fills a bench up to its width and drops down under load: at most width cards, at most headroom cards, at most queued cards. A width of 0 is an unmeasured bench and fills by cores as today; a non-positive cores count caps by width only.
func PointProviderAtProxy ¶
PointProviderAtProxy returns the config with that provider's baseURL set to the proxy. The other options, including an apiKey, stay. ok is false when there is no baseURL to replace: the caller must not claim the harness was pointed at the proxy.
func PrepareLispJobCache ¶
PrepareLispJobCache makes a private copy-on-write seed for one card. A missing prewarm seed is valid and produces an empty overlay; the card will compile there without contaminating any sibling.
func ProfileJobs ¶
ProfileJobs reads every timeline a job glob names and prints one PROFILE line per job and one PROFILE SUMMARY line with the mean per phase. A glob match may be a job directory (its timeline.tsv is read) or the timeline file itself; a match with no readable timeline is skipped. The command exits 0 even when the glob matches nothing, printing a zero summary, because an empty fleet is a measurement and not a refusal.
func Prompt ¶
func Prompt(in PromptInput) []byte
Prompt assembles the worker's prompt. Every sentence in it is a failure from the record.
func PromptDefectLine ¶
PromptDefectLine is the report line a budget stop writes into the task's report. One line, the one-line output grammar's fields, no payload.
func PromptSize ¶ added in v0.14.0
PromptSize is the size of the prompt this tool handed the harness, read back from the job directory -- the one input size the dispatcher can measure, and the thing `max_input` is a ceiling on. It reports whether it could measure at all, so the two callers can differ where they must: the pre-launch check reads an unmeasurable prompt as no bytes, which no ceiling refuses -- a check the tool cannot make is never a refusal it invents -- and the line prints the dash that is rule 12's word for an absence, never a zero.
func ProviderBaseURL ¶
ProviderBaseURL is the model's options.baseURL, or empty when the config has none. The proxy is only installed when this is an http endpoint.
func ProviderLaunchFailure ¶
ProviderLaunchFailure says whether an output tail is a provider server error, and returns the provider's ref where the tail carries one. It is the ONE classifier: `run`'s supervisor, the dispatcher's finish and the native runner all ask it the same question.
func ProviderProxyEligible ¶
ProviderProxyEligible reports whether baseURL is an http(s) endpoint the proxy can stand in front of. Anything else is left for the harness as given.
func ProviderRetryAfter ¶
ProviderRetryAfter reads the interval a provider named in its own output: the token `retry-after`, then a whole number of seconds. A log that names none returns false, and the caller falls back to --backoff.
func PublicRefusalLine ¶
PublicRefusalLine is the one refusal line the gate prints.
func PublicRefusalWhy ¶
PublicRefusalWhy is the text after "CARD REFUSED ": the fixed reason, the offending repo, the class and the worker name, each through oneline.Field.
func PublishEvidence ¶
func PublishEvidence(evidenceDir, jobID string, task, prompt, profile []byte, preimage string) error
PublishEvidence writes the three evidence files then MANIFEST last, atomically no-replace, never following a symlink. An existing complete set with identical verified bytes is an idempotent replay; differing content under the same job ID refuses. (SPEC-SWARM-PROFILES, lines 344-365)
func PublishLaunchRecord ¶
func PublishLaunchRecord(root, jobID, nonce string, r LaunchRecord) error
func PullBatch ¶
PullBatch takes up to Batch cards of one kind and one repo from Queue, runs each on the shared clone, clips between cards, and returns the process exit code: 0 when the turn ran, 2 when it could not. A card over its :effort stops the batch and returns that card and every card behind it to queue/.
func PullLanes ¶
func PullLanes(in PullLanesInput) int
PullLanes drains the lanes in order and prints one line. Every path comes from a flag; a queue that cannot be read is a refusal with one remedy line.
func PullQueue ¶
PullQueue takes up to admit cards from a bench's queue/ by renaming each into taken/ as <worker>-<name>.card, the ownership record (deprecated/docs/SPEC-JOBS.md sections 2 and 7). The rename is atomic within the directory, so two workers cannot take one card. A full bench (admit 0) reads nothing and leaves every card in queue/.
The names are sorted before the take, so a test and a reader see the same order; the take stops at admit, so the bench never crosses its capacity line.
func QuarantineCard ¶
func QuarantineCard(queueDir, reason, cardID string, cardContent []byte, meta QuarantineRecord) (string, error)
QuarantineCard atomically places a card into the quarantine hierarchy: <queue>/quarantine/<reason>/<card-id>/.
func QuarantineCardDir ¶
QuarantineCardDir returns the directory for a specific quarantined card: <queue>/quarantine/<reason>/<card-id>.
func QuarantineDir ¶
QuarantineDir returns the quarantine root: <queue>/quarantine.
func QuarantineReasonDir ¶
QuarantineReasonDir returns the path for a specific failure reason: <queue>/quarantine/<reason>.
func QueueCards ¶
QueueCards lists the cards waiting in a bench's queue/, without the .card suffix, in sorted order so two workers walk the same list and race on the same first card. A bench with no queue/ directory has no cards, not an error.
func QueuedCards ¶
QueuedCards lists the card files in <store>/queue, in name order. A directory or a non-.card file is not a card. A missing queue/ is an empty queue, not an error: a bench with nothing waiting pulls nothing.
func RateLimited ¶
RateLimited reports whether the harness's own output says the provider answered 429.
The status is read from TEXT rather than the exit code, because POSIX truncates 429 to 173: a harness cannot hand the number back through its exit status. The bare number must sit beside a status word (http, status, code, error) or be one of the phrases a provider uses, so a token count of 429 is not read as a rate limit.
func ReadBenchTable ¶
ReadBenchTable reads a benches file: one header line then one tab-separated row per bench, the seven columns name, host, root, cores, harness, auth, wall. A row that does not parse is refused with its line number, and a name used twice is refused too.
func ReadKey ¶
ReadKey reads the first line of the key file and returns the key.
The strips, in order: a leading "export ", then a leading "<VAR>=" for this worker's own variable name, then every whitespace character. An empty result is a refusal carrying the command that writes the file, because a refusal that says only "empty" has moved the guessing onto the reader.
func ReadKeyOrSecret ¶
ReadKeyOrSecret is the key the worker description names, by either half (issue #881): read from the key file, or read from this process's own environment under the name the description's `secret` carries. run and supervise both call it, so the variable must be present and non-empty in EACH process's own environment -- run's at admission, the supervisor's when it inherits that environment and starts the harness. It returns the value and an error that names the variable and never the value.
func ReadOwed ¶
ReadOwed reads an owed list: the items a pull request already knows it owes, one per line, `- ` bullets and blank lines alike. It is a FILE because an owed list is a paragraph a person wrote, and putting it in an argument would put it in the process table (the same reason a task is a file).
func ReadRegular ¶
ReadRegular is os.ReadFile for a worker-writable path: the whole file when it is a regular file within MaxRegularRecord, and a refusal when it is anything else or oversized. A symlink is not followed and a FIFO is not opened (security#30, issue #233).
func Reap ¶
Reap ends a process group: terminate, wait, kill -- and then reports whether anything in it SURVIVED, because a slot whose data home may still have a writer in it is not a free slot, and a pool that re-used it would reproduce the lock failure with a corpse.
func ReapAtTaskEnd ¶
ReapAtTaskEnd is the call `run` makes when a task ends: a worker description that sets keep_data=true leaves every slot's data alone, and one that does not frees the finished slots' data/, tmp/ and jobs/*/scratch under the root.
func ReapExpiredLeases ¶
ReapExpiredLeases returns the cards of expired leases whose pid is gone to queue/ and removes those leases, in lease id order. A lease past until= whose pid is alive is DRIFT and is left where it is; a lease still inside its term is live and is left too.
func ReapSlots ¶
ReapSlots removes, for every finished slot under the root, the slot's data/, tmp/ and jobs/*/scratch, keeping RESULT.md, usage.tsv and the logs. It returns the number of slots reaped and the bytes freed. A slot is finished when a job under it published a RESULT.md, or its newest harness log is older than Older. A live slot -- no RESULT.md and a log that has moved recently -- is untouched.
func RecordBenchWidth ¶
func RecordBenchWidth(path, name string, width int, measured, version string) (cores, rows int, err error)
RecordBenchWidth writes width, measured and version onto the bench's row in place and writes the measured table beside it at <path>.measured: one header line and one row naming the bench, its width, stamp and version. It returns the bench's core count and the table's row count for the BENCH WIDTH line.
func RecordStartupCost ¶
RecordStartupCost writes the known-answer measurement at `<root>/startup-cost.tsv`, a header and one row.
func RecordStartupCostIfAbsent ¶
RecordStartupCostIfAbsent writes the measurement from the first finished task, and only when no measurement is there yet: the first run has nothing to compare against, so it accepts its budget and leaves the next run a fact.
func RefuseFriend ¶
RefuseFriend returns the admission refusal when name is on the bus's friends list and empty otherwise. It writes nothing to the bus: it only answers.
func ReleaseQuarantined ¶
ReleaseQuarantined removes a card from quarantine and restores it to <queue>/<card-id>.card so it can be re-run after defects have been resolved.
func ReleaseSlotLeases ¶
ReleaseSlotLeases removes owner's leases: all of them with all=true, or only the ones carrying label otherwise. It reports the removed count and the owner's leases still held. It never frees a seat whose holder is still running; see ReleaseSlotLeasesForcing.
func ReleaseSlotLeasesByID ¶
ReleaseSlotLeasesByID removes EXACTLY the leases named, and only while they are still the caller's. It exists because releasing by owner and label is not releasing by identity: an owner is a bench and a label is a card's name, and two runs that share both -- two slots, two benches, a retry -- would each give away the other's seat (nova-tools#1546, Stella's hold on PR #1562). A holder that took leases learns their ids from TakeSlotLeases and hands exactly those back here.
The pid is a fence, not bookkeeping. An id whose lease has since been reaped and remade by somebody else must not be removed by a stale list, so each lease is RE-READ and left alone unless its pid is the one given. An id that is simply gone is not an error: a release is allowed to be late, and the caller's job is to stop holding, not to prove nobody tidied up first.
`slots release --owner ... --label ...` stays as it is, by owner and label, because a PERSON at a prompt wants exactly that: free whatever this owner is holding for that card. A person can see the store; a deferred cleanup cannot.
func ReleaseSlotLeasesForcing ¶
func ReleaseSlotLeasesForcing(store, owner, label string, all, force bool) (released, held, live int, err error)
ReleaseSlotLeasesForcing is the body, with the live-seat fence spelled out.
A LEASE IS NOT A TICKET SOMEBODY ELSE MAY TEAR UP (issue #1902). Deleting a lease does not stop the process holding it: the holder keeps running, keeps spending, and the seat it is sitting in is handed to the next taker. Johnny freed a live `native`'s only seat from outside and watched a second `native` take it -- two cards on a capacity-1 bench, both printing NATIVE OK -- and a card given --no-wall did the same to a bystander from inside its own shell. `--owner` is an unauthenticated string and every owner on a shared bench is the same unix user, so who CALLED release is not a fence either. The only fence that means anything is the holder: a lease whose pid is ALIVE and is not this process is KEPT and counted in live.
Releasing your OWN lease is always allowed: `run`'s dispatcher and `native`'s cleanup give back the seat they are sitting in while their own pid is alive, and that is the ordinary end of a run rather than a steal. force is the person's loud override, the way --no-wall is the loud way to ask for no containment.
func RenewSlotLease ¶
func RenewSlotLease(store, owner, label string, dur time.Duration, now time.Time) (renewed int, until time.Time, err error)
RenewSlotLease extends the until= of the lease owner holds under label by dur, and reports how many leases it renewed and the latest new until. It renews by owner and label because the heartbeat is its own process: the pid in the lease is the pull worker's for liveness, not the renewer's identity. A lease whose pid is gone is left for the next take to reap.
func ResultByID ¶
ResultByID is `result --id`: the one path by which a malformed report reaches a person. The body does NOT go through internal/oneline, because the body is the thing asked for.
func ResultPath ¶
func RetriableRateLimit ¶ added in v0.14.0
RetriableRateLimit is the question `finish` asks before it holds a slot and spends a second attempt: a 429 the dispatcher should wait out, and NOT an input that did not fit. The input limit is the more specific truth about a log that says both, and it is the one that decides, because the retry a 429 earns is the one thing an oversized task must not get.
func RouteKey ¶
RouteKey is the provider, model and key a card's requests share a queue with. The key is named by its PROFILE and never by its value: this string is printed on a STATUS line, and a secret that can reach a log is a secret that will.
func RouteQuestions ¶
RouteQuestions is the four questions route asks about the card text.
func RunPullWorker ¶
func RunPullWorker(ctx context.Context, opts PullWorkerOptions) int
RunPullWorker executes the pull worker loop.
func SQLiteOnPath ¶
func SQLiteOnPath() bool
SQLiteOnPath says whether the one program a usage source needs can be resolved. It is asked BEFORE a launch by rule 13d's refusal and AFTER one by the final read, and both ask it the same way.
func SandboxGate ¶ added in v0.12.0
SandboxGate is what `run` does ONCE, before the first worker: it asks the machine what it can enforce and then proves the wall with the real policy. A failure is a REFUSED RUN with no worker started, because a batch that runs unwalled under a green line is exactly the silent sandbox this whole tool exists to prevent.
It returns the reason= token for the RUN REFUSED line and the sentence beside it, or an empty reason when the wall is there. The two reasons are the spec's own (test 23): a machine with no backend is `no_sandbox` and a wall that failed a check is `sandbox_probe`. `notes` is where the one thing this gate can fail at without refusing the run is said: the probe directory it made and could not remove. It is the caller's stderr, never stdout, because a pass prints EXACTLY ONE `RUN NOTE` and that line is the remedy at the end (SPEC-SWARM's output grammar).
func SandboxProbeDir ¶ added in v0.12.0
SandboxProbeDir is the directory the probe of rule 10 writes in, and it is the dispatcher's own: the probe needs a --write whose PARENT is outside every named path, so that the write it expects to be denied has somewhere to be attempted.
It is ABSOLUTE, because `--pool` is used as typed and the README's own invocation types it relative (`--pool ./pool`). Rule 5 of the wall is "paths are resolved, absolute and existing", so a relative pool sent the probe `--write pool/sandbox-probe`, the wall refused it as relative, and `run` refused the whole pass and started NO WORKER for the documented line (DeepSeek's read of #88 at d0c1841, HIGH 1).
func SchedulerAdmits ¶
SchedulerAdmits is how many Jobs of this shape the scheduler places on a node with free_gb of disk and memfree_gb of memory once the floor is reserved: floor(allocatable / request) for each resource, the smaller of the two, never negative. It is the two memory terms of AdmissionLine, enforced by Kubernetes instead of counted by a person.
func ScopeGlobs ¶
ScopeGlobs is declared writes plus contextual reads: PATHS, docs/SPEC-*.md, same-package siblings, testdata under those directories, and `<package>/*_test.go` from a `TEST:` line.
func SecretFromEnv ¶
SecretFromEnv reads the variable a description names as its `secret` from this process's own environment. `nova-secrets exec` is what sets it (docs/SPEC-SECRETS.md, the second caller); the value is never written to a file, never printed, and never in a RUN or SUPERVISE line -- the refusal below names the VARIABLE and the remedy, never the value.
func ShareFromMemory ¶
ShareFromMemory derives how many cards a bench may run at once from its total usable memory and a measured per-card figure, both in bytes: the total is divided by the per-card figure scaled by ShareMemoryHeadroom. It is the memory half of a bench's width -- the number that used to be a guess. A non-positive total or per-card figure derives no share: nothing was measured, or there is nothing to divide into, and 0 is "unmeasured", never a number somebody invented.
func ShellDenialReason ¶
func ShellDenialReason(label, jobDir, wall string, rc int, d ShellDenial) string
ShellDenialReason is the ONE line an unread denial owes its caller, and the one place its words live. It separates what was measured from what was not, by construction:
- MEASURED: the step, the child's exit code, the wall this ran under, the path named, and the shell's own line, quoted.
- NOT MEASURED: which operation was denied. The field says `operation=unverified` and the sentence says why, so nobody reads a cause into it.
- THE REMEDY IS A MEASUREMENT, not a guess: re-run the card's own gate against this commit and read its stderr. On a walled run the read set is offered beside it as one candidate among the others.
`wall` is the wall's own name, or empty for a run that had none; a run with no wall is told so and nothing is attributed to a sandbox that was not there.
func SizeRoundHolds ¶
SizeRoundHolds answers the three rules for one round: load at most 1.25 x cores, throughput at least 1.5 x the previous round (vacuous for the first round, which has no W/2), and no abstain.
func SlotAdmissionWeight ¶
SlotAdmissionWeight is how many share units a card of this kind consumes at take time. Unknown and empty kinds weigh 1, so an untyped launch stays one seat.
func SlotHoldings ¶
SlotHoldings reports how many leases owner holds and the share it holds them within. It is the read `status` prints; it never reaps or grants.
func SlotUtilisation ¶
func SlotUtilisation(store string, now time.Time) (capacity, reserve, held, free int, heldBy map[string]int, shares map[string]int, err error)
SlotUtilisation reports one bench store for `nova-pulse status --slots-store`: capacity and reserve from shares.tsv, held and free after reaping expired leases with a dead pid (expired leases with a live pid are DRIFT and stay held), per-owner held counts and per-owner shares. Free is capacity-reserve-held.
func Slug ¶
Slug reduces a caller's label to the characters an id may hold. An id is a file name, a directory name and a field on an event line, so it holds no separator, no space and no "=" -- and never becomes nothing.
func StageCloneIdentity ¶
func StageCloneIdentity(repoDir string, id StagingIdentity) error
StageCloneIdentity writes the pool's identity into the job clone's LOCAL git config, so the commit the worker makes carries the pool's name whatever the bench's config held. The write itself runs with the bench config blanked, so a hostile bench config cannot redirect where local lands.
func StageJob ¶
StageJob is the launcher's staging: the pool's identity, the clone's local git config, and the no-way-out check, in that order. A pool with no identity row is refused before anything is written; a tree with a way out is refused by path. repoDir is the job clone and may be empty where the worker has not cloned yet.
func StageJobTree ¶
StageJobTree clones source into dest as the job clone. When the card declares PATHS:, the working tree is a sparse checkout of those packages and their in-module dependencies only — an unrelated package is not materialized, and the named package's tests still run (#2498 S10). An import that cannot be resolved is not an empty dependency set: staging refuses before the clone, so the worker is not handed a tree with that dependency omitted. A resolved set with no in-module directories is empty and still checks out the PATHS and TEST cones. PATHS: none, or no PATHS: line, is a full checkout so a card that declared no bound keeps the whole tree. dest is typically filepath.Join(jobDir, JobRepo).
func StagingGitEnv ¶
func StagingGitEnv(id StagingIdentity) []string
StagingGitEnv is the environment the launcher exports with every job: the bench's own git config cannot leak into what the worker commits or what the machinery reads, and the pool's identity row is exported as author and committer so a worker cloning after launch commits under the pool's name (SPEC-TOOLWORK §3 rule 1, #1665).
func StartJobLease ¶
StartJobLease takes the lease on jobDir for the running child and returns the release.
It REFUSES rather than returning a release that protects nothing. A *JobLeaseHeldError says another run holds the directory -- the same-path exclusion of issue #1585 -- and any other error says this run could not establish ownership at all, which under rule 3 is the same refusal with a different reason. The caller exits on either, before it has written anything the holder owns.
The release is safe to call more than once. It stops the heartbeat, WAITS for it, and then removes the file only while the file is still this run's -- pid and nonce both.
func StartSlotLease ¶
THE SLOT LEASE (issue #1901). The bench store's lease is a COUNT: it says an owner is holding n seats, and it does not say WHICH slot directory a seat is. So two `native` runs with the same --slot and DIFFERENT --label each took a seat, each took its own job lease -- #1585's refusal is about one job directory, and these are two -- and both started against one `<slot>/data`, which is one HOME, one cache and one opencode.db. SPEC-SWARM under **Slots** says a worker has "its own data home" and "a slot is held by exactly one worker"; under **the races, taken out**, two workers on one data home IS the 2026-09-10 `database is locked` failure, closed on purpose.
The slot lease is the job lease pointed at the slot directory, and it is the same file format, the same four rules and the same pid-and-nonce fence -- published whole, an unparseable record held rather than assumed dead, a failure to establish ownership a refusal, and a release that only removes a record that is still this run's. There is no second spelling of ownership on this bench.
It is taken AFTER the job lease so that two runs sharing one job directory keep saying exactly what #1585 made them say. The label it carries is this run's, so the refusal can name the card that is holding the slot.
func StartStamp ¶
StartStamp is the kernel's own start stamp for a pid: field 22 of /proc/<pid>/stat, the process's start time in clock ticks since boot. It is what tells a live pid from a REUSED one -- a slot file whose pid is alive under a different stamp is a different process wearing an old number, and rule 17 quarantines it rather than adopting it.
func Steal ¶
Steal takes from a victim bench's queue/ everything above its capacity line, renaming each card into the victim's taken/ as taken/<worker>-<name>.card, and returns the names taken. It never empties the victim below its line: each card is counted before it is renamed, and a victim at or below the line is left alone. A negative capacity is an unbounded line and steals nothing.
func StealCount ¶
StealCount is how many cards an idle worker may steal from a victim whose queue holds queued cards and whose capacity line is capacity: everything above the line, so the victim keeps enough to fill its own workers. A victim at or below its line is left alone; a negative capacity is an unbounded line and nothing is stolen.
func Supervise ¶
func Supervise(in SuperviseInput) int
Supervise is the whole of the supervisor's life. It returns the exit code.
func TakeCard ¶
TakeCard takes one card from a bench's queue by renaming it into the bench's taken/ as taken/<worker>-<name>.card, and reports the card's name and whether one was taken. The rename is atomic within the directory, so two workers racing for one card cannot both take it: the loser sees the card gone and looks at the next one. An empty queue is ("", false, nil), not an error.
func TakeSlotLeases ¶
func TakeSlotLeases(store, owner string, k int, dur time.Duration, label string, now time.Time, pid int) (ids []string, held, share, free int, holders string, ok bool, err error)
TakeSlotLeases grants k unweighted (weight 1) leases. See takeSlotLeases.
func TakeSlotLeasesKind ¶
func TakeSlotLeasesKind(store, owner string, k int, kind string, dur time.Duration, label string, now time.Time, pid int) (ids []string, held, share, free int, holders string, ok bool, err error)
TakeSlotLeasesKind grants k leases each carrying the kind's admission weight.
func TaskkillArgs ¶
TaskkillArgs returns the argument list for Windows taskkill.exe to reap a process tree. - When force is false, it returns ["/PID", "<pid>", "/T"] for graceful tree termination. - When force is true, it returns ["/PID", "<pid>", "/T", "/F"] for forceful tree termination. This function is platform-independent so unit tests on any OS can verify argument construction.
func TemplateNames ¶
func TemplateNames() []string
TemplateNames is every name Template answers to, in a fixed order.
func TerminateGroup ¶
TerminateGroup asks every process in a group to stop.
func ThisOS ¶
func ThisOS() string
ThisOS is the operating system whose list the wall is built from: this process's own, because the wall contains a card on THIS bench.
func TooShortForAPhrase ¶ added in v0.14.0
TooShortForAPhrase is what a description's own phrase must clear, and the reason when it does not. `input_limit_phrases: ["limit"]` would class every failed job whose log holds the word `limit` and refuse each one its retry, and non-emptiness was the only floor there was (Fable's read of #150, finding 4). A provider's refusal is a SENTENCE: PhraseFloor characters at least, and a space or a digit in it, so that no single word can be one. The table this repo ships clears its own floor, and a test says so.
func ToolchainRootNames ¶
ToolchainRootNames is the same list as names, in slash form: the provisioning standard's side of the agreement, which is about what a bench HAS rather than about how the wall grants it, and what the class test compares against.
func ToolchainRootOSes ¶
func ToolchainRootOSes() []string
ToolchainRootOSes is every operating system the one list speaks for, sorted, so the class test walks the declaration itself rather than a list of its own that could fall behind it.
func Triage ¶
func Triage(in TriageInput) int
Triage walks the jobs, folds every revision it has not folded, and writes one page.
func TriageQuarantineSummary ¶
TriageQuarantineSummary formats a one-line summary of quarantined cards for triage: TRIAGE QUARANTINE total=<n> input_limit=<n> malformed=<n> provider_exhausted=<n> infra=<n>
func TwoPhaseReap ¶
TwoPhaseReap performs a canonical two-phase reap of a process group: 1. Sends SIGTERM (or graceful tree termination) to allow processes to flush/cleanup. 2. Waits up to TerminateGrace (3s) polling for all processes in the group to exit. 3. If any process in the group remains alive, sends SIGKILL (or forceful tree kill). 4. Waits up to TerminateGrace (3s) polling to confirm all processes are reaped. It reports whether any process in the group survived.
func TwoPhaseReapWithGrace ¶
TwoPhaseReapWithGrace performs the two-phase reap using a custom grace duration. If grace <= 0, it defaults to TerminateGrace (3s).
func UnsandboxedLine ¶ added in v0.12.0
UnsandboxedLine is rule 11's one loud line, per job, printed BEFORE the job starts. It is never a default, is never implied by a missing backend, and no environment variable or file can produce it: it exists only where a person typed --no-sandbox.
func UsageRefusalLine ¶
UsageRefusalLine is the one line a usage read that did not answer leaves on the record, so that a row of dashes is never silent about the reader it needed.
func ValidWorker ¶
ValidWorker reports why worker cannot name a taken card, or nil when it is exactly one safe filename component in the expected worker-name format.
func ValidateLaunchRecord ¶
func ValidateLaunchRecord(r LaunchRecord) error
func ValidateUsageDestination ¶
ValidateUsageDestination ensures that path is not an existing directory, its parent directory exists and is a directory, and the target is writable.
func VerifyEvidence ¶
VerifyEvidence checks whether an evidence set is complete and valid. An incomplete set is not a committed snapshot.
func WallCommits ¶
WallCommits reports the branch a clone is on and the commits it holds past its base -- the commits reachable from HEAD and not from the base it cloned, which is exactly the work a card added before the wall stopped it. The base is the first remote-tracking ref named `dev`, `main` or `master`, else the first `refs/remotes/*`; a clone with no remote refs counts every commit it has, because there is no base to subtract. It reports false when the directory is not a clone or git cannot name the branch, so a report line is only decorated with numbers that are real.
func WallDeath ¶
THE WALL DEATH (issue #918). A harness that DENIES a tool call returns a tool error the model routes around and keeps working -- that is the configured fence (fence.go). But a harness build that still auto-rejects stops the model there, and the run ends with no RESULT.md while the card's commits sit in ./repo: eight of thirty cards died this way on 2026-09-16 and the batch scored them `no-result`, a model that chose to publish nothing, with the work stranded and unpushed.
WallDeath is what such a run is: the harness's own fence named a path it would not touch, the job published nothing, and ./repo is left with commits. The report NAMES the path and the commits so the harvester pushes the work -- `WALL task=<id> path=<p>` and, when there are commits past the repo's base, `commits=<n> branch=<name>`.
A FENCE REJECTION BESIDE A RESULT IS NOT A DEATH. A run that emitted the line and then published is a model that took the tool error and finished, and it is done. The result is asked first, wherever the gather looks for one (FindCardResult), so the two shapes never trade places.
func WallKind ¶
WallKind is the ONE WORD a refusal is reported by -- the `<what>` of the typed line -- and it is read out of the refusal's own sentence rather than guessed: `home` for the wall's own home_outside refusal, `write` and `read` for a path the kernel refused in one direction, `exec` for a refused execution, `fence` for the harness's own auto-reject, and `denied` when the sentence names no direction at all.
func WallLine ¶
func WallLine(task string, w WallRefusal, branch string, commits int) string
WallLine is the ONE line a wall death is reported on, to the coordinator and to the harvester. It names the task, the refused path and the last step the card reached, and -- when the clone holds commits past its base -- the count and the branch, so work a dead card had already committed is not lost with it:
WALL task=<id> path=<the refused path> step=<the last STEP number seen in the log> [commits=<n> branch=<name>]
func WallRefusedLine ¶
WallRefusedLine is the typed line a refusal is announced on THE MOMENT IT IS SEEN, before anything is decided about it and while the card is still running:
WALL REFUSED <what> <path> task=<id> step=<n>
It is a note, never a verdict: a card that goes on to publish is done, and this line having been printed takes nothing away from it.
func WallStep ¶
WallStep returns the number on the LAST `STEP <n>` line the card printed, or "" when it printed none. A card that died at its second step says so, so the remedy can name where.
func WatchIdle ¶
WatchIdle watches one card until it goes idle, until stop is closed, or forever. It sends AT MOST ONE IdleEnd and NEVER CLOSES THE CHANNEL, because a closed channel is always ready and a caller selecting on it would read a zero end -- and kill a working card -- the instant the watch decided the card was fine. An Idle of zero or less watches nothing and returns a NIL channel, which blocks forever in a select: the run then behaves exactly as it did before this file existed.
func WidthNeedsRemeasure ¶
WidthNeedsRemeasure is the adopt step's question: re-measure whenever the tool version changed under the row, or the row was never measured. A machine change arrives the same way, through probe: a bench whose cores no longer match is proved again before it is sized again.
func WorktreePath ¶
WorktreePath is the kept worktree for a repo under a slot: <slot>/worktrees/<owner>/<name>, the one place warmth lives. A repo that is not owner/name has no worktree and returns "".
func WrapSecretsExec ¶
WrapSecretsExec wraps a command line with `nova-secrets exec --as <seat> -- <cmd...>` if seat is set.
func WrapTemplate ¶
WrapTemplate puts a task's own text under its template's conditions, with the file budget written into the condition that names it, so the number in the prompt is the number the machinery will hold the worker to.
func WriteAskedResult ¶
WriteAskedResult writes the card's report when it ended by asking and published none. Like the blocked report it REFUSES to overwrite a result that exists -- a card that published owns its report -- and it says who wrote it on its own line, because a reader must never have to guess whether a worker or the machinery wrote what they are reading.
Line 1 is `RESULT: ASKED <the question, one line>`: the word is the verdict a requeue reads and the ledger counts, and the question itself is on it, bounded and escaped, so the one line a coordinator reads already holds the thing the card is waiting for. The report carries NO findings head, so it can be scored `plan-only` and never `ok` or `clean`: a report the machinery wrote can never be counted as work a worker did.
func WriteBlockedResult ¶
WriteBlockedResult writes the card's report when the run ended it and it published none. It REFUSES to overwrite a result that exists -- a card that published owns its report -- and it says who wrote it on its own line, because a reader must never have to guess whether a worker or the machinery wrote what they are reading.
When kind is empty (idle end with no refusal), the report uses CardIdleLine. When kind is set (idle end after a refusal), the report uses WallRefusedLine.
func WriteCardUsage ¶
WriteCardUsage writes one card's usage.tsv, header line and one row, atomically. The fields follow the same tab- and newline-scrubbing law as the pool's usage file, so the row is always one row.
func WriteJSON ¶
WriteJSON writes one of this package's small records whole, through a temporary file, fsync and a rename, so that a reader sees a whole record or none.
func WriteMeasuredTable ¶
WriteMeasuredTable writes one doubling round beside the benches table: one header line and one row per W, so the width on the row can be re-derived without re-running the bench.
func WriteReceipt ¶
WriteReceipt writes the receipt beside the report: label, card sha-256, the disposition, the line count, and the run and usage records when they exist.
func WriteStageTimeoutResult ¶
WriteStageTimeoutResult writes the RESULT.md for a stage timeout: RESULT: BLOCKED stage-timeout <bench> <secs>
func WriteTimeline ¶
func WriteTimeline(path string, rows []TimelineRow) error
WriteTimeline writes one card's timeline.tsv atomically: the header line and one row per event, in order. A field is scrubbed of tabs and newlines so the row is always one row.
Types ¶
type AbortedRecord ¶
type AbortedRecord struct {
Nonce string `json:"nonce"`
Reason string `json:"reason"`
At string `json:"at"`
Survivors int `json:"survivors"`
}
AbortedRecord is <job>/aborted.json: the durable acknowledgement a supervisor that lost its identify writes BEFORE its own death can be observed, so that a launch's ABSENCE can be established rather than assumed.
type AskedRun ¶
type AskedRun struct {
Capture string // the tail of <job>/harness-output.log
RC int // the child's exit code
Published bool // a RESULT.md was found where the gather looks for one
Committed bool // ./repo holds commits past its base
}
AskedRun is what one finished native run offers the question test: the tail of the card's own capture, the child's exit code, whether a report was found where the gather looks for one, and whether ./repo holds commits past its base. Every field is something the run already knows when the child is gone.
type BaseCheck ¶
type BaseCheck struct {
Repo string // a git repository holding the card's base-sha
Legs FleetLegs // the fleet's leg table
P95 KindP95 // p95 wall seconds of DONE cards, by KIND; `*` answers for any kind
}
BaseCheck is the evidence the five checks read. A nil table or an empty Repo means that evidence was not handed over.
type BatchInput ¶
type BatchInput struct {
ID string // the batch id, printed on the BATCH line
Deadline time.Duration // the whole batch's own deadline
Idle time.Duration // per-card idle timeout: a card's log not growing this long is killed
Cards string // path to the TSV: label \t slot \t model \t card-path
Root string // the root a card's RESULT.md hangs under
Runner string // the command, one process per card; "" runs `nova-swarm native` in this binary
// Harness and Auth are the native path's own configuration, read only when Runner is
// empty: with no runner script a local card runs through this binary's own `native`
// verb (issue #636), the way a bench row's card already does.
Harness string
Auth string
// Self is the nova-swarm binary a runnerless batch runs `native` from. Empty resolves
// this process's own executable, and then "nova-swarm" on PATH: a test driving Batch
// directly is not nova-swarm, and a batch that ran the test binary ran nothing.
Self string
// Slots is the slot range this batch allocates from: "<lo>-<hi>", or "<n>" for 1-<n>.
// Empty keeps the old behaviour -- allocation from 1 with no ceiling. The cards.tsv
// slot column is optional in either case; a hand slot outside the range is that card's
// own admission refusal (issue #618).
Slots string
// Worker is the worker description whose class gates admission: when its
// class is public, a card cloning an unlisted repo is refused with
// `CARD REFUSED reason=private-source ...` and never starts. The zero
// Worker is paid and gates nothing, so callers without a description keep
// today's behaviour byte for byte.
Worker Worker
// PullWait and PullPoll bound the pull that brings a remote card's files back: how
// long to wait for RESULT.md to exist on the bench, and how often to ask. Zero takes
// the spec's own numbers (30 s, one second), and the tests take short ones.
PullWait time.Duration
PullPoll time.Duration
// SlotsStore and SlotOwner are the bench slot store a card's `nova-swarm native` takes
// its one lease from, and the owner whose share it counts against (nova-tools#1546).
// They are REQUIRED of any batch that launches native -- which is every batch without
// a --runner of its own -- and the refusal is NoSlotsStoreRefusal, said once before a
// card runs rather than once per card.
//
// THE PATH IS ON THE MACHINE THAT RUNS THE CARD. For a local card that is this
// machine; for a bench row it is the bench, reached over ssh, and the path must exist
// THERE. One batch spanning local and remote benches therefore wants a store path that
// means the same thing on each, which today means naming the same absolute path on
// every bench. Said plainly here because it is the seam a reader will meet.
SlotsStore string
SlotOwner string
// Tokens is the budget word this batch carries for EVERY card (SPEC-SWARM rule 13d,
// issue #1545): a number, or the literal `unmetered`. It is REQUIRED -- a batch without
// it is exit 2 before any card starts -- and it is carried as the caller TYPED it,
// never parsed here, because rule 13d says the batch puts it "verbatim into every
// `native` argv it builds" and `native` is the one verb that decides what a budget word
// means.
//
// IT IS EACH CARD'S OWN BUDGET, NOT THE BATCH'S. The same word goes to every card, the
// shape `batch --tasks` already has: it is never divided among the cards and never a
// total for the batch. Ten cards under `--tokens 100000` may spend a million tokens
// between them, and that is what the caller said.
Tokens string
Benches string // path to the benches table; empty means no table is read
Bench string // comma-separated bench names to allocate the cards across; empty means local only
Then string // a follow-on command, run with sh -c only when every card is done; "" means none
// MaxInflight is the per-route in-flight cap of nova-tools#917: at most this many of a
// batch's cards run against one provider/model/key at a time, and the rest WAIT --
// held, not refused, holding no process and no spend, with their deadlines not begun.
// Zero is no cap, which is today's behaviour byte for byte.
//
// Measured 2026-09-17: above roughly 30-40 concurrent requests on one Muse key the tail
// latency goes to infinity, and every card launched past that point burns its whole
// deadline for nothing -- and is paid for.
MaxInflight int
// StallAfter is the FIRST-TOKEN deadline of the same issue: a card that has been running
// this long and has produced nothing at all -- no byte of log, no turn in its harness
// store -- is ended `ABSTAIN reason=stalled`.
//
// IT IS NOT THE IDLE TIMEOUT. `--idle` is about a card that WAS working and stopped, and
// every signal it has needs a first sample to compare against; a card that never spoke
// once has no such sample and is invisible to it. On 2026-09-17 a fresh known-answer
// card hung for its entire 150 s deadline having produced no token at all, while
// deepseek-flash on the same bench in the same second answered in 11 s. Zero leaves a
// card to its deadline, which is today's behaviour.
StallAfter time.Duration
// THE ROUTE SEAM (Glenn 2026-09-19, SPEC-DECIDE "nova-decide route"). When
// it is set, the model a card is dispatched with is the ladder's answer --
// which MIND does this unit of work -- and not the string the fill script
// wrote in the TSV. The TSV's model becomes the FALLBACK, which is exactly
// today's behaviour (rule 5): it stands where there is no key, where the
// provider refused, where the confidence is under the floor and where the
// rung is a mind a card cannot be dispatched to. Every card carries one
// receipt line saying which happened. nil leaves the TSV's model alone.
Route *RouteInput
// RouteSkip is the reason a launcher was explicitly told not to route.
// When it is non-empty every admitted card gets a "source":"skipped" row in
// the route log, so a skipped route is a fact in the log and not an absence
// (SPEC-DECIDE housekeeping H4, #1625). Empty means the launcher routes; a
// launcher cannot skip without a reason.
RouteSkip string
Stdout io.Writer
Stderr io.Writer
// contains filtered or unexported fields
}
BatchInput is everything the batch scatter/wait/gather needs, held apart from the command-line parsing so a test can drive it with a fake runner script.
type Bench ¶
type Bench struct {
Name string // one word; the local machine is the row "local"
Host string // an ssh alias from the caller's own ssh config, or "local"
Root string // the swarm root on that host, absolute there
Cores string // a taskset list ("1-15", "2,4,6") or "-" for no pinning
Harness string // the harness binary on that host, absolute there
Auth string // the harness auth file on that host, absolute there, mode 0600
Wall string // "sandbox" or "none": what bench probe found
Width int // the measured width, a power of two; 0 is unmeasured
Measured string // the stamp the width was measured at, or ""
Version string // the tool's sha8 the width was measured with, or ""
}
Bench is one row of the benches table: a machine that runs slots.
func LoadBenchTable ¶
LoadBenchTable reads --benches TSV, one header line and one row per bench, and validates every row: seven columns in order, an absolute root, harness and auth, a cores column that is "-" or parses, a wall that is "sandbox" or "none", and a name used once, with three optional trailing columns width, measured and version (deprecated/docs/SPEC-SWARM.md "Benches"): a row without them is unmeasured and fills by cores. A row that fails names itself in the error.
type Card ¶
Card is the affinity facts a card carries: its kind ("go", "lisp", "docs", "schema-leg") and the repo ("owner/name") it runs against.
func ParseCard ¶
ParseCard reads one card's kind and repo. The card's name is its file name without a .card suffix.
func ReadCardDir ¶
ReadCardDir reads a bench's queue: a directory of card files in source order. Each card names its kind and its repo on `kind:` and `repo:` lines; the card's name is its file name without the .card suffix. A card that names no repo has no affinity and is refused, never placed blind.
type CardBase ¶
type CardBase struct {
Repo string // the clone URL (or a local path) to stage; "" when the card names none that can be read
Sha string // base-sha: (or the sha of BASE: <ref>@<sha40>); "" when absent
Ref string // BASE: <ref> without @; the ref checked out when there is no sha
Named string // the raw value of the card's base-repo: or REPO: line, even one Repo could not be read from
}
CardBase is what a card's header says to stage into <job>/repo (nova-tools#3711).
func ReadCardBase ¶
ReadCardBase is THE reader of which repository a card works in, at which sha: staging (StageCard) and the push-time lint (internal/nsprint/card) both call it, so the repo a card is admitted with is the repo it is staged from. It reads the first 40 lines. Precedence: `base-repo: <url>`, then `REPO: <owner>/<name>` (the header every pushed card carries), then the first github clone URL anywhere in the card (CardCloneRepos). The sha is `base-sha:`, else the sha of `BASE: <ref>@<sha40>`; the ref is BASE:'s value before any @.
type CardHandOff ¶
CardHandOff is what HandOffCardOut did: Repo and Result are the paths it wrote under the out directory ("" when it wrote none), Moved says the repo was renamed (false: copied, the cross-device case, or not handed off at all).
func HandOffCardOut ¶
func HandOffCardOut(job, out string) (CardHandOff, error)
HandOffCardOut hands a native job's work to the card wrapper's out directory: the card's RESULT.md (as swarm.FindCardResult finds it) is copied to <out>/RESULT.md, and <job>/repo is moved to <out>/repo, by rename, or by copy when the two sit on different devices. An out directory that already holds either is left alone for that one (the harness's own copy wins). An empty out is no hand-off. An out that is inside the job, or that is not absolute, is refused: the wrapper would read what a sweep deletes.
type CardHeaderFinding ¶
CardHeaderFinding is one typed-header defect: the check's token, the 1-based line it sits on and the line's own text. It is the shape `deprecated/cmd/nova-swarm/lint.go` prints on a LINT DRIFT line, remedy and all.
func LintCardBase ¶
func LintCardBase(raw []byte, bc BaseCheck) []CardHeaderFinding
LintCardBase returns the base-check findings for one card, in the order the tokens are listed above.
func LintCardDepends ¶
func LintCardDepends(raw []byte, lineup Lineup) []CardHeaderFinding
LintCardDepends returns the depends-on findings for one card. lineup may be nil: an id is then not called unknown.
func LintCardHeader ¶
func LintCardHeader(raw []byte, trust TrustState, required bool) []CardHeaderFinding
LintCardHeader returns the typed-header findings for one card's bytes.
trust may be nil: with no trust state there is no paused kind, and the lint says nothing rather than guessing. required makes the three missing-line tokens apply to a card that carries no typed header at all; without it a card with no header line is left to the twelve older rules, which is what every card written before §5 is.
func LintCardTestGate ¶
func LintCardTestGate(raw []byte) []CardHeaderFinding
LintCardTestGate returns the `test-gate` findings for one card's bytes: a non-empty finding when the card's TEST: line names a package none of its gate commands run.
type CardRoute ¶
type CardRoute struct {
Model string
Rung string
Source string
Confidence float64
Floor float64
// Fallback is true where today's model stands: no key, a provider refusal,
// a confidence under the floor, or a rung no card can be dispatched to.
Fallback bool
// Why names, in one enumerated token, which fallback this was. It is empty
// where the answer decided the model.
Why string
Receipt string
}
CardRoute is one card's routing answer: the model it is dispatched with, the rung that answered, where the answer came from, the confidence the floor was applied to, and the one receipt line the card carries.
type CardWallTerms ¶
type CardWallTerms struct {
Paths []string // the card's PATHS: globs; empty when PATHS: none or absent
Reads []string // default contextual-read globs (not exhaustive): specs, siblings, testdata, TEST:
Scope []string // Paths union Reads; what AdmitsRead matches
NetDeny bool // true for header MODE: script; a model job stays nopromise
Webfetch string // always FenceDeny
}
CardWallTerms is one card's S7 wall: declared write PATHS, contextual reads, the command allowlist, webfetch deny, and --net-deny when the header is MODE: script.
func WallTerms ¶
func WallTerms(card []byte) (CardWallTerms, error)
WallTerms reads one card's S7 wall. An invalid PATHS: line is an error, the same error hygiene.ValidatePaths returns at cut. Duplicate or contradictory MODE fields refuse. MODE is the typed header's field, not a body line.
func (CardWallTerms) AdmitsRead ¶
func (t CardWallTerms) AdmitsRead(repoRel string) bool
AdmitsRead reports whether a repo-relative path matches the declared writes or the default contextual reads (specs, siblings, testdata, TEST:). Those defaults are not exhaustive: the dispatcher may authorize bounded caller/callee, build-input, and reverse-dependent reads without widening PATHS. A denied required read blocks and requests that adjustment; this helper does not guess. Dispatcher reads (slot, toolchain, read_roots) are not this question.
func (CardWallTerms) AdmitsWrite ¶
func (t CardWallTerms) AdmitsWrite(repoRel string) bool
AdmitsWrite reports whether a repo-relative path matches a declared PATHS glob. Contextual reads are not writes.
func (CardWallTerms) ReadRoots ¶
func (t CardWallTerms) ReadRoots(repoRoot string) ([]string, error)
ReadRoots is the --read directories a directory-covering PATHS glob can name today, under repoRoot, skip-if-absent. A file glob names none: granting its parent would admit siblings outside PATHS. A root that cannot be resolved, or that follows a symlink out of repoRoot, is refused before a policy is produced. TODO launcher: Rowan wires this into nativeSandboxArgv for those directory globs; file-level scope stays the matcher above.
type ClipRequest ¶
type ClipRequest struct {
Worktree string // the kept worktree under <slot>/worktrees/<owner>/<name>
Branch string // the card's topic branch to commit
Base string // the base ref the worktree is reset to, e.g. dev or origin/dev
Message string // the commit message; empty takes "clip <branch>"
Result string // the card's result file inside the worktree, e.g. RESULT.md
Harvest string // the directory the result is harvested into
}
ClipRequest is one clip: the kept worktree to reset, the card's branch to commit, the base to reset to, and where the card's result is harvested from and to.
type ClipResult ¶
ClipResult is what a clip landed: the branch commit, the harvested result, and the base the worktree now sits on.
func Clip ¶
func Clip(req ClipRequest) (ClipResult, error)
Clip is nova-work clip (section 4): commit the card's branch, harvest its result, then reset the kept worktree to base. The clone is reset rather than re-cloned, so warmth is kept and history never leaks to the next card.
type Contract ¶
type Contract struct {
Label string
ContractLine string // line 1 of RESULT.md must equal this exactly
MaxLines int // bound on evidence lines; 0 or negative means DefaultContractLines
Card []byte // the card text, hashed into the receipt
HaveRun bool // a run record exists
WallSeconds int // from the run record
ExitCode int // from the run record
HaveUsage bool // a usage file was given
TokensIn int
TokensOut int
USD string
}
Contract is one result-contract check of a job's RESULT.md.
type CredentialsProfile ¶
type DecideFunc ¶
type DecideFunc = decideFunc
DecideFunc is decideFunc under the name the pulse loop can pass across the package boundary: nova-pulse run hands in the Jev client (or its own fake) after each harvest (card 8371).
type Decision ¶
type Decision struct {
Slot int
Kind string // adopt, reclaim, unknown, unlaunched, quarantine
Reason string
File SlotFile
Exit *ExitRecord
Remaining time.Duration
}
Decision is what a recovering dispatcher does about one slot file, and the words are the ones rule 17 uses.
type Ending ¶
type Ending struct {
Sidecar Sidecar
JobDir string
Provider string
Model string
End string
RC int // -1 is no exit code: unknown, or a launch that never happened
Started time.Time
Ended time.Time
Usage ProviderUsage
Repo string
}
Ending is everything finalize needs to know about a job that has ended.
type EvidenceManifest ¶
type EvidenceManifest struct {
Schema string `json:"schema"`
JobID string `json:"job_id"`
SnapshotHash string `json:"snapshot_hash"`
}
EvidenceManifest is the prelaunch expected-hash reference published last in the evidence directory (SPEC-SWARM-PROFILES, lines 344-365).
type EvidenceSnapshot ¶
type EvidenceSnapshot struct {
Task []byte
Prompt []byte
Profile []byte
Manifest EvidenceManifest
TaskLen int
PromptLen int
ProfileLen int
}
EvidenceSnapshot holds the recovered evidence payload with bounded lengths.
func RecoverEvidence ¶
func RecoverEvidence(evidenceDir, jobID string) (*EvidenceSnapshot, error)
RecoverEvidence opens bounded regular files at the constant names, verifies manifest job ID against its directory, recomputes task/prompt/config/prefix hashes before using the snapshot. Missing evidence, mismatched hashes, or a job-ID/path/lineage conflict returns an error.
type ExitRecord ¶
type ExitRecord struct {
RC int `json:"rc"`
Signal string `json:"signal,omitempty"`
Ended string `json:"ended"`
Survivors int `json:"survivors"`
Nonce string `json:"nonce"`
Attest string `json:"attest,omitempty"`
End string `json:"end,omitempty"`
Spent int `json:"spent,omitempty"`
Observed bool `json:"observed,omitempty"`
Partial bool `json:"partial,omitempty"`
Reason string `json:"reason,omitempty"`
}
ExitRecord is <job>/exit.json: the supervisor's durable completion evidence (rule 18, step 6). An outcome with none is `unknown`, never guessed. `Attest` is the per-launch secret the supervisor minted in its own memory, written here only inside endWith after the job's whole process group is dead; its hash lives in the slot file, and a reader accepts this record as the supervisor's own word only when both the nonce AND the attestation match.
type FailureClassification ¶
type FailureClassification struct {
Kind FailureKind `json:"kind"`
Reason string `json:"reason"` // Specific reason token, e.g. "rate-limit", "provider-5xx", "input-limit", "malformed", "oom", "test-failed"
Retriable bool `json:"retriable"` // Whether the failure may be retried automatically
Quarantinable bool `json:"quarantinable"` // Whether the failure should quarantine the card when retry budget is exhausted or unrecoverable
Details string `json:"details"` // Summary or excerpt of the failure
}
FailureClassification is the complete classification decision for a failure.
func ClassifyFailure ¶
func ClassifyFailure(err error, output string, rc int, end string) FailureClassification
ClassifyFailure inspects error, output log tail, return code, and end state to assign a failure classification.
type FailureKind ¶
type FailureKind string
FailureKind is the high-level category of failure under the taxonomy.
const ( // FailureTransientProvider indicates a temporary provider error (429, 5xx, timeout). FailureTransientProvider FailureKind = "transient-provider" // FailureInfraCrash indicates an infrastructure or environment crash. FailureInfraCrash FailureKind = "infra-crash" // FailureTestFailure indicates legitimate test failures where code ran but assertions failed. FailureTestFailure FailureKind = "test-failure" // FailureCardDefect indicates unrecoverable flaws in the card specification itself. FailureCardDefect FailureKind = "card-defect" )
type Finalized ¶
type Finalized struct {
UsagePath string
UsageExisted bool
Class string
MalformedLine int
Hash string
Report Report
Published bool
}
Finalized is what finalize did, so the caller can print one line about it.
type Finding ¶
type Finding struct {
Line int
Text string
Dup bool
Rule string // the rule quoted verbatim, between backticks
File string // normalized: relative, no leading ./, backslashes read as /
FileLine string
Job string
}
Finding is one appended finding line.
func (Finding) Key ¶
Key is the part of rule 15's de-duplication key this finding carries; the report supplies repo and rev. Two findings merge only when both reports carry both AND they are equal.
type FleetLegs ¶
FleetLegs is the set of legs some bench of the fleet carries, lower case.
func ReadFleetLegs ¶
ReadFleetLegs reads the fleet's leg table: one leg per line, or a TSV whose first column is the leg. Blank lines, `#` comments and a header row whose first cell is `leg` are skipped. Legs are compared lower case.
type GateCommand ¶
type GateCommand struct {
Name string // vet | test | gofmt | lint
Line string // the command as the gate ran it, one line
Result string // pass | fail | not run
Why string // the not-run reason, or the fail sentence; "" on a pass
Out string // what the command printed, verbatim, bounded
}
GateCommand is one gate check's record: the command as the gate ran it, the verdict, why a check did not run or failed, and what the command printed, verbatim and bounded.
type GateRun ¶
GateRun is one gate command's own answer: everything it printed, its exit code, and whether it could be run at all. Err is the could-not-run case -- a command that was not found, or a bench that refused it -- and it is never a red exit: a red exit is an answer the gate reads and reports.
func ExecGateRunner ¶
ExecGateRunner is the shipped GateRunner: one bounded child in dir, its combined output verbatim, its exit code, and Err only when it could not be run at all.
type GateRunner ¶
GateRunner runs one gate command with dir as its working directory. The shipped one, ExecGateRunner, execs it on this machine through the PATH; a bench form runs it over the gate bench's own shell, the same seam `nova-pulse harvest --bench` reads; a test's fake records the argv and answers, so no test of this package runs go, gofmt or nova-swarm.
type Handback ¶
type Handback struct {
Class string // always ProviderClass5xx
Ref string // the provider's reference, "" when the words carried none
Wall time.Duration // the wall at which the provider failed the card
Route string // the failed route; the re-deal avoids it
Next string // the route after it in list order, "" when there is none
}
Handback is a card handed back to the next route.
func ProviderHandback ¶
func ProviderHandback(e ProviderExit) (Handback, bool)
ProviderHandback classes one harness exit. ok is true when the harness exited non-zero, the card published no report, and the harness's last lines are the provider's server error -- at any wall.
type IdleEnd ¶
IdleEnd is one card ended by the watch: how long it had been still, the step it had reached, and -- when the card's own output named one it never moved past -- the refusal and its one word. A card that simply stopped talking carries no refusal, and saying it hit the wall would be the very misattribution this change exists to stop.
type IdleWatch ¶
type IdleWatch struct {
Log string // the card's own capture, <job>/harness-output.log
Job string // the job directory: a card that published is finishing, not idle
Pid int // the child's process group leader
Idle time.Duration
Reader *WallReader // may be nil: the watch then reports no refusal
// contains filtered or unexported fields
}
IdleWatch is one card's watch: the file its own output lands in, its job directory, the leader of its process group, the window, and the reader already in its capture chain. The last three fields are the test's seams and are nil in every real run.
type InputLimitClass ¶
type InputLimitClass string
InputLimitClass is a refusal's class, in the words the structured line carries.
const ( LimitToken InputLimitClass = "token" LimitBytes InputLimitClass = "bytes" LimitFiles InputLimitClass = "files" )
The three ways a request is too big.
type InputLimitSignal ¶
type InputLimitSignal struct {
Class InputLimitClass
Value int
Limit int
}
InputLimitSignal is the structured refusal a harness adapter records: the class, the size that did not fit and the limit it hit, as named fields.
func ReadInputLimitSignal ¶
func ReadInputLimitSignal(log []byte) (InputLimitSignal, bool)
ReadInputLimitSignal reads the adapter's structured line from the harness log. It is the field issue #163 asks a reader to trust: the class, value and limit are named, so no prose rule is asked to decide them. It reports the signal and whether one was there at all.
type JobLease ¶
type JobLease struct {
PID int
Host string
Label string
Nonce string
Started string
Beat time.Time
Known bool
Path string
// contains filtered or unexported fields
}
JobLease is what a lease file says. Beat is the file's mtime, which is the heartbeat, and Known says whether the record parsed at all: an incomplete record names no owner and is never read as a dead one.
func HeldJobLease ¶
HeldJobLease answers the holder a take was refused for, when that is why it was refused. It is here rather than at the caller so a launcher needs no errors.As of its own.
func ReadJobLease ¶
ReadJobLease reads the lease in jobDir. A job directory with no lease answers an error satisfying errors.Is(err, os.ErrNotExist). A path that is THERE but is not a regular file -- a directory, a symlink, a socket -- is an error of its own: this process cannot tell whether that job directory is held, and rule 3 says it must not guess.
func (JobLease) Live ¶
Live reports whether this lease still speaks for a running launcher.
THE KERNEL IS ASKED WHEREVER IT CAN BE: a pid on this host that is gone is gone, and the next run of that card takes the directory over rather than waiting out a window for a launcher that crashed. Two records cannot be asked about -- one written on another host, and one that did not parse -- and for those the heartbeat is the only word there is, so they are HELD until they are older than JobLeaseStale. That is the reaper's second clause, and it is here for the same reason the reaper has it: a liveness file whose owner cannot be checked is not thereby a dead owner.
type JobLeaseHeldError ¶
JobLeaseHeldError is the refusal a take gets when a run already holds the job directory. It carries the holder so the caller's one refusal line can name it.
func (*JobLeaseHeldError) Error ¶
func (e *JobLeaseHeldError) Error() string
type JobProfile ¶
JobProfile is one job's folded timeline: its span, how many turns and tool calls it reported, and the seconds each phase spent.
type KindP95 ¶
KindP95 is the measured p95 wall, in seconds, of each kind's DONE cards.
func ReadKindP95 ¶
ReadKindP95 reads `<kind> <seconds>` rows, tab or space separated; `1526s` is allowed. Blank lines and `#` comments are skipped, and so is a first row whose seconds are not a number (a header). Any later such row is an error: a table that silently dropped a kind would turn a measured bound into a MISSING one.
type KubeJob ¶
KubeJob is a batch/v1 Job manifest. It marshals to JSON, which is YAML, so a k3s auto-deploy manifests directory applies it as written.
type LaunchRecord ¶
type LaunchRecord struct {
Context LaunchRecordContext `json:"context"`
Control LaunchRecordControl `json:"control"`
EvidenceRoot string `json:"evidence_root"`
JobID string `json:"job_id"`
ManifestHash string `json:"manifest_hash"`
Realization LaunchRecordRealization `json:"realization"`
ReservationNonce string `json:"reservation_nonce"`
Sandbox string `json:"sandbox"`
Schema string `json:"schema"`
Slot string `json:"slot"`
UsageEveryNS string `json:"usage_every_ns"`
}
LaunchRecord is the canonical launch record for each reservation.
func ReadLaunchRecord ¶
func ReadLaunchRecord(path string, budget time.Time) (LaunchRecord, error)
type LaunchRecordContext ¶
type LaunchRecordControl ¶
type LaunchRecordControl struct {
ManifestHash string `json:"manifest_hash"`
Root string `json:"root"`
SandboxSource string `json:"sandbox_source"`
Launcher LaunchRecordLauncher `json:"launcher"`
}
type LaunchRecordLauncher ¶
type LaunchRecordRealization ¶
type LaunchRecordRealization struct {
EnvHash string `json:"env_hash"`
}
type LaunchRecordRefusal ¶
type LaunchRecordRefusal struct {
Reason string
}
func (LaunchRecordRefusal) Error ¶
func (r LaunchRecordRefusal) Error() string
type LaunchRequest ¶
type LaunchRequest struct {
Harness string // the resolved harness binary the caller runs
Model string // the provider/model the caller routed this run to
Title string // the run's label, the harness's --title
Prompt string // the prompt argument the harness is handed
}
LaunchRequest is what one run hands the one launcher beyond the table. Every field is optional: an empty one takes the table's value (Harness: the row's column for goos; Model: the row's model; Title: the provider name; Prompt: the literal "PROMPT.md").
type LeaseRefusal ¶
LeaseRefusal is a take that could not grant: the owner's share is spent or the bench is at capacity minus reserve. It carries the holder list `slots take` prints so the puller can refuse with the same names.
func AsLeaseRefusal ¶
func AsLeaseRefusal(err error) (*LeaseRefusal, bool)
AsLeaseRefusal unwraps err into *LeaseRefusal if it is one.
func (*LeaseRefusal) Error ¶
func (e *LeaseRefusal) Error() string
type LegGateInput ¶
type LegGateInput struct {
Bench string // the gate bench the branch was staged on
SHA string // the staged branch's head, the sha the gate ran at
Dir string // the staged branch, outside the wall; "" is refused below, never guessed
Card string // the card file for `nova-swarm lint --card`; "" and the lint is not run
Paths []string // the card's PATHS globs; the touched packages come from them
Run GateRunner // the command seam; nil is ExecGateRunner
}
LegGateInput is everything the gate needs. The harvest fills it from what it holds: the bench it staged the branch on, the branch's head, the staged tree itself, the card's own PATHS and the card file. It holds no RESULT.md and no self-reported gate: those are data and never an input.
type LegGateResult ¶
type LegGateResult struct {
Bench string
SHA string
Commands []GateCommand
OK bool
}
LegGateResult is the gate's answer: the bench and sha it ran at, the four commands' records in the issue's order (go vet, go test, gofmt -l, nova-swarm lint --card), and the verdict. OK is true only when no command failed: a check that did not run because the leg names no Go package, or no card was handed, is the leg's own shape and not a failure; a check that COULD not run is a failure and never a pass.
func RunLegGate ¶
func RunLegGate(in LegGateInput) LegGateResult
RunLegGate runs the leg's gate itself on the staged branch and composes the verdict. It reads nothing the card wrote about its own gate: the tree is the only witness.
type Lineup ¶
Lineup is the set of card ids a lineup file named. Nil means no lineup was handed over, which is not the same as a lineup that names nothing.
func ReadLineup ¶
ReadLineup reads the card ids a lineup names. A blank line and a `#` comment are skipped. A TSV row contributes its id column; a line with no tab is one id.
type Outcome ¶
type Outcome struct {
OK bool
Line string // RESULT OK <label> line2=<...> or RESULT REFUSED <label> <reason>
Line2 string // the disposition line, verbatim
Evidence []string // the lines past line 2, bounded to MaxLines
LineCount int
}
Outcome is what CheckResult decided: the one grammar line, the disposition, the bounded evidence and the report's whole line count.
type PidRecord ¶
type PidRecord struct {
Job string `json:"job"`
Slot int `json:"slot"`
State string `json:"state"`
Pid int `json:"pid"`
Pgid int `json:"pgid"`
JobPgid int `json:"job_pgid,omitempty"`
PidStarted string `json:"pid_started"`
JobStarted string `json:"job_started,omitempty"`
RunnerPid int `json:"runner_pid"`
Started string `json:"started"`
}
PidRecord is <job>/pid: the same identity, beside the job it belongs to. It carries the pids and the start stamps its readers want, and NO launch nonce: the job directory is the first --write of the wall, so a nonce kept here is readable and writable by the worker itself (packet 2 finding 2).
type Pool ¶
type Pool struct{ Dir string }
Pool is a pool directory. The directory itself must exist -- this tool creates the structure inside one a person named, never the one a person forgot to name.
func (*Pool) Claim ¶
Claim moves a task from one state to another, and the move IS the claim: rename is atomic within a directory, so two dispatchers racing for one pending task produce one winner and one ErrNotExist. The task text is read AFTER the rename, from the path this dispatcher renamed to, never from the pending path it no longer owns.
func (*Pool) Finalize ¶
Finalize writes the usage file and the report copy, in that order, before anything moves.
func (*Pool) FindAnywhere ¶
FindAnywhere finds a task's sidecar in whichever state it sits.
func (*Pool) Free ¶
Free releases a slot: a rename made under slots.lock, and then the removal. Every release -- reclaim, unknown, launch-failed, unlaunched, free-on-finalize -- comes through here.
func (*Pool) FreeIf ¶
FreeIf releases a slot only when the file still carries the launch nonce that selected it. Recovery must never free a newer reservation that reused the number.
func (*Pool) Identify ¶
Identify is the supervisor's compare-and-swap (rule 18, step 3): under slots.lock, read the slot file, require that it STILL reads `reserved` with the nonce this supervisor was handed, and only then rename the identity into place. On any other content the supervisor aborts before it spawns anything.
func (*Pool) Jobs ¶
Jobs is every task the pool knows about whose result triage may count, in id order.
RULE 11 (SPEC-SWARM.md:156): a job whose group left a survivor "moves to `failed/` with `violation=background` in the sidecar, and `triage` does not count it". The result is QUARANTINED: a report written beside a process that was still running when it was read is not evidence, and folding it into a coordinator's page is the one way a quarantined result reaches a person as though it were a finished one. `result --id` is how it is read (rule 15), and it is still counted by `status` and by `cost`.
func (*Pool) List ¶
List returns every task in a state, in id order -- which is time order, because the id begins with its UTC stamp.
func (*Pool) Orphan ¶
Orphan rewrites a reservation whose runner is dead to `orphaned`, KEEPING THE NONCE -- under slots.lock, after re-reading the file and rechecking that it still says `reserved` with the nonce first read. If it now reads `launched`, the supervisor's identify landed first and the caller follows the adopt path instead, which is what the bool reports: an orphaning never overwrites an identify that just completed.
func (*Pool) ReadSidecar ¶
ReadSidecar reads one task's sidecar from a named state.
func (*Pool) ReadSlotBy ¶ added in v0.13.0
ReadSlotBy reads one slot file under a caller's deadline: a caller that polls this file inside a bound of its own (the launch handshake) lends that bound to the collision wait, so the retries live INSIDE the caller's clock instead of being added to it. The zero time is "no bound of mine", and reads exactly like ReadSlot.
func (*Pool) ReadUsage ¶
ReadUsage reads every usage row in the pool, in job-id order, which is time order.
func (*Pool) Reclaim ¶
Reclaim removes a job directory -- the ONE thing this tool deletes -- and it requires BOTH the usage file and the report copy, because the evidence would otherwise be inside the thing about to be removed. A copy that does not hash to its REV is refused the same way: a persistence that cannot be verified is not a persistence.
func (*Pool) ReportBytes ¶
ReportBytes reads a job's report from the RETAINED copy for a finalized job and from the job directory only for a running one, so the first triage after a reclaim reads the same bytes it would have read before it.
BOTH READS ARE STEADY. Each of these paths is published by a rename -- the retained copy by `writeAtomic`, the live RESULT.md by the harness -- so on Windows a read landing inside that replace window fails for microseconds and says nothing about the job. Read as a fact it made a published report a `no_result` on the page and threw the worker's findings away: the class this branch closed for exit.json and for `finish`'s read of the same file. A record that is GONE still answers at once, so the ErrNotExist fall-through below keeps its meaning.
func (*Pool) ReportsDir ¶
ReportsDir is <pool>/reports/<job>/, the retained record of one job.
func (*Pool) RequeueOnce ¶
RequeueOnce queues one more attempt of a finished task -- the same text as a new id, marked `requeued=1` -- through the existing requeue path (freshAttempt, p.Add). It refuses a task already carrying a requeue, so a provider_error is retried once and never twice (card 8371).
func (*Pool) Reserve ¶
Reserve writes a slot file in the `reserved` state, under slots.lock, and refuses a slot whose file exists in ANY state: the runner never allocates over an existing file, so the worker cap counts reserved slots as held.
func (*Pool) RetainedReport ¶
RetainedReport reads the retained copy of a finalized job's report, and says which of the three shapes the record takes.
func (*Pool) SlotNumbers ¶
SlotNumbers lists the slots that have a file, in number order. An unreadable name is returned too, as a number that will fail to parse into a decision and be quarantined -- a file this tool cannot read is never a free slot.
func (*Pool) TakeLock ¶
TakeLock takes the named lock inside the pool, waiting up to wait for it, and returns the release. The release is safe to call more than once.
func (*Pool) UpdateSlot ¶
UpdateSlot is a compare-and-swap on a launched slot, for the one field the supervisor learns after it has identified itself: the job's own process group.
func (*Pool) UsagePath ¶
UsagePath is <pool>/usage/<job>.tsv, one per job id, outside everything reclaim removes.
func (*Pool) WriteSidecar ¶
WriteSidecar writes a task's sidecar in the state it is in, whole, through a temporary file and a rename.
func (*Pool) WriteUsage ¶
WriteUsage writes a job's usage file, once and never again: a job's second attempt is a new job id with its own file, so cost sums each attempt once and a retry never double-counts. It reports whether the file already existed.
type PrewarmCommand ¶
PrewarmCommand is one cache-producing phase. Run is injectable so unit tests prove the complete plan without compiling the repository or reaching a network.
type PrewarmInput ¶
type PrewarmInput struct {
Root string
Source string
Repo string
Tip string
Run func(PrewarmCommand) error
}
PrewarmInput names one repository tip already present in a local checkout. Prewarm never guesses a branch or fetches a remote: fleet adoption fetches the bench mirror first and hands this verb the exact full commit it is warming.
type PrewarmResult ¶
func Prewarm ¶
func Prewarm(in PrewarmInput) (PrewarmResult, error)
Prewarm clones an exact tip into the reference path sparse staging already consumes, then fills the bench caches for modules, ordinary builds, compiled test binaries and Lisp. A new reference checkout is renamed into view only after all four phases succeed.
type Profile ¶
type Profile struct {
ID string
Worker WorkerProfile
Route RouteProfile
EnvVar string
Model string
AllowedModels []string
Prompt PromptProfile
// contains filtered or unexported fields
}
Profile is the per-job profile record of SPEC-SWARM-PROFILES. Its six binding members -- worker, route, env_var, model, allowed_models, prompt -- are exactly the fields the preimage hashes. The loader never returns a profile that did not pass every bound below.
func LoadProfile ¶
LoadProfile reads one profile file and returns the single profile it holds.
The file is a strict-JSON single-profile catalog in the spec's shape:
{"version":1,"profiles":{"<id>":{<profile record>}}}
`version` must be the integer token 1 and `profiles` must hold exactly one entry, so a file names one profile and its id. Every field is validated against its bound or enumerated set; any failure is a *ProfileRefusal naming the field, exit 2.
func (*Profile) Preimage ¶
Preimage returns sha256:<64 lowercase hex> over exactly the profile record's six binding fields, in RFC 8785 canonical field order, using the same number-free canonical encoding the catalog digest pins (SPEC-SWARM-PROFILES, encoding and admission bounds). Member order, whitespace and equivalent JSON escapes in the source file do not change it; changing any binding field does.
type ProfileRefusal ¶
ProfileRefusal is the one failure LoadProfile produces. It names the field -- never the value, because a private path or a prompt can sit in a profile field -- and its exit status is ProfileExitCode.
func (*ProfileRefusal) Error ¶
func (r *ProfileRefusal) Error() string
type PromptInput ¶
type PromptInput struct {
ID string
JobDir string
Deadline time.Duration
Files int
Tokens string
Board string
Template string
Task []byte
NoteFile string
Result string
ResultTmp string
}
PromptInput is everything the prompt says that the task text does not.
type PromptProfile ¶
type ProviderExit ¶
type ProviderExit struct {
Tail []byte // the harness's capture; only its last lines are read
Job string // the job directory: a published RESULT.md is the card's own end
RC int // the harness exit code; a clean exit is never the provider's
Wall time.Duration // how long the run took: carried on the line, never a condition
Route string // the route that failed (provider/model)
Routes []string // the ordered route list; the next one is dealt after Route
}
ProviderExit is what one finished harness exit is judged on.
type ProviderProxy ¶
type ProviderProxy struct {
// contains filtered or unexported fields
}
ProviderProxy is the localhost client the harness dials instead of the provider. HarnessURL is the baseURL written into the job config.
func ListenProviderProxy ¶
func ListenProviderProxy(cfg ProviderProxyConfig) (*ProviderProxy, error)
ListenProviderProxy listens on 127.0.0.1 and forwards to upstream. A base URL that is not http or https is refused so the caller can leave it unchanged instead of sending the harness somewhere else.
func (*ProviderProxy) Close ¶
func (p *ProviderProxy) Close() error
Close stops the listener and unblocks a body read still in progress. A read cancelled here is not UNKNOWN: the run is already over.
func (*ProviderProxy) HarnessURL ¶
func (p *ProviderProxy) HarnessURL() string
HarnessURL is the baseURL the harness dials.
func (*ProviderProxy) HeaderWait ¶
func (p *ProviderProxy) HeaderWait() time.Duration
HeaderWait is how long the proxy waits for response headers after the request is written.
func (*ProviderProxy) HeaderWall ¶
func (p *ProviderProxy) HeaderWall() time.Duration
HeaderWall is the measured gap from the written request to the expired header wait. Zero means the header wait did not end the request.
func (*ProviderProxy) Lost ¶
func (p *ProviderProxy) Lost() bool
Lost reports that the body gap or the header wait already fired. A closed channel stays lost.
func (*ProviderProxy) Requests ¶
func (p *ProviderProxy) Requests() int64
Requests is how many upstream requests were started. A request refused after a silent body is not counted.
func (*ProviderProxy) ServeHTTP ¶
func (p *ProviderProxy) ServeHTTP(w http.ResponseWriter, r *http.Request)
func (*ProviderProxy) Silence ¶
func (p *ProviderProxy) Silence() time.Duration
Silence is the gap the body timer is armed with.
func (*ProviderProxy) SilenceWall ¶
func (p *ProviderProxy) SilenceWall() time.Duration
SilenceWall is the measured gap from response headers to the body deadline. Zero means the deadline has not fired, or headers were never seen.
func (*ProviderProxy) Stalled ¶
func (p *ProviderProxy) Stalled() <-chan struct{}
Stalled is closed once, when a body gap or an expired header wait ends the request.
type ProviderProxyConfig ¶
type ProviderProxyConfig struct {
Upstream string
Silence time.Duration
After func(time.Duration) <-chan time.Time
HeaderWait time.Duration
}
ProviderProxyConfig is one proxy. Silence zero means ProviderBodySilence. After nil means a time.Timer inside readWithinSilence. A test passes After so the production gap is an event it can fire. HeaderWait zero means ProviderHeaderTimeout; a test passes a shorter wait so the suite does not wait 45s.
type ProviderUsage ¶
type ProviderUsage struct {
Values map[string]string
Observed bool // the source answered with a row
// Turns is the usage row count: the message rows folded into Values, the
// same assistant-turn count the card budget reads where the harness log
// has fewer. Zero where nothing was observed.
Turns int
}
ProviderUsage is what the harness's own accounting said, read from the data home the dispatcher exported for THIS job -- one job, one database, which is why slots exist.
func ReadCardUsage ¶
func ReadCardUsage(dataHome string, started, ended time.Time) (ProviderUsage, string, string, string)
ReadCardUsage reads one card's accounting out of its harness store and returns the values, a note, the store path that answered, and the reason the row keeps its dashes. The data home the native run chose is passed in explicitly, and the reader looks in its standard locations in order rather than guessing one path. The window is the run's own timestamps, widened five seconds each side, read against the store's `time_created` column in milliseconds. The reason is one of the four the NATIVE OK line carries -- no-sqlite3, query-failed, no-rows, or no-store -- or the empty string when the store answered.
TWO OF THOSE FOUR ARE ABSENCES AND TWO ARE FAILURES, and a caller must be able to tell them apart. `no-store` (the harness wrote no database) and `no-rows` (it wrote one and has reported nothing into this window yet) are absences: there was nothing to read. `no-sqlite3` and `query-failed` are READERS THAT STOPPED -- a missing program, a locked or corrupt database, a query past its timeout -- and a caller acting on the numbers is acting on numbers nobody could see. These last two were one token until 2026-09-19, and a caller that reported the pair as a fault cried wolf on every card killed before its first answer.
A note names a condition the caller should carry to the person reading it, most importantly sqlite3 missing from PATH, under which the token columns are dashes and the row still writes rather than the run failing on a number nobody can see.
func ReadCardUsageAfter ¶
func ReadCardUsageAfter(dataHome string, started, ended, notBefore time.Time) (ProviderUsage, string, string, string)
ReadCardUsageAfter is the same read with a FLOOR under the window, and the floor is what keeps a retried card's rows DISJOINT (SPEC-SWARM rule 13d, issue #1545).
THE DEFECT IT CLOSES, measured: the window is widened five seconds each side, because the launch's own clock and the store's need not agree to the millisecond. On a retried card the SECOND launch begins within those five seconds of the first one's end -- the launch grace's retry is seconds, and a test pins it shorter still -- so the second launch's window reached back over the first launch's rows and counted them again. Rule 13d's own worked example is exactly this: two launches finally reported at 40 and 70 "print `budget=110/100`, and their rows hold 40 and 70, never 40 and 110". Before this floor the rows held 40 and 110 and added to 150, which is what a downstream `cost` would have charged.
`notBefore` is the EARLIER launch's end. The zero time is no floor at all, which is what a first launch has and what every caller outside the retry loop wants.
func ReadJobUsageLive ¶
func ReadJobUsageLive(dataHome string) (ProviderUsage, error)
ReadJobUsageLive is rule 13d's LIVE sample: the same statement readOpenCodeUsage runs, against the same database at the same two spellings, and different from it in exactly two ways that the rule names.
IT WAITS FOR NO CHECKPOINT. "A sample reads what the database holds through whatever write-ahead log lies beside it, which is the ordinary state of a database a live harness has open, and it waits for no checkpoint; rule 13's five-second wait belongs to the final read, made when the harness is gone." A live sample that waited for a checkpoint would wait on a writer that is still running and has no reason to close its connection.
AND IT IS BOUNDED BY LiveSampleLimit, not by the tool's 20-second query timeout: a sample that hung for twenty seconds would be a sample that cannot run at a five-second interval.
A DATABASE THAT IS NOT THERE IS NOT AN ERROR, exactly as in the final read: it is the harness having reported nothing yet, which is an absence and not a failure. The three cases rule 13d keeps apart -- nothing observed, a partial observation, a read that FAILS -- are the caller's to tell apart, and this function's error is the third of them.
func ReadProviderUsage ¶
func ReadProviderUsage(source, dataHome string) (ProviderUsage, error)
ReadProviderUsage reads the provider's own accounting for one job, from the source the worker description names (rule 13). There are two: `opencode`, the job's own database in the data home this tool exported for it, and `none`, which reports nothing and under which only `--tokens unmetered` tasks may run.
A SOURCE THAT REPORTS NOTHING AND A SOURCE THAT FAILS TO READ ARE NOT THE SAME THING, and rule 13 rests on the difference: the first leaves the budget unable to fire and the deadline to end the job, and the second, three samples running, ends the job RUN BUDGET-UNVERIFIABLE, because a numeric budget the tool has stopped being able to see is a budget the caller believes is enforced and is not.
type Pull ¶
Pull is the card pull takes and where it comes from. Warm is true when the bench already held the repo in a kept worktree, so the clone is reused; otherwise Fetch names the bench mirror the cold clone comes from.
func Prefer ¶
Prefer is the pull decision of section 4. It prefers the first card, in bench order and then queue order, whose bench already holds the card's repo in a kept worktree; with no warm bench it falls back to the first card and a fetch from that bench's mirror. It refuses an empty queue rather than inventing a card.
type PullBench ¶
PullBench is one bench pull may take a card from: its slot directory, the queue in pull order, and the bench mirror a cold clone fetches from.
type PullCard ¶
type PullCard struct {
Label string
Kind string
Repo string
Effort int
Path string // the card's path in queue/, so the returned remainder can go back
}
PullCard is one card as the puller reads it from queue/: its label, the kind and repo that decide which cards may share a batch, and its :effort, the turn bound above which the card stops the batch.
type PullInput ¶
type PullInput struct {
Queue string // required: the directory cards wait in
Clone string // required: the one kept clone every card in the batch runs on
Harvest string // required: where a card's RESULT.md lands, under <harvest>/<label>/
Batch int // required: the most cards one turn takes; at least 1
Kind string // optional: take only cards of this kind
Repo string // optional: take only cards of this repo
// Run runs one card on the kept clone and reports the turns it used. A turn past the
// card's :effort stops the batch.
Run func(c PullCard) (turns int, err error)
// Clip commits the card, harvests its RESULT.md to result, and resets the clone to
// base, so the next Run starts from a clean tree.
Clip func(c PullCard, result string) error
Stdout io.Writer
Stderr io.Writer
}
PullInput is one pull turn, held apart from the command line so a test can drive it with a fake turn and a fake clip.
type PullLanesInput ¶
type PullLanesInput struct {
Queue string
Score lanes.Scorer
Floor float64
Stdout io.Writer
Stderr io.Writer
}
PullLanesInput is everything nova-swarm pull-lanes needs. The name avoids the dev-landed Pull type in warm.go, which is the affinity pull of section 4.
type PullResult ¶
PullResult names the card a pull owns and the lease it holds it under.
func PullLease ¶
PullLease takes the next card from <store>/queue under a fresh one-slot lease. It reaps first, so a dead worker's lease returns its card to the queue before this pull considers it. With no card it returns ErrNoCard; when the take refuses, no card is moved and it returns *LeaseRefusal. It never reaches for a card without a lease.
type PullSubmitInput ¶
type PullSubmitInput struct {
Bench string
Worker string
Cores int
Load1 float64
Image string
Runner string
Submit func(name string, job KubeJob) error
}
PullSubmitInput is one puller turn. Submit creates the Job; the verb writes it where k3s applies it, a test records it.
type PullSubmitResult ¶
type PullSubmitResult struct {
Worker string
Declined bool
Headroom float64
Card string
Lane string
Taken string
Job string
}
PullSubmitResult is what one turn did: declined on the load line, took nothing from an empty queue, or took Card from Lane into Taken and created Job.
func PullSubmit ¶
func PullSubmit(in PullSubmitInput) (PullSubmitResult, error)
PullSubmit is one puller turn: check the load line, take one card by rename, create its Job. A Job that cannot be created returns the card to its lane, so a failed submit never strands a taken card with no Job.
type PullWorkerOptions ¶
type PullWorkerOptions struct {
Bench string // bench name (e.g. "hulk", "space")
Slots int // slot count (default 1)
Seat string // bench seat name for nova-secrets exec
Store string // bench store directory holding queue/, taken/, slots/
Harvest string // directory where card RESULT.md is saved
Image string // container image name
Model string // default model
Runner string // custom runner command (overrides container execution)
Container string // container engine ("podman", "docker", or "none")
For time.Duration // lease duration (default 30m)
Once bool // if true, run at most one card and exit
Stdout io.Writer
Stderr io.Writer
// RunCard is an optional test hook to execute a card instead of the container/runner command.
RunCard func(ctx context.Context, label, cardPath, workDir string) (int, error)
}
PullWorkerOptions specifies the configuration of a pull worker.
type QuarantineRecord ¶
type QuarantineRecord struct {
CardID string `json:"card_id"`
Reason string `json:"reason"`
FailureKind FailureKind `json:"failure_kind"`
Attempts int `json:"attempts"`
QuarantinedAt string `json:"quarantined_at"`
Worker string `json:"worker,omitempty"`
OriginalPath string `json:"original_path,omitempty"`
Details string `json:"details,omitempty"`
}
QuarantineRecord describes a quarantined card's metadata.
func ListQuarantined ¶
func ListQuarantined(queueDir string) ([]QuarantineRecord, error)
ListQuarantined inspects <queue>/quarantine and returns all quarantined cards sorted by reason and card id.
type ReapInput ¶
type ReapInput struct {
Root string
Older time.Duration
DryRun bool
KeepData bool
Now func() time.Time
}
ReapInput is one reap pass. Root is the swarm root whose slots are reaped; Older is the age a harness log must pass, with no RESULT.md, for the slot to count as finished; DryRun counts without removing; KeepData is the worker description's keep_data, which leaves the slot's data/ where it is. Now is injectable for tests.
type ReconcileRecord ¶
type ReconcileRecord struct {
Path string `json:"path"`
CardID string `json:"card_id"`
Action TriageAction `json:"action"` // ActionRetry (recovered to queue) or ActionQuarantine
Reason string `json:"reason"`
Attempts int `json:"attempts"`
}
ReconcileRecord records the reconciliation of one card rescued from limbo.
func QuarantineGC ¶
func QuarantineGC(queueDir string, maxAttempts int) ([]ReconcileRecord, error)
QuarantineGC performs a full garbage collection and reconciliation pass on queueDir:
- Reconciles all limbo files (.provider-failed) via ReconcileQueueLimbo, transitioning exhausted or defective cards to quarantine/ and restoring retriable cards.
- Cleans up orphan lock files (.lock) in queue and taken directories where no process holds a live flock.
- Prunes empty reason directories in quarantine/.
func ReconcileQueueLimbo ¶
func ReconcileQueueLimbo(queueDir string, maxAttempts int) ([]ReconcileRecord, error)
ReconcileQueueLimbo sweeps queueDir and its taken/ subdirectories for any cards stuck in limbo with `.provider-failed` (e.g. `<card>.provider-failed`, `<card>.card.provider-failed`, or files in `.provider-failed/` directories).
If attempts < maxAttempts: restores the card to <queueDir>/<card-id>.card so it returns to the active queue. If attempts >= maxAttempts: moves the card to <queueDir>/quarantine/provider-exhausted/<card-id>/. Removes the limbo artifact so no cards are left in limbo.
type Report ¶
type Report struct {
Class string
MalformedLine int
Heading string
HasHead bool
Findings int // the head's own number, the worker's count
NotesRead int
HasNotesRead bool
Repo string
Rev string
Paragraph string
Items []Item
Gates []Gate
LeftOwed []string
OneLine string
FindingLines []Finding
Hash string
Bytes int
}
Report is one parsed RESULT.md.
func ParseReport ¶
ParseReport reads one RESULT.md into a Report. It never returns a finding from a report it classifies malformed.
func (Report) FindingCount ¶
FindingCount is the ONE number a report has: the head's own, where there is a head, and the finding lines where there is none. A report killed before it wrote its head still carries what it found (rule 3, and demanded test 8's headless report with one appended line).
type ResultLint ¶
type ResultLint struct {
Disposition string
Findings []ResultLintFinding
}
ResultLint is what LintResult found. Disposition is line 2's first word when it is one of the grammar's, "" otherwise.
func LintResult ¶
func LintResult(raw []byte, baseSHA string) ResultLint
LintResult lints one RESULT.md's text. baseSHA is the caller's base pin, used when line 1 carries no sha=; "" means none was handed over.
func (ResultLint) CountsAsDone ¶
func (r ResultLint) CountsAsDone() bool
CountsAsDone is the one question the harvest asks: is this a DONE it may count?
type ResultLintFinding ¶
ResultLintFinding is one defect of one RESULT: the check and the line-2 text it saw.
type RouteError ¶
RouteError is a route refusal with a reason token: "no-key" when the named environment variable is unset, "provider-error" when the call could not be made or its answer was malformed. The caller prints it as ROUTE REFUSED.
func (*RouteError) Error ¶
func (e *RouteError) Error() string
func (*RouteError) Unwrap ¶
func (e *RouteError) Unwrap() error
type RouteInput ¶
type RouteInput struct {
Registry *decide.Registry
Decide decideFunc
Floor float64
// Log is the escalation log every decision is written to (rule 8), and
// Usage is the fleet's usage TSV a provider call's spend is written to.
// Both are required before a call is made; see Accountable.
Log string
Usage string
Now func() time.Time
}
RouteInput is one dispatch route's seam: the ladder, the decider, the floor and where the two records go.
Decide is the same decideFunc the triage route uses -- a *decide.Client's Decide method in production, an httptest fake or an in-process function in a test -- so nothing here ever dials a provider on its own. A nil Decide is the key being absent: the rules answer, no call is made, and today's model stands.
func (RouteInput) Accountable ¶
func (in RouteInput) Accountable() (bool, string)
Accountable reports whether this input may ask the provider at all: token spend reporting is an obligation and every decision is logged, so a route that cannot say where both records go does not make the call. The reason is the line the caller prints once.
type RouteProfile ¶
type RouteProfile struct {
Provider string
Endpoint string
Credentials CredentialsProfile
}
type RouteResult ¶
type RouteResult struct {
Kind string
KindConf float64
Complexity int
NeedsStrong float64
Private float64
Worker string
Below []string
BelowFloor bool
}
RouteResult is one card's typed decision and the worker it names. BelowFloor is true when the kind's confidence was under the floor: the worker is then the default and the decision is a suggestion, never an authorization.
func RouteText ¶
func RouteText(text, baseURL, keyEnv string, rows []RouteRow, floor float64, def string) (RouteResult, error)
RouteText makes the typed decision over the card text (bounded to its first 1500 characters, the state rule 1 allows) and picks the worker. def is the worker a below-floor answer keeps. It returns a *RouteError for a missing key or a provider failure, and never prints anything: the caller owns the line.
func (RouteResult) Line ¶
func (r RouteResult) Line(cardBase string, floor float64) string
Line renders the ROUTE line for a card: the same line `nova-swarm route` prints, with the worker the decision chose (the default below the floor) and the below list naming every answer under the floor. cardBase is the card file's base name.
type RouteRow ¶
RouteRow is one row of the routes table: the worker description path for (kind, complexity), where class is public (a free/contributor route) or paid. A public-class worker is skipped for private material.
func ParseRoutes ¶
ParseRoutes reads the routes table: kind<TAB>complexity<TAB>worker json path<TAB>class, one per line, class public|paid. Anything else is a refusal, never a guess.
type RunInput ¶
type RunInput struct {
Pool *Pool
Worker Worker
Key string
Workers int
Hours float64
Max int
LaunchTimeout time.Duration
UsageInterval time.Duration
Backoff time.Duration // the wait before retrying a 429; zero takes the default
// NoAutoRetry finalizes deadline and true-429 outcomes without creating a
// descendant. It is a dispatcher-invocation policy; false preserves automatic retries.
NoAutoRetry bool
Stdout, Stderr io.Writer
Now func() time.Time
Supervisor string // this binary, re-invoked as `supervise`
WorkerFile string // the worker description, handed on to the supervisor
// THE LAUNCH SEAM (docs/SPEC-SANDBOX.md, the dispatcher caller). Sandbox is the
// resolved nova-sandbox binary every job runs inside. NoSandbox is rule 11's one loud
// workaround, typed by a person: it runs the jobs with no OS containment and says so
// once per job. The two are exclusive and the verb refuses both at once.
Sandbox string
NoSandbox bool
// THE BENCH SLOT LEASE (deprecated/docs/SPEC-SWARM.md, "Bench slot leases"). When SlotsStore
// names a store the dispatcher holds one lease per running task, labelled with the
// task id and released the moment the task ends; it never launches past SlotOwner's
// share. Both empty is the behaviour without a store, unchanged.
SlotsStore string
SlotOwner string
// SlotPoll is how long a refused take waits before it asks again; zero takes the
// 10s the spec names. SlotPID is the pid written into each lease; zero takes this
// process, so a dispatcher that dies leaves leases the next take reaps.
SlotPoll time.Duration
SlotPID int
// THE ROUTE SEAM (issue #1486, SPEC-DECIDE "nova-decide route"). When it is
// set, a claimed task is asked of the ladder -- which MIND does this unit of
// work -- before it is launched, and a card the ladder marks ask-child or
// ask-bus is parked in routed-out/ rather than run on this pool's mechanical
// model. The worker's model is the FALLBACK, which is exactly today's
// behaviour (rule 5). nil leaves every claimed task to the launch, which is
// the behaviour every existing caller has today.
Route *RouteInput
// CloneFrom is the on-disk checkout a job clone is staged from. Empty leaves
// the card to clone itself (today's STEP 1), unless the card declares PATHS:
// prepare then sets this from the pool's reference checkout
// ref/<owner>/<name>@<rev> when that checkout is present (#2498 S10).
// When set, prepare stages <job>/repo as a sparse checkout of the card's
// PATHS packages.
CloneFrom string
}
RunInput is one dispatcher run.
type SandboxJob ¶ added in v0.12.0
type SandboxJob struct {
Sandbox string // the resolved nova-sandbox binary
PoolName string // the container name on windows; accepted and ignored elsewhere
SlotDir string // the worker home for this job: the slot copy
JobDir string // the job directory: the first --write, and the cwd
DataHome string // the per-job data home, which is also the child's HOME
// CacheDir is the SHARED per-bench cache root under the swarm root (issue #1048). It is
// a --write BESIDE the job directory and the data home: the toolchain and modules are
// the same for every job under one root, so they live once. Empty when the caller has no
// shared cache root to name.
CacheDir string
ReadRoots []string // toolchain roots the worker description named
Command string // the harness, resolved on PATH by the caller
Args []string // the harness's own arguments
}
SandboxJob is one job's wrap, and every field of it is the dispatcher's own.
func (SandboxJob) SandboxArgv ¶ added in v0.12.0
func (j SandboxJob) SandboxArgv() []string
SandboxArgv is the wrap: nova-sandbox's flags, then --, then the harness verbatim. Everything after -- is the harness's own argv unchanged, so no argument is re-parsed and no quote re-interpreted (rule 12).
func (SandboxJob) SandboxCommand ¶ added in v0.12.0
func (j SandboxJob) SandboxCommand() []string
SandboxCommand is the whole argv, the binary first, which is what exec.Command is handed.
type ShellDenial ¶
ShellDenial is one denial a card's own shell reported: the path it named, the step the card had reached, and the line itself, which is the only thing a person can act on. There is no field for the operation, because the text does not carry one.
func ShellDenied ¶
func ShellDenied(log []byte) (ShellDenial, bool)
ShellDenied reports the FIRST denial a card's shell printed in one capture, and whether it printed any. The first is the one that matters: every later line is downstream of the same closed path.
type ShellDenialReader ¶
type ShellDenialReader struct {
// contains filtered or unexported fields
}
ShellDenialReader is ShellDenied asked of the bytes AS THE PARENT RECEIVES THEM (Johnny's hold on #1478 at 29047871, the #1892 class). The run used to ask ShellDenied of `<job>/harness-output.log` by path after the child was gone -- a file inside the card's own --write directory, and the card's cwd. A card could replace that name after printing the denial (unlink and write a clean file), and a read error left the denial empty: either way the run said NATIVE OK. This writer sits in the capture chain beside the logs, so the verdict is taken from the parent's own copy of the child's output, and nothing the card does to a file afterwards reaches it.
It answers exactly what ShellDenied answers of the same bytes: the FIRST denial, with the LAST `STEP <n>` in the whole capture. It is safe for the child's concurrent stdout and stderr copies and never fails a write, so it can never take the card's own log down with it.
func NewShellDenialReader ¶
func NewShellDenialReader() *ShellDenialReader
NewShellDenialReader returns an empty reader for one run's capture.
func (*ShellDenialReader) Denied ¶
func (r *ShellDenialReader) Denied() (ShellDenial, bool)
Denied is the first denial the capture held and whether it held one. A last line the child left without a newline is read too, as ShellDenied reads the end of a file, without being consumed, so a later Write still completes it.
type Sidecar ¶
type Sidecar struct {
ID string `json:"id"`
Label string `json:"label,omitempty"`
Template string `json:"template,omitempty"`
Files int `json:"files"`
Tokens int `json:"tokens,omitempty"`
Unmetered bool `json:"unmetered,omitempty"`
Deadline string `json:"deadline"`
Batch string `json:"batch,omitempty"`
From string `json:"from,omitempty"`
Requeued int `json:"requeued,omitempty"`
Reaped int `json:"reaped,omitempty"`
Launch string `json:"launch,omitempty"`
Malformed int `json:"malformed,omitempty"`
Violation string `json:"violation,omitempty"`
MaxInput int `json:"max_input,omitempty"`
Limit string `json:"limit,omitempty"`
ProviderRef string `json:"provider_ref,omitempty"`
Job string `json:"job,omitempty"`
Slot int `json:"slot,omitempty"`
Started string `json:"started,omitempty"`
Ended string `json:"ended,omitempty"`
End string `json:"end,omitempty"`
RC int `json:"rc"`
Notes int `json:"notes,omitempty"`
Class string `json:"class,omitempty"`
ReplacedBy string `json:"replaced_by,omitempty"`
Verdict *Verdict `json:"verdict,omitempty"`
}
Sidecar is everything the pool knows about a task that is not its text. It travels with the task file from pending/ to running/ to done/ or failed/.
func (Sidecar) BudgetWord ¶
BudgetWord is what a RUN line prints for this task's token budget ceiling.
type SizeRound ¶
type SizeRound struct {
W int
Cores int
Load float64
CardsPerMin float64
PrevCardsPerMin float64
Abstains int
Held bool
}
SizeRound is one doubling round's measurements.
type SlotFile ¶
type SlotFile struct {
Job string `json:"job"`
JobDir string `json:"job_dir,omitempty"`
State string `json:"state"`
Pid int `json:"pid,omitempty"`
Pgid int `json:"pgid,omitempty"`
JobPgid int `json:"job_pgid,omitempty"`
PidStarted string `json:"pid_started,omitempty"`
JobStarted string `json:"job_started,omitempty"`
RunnerPid int `json:"runner_pid"`
RunnerStarted string `json:"runner_started,omitempty"`
ReservedAt string `json:"reserved_at,omitempty"`
LaunchedAt string `json:"launched_at,omitempty"`
Nonce string `json:"nonce"`
ExitAttest string `json:"exit_attest,omitempty"`
}
SlotFile is what <pool>/slots/<n>.json holds.
type SlotLease ¶
type SlotLease struct {
ID string
Owner string
Pid int
Label string
Until time.Time
Weight int // 0 means 1: a lease written before weights
Kind string // optional card kind charged at admission
}
SlotLease is one bench slot lease: the directory name is the id.
func ListSlotLeases ¶
ListSlotLeases reads every lease in the store, in id order. A directory with no parseable lease file holds no lease and is skipped: it is a half-written take (Mkdir landed, the file never did), and take reaps it.
func (SlotLease) State ¶
State reports live, expired or DRIFT at now: past until= with a live pid is DRIFT, past until= with a dead pid is expired, anything else is live. Stranded leases stay `live` on this field so a listing fill already knows (live, DRIFT, expired) still counts them as held.
type SlotWait ¶
SlotWait is one slot's wait. Pending reports how many cards the slot's own queue holds and is the only read of that queue; Ask blocks on the coordinator's event (nova-pulse watch) and returns true when the event says to look again. Both are seams so a test drives them with a fake queue and a fake clock.
type StageOptions ¶
type StageOptions struct {
Card []byte
TargetDir string // e.g. <jobDir>/repo
JobDir string // <jobDir>, where RESULT.md is written on timeout
BenchHome string
BenchName string
Timeout time.Duration
}
StageOptions describes a card staging request.
type StageResult ¶
type StageResult struct {
BaseRepo string
BaseSha string // the card's base sha; once staged, the full sha <job>/repo's HEAD is at
Ref string // the card's BASE: ref, checked out when it names no sha
Branch string // the branch the staged checkout is on (CardStageBranch)
Mirror string
Staged bool
TimedOut bool
Wall time.Duration
}
StageResult is the outcome of a staging operation.
func StageCard ¶
func StageCard(opts StageOptions) (StageResult, error)
StageCard stages the repository for a card into TargetDir using the bench mirror. The repo, sha and ref are ReadCardBase's (base-repo:, REPO:, or a clone URL; base-sha: or BASE:). If the card names no repo that can be read, staging is skipped and Staged is false; the caller refuses such a card when CardNamesRepo says it named one (#3711). The checkout is at the sha (else the ref, else the clone's default head) on the branch CardStageBranch names, so the card commits on a branch, not a detached HEAD. If base-repo is remote and no bench mirror is found, staging fails without contacting GitHub. Staging runs git clone --reference <mirror> and git fetch/checkout <base-sha> with a hard timeout (default 120s). If the timeout expires, it writes RESULT.md:
RESULT: BLOCKED stage-timeout <bench> <secs>
and returns ErrStageTimeout.
type StagingIdentity ¶
StagingIdentity is the pool's one identity row: the name and email every commit the worker makes must carry, and the owner that row belongs to.
func LoadPoolIdentity ¶
func LoadPoolIdentity(poolDir string) (StagingIdentity, error)
LoadPoolIdentity reads the pool's identity.tsv: a header `owner\tname\temail` plus the pool's identity row. A pool with no identity row is refused: launching a job under nobody's name is how a commit ends up carrying whatever the bench's git config held. Every refusal ends with PoolIdentityRemedy.
type StartupCost ¶
type StartupCost struct {
HarnessSHA256 string
FirstUsageCacheRead int
FirstUsageTurns int
Present bool
}
StartupCost is the harness's measured first turn: what the harness spends before the card says anything. Present is false when no file has been written yet, which accepts every budget.
func ReadStartupCost ¶
func ReadStartupCost(root string) (StartupCost, error)
ReadStartupCost reads `<root>/startup-cost.tsv`. An absent file is not an error and answers Present=false: no measurement accepts every budget. A header line naming the columns is skipped.
type SuperviseInput ¶
type SuperviseInput struct {
Pool *Pool
Task string
Slot int
Nonce string
Worker Worker
Sidecar Sidecar
Key string
// THE LAUNCH SEAM (docs/SPEC-SANDBOX.md, the dispatcher caller). Sandbox is the
// resolved nova-sandbox binary the harness is wrapped in; an EMPTY one is rule 11's
// one loud workaround, which the dispatcher has already announced for this job, and
// it is reachable only from a `--no-sandbox` a person typed.
Sandbox string
UsageInterval time.Duration
Stdout, Stderr io.Writer
Now func() time.Time
// Sleep is the wait between two polls of the group the supervisor confirms dead. It
// is a seam beside Now so a test can run the bounded drain and grace waits to their
// ends without holding the machine's clock; nil is time.Sleep.
Sleep func(time.Duration)
// Term fires when this supervisor should end from outside (SIGTERM in the process
// that runs `supervise`). Nil never fires. A closed or signalled channel reaps the
// harness group and returns; default Go death of this process would leave that group.
Term <-chan struct{}
}
SuperviseInput is everything `supervise` is handed.
type TaskDecision ¶
type TaskDecision struct {
Task string
Label string
Reason string
Confidence float64
NeedsHuman float64
}
TaskDecision is one finished task's typed decision: the reason choice, its confidence, and the needs_human probability, behind the floor. It is what nova-pulse run reads to send a task to HUMAN or to retry it (card 8371).
func DecideFinished ¶
func DecideFinished(p *Pool, do DecideFunc, floor float64, now func() time.Time) ([]TaskDecision, error)
DecideFinished asks the one typed decision for every finished task in the pool's done/ and failed/ directories that has not been decided already. It is the core of `nova-swarm triage --decide`, lifted into a function so nova-pulse run can call it after each harvest (card 8371). floor 0 takes DefaultDecideFloor.
type Timeline ¶
type Timeline struct {
// contains filtered or unexported fields
}
Timeline is an io.Writer over the child's output: it timestamps the harness's own NOVA-TIMELINE report lines as they arrive and keeps one row per completed span. It is safe for the child's concurrent stdout and stderr copies; a partial line is held until its newline arrives.
func (*Timeline) Rows ¶
func (t *Timeline) Rows() []TimelineRow
Rows returns a copy of the spans observed so far, in report order.
type TimelineRow ¶
type TimelineRow struct {
Start time.Time
End time.Time
Tool string // `model` for a turn, else the tool name and command
InputTokens string // empty when the harness reported none
OutputTokens string
}
TimelineRow is one observed event: a model turn or a tool call.
func ReadTimeline ¶
func ReadTimeline(path string) ([]TimelineRow, error)
ReadTimeline reads one card's timeline.tsv, mapping its columns by the header so a reader is not wedded to a column's position. An absent file is an empty timeline and no error, which is how a job that reported nothing phases as nothing rather than failing the fold.
type TimeoutHierarchy ¶
type TimeoutHierarchy struct {
// CardTimeout is the overall execution budget for the entire card/task (5s..2h).
CardTimeout time.Duration
// StepTimeout is the per-step or per-turn execution budget (5s..CardTimeout).
StepTimeout time.Duration
// GitTimeout is the bounded budget for VCS/git sub-operations (5s..min(StepTimeout, 1m)).
GitTimeout time.Duration
}
TimeoutHierarchy defines the multi-tier execution timeouts for card execution, enforcing invariants across all three layers:
CardTimeout >= StepTimeout >= GitTimeout
func DefaultTimeoutHierarchy ¶
func DefaultTimeoutHierarchy() TimeoutHierarchy
DefaultTimeoutHierarchy returns the canonical hierarchy using default budgets.
func NewTimeoutHierarchy ¶
func NewTimeoutHierarchy(card, step, git time.Duration) TimeoutHierarchy
NewTimeoutHierarchy creates a validated, bounded timeout hierarchy.
- card is clamped to [MinExecutionTimeout, MaxExecutionTimeout] (5s..2h).
- step is bounded to [MinExecutionTimeout, CardTimeout]. If step <= 0, it defaults to min(DefaultStepTimeout, CardTimeout) or CardTimeout if CardTimeout < DefaultStepTimeout.
- git is bounded to [MinGitTimeout, min(StepTimeout, MaxGitTimeout)]. If git <= 0, it defaults to min(StepTimeout/3, MaxGitTimeout), floored at MinGitTimeout (or StepTimeout if StepTimeout < MinGitTimeout).
func (TimeoutHierarchy) CardContext ¶
func (h TimeoutHierarchy) CardContext(parent context.Context) (context.Context, context.CancelFunc)
CardContext returns a context bounded by CardTimeout.
func (TimeoutHierarchy) GitBudget ¶
func (h TimeoutHierarchy) GitBudget(remainingStep time.Duration) time.Duration
GitBudget computes the budget for a git operation given the remaining step duration. It follows the canonical 1/3 allocation rule (matching busBounds):
git := remainingStep / 3, capped at MaxGitTimeout (1m), floored at MinGitTimeout (5s).
It never exceeds remainingStep.
func (TimeoutHierarchy) GitContext ¶
func (h TimeoutHierarchy) GitContext(parent context.Context, remainingStep time.Duration) (context.Context, context.CancelFunc)
GitContext returns a context bounded by GitBudget(remainingStep).
func (TimeoutHierarchy) StepBudget ¶
func (h TimeoutHierarchy) StepBudget(remainingCard time.Duration) time.Duration
StepBudget computes the remaining budget for a step given the remaining card duration. If remainingCard is zero or negative, no budget remains. Otherwise, the budget is bounded by min(h.StepTimeout, remainingCard).
func (TimeoutHierarchy) StepContext ¶
func (h TimeoutHierarchy) StepContext(parent context.Context, remainingCard time.Duration) (context.Context, context.CancelFunc)
StepContext returns a context bounded by StepBudget(remainingCard).
func (TimeoutHierarchy) Validate ¶
func (h TimeoutHierarchy) Validate() error
Validate checks that the hierarchy satisfies the ordering invariants.
type ToolchainRoot ¶
type ToolchainRoot struct {
// Name is the token BOTH lists carry, in slash form. A name that begins with "/" is a
// SYSTEM root and is that absolute directory -- or, when Tool is set, the versioned
// prefix under which one directory is resolved; any other name is HOME-RELATIVE and is
// joined to one bench home.
Name string
// Path is the absolute directory on one machine, resolved through its symlinks. It is
// set only by ToolchainRoots and is empty in the declaration.
Path string
// Exec is the kind: true means `--read`, readable AND executable; false means
// `--read-noexec`, readable and NOT executable.
Exec bool
// Tool, when set, is the program on the bench's PATH whose resolved launcher names the
// ONE versioned directory under Name that this bench actually runs -- `go` resolving to
// /opt/homebrew/Cellar/go/1.27.1/libexec/bin/go names /opt/homebrew/Cellar/go/1.27.1.
// The version belongs to the machine, so naming the prefix itself would grant every
// version ever brewed and hard-coding one would be stale on the next `brew upgrade`.
Tool string
// Optional says the provisioning standard does not DEMAND this root, only grants it when
// the bench has it. A linux bench's roots are the standard's own installs and a missing
// one is drift; a Mac bench's toolchains are installed by hand and by brew, so its roots
// are reported and never drifted on -- a Mac with no .NET is a Mac with no .NET, and the
// bench still has to have a Go, which the standard's own `go` check is what asserts.
Optional bool
}
ToolchainRoot is one entry of the one list: a directory the bench's toolchain lives in and THE KIND OF GRANT it gets. The kind is the security decision, not a detail of the argv -- a `--read` root CARRIES EXECUTE on both bodies (landlock's read subset is EXECUTE|READ_FILE|READ_DIR, and the darwin profile grants process-exec* globally), so a tree the bench user can write to must be named as the read-only kind or a card can run whatever lands in it (Johnny's security read of #1364).
func ToolchainRootList ¶
func ToolchainRootList(goos string) []ToolchainRoot
ToolchainRootList is the declaration itself for one operating system, kinds and all: what a reader of the wall and the class test in internal/ci ask when the question is not "which paths" but "granted HOW". An OS the list does not speak for names nothing at all, which is the behaviour of every run before the roots existed and never a guess at another OS's layout. A copy, so no caller can edit the one source by holding it.
func ToolchainRoots ¶
func ToolchainRoots(goos, home string) []ToolchainRoot
ToolchainRoots is the wall's side: this machine's list, absolute, carrying each root's kind, and SKIPPED IF ABSENT. Absent is the machine's shape and not the caller's mistake -- a Mac bench has no `~/sdk` and may have no dotnet -- while rule 5 of the wall REFUSES a `--read` or a `--read-noexec` naming a path that is not there, so a root that does not exist must never reach the argv. An empty home names no HOME-relative root, because a relative root is a refusal and a root at the filesystem's top is not a toolchain; a system root needs no home and is still named.
Every path is RESOLVED THROUGH ITS SYMLINKS before it is named, because the grant is checked against the resolved target on both bodies: `/opt/homebrew/opt/openjdk` is itself a symlink into the Cellar, and granting the link would name a tree the card's `java` is not under.
func (ToolchainRoot) Home ¶
func (r ToolchainRoot) Home() bool
Home says whether this root is found under a bench home rather than at an absolute path.
type TriageAction ¶
type TriageAction string
TriageAction is the routing action determined by failure classification.
const ( // ActionRetry means the card should be retried with backoff. ActionRetry TriageAction = "retry" // ActionQuarantine means the card is placed in queue/quarantine/<reason>/<card-id>. ActionQuarantine TriageAction = "quarantine" // ActionFail means the failure is treated as normal test failure. ActionFail TriageAction = "fail" )
type TriageInput ¶
type TriageInput struct {
Pool *Pool
Batch string
Dirs []string
Since string
All bool
NoState bool
Max int
Owed []string
Stdout, Stderr io.Writer
Now func() time.Time
// Decide asks TypeSafe Jev for one typed abstain reason per finished
// task (reason choice + needs_human noul) behind Floor, and appends it
// to each TRIAGE REPORT line. Below the floor the line says decide=?
// and the pool's own class stands; a provider error keeps today's bare
// line. KeyEnv names the environment variable holding the key
// (JEV_API_KEY when empty); BaseURL overrides the endpoint, for the
// httptest fake in tests.
Decide bool
Floor float64
KeyEnv string
BaseURL string
UsagePath string
// contains filtered or unexported fields
}
TriageInput is one triage run.
type TriageRouteResult ¶
type TriageRouteResult struct {
CardID string `json:"card_id"`
Action TriageAction `json:"action"`
Reason string `json:"reason"`
FailureKind FailureKind `json:"failure_kind"`
Attempts int `json:"attempts"`
TargetDir string `json:"target_dir,omitempty"`
Message string `json:"message"`
}
TriageRouteResult holds the result of triage routing.
func RouteFailure ¶
func RouteFailure(queueDir, cardID string, cardContent []byte, attempts, maxAttempts int, fc FailureClassification) (TriageRouteResult, error)
RouteFailure routes a failed card according to the failure taxonomy:
- CardDefect -> immediately quarantined under queue/quarantine/<reason>/<card-id>
- TransientProvider -> retried if attempts < maxAttempts; quarantined if attempts >= maxAttempts
- InfraCrash -> retried if transient, quarantined if exhausted/recurrent
- TestFailure -> marked fail
type TriageState ¶
type TriageState struct {
Version int `json:"version"`
Runs int `json:"runs"`
LastISO string `json:"last_iso"`
LastCount int `json:"last_count"`
Consumed map[string]string `json:"consumed"`
}
TriageState is <pool>/triage.json: what has been folded into a page already.
type TrustState ¶
TrustState is the coordinator's per-kind state, keyed by kind: `trial`, `trusted` or `paused` (SPEC-TOOLWORK.md eligibility rule 1's TRUST listing).
func ReadTrustFixture ¶
func ReadTrustFixture(path string) (TrustState, error)
ReadTrustFixture reads the coordinator's per-kind state from a file in the shape `nova-pulse trust` prints (SPEC-TOOLWORK.md eligibility rule 1):
TRUST kind=<kind> area=<area> state=<trial|trusted|paused> cards=<n>/<N> ... TRUST OK kinds=<n> trial=<n> trusted=<n> paused=<n>
THE VERB DOES NOT EXIST YET. `nova-pulse trust` is the other half of T06a and belongs to the lane that owns `internal/pulse`; until it lands, the bench hands `lint --card` a fixture in exactly this shape with `--trust <file>`, and the day the verb ships its own stdout is the fixture. Lines that are not TRUST rows, and the TRUST OK summary, are read past, so a whole listing can be saved to a file and handed over unedited.
type Verdict ¶
type Verdict struct {
Who string `json:"who"`
Accurate int `json:"accurate"`
Wrong int `json:"wrong"`
}
Verdict is a reader's counts for one task, recorded by a person (rule 8).
type WallReader ¶
type WallReader struct {
// contains filtered or unexported fields
}
WallReader is an io.Writer over the child's own output, put in the capture chain beside the log files and the timeline so every byte is seen AS IT ARRIVES rather than re-read from a file once the child is gone. It holds a partial line until its newline, the way Timeline does, and it is safe for the child's concurrent stdout and stderr copies.
It decides nothing. It remembers the first refusal, the last STEP the card reached, and whether the card made another tool call after the refusal -- and it calls announce ONCE, with the typed line, the first time a refusal appears.
func NewWallReader ¶
func NewWallReader(task string, announce func(string)) *WallReader
NewWallReader returns a reader for one card. announce is called once, from the writing goroutine, with the typed line of the first refusal; a nil announce says nothing.
func (*WallReader) Step ¶
func (r *WallReader) Step() string
Step is the last `STEP <n>` the card printed, or "" when it printed none.
func (*WallReader) Stopped ¶
func (r *WallReader) Stopped() (WallRefusal, string, bool)
Stopped is the refusal that stopped this card -- a refusal with no tool call after it -- and false when there was no refusal or the card moved past the one there was.
type WallRefusal ¶
WallRefusal is one wall death: the path the wall refused and the last step the card reached. An empty Path or Step is the dash the report line writes.
func WallRefused ¶
func WallRefused(log []byte) (WallRefusal, bool)
WallRefused reports the first path the harness's own fence or the OS wall refused in one log, and whether either refused anything. The first is the one that matters: the model stops at it, and every later line is a consequence of the same closed path.
func WallStopped ¶
func WallStopped(log []byte) (WallRefusal, bool)
WallStopped is the refusal that STOPPED a card, which is not the same question WallRefused answers. WallRefused reports the FIRST refusal in a log whether or not the card survived it; this reports one only when the card made no further tool call after it.
The difference is a whole card. `js-under-20-bytes` took a refusal in the harness's startup banner, ran sixteen more model steps, and died in a provider stall twenty minutes later; WallRefused named the banner and the shift went looking at the wall.
type WindowsKillStrategy ¶
type WindowsKillStrategy int
WindowsKillStrategy specifies the termination mechanism on Windows: taskkill vs direct syscall.
const ( // StrategyTaskkill uses taskkill.exe /T (/F) to terminate the full process tree. StrategyTaskkill WindowsKillStrategy = iota // StrategySyscall uses direct Win32 syscall (TerminateProcess / os.Process.Kill) on the PID. StrategySyscall )
func (WindowsKillStrategy) String ¶
func (s WindowsKillStrategy) String() string
String returns the human-readable name of the strategy.
type Worker ¶
type Worker struct {
Name string `json:"name"`
Provider string `json:"provider"`
Model string `json:"model"`
BaseURL string `json:"base_url,omitempty"`
EnvVar string `json:"env_var"`
KeyFile string `json:"key_file"`
// SECRET (issue #881): the NAME of the environment variable that holds the key in
// THIS process's own environment, delivered by `nova-secrets exec` around the run
// (docs/SPEC-SECRETS.md, the second caller). It is the replacement for `key_file`
// when the key is sealed once and delivered at use and NEVER a file on disk. Exactly
// one of `key_file` and `secret` is set; run and supervise require that variable to
// be present and non-empty, and the value is never written to a file, never printed,
// and never in a RUN or SUPERVISE line.
Secret string `json:"secret,omitempty"`
Usage string `json:"usage"`
// CLASS (CARD-8390): `public` means this worker never sees private source
// (the public-class gate); `paid` is the default. Empty decodes as paid so
// no description written before the gate changes meaning by being re-read.
//
// CLASS (worker check): `class` is the one word public or paid, an OPTIONAL
// field a description may carry and the launcher never requires. `nova-swarm
// worker check` validates it when it is present. The two budgets below are
// the same fields worker check validates.
Class string `json:"class,omitempty"`
Harness string `json:"harness"`
HarnessArgs []string `json:"harness_args,omitempty"`
WorkerDir string `json:"worker_dir"`
Deadline string `json:"deadline"`
Board string `json:"board,omitempty"`
// READ ROOTS: the one field the wall added (docs/SPEC-SANDBOX.md, "there is no --root
// flag"). Every job runs inside nova-sandbox, whose read set is the OS and toolchain
// roots plus what the caller names; a toolchain installed into a USER directory -- Go
// under ~/go, node under ~/.nvm, the Studio's /Users/<user>/toolchains -- is under no
// system root, so a harness that needs one dies inside the wall and runs outside it.
// It is OPTIONAL, it is a list of absolute existing directories, and it is READ-ONLY:
// the write set is the job's own and is never configurable from a file. A worker that
// needs nothing beyond the system roots names nothing here.
ReadRoots []string `json:"read_roots,omitempty"`
// CARD BUDGET (CARD-8317): the optional per-card stop. A runaway card is a
// prompt defect, not a model one: max_turns caps the harness output log's
// assistant turns and max_cache_read caps the observed cache_read, and run
// stops the card at either with end=budget and a PROMPT-DEFECT line. Zero
// is unset; negative is refused at load.
MaxTurns int `json:"max_turns,omitempty"`
MaxCacheRead int `json:"max_cache_read,omitempty"`
// PROVIDER PHRASES: the second optional field, and it is the TRIAGE LINE'S (#103). A
// job that dies because the request did not fit is its own failure class, and the only
// evidence of it is a sentence the provider printed -- in the provider's own words.
// OpenCode says `Rate limit reached: input token limit exceeded`; the tool carries a
// table of the sentences it has met (InputLimitPhrases), and a description may ADD the
// one its provider uses. It never SUBSTITUTES: a caller teaching the tool their
// provider's words cannot silently un-teach it another's.
InputLimitPhrases []string `json:"input_limit_phrases,omitempty"`
// LAUNCH GRACE (issue #900): how long a harness may run before its exit stops
// counting as a LAUNCH failure. Nine of forty Muse requests died in under two
// seconds with a provider 5xx and wasted the slot they held; a death inside this
// window whose tail names a provider server error is a launch that did not take,
// and the dispatcher retries it instead of filing it. OPTIONAL: the default is
// DefaultLaunchGrace (15s). A slow failure -- one that takes longer than this -- is
// a real run that failed and is never retried.
LaunchGrace string `json:"launch_grace,omitempty"`
// KEEP DATA (issue #1048): a worker description that sets keep_data=true asks `run` not
// to reap the finished slots' data/ and tmp/ at task end. It is a person's debugging
// switch: the default is false, and the shared per-bench caches under <root>/cache are
// never touched either way.
KeepData bool `json:"keep_data,omitempty"`
}
Worker is a worker description, decoded strictly: an unknown field is a refusal, because a misspelled field in a file that names a key's location is a silent default.
func LoadWorker ¶
LoadWorker reads and checks a worker description, reporting EVERY independent problem in one run (ONBOARDING point 2) rather than sending a first run back once per field.
func (Worker) BudgetRefusal ¶
func (w Worker) BudgetRefusal(s StartupCost) string
BudgetRefusal is the one line a card budget below the measured startup cost is refused with, or "" when the budget stands or no measurement exists. A max_cache_read below twice the measured first cache read cannot leave room for the card; a max_turns below the measured first turns plus two stops the card before its first real turn. The refusal names both numbers.
func (Worker) DataHome ¶
DataHome is the job's own data home, exported as XDG_DATA_HOME so the harness keeps its own database there, one per job. It is the whole reason slots exist.
func (Worker) DefaultDeadline ¶
DefaultDeadline is the worker description's own, which add --deadline overrides per task.
func (Worker) HarnessConfig ¶
HarnessConfig is the config this tool writes for the harness, and it carries the environment variable's NAME, never its value: a value written here would be a key at rest in a directory nobody treats as a secret store. It is rewritten at every start, so a stale provider declaration cannot outlive a worker description.
func (Worker) HasCardBudget ¶
HasCardBudget reports whether the description carries any card budget.
func (Worker) OverCardBudget ¶
func (w Worker) OverCardBudget(u ProviderUsage, turns int) (over bool, cacheRead, max int)
OverCardBudget reports whether the observed usage and turn count exceed the description's card budget. It returns the observed cache_read (0 for an absence, which never fires) and the budget that fired: max_cache_read where the cache fired, else max_turns.
func (Worker) RefreshSlot ¶
RefreshSlot copies the home worker directory into the slot directory, ONE WAY. It is a copy rather than a mount or a link: a worker that could write back into the home copy could change the next worker's self, and the next worker would load it without anybody reading the change. The jobs/ subtree is left alone, because it is the slot's own work and not the home copy's.
Source Files
¶
- activity.go
- activity_linux.go
- admitrepo.go
- batch.go
- bench.go
- benchpull.go
- benchsize.go
- capacity.go
- cardbudget.go
- cardout.go
- cardpipeline.go
- contract.go
- cost.go
- deadline.go
- decide.go
- evidence.go
- fence.go
- fileretry.go
- fileretry_other.go
- finalize.go
- finish.go
- gatebench.go
- hangup_unix.go
- inflight.go
- inputlimit.go
- key.go
- killpoint_release.go
- launchfail.go
- launchrecord.go
- lease.go
- lintbase.go
- lintcontract.go
- lintdepends.go
- lintheader.go
- lintparent.go
- linttestgate.go
- lock.go
- lock_unix.go
- nativeasked.go
- nativebudget.go
- nativeidle.go
- nofollow_unix.go
- note.go
- opencode.go
- pathcase.go
- pool.go
- prewarm.go
- proc_linux.go
- proc_unix.go
- profile.go
- providererr.go
- providerproxy.go
- providerread.go
- providers.go
- publicclass.go
- pull.go
- pullbatch.go
- pullcard.go
- pullqueue.go
- pullsubmit.go
- pullworker.go
- quarantine.go
- queue.go
- reap.go
- regular.go
- result.go
- resultformat.go
- resultlint.go
- route.go
- routeladder.go
- run.go
- sandbox.go
- share.go
- signature.go
- slot.go
- slots.go
- slotwait.go
- slotweight.go
- sparse.go
- stage.go
- staging.go
- supervise.go
- taxonomy.go
- templates.go
- timeline.go
- timeout.go
- toolchain.go
- triage.go
- usage.go
- usagecard.go
- wall.go
- wallread.go
- wallterms.go
- warm.go
- worker.go