Documentation
¶
Overview ¶
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 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.
Index ¶
- Constants
- Variables
- func AbsResolved(path string) (string, error)
- func Alive(pid int, started string) bool
- func AppendCardUsage(path string, row UsageRow) 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 BenchGoBin(goos, home, pathEnv string) string
- func BenchPath(goos, home, pathEnv string) []string
- func BenchToolBins(goos, home string) []string
- func BriefRemedy(check string) string
- func BudgetWord(unmetered bool, tokens, spent int, observed, partial bool) string
- func CacheRoot(root string) string
- func CardCacheRead(u ProviderUsage) (int, bool)
- func CardCloneRepos(text string) []string
- func CardHeaderChecks() []string
- func CardHeaderValue(raw []byte, key string) (value string, ok bool)
- func CardIdleLine(task string, e IdleEnd) string
- func CardKindFromText(text string) string
- func CardNamesRepo(card []byte) bool
- func CardParentPaths(lines []string) []int
- func CardPaths(raw []byte) []string
- func CardPrompt(card []byte) string
- func CardReadPaths(card []byte) []string
- func CardRepoURL(value string) string
- func CardStageBranch(card []byte) string
- func CheckPublicCard(w Worker, cardText, root string) (string, bool)
- func CheckStagedTree(jobRoot string) error
- func ChildRemedy(rules []ChildRule, check string) string
- func ChildRulesParagraph() string
- func ClassifyProvider(status int, text string) string
- func CountCardTurnsIn(logPath string, u ProviderUsage) int
- func DeclareRouteModel(body []byte, provider, model string) ([]byte, bool)
- func DeniedPathRoots(denied string) []string
- func EnsureCacheDirs(root string) error
- 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 FindBenchMirror(benchHome, baseRepo string) string
- func FindCardResult(job string) (string, bool)
- func FriendGoCacheLine(name string) string
- func GoBuildCacheDir(root string) string
- func GoModCacheDir(root string) string
- func GroupAlive(pgid int, started string) bool
- func HarnessTail(jobDir string) string
- func HashBytes(data []byte) string
- func HeadlessArgv(kind, bin, model, prompt string) ([]string, error)
- func HeadlessLogin(kind string, out []byte, rc int) (loggedIn bool, said string)
- func HeadlessLoginArgv(kind string) []string
- func HeadlessProgramRoot(resolved string) string
- func HeadlessTokenNote(h HeadlessHome, env []string) string
- func HeldRulesNames() []string
- func HeldRulesText(name string) ([]byte, bool)
- func IsCardContractLine(line string) bool
- func IsCardTemplate(name string) bool
- func IsPulseTemplate(name string) bool
- func JobASDFOutputTranslations(sourceRoot string) string
- func JobLispCacheDir(sourceRoot string) string
- func KillGroup(pgid int, started string)
- func LaunchArgv(provider, goos string) ([]string, error)
- func LaunchArgvFor(provider, goos string, req LaunchRequest) ([]string, error)
- func LaunchGrace(w Worker) time.Duration
- func LaunchRow(provider string) string
- func LintBrief(raw []byte, bb BriefBase) ([]CardHeaderFinding, BriefFix)
- func LispCacheDir(root string) string
- func LostResponse(tail []byte) bool
- func MakeSlotLease(store, id, owner string, pid int, label string, until time.Time) error
- func MergeFencePermission(raw []byte, jobDir string, reads []string) ([]byte, bool)
- func MirrorCloneArgs(mirror, target string, borrow bool) []string
- func MirrorPath(benchHome, name string) string
- func NPMCacheDir(root string) string
- func NativeBudgetSourceRefusal(source string, tokens int, unmetered, usd bool, worker *Worker) string
- func NativeUsageIntervalRefusal(interval, deadline time.Duration) string
- func NativeUsageSource(worker *Worker) string
- func NextRoute(routes []string, failed string) string
- func Nonce() (string, error)
- func NormalizePath(path string) string
- func OpenCodeStoreLocations(dataHome string) []string
- func OwnRulesName(brief, unnamed string) string
- func ParseCardBase(card []byte) (baseRepo, baseSha string, ok bool)
- func ParseRouteList(s string) []string
- func PointCardAtMirrors(card, benchHome string) string
- 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 PromptDefectLine(id string, cacheRead, max, turns int) string
- func ProviderBaseURL(raw []byte, provider string) string
- func ProviderLaunchFailure(raw []byte) (ref string, ok bool)
- func ProviderProxyEligible(raw string) bool
- func PublicRefusalLine(repo, workerName string) string
- func PublicRefusalWhy(repo, workerName string) string
- func ReadJSON(path string, v any) error
- func Reap(pgid int, started string, grace time.Duration) (survived bool)
- func RecordStartupCost(root, harnessSHA256 string, cacheRead, turns int) error
- func RecordStartupCostIfAbsent(root, harnessSHA256 string, cacheRead, turns int) 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 ResolveInstructionKind(raw []byte) (work, instruction string, ok bool)
- func ResultPath(jobDir string) string
- func RulesParagraph(rules []ChildRule) string
- func SQLiteOnPath() bool
- func SecretFromEnv(name string) (string, error)
- func ShellDenialReason(label, jobDir, wall string, rc int, d ShellDenial) string
- func SlotAdmissionWeight(kind string) int
- func StageCloneIdentity(repoDir string, id StagingIdentity) error
- func StageJob(poolDir, jobRoot, repoDir string) error
- func StagedBrief(brief string, rules []ChildRule) string
- func StagingGitEnv(id StagingIdentity) []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 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 TakeSlotLeasesWaiting(ctx context.Context, store, owner string, k int, kind string, ...) (ids []string, held, share, free int, holders string, ok bool, err error)
- 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 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 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 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 AskedRun
- type BaseCheck
- type BriefBase
- type BriefFix
- type CardBase
- type CardHeaderFinding
- func LintCardBase(raw []byte, bc BaseCheck) []CardHeaderFinding
- func LintCardChild(raw []byte) []CardHeaderFinding
- func LintCardChildByReference(raw []byte, rules []ChildRule) []CardHeaderFinding
- func LintCardChildWith(raw []byte, rules []ChildRule) []CardHeaderFinding
- func LintCardDepends(raw []byte, lineup Lineup) []CardHeaderFinding
- func LintCardHeader(raw []byte, trust TrustState, required bool) []CardHeaderFinding
- func UnfilledTemplateLines(card string) []CardHeaderFinding
- type ChildRule
- type CmdRunner
- type Contract
- type ExitRecord
- type Finding
- type FleetLegs
- type Gate
- type Handback
- type HeadlessHome
- type IdleEnd
- type IdleWatch
- type Item
- type JobLease
- type JobLeaseHeldError
- type JobProfile
- type KindP95
- type LaunchRequest
- type Lineup
- type MirrorKeeper
- type MirrorRefresh
- type Outcome
- type ProviderCause
- 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 HeadlessUsage(kind string, out []byte) (ProviderUsage, error)
- func ReadCardUsageAfter(dataHome string, started, ended, notBefore time.Time) (ProviderUsage, string, string, string)
- func ReadJobUsageLiveWithin(dataHome string, limit time.Duration) (ProviderUsage, error)
- func ReadProviderUsage(source, dataHome string) (ProviderUsage, error)
- type Report
- type Rework
- type ShellDenial
- type ShellDenialReader
- type SlotLease
- type StageOptions
- type StageResult
- type StagingIdentity
- type StartupCost
- type Timeline
- type TimelineRow
- type ToolchainRoot
- type TrustState
- type UsageRow
- 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)
Constants ¶
const ( // FenceAllow 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. So the fence's // fallback is deny, and `ask` is never written: there is no terminal to answer it. FenceAllow = "allow" 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. 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.
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, 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 ( StartNamedRemedy = "" /* 199-byte string literal not displayed */ StopNamedRemedy = "" /* 167-byte string literal not displayed */ )
StartNamedRemedy and StopNamedRemedy are what the two brief-grammar tokens want, in one line, the same contract as CardHeaderRemedies. They are separate from that map because CardHeaderChecks is the four tokens of §5 rule 1; `cmd/nova-swarm/lint.go` merges these into its cardLintRemedies, so a start-named or stop-named line names its remedy.
const ( StartNamedCheck = "start-named" StopNamedCheck = "stop-named" )
StartNamedCheck and StopNamedCheck are the tokens for the two reading lines of the brief's header grammar: `START: <files or packages to read first>` and `STOP: <the condition that ends the task>` (docs/SPEC-CARD-CONTRACT.md §2). A brief missing either draws the finding; `cmd/nova-swarm/lint.go` refuses it for a tier flash brief and prints it as a note for tier pro, or no tier. Both carry a remedy in StartNamedRemedy and StopNamedRemedy above, so the note names what to write.
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 ( CauseUnknownModel = "unknown-model" // the provider does not know or serve the model id: a config error CauseAuth = "auth" // the key is missing, wrong or refused CauseCredit = "out-of-credit" // the account has no credit or quota left CauseRateLimited = "rate-limited" // a rate limit, the provider's or upstream of it Cause5xx = "provider-5xx" // the provider's own server error or outage CauseTimeout = "timeout" // the provider took too long to answer CauseOther = "other" // none of the above, or the harness recorded no cause )
The classes of a provider failure.
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 ( ClassOK = "ok" ClassClean = "clean" ClassPlanOnly = "plan-only" ClassMalformed = "malformed" )
The classes.
const ( QueueName = "queue" TakenName = "taken" )
The slot store's card directories: queue/ holds a card waiting for a lease, taken/ the card a lease holds, one <owner>-<label> each.
const ( SlotWeightRead = 1 SlotWeightHeavy = 4 )
const ( EndDone = "done" EndBudget = "budget" EndUnverifiable = "budget-unverifiable" EndFailed = "failed" EndUnknown = "unknown" // 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. 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. 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. 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 (pkg/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 ( ModelPlaceholder = "{model}" PromptPlaceholder = "{prompt}" )
The placeholders a worker description may write into harness_args.
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 (
// CacheDirName is the one shared cache directory under a swarm root.
CacheDirName = "cache"
)
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 the benches 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 CardContractWanted = "" /* 163-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 CardParentPathWanted = "" /* 371-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 ClaudeTokenEnv = "CLAUDE_CODE_OAUTH_TOKEN"
ClaudeTokenEnv is the environment name claude reads its long-lived login token from (`claude setup-token`), the bench's nova-secrets key a member hands its children with --pass. The value is never written to a file, never printed, and unset in every shell the harness starts (cmd/nova-swarm shellshim.go).
const CopiedResult = "RESULT.md"
CopiedResult is the name a published report is kept under.
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 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. 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 DefaultRulesName = "child-rules.txt"
DefaultRulesName is this repository's own rules file, fleet/child-rules.txt.
const DefaultRulesSource = "the built-in default rules"
DefaultRulesSource is where the default rules come from, as a rule's remedy names it.
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. The `run` route names its own flag and `native` takes the same flag and the same default.
const EmptyCardCheck = "empty"
EmptyCardCheck is the one finding an empty card draws (only blanks in it): a card is a child's whole brief, so an empty one is said once, never as every rule it does not quote.
const EmptyCardRemedy = "" /* 160-byte string literal not displayed */
EmptyCardRemedy is what that token wants, in the remedies' table shape.
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 GateNamesWhoseFile = "" /* 209-byte string literal not displayed */
GateNamesWhoseFile is the sentence the gate step of a card ends with (docs/SPEC-CARD-CONTRACT.md, the card's steps): a red gate line names its file and says whether the file is the child's own (yours) or is unchanged from BASE, so the child neither fixes a file it may not touch nor reports a failure that is not its own. templateCard and cardgen.Render share it.
const GoCacheLine = "GOCACHE is already set to the machine's shared build cache (JOB.md names it): keep it."
GoCacheLine is the one sentence every card carries about the build cache: the machine's shared, warm GOCACHE is already set (JOB.md names it), so the child keeps it and never exports or chooses one of its own (docs/SPEC-CARD-CONTRACT.md section 2, the staged environment). templateCard and cardgen.Render share it, so the two generators cannot drift.
const HomeRepo = "nova-tools"
HomeRepo is the repository whose rules file is DefaultRulesName: the one these files live in.
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, 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 name the result lookup above tries first.
const LedgerKind = "ledger"
LedgerKind is the KIND: of a ledger card, the card nova-card generate cuts from a ratchet ledger of internal/ci (internal/cardgen PlanLedger). Its TEST is the ledger's class test, green at the base by construction, and its proof is the ledger shrinking with that test still green, never a test red at the base. So donewhen-test-name lets a TEST that exists at the base pass for such a card, and for no other: only when the brief says KIND: ledger AND its TEST names a test under internal/ci/ (classTestPackage).
const LibrariesConsideredName = "libraries-considered"
LibrariesConsideredName is the name of the rule that switches the lint's libraries check on, in a rules file (fleet/child-rules.txt carries it): the check belongs to the project whose standard is library first, as the Go scans belong to a Go project's rules. Its sentence is what the child reads; the lint does not require the sentence verbatim, it requires the filled `Libraries considered:` line of a card that builds code.
const LibrariesConsideredRemedy = "" /* 406-byte string literal not displayed */
LibrariesConsideredRemedy is what that token wants, in the remedies' table shape.
const LibrariesConsideredRule = "rule-" + LibrariesConsideredName
LibrariesConsideredRule is the token of the line a card that builds code carries.
const LiveSampleLimit = 5 * time.Second
LiveSampleLimit is the least 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". The sampler raises it against the slowest read that answered (nova-swarm's liveSampler.readLimit), and never relates it to the interval: at a short interval slow reads would be counted as failures.
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 NoIdleWindow = time.Duration(0)
NoIdleWindow is the window `--idle 0` reaches WatchIdle as: no watch at all. 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 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.
const OpenCodeDB = "opencode/opencode.db"
OpenCodeDB is the database's path inside the job's data home (XDG_DATA_HOME).
const PatternsBlockCheck = "patterns-block-without-class-test"
PatternsBlockCheck is the finding a PATTERNS TO REFUSE paragraph draws in a card that names no class test under internal/ci (docs/SPEC-CARD-CONTRACT.md, lint-allows-quoted-patterns-in-tests).
const PatternsBlockRemedy = "" /* 202-byte string literal not displayed */
PatternsBlockRemedy is what that token wants.
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 linux: `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 PlaceholderCheck = "placeholder"
PlaceholderCheck is the token of a line of the card template left unfilled.
const PoolIdentityRemedy = "remedy: make -C fleet converge (the repository) writes the pool identity.tsv"
PoolIdentityRemedy is the one remedy every pool identity refusal names. The pool identity is bench configuration: the fleet converge writes <pool>/identity.tsv, 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 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 whitespace 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 `native` 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.
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 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 UsageIntervalFloor = time.Second
UsageIntervalFloor is the shortest interval `native` accepts. Each sample launches sqlite3 against the harness's own live database, and under a second that is more launches than there is anything new to read.
Variables ¶
var BriefBaseRemedies = map[string]string{
"paths-cover-named": "every repository path START: and THE TASK name as a file to change is in PATHS: (or NEW:); mark a START entry the card only reads `(read)`; apply the corrected PATHS: line add printed",
"paths-cover-test": "PATHS: covers the TEST package's `_test` files (`<pkg>/*_test.go`, or the new test file by name), since the card lands with the test it names; apply the corrected PATHS: line add printed",
"paths-cover-testdata": "a `<dir>/*.go` entry does not reach `<dir>/testdata`; when the package has testdata, PATHS: names `<dir>/testdata/**` too; apply the corrected PATHS: line add printed",
"paths-cover-ledgers": "a card that changes a package's Go code can add or remove the functions and error sites its class-test ledgers under internal/ci/testdata count (errcheck, discarded, staticcheck, the dead code ledger); name each such ledger on PATHS: and on SHARED:, since other cards touch it too; apply the corrected lines add printed",
"paths-cover-docs": "a card that changes a cmd/<tool> verb or flag changes its documentation: name docs/CLI.md and the tool's SPEC (docs/SPEC-<TOOL>.md) on PATHS: and on SHARED:; apply the corrected lines add printed",
"base-is-live": "BASE: is a sprint base origin holds (sprint/<name>, a base the sprint's cards use, or the trunk for the promotion stream; nova-sprint bases lists them), never a temporary branch (tmp/, wip/, scratch/) or one origin no longer holds; re-cut the card on the sprint branch",
"tier-set": "line 1 names the card's tier, `tier: flash|pro|heavy|frontier`; a card with no tier, or `-`, was dealt to friends who could not serve it",
"tla-is-frontier": "a card whose PATHS: covers tla/ changes a TLA+ model, and a model is the frontier tier's work: write `tier: frontier` on line 1, or take tla/ out of PATHS: and give the model to its own card",
"who-serves-tier": "the friend WHO: names serves the card's tier (her row's tiers, else her class), or for `WHO: friend` some friend does; name a friend who serves it, raise the tier to one she serves, or drop the WHO: line so the fleet deals it",
}
BriefBaseRemedies is what each brief token wants, in the table shape of CardBaseRemedies and beside it: these hold a brief at add (nova-sprint), not a card under `nova-swarm lint`, so they are their own table and `nova-swarm lint --rules` does not count them as its checks. paths-at-base and donewhen-test-name answer from CardBaseRemedies.
var BudgetColumns = []string{"tokens_in", "tokens_out", "reasoning"}
BudgetColumns are the columns the 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. 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>`",
"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 member pushes the card's commit at its finish; say `no gh, no push` in RULES, never as a STEP command",
"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>`",
"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>`",
"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",
}
CardBaseRemedies is what each base token wants, in the table shape of CardHeaderRemedies, so `nova-swarm lint --rules` prints them beside the rest. The brief tokens nova-sprint add holds a card brief to at its BASE tip (lintpaths.go) are beside them in BriefBaseRemedies; add runs paths-at-base and donewhen-test-name there too.
var CardChildRemedies = map[string]string{}
CardChildRemedies is what each child-rule token of the default set wants, in the table shape of CardHeaderRemedies, so `nova-swarm lint --rules` prints them beside the rest. It is built from DefaultChildRules and childScans: one row each, never a second list. A rule of a rules file is not in it; ChildRemedy answers for those.
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 (the deleted spec-pulse page 10, the-card-as-cut-writes-it, lines 12-14). Three readers touch it and two of them disagreed on one character:
- `cut` writes and accepts `RESULT <label> sha=<sha12>` -- no colon (pulse/cut.go at 27c9ffc66, and the example at that page, line 4);
- `lint --card`'s `result-first` wanted `RESULT: ` -- with one (WORKER-CARDS.md practice 1, now in the nova-work-old repository, and 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 (pulse/harvest.go at 27c9ffc66, 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 (a friend, 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` (pulse/cutkind.go at 27c9ffc66), `pulse/manager.go at 27c9ffc66`. `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 (`pulse/cut.go at 27c9ffc66`), 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. pkg/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 the pool's kinds list names; the cutter writes it from the pool row and a model never does; a declared kind whose row names no instruction kind is this finding too: name the instruction kind on that row of the one kinds list",
"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`",
"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",
"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>`",
}
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.
The two brief-grammar tokens are not in this map: CardHeaderChecks is the four of §5 rule 1, and gap_test pins that list. Their remedies are StartNamedRemedy and StopNamedRemedy below, merged into `cmd/nova-swarm/lint.go`'s cardLintRemedies.
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"), the pulse package'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 DefaultChildRules = []ChildRule{ {"worktree", "Work only in the job directory this card names.", DefaultRulesSource}, {"no-force-push", "Never force-push or rebase a shared branch.", DefaultRulesSource}, {"no-kill", "Never kill a process you did not start.", DefaultRulesSource}, {"no-server", "Never start a server on this machine.", DefaultRulesSource}, {"no-rm-rf", "No `rm -rf` outside the job directory.", DefaultRulesSource}, {"report-not-done", "Report what was not done.", DefaultRulesSource}, }
DefaultChildRules is the set that holds for any project, used when the coordinator names no rules file, in the order the RULES paragraph of the template prints them. A rule that holds for one project only (its build caches, its test flags, its commit trailer, its CI) belongs in that project's rules file.
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 ProviderRetryDelay = func(failed int) time.Duration { return providerRetryDelay(failed, os.Getenv("NOVA_SWARM_PROVIDER_BACKOFF")) }
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 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.
Functions ¶
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 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, and a tab or a newline in a value is scrubbed so the row is always one row.
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 BenchGoBin ¶
BenchGoBin is the directory of the Go the bench's card environment runs: the first `go` in BenchToolBins, then GOPATH/bin under home, then pathEnv, resolved through its symlinks to the directory it REALLY lives in -- GOROOT/bin, holding `go` and `gofmt` and nothing else -- or "" when the bench has none. A `go` that resolves outside every root the wall executes is the standard's drift to report (tools/benchstandard, the wall check), not a root to widen here. It reads the filesystem only and runs nothing.
func BenchPath ¶
BenchPath is what native puts on a card's PATH ahead of the member's own, in order: BenchToolBins, then BenchGoBin when the bench has a Go (`gofmt` lives beside `go` in GOROOT/bin, and `~/sdk/bin` may link `go` alone). Empty names nothing and leaves the child's PATH as it was.
func BenchToolBins ¶
BenchToolBins is the `bin` of each home root the wall grants EXECUTE on, under home and in the list's order, skipped if absent: on a provisioned bench `~/sdk/bin`, the standard's links to every sdk tool. It reads the filesystem only and runs nothing.
func BriefRemedy ¶
BriefRemedy is what a brief token wants: BriefBaseRemedies, else CardBaseRemedies.
func BudgetWord ¶
BudgetWord is the 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: what is said of the word, the sum, the stop, the record and a source that cannot be read holds for a native card too. 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 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 CardHeaderValue ¶
CardHeaderValue is the value of one key of a card's typed header block, read as the lint and the gate read the block (the unbroken run of `KEY: value` lines under the contract line); ok is false when the block has no such line. nova-sprint add reads DEPENDS-ON: and PATHS: through it.
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 CardPaths ¶
CardPaths is the typed header's PATHS and NEW globs (docs/SPEC-SPRINT.md, "A card is a tree of steps"). Both name files the card may change; a key in the body or a step grants no scope. PATHS: none names no files.
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: worker/<label> from line 1 (RESULT: <label> sha=...), worker/card when line 1 names no label.
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 ChildRemedy ¶
ChildRemedy is what one child-rule check wants under the given rule set: the scan's remedy for a `step-` token, the rule's sentence for a `rule-` token of the set, and "" for a token that is neither.
func ChildRulesParagraph ¶
func ChildRulesParagraph() string
ChildRulesParagraph is the RULES paragraph of the default rules, one sentence per line: what `template --name card` prints and what a card quotes.
func ClassifyProvider ¶
ClassifyProvider is the class of a provider failure from its HTTP status (0 when none) and its words (the message and whatever body came with it). The status decides where it is unambiguous; the words decide the rest, and refine a 429 that is a quota and a 4xx that is the provider's own server error.
func CountCardTurnsIn ¶
func CountCardTurnsIn(logPath string, u ProviderUsage) int
CountCardTurnsIn counts a job's assistant turns against a NAMED log: the log's assistant lines, or the usage row count where the log has fewer, because the two routes keep the harness's words in two different files: rule 13d names this log and rule 13b counts the turns in it, so the log to count is `<job>/harness-output.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.
func DeclareRouteModel ¶
DeclareRouteModel names a launch's model in the job's harness config as a model of its provider (`provider.<provider>.models.<model>: {}`), so the harness knows the model whatever its catalog holds at start. An entry already there is kept as it is.
Every job runs the harness in a fresh data home, so the model catalog fetches at each start; when that fetch is slow or loses the race it falls back to the snapshot built into the binary, which predates the newer models, and the launch fails with `ProviderModelNotFoundError`. Declaring the model in the harness config prevents that failure whatever the catalog fetch yields.
ok is false when the config is not a JSON object or the model is not provider/model; the body is then returned unchanged.
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 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 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 -- 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 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), then `repo/` and one directory below it. 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. It is exported because `native` asks whether the harness published anything at all. A framed card's finish that the gh shim recorded (cardcontract.FinishName, in the card contract's shape) is a published result too, after the job root's RESULT.md: a child whose `gh pr create` or `gh pr review` is its end published (docs/SPEC-CARD-CONTRACT.md).
func FriendGoCacheLine ¶
FriendGoCacheLine is the cache line of a friend's card (WHO: friend): a friend works in her own working directory, so her GOCACHE is her own build cache, named by path and said so, warm across her cards, not the machine's cache the fleet's children share (docs/FRIENDS.md, the working directory; docs/SPEC-CARD-CONTRACT.md section 2). cmd/nova-sprint's friendBrief writes it into the working-directory line it delivers.
func GoBuildCacheDir ¶
GoBuildCacheDir is the shared build cache (GOCACHE) under a swarm root: the one the child is handed, JOB.md names and the member's lazy cleaner holds under gocache.Limit.
func GoModCacheDir ¶
GoModCacheDir is the shared module cache (GOMODCACHE) under a swarm root: the one name native hands the child, so the directory made here is the one the child uses.
func GroupAlive ¶
GroupAlive reports whether ANY process remains in a group.
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 HeadlessArgv ¶
HeadlessArgv is the argv of one headless harness launch: the binary, its one-shot form, the model, machine output, the permission mode a walled child runs under -- the wall is the boundary, as it is for an opencode child whose own fence allows every tool inside it (fence.go) -- and the prompt last, after `--` where the program takes one. The web tools are OFF in all three, as the opencode child's fence denies webfetch (fence.go FencePermission): a card's shell reaches the network as the wall allows and its harness's own fetch and search tools reach nothing the wall does not, so the child has no wider network than an opencode child (docs/SPEC-SWARM.md, what a card can reach). An unknown kind is refused, never guessed.
func HeadlessLogin ¶
HeadlessLogin reads a login verb's output: whether the harness is logged in, and the line it said. claude prints JSON with `loggedIn`; codex exits 0 saying `Logged in ...` and 1 saying `Not logged in`; grok's model list opens with `You are logged in ...` or `You are not authenticated.`.
func HeadlessLoginArgv ¶
HeadlessLoginArgv is the verb that says whether a headless harness is logged in, read by HeadlessLogin; nil for a kind that is not headless.
func HeadlessProgramRoot ¶
HeadlessProgramRoot is the install a resolved (symlink-free) headless binary runs from, the one directory the wall reads for it: the binary's own directory, and when that is a `bin` directory the install above it, where the program keeps what it runs beside bin (codex: codex-path and codex-resources). A launch is by the resolved path because the wall reads the install and not the symlink chain that leads to it (`~/.local/bin/codex` points into `~/.codex/packages`, which no mount names), so the bench's login directory stays on no mount list.
func HeadlessTokenNote ¶
func HeadlessTokenNote(h HeadlessHome, env []string) string
HeadlessTokenNote is the one line a run says when its harness reads its login from h.Token and env, the child's environment, carries no value for it: the harness will answer logged out, a provider failure of class auth, and the line names the remedy. "" when the harness reads no token or env carries one. The line names the variable, never a value.
func HeldRulesNames ¶
func HeldRulesNames() []string
HeldRulesNames is every rules file this build holds, by base name.
func HeldRulesText ¶
HeldRulesText is the text of the held rules file name (a base name under fleet/), and whether this build holds it.
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 -- 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 IsPulseTemplate ¶
IsPulseTemplate reports whether name is one of the pulse card templates or the cost table nova-pulse `cut` reads. 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 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 (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 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 LintBrief ¶
func LintBrief(raw []byte, bb BriefBase) ([]CardHeaderFinding, BriefFix)
LintBrief holds one card brief to the brief checks at its base, in the order the tokens are listed above, and returns the corrected header lines that answer the PATHS, NEW and SHARED findings in one step.
func LispCacheDir ¶
LispCacheDir is the shared ASDF output directory under a swarm root.
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 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) -- 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 (StageCard): a local clone of the bench mirror, so staging never reads the network. borrow borrows the mirror's objects (`--shared`: the clone's alternates name the mirror's object directory, and no object is copied), for a mirror that keeps every object (mirrorKeepsObjects); otherwise the objects are copied in (`--reference` then `--dissociate`), so the clone owes nothing to a repository that may drop one. The copy is the cost a 36-thread bench paid on 2026-10-02: 21 s at load 31 against 3.4 s borrowed, 73-79 s median under the afternoon's load, over a 120 s wall (docs/SPEC-SWARM.md, the clone). The mirror and the target follow `--`: a card names the repository, and a name that starts with `-` is an operand, never an option.
func MirrorPath ¶
MirrorPath is the bench mirror of the repository named name: the first place FindBenchMirror looks, so a stage finds what a keeper made.
func NPMCacheDir ¶
NPMCacheDir is the shared npm cache (NPM_CONFIG_CACHE) under a swarm root.
func NativeBudgetSourceRefusal ¶
func NativeBudgetSourceRefusal(source string, tokens int, unmetered, usd bool, worker *Worker) string
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.
`usd` is whether a dollar budget (--usd) was given: it is read from the same source, the harness's own cost beside its tokens (nova-tools #5094).
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 the budget-source rule above and not a default anybody should retype.
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 OwnRulesName ¶
OwnRulesName is the held rules file of the repository the brief's REPO: (or base-repo:) names: DefaultRulesName for HomeRepo, child-rules.<repo>.txt when this build holds one, and "" for a repository with none (its cards carry their own rules). A brief that names no repository has none of its own: it is unnamed, the add's held set.
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 whitespace, empty entries dropped, first occurrence winning.
func PointCardAtMirrors ¶
PointCardAtMirrors writes the bench's own mirror directory into a card's clone step: the harness runs with HOME set to the slot's data home, where no mirror is, so the card the member hands it names the directory MirrorPath makes under benchHome instead of $HOME. A card with no clone step, or a benchHome of "", is returned as it was.
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 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 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 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 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 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 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, a friend'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. A friend 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 ResolveInstructionKind ¶
ResolveInstructionKind is the instruction kind the card lint resolves for one card's bytes: its `KIND:` line read through the instruction-kind column of the one kinds list, pkg/hygiene/kinds.txt (docs/SPEC-ISA.md). It returns the work kind too, so a caller can print both. ok is false when the card carries no KIND: line, the kind is not declared, or its row names no instruction kind; LintCardHeader refuses each of those as kind-declared.
func ResultPath ¶
func RulesParagraph ¶
RulesParagraph is the RULES paragraph of a rule set, one sentence per line.
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 the budget-source refusal and AFTER one by the final read, and both ask it the same way.
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 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 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 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 StagedBrief ¶
StagedBrief is the brief a child is handed: the card's text, then its rule set's RULES paragraph (RulesParagraph), after one blank line. A brief that already quotes every sentence of the set (a card written before rules by reference) is handed as it is, and an empty brief stays empty: a card with no brief has no task to hold to rules.
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).
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 -- 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. 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 -- whose 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 `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 the job lease says. 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 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 TakeSlotLeasesWaiting ¶
func TakeSlotLeasesWaiting(ctx context.Context, store, owner string, k int, kind string, dur, wait time.Duration, label string, pid int) (ids []string, held, share, free int, holders string, ok bool, err error)
TakeSlotLeasesWaiting requests k weighted leases for owner, waiting up to wait duration when capacity is occupied. When wait is 0, it makes a single attempt. It checks ctx for cancellation during the wait (docs/SPEC-SWARM.md, "Bench slot leases").
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 ¶
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; a floor was needed beyond non-emptiness. A provider's refusal is a SENTENCE: PhraseFloor characters at least, and whitespace or a digit in it, so that no single word can be one.
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 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 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 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 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 BriefBase ¶
type BriefBase struct {
// Repo is a git repository holding Sha, the commit at the tip of the brief's BASE:
// (or the commit BASE: pins); Missing says why they were not handed over, "" when they
// were.
Repo, Sha, Missing string
// Gone says origin holds no branch of BASE's name: deleted, or never pushed.
Gone bool
// Listed is the bases the sprint's store lists in use (the bases of its cards).
Listed []string
// Friends is each friend's tiers, by name; nil when the friends table was not read.
Friends map[string][]string
}
BriefBase is the evidence the brief checks read beside the brief.
type BriefFix ¶
type BriefFix []string
BriefFix is the corrected header lines of a brief: each line the checks would change, as it would read with every addition the findings ask for, in the order PATHS, NEW, SHARED, line 1.
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
Stage []string // the recipe files the header's `Stage:` lines name, in order (docs/SPEC-CARD-CONTRACT.md)
}
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 member's frame call it, so the repo a card is staged from is the repo it names. It reads the card's HEADER only (docs/SPEC-CARD-CONTRACT.md: the frame is the card's data, never its prose): line 1 and the `key: value` lines that follow it, up to the first line of another form; a line in the body that looks like a header names nothing. Precedence: `base-repo: <url>`, then `REPO: <owner>/<name>` (the header every pushed card carries). The sha is `base-sha:`, else the sha of `BASE: <ref>@<sha40>`; the ref is BASE:'s value before any @.
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 `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 LintCardChild ¶
func LintCardChild(raw []byte) []CardHeaderFinding
LintCardChild returns the child-rule findings for one card under the default rules.
func LintCardChildByReference ¶
func LintCardChildByReference(raw []byte, rules []ChildRule) []CardHeaderFinding
LintCardChildByReference is the child-rule findings for a card whose rules the member injects at stage time: the card is linted as the child is handed it (StagedBrief), so a card that does not carry the rules passes their presence, and a line of the card that contradicts them (a forbidden command, an unfilled Libraries considered line) is still refused. An empty card is still the one finding EmptyCardCheck.
func LintCardChildWith ¶
func LintCardChildWith(raw []byte, rules []ChildRule) []CardHeaderFinding
LintCardChildWith returns the child-rule findings for one card under a rule set: a `rule-<name>` for every required sentence the card does not quote, then a `step-<what>` for every line that runs a forbidden command, each with its line and text. Line 1 carries the missing sentences: the card lacks them everywhere. An empty card is one finding, EmptyCardCheck.
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 UnfilledTemplateLines ¶
func UnfilledTemplateLines(card string) []CardHeaderFinding
UnfilledTemplateLines is every line of a card that is a line of the card template (Template("card")) still holding its <...> fill-ins, as the template printed it: a card cut from the template and handed out with REPO: <owner>/<name> stages nothing and lands nowhere (tool ledger W12). A line partly filled in, or a card not cut from the template, is not this finding: only the template's own lines are read, so a card that writes `--count <n>` in its task is never caught by it. nova-swarm lint names each on a NOTE line and nova-sprint add says so on its own.
type ChildRule ¶
ChildRule is one rule of the child brief: Name is the token (`rule-<Name>` is the lint check), Sentence is what the card quotes verbatim, Source is where the rule came from.
func HeldRules ¶
HeldRules is the rule set of the held rules file name, its rules' source fleet/<name>.
func ParseChildRules ¶
ParseChildRules reads a rules file: one required sentence per line. A blank line and a line starting with `#` are skipped. A line may open with `[name]`, the token its finding carries (`rule-<name>`); a line with none is named `line<n>` by its line number. Two rules of one name, two of one sentence, a name that is not kebab case and a file with no rule at all are refused, each problem named with its line, and every problem of the file is reported together, never the first alone. source names the file in each rule's remedy.
func ReadChildRules ¶
ReadChildRules reads and parses the rules file at path.
type CmdRunner ¶
type CmdRunner func(ctx context.Context, dir string, env []string, name string, args ...string) error
CmdRunner runs one command in dir with env added to the process's own, and returns its failure. A test's fake records the call and runs nothing.
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 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 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.
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 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
Cause ProviderCause // what failed, from the harness's words; native sets a better record's when it has one
}
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 HeadlessHome ¶
type HeadlessHome struct {
Source string // the harness's own directory on the bench, where Login is copied from
Dir string // the private home under the data home
Env []string // NAME=value entries the child carries, nil when the harness reads none
Login []string // the credential files, by name, copied from Source to Dir: nothing else is
Token string // the environment name the harness reads its login from in place of a file, "" for none
}
HeadlessHome is the home a headless harness runs from: a PRIVATE directory under the child's data home, which is the child's HOME and a write of the wall, so the harness writes whatever it keeps (sessions, databases, caches, its config) there and the card discards it. The bench's own login directory (`~/.claude`, `~/.codex`, `~/.grok`) is never mounted, readable or writable: it holds the whole login, the interactive history, the harness's config and hooks, and a write there is a code path outside the wall. What the harness needs of it is its credential file alone, Login, copied into Dir before the launch (docs/SPEC-SWARM.md, what a card can reach), or, for a harness whose file is a refreshable OAuth login, no file at all: its login is the token the run hands it by name, Token. The harness is pointed at Dir by name in Env (claude: CLAUDE_CONFIG_DIR, codex: CODEX_HOME) or, grok reading HOME alone, by being where HOME puts it.
func HeadlessHomeOf ¶
func HeadlessHomeOf(kind, benchHome, dataHome string) HeadlessHome
HeadlessHomeOf is HeadlessHome for one harness: Source under the bench's home directory, Dir under the child's data home. codex's and grok's credential file is `auth.json`. claude's `.credentials.json` is a refreshable OAuth login: a copy taken per launch takes the refresh a turn makes, the next launch discards the copy, and the bench's own refresh token goes stale; and a machine that keeps the login in the macOS keychain has no file to copy and no keychain inside the wall. So claude copies nothing, and its login is the long-lived token in ClaudeTokenEnv, which the run hands it by name (a --pass secret).
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 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 whitespace 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 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 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 MirrorKeeper ¶
type MirrorKeeper struct {
Origin string // the remote: a URL or a path
Mirror string // the bare mirror's directory (MirrorPath)
Ref string // the branch whose tip the cache is warmed at; "" is the mirror's HEAD branch
WarmDir string // where the warm checkout lives, inside the member's work root
GoCache string // GOCACHE the warm builds fill: the one the member's cards share
Run CmdRunner // runs the warm commands; nil is exec
}
MirrorKeeper keeps one bare mirror of Origin at Mirror and a build cache warmed at the mirror's Ref tip. A job's clone borrows the mirror's objects (StageCard, MirrorCloneArgs), so a card's clone reads no network and its first build reads a warm GOCACHE: the two waits the sprint measured in a card's wall (docs/SPEC-SWARM.md, the warm clones and caches card). The mirror carries gc.auto=0 and is only ever fetched into, so no object a live checkout borrows is dropped (mirrorKeepsObjects).
func (MirrorKeeper) Refresh ¶
func (k MirrorKeeper) Refresh(ctx context.Context) (MirrorRefresh, error)
Refresh creates the mirror when absent and fetches into it otherwise, then warms the cache when the Ref tip moved. A warm failure is returned beside the refresh: the mirror is good, and a cold cache costs time, never correctness. It runs at the member's start and after each landing the member sees.
type MirrorRefresh ¶
type MirrorRefresh struct {
Created bool // the mirror did not exist and was cloned
Moved bool // the Ref tip is not the one the cache was last warmed at
Tip string // the Ref tip after the refresh
}
MirrorRefresh is what one Refresh did.
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 ProviderCause ¶
type ProviderCause struct {
Class string // one of the Cause* classes
Status int // the HTTP status, 0 when the record names none
Message string // the provider's own words: one line, bounded, secrets removed
}
ProviderCause is what one provider failure was, as the harness's own record says it.
func CauseFromSessionError ¶
func CauseFromSessionError(raw string) (ProviderCause, bool)
CauseFromSessionError is the cause the harness's session database recorded on the message the provider failed (its `error` JSON); ok is false when raw holds no error.
func CauseFromText ¶
func CauseFromText(text string) ProviderCause
CauseFromText is the cause a line of text records: a harness log's error line, or the last lines of the harness's output (its error envelope). The status is the one the text names, the message the provider's own words in it, else the text itself.
func HeadlessFailure ¶
func HeadlessFailure(kind string, out []byte) (ProviderCause, bool)
HeadlessFailure is the failure a headless child reported in its own output, when it did: claude's result with `is_error` (its `result` is the message), codex's and grok's `{"type":"error","message":...}` line, codex's `turn.failed`. The cause is classed as a provider's log line is (CauseFromText), so an expired login is `auth` and a quota is `out-of-credit`. ok is false when the output names none.
func (ProviderCause) Reason ¶
func (c ProviderCause) Reason() string
Reason is the cause as the finish carries it:
provider: class=<class> status=<status|-> msg=<message>
msg is last and carries the rest of the line.
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 HeadlessUsage ¶
func HeadlessUsage(kind string, out []byte) (ProviderUsage, error)
HeadlessUsage is the usage a headless child printed, read from its capture once it has gone: claude's one JSON result (its `usage` and `total_cost_usd`), grok's (the same names), codex's last `turn.completed` event of its JSONL (no cost: the route's price sheet prices it). A capture with no such record reports nothing (Observed false), an absence; one whose record will not parse is an error, a read that failed.
The Values are the usage row's columns (TokenColumns, cost, provider, model): a class the harness did not report stays a dash, never 0 (rule 12).
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 ReadJobUsageLiveWithin ¶
func ReadJobUsageLiveWithin(dataHome string, limit time.Duration) (ProviderUsage, error)
ReadJobUsageLiveWithin is rule 13d's LIVE sample: the same statement openCodeReader.read 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 the caller's limit (LiveSampleLimit is the least a sampler gives), 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. There are two: `opencode`, the job's own `opencode.db` 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 the budget 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 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.
type Rework ¶
Rework is what a rework's stage carries: the last pushed head of any earlier attempt and the attempt it is the head of (the frame's PrevHead and PrevFrom); Prev is "" when no earlier attempt pushed, and the checkout is the bare tip.
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 (a friend'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 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 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
// Base and Branch, when set, are the frame's (pkg/cardcontract): the repository,
// ref and sha the member's packet names and the branch it pushes, staged in place of
// what the card's header lines say (docs/SPEC-CARD-CONTRACT.md layer 2).
Base *CardBase
Branch string
// Rework, when set, is a rework's (an attempt after the first): the checkout is staged
// at the tip of the base branch on origin, with the work of the last earlier attempt that
// pushed carried on top where it applies cleanly (restageAtTip). A first attempt leaves it
// nil and is staged as before.
Rework *Rework
// contains filtered or unexported fields
}
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
// Clone, Fetch and Checkout sum their Git command times; Clone includes
// the initial checkout, and Fetch excludes probes and the retry wait.
Clone, Fetch, Checkout time.Duration
// Carry is a rework's staging at the tip (restageAtTip); nil for a first attempt and for
// a rework whose base is a sha or a tag, which never moves.
Carry *cardcontract.Carry
}
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 --shared <mirror> (MirrorCloneArgs) 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.
func ParseIdentity ¶
func ParseIdentity(s string) (StagingIdentity, error)
ParseIdentity reads a pool identity given on the command line, as a loop's argv in nova-config carries it (`--identity <owner>,<name>,<email>`), so no identity.tsv has to be written into a pool by hand: the owner is the text before the first comma, the email the text after the last, the name what lies between (a name may hold a comma). Each is required, and the email holds an @.
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 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 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 untrusted files placed there by the bench user.
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 Darwin bench may lack `~/sdk` or dotnet, and 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 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 (the toolwork spec's eligibility rule):
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 retired pulse package; 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 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 )
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, a machine'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
- benchsize.go
- cardbudget.go
- cardresult.go
- contract.go
- deadline.go
- declaremodel.go
- fence.go
- fileretry.go
- fileretry_other.go
- finalize.go
- headless.go
- heldrules.go
- inputlimit.go
- key.go
- launchfail.go
- lease.go
- lintbase.go
- lintchild.go
- lintcontract.go
- lintdepends.go
- lintheader.go
- lintparent.go
- lintpaths.go
- lock.go
- lock_unix.go
- mirror.go
- nativeasked.go
- nativebudget.go
- nativeidle.go
- nofollow_unix.go
- opencode.go
- pathcase.go
- placeholders.go
- pool.go
- prewarm.go
- proc_linux.go
- proc_unix.go
- providercause.go
- providererr.go
- providerproxy.go
- providerread.go
- providers.go
- publicclass.go
- reap.go
- regular.go
- restage.go
- result.go
- resultformat.go
- signature.go
- slot.go
- slots.go
- slotweight.go
- stage.go
- staging.go
- templates.go
- timeline.go
- timeout.go
- toolchain.go
- usage.go
- usagecard.go
- wall.go
- wallread.go
- worker.go